Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6:
qlge: Fix page size ifdef test.
net: Rationalise email address: Network Specific Parts
dsa: fix compile bug on s390
netns: mib6 section fixlet
enic: Fix Kconfig headline description
de2104x: wrong MAC address fix
s390: claw compile fixlet
net: export genphy_restart_aneg
cxgb3: extend copyrights to 2008
cxgb3: update driver version
net/phy: add missing kernel-doc
pktgen: fix skb leak in case of failure
mISDN/dsp_cmx.c: fix size checks
misdn: use nonseekable_open()
net: fix driver build errors due to missing net/ip6_checksum.h include
diff --git a/Documentation/00-INDEX b/Documentation/00-INDEX
index 7306081..4382778 100644
--- a/Documentation/00-INDEX
+++ b/Documentation/00-INDEX
@@ -159,8 +159,6 @@
- info on using the Hayes ESP serial driver.
highuid.txt
- notes on the change from 16 bit to 32 bit user/group IDs.
-hpet.txt
- - High Precision Event Timer Driver for Linux.
timers/
- info on the timer related topics
hw_random.txt
diff --git a/Documentation/blackfin/kgdb.txt b/Documentation/blackfin/kgdb.txt
deleted file mode 100644
index 84f6a48..0000000
--- a/Documentation/blackfin/kgdb.txt
+++ /dev/null
@@ -1,155 +0,0 @@
- A Simple Guide to Configure KGDB
-
- Sonic Zhang <sonic.zhang@analog.com>
- Aug. 24th 2006
-
-
-This KGDB patch enables the kernel developer to do source level debugging on
-the kernel for the Blackfin architecture. The debugging works over either the
-ethernet interface or one of the uarts. Both software breakpoints and
-hardware breakpoints are supported in this version.
-http://docs.blackfin.uclinux.org/doku.php?id=kgdb
-
-
-2 known issues:
-1. This bug:
- http://blackfin.uclinux.org/tracker/index.php?func=detail&aid=544&group_id=18&atid=145
- The GDB client for Blackfin uClinux causes incorrect values of local
- variables to be displayed when the user breaks the running of kernel in GDB.
-2. Because of a hardware bug in Blackfin 533 v1.0.3:
- 05000067 - Watchpoints (Hardware Breakpoints) are not supported
- Hardware breakpoints cannot be set properly.
-
-
-Debug over Ethernet:
-
-1. Compile and install the cross platform version of gdb for blackfin, which
- can be found at $(BINROOT)/bfin-elf-gdb.
-
-2. Apply this patch to the 2.6.x kernel. Select the menuconfig option under
- "Kernel hacking" -> "Kernel debugging" -> "KGDB: kernel debug with remote gdb".
- With this selected, option "Full Symbolic/Source Debugging support" and
- "Compile the kernel with frame pointers" are also selected.
-
-3. Select option "KGDB: connect over (Ethernet)". Add "kgdboe=@target-IP/,@host-IP/" to
- the option "Compiled-in Kernel Boot Parameter" under "Kernel hacking".
-
-4. Connect minicom to the serial port and boot the kernel image.
-
-5. Configure the IP "/> ifconfig eth0 target-IP"
-
-6. Start GDB client "bfin-elf-gdb vmlinux".
-
-7. Connect to the target "(gdb) target remote udp:target-IP:6443".
-
-8. Set software breakpoint "(gdb) break sys_open".
-
-9. Continue "(gdb) c".
-
-10. Run ls in the target console "/> ls".
-
-11. Breakpoint hits. "Breakpoint 1: sys_open(..."
-
-12. Display local variables and function paramters.
- (*) This operation gives wrong results, see known issue 1.
-
-13. Single stepping "(gdb) si".
-
-14. Remove breakpoint 1. "(gdb) del 1"
-
-15. Set hardware breakpoint "(gdb) hbreak sys_open".
-
-16. Continue "(gdb) c".
-
-17. Run ls in the target console "/> ls".
-
-18. Hardware breakpoint hits. "Breakpoint 1: sys_open(...".
- (*) This hardware breakpoint will not be hit, see known issue 2.
-
-19. Continue "(gdb) c".
-
-20. Interrupt the target in GDB "Ctrl+C".
-
-21. Detach from the target "(gdb) detach".
-
-22. Exit GDB "(gdb) quit".
-
-
-Debug over the UART:
-
-1. Compile and install the cross platform version of gdb for blackfin, which
- can be found at $(BINROOT)/bfin-elf-gdb.
-
-2. Apply this patch to the 2.6.x kernel. Select the menuconfig option under
- "Kernel hacking" -> "Kernel debugging" -> "KGDB: kernel debug with remote gdb".
- With this selected, option "Full Symbolic/Source Debugging support" and
- "Compile the kernel with frame pointers" are also selected.
-
-3. Select option "KGDB: connect over (UART)". Set "KGDB: UART port number" to be
- a different one from the console. Don't forget to change the mode of
- blackfin serial driver to PIO. Otherwise kgdb works incorrectly on UART.
-
-4. If you want connect to kgdb when the kernel boots, enable
- "KGDB: Wait for gdb connection early"
-
-5. Compile kernel.
-
-6. Connect minicom to the serial port of the console and boot the kernel image.
-
-7. Start GDB client "bfin-elf-gdb vmlinux".
-
-8. Set the baud rate in GDB "(gdb) set remotebaud 57600".
-
-9. Connect to the target on the second serial port "(gdb) target remote /dev/ttyS1".
-
-10. Set software breakpoint "(gdb) break sys_open".
-
-11. Continue "(gdb) c".
-
-12. Run ls in the target console "/> ls".
-
-13. A breakpoint is hit. "Breakpoint 1: sys_open(..."
-
-14. All other operations are the same as that in KGDB over Ethernet.
-
-
-Debug over the same UART as console:
-
-1. Compile and install the cross platform version of gdb for blackfin, which
- can be found at $(BINROOT)/bfin-elf-gdb.
-
-2. Apply this patch to the 2.6.x kernel. Select the menuconfig option under
- "Kernel hacking" -> "Kernel debugging" -> "KGDB: kernel debug with remote gdb".
- With this selected, option "Full Symbolic/Source Debugging support" and
- "Compile the kernel with frame pointers" are also selected.
-
-3. Select option "KGDB: connect over UART". Set "KGDB: UART port number" to console.
- Don't forget to change the mode of blackfin serial driver to PIO.
- Otherwise kgdb works incorrectly on UART.
-
-4. If you want connect to kgdb when the kernel boots, enable
- "KGDB: Wait for gdb connection early"
-
-5. Connect minicom to the serial port and boot the kernel image.
-
-6. (Optional) Ask target to wait for gdb connection by entering Ctrl+A. In minicom, you should enter Ctrl+A+A.
-
-7. Start GDB client "bfin-elf-gdb vmlinux".
-
-8. Set the baud rate in GDB "(gdb) set remotebaud 57600".
-
-9. Connect to the target "(gdb) target remote /dev/ttyS0".
-
-10. Set software breakpoint "(gdb) break sys_open".
-
-11. Continue "(gdb) c". Then enter Ctrl+C twice to stop GDB connection.
-
-12. Run ls in the target console "/> ls". Dummy string can be seen on the console.
-
-13. Then connect the gdb to target again. "(gdb) target remote /dev/ttyS0".
- Now you will find a breakpoint is hit. "Breakpoint 1: sys_open(..."
-
-14. All other operations are the same as that in KGDB over Ethernet. The only
- difference is that after continue command in GDB, please stop GDB
- connection by 2 "Ctrl+C"s and connect again after breakpoints are hit or
- Ctrl+A is entered.
diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt
index 3d2d0c2..cc8093c 100644
--- a/Documentation/feature-removal-schedule.txt
+++ b/Documentation/feature-removal-schedule.txt
@@ -287,14 +287,6 @@
---------------------------
-What: old style serial driver for ColdFire (CONFIG_SERIAL_COLDFIRE)
-When: 2.6.28
-Why: This driver still uses the old interface and has been replaced
- by CONFIG_SERIAL_MCF.
-Who: Sebastian Siewior <sebastian@breakpoint.cc>
-
----------------------------
-
What: /sys/o2cb symlink
When: January 2010
Why: /sys/fs/o2cb is the proper location for this information - /sys/o2cb
diff --git a/Documentation/filesystems/ext4.txt b/Documentation/filesystems/ext4.txt
index 0d53949..eb154ef 100644
--- a/Documentation/filesystems/ext4.txt
+++ b/Documentation/filesystems/ext4.txt
@@ -32,9 +32,9 @@
you will need to merge your changes with the version from e2fsprogs
1.41.x.
- - Create a new filesystem using the ext4dev filesystem type:
+ - Create a new filesystem using the ext4 filesystem type:
- # mke2fs -t ext4dev /dev/hda1
+ # mke2fs -t ext4 /dev/hda1
Or configure an existing ext3 filesystem to support extents and set
the test_fs flag to indicate that it's ok for an in-development
@@ -47,13 +47,13 @@
# tune2fs -I 256 /dev/hda1
- (Note: we currently do not have tools to convert an ext4dev
+ (Note: we currently do not have tools to convert an ext4
filesystem back to ext3; so please do not do try this on production
filesystems.)
- Mounting:
- # mount -t ext4dev /dev/hda1 /wherever
+ # mount -t ext4 /dev/hda1 /wherever
- When comparing performance with other filesystems, remember that
ext3/4 by default offers higher data integrity guarantees than most.
@@ -177,6 +177,11 @@
your disks are battery-backed in one way or another,
disabling barriers may safely improve performance.
+inode_readahead=n This tuning parameter controls the maximum
+ number of inode table blocks that ext4's inode
+ table readahead algorithm will pre-read into
+ the buffer cache. The default value is 32 blocks.
+
orlov (*) This enables the new Orlov block allocator. It is
enabled by default.
@@ -218,6 +223,11 @@
errors=continue Keep going on a filesystem error.
errors=panic Panic and halt the machine if an error occurs.
+data_err=ignore(*) Just print an error message if an error occurs
+ in a file data buffer in ordered mode.
+data_err=abort Abort the journal if an error occurs in a file
+ data buffer in ordered mode.
+
grpid Give objects the same group ID as their creator.
bsdgroups
@@ -252,6 +262,7 @@
delalloc (*) Deferring block allocation until write-out time.
nodelalloc Disable delayed allocation. Blocks are allocation
when data is copied from user to page cache.
+
Data Mode
=========
There are 3 different data modes:
diff --git a/Documentation/filesystems/fiemap.txt b/Documentation/filesystems/fiemap.txt
new file mode 100644
index 0000000..1e3defc
--- /dev/null
+++ b/Documentation/filesystems/fiemap.txt
@@ -0,0 +1,228 @@
+============
+Fiemap Ioctl
+============
+
+The fiemap ioctl is an efficient method for userspace to get file
+extent mappings. Instead of block-by-block mapping (such as bmap), fiemap
+returns a list of extents.
+
+
+Request Basics
+--------------
+
+A fiemap request is encoded within struct fiemap:
+
+struct fiemap {
+ __u64 fm_start; /* logical offset (inclusive) at
+ * which to start mapping (in) */
+ __u64 fm_length; /* logical length of mapping which
+ * userspace cares about (in) */
+ __u32 fm_flags; /* FIEMAP_FLAG_* flags for request (in/out) */
+ __u32 fm_mapped_extents; /* number of extents that were
+ * mapped (out) */
+ __u32 fm_extent_count; /* size of fm_extents array (in) */
+ __u32 fm_reserved;
+ struct fiemap_extent fm_extents[0]; /* array of mapped extents (out) */
+};
+
+
+fm_start, and fm_length specify the logical range within the file
+which the process would like mappings for. Extents returned mirror
+those on disk - that is, the logical offset of the 1st returned extent
+may start before fm_start, and the range covered by the last returned
+extent may end after fm_length. All offsets and lengths are in bytes.
+
+Certain flags to modify the way in which mappings are looked up can be
+set in fm_flags. If the kernel doesn't understand some particular
+flags, it will return EBADR and the contents of fm_flags will contain
+the set of flags which caused the error. If the kernel is compatible
+with all flags passed, the contents of fm_flags will be unmodified.
+It is up to userspace to determine whether rejection of a particular
+flag is fatal to it's operation. This scheme is intended to allow the
+fiemap interface to grow in the future but without losing
+compatibility with old software.
+
+fm_extent_count specifies the number of elements in the fm_extents[] array
+that can be used to return extents. If fm_extent_count is zero, then the
+fm_extents[] array is ignored (no extents will be returned), and the
+fm_mapped_extents count will hold the number of extents needed in
+fm_extents[] to hold the file's current mapping. Note that there is
+nothing to prevent the file from changing between calls to FIEMAP.
+
+The following flags can be set in fm_flags:
+
+* FIEMAP_FLAG_SYNC
+If this flag is set, the kernel will sync the file before mapping extents.
+
+* FIEMAP_FLAG_XATTR
+If this flag is set, the extents returned will describe the inodes
+extended attribute lookup tree, instead of it's data tree.
+
+
+Extent Mapping
+--------------
+
+Extent information is returned within the embedded fm_extents array
+which userspace must allocate along with the fiemap structure. The
+number of elements in the fiemap_extents[] array should be passed via
+fm_extent_count. The number of extents mapped by kernel will be
+returned via fm_mapped_extents. If the number of fiemap_extents
+allocated is less than would be required to map the requested range,
+the maximum number of extents that can be mapped in the fm_extent[]
+array will be returned and fm_mapped_extents will be equal to
+fm_extent_count. In that case, the last extent in the array will not
+complete the requested range and will not have the FIEMAP_EXTENT_LAST
+flag set (see the next section on extent flags).
+
+Each extent is described by a single fiemap_extent structure as
+returned in fm_extents.
+
+struct fiemap_extent {
+ __u64 fe_logical; /* logical offset in bytes for the start of
+ * the extent */
+ __u64 fe_physical; /* physical offset in bytes for the start
+ * of the extent */
+ __u64 fe_length; /* length in bytes for the extent */
+ __u64 fe_reserved64[2];
+ __u32 fe_flags; /* FIEMAP_EXTENT_* flags for this extent */
+ __u32 fe_reserved[3];
+};
+
+All offsets and lengths are in bytes and mirror those on disk. It is valid
+for an extents logical offset to start before the request or it's logical
+length to extend past the request. Unless FIEMAP_EXTENT_NOT_ALIGNED is
+returned, fe_logical, fe_physical, and fe_length will be aligned to the
+block size of the file system. With the exception of extents flagged as
+FIEMAP_EXTENT_MERGED, adjacent extents will not be merged.
+
+The fe_flags field contains flags which describe the extent returned.
+A special flag, FIEMAP_EXTENT_LAST is always set on the last extent in
+the file so that the process making fiemap calls can determine when no
+more extents are available, without having to call the ioctl again.
+
+Some flags are intentionally vague and will always be set in the
+presence of other more specific flags. This way a program looking for
+a general property does not have to know all existing and future flags
+which imply that property.
+
+For example, if FIEMAP_EXTENT_DATA_INLINE or FIEMAP_EXTENT_DATA_TAIL
+are set, FIEMAP_EXTENT_NOT_ALIGNED will also be set. A program looking
+for inline or tail-packed data can key on the specific flag. Software
+which simply cares not to try operating on non-aligned extents
+however, can just key on FIEMAP_EXTENT_NOT_ALIGNED, and not have to
+worry about all present and future flags which might imply unaligned
+data. Note that the opposite is not true - it would be valid for
+FIEMAP_EXTENT_NOT_ALIGNED to appear alone.
+
+* FIEMAP_EXTENT_LAST
+This is the last extent in the file. A mapping attempt past this
+extent will return nothing.
+
+* FIEMAP_EXTENT_UNKNOWN
+The location of this extent is currently unknown. This may indicate
+the data is stored on an inaccessible volume or that no storage has
+been allocated for the file yet.
+
+* FIEMAP_EXTENT_DELALLOC
+ - This will also set FIEMAP_EXTENT_UNKNOWN.
+Delayed allocation - while there is data for this extent, it's
+physical location has not been allocated yet.
+
+* FIEMAP_EXTENT_ENCODED
+This extent does not consist of plain filesystem blocks but is
+encoded (e.g. encrypted or compressed). Reading the data in this
+extent via I/O to the block device will have undefined results.
+
+Note that it is *always* undefined to try to update the data
+in-place by writing to the indicated location without the
+assistance of the filesystem, or to access the data using the
+information returned by the FIEMAP interface while the filesystem
+is mounted. In other words, user applications may only read the
+extent data via I/O to the block device while the filesystem is
+unmounted, and then only if the FIEMAP_EXTENT_ENCODED flag is
+clear; user applications must not try reading or writing to the
+filesystem via the block device under any other circumstances.
+
+* FIEMAP_EXTENT_DATA_ENCRYPTED
+ - This will also set FIEMAP_EXTENT_ENCODED
+The data in this extent has been encrypted by the file system.
+
+* FIEMAP_EXTENT_NOT_ALIGNED
+Extent offsets and length are not guaranteed to be block aligned.
+
+* FIEMAP_EXTENT_DATA_INLINE
+ This will also set FIEMAP_EXTENT_NOT_ALIGNED
+Data is located within a meta data block.
+
+* FIEMAP_EXTENT_DATA_TAIL
+ This will also set FIEMAP_EXTENT_NOT_ALIGNED
+Data is packed into a block with data from other files.
+
+* FIEMAP_EXTENT_UNWRITTEN
+Unwritten extent - the extent is allocated but it's data has not been
+initialized. This indicates the extent's data will be all zero if read
+through the filesystem but the contents are undefined if read directly from
+the device.
+
+* FIEMAP_EXTENT_MERGED
+This will be set when a file does not support extents, i.e., it uses a block
+based addressing scheme. Since returning an extent for each block back to
+userspace would be highly inefficient, the kernel will try to merge most
+adjacent blocks into 'extents'.
+
+
+VFS -> File System Implementation
+---------------------------------
+
+File systems wishing to support fiemap must implement a ->fiemap callback on
+their inode_operations structure. The fs ->fiemap call is responsible for
+defining it's set of supported fiemap flags, and calling a helper function on
+each discovered extent:
+
+struct inode_operations {
+ ...
+
+ int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
+ u64 len);
+
+->fiemap is passed struct fiemap_extent_info which describes the
+fiemap request:
+
+struct fiemap_extent_info {
+ unsigned int fi_flags; /* Flags as passed from user */
+ unsigned int fi_extents_mapped; /* Number of mapped extents */
+ unsigned int fi_extents_max; /* Size of fiemap_extent array */
+ struct fiemap_extent *fi_extents_start; /* Start of fiemap_extent array */
+};
+
+It is intended that the file system should not need to access any of this
+structure directly.
+
+
+Flag checking should be done at the beginning of the ->fiemap callback via the
+fiemap_check_flags() helper:
+
+int fiemap_check_flags(struct fiemap_extent_info *fieinfo, u32 fs_flags);
+
+The struct fieinfo should be passed in as recieved from ioctl_fiemap(). The
+set of fiemap flags which the fs understands should be passed via fs_flags. If
+fiemap_check_flags finds invalid user flags, it will place the bad values in
+fieinfo->fi_flags and return -EBADR. If the file system gets -EBADR, from
+fiemap_check_flags(), it should immediately exit, returning that error back to
+ioctl_fiemap().
+
+
+For each extent in the request range, the file system should call
+the helper function, fiemap_fill_next_extent():
+
+int fiemap_fill_next_extent(struct fiemap_extent_info *info, u64 logical,
+ u64 phys, u64 len, u32 flags, u32 dev);
+
+fiemap_fill_next_extent() will use the passed values to populate the
+next free extent in the fm_extents array. 'General' extent flags will
+automatically be set from specific flags on behalf of the calling file
+system so that the userspace API is not broken.
+
+fiemap_fill_next_extent() returns 0 on success, and 1 when the
+user-supplied fm_extents array is full. If an error is encountered
+while copying the extent to user memory, -EFAULT will be returned.
diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt
index f566ad9..b488eda 100644
--- a/Documentation/filesystems/proc.txt
+++ b/Documentation/filesystems/proc.txt
@@ -923,45 +923,44 @@
The "procs_blocked" line gives the number of processes currently blocked,
waiting for I/O to complete.
+
1.9 Ext4 file system parameters
------------------------------
-Ext4 file system have one directory per partition under /proc/fs/ext4/
-# ls /proc/fs/ext4/hdc/
-group_prealloc max_to_scan mb_groups mb_history min_to_scan order2_req
-stats stream_req
-mb_groups:
-This file gives the details of multiblock allocator buddy cache of free blocks
+Information about mounted ext4 file systems can be found in
+/proc/fs/ext4. Each mounted filesystem will have a directory in
+/proc/fs/ext4 based on its device name (i.e., /proc/fs/ext4/hdc or
+/proc/fs/ext4/dm-0). The files in each per-device directory are shown
+in Table 1-10, below.
-mb_history:
-Multiblock allocation history.
+Table 1-10: Files in /proc/fs/ext4/<devname>
+..............................................................................
+ File Content
+ mb_groups details of multiblock allocator buddy cache of free blocks
+ mb_history multiblock allocation history
+ stats controls whether the multiblock allocator should start
+ collecting statistics, which are shown during the unmount
+ group_prealloc the multiblock allocator will round up allocation
+ requests to a multiple of this tuning parameter if the
+ stripe size is not set in the ext4 superblock
+ max_to_scan The maximum number of extents the multiblock allocator
+ will search to find the best extent
+ min_to_scan The minimum number of extents the multiblock allocator
+ will search to find the best extent
+ order2_req Tuning parameter which controls the minimum size for
+ requests (as a power of 2) where the buddy cache is
+ used
+ stream_req Files which have fewer blocks than this tunable
+ parameter will have their blocks allocated out of a
+ block group specific preallocation pool, so that small
+ files are packed closely together. Each large file
+ will have its blocks allocated out of its own unique
+ preallocation pool.
+inode_readahead Tuning parameter which controls the maximum number of
+ inode table blocks that ext4's inode table readahead
+ algorithm will pre-read into the buffer cache
+..............................................................................
-stats:
-This file indicate whether the multiblock allocator should start collecting
-statistics. The statistics are shown during unmount
-
-group_prealloc:
-The multiblock allocator normalize the block allocation request to
-group_prealloc filesystem blocks if we don't have strip value set.
-The stripe value can be specified at mount time or during mke2fs.
-
-max_to_scan:
-How long multiblock allocator can look for a best extent (in found extents)
-
-min_to_scan:
-How long multiblock allocator must look for a best extent
-
-order2_req:
-Multiblock allocator use 2^N search using buddies only for requests greater
-than or equal to order2_req. The request size is specfied in file system
-blocks. A value of 2 indicate only if the requests are greater than or equal
-to 4 blocks.
-
-stream_req:
-Files smaller than stream_req are served by the stream allocator, whose
-purpose is to pack requests as close each to other as possible to
-produce smooth I/O traffic. Avalue of 16 indicate that file smaller than 16
-filesystem block size will use group based preallocation.
------------------------------------------------------------------------------
Summary
@@ -1332,13 +1331,6 @@
Because the NMI watchdog shares registers with oprofile, by disabling the NMI
watchdog, oprofile may have more registers to utilize.
-maps_protect
-------------
-
-Enables/Disables the protection of the per-process proc entries "maps" and
-"smaps". When enabled, the contents of these files are visible only to
-readers that are allowed to ptrace() the given process.
-
msgmni
------
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 2ca9c8f..2443f5b 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -658,11 +658,12 @@
earlyprintk= [X86-32,X86-64,SH,BLACKFIN]
earlyprintk=vga
earlyprintk=serial[,ttySn[,baudrate]]
+ earlyprintk=dbgp
Append ",keep" to not disable it when the real console
takes over.
- Only vga or serial at a time, not both.
+ Only vga or serial or usb debug port at a time.
Currently only ttyS0 and ttyS1 are supported.
@@ -1231,6 +1232,29 @@
or
memmap=0x10000$0x18690000
+ memory_corruption_check=0/1 [X86]
+ Some BIOSes seem to corrupt the first 64k of
+ memory when doing things like suspend/resume.
+ Setting this option will scan the memory
+ looking for corruption. Enabling this will
+ both detect corruption and prevent the kernel
+ from using the memory being corrupted.
+ However, its intended as a diagnostic tool; if
+ repeatable BIOS-originated corruption always
+ affects the same memory, you can use memmap=
+ to prevent the kernel from using that memory.
+
+ memory_corruption_check_size=size [X86]
+ By default it checks for corruption in the low
+ 64k, making this memory unavailable for normal
+ use. Use this parameter to scan for
+ corruption in more or less memory.
+
+ memory_corruption_check_period=seconds [X86]
+ By default it checks for corruption every 60
+ seconds. Use this parameter to check at some
+ other rate. 0 disables periodic checking.
+
memtest= [KNL,X86] Enable memtest
Format: <integer>
range: 0,4 : pattern number
@@ -1428,6 +1452,12 @@
nolapic_timer [X86-32,APIC] Do not use the local APIC timer.
+ nox2apic [X86-64,APIC] Do not enable x2APIC mode.
+
+ x2apic_phys [X86-64,APIC] Use x2apic physical mode instead of
+ default x2apic cluster mode on platforms
+ supporting x2apic.
+
noltlbs [PPC] Do not use large page/tlb entries for kernel
lowmem mapping on PPC40x.
diff --git a/Documentation/laptops/disk-shock-protection.txt b/Documentation/laptops/disk-shock-protection.txt
new file mode 100644
index 0000000..0e6ba26
--- /dev/null
+++ b/Documentation/laptops/disk-shock-protection.txt
@@ -0,0 +1,149 @@
+Hard disk shock protection
+==========================
+
+Author: Elias Oltmanns <eo@nebensachen.de>
+Last modified: 2008-10-03
+
+
+0. Contents
+-----------
+
+1. Intro
+2. The interface
+3. References
+4. CREDITS
+
+
+1. Intro
+--------
+
+ATA/ATAPI-7 specifies the IDLE IMMEDIATE command with unload feature.
+Issuing this command should cause the drive to switch to idle mode and
+unload disk heads. This feature is being used in modern laptops in
+conjunction with accelerometers and appropriate software to implement
+a shock protection facility. The idea is to stop all I/O operations on
+the internal hard drive and park its heads on the ramp when critical
+situations are anticipated. The desire to have such a feature
+available on GNU/Linux systems has been the original motivation to
+implement a generic disk head parking interface in the Linux kernel.
+Please note, however, that other components have to be set up on your
+system in order to get disk shock protection working (see
+section 3. References below for pointers to more information about
+that).
+
+
+2. The interface
+----------------
+
+For each ATA device, the kernel exports the file
+block/*/device/unload_heads in sysfs (here assumed to be mounted under
+/sys). Access to /sys/block/*/device/unload_heads is denied with
+-EOPNOTSUPP if the device does not support the unload feature.
+Otherwise, writing an integer value to this file will take the heads
+of the respective drive off the platter and block all I/O operations
+for the specified number of milliseconds. When the timeout expires and
+no further disk head park request has been issued in the meantime,
+normal operation will be resumed. The maximal value accepted for a
+timeout is 30000 milliseconds. Exceeding this limit will return
+-EOVERFLOW, but heads will be parked anyway and the timeout will be
+set to 30 seconds. However, you can always change a timeout to any
+value between 0 and 30000 by issuing a subsequent head park request
+before the timeout of the previous one has expired. In particular, the
+total timeout can exceed 30 seconds and, more importantly, you can
+cancel a previously set timeout and resume normal operation
+immediately by specifying a timeout of 0. Values below -2 are rejected
+with -EINVAL (see below for the special meaning of -1 and -2). If the
+timeout specified for a recent head park request has not yet expired,
+reading from /sys/block/*/device/unload_heads will report the number
+of milliseconds remaining until normal operation will be resumed;
+otherwise, reading the unload_heads attribute will return 0.
+
+For example, do the following in order to park the heads of drive
+/dev/sda and stop all I/O operations for five seconds:
+
+# echo 5000 > /sys/block/sda/device/unload_heads
+
+A simple
+
+# cat /sys/block/sda/device/unload_heads
+
+will show you how many milliseconds are left before normal operation
+will be resumed.
+
+A word of caution: The fact that the interface operates on a basis of
+milliseconds may raise expectations that cannot be satisfied in
+reality. In fact, the ATA specs clearly state that the time for an
+unload operation to complete is vendor specific. The hint in ATA-7
+that this will typically be within 500 milliseconds apparently has
+been dropped in ATA-8.
+
+There is a technical detail of this implementation that may cause some
+confusion and should be discussed here. When a head park request has
+been issued to a device successfully, all I/O operations on the
+controller port this device is attached to will be deferred. That is
+to say, any other device that may be connected to the same port will
+be affected too. The only exception is that a subsequent head unload
+request to that other device will be executed immediately. Further
+operations on that port will be deferred until the timeout specified
+for either device on the port has expired. As far as PATA (old style
+IDE) configurations are concerned, there can only be two devices
+attached to any single port. In SATA world we have port multipliers
+which means that a user-issued head parking request to one device may
+actually result in stopping I/O to a whole bunch of devices. However,
+since this feature is supposed to be used on laptops and does not seem
+to be very useful in any other environment, there will be mostly one
+device per port. Even if the CD/DVD writer happens to be connected to
+the same port as the hard drive, it generally *should* recover just
+fine from the occasional buffer under-run incurred by a head park
+request to the HD. Actually, when you are using an ide driver rather
+than its libata counterpart (i.e. your disk is called /dev/hda
+instead of /dev/sda), then parking the heads of one drive (drive X)
+will generally not affect the mode of operation of another drive
+(drive Y) on the same port as described above. It is only when a port
+reset is required to recover from an exception on drive Y that further
+I/O operations on that drive (and the reset itself) will be delayed
+until drive X is no longer in the parked state.
+
+Finally, there are some hard drives that only comply with an earlier
+version of the ATA standard than ATA-7, but do support the unload
+feature nonetheless. Unfortunately, there is no safe way Linux can
+detect these devices, so you won't be able to write to the
+unload_heads attribute. If you know that your device really does
+support the unload feature (for instance, because the vendor of your
+laptop or the hard drive itself told you so), then you can tell the
+kernel to enable the usage of this feature for that drive by writing
+the special value -1 to the unload_heads attribute:
+
+# echo -1 > /sys/block/sda/device/unload_heads
+
+will enable the feature for /dev/sda, and giving -2 instead of -1 will
+disable it again.
+
+
+3. References
+-------------
+
+There are several laptops from different vendors featuring shock
+protection capabilities. As manufacturers have refused to support open
+source development of the required software components so far, Linux
+support for shock protection varies considerably between different
+hardware implementations. Ideally, this section should contain a list
+of pointers at different projects aiming at an implementation of shock
+protection on different systems. Unfortunately, I only know of a
+single project which, although still considered experimental, is fit
+for use. Please feel free to add projects that have been the victims
+of my ignorance.
+
+- http://www.thinkwiki.org/wiki/HDAPS
+ See this page for information about Linux support of the hard disk
+ active protection system as implemented in IBM/Lenovo Thinkpads.
+
+
+4. CREDITS
+----------
+
+This implementation of disk head parking has been inspired by a patch
+originally published by Jon Escombe <lists@dresco.co.uk>. My efforts
+to develop an implementation of this feature that is fit to be merged
+into mainline have been aided by various kernel developers, in
+particular by Tejun Heo and Bartlomiej Zolnierkiewicz.
diff --git a/Documentation/pcmcia/driver-changes.txt b/Documentation/pcmcia/driver-changes.txt
index 96f155e..0599343 100644
--- a/Documentation/pcmcia/driver-changes.txt
+++ b/Documentation/pcmcia/driver-changes.txt
@@ -1,5 +1,11 @@
This file details changes in 2.6 which affect PCMCIA card driver authors:
+* New configuration loop helper (as of 2.6.28)
+ By calling pcmcia_loop_config(), a driver can iterate over all available
+ configuration options. During a driver's probe() phase, one doesn't need
+ to use pcmcia_get_{first,next}_tuple, pcmcia_get_tuple_data and
+ pcmcia_parse_tuple directly in most if not all cases.
+
* New release helper (as of 2.6.17)
Instead of calling pcmcia_release_{configuration,io,irq,win}, all that's
necessary now is calling pcmcia_disable_device. As there is no valid
diff --git a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
index b54cb50..87a7c07a 100644
--- a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
+++ b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
@@ -5073,8 +5073,7 @@
with <constant>SNDRV_DMA_TYPE_CONTINUOUS</constant> type and the
<function>snd_dma_continuous_data(GFP_KERNEL)</function> device pointer,
where <constant>GFP_KERNEL</constant> is the kernel allocation flag to
- use. For the SBUS, <constant>SNDRV_DMA_TYPE_SBUS</constant> and
- <function>snd_dma_sbus_data(sbus_dev)</function> are used instead.
+ use.
For the PCI scatter-gather buffers, use
<constant>SNDRV_DMA_TYPE_DEV_SG</constant> with
<function>snd_dma_pci_data(pci)</function>
diff --git a/Documentation/sparc/sbus_drivers.txt b/Documentation/sparc/sbus_drivers.txt
deleted file mode 100644
index eb1e28a..0000000
--- a/Documentation/sparc/sbus_drivers.txt
+++ /dev/null
@@ -1,309 +0,0 @@
-
- Writing SBUS Drivers
-
- David S. Miller (davem@redhat.com)
-
- The SBUS driver interfaces of the Linux kernel have been
-revamped completely for 2.4.x for several reasons. Foremost were
-performance and complexity concerns. This document details these
-new interfaces and how they are used to write an SBUS device driver.
-
- SBUS drivers need to include <asm/sbus.h> to get access
-to functions and structures described here.
-
- Probing and Detection
-
- Each SBUS device inside the machine is described by a
-structure called "struct sbus_dev". Likewise, each SBUS bus
-found in the system is described by a "struct sbus_bus". For
-each SBUS bus, the devices underneath are hung in a tree-like
-fashion off of the bus structure.
-
- The SBUS device structure contains enough information
-for you to implement your device probing algorithm and obtain
-the bits necessary to run your device. The most commonly
-used members of this structure, and their typical usage,
-will be detailed below.
-
- Here is a piece of skeleton code for performing a device
-probe in an SBUS driver under Linux:
-
- static int __devinit mydevice_probe_one(struct sbus_dev *sdev)
- {
- struct mysdevice *mp = kzalloc(sizeof(*mp), GFP_KERNEL);
-
- if (!mp)
- return -ENODEV;
-
- ...
- dev_set_drvdata(&sdev->ofdev.dev, mp);
- return 0;
- ...
- }
-
- static int __devinit mydevice_probe(struct of_device *dev,
- const struct of_device_id *match)
- {
- struct sbus_dev *sdev = to_sbus_device(&dev->dev);
-
- return mydevice_probe_one(sdev);
- }
-
- static int __devexit mydevice_remove(struct of_device *dev)
- {
- struct sbus_dev *sdev = to_sbus_device(&dev->dev);
- struct mydevice *mp = dev_get_drvdata(&dev->dev);
-
- return mydevice_remove_one(sdev, mp);
- }
-
- static struct of_device_id mydevice_match[] = {
- {
- .name = "mydevice",
- },
- {},
- };
-
- MODULE_DEVICE_TABLE(of, mydevice_match);
-
- static struct of_platform_driver mydevice_driver = {
- .match_table = mydevice_match,
- .probe = mydevice_probe,
- .remove = __devexit_p(mydevice_remove),
- .driver = {
- .name = "mydevice",
- },
- };
-
- static int __init mydevice_init(void)
- {
- return of_register_driver(&mydevice_driver, &sbus_bus_type);
- }
-
- static void __exit mydevice_exit(void)
- {
- of_unregister_driver(&mydevice_driver);
- }
-
- module_init(mydevice_init);
- module_exit(mydevice_exit);
-
- The mydevice_match table is a series of entries which
-describes what SBUS devices your driver is meant for. In the
-simplest case you specify a string for the 'name' field. Every
-SBUS device with a 'name' property matching your string will
-be passed one-by-one to your .probe method.
-
- You should store away your device private state structure
-pointer in the drvdata area so that you can retrieve it later on
-in your .remove method.
-
- Any memory allocated, registers mapped, IRQs registered,
-etc. must be undone by your .remove method so that all resources
-of your device are released by the time it returns.
-
- You should _NOT_ use the for_each_sbus(), for_each_sbusdev(),
-and for_all_sbusdev() interfaces. They are deprecated, will be
-removed, and no new driver should reference them ever.
-
- Mapping and Accessing I/O Registers
-
- Each SBUS device structure contains an array of descriptors
-which describe each register set. We abuse struct resource for that.
-They each correspond to the "reg" properties provided by the OBP firmware.
-
- Before you can access your device's registers you must map
-them. And later if you wish to shutdown your driver (for module
-unload or similar) you must unmap them. You must treat them as
-a resource, which you allocate (map) before using and free up
-(unmap) when you are done with it.
-
- The mapping information is stored in an opaque value
-typed as an "unsigned long". This is the type of the return value
-of the mapping interface, and the arguments to the unmapping
-interface. Let's say you want to map the first set of registers.
-Perhaps part of your driver software state structure looks like:
-
- struct mydevice {
- unsigned long control_regs;
- ...
- struct sbus_dev *sdev;
- ...
- };
-
- At initialization time you then use the sbus_ioremap
-interface to map in your registers, like so:
-
- static void init_one_mydevice(struct sbus_dev *sdev)
- {
- struct mydevice *mp;
- ...
-
- mp->control_regs = sbus_ioremap(&sdev->resource[0], 0,
- CONTROL_REGS_SIZE, "mydevice regs");
- if (!mp->control_regs) {
- /* Failure, cleanup and return. */
- }
- }
-
- Second argument to sbus_ioremap is an offset for
-cranky devices with broken OBP PROM. The sbus_ioremap uses only
-a start address and flags from the resource structure.
-Therefore it is possible to use the same resource to map
-several sets of registers or even to fabricate a resource
-structure if driver gets physical address from some private place.
-This practice is discouraged though. Use whatever OBP PROM
-provided to you.
-
- And here is how you might unmap these registers later at
-driver shutdown or module unload time, using the sbus_iounmap
-interface:
-
- static void mydevice_unmap_regs(struct mydevice *mp)
- {
- sbus_iounmap(mp->control_regs, CONTROL_REGS_SIZE);
- }
-
- Finally, to actually access your registers there are 6
-interface routines at your disposal. Accesses are byte (8 bit),
-word (16 bit), or longword (32 bit) sized. Here they are:
-
- u8 sbus_readb(unsigned long reg) /* read byte */
- u16 sbus_readw(unsigned long reg) /* read word */
- u32 sbus_readl(unsigned long reg) /* read longword */
- void sbus_writeb(u8 value, unsigned long reg) /* write byte */
- void sbus_writew(u16 value, unsigned long reg) /* write word */
- void sbus_writel(u32 value, unsigned long reg) /* write longword */
-
- So, let's say your device has a control register of some sort
-at offset zero. The following might implement resetting your device:
-
- #define CONTROL 0x00UL
-
- #define CONTROL_RESET 0x00000001 /* Reset hardware */
-
- static void mydevice_reset(struct mydevice *mp)
- {
- sbus_writel(CONTROL_RESET, mp->regs + CONTROL);
- }
-
- Or perhaps there is a data port register at an offset of
-16 bytes which allows you to read bytes from a fifo in the device:
-
- #define DATA 0x10UL
-
- static u8 mydevice_get_byte(struct mydevice *mp)
- {
- return sbus_readb(mp->regs + DATA);
- }
-
- It's pretty straightforward, and clueful readers may have
-noticed that these interfaces mimick the PCI interfaces of the
-Linux kernel. This was not by accident.
-
- WARNING:
-
- DO NOT try to treat these opaque register mapping
- values as a memory mapped pointer to some structure
- which you can dereference.
-
- It may be memory mapped, it may not be. In fact it
- could be a physical address, or it could be the time
- of day xor'd with 0xdeadbeef. :-)
-
- Whatever it is, it's an implementation detail. The
- interface was done this way to shield the driver
- author from such complexities.
-
- Doing DVMA
-
- SBUS devices can perform DMA transactions in a way similar
-to PCI but dissimilar to ISA, e.g. DMA masters supply address.
-In contrast to PCI, however, that address (a bus address) is
-translated by IOMMU before a memory access is performed and therefore
-it is virtual. Sun calls this procedure DVMA.
-
- Linux supports two styles of using SBUS DVMA: "consistent memory"
-and "streaming DVMA". CPU view of consistent memory chunk is, well,
-consistent with a view of a device. Think of it as an uncached memory.
-Typically this way of doing DVMA is not very fast and drivers use it
-mostly for control blocks or queues. On some CPUs we cannot flush or
-invalidate individual pages or cache lines and doing explicit flushing
-over ever little byte in every control block would be wasteful.
-
-Streaming DVMA is a preferred way to transfer large amounts of data.
-This process works in the following way:
-1. a CPU stops accessing a certain part of memory,
- flushes its caches covering that memory;
-2. a device does DVMA accesses, then posts an interrupt;
-3. CPU invalidates its caches and starts to access the memory.
-
-A single streaming DVMA operation can touch several discontiguous
-regions of a virtual bus address space. This is called a scatter-gather
-DVMA.
-
-[TBD: Why do not we neither Solaris attempt to map disjoint pages
-into a single virtual chunk with the help of IOMMU, so that non SG
-DVMA masters would do SG? It'd be very helpful for RAID.]
-
- In order to perform a consistent DVMA a driver does something
-like the following:
-
- char *mem; /* Address in the CPU space */
- u32 busa; /* Address in the SBus space */
-
- mem = (char *) sbus_alloc_consistent(sdev, MYMEMSIZE, &busa);
-
- Then mem is used when CPU accesses this memory and u32
-is fed to the device so that it can do DVMA. This is typically
-done with an sbus_writel() into some device register.
-
- Do not forget to free the DVMA resources once you are done:
-
- sbus_free_consistent(sdev, MYMEMSIZE, mem, busa);
-
- Streaming DVMA is more interesting. First you allocate some
-memory suitable for it or pin down some user pages. Then it all works
-like this:
-
- char *mem = argumen1;
- unsigned int size = argument2;
- u32 busa; /* Address in the SBus space */
-
- *mem = 1; /* CPU can access */
- busa = sbus_map_single(sdev, mem, size);
- if (busa == 0) .......
-
- /* Tell the device to use busa here */
- /* CPU cannot access the memory without sbus_dma_sync_single() */
-
- sbus_unmap_single(sdev, busa, size);
- if (*mem == 0) .... /* CPU can access again */
-
- It is possible to retain mappings and ask the device to
-access data again and again without calling sbus_unmap_single.
-However, CPU caches must be invalidated with sbus_dma_sync_single
-before such access.
-
-[TBD but what about writeback caches here... do we have any?]
-
- There is an equivalent set of functions doing the same thing
-only with several memory segments at once for devices capable of
-scatter-gather transfers. Use the Source, Luke.
-
- Examples
-
- drivers/net/sunhme.c
- This is a complicated driver which illustrates many concepts
-discussed above and plus it handles both PCI and SBUS boards.
-
- drivers/scsi/esp.c
- Check it out for scatter-gather DVMA.
-
- drivers/sbus/char/bpp.c
- A non-DVMA device.
-
- drivers/net/sunlance.c
- Lance driver abuses consistent mappings for data transfer.
-It is a nifty trick which we do not particularly recommend...
-Just check it out and know that it's legal.
diff --git a/Documentation/timers/00-INDEX b/Documentation/timers/00-INDEX
new file mode 100644
index 0000000..397dc35
--- /dev/null
+++ b/Documentation/timers/00-INDEX
@@ -0,0 +1,10 @@
+00-INDEX
+ - this file
+highres.txt
+ - High resolution timers and dynamic ticks design notes
+hpet.txt
+ - High Precision Event Timer Driver for Linux
+hrtimers.txt
+ - subsystem for high-resolution kernel timers
+timer_stats.txt
+ - timer usage statistics
diff --git a/Documentation/hpet.txt b/Documentation/timers/hpet.txt
similarity index 81%
rename from Documentation/hpet.txt
rename to Documentation/timers/hpet.txt
index 6ad52d9..e7c09ab 100644
--- a/Documentation/hpet.txt
+++ b/Documentation/timers/hpet.txt
@@ -1,21 +1,32 @@
High Precision Event Timer Driver for Linux
-The High Precision Event Timer (HPET) hardware is the future replacement
-for the 8254 and Real Time Clock (RTC) periodic timer functionality.
-Each HPET can have up to 32 timers. It is possible to configure the
-first two timers as legacy replacements for 8254 and RTC periodic timers.
-A specification done by Intel and Microsoft can be found at
-<http://www.intel.com/technology/architecture/hpetspec.htm>.
+The High Precision Event Timer (HPET) hardware follows a specification
+by Intel and Microsoft which can be found at
+
+ http://www.intel.com/technology/architecture/hpetspec.htm
+
+Each HPET has one fixed-rate counter (at 10+ MHz, hence "High Precision")
+and up to 32 comparators. Normally three or more comparators are provided,
+each of which can generate oneshot interupts and at least one of which has
+additional hardware to support periodic interrupts. The comparators are
+also called "timers", which can be misleading since usually timers are
+independent of each other ... these share a counter, complicating resets.
+
+HPET devices can support two interrupt routing modes. In one mode, the
+comparators are additional interrupt sources with no particular system
+role. Many x86 BIOS writers don't route HPET interrupts at all, which
+prevents use of that mode. They support the other "legacy replacement"
+mode where the first two comparators block interrupts from 8254 timers
+and from the RTC.
The driver supports detection of HPET driver allocation and initialization
of the HPET before the driver module_init routine is called. This enables
platform code which uses timer 0 or 1 as the main timer to intercept HPET
initialization. An example of this initialization can be found in
-arch/i386/kernel/time_hpet.c.
+arch/x86/kernel/hpet.c.
-The driver provides two APIs which are very similar to the API found in
-the rtc.c driver. There is a user space API and a kernel space API.
-An example user space program is provided below.
+The driver provides a userspace API which resembles the API found in the
+RTC driver framework. An example user space program is provided below.
#include <stdio.h>
#include <stdlib.h>
@@ -286,15 +297,3 @@
return;
}
-
-The kernel API has three interfaces exported from the driver:
-
- hpet_register(struct hpet_task *tp, int periodic)
- hpet_unregister(struct hpet_task *tp)
- hpet_control(struct hpet_task *tp, unsigned int cmd, unsigned long arg)
-
-The kernel module using this interface fills in the ht_func and ht_data
-members of the hpet_task structure before calling hpet_register.
-hpet_control simply vectors to the hpet_ioctl routine and has the same
-commands and respective arguments as the user API. hpet_unregister
-is used to terminate usage of the HPET timer reserved by hpet_register.
diff --git a/Documentation/video4linux/CARDLIST.bttv b/Documentation/video4linux/CARDLIST.bttv
index f32efb6..60ba668 100644
--- a/Documentation/video4linux/CARDLIST.bttv
+++ b/Documentation/video4linux/CARDLIST.bttv
@@ -150,3 +150,4 @@
149 -> Typhoon TV-Tuner PCI (50684)
150 -> Geovision GV-600 [008a:763c]
151 -> Kozumi KTV-01C
+152 -> Encore ENL TV-FM-2 [1000:1801]
diff --git a/Documentation/video4linux/CARDLIST.cx23885 b/Documentation/video4linux/CARDLIST.cx23885
index f0e613b..64823cc 100644
--- a/Documentation/video4linux/CARDLIST.cx23885
+++ b/Documentation/video4linux/CARDLIST.cx23885
@@ -9,3 +9,5 @@
8 -> Hauppauge WinTV-HVR1700 [0070:8101]
9 -> Hauppauge WinTV-HVR1400 [0070:8010]
10 -> DViCO FusionHDTV7 Dual Express [18ac:d618]
+ 11 -> DViCO FusionHDTV DVB-T Dual Express [18ac:db78]
+ 12 -> Leadtek Winfast PxDVR3200 H [107d:6681]
diff --git a/Documentation/video4linux/CARDLIST.cx88 b/Documentation/video4linux/CARDLIST.cx88
index 7cf5685..a5227e3 100644
--- a/Documentation/video4linux/CARDLIST.cx88
+++ b/Documentation/video4linux/CARDLIST.cx88
@@ -66,3 +66,11 @@
65 -> DViCO FusionHDTV 7 Gold [18ac:d610]
66 -> Prolink Pixelview MPEG 8000GT [1554:4935]
67 -> Kworld PlusTV HD PCI 120 (ATSC 120) [17de:08c1]
+ 68 -> Hauppauge WinTV-HVR4000 DVB-S/S2/T/Hybrid [0070:6900,0070:6904,0070:6902]
+ 69 -> Hauppauge WinTV-HVR4000(Lite) DVB-S/S2 [0070:6905,0070:6906]
+ 70 -> TeVii S460 DVB-S/S2 [d460:9022]
+ 71 -> Omicom SS4 DVB-S/S2 PCI [A044:2011]
+ 72 -> TBS 8920 DVB-S/S2 [8920:8888]
+ 73 -> TeVii S420 DVB-S [d420:9022]
+ 74 -> Prolink Pixelview Global Extreme [1554:4976]
+ 75 -> PROF 7300 DVB-S/S2 [B033:3033]
diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx
index 53449cb..187cc48 100644
--- a/Documentation/video4linux/CARDLIST.em28xx
+++ b/Documentation/video4linux/CARDLIST.em28xx
@@ -1,5 +1,5 @@
0 -> Unknown EM2800 video grabber (em2800) [eb1a:2800]
- 1 -> Unknown EM2750/28xx video grabber (em2820/em2840) [eb1a:2820,eb1a:2821,eb1a:2860,eb1a:2861,eb1a:2870,eb1a:2881,eb1a:2883]
+ 1 -> Unknown EM2750/28xx video grabber (em2820/em2840) [eb1a:2820,eb1a:2860,eb1a:2861,eb1a:2870,eb1a:2881,eb1a:2883]
2 -> Terratec Cinergy 250 USB (em2820/em2840) [0ccd:0036]
3 -> Pinnacle PCTV USB 2 (em2820/em2840) [2304:0208]
4 -> Hauppauge WinTV USB 2 (em2820/em2840) [2040:4200,2040:4201]
@@ -12,7 +12,7 @@
11 -> Terratec Hybrid XS (em2880) [0ccd:0042]
12 -> Kworld PVR TV 2800 RF (em2820/em2840)
13 -> Terratec Prodigy XS (em2880) [0ccd:0047]
- 14 -> Pixelview Prolink PlayTV USB 2.0 (em2820/em2840)
+ 14 -> Pixelview Prolink PlayTV USB 2.0 (em2820/em2840) [eb1a:2821]
15 -> V-Gear PocketTV (em2800)
16 -> Hauppauge WinTV HVR 950 (em2883) [2040:6513,2040:6517,2040:651b,2040:651f]
17 -> Pinnacle PCTV HD Pro Stick (em2880) [2304:0227]
diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134
index 39868af..dc67eef 100644
--- a/Documentation/video4linux/CARDLIST.saa7134
+++ b/Documentation/video4linux/CARDLIST.saa7134
@@ -76,7 +76,7 @@
75 -> AVerMedia AVerTVHD MCE A180 [1461:1044]
76 -> SKNet MonsterTV Mobile [1131:4ee9]
77 -> Pinnacle PCTV 40i/50i/110i (saa7133) [11bd:002e]
- 78 -> ASUSTeK P7131 Dual [1043:4862,1043:4857]
+ 78 -> ASUSTeK P7131 Dual [1043:4862]
79 -> Sedna/MuchTV PC TV Cardbus TV/Radio (ITO25 Rev:2B)
80 -> ASUS Digimatrix TV [1043:0210]
81 -> Philips Tiger reference design [1131:2018]
@@ -145,3 +145,9 @@
144 -> Beholder BeholdTV M6 Extra [5ace:6193]
145 -> AVerMedia MiniPCI DVB-T Hybrid M103 [1461:f636]
146 -> ASUSTeK P7131 Analog
+147 -> Asus Tiger 3in1 [1043:4878]
+148 -> Encore ENLTV-FM v5.3 [1a7f:2008]
+149 -> Avermedia PCI pure analog (M135A) [1461:f11d]
+150 -> Zogis Real Angel 220
+151 -> ADS Tech Instant HDTV [1421:0380]
+152 -> Asus Tiger Rev:1.00 [1043:4857]
diff --git a/Documentation/video4linux/CARDLIST.tuner b/Documentation/video4linux/CARDLIST.tuner
index 0e23946..30bbdda 100644
--- a/Documentation/video4linux/CARDLIST.tuner
+++ b/Documentation/video4linux/CARDLIST.tuner
@@ -74,3 +74,4 @@
tuner=73 - Samsung TCPG 6121P30A
tuner=75 - Philips TEA5761 FM Radio
tuner=76 - Xceive 5000 tuner
+tuner=77 - TCL tuner MF02GIP-5N-E
diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt
index 9a3e4d7..004818f 100644
--- a/Documentation/video4linux/gspca.txt
+++ b/Documentation/video4linux/gspca.txt
@@ -7,6 +7,7 @@
xxxx vend:prod
----
spca501 0000:0000 MystFromOri Unknow Camera
+m5602 0402:5602 ALi Video Camera Controller
spca501 040a:0002 Kodak DVC-325
spca500 040a:0300 Kodak EZ200
zc3xx 041e:041e Creative WebCam Live!
@@ -42,6 +43,7 @@
zc3xx 0458:700c Genius VideoCam V3
zc3xx 0458:700f Genius VideoCam Web V2
sonixj 0458:7025 Genius Eye 311Q
+sonixj 0458:702e Genius Slim 310 NB
sonixj 045e:00f5 MicroSoft VX3000
sonixj 045e:00f7 MicroSoft VX1000
ov519 045e:028c Micro$oft xbox cam
@@ -81,7 +83,7 @@
spca561 046d:092c Logitech QC chat Elch2
spca561 046d:092d Logitech QC Elch2
spca561 046d:092e Logitech QC Elch2
-spca561 046d:092f Logitech QC Elch2
+spca561 046d:092f Logitech QuickCam Express Plus
sunplus 046d:0960 Logitech ClickSmart 420
sunplus 0471:0322 Philips DMVC1300K
zc3xx 0471:0325 Philips SPC 200 NC
@@ -96,6 +98,29 @@
sunplus 04a5:3008 Benq DC 1500
sunplus 04a5:300a Benq DC 3410
spca500 04a5:300c Benq DC 1016
+finepix 04cb:0104 Fujifilm FinePix 4800
+finepix 04cb:0109 Fujifilm FinePix A202
+finepix 04cb:010b Fujifilm FinePix A203
+finepix 04cb:010f Fujifilm FinePix A204
+finepix 04cb:0111 Fujifilm FinePix A205
+finepix 04cb:0113 Fujifilm FinePix A210
+finepix 04cb:0115 Fujifilm FinePix A303
+finepix 04cb:0117 Fujifilm FinePix A310
+finepix 04cb:0119 Fujifilm FinePix F401
+finepix 04cb:011b Fujifilm FinePix F402
+finepix 04cb:011d Fujifilm FinePix F410
+finepix 04cb:0121 Fujifilm FinePix F601
+finepix 04cb:0123 Fujifilm FinePix F700
+finepix 04cb:0125 Fujifilm FinePix M603
+finepix 04cb:0127 Fujifilm FinePix S300
+finepix 04cb:0129 Fujifilm FinePix S304
+finepix 04cb:012b Fujifilm FinePix S500
+finepix 04cb:012d Fujifilm FinePix S602
+finepix 04cb:012f Fujifilm FinePix S700
+finepix 04cb:0131 Fujifilm FinePix unknown model
+finepix 04cb:013b Fujifilm FinePix unknown model
+finepix 04cb:013d Fujifilm FinePix unknown model
+finepix 04cb:013f Fujifilm FinePix F420
sunplus 04f1:1001 JVC GC A50
spca561 04fc:0561 Flexcam 100
sunplus 04fc:500c Sunplus CA500C
@@ -181,6 +206,7 @@
pac207 093a:2470 Genius GF112
pac207 093a:2471 Genius VideoCam ge111
pac207 093a:2472 Genius VideoCam ge110
+pac207 093a:2476 Genius e-Messenger 112
pac7311 093a:2600 PAC7311 Typhoon
pac7311 093a:2601 Philips SPC 610 NC
pac7311 093a:2603 PAC7312
diff --git a/Documentation/video4linux/m5602.txt b/Documentation/video4linux/m5602.txt
new file mode 100644
index 0000000..4450ab1
--- /dev/null
+++ b/Documentation/video4linux/m5602.txt
@@ -0,0 +1,12 @@
+This document describes the ALi m5602 bridge connected
+to the following supported sensors:
+OmniVision OV9650,
+Samsung s5k83a,
+Samsung s5k4aa,
+Micron mt9m111,
+Pixel plus PO1030
+
+This driver mimics the windows drivers, which have a braindead implementation sending bayer-encoded frames at VGA resolution.
+In a perfect world we should be able to reprogram the m5602 and the connected sensor in hardware instead, supporting a range of resolutions and pixelformats
+
+Anyway, have fun and please report any bugs to m560x-driver-devel@lists.sourceforge.net
diff --git a/Documentation/video4linux/soc-camera.txt b/Documentation/video4linux/soc-camera.txt
new file mode 100644
index 0000000..178ef3c
--- /dev/null
+++ b/Documentation/video4linux/soc-camera.txt
@@ -0,0 +1,120 @@
+ Soc-Camera Subsystem
+ ====================
+
+Terminology
+-----------
+
+The following terms are used in this document:
+ - camera / camera device / camera sensor - a video-camera sensor chip, capable
+ of connecting to a variety of systems and interfaces, typically uses i2c for
+ control and configuration, and a parallel or a serial bus for data.
+ - camera host - an interface, to which a camera is connected. Typically a
+ specialised interface, present on many SoCs, e.g., PXA27x and PXA3xx, SuperH,
+ AVR32, i.MX27, i.MX31.
+ - camera host bus - a connection between a camera host and a camera. Can be
+ parallel or serial, consists of data and control lines, e.g., clock, vertical
+ and horizontal synchronization signals.
+
+Purpose of the soc-camera subsystem
+-----------------------------------
+
+The soc-camera subsystem provides a unified API between camera host drivers and
+camera sensor drivers. It implements a V4L2 interface to the user, currently
+only the mmap method is supported.
+
+This subsystem has been written to connect drivers for System-on-Chip (SoC)
+video capture interfaces with drivers for CMOS camera sensor chips to enable
+the reuse of sensor drivers with various hosts. The subsystem has been designed
+to support multiple camera host interfaces and multiple cameras per interface,
+although most applications have only one camera sensor.
+
+Existing drivers
+----------------
+
+As of 2.6.27-rc4 there are two host drivers in the mainline: pxa_camera.c for
+PXA27x SoCs and sh_mobile_ceu_camera.c for SuperH SoCs, and four sensor drivers:
+mt9m001.c, mt9m111.c, mt9v022.c and a generic soc_camera_platform.c driver. This
+list is not supposed to be updated, look for more examples in your tree.
+
+Camera host API
+---------------
+
+A host camera driver is registered using the
+
+soc_camera_host_register(struct soc_camera_host *);
+
+function. The host object can be initialized as follows:
+
+static struct soc_camera_host pxa_soc_camera_host = {
+ .drv_name = PXA_CAM_DRV_NAME,
+ .ops = &pxa_soc_camera_host_ops,
+};
+
+All camera host methods are passed in a struct soc_camera_host_ops:
+
+static struct soc_camera_host_ops pxa_soc_camera_host_ops = {
+ .owner = THIS_MODULE,
+ .add = pxa_camera_add_device,
+ .remove = pxa_camera_remove_device,
+ .suspend = pxa_camera_suspend,
+ .resume = pxa_camera_resume,
+ .set_fmt_cap = pxa_camera_set_fmt_cap,
+ .try_fmt_cap = pxa_camera_try_fmt_cap,
+ .init_videobuf = pxa_camera_init_videobuf,
+ .reqbufs = pxa_camera_reqbufs,
+ .poll = pxa_camera_poll,
+ .querycap = pxa_camera_querycap,
+ .try_bus_param = pxa_camera_try_bus_param,
+ .set_bus_param = pxa_camera_set_bus_param,
+};
+
+.add and .remove methods are called when a sensor is attached to or detached
+from the host, apart from performing host-internal tasks they shall also call
+sensor driver's .init and .release methods respectively. .suspend and .resume
+methods implement host's power-management functionality and its their
+responsibility to call respective sensor's methods. .try_bus_param and
+.set_bus_param are used to negotiate physical connection parameters between the
+host and the sensor. .init_videobuf is called by soc-camera core when a
+video-device is opened, further video-buffer management is implemented completely
+by the specific camera host driver. The rest of the methods are called from
+respective V4L2 operations.
+
+Camera API
+----------
+
+Sensor drivers can use struct soc_camera_link, typically provided by the
+platform, and used to specify to which camera host bus the sensor is connected,
+and arbitrarily provide platform .power and .reset methods for the camera.
+soc_camera_device_register() and soc_camera_device_unregister() functions are
+used to add a sensor driver to or remove one from the system. The registration
+function takes a pointer to struct soc_camera_device as the only parameter.
+This struct can be initialized as follows:
+
+ /* link to driver operations */
+ icd->ops = &mt9m001_ops;
+ /* link to the underlying physical (e.g., i2c) device */
+ icd->control = &client->dev;
+ /* window geometry */
+ icd->x_min = 20;
+ icd->y_min = 12;
+ icd->x_current = 20;
+ icd->y_current = 12;
+ icd->width_min = 48;
+ icd->width_max = 1280;
+ icd->height_min = 32;
+ icd->height_max = 1024;
+ icd->y_skip_top = 1;
+ /* camera bus ID, typically obtained from platform data */
+ icd->iface = icl->bus_id;
+
+struct soc_camera_ops provides .probe and .remove methods, which are called by
+the soc-camera core, when a camera is matched against or removed from a camera
+host bus, .init, .release, .suspend, and .resume are called from the camera host
+driver as discussed above. Other members of this struct provide respective V4L2
+functionality.
+
+struct soc_camera_device also links to an array of struct soc_camera_data_format,
+listing pixel formats, supported by the camera.
+
+--
+Author: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
diff --git a/MAINTAINERS b/MAINTAINERS
index 68781ed..988b0a8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -72,6 +72,7 @@
L: Mailing list that is relevant to this area
W: Web-page with status/info
T: SCM tree type and location. Type is one of: git, hg, quilt.
+F: Applicable files and/or directories
S: Status, one of the following:
Supported: Someone is actually paid to look after this.
@@ -102,14 +103,14 @@
L: netdev@vger.kernel.org
S: Maintained
-3W-XXXX ATA-RAID CONTROLLER DRIVER
+3W-9XXX SATA-RAID CONTROLLER DRIVER
P: Adam Radford
M: linuxraid@amcc.com
L: linux-scsi@vger.kernel.org
W: http://www.amcc.com
S: Supported
-3W-9XXX SATA-RAID CONTROLLER DRIVER
+3W-XXXX ATA-RAID CONTROLLER DRIVER
P: Adam Radford
M: linuxraid@amcc.com
L: linux-scsi@vger.kernel.org
@@ -163,16 +164,11 @@
L: linux-m68k@lists.linux-m68k.org
S: Maintained
-AFS FILESYSTEM & AF_RXRPC SOCKET DOMAIN
-P: David Howells
-M: dhowells@redhat.com
-L: linux-afs@lists.infradead.org
-S: Supported
-
-AIO
-P: Benjamin LaHaise
-M: bcrl@kvack.org
-L: linux-aio@kvack.org
+AACRAID SCSI RAID DRIVER
+P: Adaptec OEM Raid Solutions
+M: aacraid@adaptec.com
+L: linux-scsi@vger.kernel.org
+W: http://www.adaptec.com/
S: Supported
ABIT UGURU 1,2 HARDWARE MONITOR DRIVER
@@ -193,27 +189,6 @@
L: linux-acenic@sunsite.dk
S: Maintained
-IPS SCSI RAID DRIVER
-P: Adaptec OEM Raid Solutions
-M: aacraid@adaptec.com
-L: linux-scsi@vger.kernel.org
-W: http://www.adaptec.com/
-S: Maintained
-
-DPT_I2O SCSI RAID DRIVER
-P: Adaptec OEM Raid Solutions
-M: aacraid@adaptec.com
-L: linux-scsi@vger.kernel.org
-W: http://www.adaptec.com/
-S: Maintained
-
-AACRAID SCSI RAID DRIVER
-P: Adaptec OEM Raid Solutions
-M: aacraid@adaptec.com
-L: linux-scsi@vger.kernel.org
-W: http://www.adaptec.com/
-S: Supported
-
ACER WMI LAPTOP EXTRAS
P: Carlos Corbacho
M: carlos@strangeworlds.co.uk
@@ -327,6 +302,12 @@
M: zippel@linux-m68k.org
S: Maintained
+AFS FILESYSTEM & AF_RXRPC SOCKET DOMAIN
+P: David Howells
+M: dhowells@redhat.com
+L: linux-afs@lists.infradead.org
+S: Supported
+
AGPGART DRIVER
P: David Airlie
M: airlied@linux.ie
@@ -345,6 +326,12 @@
L: linux-scsi@vger.kernel.org
S: Maintained
+AIO
+P: Benjamin LaHaise
+M: bcrl@kvack.org
+L: linux-aio@kvack.org
+S: Supported
+
ALCATEL SPEEDTOUCH USB DRIVER
P: Duncan Sands
M: duncan.sands@free.fr
@@ -390,6 +377,11 @@
T: git://git.kernel.org/pub/scm/linux/kernel/git/joro/linux-2.6-iommu.git
S: Supported
+AMD MICROCODE UPDATE SUPPORT
+P: Peter Oruba
+M: peter.oruba@amd.com
+S: Supported
+
AMS (Apple Motion Sensor) DRIVER
P: Stelian Pop
M: stelian@popies.net
@@ -732,7 +724,7 @@
W: http://sourceforge.net/projects/xscaleiop
S: Supported
-ATA OVER ETHERNET DRIVER
+ATA OVER ETHERNET (AOE) DRIVER
P: Ed L. Cashin
M: ecashin@coraid.com
W: http://www.coraid.com/support/linux
@@ -865,11 +857,48 @@
W: http://www.linux-ax25.org/
S: Maintained
+B43 WIRELESS DRIVER
+P: Michael Buesch
+M: mb@bu3sch.de
+P: Stefano Brivio
+M: stefano.brivio@polimi.it
+L: linux-wireless@vger.kernel.org
+W: http://linuxwireless.org/en/users/Drivers/b43
+S: Maintained
+
+B43LEGACY WIRELESS DRIVER
+P: Larry Finger
+M: Larry.Finger@lwfinger.net
+P: Stefano Brivio
+M: stefano.brivio@polimi.it
+L: linux-wireless@vger.kernel.org
+W: http://linuxwireless.org/en/users/Drivers/b43
+S: Maintained
+
BACKLIGHT CLASS/SUBSYSTEM
P: Richard Purdie
M: rpurdie@rpsys.net
S: Maintained
+BAYCOM/HDLCDRV DRIVERS FOR AX.25
+P: Thomas Sailer
+M: t.sailer@alumni.ethz.ch
+L: linux-hams@vger.kernel.org
+W: http://www.baycom.org/~tom/ham/ham.html
+S: Maintained
+
+BEFS FILE SYSTEM
+P: Sergey S. Kostyliov
+M: rathamahata@php4.ru
+L: linux-kernel@vger.kernel.org
+S: Maintained
+
+BFS FILE SYSTEM
+P: Tigran A. Aivazian
+M: tigran@aivazian.fsnet.co.uk
+L: linux-kernel@vger.kernel.org
+S: Maintained
+
BLACKFIN ARCHITECTURE
P: Bryan Wu
M: cooloney@kernel.org
@@ -905,43 +934,6 @@
W: http://blackfin.uclinux.org
S: Supported
-BAYCOM/HDLCDRV DRIVERS FOR AX.25
-P: Thomas Sailer
-M: t.sailer@alumni.ethz.ch
-L: linux-hams@vger.kernel.org
-W: http://www.baycom.org/~tom/ham/ham.html
-S: Maintained
-
-B43 WIRELESS DRIVER
-P: Michael Buesch
-M: mb@bu3sch.de
-P: Stefano Brivio
-M: stefano.brivio@polimi.it
-L: linux-wireless@vger.kernel.org
-W: http://linuxwireless.org/en/users/Drivers/b43
-S: Maintained
-
-B43LEGACY WIRELESS DRIVER
-P: Larry Finger
-M: Larry.Finger@lwfinger.net
-P: Stefano Brivio
-M: stefano.brivio@polimi.it
-L: linux-wireless@vger.kernel.org
-W: http://linuxwireless.org/en/users/Drivers/b43
-S: Maintained
-
-BEFS FILE SYSTEM
-P: Sergey S. Kostyliov
-M: rathamahata@php4.ru
-L: linux-kernel@vger.kernel.org
-S: Maintained
-
-BFS FILE SYSTEM
-P: Tigran A. Aivazian
-M: tigran@aivazian.fsnet.co.uk
-L: linux-kernel@vger.kernel.org
-S: Maintained
-
BLACKFIN I2C TWI DRIVER
P: Sonic Zhang
M: sonic.zhang@analog.com
@@ -1035,15 +1027,6 @@
L: video4linux-list@redhat.com
S: Maintained
-CAN NETWORK LAYER
-P: Urs Thuermann
-M: urs.thuermann@volkswagen.de
-P: Oliver Hartkopp
-M: oliver.hartkopp@volkswagen.de
-L: socketcan-core@lists.berlios.de (subscribers-only)
-W: http://developer.berlios.de/projects/socketcan/
-S: Maintained
-
CALGARY x86-64 IOMMU
P: Muli Ben-Yehuda
M: muli@il.ibm.com
@@ -1053,6 +1036,15 @@
L: discuss@x86-64.org
S: Maintained
+CAN NETWORK LAYER
+P: Urs Thuermann
+M: urs.thuermann@volkswagen.de
+P: Oliver Hartkopp
+M: oliver.hartkopp@volkswagen.de
+L: socketcan-core@lists.berlios.de (subscribers-only)
+W: http://developer.berlios.de/projects/socketcan/
+S: Maintained
+
CELL BROADBAND ENGINE ARCHITECTURE
P: Arnd Bergmann
M: arnd@arndb.de
@@ -1061,13 +1053,6 @@
W: http://www.ibm.com/developerworks/power/cell/
S: Supported
-CISCO 10G ETHERNET DRIVER
-P: Scott Feldman
-M: scofeldm@cisco.com
-P: Joe Eykholt
-M: jeykholt@cisco.com
-S: Supported
-
CFAG12864B LCD DRIVER
P: Miguel Ojeda Sandonis
M: miguel.ojeda.sandonis@gmail.com
@@ -1099,19 +1084,11 @@
M: jschopp@austin.ibm.com
S: Supported
-COMMON INTERNET FILE SYSTEM (CIFS)
-P: Steve French
-M: sfrench@samba.org
-L: linux-cifs-client@lists.samba.org
-L: samba-technical@lists.samba.org
-W: http://linux-cifs.samba.org/
-T: git kernel.org:/pub/scm/linux/kernel/git/sfrench/cifs-2.6.git
-S: Supported
-
-CONFIGFS
-P: Joel Becker
-M: joel.becker@oracle.com
-L: linux-kernel@vger.kernel.org
+CISCO 10G ETHERNET DRIVER
+P: Scott Feldman
+M: scofeldm@cisco.com
+P: Joe Eykholt
+M: jeykholt@cisco.com
S: Supported
CIRRUS LOGIC EP93XX ETHERNET DRIVER
@@ -1147,6 +1124,15 @@
W: http://www.coda.cs.cmu.edu/
S: Maintained
+COMMON INTERNET FILE SYSTEM (CIFS)
+P: Steve French
+M: sfrench@samba.org
+L: linux-cifs-client@lists.samba.org
+L: samba-technical@lists.samba.org
+W: http://linux-cifs.samba.org/
+T: git kernel.org:/pub/scm/linux/kernel/git/sfrench/cifs-2.6.git
+S: Supported
+
COMPACTPCI HOTPLUG CORE
P: Scott Murray
M: scottm@somanetworks.com
@@ -1186,6 +1172,12 @@
W: http://accessrunner.sourceforge.net/
S: Maintained
+CONFIGFS
+P: Joel Becker
+M: joel.becker@oracle.com
+L: linux-kernel@vger.kernel.org
+S: Supported
+
CONTROL GROUPS (CGROUPS)
P: Paul Menage
M: menage@google.com
@@ -1309,6 +1301,20 @@
L: linux-hams@vger.kernel.org
S: Maintained
+DAVICOM FAST ETHERNET (DMFE) NETWORK DRIVER
+P: Tobias Ringstrom
+M: tori@unhappy.mine.nu
+L: netdev@vger.kernel.org
+S: Maintained
+
+DC390/AM53C974 SCSI driver
+P: Kurt Garloff
+M: garloff@suse.de
+W: http://www.garloff.de/kurt/linux/dc390/
+P: Guennadi Liakhovetski
+M: g.liakhovetski@gmx.de
+S: Maintained
+
DC395x SCSI driver
P: Oliver Neukum
M: oliver@neukum.name
@@ -1321,14 +1327,6 @@
L: http://lists.twibble.org/mailman/listinfo/dc395x/
S: Maintained
-DC390/AM53C974 SCSI driver
-P: Kurt Garloff
-M: garloff@suse.de
-W: http://www.garloff.de/kurt/linux/dc390/
-P: Guennadi Liakhovetski
-M: g.liakhovetski@gmx.de
-S: Maintained
-
DCCP PROTOCOL
P: Arnaldo Carvalho de Melo
M: acme@ghostprotocols.net
@@ -1359,12 +1357,6 @@
M: Douglas_Warzecha@dell.com
S: Maintained
-DEVICE-MAPPER (LVM)
-P: Alasdair Kergon
-L: dm-devel@redhat.com
-W: http://sources.redhat.com/dm
-S: Maintained
-
DEVICE NUMBER REGISTRY
P: Torben Mathiasen
M: device@lanana.org
@@ -1372,6 +1364,12 @@
L: linux-kernel@vger.kernel.org
S: Maintained
+DEVICE-MAPPER (LVM)
+P: Alasdair Kergon
+L: dm-devel@redhat.com
+W: http://sources.redhat.com/dm
+S: Maintained
+
DIGI INTL. EPCA DRIVER
P: Digi International, Inc
M: Eng.Linux@digi.com
@@ -1379,7 +1377,7 @@
W: http://www.digi.com
S: Orphan
-DIRECTORY NOTIFICATION
+DIRECTORY NOTIFICATION (DNOTIFY)
P: Stephen Rothwell
M: sfr@canb.auug.org.au
L: linux-kernel@vger.kernel.org
@@ -1393,13 +1391,13 @@
W: http://www.win.tue.nl/~aeb/partitions/partition_types-1.html
S: Maintained
-DISKQUOTA:
+DISKQUOTA
P: Jan Kara
M: jack@suse.cz
L: linux-kernel@vger.kernel.org
S: Maintained
-DISTRIBUTED LOCK MANAGER
+DISTRIBUTED LOCK MANAGER (DLM)
P: Christine Caulfield
M: ccaulfie@redhat.com
P: David Teigland
@@ -1409,12 +1407,6 @@
T: git kernel.org:/pub/scm/linux/kernel/git/teigland/dlm.git
S: Supported
-DAVICOM FAST ETHERNET (DMFE) NETWORK DRIVER
-P: Tobias Ringstrom
-M: tori@unhappy.mine.nu
-L: netdev@vger.kernel.org
-S: Maintained
-
DMA GENERIC OFFLOAD ENGINE SUBSYSTEM
P: Maciej Sosnowski
M: maciej.sosnowski@intel.com
@@ -1454,6 +1446,13 @@
L: blinux-list@redhat.com
S: Maintained
+DPT_I2O SCSI RAID DRIVER
+P: Adaptec OEM Raid Solutions
+M: aacraid@adaptec.com
+L: linux-scsi@vger.kernel.org
+W: http://www.adaptec.com/
+S: Maintained
+
DRIVER CORE, KOBJECTS, AND SYSFS
P: Greg Kroah-Hartman
M: gregkh@suse.de
@@ -1602,7 +1601,7 @@
W: http://aeschi.ch.eu.org/efs/
S: Orphan
-EHCA (IBM GX bus InfiniBand adapter) DRIVER:
+EHCA (IBM GX bus InfiniBand adapter) DRIVER
P: Hoang-Nam Nguyen
M: hnguyen@de.ibm.com
P: Christoph Raisch
@@ -1659,9 +1658,10 @@
S: Maintained
EXT4 FILE SYSTEM
-P: Stephen Tweedie, Andrew Morton
-M: sct@redhat.com, akpm@linux-foundation.org, adilger@sun.com
+P: Theodore Ts'o
+M: tytso@mit.edu, adilger@sun.com
L: linux-ext4@vger.kernel.org
+W: http://ext4.wiki.kernel.org
S: Maintained
F71805F HARDWARE MONITORING DRIVER
@@ -1681,6 +1681,42 @@
M: akinobu.mita@gmail.com
S: Supported
+FILE LOCKING (flock() and fcntl()/lockf())
+P: Matthew Wilcox
+M: matthew@wil.cx
+L: linux-fsdevel@vger.kernel.org
+S: Maintained
+
+FILESYSTEMS (VFS and infrastructure)
+P: Alexander Viro
+M: viro@zeniv.linux.org.uk
+L: linux-fsdevel@vger.kernel.org
+S: Maintained
+
+FIREWIRE SUBSYSTEM (drivers/firewire, <linux/firewire*.h>)
+P: Kristian Hoegsberg, Stefan Richter
+M: krh@redhat.com, stefanr@s5r6.in-berlin.de
+L: linux1394-devel@lists.sourceforge.net
+W: http://www.linux1394.org/
+T: git kernel.org:/pub/scm/linux/kernel/git/ieee1394/linux1394-2.6.git
+S: Maintained
+
+FIRMWARE LOADER (request_firmware)
+L: linux-kernel@vger.kernel.org
+S: Orphan
+
+FPU EMULATOR
+P: Bill Metzenthen
+M: billm@suburbia.net
+W: http://suburbia.net/~billm/floating-point/emulator/
+S: Maintained
+
+FRAME RELAY DLCI/FRAD (Sangoma drivers too)
+P: Mike McLagan
+M: mike.mclagan@linux.org
+L: netdev@vger.kernel.org
+S: Maintained
+
FRAMEBUFFER LAYER
P: Antonino Daplas
M: adaplas@gmail.com
@@ -1746,42 +1782,6 @@
L: linuxppc-dev@ozlabs.org
S: Supported
-FILE LOCKING (flock() and fcntl()/lockf())
-P: Matthew Wilcox
-M: matthew@wil.cx
-L: linux-fsdevel@vger.kernel.org
-S: Maintained
-
-FILESYSTEMS (VFS and infrastructure)
-P: Alexander Viro
-M: viro@zeniv.linux.org.uk
-L: linux-fsdevel@vger.kernel.org
-S: Maintained
-
-FIREWIRE SUBSYSTEM (drivers/firewire, <linux/firewire*.h>)
-P: Kristian Hoegsberg, Stefan Richter
-M: krh@redhat.com, stefanr@s5r6.in-berlin.de
-L: linux1394-devel@lists.sourceforge.net
-W: http://www.linux1394.org/
-T: git kernel.org:/pub/scm/linux/kernel/git/ieee1394/linux1394-2.6.git
-S: Maintained
-
-FIRMWARE LOADER (request_firmware)
-L: linux-kernel@vger.kernel.org
-S: Orphan
-
-FPU EMULATOR
-P: Bill Metzenthen
-M: billm@suburbia.net
-W: http://suburbia.net/~billm/floating-point/emulator/
-S: Maintained
-
-FRAME RELAY DLCI/FRAD (Sangoma drivers too)
-P: Mike McLagan
-M: mike.mclagan@linux.org
-L: netdev@vger.kernel.org
-S: Maintained
-
FREEVXFS FILESYSTEM
P: Christoph Hellwig
M: hch@infradead.org
@@ -1853,6 +1853,13 @@
W: http://gigaset307x.sourceforge.net/
S: Maintained
+HARD DRIVE ACTIVE PROTECTION SYSTEM (HDAPS) DRIVER
+P: Robert Love
+M: rlove@rlove.org
+M: linux-kernel@vger.kernel.org
+W: http://www.kernel.org/pub/linux/kernel/people/rml/hdaps/
+S: Maintained
+
HARDWARE MONITORING
L: lm-sensors@lm-sensors.org
W: http://www.lm-sensors.org/
@@ -1861,13 +1868,6 @@
HARDWARE RANDOM NUMBER GENERATOR CORE
S: Orphan
-HARD DRIVE ACTIVE PROTECTION SYSTEM (HDAPS) DRIVER
-P: Robert Love
-M: rlove@rlove.org
-M: linux-kernel@vger.kernel.org
-W: http://www.kernel.org/pub/linux/kernel/people/rml/hdaps/
-S: Maintained
-
HARMONY SOUND DRIVER
P: Kyle McMartin
M: kyle@mcmartin.ca
@@ -1881,6 +1881,24 @@
W: http://www.nyx.net/~arobinso
S: Maintained
+HEWLETT-PACKARD FIBRE CHANNEL 64-bit/66MHz PCI non-intelligent HBA
+P: Chirag Kantharia
+M: chirag.kantharia@hp.com
+L: iss_storagedev@hp.com
+S: Maintained
+
+HEWLETT-PACKARD SMART2 RAID DRIVER
+P: Chirag Kantharia
+M: chirag.kantharia@hp.com
+L: iss_storagedev@hp.com
+S: Maintained
+
+HEWLETT-PACKARD SMART CISS RAID DRIVER (cciss)
+P: Mike Miller
+M: mike.miller@hp.com
+L: iss_storagedev@hp.com
+S: Supported
+
HFS FILESYSTEM
P: Roman Zippel
M: zippel@linux-m68k.org
@@ -1894,6 +1912,14 @@
W: http://drama.obuda.kando.hu/~fero/cgi-bin/hgafb.shtml
S: Maintained
+HIBERNATION (aka Software Suspend, aka swsusp)
+P: Pavel Machek
+M: pavel@suse.cz
+P: Rafael J. Wysocki
+M: rjw@sisk.pl
+L: linux-pm@lists.linux-foundation.org
+S: Supported
+
HID CORE LAYER
P: Jiri Kosina
M: jkosina@suse.cz
@@ -1926,24 +1952,6 @@
L: linux-hippi@sunsite.dk
S: Maintained
-HEWLETT-PACKARD FIBRE CHANNEL 64-bit/66MHz PCI non-intelligent HBA
-P: Chirag Kantharia
-M: chirag.kantharia@hp.com
-L: iss_storagedev@hp.com
-S: Maintained
-
-HEWLETT-PACKARD SMART2 RAID DRIVER
-P: Chirag Kantharia
-M: chirag.kantharia@hp.com
-L: iss_storagedev@hp.com
-S: Maintained
-
-HEWLETT-PACKARD SMART CISS RAID DRIVER (cciss)
-P: Mike Miller
-M: mike.miller@hp.com
-L: iss_storagedev@hp.com
-S: Supported
-
HOST AP DRIVER
P: Jouni Malinen
M: j@w1.fi
@@ -1952,16 +1960,16 @@
W: http://hostap.epitest.fi/
S: Maintained
-HP100: Driver for HP 10/100 Mbit/s Voice Grade Network Adapter Series
-P: Jaroslav Kysela
-M: perex@perex.cz
-S: Maintained
-
HP COMPAQ TC1100 TABLET WMI EXTRAS DRIVER
P: Carlos Corbacho
M: carlos@strangeworlds.co.uk
S: Odd Fixes
+HP100: Driver for HP 10/100 Mbit/s Voice Grade Network Adapter Series
+P: Jaroslav Kysela
+M: perex@perex.cz
+S: Maintained
+
HPET: High Precision Event Timers driver (drivers/char/hpet.c)
P: Clemens Ladisch
M: clemens@ladisch.de
@@ -2041,14 +2049,6 @@
T: git kernel.org:/pub/scm/linux/kernel/git/aegl/linux-2.6.git
S: Maintained
-SN-IA64 (Itanium) SUB-PLATFORM
-P: Jes Sorensen
-M: jes@sgi.com
-L: linux-altix@sgi.com
-L: linux-ia64@vger.kernel.org
-W: http://www.sgi.com/altix
-S: Maintained
-
IBM MCA SCSI SUBSYSTEM DRIVER
P: Michael Lang
M: langa2@kph.uni-mainz.de
@@ -2132,14 +2132,6 @@
T: git kernel.org:/pub/scm/linux/kernel/git/roland/infiniband.git
S: Supported
-INPUT (KEYBOARD, MOUSE, JOYSTICK, TOUCHSCREEN) DRIVERS
-P: Dmitry Torokhov
-M: dmitry.torokhov@gmail.com
-M: dtor@mail.ru
-L: linux-input@vger.kernel.org
-T: git kernel.org:/pub/scm/linux/kernel/git/dtor/input.git
-S: Maintained
-
INOTIFY
P: John McCutchan
M: ttb@tentacle.dhs.org
@@ -2148,6 +2140,14 @@
L: linux-kernel@vger.kernel.org
S: Maintained
+INPUT (KEYBOARD, MOUSE, JOYSTICK, TOUCHSCREEN) DRIVERS
+P: Dmitry Torokhov
+M: dmitry.torokhov@gmail.com
+M: dtor@mail.ru
+L: linux-input@vger.kernel.org
+T: git kernel.org:/pub/scm/linux/kernel/git/dtor/input.git
+S: Maintained
+
INTEL FRAMEBUFFER DRIVER (excluding 810 and 815)
P: Sylvain Meyer
M: sylvain.meyer@worldonline.fr
@@ -2252,7 +2252,7 @@
L: linux-mips@linux-mips.org
S: Maintained
-IP MASQUERADING:
+IP MASQUERADING
P: Juanjo Ciarlante
M: jjciarla@raiz.uncu.edu.ar
S: Maintained
@@ -2267,7 +2267,7 @@
L: netdev@vger.kernel.org
S: Maintained
-IPATH DRIVER:
+IPATH DRIVER
P: Ralph Campbell
M: infinipath@qlogic.com
L: general@lists.openfabrics.org
@@ -2281,13 +2281,25 @@
W: http://openipmi.sourceforge.net/
S: Supported
-IPX NETWORK LAYER
-P: Arnaldo Carvalho de Melo
-M: acme@ghostprotocols.net
-L: netdev@vger.kernel.org
+IPS SCSI RAID DRIVER
+P: Adaptec OEM Raid Solutions
+M: aacraid@adaptec.com
+L: linux-scsi@vger.kernel.org
+W: http://www.adaptec.com/
S: Maintained
-IPWIRELES DRIVER
+IPVS
+P: Wensong Zhang
+M: wensong@linux-vs.org
+P: Simon Horman
+M: horms@verge.net.au
+P: Julian Anastasov
+M: ja@ssi.bg
+L: netdev@vger.kernel.org
+L: lvs-devel@vger.kernel.org
+S: Maintained
+
+IPWIRELESS DRIVER
P: Jiri Kosina
M: jkosina@suse.cz
P: David Sterba
@@ -2295,6 +2307,12 @@
S: Maintained
T: git://git.kernel.org/pub/scm/linux/kernel/git/jikos/ipwireless_cs.git
+IPX NETWORK LAYER
+P: Arnaldo Carvalho de Melo
+M: acme@ghostprotocols.net
+L: netdev@vger.kernel.org
+S: Maintained
+
IRDA SUBSYSTEM
P: Samuel Ortiz
M: samuel@sortiz.org
@@ -2302,6 +2320,11 @@
W: http://irda.sourceforge.net/
S: Maintained
+ISAPNP
+P: Jaroslav Kysela
+M: perex@perex.cz
+S: Maintained
+
ISCSI
P: Mike Christie
M: michaelc@cs.wisc.edu
@@ -2310,11 +2333,6 @@
T: git kernel.org:/pub/scm/linux/kernel/mnc/linux-2.6-iscsi.git
S: Maintained
-ISAPNP
-P: Jaroslav Kysela
-M: perex@perex.cz
-S: Maintained
-
ISDN SUBSYSTEM
P: Karsten Keil
M: kkeil@suse.de
@@ -2339,6 +2357,14 @@
W: http://www.ivtvdriver.org
S: Maintained
+JFS FILESYSTEM
+P: Dave Kleikamp
+M: shaggy@austin.ibm.com
+L: jfs-discussion@lists.sourceforge.net
+W: http://jfs.sourceforge.net/
+T: git kernel.org:/pub/scm/linux/kernel/git/shaggy/jfs-2.6.git
+S: Supported
+
JME NETWORK DRIVER
P: Guo-Fu Tseng
M: cooldavid@cooldavid.org
@@ -2352,24 +2378,6 @@
W: http://www.linux-mtd.infradead.org/doc/jffs2.html
S: Maintained
-UBI FILE SYSTEM (UBIFS)
-P: Artem Bityutskiy
-M: dedekind@infradead.org
-P: Adrian Hunter
-M: ext-adrian.hunter@nokia.com
-L: linux-mtd@lists.infradead.org
-T: git git://git.infradead.org/~dedekind/ubifs-2.6.git
-W: http://www.linux-mtd.infradead.org/doc/ubifs.html
-S: Maintained
-
-JFS FILESYSTEM
-P: Dave Kleikamp
-M: shaggy@austin.ibm.com
-L: jfs-discussion@lists.sourceforge.net
-W: http://jfs.sourceforge.net/
-T: git kernel.org:/pub/scm/linux/kernel/git/shaggy/jfs-2.6.git
-S: Supported
-
JOURNALLING LAYER FOR BLOCK DEVICES (JBD)
P: Stephen Tweedie, Andrew Morton
M: sct@redhat.com, akpm@linux-foundation.org
@@ -2606,11 +2614,6 @@
L: linuxppc-dev@ozlabs.org
S: Supported
-LLC (802.2)
-P: Arnaldo Carvalho de Melo
-M: acme@ghostprotocols.net
-S: Maintained
-
LINUX SECURITY MODULE (LSM) FRAMEWORK
P: Chris Wright
M: chrisw@sous-sol.org
@@ -2618,6 +2621,11 @@
T: git kernel.org:/pub/scm/linux/kernel/git/chrisw/lsm-2.6.git
S: Supported
+LLC (802.2)
+P: Arnaldo Carvalho de Melo
+M: acme@ghostprotocols.net
+S: Maintained
+
LM83 HARDWARE MONITOR DRIVER
P: Jean Delvare
M: khali@linux-fr.org
@@ -2718,14 +2726,6 @@
L: netdev@vger.kernel.org
S: Maintained
-MARVELL YUKON / SYSKONNECT DRIVER
-P: Mirko Lindner
-M: mlindner@syskonnect.de
-P: Ralph Roesler
-M: rroesler@syskonnect.de
-W: http://www.syskonnect.com
-S: Supported
-
MAN-PAGES: MANUAL PAGES FOR LINUX -- Sections 2, 3, 4, 5, and 7
P: Michael Kerrisk
M: mtk.manpages@gmail.com
@@ -2745,6 +2745,14 @@
L: netdev@vger.kernel.org
S: Supported
+MARVELL YUKON / SYSKONNECT DRIVER
+P: Mirko Lindner
+M: mlindner@syskonnect.de
+P: Ralph Roesler
+M: rroesler@syskonnect.de
+W: http://www.syskonnect.com
+S: Supported
+
MATROX FRAMEBUFFER DRIVER
P: Petr Vandrovec
M: vandrove@vc.cvut.cz
@@ -2781,15 +2789,6 @@
L: linux-kernel@vger.kernel.org
S: Maintained
-MEI MN10300/AM33 PORT
-P: David Howells
-M: dhowells@redhat.com
-P: Koichi Yasutake
-M: yasutake.koichi@jp.panasonic.com
-L: linux-am33-list@redhat.com (moderated for non-subscribers)
-W: ftp://ftp.redhat.com/pub/redhat/gnupro/AM33/
-S: Maintained
-
MEMORY TECHNOLOGY DEVICES (MTD)
P: David Woodhouse
M: dwmw2@infradead.org
@@ -2798,14 +2797,6 @@
T: git git://git.infradead.org/mtd-2.6.git
S: Maintained
-UNSORTED BLOCK IMAGES (UBI)
-P: Artem Bityutskiy
-M: dedekind@infradead.org
-W: http://www.linux-mtd.infradead.org/
-L: linux-mtd@lists.infradead.org
-T: git git://git.infradead.org/~dedekind/ubi-2.6.git
-S: Maintained
-
MICROTEK X6 SCANNER
P: Oliver Neukum
M: oliver@neukum.name
@@ -3037,17 +3028,6 @@
W: http://www.netxen.com
S: Supported
-IPVS
-P: Wensong Zhang
-M: wensong@linux-vs.org
-P: Simon Horman
-M: horms@verge.net.au
-P: Julian Anastasov
-M: ja@ssi.bg
-L: netdev@vger.kernel.org
-L: lvs-devel@vger.kernel.org
-S: Maintained
-
NFS, SUNRPC, AND LOCKD CLIENTS
P: Trond Myklebust
M: Trond.Myklebust@netapp.com
@@ -3093,22 +3073,6 @@
L: linux-fbdev-devel@lists.sourceforge.net (moderated for non-subscribers)
S: Maintained
-OPENCORES I2C BUS DRIVER
-P: Peter Korsgaard
-M: jacmet@sunsite.dk
-L: i2c@lm-sensors.org
-S: Maintained
-
-ORACLE CLUSTER FILESYSTEM 2 (OCFS2)
-P: Mark Fasheh
-M: mfasheh@suse.com
-P: Joel Becker
-M: joel.becker@oracle.com
-L: ocfs2-devel@oss.oracle.com (moderated for non-subscribers)
-W: http://oss.oracle.com/projects/ocfs2/
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/mfasheh/ocfs2.git
-S: Supported
-
OMFS FILESYSTEM
P: Bob Copeland
M: me@bobcopeland.com
@@ -3144,12 +3108,28 @@
L: linux-scsi@vger.kernel.org
S: Maintained
+OPENCORES I2C BUS DRIVER
+P: Peter Korsgaard
+M: jacmet@sunsite.dk
+L: i2c@lm-sensors.org
+S: Maintained
+
OPROFILE
P: Robert Richter
M: robert.richter@amd.com
L: oprofile-list@lists.sf.net
S: Maintained
+ORACLE CLUSTER FILESYSTEM 2 (OCFS2)
+P: Mark Fasheh
+M: mfasheh@suse.com
+P: Joel Becker
+M: joel.becker@oracle.com
+L: ocfs2-devel@oss.oracle.com (moderated for non-subscribers)
+W: http://oss.oracle.com/projects/ocfs2/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mfasheh/ocfs2.git
+S: Supported
+
ORINOCO DRIVER
P: Pavel Roskin
M: proski@gnu.org
@@ -3161,6 +3141,14 @@
W: http://www.nongnu.org/orinoco/
S: Maintained
+P54 WIRELESS DRIVER
+P: Michael Wu
+M: flamingice@sourmilk.net
+L: linux-wireless@vger.kernel.org
+W: http://prism54.org
+T: git kernel.org:/pub/scm/linux/kernel/git/mwu/mac80211-drivers.git
+S: Maintained
+
PA SEMI ETHERNET DRIVER
P: Olof Johansson
M: olof@lixom.net
@@ -3173,10 +3161,32 @@
L: i2c@lm-sensors.org
S: Maintained
+PANASONIC MN10300/AM33 PORT
+P: David Howells
+M: dhowells@redhat.com
+P: Koichi Yasutake
+M: yasutake.koichi@jp.panasonic.com
+L: linux-am33-list@redhat.com (moderated for non-subscribers)
+W: ftp://ftp.redhat.com/pub/redhat/gnupro/AM33/
+S: Maintained
+
PARALLEL PORT SUPPORT
L: linux-parport@lists.infradead.org (subscribers-only)
S: Orphan
+PARAVIRT_OPS INTERFACE
+P: Jeremy Fitzhardinge
+M: jeremy@xensource.com
+P: Chris Wright
+M: chrisw@sous-sol.org
+P: Zachary Amsden
+M: zach@vmware.com
+P: Rusty Russell
+M: rusty@rustcorp.com.au
+L: virtualization@lists.osdl.org
+L: linux-kernel@vger.kernel.org
+S: Supported
+
PARIDE DRIVERS FOR PARALLEL PORT IDE DEVICES
P: Tim Waugh
M: tim@cyberelk.net
@@ -3196,19 +3206,6 @@
T: git kernel.org:/pub/scm/linux/kernel/git/kyle/parisc-2.6.git
S: Maintained
-PARAVIRT_OPS INTERFACE
-P: Jeremy Fitzhardinge
-M: jeremy@xensource.com
-P: Chris Wright
-M: chrisw@sous-sol.org
-P: Zachary Amsden
-M: zach@vmware.com
-P: Rusty Russell
-M: rusty@rustcorp.com.au
-L: virtualization@lists.osdl.org
-L: linux-kernel@vger.kernel.org
-S: Supported
-
PC87360 HARDWARE MONITORING DRIVER
P: Jim Cromie
M: jim.cromie@gmail.com
@@ -3338,14 +3335,6 @@
W: ftp://ftp.kernel.org/pub/linux/kernel/people/rml/preempt-kernel
S: Supported
-P54 WIRELESS DRIVER
-P: Michael Wu
-M: flamingice@sourmilk.net
-L: linux-wireless@vger.kernel.org
-W: http://prism54.org
-T: git kernel.org:/pub/scm/linux/kernel/git/mwu/mac80211-drivers.git
-S: Maintained
-
PRISM54 WIRELESS DRIVER
P: Luis R. Rodriguez
M: mcgrof@gmail.com
@@ -3436,13 +3425,7 @@
L: linux-fbdev-devel@lists.sourceforge.net (moderated for non-subscribers)
S: Maintained
-RAYLINK/WEBGEAR 802.11 WIRELESS LAN DRIVER
-P: Corey Thomas
-M: coreythomas@charter.net
-L: linux-wireless@vger.kernel.org
-S: Maintained
-
-RALINK RT2X00 WLAN DRIVER
+RALINK RT2X00 WIRELESS LAN DRIVER
P: rt2x00 project
L: linux-wireless@vger.kernel.org
L: rt2400-devel@lists.sourceforge.net
@@ -3467,6 +3450,18 @@
L: linux-kernel@vger.kernel.org
S: Maintained
+RAYLINK/WEBGEAR 802.11 WIRELESS LAN DRIVER
+P: Corey Thomas
+M: coreythomas@charter.net
+L: linux-wireless@vger.kernel.org
+S: Maintained
+
+RCUTORTURE MODULE
+P: Josh Triplett
+M: josh@freedesktop.org
+L: linux-kernel@vger.kernel.org
+S: Maintained
+
RDC R-321X SoC
P: Florian Fainelli
M: florian.fainelli@telecomint.eu
@@ -3486,12 +3481,6 @@
L: linux-kernel@vger.kernel.org
S: Supported
-RCUTORTURE MODULE
-P: Josh Triplett
-M: josh@freedesktop.org
-L: linux-kernel@vger.kernel.org
-S: Maintained
-
REAL TIME CLOCK DRIVER
P: Paul Gortmaker
M: p_gortmaker@yahoo.com
@@ -3515,6 +3504,9 @@
S: Maintained
F: net/rfkill
+RISCOM8 DRIVER
+S: Orphan
+
ROCKETPORT DRIVER
P: Comtrol Corp.
W: http://www.comtrol.com
@@ -3527,9 +3519,6 @@
W: http://www.linux-ax25.org/
S: Maintained
-RISCOM8 DRIVER
-S: Orphan
-
RTL818X WIRELESS DRIVER
P: Michael Wu
M: flamingice@sourmilk.net
@@ -3669,6 +3658,12 @@
L: sdricohcs-devel@lists.sourceforge.net (subscribers-only)
S: Maintained
+SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) DRIVER
+P: Pierre Ossman
+M: drzeus-sdhci@drzeus.cx
+L: sdhci-devel@list.drzeus.cx
+S: Maintained
+
SECURITY CONTACT
P: Security Officers
M: security@kernel.org
@@ -3692,19 +3687,13 @@
M: jirislaby@gmail.com
S: Maintained
-SERIAL ATA (SATA) SUBSYSTEM:
+SERIAL ATA (SATA) SUBSYSTEM
P: Jeff Garzik
M: jgarzik@pobox.com
L: linux-ide@vger.kernel.org
T: git kernel.org:/pub/scm/linux/kernel/git/jgarzik/libata-dev.git
S: Supported
-SGI SN-IA64 (Altix) SERIAL CONSOLE DRIVER
-P: Pat Gefre
-M: pfg@sgi.com
-L: linux-ia64@vger.kernel.org
-S: Supported
-
SFC NETWORK DRIVER
P: Steve Hodgson
P: Ben Hutchings
@@ -3712,6 +3701,17 @@
M: linux-net-drivers@solarflare.com
S: Supported
+SGI GRU DRIVER
+P: Jack Steiner
+M: steiner@sgi.com
+S: Maintained
+
+SGI SN-IA64 (Altix) SERIAL CONSOLE DRIVER
+P: Pat Gefre
+M: pfg@sgi.com
+L: linux-ia64@vger.kernel.org
+S: Supported
+
SGI VISUAL WORKSTATION 320 AND 540
P: Andrey Panin
M: pazke@donpac.ru
@@ -3719,16 +3719,24 @@
W: http://linux-visws.sf.net
S: Maintained for 2.6.
-SGI GRU DRIVER
-P: Jack Steiner
-M: steiner@sgi.com
-S: Maintained
-
SGI XP/XPC/XPNET DRIVER
P: Dean Nelson
M: dcn@sgi.com
S: Maintained
+SHARP LH SUPPORT (LH7952X & LH7A40X)
+P: Marc Singer
+M: elf@buici.com
+W: http://projects.buici.com/arm
+L: linux-arm-kernel@lists.arm.linux.org.uk (subscribers-only)
+S: Maintained
+
+SHPC HOTPLUG DRIVER
+P: Kristen Carlson Accardi
+M: kristen.c.accardi@intel.com
+L: linux-pci@vger.kernel.org
+S: Supported
+
SIMTEC EB110ATX (Chalice CATS)
P: Ben Dooks
P: Vincent Sanders
@@ -3774,6 +3782,12 @@
W: http://www.winischhofer.at/linuxsisusbvga.shtml
S: Maintained
+SKGE, SKY2 10/100/1000 GIGABIT ETHERNET DRIVERS
+P: Stephen Hemminger
+M: shemminger@linux-foundation.org
+L: netdev@vger.kernel.org
+S: Maintained
+
SLAB ALLOCATOR
P: Christoph Lameter
M: cl@linux-foundation.org
@@ -3800,12 +3814,25 @@
M: bn@niasdigital.com
S: Maintained
+SN-IA64 (Itanium) SUB-PLATFORM
+P: Jes Sorensen
+M: jes@sgi.com
+L: linux-altix@sgi.com
+L: linux-ia64@vger.kernel.org
+W: http://www.sgi.com/altix
+S: Maintained
+
SOC-CAMERA V4L2 SUBSYSTEM
P: Guennadi Liakhovetski
M: g.liakhovetski@gmx.de
L: video4linux-list@redhat.com
S: Maintained
+SOEKRIS NET48XX LED SUPPORT
+P: Chris Boot
+M: bootc@bootc.net
+S: Maintained
+
SOFTWARE RAID (Multiple Disks) SUPPORT
P: Ingo Molnar
M: mingo@redhat.com
@@ -3814,24 +3841,6 @@
L: linux-raid@vger.kernel.org
S: Supported
-HIBERNATION (aka Software Suspend, aka swsusp):
-P: Pavel Machek
-M: pavel@suse.cz
-P: Rafael J. Wysocki
-M: rjw@sisk.pl
-L: linux-pm@lists.linux-foundation.org
-S: Supported
-
-SUSPEND TO RAM:
-P: Len Brown
-M: len.brown@intel.com
-P: Pavel Machek
-M: pavel@suse.cz
-P: Rafael J. Wysocki
-M: rjw@sisk.pl
-L: linux-pm@lists.linux-foundation.org
-S: Supported
-
SONIC NETWORK DRIVER
P: Thomas Bogendoerfer
M: tsbogend@alpha.franken.de
@@ -3876,59 +3885,7 @@
W: http://alsa-project.org/main/index.php/ASoC
S: Supported
-SPI SUBSYSTEM
-P: David Brownell
-M: dbrownell@users.sourceforge.net
-L: spi-devel-general@lists.sourceforge.net
-S: Maintained
-
-SPU FILE SYSTEM
-P: Jeremy Kerr
-M: jk@ozlabs.org
-L: linuxppc-dev@ozlabs.org
-L: cbe-oss-dev@ozlabs.org
-W: http://www.ibm.com/developerworks/power/cell/
-S: Supported
-
-STABLE BRANCH:
-P: Greg Kroah-Hartman
-M: greg@kroah.com
-P: Chris Wright
-M: chrisw@sous-sol.org
-L: stable@kernel.org
-S: Maintained
-
-SHARP LH SUPPORT (LH7952X & LH7A40X)
-P: Marc Singer
-M: elf@buici.com
-W: http://projects.buici.com/arm
-L: linux-arm-kernel@lists.arm.linux.org.uk (subscribers-only)
-S: Maintained
-
-SHPC HOTPLUG DRIVER
-P: Kristen Carlson Accardi
-M: kristen.c.accardi@intel.com
-L: linux-pci@vger.kernel.org
-S: Supported
-
-SECURE DIGITAL HOST CONTROLLER INTERFACE DRIVER
-P: Pierre Ossman
-M: drzeus-sdhci@drzeus.cx
-L: sdhci-devel@list.drzeus.cx
-S: Maintained
-
-SKGE, SKY2 10/100/1000 GIGABIT ETHERNET DRIVERS
-P: Stephen Hemminger
-M: shemminger@linux-foundation.org
-L: netdev@vger.kernel.org
-S: Maintained
-
-SOEKRIS NET48XX LED SUPPORT
-P: Chris Boot
-M: bootc@bootc.net
-S: Maintained
-
-SPARC (sparc32):
+SPARC (sparc32)
P: William L. Irwin
M: wli@holomorphy.com
L: sparclinux@vger.kernel.org
@@ -3940,6 +3897,12 @@
L: linux-kernel@vger.kernel.org ?
S: Supported
+SPI SUBSYSTEM
+P: David Brownell
+M: dbrownell@users.sourceforge.net
+L: spi-devel-general@lists.sourceforge.net
+S: Maintained
+
SPIDERNET NETWORK DRIVER for CELL
P: Ishizaki Kou
M: kou.ishizaki@toshiba.co.jp
@@ -3948,12 +3911,28 @@
L: netdev@vger.kernel.org
S: Supported
+SPU FILE SYSTEM
+P: Jeremy Kerr
+M: jk@ozlabs.org
+L: linuxppc-dev@ozlabs.org
+L: cbe-oss-dev@ozlabs.org
+W: http://www.ibm.com/developerworks/power/cell/
+S: Supported
+
SRM (Alpha) environment access
P: Jan-Benedict Glaw
M: jbglaw@lug-owl.de
L: linux-kernel@vger.kernel.org
S: Maintained
+STABLE BRANCH:
+P: Greg Kroah-Hartman
+M: greg@kroah.com
+P: Chris Wright
+M: chrisw@sous-sol.org
+L: stable@kernel.org
+S: Maintained
+
STARFIRE/DURALAN NETWORK DRIVER
P: Ion Badulescu
M: ionut@cs.columbia.edu
@@ -3969,6 +3948,12 @@
W: http://www.stradis.com/
S: Maintained
+SUN3/3X
+P: Sam Creasey
+M: sammy@sammy.net
+W: http://sammy.net/sun3/
+S: Maintained
+
SUPERH
P: Paul Mundt
M: lethal@linux-sh.org
@@ -3977,11 +3962,15 @@
T: git kernel.org:/pub/scm/linux/kernel/git/lethal/sh-2.6.git
S: Supported
-SUN3/3X
-P: Sam Creasey
-M: sammy@sammy.net
-W: http://sammy.net/sun3/
-S: Maintained
+SUSPEND TO RAM
+P: Len Brown
+M: len.brown@intel.com
+P: Pavel Machek
+M: pavel@suse.cz
+P: Rafael J. Wysocki
+M: rjw@sisk.pl
+L: linux-pm@lists.linux-foundation.org
+S: Supported
SVGA HANDLING
P: Martin Mares
@@ -4027,7 +4016,7 @@
M: mark.gross@intel.com
S: Supported
-TENSILICA XTENSA PORT (xtensa):
+TENSILICA XTENSA PORT (xtensa)
P: Chris Zankel
M: chris@zankel.net
S: Maintained
@@ -4141,6 +4130,16 @@
L: linux-scsi@vger.kernel.org
S: Maintained
+UBI FILE SYSTEM (UBIFS)
+P: Artem Bityutskiy
+M: dedekind@infradead.org
+P: Adrian Hunter
+M: ext-adrian.hunter@nokia.com
+L: linux-mtd@lists.infradead.org
+T: git git://git.infradead.org/~dedekind/ubifs-2.6.git
+W: http://www.linux-mtd.infradead.org/doc/ubifs.html
+S: Maintained
+
UCLINUX (AND M68KNOMMU)
P: Greg Ungerer
M: gerg@uclinux.org
@@ -4166,7 +4165,7 @@
L: linux-kernel@vger.kernel.org
S: Maintained
-UltraSPARC (sparc64):
+UltraSPARC (sparc64)
P: David S. Miller
M: davem@davemloft.net
L: sparclinux@vger.kernel.org
@@ -4180,6 +4179,14 @@
W: http://www.kernel.dk
S: Maintained
+UNSORTED BLOCK IMAGES (UBI)
+P: Artem Bityutskiy
+M: dedekind@infradead.org
+W: http://www.linux-mtd.infradead.org/
+L: linux-mtd@lists.infradead.org
+T: git git://git.infradead.org/~dedekind/ubi-2.6.git
+S: Maintained
+
USB ACM DRIVER
P: Oliver Neukum
M: oliver@neukum.name
@@ -4449,7 +4456,7 @@
W: http://royale.zerezo.com/zr364xx/
S: Maintained
-USER-MODE LINUX
+USER-MODE LINUX (UML)
P: Jeff Dike
M: jdike@addtoit.com
L: user-mode-linux-devel@lists.sourceforge.net
@@ -4473,7 +4480,7 @@
T: git://git.kernel.org/pub/scm/utils/util-linux-ng/util-linux-ng.git
S: Maintained
-VFAT/FAT/MSDOS FILESYSTEM:
+VFAT/FAT/MSDOS FILESYSTEM
P: OGAWA Hirofumi
M: hirofumi@mail.parknet.co.jp
L: linux-kernel@vger.kernel.org
@@ -4496,7 +4503,7 @@
L: netdev@vger.kernel.org
S: Maintained
-VIDEO FOR LINUX
+VIDEO FOR LINUX (V4L)
P: Mauro Carvalho Chehab
M: mchehab@infradead.org
M: v4l-dvb-maintainer@linuxtv.org
@@ -4602,6 +4609,17 @@
L: linux-x25@vger.kernel.org
S: Maintained
+X86 ARCHITECTURE (32-BIT AND 64-BIT)
+P: Thomas Gleixner
+M: tglx@linutronix.de
+P: Ingo Molnar
+M: mingo@redhat.com
+P: H. Peter Anvin
+M: hpa@zytor.com
+L: linux-kernel@vger.kernel.org
+T: git://git.kernel.org/pub/scm/linux/kernel/git/x86/linux-2.6-x86.git
+S: Maintained
+
XEN HYPERVISOR INTERFACE
P: Jeremy Fitzhardinge
M: jeremy@xensource.com
@@ -4633,17 +4651,6 @@
L: linux-serial@vger.kernel.org
S: Maintained
-X86 ARCHITECTURE (32-BIT AND 64-BIT)
-P: Thomas Gleixner
-M: tglx@linutronix.de
-P: Ingo Molnar
-M: mingo@redhat.com
-P: H. Peter Anvin
-M: hpa@zytor.com
-L: linux-kernel@vger.kernel.org
-T: git://git.kernel.org/pub/scm/linux/kernel/git/x86/linux-2.6-x86.git
-S: Maintained
-
YAM DRIVER FOR AX.25
P: Jean-Paul Roubelat
M: jpr@f6fbb.org
diff --git a/arch/Kconfig b/arch/Kconfig
index 364c6da..0267bab 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -13,6 +13,20 @@
If unsure, say N.
+config OPROFILE_IBS
+ bool "OProfile AMD IBS support (EXPERIMENTAL)"
+ default n
+ depends on OPROFILE && SMP && X86
+ help
+ Instruction-Based Sampling (IBS) is a new profiling
+ technique that provides rich, precise program performance
+ information. IBS is introduced by AMD Family10h processors
+ (AMD Opteron Quad-Core processor “Barcelona”) to overcome
+ the limitations of conventional performance counter
+ sampling.
+
+ If unsure, say N.
+
config HAVE_OPROFILE
def_bool n
diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig
index 1bec55d..ee35226 100644
--- a/arch/alpha/Kconfig
+++ b/arch/alpha/Kconfig
@@ -5,6 +5,7 @@
config ALPHA
bool
default y
+ select HAVE_AOUT
select HAVE_IDE
select HAVE_OPROFILE
help
@@ -68,9 +69,6 @@
depends on SMP
default y
-config ARCH_SUPPORTS_AOUT
- def_bool y
-
source "init/Kconfig"
diff --git a/arch/alpha/include/asm/statfs.h b/arch/alpha/include/asm/statfs.h
index ad15830..de35cd4 100644
--- a/arch/alpha/include/asm/statfs.h
+++ b/arch/alpha/include/asm/statfs.h
@@ -1,6 +1,10 @@
#ifndef _ALPHA_STATFS_H
#define _ALPHA_STATFS_H
+/* Alpha is the only 64-bit platform with 32-bit statfs. And doesn't
+ even seem to implement statfs64 */
+#define __statfs_word __u32
+
#include <asm-generic/statfs.h>
#endif
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index efeed65..4853f9d 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -8,6 +8,7 @@
config ARM
bool
default y
+ select HAVE_AOUT
select HAVE_IDE
select RTC_LIB
select SYS_SUPPORTS_APM_EMULATION
@@ -140,9 +141,6 @@
bool
default y
-config ARCH_SUPPORTS_AOUT
- def_bool y
-
config ARCH_MAY_HAVE_PC_FDC
bool
diff --git a/arch/arm/include/asm/statfs.h b/arch/arm/include/asm/statfs.h
index a02e6a8..079447c 100644
--- a/arch/arm/include/asm/statfs.h
+++ b/arch/arm/include/asm/statfs.h
@@ -1,42 +1,12 @@
#ifndef _ASMARM_STATFS_H
#define _ASMARM_STATFS_H
-#ifndef __KERNEL_STRICT_NAMES
-# include <linux/types.h>
-typedef __kernel_fsid_t fsid_t;
-#endif
-
-struct statfs {
- __u32 f_type;
- __u32 f_bsize;
- __u32 f_blocks;
- __u32 f_bfree;
- __u32 f_bavail;
- __u32 f_files;
- __u32 f_ffree;
- __kernel_fsid_t f_fsid;
- __u32 f_namelen;
- __u32 f_frsize;
- __u32 f_spare[5];
-};
-
/*
* With EABI there is 4 bytes of padding added to this structure.
* Let's pack it so the padding goes away to simplify dual ABI support.
* Note that user space does NOT have to pack this structure.
*/
-struct statfs64 {
- __u32 f_type;
- __u32 f_bsize;
- __u64 f_blocks;
- __u64 f_bfree;
- __u64 f_bavail;
- __u64 f_files;
- __u64 f_ffree;
- __kernel_fsid_t f_fsid;
- __u32 f_namelen;
- __u32 f_frsize;
- __u32 f_spare[5];
-} __attribute__ ((packed,aligned(4)));
+#define ARCH_PACK_STATFS64 __attribute__((packed,aligned(4)))
+#include <asm-generic/statfs.h>
#endif
diff --git a/arch/arm/kernel/dma.c b/arch/arm/kernel/dma.c
index ba99a20..d006085 100644
--- a/arch/arm/kernel/dma.c
+++ b/arch/arm/kernel/dma.c
@@ -26,23 +26,6 @@
static dma_t dma_chan[MAX_DMA_CHANNELS];
/*
- * Get dma list for /proc/dma
- */
-int get_dma_list(char *buf)
-{
- dma_t *dma;
- char *p = buf;
- int i;
-
- for (i = 0, dma = dma_chan; i < MAX_DMA_CHANNELS; i++, dma++)
- if (dma->lock)
- p += sprintf(p, "%2d: %14s %s\n", i,
- dma->d_ops->type, dma->device_id);
-
- return p - buf;
-}
-
-/*
* Request DMA channel
*
* On certain platforms, we have to allocate an interrupt as well...
diff --git a/arch/arm/mach-orion5x/include/mach/orion5x.h b/arch/arm/mach-orion5x/include/mach/orion5x.h
index e67c843..9f5ce1c 100644
--- a/arch/arm/mach-orion5x/include/mach/orion5x.h
+++ b/arch/arm/mach-orion5x/include/mach/orion5x.h
@@ -157,9 +157,11 @@
#define CPU_CONF ORION5X_BRIDGE_REG(0x100)
#define CPU_CTRL ORION5X_BRIDGE_REG(0x104)
#define CPU_RESET_MASK ORION5X_BRIDGE_REG(0x108)
+#define WDT_RESET 0x0002
#define CPU_SOFT_RESET ORION5X_BRIDGE_REG(0x10c)
#define POWER_MNG_CTRL_REG ORION5X_BRIDGE_REG(0x11C)
#define BRIDGE_CAUSE ORION5X_BRIDGE_REG(0x110)
+#define WDT_INT_REQ 0x0008
#define BRIDGE_MASK ORION5X_BRIDGE_REG(0x114)
#define BRIDGE_INT_TIMER0 0x0002
#define BRIDGE_INT_TIMER1 0x0004
diff --git a/arch/arm/mach-pxa/include/mach/camera.h b/arch/arm/mach-pxa/include/mach/camera.h
index 39516ce..31abe6d 100644
--- a/arch/arm/mach-pxa/include/mach/camera.h
+++ b/arch/arm/mach-pxa/include/mach/camera.h
@@ -36,8 +36,6 @@
struct pxacamera_platform_data {
int (*init)(struct device *);
- int (*power)(struct device *, int);
- int (*reset)(struct device *, int);
unsigned long flags;
unsigned long mclk_10khz;
diff --git a/arch/arm/plat-omap/devices.c b/arch/arm/plat-omap/devices.c
index a716ecd..97187fa 100644
--- a/arch/arm/plat-omap/devices.c
+++ b/arch/arm/plat-omap/devices.c
@@ -441,16 +441,8 @@
#if defined(CONFIG_OMAP_WATCHDOG) || defined(CONFIG_OMAP_WATCHDOG_MODULE)
-#ifdef CONFIG_ARCH_OMAP24XX
-#define OMAP_WDT_BASE 0x48022000
-#else
-#define OMAP_WDT_BASE 0xfffeb000
-#endif
-
static struct resource wdt_resources[] = {
{
- .start = OMAP_WDT_BASE,
- .end = OMAP_WDT_BASE + 0x4f,
.flags = IORESOURCE_MEM,
},
};
@@ -464,6 +456,19 @@
static void omap_init_wdt(void)
{
+ if (cpu_is_omap16xx())
+ wdt_resources[0].start = 0xfffeb000;
+ else if (cpu_is_omap2420())
+ wdt_resources[0].start = 0x48022000; /* WDT2 */
+ else if (cpu_is_omap2430())
+ wdt_resources[0].start = 0x49016000; /* WDT2 */
+ else if (cpu_is_omap343x())
+ wdt_resources[0].start = 0x48314000; /* WDT2 */
+ else
+ return;
+
+ wdt_resources[0].end = wdt_resources[0].start + 0x4f;
+
(void) platform_device_register(&omap_wdt_device);
}
#else
diff --git a/arch/avr32/boards/atngw100/setup.c b/arch/avr32/boards/atngw100/setup.c
index b8286f1..6c54580 100644
--- a/arch/avr32/boards/atngw100/setup.c
+++ b/arch/avr32/boards/atngw100/setup.c
@@ -9,6 +9,7 @@
*/
#include <linux/clk.h>
#include <linux/etherdevice.h>
+#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/i2c.h>
#include <linux/i2c-gpio.h>
@@ -53,8 +54,11 @@
};
static struct mci_platform_data __initdata mci0_data = {
- .detect_pin = GPIO_PIN_PC(25),
- .wp_pin = GPIO_PIN_PE(0),
+ .slot[0] = {
+ .bus_width = 4,
+ .detect_pin = GPIO_PIN_PC(25),
+ .wp_pin = GPIO_PIN_PE(0),
+ },
};
/*
@@ -190,7 +194,7 @@
* PB28/EXTINT3 doesn't; it should be SMBALERT# (for PMBus),
* but it's not available off-board.
*/
- at32_select_periph(GPIO_PIN_PB(28), 0, AT32_GPIOF_PULLUP);
+ at32_select_periph(GPIO_PIOB_BASE, 1 << 28, 0, AT32_GPIOF_PULLUP);
at32_select_gpio(i2c_gpio_data.sda_pin,
AT32_GPIOF_MULTIDRV | AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH);
at32_select_gpio(i2c_gpio_data.scl_pin,
@@ -204,6 +208,15 @@
static int __init atngw100_arch_init(void)
{
+ /* PB30 is the otherwise unused jumper on the mainboard, with an
+ * external pullup; the jumper grounds it. Use it however you
+ * like, including letting U-Boot or Linux tweak boot sequences.
+ */
+ at32_select_gpio(GPIO_PIN_PB(30), 0);
+ gpio_request(GPIO_PIN_PB(30), "j15");
+ gpio_direction_input(GPIO_PIN_PB(30));
+ gpio_export(GPIO_PIN_PB(30), false);
+
/* set_irq_type() after the arch_initcall for EIC has run, and
* before the I2C subsystem could try using this IRQ.
*/
diff --git a/arch/avr32/boards/atstk1000/atstk1002.c b/arch/avr32/boards/atstk1000/atstk1002.c
index dfc3443..29e5b51 100644
--- a/arch/avr32/boards/atstk1000/atstk1002.c
+++ b/arch/avr32/boards/atstk1000/atstk1002.c
@@ -232,7 +232,7 @@
goto err_set_clk;
}
- at32_select_periph(GPIO_PIN_PA(30), GPIO_PERIPH_A, 0);
+ at32_select_periph(GPIO_PIOA_BASE, (1 << 30), GPIO_PERIPH_A, 0);
at73c213_data.dac_clk = gclk;
err_set_clk:
@@ -264,16 +264,20 @@
#ifndef CONFIG_BOARD_ATSTK100X_SW2_CUSTOM
+static struct mci_platform_data __initdata mci0_data = {
+ .slot[0] = {
+ .bus_width = 4,
+
/* MMC card detect requires MACB0 *NOT* be used */
#ifdef CONFIG_BOARD_ATSTK1002_SW6_CUSTOM
-static struct mci_platform_data __initdata mci0_data = {
- .detect_pin = GPIO_PIN_PC(14), /* gpio30/sdcd */
- .wp_pin = GPIO_PIN_PC(15), /* gpio31/sdwp */
-};
-#define MCI_PDATA &mci0_data
+ .detect_pin = GPIO_PIN_PC(14), /* gpio30/sdcd */
+ .wp_pin = GPIO_PIN_PC(15), /* gpio31/sdwp */
#else
-#define MCI_PDATA NULL
+ .detect_pin = -ENODEV,
+ .wp_pin = -ENODEV,
#endif /* SW6 for sd{cd,wp} routing */
+ },
+};
#endif /* SW2 for MMC signal routing */
@@ -326,13 +330,14 @@
at32_add_device_spi(1, spi1_board_info, ARRAY_SIZE(spi1_board_info));
#endif
#ifndef CONFIG_BOARD_ATSTK100X_SW2_CUSTOM
- at32_add_device_mci(0, MCI_PDATA);
+ at32_add_device_mci(0, &mci0_data);
#endif
#ifdef CONFIG_BOARD_ATSTK1002_SW5_CUSTOM
set_hw_addr(at32_add_device_eth(1, ð_data[1]));
#else
at32_add_device_lcdc(0, &atstk1000_lcdc_data,
- fbmem_start, fbmem_size, 0);
+ fbmem_start, fbmem_size,
+ ATMEL_LCDC_PRI_24BIT | ATMEL_LCDC_PRI_CONTROL);
#endif
at32_add_device_usba(0, NULL);
#ifndef CONFIG_BOARD_ATSTK100X_SW3_CUSTOM
diff --git a/arch/avr32/boards/atstk1000/atstk1003.c b/arch/avr32/boards/atstk1000/atstk1003.c
index 0cf6641..be089d7f 100644
--- a/arch/avr32/boards/atstk1000/atstk1003.c
+++ b/arch/avr32/boards/atstk1000/atstk1003.c
@@ -19,6 +19,7 @@
#include <linux/spi/spi.h>
#include <asm/setup.h>
+#include <asm/atmel-mci.h>
#include <mach/at32ap700x.h>
#include <mach/board.h>
@@ -66,6 +67,16 @@
} };
#endif
+#ifndef CONFIG_BOARD_ATSTK100X_SW2_CUSTOM
+static struct mci_platform_data __initdata mci0_data = {
+ .slot[0] = {
+ .bus_width = 4,
+ .detect_pin = -ENODEV,
+ .wp_pin = -ENODEV,
+ },
+};
+#endif
+
#ifdef CONFIG_BOARD_ATSTK1000_EXTDAC
static void __init atstk1003_setup_extdac(void)
{
@@ -84,7 +95,7 @@
goto err_set_clk;
}
- at32_select_periph(GPIO_PIN_PA(30), GPIO_PERIPH_A, 0);
+ at32_select_periph(GPIO_PIOA_BASE, (1 << 30), GPIO_PERIPH_A, 0);
at73c213_data.dac_clk = gclk;
err_set_clk:
@@ -154,7 +165,7 @@
at32_add_device_spi(1, spi1_board_info, ARRAY_SIZE(spi1_board_info));
#endif
#ifndef CONFIG_BOARD_ATSTK100X_SW2_CUSTOM
- at32_add_device_mci(0, NULL);
+ at32_add_device_mci(0, &mci0_data);
#endif
at32_add_device_usba(0, NULL);
#ifndef CONFIG_BOARD_ATSTK100X_SW3_CUSTOM
diff --git a/arch/avr32/boards/atstk1000/atstk1004.c b/arch/avr32/boards/atstk1000/atstk1004.c
index 50a5273..248ef23 100644
--- a/arch/avr32/boards/atstk1000/atstk1004.c
+++ b/arch/avr32/boards/atstk1000/atstk1004.c
@@ -21,6 +21,7 @@
#include <video/atmel_lcdc.h>
#include <asm/setup.h>
+#include <asm/atmel-mci.h>
#include <mach/at32ap700x.h>
#include <mach/board.h>
@@ -71,6 +72,16 @@
} };
#endif
+#ifndef CONFIG_BOARD_ATSTK100X_SW2_CUSTOM
+static struct mci_platform_data __initdata mci0_data = {
+ .slot[0] = {
+ .bus_width = 4,
+ .detect_pin = -ENODEV,
+ .wp_pin = -ENODEV,
+ },
+};
+#endif
+
#ifdef CONFIG_BOARD_ATSTK1000_EXTDAC
static void __init atstk1004_setup_extdac(void)
{
@@ -89,7 +100,7 @@
goto err_set_clk;
}
- at32_select_periph(GPIO_PIN_PA(30), GPIO_PERIPH_A, 0);
+ at32_select_periph(GPIO_PIOA_BASE, (1 << 30), GPIO_PERIPH_A, 0);
at73c213_data.dac_clk = gclk;
err_set_clk:
@@ -137,10 +148,11 @@
at32_add_device_spi(1, spi1_board_info, ARRAY_SIZE(spi1_board_info));
#endif
#ifndef CONFIG_BOARD_ATSTK100X_SW2_CUSTOM
- at32_add_device_mci(0, NULL);
+ at32_add_device_mci(0, &mci0_data);
#endif
at32_add_device_lcdc(0, &atstk1000_lcdc_data,
- fbmem_start, fbmem_size, 0);
+ fbmem_start, fbmem_size,
+ ATMEL_LCDC_PRI_24BIT | ATMEL_LCDC_PRI_CONTROL);
at32_add_device_usba(0, NULL);
#ifndef CONFIG_BOARD_ATSTK100X_SW3_CUSTOM
at32_add_device_ssc(0, ATMEL_SSC_TX);
diff --git a/arch/avr32/include/asm/a.out.h b/arch/avr32/include/asm/a.out.h
deleted file mode 100644
index e46375a..0000000
--- a/arch/avr32/include/asm/a.out.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef __ASM_AVR32_A_OUT_H
-#define __ASM_AVR32_A_OUT_H
-
-struct exec
-{
- unsigned long a_info; /* Use macros N_MAGIC, etc for access */
- unsigned a_text; /* length of text, in bytes */
- unsigned a_data; /* length of data, in bytes */
- unsigned a_bss; /* length of uninitialized data area for file, in bytes */
- unsigned a_syms; /* length of symbol table data in file, in bytes */
- unsigned a_entry; /* start address */
- unsigned a_trsize; /* length of relocation info for text, in bytes */
- unsigned a_drsize; /* length of relocation info for data, in bytes */
-};
-
-#define N_TRSIZE(a) ((a).a_trsize)
-#define N_DRSIZE(a) ((a).a_drsize)
-#define N_SYMSIZE(a) ((a).a_syms)
-
-#endif /* __ASM_AVR32_A_OUT_H */
diff --git a/arch/avr32/include/asm/atmel-mci.h b/arch/avr32/include/asm/atmel-mci.h
index c2ea6e1..59f3fad 100644
--- a/arch/avr32/include/asm/atmel-mci.h
+++ b/arch/avr32/include/asm/atmel-mci.h
@@ -1,9 +1,39 @@
#ifndef __ASM_AVR32_ATMEL_MCI_H
#define __ASM_AVR32_ATMEL_MCI_H
-struct mci_platform_data {
+#define ATMEL_MCI_MAX_NR_SLOTS 2
+
+struct dma_slave;
+
+/**
+ * struct mci_slot_pdata - board-specific per-slot configuration
+ * @bus_width: Number of data lines wired up the slot
+ * @detect_pin: GPIO pin wired to the card detect switch
+ * @wp_pin: GPIO pin wired to the write protect sensor
+ *
+ * If a given slot is not present on the board, @bus_width should be
+ * set to 0. The other fields are ignored in this case.
+ *
+ * Any pins that aren't available should be set to a negative value.
+ *
+ * Note that support for multiple slots is experimental -- some cards
+ * might get upset if we don't get the clock management exactly right.
+ * But in most cases, it should work just fine.
+ */
+struct mci_slot_pdata {
+ unsigned int bus_width;
int detect_pin;
int wp_pin;
};
+/**
+ * struct mci_platform_data - board-specific MMC/SDcard configuration
+ * @dma_slave: DMA slave interface to use in data transfers, or NULL.
+ * @slot: Per-slot configuration data.
+ */
+struct mci_platform_data {
+ struct dma_slave *dma_slave;
+ struct mci_slot_pdata slot[ATMEL_MCI_MAX_NR_SLOTS];
+};
+
#endif /* __ASM_AVR32_ATMEL_MCI_H */
diff --git a/arch/avr32/include/asm/byteorder.h b/arch/avr32/include/asm/byteorder.h
index d77b48b..8e3af02 100644
--- a/arch/avr32/include/asm/byteorder.h
+++ b/arch/avr32/include/asm/byteorder.h
@@ -7,6 +7,9 @@
#include <asm/types.h>
#include <linux/compiler.h>
+#define __BIG_ENDIAN
+#define __SWAB_64_THRU_32__
+
#ifdef __CHECKER__
extern unsigned long __builtin_bswap_32(unsigned long x);
extern unsigned short __builtin_bswap_16(unsigned short x);
@@ -17,15 +20,18 @@
* the result.
*/
#if !(__GNUC__ == 4 && __GNUC_MINOR__ < 2)
-#define __arch__swab32(x) __builtin_bswap_32(x)
-#define __arch__swab16(x) __builtin_bswap_16(x)
+static inline __attribute_const__ __u16 __arch_swab16(__u16 val)
+{
+ return __builtin_bswap_16(val);
+}
+#define __arch_swab16 __arch_swab16
+
+static inline __attribute_const__ __u32 __arch_swab32(__u32 val)
+{
+ return __builtin_bswap_32(val);
+}
+#define __arch_swab32 __arch_swab32
#endif
-#if !defined(__STRICT_ANSI__) || defined(__KERNEL__)
-# define __BYTEORDER_HAS_U64__
-# define __SWAB_64_THRU_32__
-#endif
-
-#include <linux/byteorder/big_endian.h>
-
+#include <linux/byteorder.h>
#endif /* __ASM_AVR32_BYTEORDER_H */
diff --git a/arch/avr32/include/asm/io.h b/arch/avr32/include/asm/io.h
index a520f77..22c97ef 100644
--- a/arch/avr32/include/asm/io.h
+++ b/arch/avr32/include/asm/io.h
@@ -160,6 +160,14 @@
#define readw_relaxed readw
#define readl_relaxed readl
+#define readb_be __raw_readb
+#define readw_be __raw_readw
+#define readl_be __raw_readl
+
+#define writeb_be __raw_writeb
+#define writew_be __raw_writew
+#define writel_be __raw_writel
+
#define __BUILD_MEMORY_STRING(bwl, type) \
static inline void writes##bwl(volatile void __iomem *addr, \
const void *data, unsigned int count) \
diff --git a/arch/avr32/kernel/process.c b/arch/avr32/kernel/process.c
index 2c08ac9..134d530 100644
--- a/arch/avr32/kernel/process.c
+++ b/arch/avr32/kernel/process.c
@@ -9,6 +9,7 @@
#include <linux/module.h>
#include <linux/kallsyms.h>
#include <linux/fs.h>
+#include <linux/pm.h>
#include <linux/ptrace.h>
#include <linux/reboot.h>
#include <linux/tick.h>
@@ -20,7 +21,7 @@
#include <mach/pm.h>
-void (*pm_power_off)(void) = NULL;
+void (*pm_power_off)(void);
EXPORT_SYMBOL(pm_power_off);
/*
diff --git a/arch/avr32/kernel/setup.c b/arch/avr32/kernel/setup.c
index d8e623c..5c70839 100644
--- a/arch/avr32/kernel/setup.c
+++ b/arch/avr32/kernel/setup.c
@@ -283,6 +283,25 @@
}
early_param("fbmem", early_parse_fbmem);
+/*
+ * Pick out the memory size. We look for mem=size@start,
+ * where start and size are "size[KkMmGg]"
+ */
+static int __init early_mem(char *p)
+{
+ resource_size_t size, start;
+
+ start = system_ram->start;
+ size = memparse(p, &p);
+ if (*p == '@')
+ start = memparse(p + 1, &p);
+
+ system_ram->start = start;
+ system_ram->end = system_ram->start + size - 1;
+ return 0;
+}
+early_param("mem", early_mem);
+
static int __init parse_tag_core(struct tag *tag)
{
if (tag->hdr.size > 2) {
diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c
index e01dbe4..813b684 100644
--- a/arch/avr32/mach-at32ap/at32ap700x.c
+++ b/arch/avr32/mach-at32ap/at32ap700x.c
@@ -82,8 +82,9 @@
.num_resources = ARRAY_SIZE(_name##_id##_resource), \
}
-#define select_peripheral(pin, periph, flags) \
- at32_select_periph(GPIO_PIN_##pin, GPIO_##periph, flags)
+#define select_peripheral(port, pin_mask, periph, flags) \
+ at32_select_periph(GPIO_##port##_BASE, pin_mask, \
+ GPIO_##periph, flags)
#define DEV_CLK(_name, devname, bus, _index) \
static struct clk devname##_##_name = { \
@@ -871,6 +872,7 @@
struct platform_device *__init at32_add_device_psif(unsigned int id)
{
struct platform_device *pdev;
+ u32 pin_mask;
if (!(id == 0 || id == 1))
return NULL;
@@ -881,20 +883,22 @@
switch (id) {
case 0:
+ pin_mask = (1 << 8) | (1 << 9); /* CLOCK & DATA */
+
if (platform_device_add_resources(pdev, atmel_psif0_resource,
ARRAY_SIZE(atmel_psif0_resource)))
goto err_add_resources;
atmel_psif0_pclk.dev = &pdev->dev;
- select_peripheral(PA(8), PERIPH_A, 0); /* CLOCK */
- select_peripheral(PA(9), PERIPH_A, 0); /* DATA */
+ select_peripheral(PIOA, pin_mask, PERIPH_A, 0);
break;
case 1:
+ pin_mask = (1 << 11) | (1 << 12); /* CLOCK & DATA */
+
if (platform_device_add_resources(pdev, atmel_psif1_resource,
ARRAY_SIZE(atmel_psif1_resource)))
goto err_add_resources;
atmel_psif1_pclk.dev = &pdev->dev;
- select_peripheral(PB(11), PERIPH_A, 0); /* CLOCK */
- select_peripheral(PB(12), PERIPH_A, 0); /* DATA */
+ select_peripheral(PIOB, pin_mask, PERIPH_A, 0);
break;
default:
return NULL;
@@ -958,26 +962,30 @@
static inline void configure_usart0_pins(void)
{
- select_peripheral(PA(8), PERIPH_B, 0); /* RXD */
- select_peripheral(PA(9), PERIPH_B, 0); /* TXD */
+ u32 pin_mask = (1 << 8) | (1 << 9); /* RXD & TXD */
+
+ select_peripheral(PIOA, pin_mask, PERIPH_B, 0);
}
static inline void configure_usart1_pins(void)
{
- select_peripheral(PA(17), PERIPH_A, 0); /* RXD */
- select_peripheral(PA(18), PERIPH_A, 0); /* TXD */
+ u32 pin_mask = (1 << 17) | (1 << 18); /* RXD & TXD */
+
+ select_peripheral(PIOA, pin_mask, PERIPH_A, 0);
}
static inline void configure_usart2_pins(void)
{
- select_peripheral(PB(26), PERIPH_B, 0); /* RXD */
- select_peripheral(PB(27), PERIPH_B, 0); /* TXD */
+ u32 pin_mask = (1 << 26) | (1 << 27); /* RXD & TXD */
+
+ select_peripheral(PIOB, pin_mask, PERIPH_B, 0);
}
static inline void configure_usart3_pins(void)
{
- select_peripheral(PB(18), PERIPH_B, 0); /* RXD */
- select_peripheral(PB(17), PERIPH_B, 0); /* TXD */
+ u32 pin_mask = (1 << 18) | (1 << 17); /* RXD & TXD */
+
+ select_peripheral(PIOB, pin_mask, PERIPH_B, 0);
}
static struct platform_device *__initdata at32_usarts[4];
@@ -1057,59 +1065,69 @@
at32_add_device_eth(unsigned int id, struct eth_platform_data *data)
{
struct platform_device *pdev;
+ u32 pin_mask;
switch (id) {
case 0:
pdev = &macb0_device;
- select_peripheral(PC(3), PERIPH_A, 0); /* TXD0 */
- select_peripheral(PC(4), PERIPH_A, 0); /* TXD1 */
- select_peripheral(PC(7), PERIPH_A, 0); /* TXEN */
- select_peripheral(PC(8), PERIPH_A, 0); /* TXCK */
- select_peripheral(PC(9), PERIPH_A, 0); /* RXD0 */
- select_peripheral(PC(10), PERIPH_A, 0); /* RXD1 */
- select_peripheral(PC(13), PERIPH_A, 0); /* RXER */
- select_peripheral(PC(15), PERIPH_A, 0); /* RXDV */
- select_peripheral(PC(16), PERIPH_A, 0); /* MDC */
- select_peripheral(PC(17), PERIPH_A, 0); /* MDIO */
+ pin_mask = (1 << 3); /* TXD0 */
+ pin_mask |= (1 << 4); /* TXD1 */
+ pin_mask |= (1 << 7); /* TXEN */
+ pin_mask |= (1 << 8); /* TXCK */
+ pin_mask |= (1 << 9); /* RXD0 */
+ pin_mask |= (1 << 10); /* RXD1 */
+ pin_mask |= (1 << 13); /* RXER */
+ pin_mask |= (1 << 15); /* RXDV */
+ pin_mask |= (1 << 16); /* MDC */
+ pin_mask |= (1 << 17); /* MDIO */
if (!data->is_rmii) {
- select_peripheral(PC(0), PERIPH_A, 0); /* COL */
- select_peripheral(PC(1), PERIPH_A, 0); /* CRS */
- select_peripheral(PC(2), PERIPH_A, 0); /* TXER */
- select_peripheral(PC(5), PERIPH_A, 0); /* TXD2 */
- select_peripheral(PC(6), PERIPH_A, 0); /* TXD3 */
- select_peripheral(PC(11), PERIPH_A, 0); /* RXD2 */
- select_peripheral(PC(12), PERIPH_A, 0); /* RXD3 */
- select_peripheral(PC(14), PERIPH_A, 0); /* RXCK */
- select_peripheral(PC(18), PERIPH_A, 0); /* SPD */
+ pin_mask |= (1 << 0); /* COL */
+ pin_mask |= (1 << 1); /* CRS */
+ pin_mask |= (1 << 2); /* TXER */
+ pin_mask |= (1 << 5); /* TXD2 */
+ pin_mask |= (1 << 6); /* TXD3 */
+ pin_mask |= (1 << 11); /* RXD2 */
+ pin_mask |= (1 << 12); /* RXD3 */
+ pin_mask |= (1 << 14); /* RXCK */
+ pin_mask |= (1 << 18); /* SPD */
}
+
+ select_peripheral(PIOC, pin_mask, PERIPH_A, 0);
+
break;
case 1:
pdev = &macb1_device;
- select_peripheral(PD(13), PERIPH_B, 0); /* TXD0 */
- select_peripheral(PD(14), PERIPH_B, 0); /* TXD1 */
- select_peripheral(PD(11), PERIPH_B, 0); /* TXEN */
- select_peripheral(PD(12), PERIPH_B, 0); /* TXCK */
- select_peripheral(PD(10), PERIPH_B, 0); /* RXD0 */
- select_peripheral(PD(6), PERIPH_B, 0); /* RXD1 */
- select_peripheral(PD(5), PERIPH_B, 0); /* RXER */
- select_peripheral(PD(4), PERIPH_B, 0); /* RXDV */
- select_peripheral(PD(3), PERIPH_B, 0); /* MDC */
- select_peripheral(PD(2), PERIPH_B, 0); /* MDIO */
+ pin_mask = (1 << 13); /* TXD0 */
+ pin_mask |= (1 << 14); /* TXD1 */
+ pin_mask |= (1 << 11); /* TXEN */
+ pin_mask |= (1 << 12); /* TXCK */
+ pin_mask |= (1 << 10); /* RXD0 */
+ pin_mask |= (1 << 6); /* RXD1 */
+ pin_mask |= (1 << 5); /* RXER */
+ pin_mask |= (1 << 4); /* RXDV */
+ pin_mask |= (1 << 3); /* MDC */
+ pin_mask |= (1 << 2); /* MDIO */
+
+ if (!data->is_rmii)
+ pin_mask |= (1 << 15); /* SPD */
+
+ select_peripheral(PIOD, pin_mask, PERIPH_B, 0);
if (!data->is_rmii) {
- select_peripheral(PC(19), PERIPH_B, 0); /* COL */
- select_peripheral(PC(23), PERIPH_B, 0); /* CRS */
- select_peripheral(PC(26), PERIPH_B, 0); /* TXER */
- select_peripheral(PC(27), PERIPH_B, 0); /* TXD2 */
- select_peripheral(PC(28), PERIPH_B, 0); /* TXD3 */
- select_peripheral(PC(29), PERIPH_B, 0); /* RXD2 */
- select_peripheral(PC(30), PERIPH_B, 0); /* RXD3 */
- select_peripheral(PC(24), PERIPH_B, 0); /* RXCK */
- select_peripheral(PD(15), PERIPH_B, 0); /* SPD */
+ pin_mask = (1 << 19); /* COL */
+ pin_mask |= (1 << 23); /* CRS */
+ pin_mask |= (1 << 26); /* TXER */
+ pin_mask |= (1 << 27); /* TXD2 */
+ pin_mask |= (1 << 28); /* TXD3 */
+ pin_mask |= (1 << 29); /* RXD2 */
+ pin_mask |= (1 << 30); /* RXD3 */
+ pin_mask |= (1 << 24); /* RXCK */
+
+ select_peripheral(PIOC, pin_mask, PERIPH_B, 0);
}
break;
@@ -1177,23 +1195,28 @@
{ GPIO_PIN_PB(2), GPIO_PIN_PB(3),
GPIO_PIN_PB(4), GPIO_PIN_PA(27), };
struct platform_device *pdev;
+ u32 pin_mask;
switch (id) {
case 0:
pdev = &atmel_spi0_device;
+ pin_mask = (1 << 1) | (1 << 2); /* MOSI & SCK */
+
/* pullup MISO so a level is always defined */
- select_peripheral(PA(0), PERIPH_A, AT32_GPIOF_PULLUP);
- select_peripheral(PA(1), PERIPH_A, 0); /* MOSI */
- select_peripheral(PA(2), PERIPH_A, 0); /* SCK */
+ select_peripheral(PIOA, (1 << 0), PERIPH_A, AT32_GPIOF_PULLUP);
+ select_peripheral(PIOA, pin_mask, PERIPH_A, 0);
+
at32_spi_setup_slaves(0, b, n, spi0_pins);
break;
case 1:
pdev = &atmel_spi1_device;
+ pin_mask = (1 << 1) | (1 << 5); /* MOSI */
+
/* pullup MISO so a level is always defined */
- select_peripheral(PB(0), PERIPH_B, AT32_GPIOF_PULLUP);
- select_peripheral(PB(1), PERIPH_B, 0); /* MOSI */
- select_peripheral(PB(5), PERIPH_B, 0); /* SCK */
+ select_peripheral(PIOB, (1 << 0), PERIPH_B, AT32_GPIOF_PULLUP);
+ select_peripheral(PIOB, pin_mask, PERIPH_B, 0);
+
at32_spi_setup_slaves(1, b, n, spi1_pins);
break;
@@ -1226,6 +1249,7 @@
unsigned int n)
{
struct platform_device *pdev;
+ u32 pin_mask;
if (id != 0)
return NULL;
@@ -1238,8 +1262,9 @@
ARRAY_SIZE(atmel_twi0_resource)))
goto err_add_resources;
- select_peripheral(PA(6), PERIPH_A, 0); /* SDA */
- select_peripheral(PA(7), PERIPH_A, 0); /* SDL */
+ pin_mask = (1 << 6) | (1 << 7); /* SDA & SDL */
+
+ select_peripheral(PIOA, pin_mask, PERIPH_A, 0);
atmel_twi0_pclk.dev = &pdev->dev;
@@ -1272,10 +1297,16 @@
struct platform_device *__init
at32_add_device_mci(unsigned int id, struct mci_platform_data *data)
{
- struct mci_platform_data _data;
struct platform_device *pdev;
+ struct dw_dma_slave *dws;
+ u32 pioa_mask;
+ u32 piob_mask;
- if (id != 0)
+ if (id != 0 || !data)
+ return NULL;
+
+ /* Must have at least one usable slot */
+ if (!data->slot[0].bus_width && !data->slot[1].bus_width)
return NULL;
pdev = platform_device_alloc("atmel_mci", id);
@@ -1286,28 +1317,80 @@
ARRAY_SIZE(atmel_mci0_resource)))
goto fail;
- if (!data) {
- data = &_data;
- memset(data, -1, sizeof(struct mci_platform_data));
- data->detect_pin = GPIO_PIN_NONE;
- data->wp_pin = GPIO_PIN_NONE;
- }
+ if (data->dma_slave)
+ dws = kmemdup(to_dw_dma_slave(data->dma_slave),
+ sizeof(struct dw_dma_slave), GFP_KERNEL);
+ else
+ dws = kzalloc(sizeof(struct dw_dma_slave), GFP_KERNEL);
+
+ dws->slave.dev = &pdev->dev;
+ dws->slave.dma_dev = &dw_dmac0_device.dev;
+ dws->slave.reg_width = DMA_SLAVE_WIDTH_32BIT;
+ dws->cfg_hi = (DWC_CFGH_SRC_PER(0)
+ | DWC_CFGH_DST_PER(1));
+ dws->cfg_lo &= ~(DWC_CFGL_HS_DST_POL
+ | DWC_CFGL_HS_SRC_POL);
+
+ data->dma_slave = &dws->slave;
if (platform_device_add_data(pdev, data,
sizeof(struct mci_platform_data)))
goto fail;
- select_peripheral(PA(10), PERIPH_A, 0); /* CLK */
- select_peripheral(PA(11), PERIPH_A, 0); /* CMD */
- select_peripheral(PA(12), PERIPH_A, 0); /* DATA0 */
- select_peripheral(PA(13), PERIPH_A, 0); /* DATA1 */
- select_peripheral(PA(14), PERIPH_A, 0); /* DATA2 */
- select_peripheral(PA(15), PERIPH_A, 0); /* DATA3 */
+ /* CLK line is common to both slots */
+ pioa_mask = 1 << 10;
- if (gpio_is_valid(data->detect_pin))
- at32_select_gpio(data->detect_pin, 0);
- if (gpio_is_valid(data->wp_pin))
- at32_select_gpio(data->wp_pin, 0);
+ switch (data->slot[0].bus_width) {
+ case 4:
+ pioa_mask |= 1 << 13; /* DATA1 */
+ pioa_mask |= 1 << 14; /* DATA2 */
+ pioa_mask |= 1 << 15; /* DATA3 */
+ /* fall through */
+ case 1:
+ pioa_mask |= 1 << 11; /* CMD */
+ pioa_mask |= 1 << 12; /* DATA0 */
+
+ if (gpio_is_valid(data->slot[0].detect_pin))
+ at32_select_gpio(data->slot[0].detect_pin, 0);
+ if (gpio_is_valid(data->slot[0].wp_pin))
+ at32_select_gpio(data->slot[0].wp_pin, 0);
+ break;
+ case 0:
+ /* Slot is unused */
+ break;
+ default:
+ goto fail;
+ }
+
+ select_peripheral(PIOA, pioa_mask, PERIPH_A, 0);
+ piob_mask = 0;
+
+ switch (data->slot[1].bus_width) {
+ case 4:
+ piob_mask |= 1 << 8; /* DATA1 */
+ piob_mask |= 1 << 9; /* DATA2 */
+ piob_mask |= 1 << 10; /* DATA3 */
+ /* fall through */
+ case 1:
+ piob_mask |= 1 << 6; /* CMD */
+ piob_mask |= 1 << 7; /* DATA0 */
+ select_peripheral(PIOB, piob_mask, PERIPH_B, 0);
+
+ if (gpio_is_valid(data->slot[1].detect_pin))
+ at32_select_gpio(data->slot[1].detect_pin, 0);
+ if (gpio_is_valid(data->slot[1].wp_pin))
+ at32_select_gpio(data->slot[1].wp_pin, 0);
+ break;
+ case 0:
+ /* Slot is unused */
+ break;
+ default:
+ if (!data->slot[0].bus_width)
+ goto fail;
+
+ data->slot[1].bus_width = 0;
+ break;
+ }
atmel_mci0_pclk.dev = &pdev->dev;
@@ -1353,13 +1436,14 @@
struct platform_device *__init
at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data,
unsigned long fbmem_start, unsigned long fbmem_len,
- unsigned int pin_config)
+ u64 pin_mask)
{
struct platform_device *pdev;
struct atmel_lcdfb_info *info;
struct fb_monspecs *monspecs;
struct fb_videomode *modedb;
unsigned int modedb_size;
+ u32 portc_mask, portd_mask, porte_mask;
/*
* Do a deep copy of the fb data, monspecs and modedb. Make
@@ -1381,76 +1465,21 @@
case 0:
pdev = &atmel_lcdfb0_device;
- switch (pin_config) {
- case 0:
- select_peripheral(PC(19), PERIPH_A, 0); /* CC */
- select_peripheral(PC(20), PERIPH_A, 0); /* HSYNC */
- select_peripheral(PC(21), PERIPH_A, 0); /* PCLK */
- select_peripheral(PC(22), PERIPH_A, 0); /* VSYNC */
- select_peripheral(PC(23), PERIPH_A, 0); /* DVAL */
- select_peripheral(PC(24), PERIPH_A, 0); /* MODE */
- select_peripheral(PC(25), PERIPH_A, 0); /* PWR */
- select_peripheral(PC(26), PERIPH_A, 0); /* DATA0 */
- select_peripheral(PC(27), PERIPH_A, 0); /* DATA1 */
- select_peripheral(PC(28), PERIPH_A, 0); /* DATA2 */
- select_peripheral(PC(29), PERIPH_A, 0); /* DATA3 */
- select_peripheral(PC(30), PERIPH_A, 0); /* DATA4 */
- select_peripheral(PC(31), PERIPH_A, 0); /* DATA5 */
- select_peripheral(PD(0), PERIPH_A, 0); /* DATA6 */
- select_peripheral(PD(1), PERIPH_A, 0); /* DATA7 */
- select_peripheral(PD(2), PERIPH_A, 0); /* DATA8 */
- select_peripheral(PD(3), PERIPH_A, 0); /* DATA9 */
- select_peripheral(PD(4), PERIPH_A, 0); /* DATA10 */
- select_peripheral(PD(5), PERIPH_A, 0); /* DATA11 */
- select_peripheral(PD(6), PERIPH_A, 0); /* DATA12 */
- select_peripheral(PD(7), PERIPH_A, 0); /* DATA13 */
- select_peripheral(PD(8), PERIPH_A, 0); /* DATA14 */
- select_peripheral(PD(9), PERIPH_A, 0); /* DATA15 */
- select_peripheral(PD(10), PERIPH_A, 0); /* DATA16 */
- select_peripheral(PD(11), PERIPH_A, 0); /* DATA17 */
- select_peripheral(PD(12), PERIPH_A, 0); /* DATA18 */
- select_peripheral(PD(13), PERIPH_A, 0); /* DATA19 */
- select_peripheral(PD(14), PERIPH_A, 0); /* DATA20 */
- select_peripheral(PD(15), PERIPH_A, 0); /* DATA21 */
- select_peripheral(PD(16), PERIPH_A, 0); /* DATA22 */
- select_peripheral(PD(17), PERIPH_A, 0); /* DATA23 */
- break;
- case 1:
- select_peripheral(PE(0), PERIPH_B, 0); /* CC */
- select_peripheral(PC(20), PERIPH_A, 0); /* HSYNC */
- select_peripheral(PC(21), PERIPH_A, 0); /* PCLK */
- select_peripheral(PC(22), PERIPH_A, 0); /* VSYNC */
- select_peripheral(PE(1), PERIPH_B, 0); /* DVAL */
- select_peripheral(PE(2), PERIPH_B, 0); /* MODE */
- select_peripheral(PC(25), PERIPH_A, 0); /* PWR */
- select_peripheral(PE(3), PERIPH_B, 0); /* DATA0 */
- select_peripheral(PE(4), PERIPH_B, 0); /* DATA1 */
- select_peripheral(PE(5), PERIPH_B, 0); /* DATA2 */
- select_peripheral(PE(6), PERIPH_B, 0); /* DATA3 */
- select_peripheral(PE(7), PERIPH_B, 0); /* DATA4 */
- select_peripheral(PC(31), PERIPH_A, 0); /* DATA5 */
- select_peripheral(PD(0), PERIPH_A, 0); /* DATA6 */
- select_peripheral(PD(1), PERIPH_A, 0); /* DATA7 */
- select_peripheral(PE(8), PERIPH_B, 0); /* DATA8 */
- select_peripheral(PE(9), PERIPH_B, 0); /* DATA9 */
- select_peripheral(PE(10), PERIPH_B, 0); /* DATA10 */
- select_peripheral(PE(11), PERIPH_B, 0); /* DATA11 */
- select_peripheral(PE(12), PERIPH_B, 0); /* DATA12 */
- select_peripheral(PD(7), PERIPH_A, 0); /* DATA13 */
- select_peripheral(PD(8), PERIPH_A, 0); /* DATA14 */
- select_peripheral(PD(9), PERIPH_A, 0); /* DATA15 */
- select_peripheral(PE(13), PERIPH_B, 0); /* DATA16 */
- select_peripheral(PE(14), PERIPH_B, 0); /* DATA17 */
- select_peripheral(PE(15), PERIPH_B, 0); /* DATA18 */
- select_peripheral(PE(16), PERIPH_B, 0); /* DATA19 */
- select_peripheral(PE(17), PERIPH_B, 0); /* DATA20 */
- select_peripheral(PE(18), PERIPH_B, 0); /* DATA21 */
- select_peripheral(PD(16), PERIPH_A, 0); /* DATA22 */
- select_peripheral(PD(17), PERIPH_A, 0); /* DATA23 */
- break;
- default:
- goto err_invalid_id;
- }
+ if (pin_mask == 0ULL)
+ /* Default to "full" lcdc control signals and 24bit */
+ pin_mask = ATMEL_LCDC_PRI_24BIT | ATMEL_LCDC_PRI_CONTROL;
+
+ /* LCDC on port C */
+ portc_mask = (pin_mask & 0xfff80000) >> 19;
+ select_peripheral(PIOC, portc_mask, PERIPH_A, 0);
+
+ /* LCDC on port D */
+ portd_mask = pin_mask & 0x0003ffff;
+ select_peripheral(PIOD, portd_mask, PERIPH_A, 0);
+
+ /* LCDC on port E */
+ porte_mask = (pin_mask >> 32) & 0x0007ffff;
+ select_peripheral(PIOE, porte_mask, PERIPH_B, 0);
clk_set_parent(&atmel_lcdfb0_pixclk, &pll0);
clk_set_rate(&atmel_lcdfb0_pixclk, clk_get_rate(&pll0));
@@ -1499,6 +1528,7 @@
struct platform_device *__init at32_add_device_pwm(u32 mask)
{
struct platform_device *pdev;
+ u32 pin_mask;
if (!mask)
return NULL;
@@ -1514,14 +1544,21 @@
if (platform_device_add_data(pdev, &mask, sizeof(mask)))
goto out_free_pdev;
+ pin_mask = 0;
if (mask & (1 << 0))
- select_peripheral(PA(28), PERIPH_A, 0);
+ pin_mask |= (1 << 28);
if (mask & (1 << 1))
- select_peripheral(PA(29), PERIPH_A, 0);
+ pin_mask |= (1 << 29);
+ if (pin_mask > 0)
+ select_peripheral(PIOA, pin_mask, PERIPH_A, 0);
+
+ pin_mask = 0;
if (mask & (1 << 2))
- select_peripheral(PA(21), PERIPH_B, 0);
+ pin_mask |= (1 << 21);
if (mask & (1 << 3))
- select_peripheral(PA(22), PERIPH_B, 0);
+ pin_mask |= (1 << 22);
+ if (pin_mask > 0)
+ select_peripheral(PIOA, pin_mask, PERIPH_B, 0);
atmel_pwm0_mck.dev = &pdev->dev;
@@ -1562,52 +1599,65 @@
at32_add_device_ssc(unsigned int id, unsigned int flags)
{
struct platform_device *pdev;
+ u32 pin_mask = 0;
switch (id) {
case 0:
pdev = &ssc0_device;
if (flags & ATMEL_SSC_RF)
- select_peripheral(PA(21), PERIPH_A, 0); /* RF */
+ pin_mask |= (1 << 21); /* RF */
if (flags & ATMEL_SSC_RK)
- select_peripheral(PA(22), PERIPH_A, 0); /* RK */
+ pin_mask |= (1 << 22); /* RK */
if (flags & ATMEL_SSC_TK)
- select_peripheral(PA(23), PERIPH_A, 0); /* TK */
+ pin_mask |= (1 << 23); /* TK */
if (flags & ATMEL_SSC_TF)
- select_peripheral(PA(24), PERIPH_A, 0); /* TF */
+ pin_mask |= (1 << 24); /* TF */
if (flags & ATMEL_SSC_TD)
- select_peripheral(PA(25), PERIPH_A, 0); /* TD */
+ pin_mask |= (1 << 25); /* TD */
if (flags & ATMEL_SSC_RD)
- select_peripheral(PA(26), PERIPH_A, 0); /* RD */
+ pin_mask |= (1 << 26); /* RD */
+
+ if (pin_mask > 0)
+ select_peripheral(PIOA, pin_mask, PERIPH_A, 0);
+
break;
case 1:
pdev = &ssc1_device;
if (flags & ATMEL_SSC_RF)
- select_peripheral(PA(0), PERIPH_B, 0); /* RF */
+ pin_mask |= (1 << 0); /* RF */
if (flags & ATMEL_SSC_RK)
- select_peripheral(PA(1), PERIPH_B, 0); /* RK */
+ pin_mask |= (1 << 1); /* RK */
if (flags & ATMEL_SSC_TK)
- select_peripheral(PA(2), PERIPH_B, 0); /* TK */
+ pin_mask |= (1 << 2); /* TK */
if (flags & ATMEL_SSC_TF)
- select_peripheral(PA(3), PERIPH_B, 0); /* TF */
+ pin_mask |= (1 << 3); /* TF */
if (flags & ATMEL_SSC_TD)
- select_peripheral(PA(4), PERIPH_B, 0); /* TD */
+ pin_mask |= (1 << 4); /* TD */
if (flags & ATMEL_SSC_RD)
- select_peripheral(PA(5), PERIPH_B, 0); /* RD */
+ pin_mask |= (1 << 5); /* RD */
+
+ if (pin_mask > 0)
+ select_peripheral(PIOA, pin_mask, PERIPH_B, 0);
+
break;
case 2:
pdev = &ssc2_device;
if (flags & ATMEL_SSC_TD)
- select_peripheral(PB(13), PERIPH_A, 0); /* TD */
+ pin_mask |= (1 << 13); /* TD */
if (flags & ATMEL_SSC_RD)
- select_peripheral(PB(14), PERIPH_A, 0); /* RD */
+ pin_mask |= (1 << 14); /* RD */
if (flags & ATMEL_SSC_TK)
- select_peripheral(PB(15), PERIPH_A, 0); /* TK */
+ pin_mask |= (1 << 15); /* TK */
if (flags & ATMEL_SSC_TF)
- select_peripheral(PB(16), PERIPH_A, 0); /* TF */
+ pin_mask |= (1 << 16); /* TF */
if (flags & ATMEL_SSC_RF)
- select_peripheral(PB(17), PERIPH_A, 0); /* RF */
+ pin_mask |= (1 << 17); /* RF */
if (flags & ATMEL_SSC_RK)
- select_peripheral(PB(18), PERIPH_A, 0); /* RK */
+ pin_mask |= (1 << 18); /* RK */
+
+ if (pin_mask > 0)
+ select_peripheral(PIOB, pin_mask, PERIPH_A, 0);
+
break;
default:
return NULL;
@@ -1745,14 +1795,15 @@
unsigned int cs, unsigned int extint)
{
static unsigned int extint_pin_map[4] __initdata = {
- GPIO_PIN_PB(25),
- GPIO_PIN_PB(26),
- GPIO_PIN_PB(27),
- GPIO_PIN_PB(28),
+ (1 << 25),
+ (1 << 26),
+ (1 << 27),
+ (1 << 28),
};
static bool common_pins_initialized __initdata = false;
unsigned int extint_pin;
int ret;
+ u32 pin_mask;
if (extint >= ARRAY_SIZE(extint_pin_map))
return -EINVAL;
@@ -1766,7 +1817,8 @@
if (ret)
return ret;
- select_peripheral(PE(21), PERIPH_A, 0); /* NCS4 -> OE_N */
+ /* NCS4 -> OE_N */
+ select_peripheral(PIOE, (1 << 21), PERIPH_A, 0);
hmatrix_sfr_set_bits(HMATRIX_SLAVE_EBI, HMATRIX_EBI_CF0_ENABLE);
break;
case 5:
@@ -1776,7 +1828,8 @@
if (ret)
return ret;
- select_peripheral(PE(22), PERIPH_A, 0); /* NCS5 -> OE_N */
+ /* NCS5 -> OE_N */
+ select_peripheral(PIOE, (1 << 22), PERIPH_A, 0);
hmatrix_sfr_set_bits(HMATRIX_SLAVE_EBI, HMATRIX_EBI_CF1_ENABLE);
break;
default:
@@ -1784,14 +1837,17 @@
}
if (!common_pins_initialized) {
- select_peripheral(PE(19), PERIPH_A, 0); /* CFCE1 -> CS0_N */
- select_peripheral(PE(20), PERIPH_A, 0); /* CFCE2 -> CS1_N */
- select_peripheral(PE(23), PERIPH_A, 0); /* CFRNW -> DIR */
- select_peripheral(PE(24), PERIPH_A, 0); /* NWAIT <- IORDY */
+ pin_mask = (1 << 19); /* CFCE1 -> CS0_N */
+ pin_mask |= (1 << 20); /* CFCE2 -> CS1_N */
+ pin_mask |= (1 << 23); /* CFRNW -> DIR */
+ pin_mask |= (1 << 24); /* NWAIT <- IORDY */
+
+ select_peripheral(PIOE, pin_mask, PERIPH_A, 0);
+
common_pins_initialized = true;
}
- at32_select_periph(extint_pin, GPIO_PERIPH_A, AT32_GPIOF_DEGLITCH);
+ select_peripheral(PIOB, extint_pin, PERIPH_A, AT32_GPIOF_DEGLITCH);
pdev->resource[1].start = EIM_IRQ_BASE + extint;
pdev->resource[1].end = pdev->resource[1].start;
@@ -1930,6 +1986,7 @@
{
struct platform_device *pdev;
struct ac97c_platform_data _data;
+ u32 pin_mask;
if (id != 0)
return NULL;
@@ -1956,10 +2013,10 @@
sizeof(struct ac97c_platform_data)))
goto fail;
- select_peripheral(PB(20), PERIPH_B, 0); /* SDO */
- select_peripheral(PB(21), PERIPH_B, 0); /* SYNC */
- select_peripheral(PB(22), PERIPH_B, 0); /* SCLK */
- select_peripheral(PB(23), PERIPH_B, 0); /* SDI */
+ pin_mask = (1 << 20) | (1 << 21); /* SDO & SYNC */
+ pin_mask |= (1 << 22) | (1 << 23); /* SCLK & SDI */
+
+ select_peripheral(PIOB, pin_mask, PERIPH_B, 0);
/* TODO: gpio_is_valid(data->reset_pin) with kernel 2.6.26. */
if (data->reset_pin != GPIO_PIN_NONE)
@@ -2001,6 +2058,7 @@
struct platform_device *__init at32_add_device_abdac(unsigned int id)
{
struct platform_device *pdev;
+ u32 pin_mask;
if (id != 0)
return NULL;
@@ -2013,10 +2071,10 @@
ARRAY_SIZE(abdac0_resource)))
goto err_add_resources;
- select_peripheral(PB(20), PERIPH_A, 0); /* DATA1 */
- select_peripheral(PB(21), PERIPH_A, 0); /* DATA0 */
- select_peripheral(PB(22), PERIPH_A, 0); /* DATAN1 */
- select_peripheral(PB(23), PERIPH_A, 0); /* DATAN0 */
+ pin_mask = (1 << 20) | (1 << 22); /* DATA1 & DATAN1 */
+ pin_mask |= (1 << 21) | (1 << 23); /* DATA0 & DATAN0 */
+
+ select_peripheral(PIOB, pin_mask, PERIPH_A, 0);
abdac0_pclk.dev = &pdev->dev;
abdac0_sample_clk.dev = &pdev->dev;
@@ -2073,7 +2131,7 @@
.index = 4,
};
-struct clk *at32_clock_list[] = {
+static __initdata struct clk *init_clocks[] = {
&osc32k,
&osc0,
&osc1,
@@ -2137,7 +2195,6 @@
&gclk3,
&gclk4,
};
-unsigned int at32_nr_clocks = ARRAY_SIZE(at32_clock_list);
void __init setup_platform(void)
{
@@ -2168,14 +2225,19 @@
genclk_init_parent(&abdac0_sample_clk);
/*
- * Turn on all clocks that have at least one user already, and
- * turn off everything else. We only do this for module
- * clocks, and even though it isn't particularly pretty to
- * check the address of the mode function, it should do the
- * trick...
+ * Build initial dynamic clock list by registering all clocks
+ * from the array.
+ * At the same time, turn on all clocks that have at least one
+ * user already, and turn off everything else. We only do this
+ * for module clocks, and even though it isn't particularly
+ * pretty to check the address of the mode function, it should
+ * do the trick...
*/
- for (i = 0; i < ARRAY_SIZE(at32_clock_list); i++) {
- struct clk *clk = at32_clock_list[i];
+ for (i = 0; i < ARRAY_SIZE(init_clocks); i++) {
+ struct clk *clk = init_clocks[i];
+
+ /* first, register clock */
+ at32_clk_register(clk);
if (clk->users == 0)
continue;
diff --git a/arch/avr32/mach-at32ap/clock.c b/arch/avr32/mach-at32ap/clock.c
index 6c27dda..138a00a 100644
--- a/arch/avr32/mach-at32ap/clock.c
+++ b/arch/avr32/mach-at32ap/clock.c
@@ -15,24 +15,40 @@
#include <linux/err.h>
#include <linux/device.h>
#include <linux/string.h>
+#include <linux/list.h>
#include <mach/chip.h>
#include "clock.h"
+/* at32 clock list */
+static LIST_HEAD(at32_clock_list);
+
static DEFINE_SPINLOCK(clk_lock);
+static DEFINE_SPINLOCK(clk_list_lock);
+
+void at32_clk_register(struct clk *clk)
+{
+ spin_lock(&clk_list_lock);
+ /* add the new item to the end of the list */
+ list_add_tail(&clk->list, &at32_clock_list);
+ spin_unlock(&clk_list_lock);
+}
struct clk *clk_get(struct device *dev, const char *id)
{
- int i;
+ struct clk *clk;
- for (i = 0; i < at32_nr_clocks; i++) {
- struct clk *clk = at32_clock_list[i];
+ spin_lock(&clk_list_lock);
- if (clk->dev == dev && strcmp(id, clk->name) == 0)
+ list_for_each_entry(clk, &at32_clock_list, list) {
+ if (clk->dev == dev && strcmp(id, clk->name) == 0) {
+ spin_unlock(&clk_list_lock);
return clk;
+ }
}
+ spin_unlock(&clk_list_lock);
return ERR_PTR(-ENOENT);
}
EXPORT_SYMBOL(clk_get);
@@ -203,8 +219,8 @@
/* cost of this scan is small, but not linear... */
r->nest = nest + NEST_DELTA;
- for (i = 3; i < at32_nr_clocks; i++) {
- clk = at32_clock_list[i];
+
+ list_for_each_entry(clk, &at32_clock_list, list) {
if (clk->parent == parent)
dump_clock(clk, r);
}
@@ -215,6 +231,7 @@
{
struct clkinf r;
int i;
+ struct clk *clk;
/* show all the power manager registers */
seq_printf(s, "MCCTRL = %8x\n", pm_readl(MCCTRL));
@@ -234,14 +251,25 @@
seq_printf(s, "\n");
- /* show clock tree as derived from the three oscillators
- * we "know" are at the head of the list
- */
r.s = s;
r.nest = 0;
- dump_clock(at32_clock_list[0], &r);
- dump_clock(at32_clock_list[1], &r);
- dump_clock(at32_clock_list[2], &r);
+ /* protected from changes on the list while dumping */
+ spin_lock(&clk_list_lock);
+
+ /* show clock tree as derived from the three oscillators */
+ clk = clk_get(NULL, "osc32k");
+ dump_clock(clk, &r);
+ clk_put(clk);
+
+ clk = clk_get(NULL, "osc0");
+ dump_clock(clk, &r);
+ clk_put(clk);
+
+ clk = clk_get(NULL, "osc1");
+ dump_clock(clk, &r);
+ clk_put(clk);
+
+ spin_unlock(&clk_list_lock);
return 0;
}
diff --git a/arch/avr32/mach-at32ap/clock.h b/arch/avr32/mach-at32ap/clock.h
index bb8e1f2..623bf0e 100644
--- a/arch/avr32/mach-at32ap/clock.h
+++ b/arch/avr32/mach-at32ap/clock.h
@@ -12,8 +12,13 @@
* published by the Free Software Foundation.
*/
#include <linux/clk.h>
+#include <linux/list.h>
+
+
+void at32_clk_register(struct clk *clk);
struct clk {
+ struct list_head list; /* linking element */
const char *name; /* Clock name/function */
struct device *dev; /* Device the clock is used by */
struct clk *parent; /* Parent clock, if any */
@@ -25,6 +30,3 @@
u16 users; /* Enabled if non-zero */
u16 index; /* Sibling index */
};
-
-extern struct clk *at32_clock_list[];
-extern unsigned int at32_nr_clocks;
diff --git a/arch/avr32/mach-at32ap/include/mach/at32ap700x.h b/arch/avr32/mach-at32ap/include/mach/at32ap700x.h
index 1e9852d..a77d372 100644
--- a/arch/avr32/mach-at32ap/include/mach/at32ap700x.h
+++ b/arch/avr32/mach-at32ap/include/mach/at32ap700x.h
@@ -83,4 +83,132 @@
#define HMATRIX_BASE 0xfff00800
#define SDRAMC_BASE 0xfff03800
+/* LCDC on port C */
+#define ATMEL_LCDC_PC_CC (1ULL << 19)
+#define ATMEL_LCDC_PC_HSYNC (1ULL << 20)
+#define ATMEL_LCDC_PC_PCLK (1ULL << 21)
+#define ATMEL_LCDC_PC_VSYNC (1ULL << 22)
+#define ATMEL_LCDC_PC_DVAL (1ULL << 23)
+#define ATMEL_LCDC_PC_MODE (1ULL << 24)
+#define ATMEL_LCDC_PC_PWR (1ULL << 25)
+#define ATMEL_LCDC_PC_DATA0 (1ULL << 26)
+#define ATMEL_LCDC_PC_DATA1 (1ULL << 27)
+#define ATMEL_LCDC_PC_DATA2 (1ULL << 28)
+#define ATMEL_LCDC_PC_DATA3 (1ULL << 29)
+#define ATMEL_LCDC_PC_DATA4 (1ULL << 30)
+#define ATMEL_LCDC_PC_DATA5 (1ULL << 31)
+
+/* LCDC on port D */
+#define ATMEL_LCDC_PD_DATA6 (1ULL << 0)
+#define ATMEL_LCDC_PD_DATA7 (1ULL << 1)
+#define ATMEL_LCDC_PD_DATA8 (1ULL << 2)
+#define ATMEL_LCDC_PD_DATA9 (1ULL << 3)
+#define ATMEL_LCDC_PD_DATA10 (1ULL << 4)
+#define ATMEL_LCDC_PD_DATA11 (1ULL << 5)
+#define ATMEL_LCDC_PD_DATA12 (1ULL << 6)
+#define ATMEL_LCDC_PD_DATA13 (1ULL << 7)
+#define ATMEL_LCDC_PD_DATA14 (1ULL << 8)
+#define ATMEL_LCDC_PD_DATA15 (1ULL << 9)
+#define ATMEL_LCDC_PD_DATA16 (1ULL << 10)
+#define ATMEL_LCDC_PD_DATA17 (1ULL << 11)
+#define ATMEL_LCDC_PD_DATA18 (1ULL << 12)
+#define ATMEL_LCDC_PD_DATA19 (1ULL << 13)
+#define ATMEL_LCDC_PD_DATA20 (1ULL << 14)
+#define ATMEL_LCDC_PD_DATA21 (1ULL << 15)
+#define ATMEL_LCDC_PD_DATA22 (1ULL << 16)
+#define ATMEL_LCDC_PD_DATA23 (1ULL << 17)
+
+/* LCDC on port E */
+#define ATMEL_LCDC_PE_CC (1ULL << (32 + 0))
+#define ATMEL_LCDC_PE_DVAL (1ULL << (32 + 1))
+#define ATMEL_LCDC_PE_MODE (1ULL << (32 + 2))
+#define ATMEL_LCDC_PE_DATA0 (1ULL << (32 + 3))
+#define ATMEL_LCDC_PE_DATA1 (1ULL << (32 + 4))
+#define ATMEL_LCDC_PE_DATA2 (1ULL << (32 + 5))
+#define ATMEL_LCDC_PE_DATA3 (1ULL << (32 + 6))
+#define ATMEL_LCDC_PE_DATA4 (1ULL << (32 + 7))
+#define ATMEL_LCDC_PE_DATA8 (1ULL << (32 + 8))
+#define ATMEL_LCDC_PE_DATA9 (1ULL << (32 + 9))
+#define ATMEL_LCDC_PE_DATA10 (1ULL << (32 + 10))
+#define ATMEL_LCDC_PE_DATA11 (1ULL << (32 + 11))
+#define ATMEL_LCDC_PE_DATA12 (1ULL << (32 + 12))
+#define ATMEL_LCDC_PE_DATA16 (1ULL << (32 + 13))
+#define ATMEL_LCDC_PE_DATA17 (1ULL << (32 + 14))
+#define ATMEL_LCDC_PE_DATA18 (1ULL << (32 + 15))
+#define ATMEL_LCDC_PE_DATA19 (1ULL << (32 + 16))
+#define ATMEL_LCDC_PE_DATA20 (1ULL << (32 + 17))
+#define ATMEL_LCDC_PE_DATA21 (1ULL << (32 + 18))
+
+
+#define ATMEL_LCDC(PORT, PIN) (ATMEL_LCDC_##PORT##_##PIN)
+
+
+#define ATMEL_LCDC_PRI_24B_DATA ( \
+ ATMEL_LCDC(PC, DATA0) | ATMEL_LCDC(PC, DATA1) | \
+ ATMEL_LCDC(PC, DATA2) | ATMEL_LCDC(PC, DATA3) | \
+ ATMEL_LCDC(PC, DATA4) | ATMEL_LCDC(PC, DATA5) | \
+ ATMEL_LCDC(PD, DATA6) | ATMEL_LCDC(PD, DATA7) | \
+ ATMEL_LCDC(PD, DATA8) | ATMEL_LCDC(PD, DATA9) | \
+ ATMEL_LCDC(PD, DATA10) | ATMEL_LCDC(PD, DATA11) | \
+ ATMEL_LCDC(PD, DATA12) | ATMEL_LCDC(PD, DATA13) | \
+ ATMEL_LCDC(PD, DATA14) | ATMEL_LCDC(PD, DATA15) | \
+ ATMEL_LCDC(PD, DATA16) | ATMEL_LCDC(PD, DATA17) | \
+ ATMEL_LCDC(PD, DATA18) | ATMEL_LCDC(PD, DATA19) | \
+ ATMEL_LCDC(PD, DATA20) | ATMEL_LCDC(PD, DATA21) | \
+ ATMEL_LCDC(PD, DATA22) | ATMEL_LCDC(PD, DATA23))
+
+#define ATMEL_LCDC_ALT_24B_DATA ( \
+ ATMEL_LCDC(PE, DATA0) | ATMEL_LCDC(PE, DATA1) | \
+ ATMEL_LCDC(PE, DATA2) | ATMEL_LCDC(PE, DATA3) | \
+ ATMEL_LCDC(PE, DATA4) | ATMEL_LCDC(PC, DATA5) | \
+ ATMEL_LCDC(PD, DATA6) | ATMEL_LCDC(PD, DATA7) | \
+ ATMEL_LCDC(PE, DATA8) | ATMEL_LCDC(PE, DATA9) | \
+ ATMEL_LCDC(PE, DATA10) | ATMEL_LCDC(PE, DATA11) | \
+ ATMEL_LCDC(PE, DATA12) | ATMEL_LCDC(PD, DATA13) | \
+ ATMEL_LCDC(PD, DATA14) | ATMEL_LCDC(PD, DATA15) | \
+ ATMEL_LCDC(PE, DATA16) | ATMEL_LCDC(PE, DATA17) | \
+ ATMEL_LCDC(PE, DATA18) | ATMEL_LCDC(PE, DATA19) | \
+ ATMEL_LCDC(PE, DATA20) | ATMEL_LCDC(PE, DATA21) | \
+ ATMEL_LCDC(PD, DATA22) | ATMEL_LCDC(PD, DATA23))
+
+#define ATMEL_LCDC_PRI_15B_DATA ( \
+ ATMEL_LCDC(PC, DATA0) | ATMEL_LCDC(PC, DATA1) | \
+ ATMEL_LCDC(PC, DATA2) | ATMEL_LCDC(PC, DATA3) | \
+ ATMEL_LCDC(PC, DATA4) | ATMEL_LCDC(PC, DATA5) | \
+ ATMEL_LCDC(PD, DATA8) | ATMEL_LCDC(PD, DATA9) | \
+ ATMEL_LCDC(PD, DATA10) | ATMEL_LCDC(PD, DATA11) | \
+ ATMEL_LCDC(PD, DATA12) | ATMEL_LCDC(PD, DATA16) | \
+ ATMEL_LCDC(PD, DATA17) | ATMEL_LCDC(PD, DATA18) | \
+ ATMEL_LCDC(PD, DATA19) | ATMEL_LCDC(PD, DATA20))
+
+#define ATMEL_LCDC_ALT_15B_DATA ( \
+ ATMEL_LCDC(PE, DATA0) | ATMEL_LCDC(PE, DATA1) | \
+ ATMEL_LCDC(PE, DATA2) | ATMEL_LCDC(PE, DATA3) | \
+ ATMEL_LCDC(PE, DATA4) | ATMEL_LCDC(PC, DATA5) | \
+ ATMEL_LCDC(PE, DATA8) | ATMEL_LCDC(PE, DATA9) | \
+ ATMEL_LCDC(PE, DATA10) | ATMEL_LCDC(PE, DATA11) | \
+ ATMEL_LCDC(PE, DATA12) | ATMEL_LCDC(PE, DATA16) | \
+ ATMEL_LCDC(PE, DATA17) | ATMEL_LCDC(PE, DATA18) | \
+ ATMEL_LCDC(PE, DATA19) | ATMEL_LCDC(PE, DATA20))
+
+#define ATMEL_LCDC_PRI_CONTROL ( \
+ ATMEL_LCDC(PC, CC) | ATMEL_LCDC(PC, DVAL) | \
+ ATMEL_LCDC(PC, MODE) | ATMEL_LCDC(PC, PWR))
+
+#define ATMEL_LCDC_ALT_CONTROL ( \
+ ATMEL_LCDC(PE, CC) | ATMEL_LCDC(PE, DVAL) | \
+ ATMEL_LCDC(PE, MODE) | ATMEL_LCDC(PC, PWR))
+
+#define ATMEL_LCDC_CONTROL ( \
+ ATMEL_LCDC(PC, HSYNC) | ATMEL_LCDC(PC, VSYNC) | \
+ ATMEL_LCDC(PC, PCLK))
+
+#define ATMEL_LCDC_PRI_24BIT (ATMEL_LCDC_CONTROL | ATMEL_LCDC_PRI_24B_DATA)
+
+#define ATMEL_LCDC_ALT_24BIT (ATMEL_LCDC_CONTROL | ATMEL_LCDC_ALT_24B_DATA)
+
+#define ATMEL_LCDC_PRI_15BIT (ATMEL_LCDC_CONTROL | ATMEL_LCDC_PRI_15B_DATA)
+
+#define ATMEL_LCDC_ALT_15BIT (ATMEL_LCDC_CONTROL | ATMEL_LCDC_ALT_15B_DATA)
+
#endif /* __ASM_ARCH_AT32AP700X_H__ */
diff --git a/arch/avr32/mach-at32ap/include/mach/board.h b/arch/avr32/mach-at32ap/include/mach/board.h
index e60e907..c48386d 100644
--- a/arch/avr32/mach-at32ap/include/mach/board.h
+++ b/arch/avr32/mach-at32ap/include/mach/board.h
@@ -43,7 +43,7 @@
struct platform_device *
at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data,
unsigned long fbmem_start, unsigned long fbmem_len,
- unsigned int pin_config);
+ u64 pin_mask);
struct usba_platform_data;
struct platform_device *
diff --git a/arch/avr32/mach-at32ap/include/mach/io.h b/arch/avr32/mach-at32ap/include/mach/io.h
index 4ec6abc..22ea79b 100644
--- a/arch/avr32/mach-at32ap/include/mach/io.h
+++ b/arch/avr32/mach-at32ap/include/mach/io.h
@@ -1,8 +1,7 @@
#ifndef __ASM_AVR32_ARCH_AT32AP_IO_H
#define __ASM_AVR32_ARCH_AT32AP_IO_H
-/* For "bizarre" halfword swapping */
-#include <linux/byteorder/swabb.h>
+#include <linux/swab.h>
#if defined(CONFIG_AP700X_32_BIT_SMC)
# define __swizzle_addr_b(addr) (addr ^ 3UL)
diff --git a/arch/avr32/mach-at32ap/include/mach/portmux.h b/arch/avr32/mach-at32ap/include/mach/portmux.h
index b1abe6b..21c7937 100644
--- a/arch/avr32/mach-at32ap/include/mach/portmux.h
+++ b/arch/avr32/mach-at32ap/include/mach/portmux.h
@@ -21,9 +21,10 @@
#define AT32_GPIOF_DEGLITCH 0x00000008 /* (IN) Filter glitches */
#define AT32_GPIOF_MULTIDRV 0x00000010 /* Enable multidriver option */
-void at32_select_periph(unsigned int pin, unsigned int periph,
- unsigned long flags);
+void at32_select_periph(unsigned int port, unsigned int pin,
+ unsigned int periph, unsigned long flags);
void at32_select_gpio(unsigned int pin, unsigned long flags);
+void at32_deselect_pin(unsigned int pin);
void at32_reserve_pin(unsigned int pin);
#endif /* __ASM_ARCH_PORTMUX_H__ */
diff --git a/arch/avr32/mach-at32ap/pdc.c b/arch/avr32/mach-at32ap/pdc.c
index 1040bda..61ab15a 100644
--- a/arch/avr32/mach-at32ap/pdc.c
+++ b/arch/avr32/mach-at32ap/pdc.c
@@ -35,7 +35,6 @@
}
static struct platform_driver pdc_driver = {
- .probe = pdc_probe,
.driver = {
.name = "pdc",
},
@@ -43,6 +42,6 @@
static int __init pdc_init(void)
{
- return platform_driver_register(&pdc_driver);
+ return platform_driver_probe(&pdc_driver, pdc_probe);
}
arch_initcall(pdc_init);
diff --git a/arch/avr32/mach-at32ap/pio.c b/arch/avr32/mach-at32ap/pio.c
index 405ee6b..ed81a8b 100644
--- a/arch/avr32/mach-at32ap/pio.c
+++ b/arch/avr32/mach-at32ap/pio.c
@@ -50,35 +50,48 @@
}
/* Pin multiplexing API */
+static DEFINE_SPINLOCK(pio_lock);
-void __init at32_select_periph(unsigned int pin, unsigned int periph,
- unsigned long flags)
+void __init at32_select_periph(unsigned int port, u32 pin_mask,
+ unsigned int periph, unsigned long flags)
{
struct pio_device *pio;
- unsigned int pin_index = pin & 0x1f;
- u32 mask = 1 << pin_index;
- pio = gpio_to_pio(pin);
+ /* assign and verify pio */
+ pio = gpio_to_pio(port);
if (unlikely(!pio)) {
- printk("pio: invalid pin %u\n", pin);
+ printk(KERN_WARNING "pio: invalid port %u\n", port);
goto fail;
}
- if (unlikely(test_and_set_bit(pin_index, &pio->pinmux_mask)
- || gpiochip_is_requested(&pio->chip, pin_index))) {
- printk("%s: pin %u is busy\n", pio->name, pin_index);
+ /* Test if any of the requested pins is already muxed */
+ spin_lock(&pio_lock);
+ if (unlikely(pio->pinmux_mask & pin_mask)) {
+ printk(KERN_WARNING "%s: pin(s) busy (requested 0x%x, busy 0x%x)\n",
+ pio->name, pin_mask, pio->pinmux_mask & pin_mask);
+ spin_unlock(&pio_lock);
goto fail;
}
- pio_writel(pio, PUER, mask);
+ pio->pinmux_mask |= pin_mask;
+
+ /* enable pull ups */
+ pio_writel(pio, PUER, pin_mask);
+
+ /* select either peripheral A or B */
if (periph)
- pio_writel(pio, BSR, mask);
+ pio_writel(pio, BSR, pin_mask);
else
- pio_writel(pio, ASR, mask);
+ pio_writel(pio, ASR, pin_mask);
- pio_writel(pio, PDR, mask);
+ /* enable peripheral control */
+ pio_writel(pio, PDR, pin_mask);
+
+ /* Disable pull ups if not requested. */
if (!(flags & AT32_GPIOF_PULLUP))
- pio_writel(pio, PUDR, mask);
+ pio_writel(pio, PUDR, pin_mask);
+
+ spin_unlock(&pio_lock);
return;
@@ -134,6 +147,25 @@
dump_stack();
}
+/*
+ * Undo a previous pin reservation. Will not affect the hardware
+ * configuration.
+ */
+void at32_deselect_pin(unsigned int pin)
+{
+ struct pio_device *pio;
+ unsigned int pin_index = pin & 0x1f;
+
+ pio = gpio_to_pio(pin);
+ if (unlikely(!pio)) {
+ printk("pio: invalid pin %u\n", pin);
+ dump_stack();
+ return;
+ }
+
+ clear_bit(pin_index, &pio->pinmux_mask);
+}
+
/* Reserve a pin, preventing anyone else from changing its configuration. */
void __init at32_reserve_pin(unsigned int pin)
{
@@ -382,7 +414,6 @@
}
static struct platform_driver pio_driver = {
- .probe = pio_probe,
.driver = {
.name = "pio",
},
@@ -390,7 +421,7 @@
static int __init pio_init(void)
{
- return platform_driver_register(&pio_driver);
+ return platform_driver_probe(&pio_driver, pio_probe);
}
postcore_initcall(pio_init);
diff --git a/arch/avr32/oprofile/Makefile b/arch/avr32/oprofile/Makefile
index 1fe81c3..e0eb520 100644
--- a/arch/avr32/oprofile/Makefile
+++ b/arch/avr32/oprofile/Makefile
@@ -5,4 +5,4 @@
event_buffer.o oprofile_files.o \
oprofilefs.o oprofile_stats.o \
timer_int.o)
-oprofile-y += op_model_avr32.o
+oprofile-y += op_model_avr32.o backtrace.o
diff --git a/arch/avr32/oprofile/backtrace.c b/arch/avr32/oprofile/backtrace.c
new file mode 100644
index 0000000..75d9ad6
--- /dev/null
+++ b/arch/avr32/oprofile/backtrace.c
@@ -0,0 +1,81 @@
+/*
+ * AVR32 specific backtracing code for oprofile
+ *
+ * Copyright 2008 Weinmann GmbH
+ *
+ * Author: Nikolaus Voss <n.voss@weinmann.de>
+ *
+ * Based on i386 oprofile backtrace code by John Levon and David Smith
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/oprofile.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+
+/* The first two words of each frame on the stack look like this if we have
+ * frame pointers */
+struct frame_head {
+ unsigned long lr;
+ struct frame_head *fp;
+};
+
+/* copied from arch/avr32/kernel/process.c */
+static inline int valid_stack_ptr(struct thread_info *tinfo, unsigned long p)
+{
+ return (p > (unsigned long)tinfo)
+ && (p < (unsigned long)tinfo + THREAD_SIZE - 3);
+}
+
+/* copied from arch/x86/oprofile/backtrace.c */
+static struct frame_head *dump_user_backtrace(struct frame_head *head)
+{
+ struct frame_head bufhead[2];
+
+ /* Also check accessibility of one struct frame_head beyond */
+ if (!access_ok(VERIFY_READ, head, sizeof(bufhead)))
+ return NULL;
+ if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead)))
+ return NULL;
+
+ oprofile_add_trace(bufhead[0].lr);
+
+ /* frame pointers should strictly progress back up the stack
+ * (towards higher addresses) */
+ if (bufhead[0].fp <= head)
+ return NULL;
+
+ return bufhead[0].fp;
+}
+
+void avr32_backtrace(struct pt_regs * const regs, unsigned int depth)
+{
+ /* Get first frame pointer */
+ struct frame_head *head = (struct frame_head *)(regs->r7);
+
+ if (!user_mode(regs)) {
+#ifdef CONFIG_FRAME_POINTER
+ /*
+ * Traverse the kernel stack from frame to frame up to
+ * "depth" steps.
+ */
+ while (depth-- && valid_stack_ptr(task_thread_info(current),
+ (unsigned long)head)) {
+ oprofile_add_trace(head->lr);
+ if (head->fp <= head)
+ break;
+ head = head->fp;
+ }
+#endif
+ } else {
+ /* Assume we have frame pointers in user mode process */
+ while (depth-- && head)
+ head = dump_user_backtrace(head);
+ }
+}
+
+
diff --git a/arch/avr32/oprofile/op_model_avr32.c b/arch/avr32/oprofile/op_model_avr32.c
index df42325..a3e9b3c 100644
--- a/arch/avr32/oprofile/op_model_avr32.c
+++ b/arch/avr32/oprofile/op_model_avr32.c
@@ -22,6 +22,8 @@
#define AVR32_PERFCTR_IRQ_GROUP 0
#define AVR32_PERFCTR_IRQ_LINE 1
+void avr32_backtrace(struct pt_regs * const regs, unsigned int depth);
+
enum { PCCNT, PCNT0, PCNT1, NR_counter };
struct avr32_perf_counter {
@@ -223,6 +225,8 @@
memcpy(ops, &avr32_perf_counter_ops,
sizeof(struct oprofile_operations));
+ ops->backtrace = avr32_backtrace;
+
printk(KERN_INFO "oprofile: using AVR32 performance monitoring.\n");
return 0;
diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig
index 4154ff1..8102c79 100644
--- a/arch/blackfin/Kconfig
+++ b/arch/blackfin/Kconfig
@@ -162,16 +162,28 @@
config BF561
bool "BF561"
help
- Not Supported Yet - Work in progress - BF561 Processor Support.
+ BF561 Processor Support.
endchoice
+config BF_REV_MIN
+ int
+ default 0 if (BF52x || BF54x)
+ default 2 if (BF537 || BF536 || BF534)
+ default 3 if (BF561 ||BF533 || BF532 || BF531)
+
+config BF_REV_MAX
+ int
+ default 2 if (BF52x || BF54x)
+ default 3 if (BF537 || BF536 || BF534)
+ default 5 if (BF561)
+ default 6 if (BF533 || BF532 || BF531)
+
choice
prompt "Silicon Rev"
- default BF_REV_0_1 if BF527
- default BF_REV_0_2 if BF537
- default BF_REV_0_3 if BF533
- default BF_REV_0_0 if BF549
+ default BF_REV_0_1 if (BF52x || BF54x)
+ default BF_REV_0_2 if (BF534 || BF536 || BF537)
+ default BF_REV_0_3 if (BF531 || BF532 || BF533 || BF561)
config BF_REV_0_0
bool "0.0"
@@ -183,7 +195,7 @@
config BF_REV_0_2
bool "0.2"
- depends on (BF537 || BF536 || BF534)
+ depends on (BF52x || BF537 || BF536 || BF534 || BF54x)
config BF_REV_0_3
bool "0.3"
@@ -197,6 +209,10 @@
bool "0.5"
depends on (BF561 || BF533 || BF532 || BF531)
+config BF_REV_0_6
+ bool "0.6"
+ depends on (BF533 || BF532 || BF531)
+
config BF_REV_ANY
bool "any"
@@ -249,7 +265,7 @@
config MEM_MT48LC32M16A2TG_75
bool
- depends on (BFIN527_EZKIT || BFIN532_IP0X || BLACKSTAMP)
+ depends on (BFIN527_EZKIT || BFIN532_IP0X || BLACKSTAMP || BFIN526_EZBRD)
default y
source "arch/blackfin/mach-bf527/Kconfig"
@@ -286,13 +302,20 @@
memory region is used to capture NULL pointer references as well
as some core kernel functions.
+config ROM_BASE
+ hex "Kernel ROM Base"
+ default "0x20040000"
+ range 0x20000000 0x20400000 if !(BF54x || BF561)
+ range 0x20000000 0x30000000 if (BF54x || BF561)
+ help
+
comment "Clock/PLL Setup"
config CLKIN_HZ
int "Frequency of the crystal on the board in Hz"
default "11059200" if BFIN533_STAMP
default "27000000" if BFIN533_EZKIT
- default "25000000" if (BFIN537_STAMP || BFIN527_EZKIT || H8606_HVSISTEMAS || BLACKSTAMP)
+ default "25000000" if (BFIN537_STAMP || BFIN527_EZKIT || H8606_HVSISTEMAS || BLACKSTAMP || BFIN526_EZBRD)
default "30000000" if BFIN561_EZKIT
default "24576000" if PNAV10
default "10000000" if BFIN532_IP0X
@@ -332,7 +355,7 @@
default "22" if BFIN533_BLUETECHNIX_CM
default "20" if (BFIN537_BLUETECHNIX_CM || BFIN527_BLUETECHNIX_CM || BFIN561_BLUETECHNIX_CM)
default "20" if BFIN561_EZKIT
- default "16" if (H8606_HVSISTEMAS || BLACKSTAMP)
+ default "16" if (H8606_HVSISTEMAS || BLACKSTAMP || BFIN526_EZBRD)
help
This controls the frequency of the on-chip PLL. This can be between 1 and 64.
PLL Frequency = (Crystal Frequency) * (this setting)
@@ -368,14 +391,6 @@
This can be between 1 and 15
System Clock = (PLL frequency) / (this setting)
-config MAX_MEM_SIZE
- int "Max SDRAM Memory Size in MBytes"
- depends on !MPU
- default 512
- help
- This is the max memory size that the kernel will create CPLB
- tables for. Your system will not be able to handle any more.
-
choice
prompt "DDR SDRAM Chip Type"
depends on BFIN_KERNEL_CLOCK
@@ -389,6 +404,14 @@
bool "MT46V32M16_5B"
endchoice
+config MAX_MEM_SIZE
+ int "Max SDRAM Memory Size in MBytes"
+ depends on !MPU
+ default 512
+ help
+ This is the max memory size that the kernel will create CPLB
+ tables for. Your system will not be able to handle any more.
+
#
# Max & Min Speeds for various Chips
#
@@ -455,8 +478,6 @@
source kernel/time/Kconfig
-comment "Memory Setup"
-
comment "Misc"
choice
@@ -622,6 +643,15 @@
If enabled, the CPLB Switch Tables are linked
into L1 data memory. (less latency)
+config APP_STACK_L1
+ bool "Support locating application stack in L1 Scratch Memory"
+ default y
+ help
+ If enabled the application stack can be located in L1
+ scratch memory (less latency).
+
+ Currently only works with FLAT binaries.
+
comment "Speed Optimizations"
config BFIN_INS_LOWOVERHEAD
bool "ins[bwl] low overhead, higher interrupt latency"
@@ -755,6 +785,13 @@
endchoice
+config BFIN_L2_CACHEABLE
+ bool "Cache L2 SRAM"
+ depends on (BFIN_DCACHE || BFIN_ICACHE) && (BF54x || BF561)
+ default n
+ help
+ Select to make L2 SRAM cacheable in L1 data and instruction cache.
+
config MPU
bool "Enable the memory protection unit (EXPERIMENTAL)"
default n
diff --git a/arch/blackfin/Kconfig.debug b/arch/blackfin/Kconfig.debug
index c468624..3ad2598 100644
--- a/arch/blackfin/Kconfig.debug
+++ b/arch/blackfin/Kconfig.debug
@@ -2,6 +2,22 @@
source "lib/Kconfig.debug"
+config HAVE_ARCH_KGDB
+ def_bool y
+
+config DEBUG_VERBOSE
+ bool "Verbose fault messages"
+ default y
+ select PRINTK
+ help
+ When a program crashes due to an exception, or the kernel detects
+ an internal error, the kernel can print a not so brief message
+ explaining what the problem was. This debugging information is
+ useful to developers and kernel hackers when tracking down problems,
+ but mostly meaningless to other people. This is always helpful for
+ debugging but serves no purpose on a production system.
+ Most people should say N here.
+
config DEBUG_MMRS
bool "Generate Blackfin MMR tree"
select DEBUG_FS
@@ -22,6 +38,44 @@
hardware error interrupts and need to know where they are coming
from.
+config DEBUG_DOUBLEFAULT
+ bool "Debug Double Faults"
+ default n
+ help
+ If an exception is caused while executing code within the exception
+ handler, the NMI handler, the reset vector, or in emulator mode,
+ a double fault occurs. On the Blackfin, this is a unrecoverable
+ event. You have two options:
+ - RESET exactly when double fault occurs. The excepting
+ instruction address is stored in RETX, where the next kernel
+ boot will print it out.
+ - Print debug message. This is much more error prone, although
+ easier to handle. It is error prone since:
+ - The excepting instruction is not committed.
+ - All writebacks from the instruction are prevented.
+ - The generated exception is not taken.
+ - The EXCAUSE field is updated with an unrecoverable event
+ The only way to check this is to see if EXCAUSE contains the
+ unrecoverable event value at every exception return. By selecting
+ this option, you are skipping over the faulting instruction, and
+ hoping things stay together enough to print out a debug message.
+
+ This does add a little kernel code, but is the only method to debug
+ double faults - if unsure say "Y"
+
+choice
+ prompt "Double Fault Failure Method"
+ default DEBUG_DOUBLEFAULT_PRINT
+ depends on DEBUG_DOUBLEFAULT
+
+config DEBUG_DOUBLEFAULT_PRINT
+ bool "Print"
+
+config DEBUG_DOUBLEFAULT_RESET
+ bool "Reset"
+
+endchoice
+
config DEBUG_ICACHE_CHECK
bool "Check Instruction cache coherency"
depends on DEBUG_KERNEL
@@ -143,6 +197,7 @@
config EARLY_PRINTK
bool "Early printk"
default n
+ select SERIAL_CORE_CONSOLE
help
This option enables special console drivers which allow the kernel
to print messages very early in the bootup process.
diff --git a/arch/blackfin/Makefile b/arch/blackfin/Makefile
index eac0533..6bf5097 100644
--- a/arch/blackfin/Makefile
+++ b/arch/blackfin/Makefile
@@ -67,6 +67,7 @@
rev-$(CONFIG_BF_REV_0_3) := 0.3
rev-$(CONFIG_BF_REV_0_4) := 0.4
rev-$(CONFIG_BF_REV_0_5) := 0.5
+rev-$(CONFIG_BF_REV_0_6) := 0.6
rev-$(CONFIG_BF_REV_NONE) := none
rev-$(CONFIG_BF_REV_ANY) := any
diff --git a/arch/blackfin/configs/BF526-EZBRD_defconfig b/arch/blackfin/configs/BF526-EZBRD_defconfig
new file mode 100644
index 0000000..c33bf6f
--- /dev/null
+++ b/arch/blackfin/configs/BF526-EZBRD_defconfig
@@ -0,0 +1,1427 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.26.3
+# Thu Aug 28 16:49:53 2008
+#
+# CONFIG_MMU is not set
+# CONFIG_FPU is not set
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set
+CONFIG_BLACKFIN=y
+CONFIG_ZONE_DMA=y
+CONFIG_GENERIC_FIND_NEXT_BIT=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_GENERIC_GPIO=y
+CONFIG_FORCE_MAX_ZONEORDER=14
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+CONFIG_LOCALVERSION_AUTO=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_AUDIT is not set
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+# CONFIG_GROUP_SCHED is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+# CONFIG_RELAY is not set
+# CONFIG_NAMESPACES is not set
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_COMPAT_BRK=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+# CONFIG_MARKERS is not set
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_HAVE_KPROBES is not set
+# CONFIG_HAVE_KRETPROBES is not set
+# CONFIG_HAVE_DMA_ATTRS is not set
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+CONFIG_TINY_SHMEM=y
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+# CONFIG_BLK_DEV_BSG is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+# CONFIG_IOSCHED_AS is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+# CONFIG_DEFAULT_AS is not set
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+CONFIG_DEFAULT_NOOP=y
+CONFIG_DEFAULT_IOSCHED="noop"
+CONFIG_CLASSIC_RCU=y
+# CONFIG_PREEMPT_NONE is not set
+CONFIG_PREEMPT_VOLUNTARY=y
+# CONFIG_PREEMPT is not set
+
+#
+# Blackfin Processor Options
+#
+
+#
+# Processor and Board Settings
+#
+# CONFIG_BF522 is not set
+# CONFIG_BF523 is not set
+# CONFIG_BF524 is not set
+# CONFIG_BF525 is not set
+CONFIG_BF526=y
+# CONFIG_BF527 is not set
+# CONFIG_BF531 is not set
+# CONFIG_BF532 is not set
+# CONFIG_BF533 is not set
+# CONFIG_BF534 is not set
+# CONFIG_BF536 is not set
+# CONFIG_BF537 is not set
+# CONFIG_BF542 is not set
+# CONFIG_BF544 is not set
+# CONFIG_BF547 is not set
+# CONFIG_BF548 is not set
+# CONFIG_BF549 is not set
+# CONFIG_BF561 is not set
+CONFIG_BF_REV_0_0=y
+# CONFIG_BF_REV_0_1 is not set
+# CONFIG_BF_REV_0_2 is not set
+# CONFIG_BF_REV_0_3 is not set
+# CONFIG_BF_REV_0_4 is not set
+# CONFIG_BF_REV_0_5 is not set
+# CONFIG_BF_REV_ANY is not set
+# CONFIG_BF_REV_NONE is not set
+CONFIG_BF52x=y
+CONFIG_MEM_MT48LC32M16A2TG_75=y
+# CONFIG_BFIN527_EZKIT is not set
+# CONFIG_BFIN527_BLUETECHNIX_CM is not set
+CONFIG_BFIN526_EZBRD=y
+
+#
+# BF527 Specific Configuration
+#
+
+#
+# Alternative Multiplexing Scheme
+#
+# CONFIG_BF527_SPORT0_PORTF is not set
+CONFIG_BF527_SPORT0_PORTG=y
+CONFIG_BF527_SPORT0_TSCLK_PG10=y
+# CONFIG_BF527_SPORT0_TSCLK_PG14 is not set
+CONFIG_BF527_UART1_PORTF=y
+# CONFIG_BF527_UART1_PORTG is not set
+# CONFIG_BF527_NAND_D_PORTF is not set
+CONFIG_BF527_NAND_D_PORTH=y
+
+#
+# Interrupt Priority Assignment
+#
+
+#
+# Priority
+#
+CONFIG_IRQ_PLL_WAKEUP=7
+CONFIG_IRQ_DMA0_ERROR=7
+CONFIG_IRQ_DMAR0_BLK=7
+CONFIG_IRQ_DMAR1_BLK=7
+CONFIG_IRQ_DMAR0_OVR=7
+CONFIG_IRQ_DMAR1_OVR=7
+CONFIG_IRQ_PPI_ERROR=7
+CONFIG_IRQ_MAC_ERROR=7
+CONFIG_IRQ_SPORT0_ERROR=7
+CONFIG_IRQ_SPORT1_ERROR=7
+CONFIG_IRQ_UART0_ERROR=7
+CONFIG_IRQ_UART1_ERROR=7
+CONFIG_IRQ_RTC=8
+CONFIG_IRQ_PPI=8
+CONFIG_IRQ_SPORT0_RX=9
+CONFIG_IRQ_SPORT0_TX=9
+CONFIG_IRQ_SPORT1_RX=9
+CONFIG_IRQ_SPORT1_TX=9
+CONFIG_IRQ_TWI=10
+CONFIG_IRQ_SPI=10
+CONFIG_IRQ_UART0_RX=10
+CONFIG_IRQ_UART0_TX=10
+CONFIG_IRQ_UART1_RX=10
+CONFIG_IRQ_UART1_TX=10
+CONFIG_IRQ_OPTSEC=11
+CONFIG_IRQ_CNT=11
+CONFIG_IRQ_MAC_RX=11
+CONFIG_IRQ_PORTH_INTA=11
+CONFIG_IRQ_MAC_TX=11
+CONFIG_IRQ_PORTH_INTB=11
+CONFIG_IRQ_TMR0=12
+CONFIG_IRQ_TMR1=12
+CONFIG_IRQ_TMR2=12
+CONFIG_IRQ_TMR3=12
+CONFIG_IRQ_TMR4=12
+CONFIG_IRQ_TMR5=12
+CONFIG_IRQ_TMR6=12
+CONFIG_IRQ_TMR7=12
+CONFIG_IRQ_PORTG_INTA=12
+CONFIG_IRQ_PORTG_INTB=12
+CONFIG_IRQ_MEM_DMA0=13
+CONFIG_IRQ_MEM_DMA1=13
+CONFIG_IRQ_WATCH=13
+CONFIG_IRQ_PORTF_INTA=13
+CONFIG_IRQ_PORTF_INTB=13
+CONFIG_IRQ_SPI_ERROR=7
+CONFIG_IRQ_NFC_ERROR=7
+CONFIG_IRQ_HDMA_ERROR=7
+CONFIG_IRQ_HDMA=7
+CONFIG_IRQ_USB_EINT=10
+CONFIG_IRQ_USB_INT0=11
+CONFIG_IRQ_USB_INT1=11
+CONFIG_IRQ_USB_INT2=11
+CONFIG_IRQ_USB_DMA=11
+
+#
+# Board customizations
+#
+# CONFIG_CMDLINE_BOOL is not set
+CONFIG_BOOT_LOAD=0x1000
+
+#
+# Clock/PLL Setup
+#
+CONFIG_CLKIN_HZ=25000000
+# CONFIG_BFIN_KERNEL_CLOCK is not set
+CONFIG_MAX_MEM_SIZE=512
+CONFIG_MAX_VCO_HZ=400000000
+CONFIG_MIN_VCO_HZ=50000000
+CONFIG_MAX_SCLK_HZ=133333333
+CONFIG_MIN_SCLK_HZ=27000000
+
+#
+# Kernel Timer/Scheduler
+#
+# CONFIG_HZ_100 is not set
+CONFIG_HZ_250=y
+# CONFIG_HZ_300 is not set
+# CONFIG_HZ_1000 is not set
+CONFIG_HZ=250
+# CONFIG_SCHED_HRTICK is not set
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+# CONFIG_CYCLES_CLOCKSOURCE is not set
+# CONFIG_TICK_ONESHOT is not set
+# CONFIG_NO_HZ is not set
+# CONFIG_HIGH_RES_TIMERS is not set
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+
+#
+# Memory Setup
+#
+
+#
+# Misc
+#
+CONFIG_BFIN_SCRATCH_REG_RETN=y
+# CONFIG_BFIN_SCRATCH_REG_RETE is not set
+# CONFIG_BFIN_SCRATCH_REG_CYCLES is not set
+
+#
+# Blackfin Kernel Optimizations
+#
+
+#
+# Memory Optimizations
+#
+CONFIG_I_ENTRY_L1=y
+CONFIG_EXCPT_IRQ_SYSC_L1=y
+CONFIG_DO_IRQ_L1=y
+CONFIG_CORE_TIMER_IRQ_L1=y
+CONFIG_IDLE_L1=y
+# CONFIG_SCHEDULE_L1 is not set
+CONFIG_ARITHMETIC_OPS_L1=y
+CONFIG_ACCESS_OK_L1=y
+# CONFIG_MEMSET_L1 is not set
+# CONFIG_MEMCPY_L1 is not set
+# CONFIG_SYS_BFIN_SPINLOCK_L1 is not set
+# CONFIG_IP_CHECKSUM_L1 is not set
+CONFIG_CACHELINE_ALIGNED_L1=y
+# CONFIG_SYSCALL_TAB_L1 is not set
+# CONFIG_CPLB_SWITCH_TAB_L1 is not set
+
+#
+# Speed Optimizations
+#
+CONFIG_BFIN_INS_LOWOVERHEAD=y
+CONFIG_RAMKERNEL=y
+# CONFIG_ROMKERNEL is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+# CONFIG_SPARSEMEM_STATIC is not set
+# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_RESOURCES_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=1
+CONFIG_VIRT_TO_BUS=y
+CONFIG_BFIN_GPTIMERS=y
+CONFIG_BFIN_DMA_5XX=y
+# CONFIG_DMA_UNCACHED_4M is not set
+# CONFIG_DMA_UNCACHED_2M is not set
+CONFIG_DMA_UNCACHED_1M=y
+# CONFIG_DMA_UNCACHED_NONE is not set
+
+#
+# Cache Support
+#
+CONFIG_BFIN_ICACHE=y
+CONFIG_BFIN_DCACHE=y
+# CONFIG_BFIN_DCACHE_BANKA is not set
+# CONFIG_BFIN_ICACHE_LOCK is not set
+CONFIG_BFIN_WB=y
+# CONFIG_BFIN_WT is not set
+# CONFIG_MPU is not set
+
+#
+# Asynchonous Memory Configuration
+#
+
+#
+# EBIU_AMGCTL Global Control
+#
+CONFIG_C_AMCKEN=y
+CONFIG_C_CDPRIO=y
+# CONFIG_C_AMBEN is not set
+# CONFIG_C_AMBEN_B0 is not set
+# CONFIG_C_AMBEN_B0_B1 is not set
+# CONFIG_C_AMBEN_B0_B1_B2 is not set
+CONFIG_C_AMBEN_ALL=y
+
+#
+# EBIU_AMBCTL Control
+#
+CONFIG_BANK_0=0x7BB0
+CONFIG_BANK_1=0x5554
+CONFIG_BANK_2=0x7BB0
+CONFIG_BANK_3=0xFFC0
+
+#
+# Bus options (PCI, PCMCIA, EISA, MCA, ISA)
+#
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Executable file formats
+#
+CONFIG_BINFMT_ELF_FDPIC=y
+CONFIG_BINFMT_FLAT=y
+CONFIG_BINFMT_ZFLAT=y
+# CONFIG_BINFMT_SHARED_FLAT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options
+#
+# CONFIG_PM is not set
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+# CONFIG_PM_WAKEUP_BY_GPIO is not set
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+
+#
+# Networking
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+# CONFIG_XFRM_USER is not set
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_XFRM_STATISTICS is not set
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+# CONFIG_IP_PNP_DHCP is not set
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+# CONFIG_INET_TUNNEL is not set
+CONFIG_INET_XFRM_MODE_TRANSPORT=y
+CONFIG_INET_XFRM_MODE_TUNNEL=y
+CONFIG_INET_XFRM_MODE_BEET=y
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG=y
+CONFIG_INET_TCP_DIAG=y
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IPV6 is not set
+# CONFIG_NETLABEL is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+
+#
+# Wireless
+#
+# CONFIG_CFG80211 is not set
+# CONFIG_WIRELESS_EXT is not set
+# CONFIG_MAC80211 is not set
+# CONFIG_IEEE80211 is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_FW_LOADER is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_REDBOOT_PARTS is not set
+# CONFIG_MTD_CMDLINE_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=m
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+# CONFIG_MTD_CFI is not set
+CONFIG_MTD_JEDECPROBE=m
+CONFIG_MTD_GEN_PROBE=m
+# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_CFI_INTELEXT is not set
+# CONFIG_MTD_CFI_AMDSTD is not set
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_RAM=y
+CONFIG_MTD_ROM=m
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+CONFIG_MTD_COMPLEX_MAPPINGS=y
+# CONFIG_MTD_PHYSMAP is not set
+# CONFIG_MTD_GPIO_ADDR is not set
+# CONFIG_MTD_UCLINUX is not set
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_DATAFLASH is not set
+# CONFIG_MTD_M25P80 is not set
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+CONFIG_MTD_NAND=m
+# CONFIG_MTD_NAND_VERIFY_WRITE is not set
+# CONFIG_MTD_NAND_ECC_SMC is not set
+# CONFIG_MTD_NAND_MUSEUM_IDS is not set
+CONFIG_MTD_NAND_BFIN=m
+CONFIG_BFIN_NAND_BASE=0x20212000
+CONFIG_BFIN_NAND_CLE=2
+CONFIG_BFIN_NAND_ALE=1
+CONFIG_BFIN_NAND_READY=3
+CONFIG_MTD_NAND_IDS=m
+# CONFIG_MTD_NAND_BF5XX is not set
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+# CONFIG_MTD_NAND_NANDSIM is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_ALAUDA is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_UB is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=4096
+# CONFIG_BLK_DEV_XIP is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NETDEVICES_MULTIQUEUE is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_VETH is not set
+CONFIG_PHYLIB=y
+
+#
+# MII PHY device drivers
+#
+# CONFIG_MARVELL_PHY is not set
+# CONFIG_DAVICOM_PHY is not set
+# CONFIG_QSEMI_PHY is not set
+# CONFIG_LXT_PHY is not set
+# CONFIG_CICADA_PHY is not set
+# CONFIG_VITESSE_PHY is not set
+# CONFIG_SMSC_PHY is not set
+# CONFIG_BROADCOM_PHY is not set
+# CONFIG_ICPLUS_PHY is not set
+# CONFIG_REALTEK_PHY is not set
+# CONFIG_FIXED_PHY is not set
+# CONFIG_MDIO_BITBANG is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+CONFIG_BFIN_MAC=y
+CONFIG_BFIN_TX_DESC_NUM=10
+CONFIG_BFIN_RX_DESC_NUM=20
+CONFIG_BFIN_MAC_RMII=y
+# CONFIG_SMC91X is not set
+# CONFIG_SMSC911X is not set
+# CONFIG_DM9000 is not set
+# CONFIG_ENC28J60 is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_B44 is not set
+CONFIG_NETDEV_1000=y
+# CONFIG_E1000E_ENABLED is not set
+# CONFIG_AX88180 is not set
+CONFIG_NETDEV_10000=y
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+# CONFIG_WLAN_80211 is not set
+# CONFIG_IWLWIFI_LEDS is not set
+
+#
+# USB Network Adapters
+#
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
+# CONFIG_USB_USBNET is not set
+# CONFIG_WAN is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+CONFIG_INPUT_MISC=y
+# CONFIG_INPUT_ATI_REMOTE is not set
+# CONFIG_INPUT_ATI_REMOTE2 is not set
+# CONFIG_INPUT_KEYSPAN_REMOTE is not set
+# CONFIG_INPUT_POWERMATE is not set
+# CONFIG_INPUT_YEALINK is not set
+# CONFIG_INPUT_UINPUT is not set
+# CONFIG_TWI_KEYPAD is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+# CONFIG_AD9960 is not set
+# CONFIG_SPI_ADC_BF533 is not set
+# CONFIG_BF5xx_PPIFCD is not set
+# CONFIG_BFIN_SIMPLE_TIMER is not set
+# CONFIG_BF5xx_PPI is not set
+# CONFIG_BFIN_SPORT is not set
+# CONFIG_BFIN_TIMER_LATENCY is not set
+# CONFIG_TWI_LCD is not set
+CONFIG_SIMPLE_GPIO=m
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+CONFIG_DEVKMEM=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_BFIN=y
+CONFIG_SERIAL_BFIN_CONSOLE=y
+CONFIG_SERIAL_BFIN_DMA=y
+# CONFIG_SERIAL_BFIN_PIO is not set
+# CONFIG_SERIAL_BFIN_UART0 is not set
+CONFIG_SERIAL_BFIN_UART1=y
+# CONFIG_BFIN_UART1_CTSRTS is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_SERIAL_BFIN_SPORT is not set
+CONFIG_UNIX98_PTYS=y
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_BFIN_OTP=y
+# CONFIG_BFIN_OTP_WRITE_ENABLE is not set
+
+#
+# CAN, the car bus and industrial fieldbus
+#
+# CONFIG_CAN4LINUX is not set
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=y
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_CHARDEV=m
+CONFIG_I2C_HELPER_AUTO=y
+
+#
+# I2C Hardware Bus support
+#
+CONFIG_I2C_BLACKFIN_TWI=y
+CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ=50
+# CONFIG_I2C_GPIO is not set
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_SIMTEC is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_TINY_USB is not set
+# CONFIG_I2C_PCA_PLATFORM is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_DS1682 is not set
+# CONFIG_SENSORS_AD5252 is not set
+# CONFIG_SENSORS_EEPROM is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_MAX6875 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+CONFIG_SPI=y
+CONFIG_SPI_MASTER=y
+
+#
+# SPI Master Controller Drivers
+#
+CONFIG_SPI_BFIN=y
+# CONFIG_SPI_BITBANG is not set
+
+#
+# SPI Protocol Masters
+#
+# CONFIG_SPI_AT25 is not set
+# CONFIG_SPI_SPIDEV is not set
+# CONFIG_SPI_TLE62X0 is not set
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+CONFIG_HWMON=y
+# CONFIG_HWMON_VID is not set
+# CONFIG_SENSORS_AD7418 is not set
+# CONFIG_SENSORS_ADM1021 is not set
+# CONFIG_SENSORS_ADM1025 is not set
+# CONFIG_SENSORS_ADM1026 is not set
+# CONFIG_SENSORS_ADM1029 is not set
+# CONFIG_SENSORS_ADM1031 is not set
+# CONFIG_SENSORS_ADM9240 is not set
+# CONFIG_SENSORS_ADT7470 is not set
+# CONFIG_SENSORS_ADT7473 is not set
+# CONFIG_SENSORS_ATXP1 is not set
+# CONFIG_SENSORS_DS1621 is not set
+# CONFIG_SENSORS_F71805F is not set
+# CONFIG_SENSORS_F71882FG is not set
+# CONFIG_SENSORS_F75375S is not set
+# CONFIG_SENSORS_GL518SM is not set
+# CONFIG_SENSORS_GL520SM is not set
+# CONFIG_SENSORS_IT87 is not set
+# CONFIG_SENSORS_LM63 is not set
+# CONFIG_SENSORS_LM70 is not set
+# CONFIG_SENSORS_LM75 is not set
+# CONFIG_SENSORS_LM77 is not set
+# CONFIG_SENSORS_LM78 is not set
+# CONFIG_SENSORS_LM80 is not set
+# CONFIG_SENSORS_LM83 is not set
+# CONFIG_SENSORS_LM85 is not set
+# CONFIG_SENSORS_LM87 is not set
+# CONFIG_SENSORS_LM90 is not set
+# CONFIG_SENSORS_LM92 is not set
+# CONFIG_SENSORS_LM93 is not set
+# CONFIG_SENSORS_MAX1619 is not set
+# CONFIG_SENSORS_MAX6650 is not set
+# CONFIG_SENSORS_PC87360 is not set
+# CONFIG_SENSORS_PC87427 is not set
+# CONFIG_SENSORS_DME1737 is not set
+# CONFIG_SENSORS_SMSC47M1 is not set
+# CONFIG_SENSORS_SMSC47M192 is not set
+# CONFIG_SENSORS_SMSC47B397 is not set
+# CONFIG_SENSORS_ADS7828 is not set
+# CONFIG_SENSORS_THMC50 is not set
+# CONFIG_SENSORS_VT1211 is not set
+# CONFIG_SENSORS_W83781D is not set
+# CONFIG_SENSORS_W83791D is not set
+# CONFIG_SENSORS_W83792D is not set
+# CONFIG_SENSORS_W83793 is not set
+# CONFIG_SENSORS_W83L785TS is not set
+# CONFIG_SENSORS_W83L786NG is not set
+# CONFIG_SENSORS_W83627HF is not set
+# CONFIG_SENSORS_W83627EHF is not set
+# CONFIG_HWMON_DEBUG_CHIP is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_HWMON is not set
+CONFIG_WATCHDOG=y
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+
+#
+# Watchdog Device Drivers
+#
+# CONFIG_SOFT_WATCHDOG is not set
+CONFIG_BFIN_WDT=y
+
+#
+# USB-based Watchdog Cards
+#
+# CONFIG_USBPCWATCHDOG is not set
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_PASIC3 is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_DVB_CORE is not set
+# CONFIG_VIDEO_MEDIA is not set
+
+#
+# Multimedia drivers
+#
+# CONFIG_DAB is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+# CONFIG_FB is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+CONFIG_DUMMY_CONSOLE=y
+
+#
+# Sound
+#
+CONFIG_SOUND=m
+
+#
+# Advanced Linux Sound Architecture
+#
+CONFIG_SND=m
+CONFIG_SND_TIMER=m
+CONFIG_SND_PCM=m
+# CONFIG_SND_SEQUENCER is not set
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=m
+CONFIG_SND_PCM_OSS=m
+CONFIG_SND_PCM_OSS_PLUGINS=y
+# CONFIG_SND_DYNAMIC_MINORS is not set
+CONFIG_SND_SUPPORT_OLD_API=y
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+
+#
+# Generic devices
+#
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+
+#
+# SPI devices
+#
+
+#
+# ALSA Blackfin devices
+#
+# CONFIG_SND_BLACKFIN_AD1836 is not set
+# CONFIG_SND_BFIN_AD73311 is not set
+# CONFIG_SND_BFIN_AD73322 is not set
+
+#
+# USB devices
+#
+# CONFIG_SND_USB_AUDIO is not set
+# CONFIG_SND_USB_CAIAQ is not set
+
+#
+# System on Chip audio support
+#
+CONFIG_SND_SOC=m
+CONFIG_SND_BF5XX_I2S=m
+CONFIG_SND_BF5XX_SOC_SSM2602=m
+# CONFIG_SND_BF5XX_AC97 is not set
+CONFIG_SND_BF5XX_SOC_SPORT=m
+CONFIG_SND_BF5XX_SOC_I2S=m
+CONFIG_SND_BF5XX_SPORT_NUM=0
+
+#
+# ALSA SoC audio for Freescale SOCs
+#
+
+#
+# SoC Audio for the Texas Instruments OMAP
+#
+CONFIG_SND_SOC_SSM2602=m
+
+#
+# Open Sound System
+#
+# CONFIG_SOUND_PRIME is not set
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=y
+# CONFIG_HID_DEBUG is not set
+# CONFIG_HIDRAW is not set
+
+#
+# USB Input Devices
+#
+CONFIG_USB_HID=y
+# CONFIG_USB_HIDINPUT_POWERBOOK is not set
+# CONFIG_HID_FF is not set
+# CONFIG_USB_HIDDEV is not set
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set
+
+#
+# Miscellaneous USB options
+#
+# CONFIG_USB_DEVICEFS is not set
+CONFIG_USB_DEVICE_CLASS=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_OTG is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+CONFIG_USB_OTG_BLACKLIST_HUB=y
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_C67X00_HCD is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_ISP1760_HCD is not set
+# CONFIG_USB_ISP1362_HCD is not set
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD is not set
+CONFIG_USB_MUSB_HDRC=y
+CONFIG_USB_MUSB_SOC=y
+
+#
+# Blackfin high speed USB support
+#
+CONFIG_USB_MUSB_HOST=y
+# CONFIG_USB_MUSB_PERIPHERAL is not set
+# CONFIG_USB_MUSB_OTG is not set
+CONFIG_USB_MUSB_HDRC_HCD=y
+CONFIG_MUSB_PIO_ONLY=y
+CONFIG_USB_MUSB_LOGLEVEL=0
+
+#
+# USB Device Class drivers
+#
+# CONFIG_USB_ACM is not set
+# CONFIG_USB_PRINTER is not set
+# CONFIG_USB_WDM is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+#
+
+#
+# may also be needed; see USB_STORAGE Help for more information
+#
+# CONFIG_USB_LIBUSUAL is not set
+
+#
+# USB Imaging devices
+#
+# CONFIG_USB_MDC800 is not set
+CONFIG_USB_MON=y
+
+#
+# USB port drivers
+#
+# CONFIG_USB_SERIAL is not set
+
+#
+# USB Miscellaneous drivers
+#
+# CONFIG_USB_EMI62 is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_ADUTUX is not set
+# CONFIG_USB_AUERSWALD is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_LEGOTOWER is not set
+# CONFIG_USB_LCD is not set
+# CONFIG_USB_BERRY_CHARGE is not set
+# CONFIG_USB_LED is not set
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+# CONFIG_USB_CYTHERM is not set
+# CONFIG_USB_PHIDGET is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_SISUSBVGA is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_ISIGHTFW is not set
+# CONFIG_USB_GADGET is not set
+# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_NEW_LEDS is not set
+# CONFIG_ACCESSIBILITY is not set
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# I2C RTC drivers
+#
+# CONFIG_RTC_DRV_DS1307 is not set
+# CONFIG_RTC_DRV_DS1374 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+# CONFIG_RTC_DRV_RS5C372 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_X1205 is not set
+# CONFIG_RTC_DRV_PCF8563 is not set
+# CONFIG_RTC_DRV_PCF8583 is not set
+# CONFIG_RTC_DRV_M41T80 is not set
+# CONFIG_RTC_DRV_S35390A is not set
+# CONFIG_RTC_DRV_FM3130 is not set
+
+#
+# SPI RTC drivers
+#
+# CONFIG_RTC_DRV_MAX6902 is not set
+# CONFIG_RTC_DRV_R9701 is not set
+# CONFIG_RTC_DRV_RS5C348 is not set
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+CONFIG_RTC_DRV_BFIN=y
+# CONFIG_UIO is not set
+
+#
+# File systems
+#
+# CONFIG_EXT2_FS is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_EXT4DEV_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_DNOTIFY is not set
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_SYSFS=y
+# CONFIG_TMPFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_YAFFS_FS=m
+CONFIG_YAFFS_YAFFS1=y
+# CONFIG_YAFFS_9BYTE_TAGS is not set
+# CONFIG_YAFFS_DOES_ECC is not set
+CONFIG_YAFFS_YAFFS2=y
+CONFIG_YAFFS_AUTO_YAFFS2=y
+# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set
+# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set
+# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set
+CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y
+CONFIG_JFFS2_FS=m
+CONFIG_JFFS2_FS_DEBUG=0
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
+# CONFIG_JFFS2_SUMMARY is not set
+# CONFIG_JFFS2_FS_XATTR is not set
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_ZLIB=y
+# CONFIG_JFFS2_LZO is not set
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=m
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+# CONFIG_NFS_V4 is not set
+# CONFIG_NFSD is not set
+CONFIG_LOCKD=m
+CONFIG_LOCKD_V4=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=m
+# CONFIG_SUNRPC_BIND34 is not set
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_SMB_FS=m
+# CONFIG_SMB_NLS_DEFAULT is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+CONFIG_NLS=m
+CONFIG_NLS_DEFAULT="iso8859-1"
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_UTF8 is not set
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+CONFIG_DEBUG_FS=y
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_SAMPLES is not set
+CONFIG_DEBUG_MMRS=y
+CONFIG_DEBUG_HUNT_FOR_ZERO=y
+CONFIG_DEBUG_BFIN_HWTRACE_ON=y
+CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_OFF=y
+# CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_ONE is not set
+# CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_TWO is not set
+CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION=0
+# CONFIG_DEBUG_BFIN_HWTRACE_EXPAND is not set
+# CONFIG_DEBUG_BFIN_NO_KERN_HWTRACE is not set
+CONFIG_EARLY_PRINTK=y
+CONFIG_CPLB_INFO=y
+CONFIG_ACCESS_CHECK=y
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+CONFIG_SECURITY=y
+# CONFIG_SECURITY_NETWORK is not set
+# CONFIG_SECURITY_CAPABILITIES is not set
+# CONFIG_SECURITY_ROOTPLUG is not set
+CONFIG_SECURITY_DEFAULT_MMAP_MIN_ADDR=0
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+# CONFIG_CRYPTO_MANAGER is not set
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+# CONFIG_CRYPTO_CBC is not set
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_XCBC is not set
+
+#
+# Digest
+#
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_MD4 is not set
+# CONFIG_CRYPTO_MD5 is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
+
+#
+# Ciphers
+#
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_DES is not set
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+
+#
+# Compression
+#
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_LZO is not set
+CONFIG_CRYPTO_HW=y
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+# CONFIG_GENERIC_FIND_FIRST_BIT is not set
+CONFIG_CRC_CCITT=m
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=m
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/blackfin/configs/H8606_defconfig b/arch/blackfin/configs/H8606_defconfig
index ba0bee9..1fc31f1 100644
--- a/arch/blackfin/configs/H8606_defconfig
+++ b/arch/blackfin/configs/H8606_defconfig
@@ -1,6 +1,6 @@
#
# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.22.12
+# Linux kernel version: 2.6.22.14
#
# CONFIG_MMU is not set
# CONFIG_FPU is not set
@@ -192,7 +192,7 @@
# CONFIG_BFIN_KERNEL_CLOCK is not set
CONFIG_MAX_VCO_HZ=400000000
CONFIG_MIN_VCO_HZ=50000000
-CONFIG_MAX_SCLK_HZ=133000000
+CONFIG_MAX_SCLK_HZ=133333333
CONFIG_MIN_SCLK_HZ=27000000
#
@@ -516,7 +516,7 @@
#
# CONFIG_MTD_DATAFLASH is not set
CONFIG_MTD_M25P80=y
-CONFIG_M25PXX_USE_FAST_READ=y
+# CONFIG_M25PXX_USE_FAST_READ is not set
# CONFIG_MTD_SLRAM is not set
# CONFIG_MTD_PHRAM is not set
# CONFIG_MTD_MTDRAM is not set
@@ -635,25 +635,25 @@
# CONFIG_INPUT_MOUSEDEV is not set
# CONFIG_INPUT_JOYDEV is not set
# CONFIG_INPUT_TSDEV is not set
-CONFIG_INPUT_EVDEV=m
+CONFIG_INPUT_EVDEV=y
# CONFIG_INPUT_EVBUG is not set
#
# Input Device Drivers
#
-# CONFIG_INPUT_KEYBOARD is not set
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ATKBD is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+CONFIG_KEYBOARD_GPIO=y
# CONFIG_INPUT_MOUSE is not set
# CONFIG_INPUT_JOYSTICK is not set
# CONFIG_INPUT_TABLET is not set
# CONFIG_INPUT_TOUCHSCREEN is not set
-CONFIG_INPUT_MISC=y
-# CONFIG_INPUT_ATI_REMOTE is not set
-# CONFIG_INPUT_ATI_REMOTE2 is not set
-# CONFIG_INPUT_KEYSPAN_REMOTE is not set
-# CONFIG_INPUT_POWERMATE is not set
-# CONFIG_INPUT_YEALINK is not set
-# CONFIG_INPUT_UINPUT is not set
-# CONFIG_BF53X_PFBUTTONS is not set
+# CONFIG_INPUT_MISC is not set
#
# Hardware I/O ports
@@ -681,7 +681,15 @@
#
# Serial drivers
#
-# CONFIG_SERIAL_8250 is not set
+CONFIG_SERIAL_8250=y
+# CONFIG_SERIAL_8250_CONSOLE is not set
+CONFIG_SERIAL_8250_NR_UARTS=4
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+CONFIG_SERIAL_8250_EXTENDED=y
+# CONFIG_SERIAL_8250_MANY_PORTS is not set
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+# CONFIG_SERIAL_8250_DETECT_IRQ is not set
+# CONFIG_SERIAL_8250_RSA is not set
#
# Non-8250 serial port support
diff --git a/arch/blackfin/include/asm/a.out.h b/arch/blackfin/include/asm/a.out.h
deleted file mode 100644
index 6c3d652..0000000
--- a/arch/blackfin/include/asm/a.out.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef __BFIN_A_OUT_H__
-#define __BFIN_A_OUT_H__
-
-struct exec {
- unsigned long a_info; /* Use macros N_MAGIC, etc for access */
- unsigned a_text; /* length of text, in bytes */
- unsigned a_data; /* length of data, in bytes */
- unsigned a_bss; /* length of uninitialized data area for file, in bytes */
- unsigned a_syms; /* length of symbol table data in file, in bytes */
- unsigned a_entry; /* start address */
- unsigned a_trsize; /* length of relocation info for text, in bytes */
- unsigned a_drsize; /* length of relocation info for data, in bytes */
-};
-
-#define N_TRSIZE(a) ((a).a_trsize)
-#define N_DRSIZE(a) ((a).a_drsize)
-#define N_SYMSIZE(a) ((a).a_syms)
-
-#endif /* __BFIN_A_OUT_H__ */
diff --git a/arch/blackfin/include/asm/bfin-global.h b/arch/blackfin/include/asm/bfin-global.h
index 7ba70de..56dcb0a 100644
--- a/arch/blackfin/include/asm/bfin-global.h
+++ b/arch/blackfin/include/asm/bfin-global.h
@@ -63,7 +63,6 @@
extern void init_exception_vectors(void);
extern void program_IAR(void);
-extern void bfin_reset(void);
extern asmlinkage void lower_to_irq14(void);
extern asmlinkage void bfin_return_from_exception(void);
extern asmlinkage void evt14_softirq(void);
@@ -92,6 +91,8 @@
extern void *sram_alloc_with_lsl(size_t, unsigned long);
extern int sram_free_with_lsl(const void*);
+extern void *isram_memcpy(void *dest, const void *src, size_t n);
+
extern const char bfin_board_name[];
extern unsigned long bfin_sic_iwr[];
@@ -104,7 +105,7 @@
_stext_l2[], _etext_l2[], _sdata_l2[], _edata_l2[], _sbss_l2[],
_ebss_l2[], _l2_lma_start[];
-/* only used when CONFIG_MTD_UCLINUX */
+/* only used when MTD_UCLINUX */
extern unsigned long memory_mtd_start, memory_mtd_end, mtd_size;
#ifdef CONFIG_BFIN_ICACHE_LOCK
diff --git a/arch/blackfin/include/asm/bfrom.h b/arch/blackfin/include/asm/bfrom.h
new file mode 100644
index 0000000..cfe8024
--- /dev/null
+++ b/arch/blackfin/include/asm/bfrom.h
@@ -0,0 +1,85 @@
+/* Blackfin on-chip ROM API
+ *
+ * Copyright 2008 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef __BFROM_H__
+#define __BFROM_H__
+
+#include <linux/types.h>
+
+/* Possible syscontrol action flags */
+#define SYSCTRL_READ 0x00000000 /* read registers */
+#define SYSCTRL_WRITE 0x00000001 /* write registers */
+#define SYSCTRL_SYSRESET 0x00000002 /* perform system reset */
+#define SYSCTRL_CORERESET 0x00000004 /* perform core reset */
+#define SYSCTRL_SOFTRESET 0x00000006 /* perform core and system reset */
+#define SYSCTRL_VRCTL 0x00000010 /* read/write VR_CTL register */
+#define SYSCTRL_EXTVOLTAGE 0x00000020 /* VDDINT supplied externally */
+#define SYSCTRL_INTVOLTAGE 0x00000000 /* VDDINT generated by on-chip regulator */
+#define SYSCTRL_OTPVOLTAGE 0x00000040 /* For Factory Purposes Only */
+#define SYSCTRL_PLLCTL 0x00000100 /* read/write PLL_CTL register */
+#define SYSCTRL_PLLDIV 0x00000200 /* read/write PLL_DIV register */
+#define SYSCTRL_LOCKCNT 0x00000400 /* read/write PLL_LOCKCNT register */
+#define SYSCTRL_PLLSTAT 0x00000800 /* read/write PLL_STAT register */
+
+typedef struct ADI_SYSCTRL_VALUES {
+ uint16_t uwVrCtl;
+ uint16_t uwPllCtl;
+ uint16_t uwPllDiv;
+ uint16_t uwPllLockCnt;
+ uint16_t uwPllStat;
+} ADI_SYSCTRL_VALUES;
+
+static uint32_t (* const bfrom_SysControl)(uint32_t action_flags, ADI_SYSCTRL_VALUES *power_settings, void *reserved) = (void *)0xEF000038;
+
+/* We need a dedicated function since we need to screw with the stack pointer
+ * when resetting. The on-chip ROM will save/restore registers on the stack
+ * when doing a system reset, so the stack cannot be outside of the chip.
+ */
+__attribute__((__noreturn__))
+static inline void bfrom_SoftReset(void *new_stack)
+{
+ while (1)
+ __asm__ __volatile__(
+ "sp = %[stack];"
+ "jump (%[bfrom_syscontrol]);"
+ : : [bfrom_syscontrol] "p"(bfrom_SysControl),
+ "q0"(SYSCTRL_SOFTRESET),
+ "q1"(0),
+ "q2"(NULL),
+ [stack] "p"(new_stack)
+ );
+}
+
+/* OTP Functions */
+static uint32_t (* const bfrom_OtpCommand)(uint32_t command, uint32_t value) = (void *)0xEF000018;
+static uint32_t (* const bfrom_OtpRead)(uint32_t page, uint32_t flags, uint64_t *page_content) = (void *)0xEF00001A;
+static uint32_t (* const bfrom_OtpWrite)(uint32_t page, uint32_t flags, uint64_t *page_content) = (void *)0xEF00001C;
+
+/* otp command: defines for "command" */
+#define OTP_INIT 0x00000001
+#define OTP_CLOSE 0x00000002
+
+/* otp read/write: defines for "flags" */
+#define OTP_LOWER_HALF 0x00000000 /* select upper/lower 64-bit half (bit 0) */
+#define OTP_UPPER_HALF 0x00000001
+#define OTP_NO_ECC 0x00000010 /* do not use ECC */
+#define OTP_LOCK 0x00000020 /* sets page protection bit for page */
+#define OTP_CHECK_FOR_PREV_WRITE 0x00000080
+
+/* Return values for all functions */
+#define OTP_SUCCESS 0x00000000
+#define OTP_MASTER_ERROR 0x001
+#define OTP_WRITE_ERROR 0x003
+#define OTP_READ_ERROR 0x005
+#define OTP_ACC_VIO_ERROR 0x009
+#define OTP_DATA_MULT_ERROR 0x011
+#define OTP_ECC_MULT_ERROR 0x021
+#define OTP_PREV_WR_ERROR 0x041
+#define OTP_DATA_SB_WARN 0x100
+#define OTP_ECC_SB_WARN 0x200
+
+#endif
diff --git a/arch/blackfin/include/asm/cacheflush.h b/arch/blackfin/include/asm/cacheflush.h
index d81a775..5ef9e35 100644
--- a/arch/blackfin/include/asm/cacheflush.h
+++ b/arch/blackfin/include/asm/cacheflush.h
@@ -30,8 +30,6 @@
#ifndef _BLACKFIN_CACHEFLUSH_H
#define _BLACKFIN_CACHEFLUSH_H
-#include <asm/cplb.h>
-
extern void blackfin_icache_dcache_flush_range(unsigned int, unsigned int);
extern void blackfin_icache_flush_range(unsigned int, unsigned int);
extern void blackfin_dcache_flush_range(unsigned int, unsigned int);
diff --git a/arch/blackfin/include/asm/cplb.h b/arch/blackfin/include/asm/cplb.h
index 05d6f05..9e8b403 100644
--- a/arch/blackfin/include/asm/cplb.h
+++ b/arch/blackfin/include/asm/cplb.h
@@ -55,7 +55,13 @@
#endif
#define L1_DMEMORY (CPLB_LOCK | CPLB_COMMON)
-#define L2_MEMORY (CPLB_COMMON)
+#ifdef CONFIG_BFIN_L2_CACHEABLE
+#define L2_IMEMORY (SDRAM_IGENERIC)
+#define L2_DMEMORY (SDRAM_DGENERIC)
+#else
+#define L2_IMEMORY (CPLB_COMMON)
+#define L2_DMEMORY (CPLB_COMMON)
+#endif
#define SDRAM_DNON_CHBL (CPLB_COMMON)
#define SDRAM_EBIU (CPLB_COMMON)
#define SDRAM_OOPS (CPLB_VALID | ANOMALY_05000158_WORKAROUND | CPLB_LOCK | CPLB_DIRTY)
diff --git a/arch/blackfin/include/asm/cplbinit.h b/arch/blackfin/include/asm/cplbinit.h
index 0eb1c1b..d179b74 100644
--- a/arch/blackfin/include/asm/cplbinit.h
+++ b/arch/blackfin/include/asm/cplbinit.h
@@ -90,6 +90,20 @@
extern unsigned long reserved_mem_dcache_on;
extern unsigned long reserved_mem_icache_on;
-extern void generate_cpl_tables(void);
+extern void generate_cplb_tables(void);
+
+static inline int bfin_addr_dcachable(unsigned long addr)
+{
+#ifdef CONFIG_BFIN_DCACHE
+ if (addr < (_ramend - DMA_UNCACHED_REGION))
+ return 1;
+#endif
+
+ if (reserved_mem_dcache_on &&
+ addr >= _ramend && addr < physical_mem_end)
+ return 1;
+
+ return 0;
+}
#endif
diff --git a/arch/blackfin/include/asm/cpumask.h b/arch/blackfin/include/asm/cpumask.h
deleted file mode 100644
index b20a8e9..0000000
--- a/arch/blackfin/include/asm/cpumask.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _ASM_BLACKFIN_CPUMASK_H
-#define _ASM_BLACKFIN_CPUMASK_H
-
-#include <asm-generic/cpumask.h>
-
-#endif /* _ASM_BLACKFIN_CPUMASK_H */
diff --git a/arch/blackfin/include/asm/dma-mapping.h b/arch/blackfin/include/asm/dma-mapping.h
index 1a13c2f..ede748d 100644
--- a/arch/blackfin/include/asm/dma-mapping.h
+++ b/arch/blackfin/include/asm/dma-mapping.h
@@ -80,4 +80,15 @@
extern void dma_unmap_sg(struct device *dev, struct scatterlist *sg,
int nhwentries, enum dma_data_direction direction);
+static inline void dma_sync_single_for_cpu(struct device *dev,
+ dma_addr_t handle, size_t size,
+ enum dma_data_direction dir)
+{
+}
+
+static inline void dma_sync_single_for_device(struct device *dev,
+ dma_addr_t handle, size_t size,
+ enum dma_data_direction dir)
+{
+}
#endif /* _BLACKFIN_DMA_MAPPING_H */
diff --git a/arch/blackfin/include/asm/kgdb.h b/arch/blackfin/include/asm/kgdb.h
index 0f73847..26ebac6 100644
--- a/arch/blackfin/include/asm/kgdb.h
+++ b/arch/blackfin/include/asm/kgdb.h
@@ -124,9 +124,16 @@
/* Number of bytes of registers. */
#define NUMREGBYTES BFIN_NUM_REGS*4
-#define BREAKPOINT() asm(" EXCPT 2;");
-#define BREAK_INSTR_SIZE 2
-#define HW_BREAKPOINT_NUM 6
+static inline void arch_kgdb_breakpoint(void)
+{
+ asm(" EXCPT 2;");
+}
+#define BREAK_INSTR_SIZE 2
+#define CACHE_FLUSH_IS_SAFE 1
+#define HW_INST_WATCHPOINT_NUM 6
+#define HW_WATCHPOINT_NUM 8
+#define TYPE_INST_WATCHPOINT 0
+#define TYPE_DATA_WATCHPOINT 1
/* Instruction watchpoint address control register bits mask */
#define WPPWR 0x1
@@ -163,10 +170,11 @@
#define WPDAEN1 0x8
#define WPDCNTEN0 0x10
#define WPDCNTEN1 0x20
+
#define WPDSRC0 0xc0
-#define WPDACC0 0x300
+#define WPDACC0_OFFSET 8
#define WPDSRC1 0xc00
-#define WPDACC1 0x3000
+#define WPDACC1_OFFSET 12
/* Watchpoint status register bits mask */
#define STATIA0 0x1
@@ -178,7 +186,4 @@
#define STATDA0 0x40
#define STATDA1 0x80
-extern void kgdb_print(const char *fmt, ...);
-extern void init_kgdb_uart(void);
-
#endif
diff --git a/arch/blackfin/include/asm/mmu_context.h b/arch/blackfin/include/asm/mmu_context.h
index 8529552..35593dd 100644
--- a/arch/blackfin/include/asm/mmu_context.h
+++ b/arch/blackfin/include/asm/mmu_context.h
@@ -45,49 +45,12 @@
extern int l1sram_free(const void*);
extern void *l1sram_alloc_max(void*);
-static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk)
-{
-}
-
-/* Called when creating a new context during fork() or execve(). */
-static inline int
-init_new_context(struct task_struct *tsk, struct mm_struct *mm)
-{
-#ifdef CONFIG_MPU
- unsigned long p = __get_free_pages(GFP_KERNEL, page_mask_order);
- mm->context.page_rwx_mask = (unsigned long *)p;
- memset(mm->context.page_rwx_mask, 0,
- page_mask_nelts * 3 * sizeof(long));
-#endif
- return 0;
-}
-
static inline void free_l1stack(void)
{
nr_l1stack_tasks--;
if (nr_l1stack_tasks == 0)
l1sram_free(l1_stack_base);
}
-static inline void destroy_context(struct mm_struct *mm)
-{
- struct sram_list_struct *tmp;
-
- if (current_l1_stack_save == mm->context.l1_stack_save)
- current_l1_stack_save = NULL;
- if (mm->context.l1_stack_save)
- free_l1stack();
-
- while ((tmp = mm->context.sram_list)) {
- mm->context.sram_list = tmp->next;
- sram_free(tmp->addr);
- kfree(tmp);
- }
-#ifdef CONFIG_MPU
- if (current_rwx_mask == mm->context.page_rwx_mask)
- current_rwx_mask = NULL;
- free_pages((unsigned long)mm->context.page_rwx_mask, page_mask_order);
-#endif
-}
static inline unsigned long
alloc_l1stack(unsigned long length, unsigned long *stack_base)
@@ -134,6 +97,7 @@
}
#endif
+#ifdef CONFIG_APP_STACK_L1
/* L1 stack switching. */
if (!next_mm->context.l1_stack_save)
return;
@@ -144,6 +108,7 @@
}
current_l1_stack_save = next_mm->context.l1_stack_save;
memcpy(l1_stack_base, current_l1_stack_save, l1_stack_len);
+#endif
}
#ifdef CONFIG_MPU
@@ -180,4 +145,44 @@
}
#endif
+static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk)
+{
+}
+
+/* Called when creating a new context during fork() or execve(). */
+static inline int
+init_new_context(struct task_struct *tsk, struct mm_struct *mm)
+{
+#ifdef CONFIG_MPU
+ unsigned long p = __get_free_pages(GFP_KERNEL, page_mask_order);
+ mm->context.page_rwx_mask = (unsigned long *)p;
+ memset(mm->context.page_rwx_mask, 0,
+ page_mask_nelts * 3 * sizeof(long));
+#endif
+ return 0;
+}
+
+static inline void destroy_context(struct mm_struct *mm)
+{
+ struct sram_list_struct *tmp;
+
+#ifdef CONFIG_APP_STACK_L1
+ if (current_l1_stack_save == mm->context.l1_stack_save)
+ current_l1_stack_save = 0;
+ if (mm->context.l1_stack_save)
+ free_l1stack();
+#endif
+
+ while ((tmp = mm->context.sram_list)) {
+ mm->context.sram_list = tmp->next;
+ sram_free(tmp->addr);
+ kfree(tmp);
+ }
+#ifdef CONFIG_MPU
+ if (current_rwx_mask == mm->context.page_rwx_mask)
+ current_rwx_mask = NULL;
+ free_pages((unsigned long)mm->context.page_rwx_mask, page_mask_order);
+#endif
+}
+
#endif
diff --git a/arch/blackfin/include/asm/processor.h b/arch/blackfin/include/asm/processor.h
index 6f3995b..e3e9b41 100644
--- a/arch/blackfin/include/asm/processor.h
+++ b/arch/blackfin/include/asm/processor.h
@@ -134,6 +134,12 @@
return revid;
}
+static inline uint16_t __pure bfin_cpuid(void)
+{
+ return (bfin_read_CHIPID() & CHIPID_FAMILY) >> 12;
+
+}
+
static inline uint32_t __pure bfin_compiled_revid(void)
{
#if defined(CONFIG_BF_REV_0_0)
diff --git a/arch/blackfin/include/asm/ptrace.h b/arch/blackfin/include/asm/ptrace.h
index a45a80e..e3f086d 100644
--- a/arch/blackfin/include/asm/ptrace.h
+++ b/arch/blackfin/include/asm/ptrace.h
@@ -158,6 +158,8 @@
#define PT_SEQSTAT 8
#define PT_IPEND 4
+#define PT_ORIG_R0 208
+#define PT_ORIG_P0 212
#define PT_SYSCFG 216
#define PT_TEXT_ADDR 220
#define PT_TEXT_END_ADDR 224
diff --git a/arch/blackfin/include/asm/traps.h b/arch/blackfin/include/asm/traps.h
index f0e5f94..34f7295 100644
--- a/arch/blackfin/include/asm/traps.h
+++ b/arch/blackfin/include/asm/traps.h
@@ -59,6 +59,9 @@
level " or a 16-bit register is accessed with a 32-bit instruction.\n"
#define HWC_x3(level) \
"External Memory Addressing Error\n"
+#define EXC_0x04(level) \
+ "Unimplmented exception occured\n" \
+ level " - Maybe you forgot to install a custom exception handler?\n"
#define HWC_x12(level) \
"Performance Monitor Overflow\n"
#define HWC_x18(level) \
@@ -84,7 +87,7 @@
level " a particular processor implementation.\n"
#define EXC_0x22(level) \
"Illegal instruction combination\n" \
- level " - See section for multi-issue rules in the ADSP-BF53x Blackfin\n" \
+ level " - See section for multi-issue rules in the Blackfin\n" \
level " Processor Instruction Set Reference.\n"
#define EXC_0x23(level) \
"Data access CPLB protection violation\n" \
diff --git a/arch/blackfin/kernel/asm-offsets.c b/arch/blackfin/kernel/asm-offsets.c
index 881afe9..9bb85dd 100644
--- a/arch/blackfin/kernel/asm-offsets.c
+++ b/arch/blackfin/kernel/asm-offsets.c
@@ -60,6 +60,7 @@
DEFINE(KERNEL_STACK_SIZE, THREAD_SIZE);
/* offsets into the pt_regs */
+ DEFINE(PT_ORIG_R0, offsetof(struct pt_regs, orig_r0));
DEFINE(PT_ORIG_P0, offsetof(struct pt_regs, orig_p0));
DEFINE(PT_ORIG_PC, offsetof(struct pt_regs, orig_pc));
DEFINE(PT_R0, offsetof(struct pt_regs, r0));
diff --git a/arch/blackfin/kernel/bfin_dma_5xx.c b/arch/blackfin/kernel/bfin_dma_5xx.c
index 93229b3..339293d 100644
--- a/arch/blackfin/kernel/bfin_dma_5xx.c
+++ b/arch/blackfin/kernel/bfin_dma_5xx.c
@@ -117,15 +117,14 @@
#ifdef CONFIG_BF54x
if (channel >= CH_UART2_RX && channel <= CH_UART3_TX) {
- if (strncmp(device_id, "BFIN_UART", 9) == 0) {
- dma_ch[channel].regs->peripheral_map &= 0x0FFF;
- dma_ch[channel].regs->peripheral_map |=
+ unsigned int per_map;
+ per_map = dma_ch[channel].regs->peripheral_map & 0xFFF;
+ if (strncmp(device_id, "BFIN_UART", 9) == 0)
+ dma_ch[channel].regs->peripheral_map = per_map |
((channel - CH_UART2_RX + 0xC)<<12);
- } else {
- dma_ch[channel].regs->peripheral_map &= 0x0FFF;
- dma_ch[channel].regs->peripheral_map |=
+ else
+ dma_ch[channel].regs->peripheral_map = per_map |
((channel - CH_UART2_RX + 0x6)<<12);
- }
}
#endif
diff --git a/arch/blackfin/kernel/bfin_gpio.c b/arch/blackfin/kernel/bfin_gpio.c
index ecbd141..6e08f42 100644
--- a/arch/blackfin/kernel/bfin_gpio.c
+++ b/arch/blackfin/kernel/bfin_gpio.c
@@ -231,14 +231,14 @@
}
#endif
-void gpio_error(unsigned gpio)
+static void gpio_error(unsigned gpio)
{
printk(KERN_ERR "bfin-gpio: GPIO %d wasn't requested!\n", gpio);
}
static void set_label(unsigned short ident, const char *label)
{
- if (label && str_ident) {
+ if (label) {
strncpy(str_ident[ident].name, label,
RESOURCE_LABEL_SIZE);
str_ident[ident].name[RESOURCE_LABEL_SIZE - 1] = 0;
@@ -247,9 +247,6 @@
static char *get_label(unsigned short ident)
{
- if (!str_ident)
- return "UNKNOWN";
-
return (*str_ident[ident].name ? str_ident[ident].name : "UNKNOWN");
}
@@ -260,7 +257,7 @@
printk(KERN_ERR "Please provide none-null label\n");
}
- if (label && str_ident)
+ if (label)
return strncmp(str_ident[ident].name,
label, strlen(label));
else
diff --git a/arch/blackfin/kernel/cplb-mpu/cplbinit.c b/arch/blackfin/kernel/cplb-mpu/cplbinit.c
index 4806010..55af729 100644
--- a/arch/blackfin/kernel/cplb-mpu/cplbinit.c
+++ b/arch/blackfin/kernel/cplb-mpu/cplbinit.c
@@ -36,7 +36,7 @@
int first_switched_icplb, first_switched_dcplb;
int first_mask_dcplb;
-void __init generate_cpl_tables(void)
+void __init generate_cplb_tables(void)
{
int i_d, i_i;
unsigned long addr;
@@ -83,8 +83,18 @@
dcplb_tbl[i_d].addr = L1_DATA_A_START;
dcplb_tbl[i_d++].data = L1_DMEMORY | PAGE_SIZE_4MB;
#endif
+#if L1_CODE_LENGTH > 0
icplb_tbl[i_i].addr = L1_CODE_START;
icplb_tbl[i_i++].data = L1_IMEMORY | PAGE_SIZE_4MB;
+#endif
+
+ /* Cover L2 memory */
+#if L2_LENGTH > 0
+ dcplb_tbl[i_d].addr = L2_START;
+ dcplb_tbl[i_d++].data = L2_DMEMORY | PAGE_SIZE_1MB;
+ icplb_tbl[i_i].addr = L2_START;
+ icplb_tbl[i_i++].data = L2_IMEMORY | PAGE_SIZE_1MB;
+#endif
first_mask_dcplb = i_d;
first_switched_dcplb = i_d + (1 << page_mask_order);
diff --git a/arch/blackfin/kernel/cplb-mpu/cplbmgr.c b/arch/blackfin/kernel/cplb-mpu/cplbmgr.c
index 99f2831..5094677 100644
--- a/arch/blackfin/kernel/cplb-mpu/cplbmgr.c
+++ b/arch/blackfin/kernel/cplb-mpu/cplbmgr.c
@@ -322,9 +322,11 @@
void flush_switched_cplbs(void)
{
int i;
+ unsigned long flags;
nr_cplb_flush++;
+ local_irq_save(flags);
disable_icplb();
for (i = first_switched_icplb; i < MAX_CPLBS; i++) {
icplb_tbl[i].data = 0;
@@ -338,6 +340,8 @@
bfin_write32(DCPLB_DATA0 + i * 4, 0);
}
enable_dcplb();
+ local_irq_restore(flags);
+
}
void set_mask_dcplbs(unsigned long *masks)
@@ -345,10 +349,15 @@
int i;
unsigned long addr = (unsigned long)masks;
unsigned long d_data;
- current_rwx_mask = masks;
+ unsigned long flags;
- if (!masks)
+ if (!masks) {
+ current_rwx_mask = masks;
return;
+ }
+
+ local_irq_save(flags);
+ current_rwx_mask = masks;
d_data = CPLB_SUPV_WR | CPLB_VALID | CPLB_DIRTY | PAGE_SIZE_4KB;
#ifdef CONFIG_BFIN_DCACHE
@@ -367,4 +376,5 @@
addr += PAGE_SIZE;
}
enable_dcplb();
+ local_irq_restore(flags);
}
diff --git a/arch/blackfin/kernel/cplb-nompu/cplbinit.c b/arch/blackfin/kernel/cplb-nompu/cplbinit.c
index 728f708..301252e 100644
--- a/arch/blackfin/kernel/cplb-nompu/cplbinit.c
+++ b/arch/blackfin/kernel/cplb-nompu/cplbinit.c
@@ -168,8 +168,8 @@
.end = L2_START + L2_LENGTH,
.psize = SIZE_1M,
.attr = SWITCH_T | I_CPLB | D_CPLB,
- .i_conf = L2_MEMORY,
- .d_conf = L2_MEMORY,
+ .i_conf = L2_IMEMORY,
+ .d_conf = L2_DMEMORY,
.valid = (L2_LENGTH > 0),
.name = "L2 Memory",
},
@@ -308,7 +308,7 @@
}
}
-void __init generate_cpl_tables(void)
+void __init generate_cplb_tables(void)
{
u16 i, j, process;
diff --git a/arch/blackfin/kernel/early_printk.c b/arch/blackfin/kernel/early_printk.c
index 60f67f9..1f4e3d2 100644
--- a/arch/blackfin/kernel/early_printk.c
+++ b/arch/blackfin/kernel/early_printk.c
@@ -35,6 +35,9 @@
extern struct console *bfin_earlyserial_init(unsigned int port,
unsigned int cflag);
#endif
+#ifdef CONFIG_BFIN_JTAG_COMM
+extern struct console *bfin_jc_early_init(void);
+#endif
static struct console *early_console;
@@ -142,6 +145,15 @@
early_console = earlyserial_init(buf);
}
#endif
+
+#ifdef CONFIG_BFIN_JTAG_COMM
+ /* Check for Blackfin JTAG */
+ if (!strncmp(buf, "jtag", 4)) {
+ buf += 4;
+ early_console = bfin_jc_early_init();
+ }
+#endif
+
#ifdef CONFIG_FB
/* TODO: add framebuffer console support */
#endif
diff --git a/arch/blackfin/kernel/kgdb.c b/arch/blackfin/kernel/kgdb.c
index a1f9641..b795a20 100644
--- a/arch/blackfin/kernel/kgdb.c
+++ b/arch/blackfin/kernel/kgdb.c
@@ -1,32 +1,9 @@
/*
- * File: arch/blackfin/kernel/kgdb.c
- * Based on:
- * Author: Sonic Zhang
+ * arch/blackfin/kernel/kgdb.c - Blackfin kgdb pieces
*
- * Created:
- * Description:
+ * Copyright 2005-2008 Analog Devices Inc.
*
- * Rev: $Id: kgdb_bfin_linux-2.6.x.patch 4934 2007-02-13 09:32:11Z sonicz $
- *
- * Modified:
- * Copyright 2005-2006 Analog Devices Inc.
- *
- * Bugs: Enter bugs at http://blackfin.uclinux.org/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see the file COPYING, or write
- * to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * Licensed under the GPL-2 or later.
*/
#include <linux/string.h>
@@ -39,24 +16,29 @@
#include <linux/kgdb.h>
#include <linux/console.h>
#include <linux/init.h>
-#include <linux/debugger.h>
#include <linux/errno.h>
#include <linux/irq.h>
+#include <linux/uaccess.h>
#include <asm/system.h>
#include <asm/traps.h>
#include <asm/blackfin.h>
+#include <asm/dma.h>
/* Put the error code here just in case the user cares. */
-int gdb_bf533errcode;
+int gdb_bfin_errcode;
/* Likewise, the vector number here (since GDB only gets the signal
number through the usual means, and that's not very specific). */
-int gdb_bf533vector = -1;
+int gdb_bfin_vector = -1;
#if KGDB_MAX_NO_CPUS != 8
#error change the definition of slavecpulocks
#endif
-void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+#ifdef CONFIG_BFIN_WDT
+# error "Please unselect blackfin watchdog driver before build KGDB."
+#endif
+
+void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
{
gdb_regs[BFIN_R0] = regs->r0;
gdb_regs[BFIN_R1] = regs->r1;
@@ -133,7 +115,7 @@
gdb_regs[BFIN_SEQSTAT] = p->thread.seqstat;
}
-void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *regs)
+void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs)
{
regs->r0 = gdb_regs[BFIN_R0];
regs->r1 = gdb_regs[BFIN_R1];
@@ -199,171 +181,208 @@
unsigned int dataacc:2;
unsigned short count;
unsigned int addr;
-} breakinfo[HW_BREAKPOINT_NUM];
+} breakinfo[HW_WATCHPOINT_NUM];
-int kgdb_arch_init(void)
-{
- debugger_step = 0;
-
- kgdb_remove_all_hw_break();
- return 0;
-}
-
-int kgdb_set_hw_break(unsigned long addr)
+int bfin_set_hw_break(unsigned long addr, int len, enum kgdb_bptype type)
{
int breakno;
- for (breakno = 0; breakno < HW_BREAKPOINT_NUM; breakno++)
- if (!breakinfo[breakno].occupied) {
+ int bfin_type;
+ int dataacc = 0;
+
+ switch (type) {
+ case BP_HARDWARE_BREAKPOINT:
+ bfin_type = TYPE_INST_WATCHPOINT;
+ break;
+ case BP_WRITE_WATCHPOINT:
+ dataacc = 1;
+ bfin_type = TYPE_DATA_WATCHPOINT;
+ break;
+ case BP_READ_WATCHPOINT:
+ dataacc = 2;
+ bfin_type = TYPE_DATA_WATCHPOINT;
+ break;
+ case BP_ACCESS_WATCHPOINT:
+ dataacc = 3;
+ bfin_type = TYPE_DATA_WATCHPOINT;
+ break;
+ default:
+ return -ENOSPC;
+ }
+
+ /* Becasue hardware data watchpoint impelemented in current
+ * Blackfin can not trigger an exception event as the hardware
+ * instrction watchpoint does, we ignaore all data watch point here.
+ * They can be turned on easily after future blackfin design
+ * supports this feature.
+ */
+ for (breakno = 0; breakno < HW_INST_WATCHPOINT_NUM; breakno++)
+ if (bfin_type == breakinfo[breakno].type
+ && !breakinfo[breakno].occupied) {
breakinfo[breakno].occupied = 1;
breakinfo[breakno].enabled = 1;
- breakinfo[breakno].type = 1;
breakinfo[breakno].addr = addr;
+ breakinfo[breakno].dataacc = dataacc;
+ breakinfo[breakno].count = 0;
return 0;
}
return -ENOSPC;
}
-int kgdb_remove_hw_break(unsigned long addr)
+int bfin_remove_hw_break(unsigned long addr, int len, enum kgdb_bptype type)
{
int breakno;
- for (breakno = 0; breakno < HW_BREAKPOINT_NUM; breakno++)
- if (breakinfo[breakno].addr == addr)
- memset(&(breakinfo[breakno]), 0, sizeof(struct hw_breakpoint));
+ int bfin_type;
+
+ switch (type) {
+ case BP_HARDWARE_BREAKPOINT:
+ bfin_type = TYPE_INST_WATCHPOINT;
+ break;
+ case BP_WRITE_WATCHPOINT:
+ case BP_READ_WATCHPOINT:
+ case BP_ACCESS_WATCHPOINT:
+ bfin_type = TYPE_DATA_WATCHPOINT;
+ break;
+ default:
+ return 0;
+ }
+ for (breakno = 0; breakno < HW_WATCHPOINT_NUM; breakno++)
+ if (bfin_type == breakinfo[breakno].type
+ && breakinfo[breakno].occupied
+ && breakinfo[breakno].addr == addr) {
+ breakinfo[breakno].occupied = 0;
+ breakinfo[breakno].enabled = 0;
+ }
return 0;
}
-void kgdb_remove_all_hw_break(void)
-{
- memset(breakinfo, 0, sizeof(struct hw_breakpoint)*8);
-}
-
-/*
-void kgdb_show_info(void)
-{
- printk(KERN_DEBUG "hwd: wpia0=0x%x, wpiacnt0=%d, wpiactl=0x%x, wpstat=0x%x\n",
- bfin_read_WPIA0(), bfin_read_WPIACNT0(),
- bfin_read_WPIACTL(), bfin_read_WPSTAT());
-}
-*/
-
-void kgdb_correct_hw_break(void)
+void bfin_remove_all_hw_break(void)
{
int breakno;
- int correctit;
- uint32_t wpdactl = bfin_read_WPDACTL();
- correctit = 0;
- for (breakno = 0; breakno < HW_BREAKPOINT_NUM; breakno++) {
- if (breakinfo[breakno].type == 1) {
+ memset(breakinfo, 0, sizeof(struct hw_breakpoint)*HW_WATCHPOINT_NUM);
+
+ for (breakno = 0; breakno < HW_INST_WATCHPOINT_NUM; breakno++)
+ breakinfo[breakno].type = TYPE_INST_WATCHPOINT;
+ for (; breakno < HW_WATCHPOINT_NUM; breakno++)
+ breakinfo[breakno].type = TYPE_DATA_WATCHPOINT;
+}
+
+void bfin_correct_hw_break(void)
+{
+ int breakno;
+ unsigned int wpiactl = 0;
+ unsigned int wpdactl = 0;
+ int enable_wp = 0;
+
+ for (breakno = 0; breakno < HW_WATCHPOINT_NUM; breakno++)
+ if (breakinfo[breakno].enabled) {
+ enable_wp = 1;
+
switch (breakno) {
case 0:
- if (breakinfo[breakno].enabled && !(wpdactl & WPIAEN0)) {
- correctit = 1;
- wpdactl &= ~(WPIREN01|EMUSW0);
- wpdactl |= WPIAEN0|WPICNTEN0;
- bfin_write_WPIA0(breakinfo[breakno].addr);
- bfin_write_WPIACNT0(breakinfo[breakno].skip);
- } else if (!breakinfo[breakno].enabled && (wpdactl & WPIAEN0)) {
- correctit = 1;
- wpdactl &= ~WPIAEN0;
- }
+ wpiactl |= WPIAEN0|WPICNTEN0;
+ bfin_write_WPIA0(breakinfo[breakno].addr);
+ bfin_write_WPIACNT0(breakinfo[breakno].count
+ + breakinfo->skip);
break;
-
case 1:
- if (breakinfo[breakno].enabled && !(wpdactl & WPIAEN1)) {
- correctit = 1;
- wpdactl &= ~(WPIREN01|EMUSW1);
- wpdactl |= WPIAEN1|WPICNTEN1;
- bfin_write_WPIA1(breakinfo[breakno].addr);
- bfin_write_WPIACNT1(breakinfo[breakno].skip);
- } else if (!breakinfo[breakno].enabled && (wpdactl & WPIAEN1)) {
- correctit = 1;
- wpdactl &= ~WPIAEN1;
- }
+ wpiactl |= WPIAEN1|WPICNTEN1;
+ bfin_write_WPIA1(breakinfo[breakno].addr);
+ bfin_write_WPIACNT1(breakinfo[breakno].count
+ + breakinfo->skip);
break;
-
case 2:
- if (breakinfo[breakno].enabled && !(wpdactl & WPIAEN2)) {
- correctit = 1;
- wpdactl &= ~(WPIREN23|EMUSW2);
- wpdactl |= WPIAEN2|WPICNTEN2;
- bfin_write_WPIA2(breakinfo[breakno].addr);
- bfin_write_WPIACNT2(breakinfo[breakno].skip);
- } else if (!breakinfo[breakno].enabled && (wpdactl & WPIAEN2)) {
- correctit = 1;
- wpdactl &= ~WPIAEN2;
- }
+ wpiactl |= WPIAEN2|WPICNTEN2;
+ bfin_write_WPIA2(breakinfo[breakno].addr);
+ bfin_write_WPIACNT2(breakinfo[breakno].count
+ + breakinfo->skip);
break;
-
case 3:
- if (breakinfo[breakno].enabled && !(wpdactl & WPIAEN3)) {
- correctit = 1;
- wpdactl &= ~(WPIREN23|EMUSW3);
- wpdactl |= WPIAEN3|WPICNTEN3;
- bfin_write_WPIA3(breakinfo[breakno].addr);
- bfin_write_WPIACNT3(breakinfo[breakno].skip);
- } else if (!breakinfo[breakno].enabled && (wpdactl & WPIAEN3)) {
- correctit = 1;
- wpdactl &= ~WPIAEN3;
- }
+ wpiactl |= WPIAEN3|WPICNTEN3;
+ bfin_write_WPIA3(breakinfo[breakno].addr);
+ bfin_write_WPIACNT3(breakinfo[breakno].count
+ + breakinfo->skip);
break;
case 4:
- if (breakinfo[breakno].enabled && !(wpdactl & WPIAEN4)) {
- correctit = 1;
- wpdactl &= ~(WPIREN45|EMUSW4);
- wpdactl |= WPIAEN4|WPICNTEN4;
- bfin_write_WPIA4(breakinfo[breakno].addr);
- bfin_write_WPIACNT4(breakinfo[breakno].skip);
- } else if (!breakinfo[breakno].enabled && (wpdactl & WPIAEN4)) {
- correctit = 1;
- wpdactl &= ~WPIAEN4;
- }
+ wpiactl |= WPIAEN4|WPICNTEN4;
+ bfin_write_WPIA4(breakinfo[breakno].addr);
+ bfin_write_WPIACNT4(breakinfo[breakno].count
+ + breakinfo->skip);
break;
case 5:
- if (breakinfo[breakno].enabled && !(wpdactl & WPIAEN5)) {
- correctit = 1;
- wpdactl &= ~(WPIREN45|EMUSW5);
- wpdactl |= WPIAEN5|WPICNTEN5;
- bfin_write_WPIA5(breakinfo[breakno].addr);
- bfin_write_WPIACNT5(breakinfo[breakno].skip);
- } else if (!breakinfo[breakno].enabled && (wpdactl & WPIAEN5)) {
- correctit = 1;
- wpdactl &= ~WPIAEN5;
- }
+ wpiactl |= WPIAEN5|WPICNTEN5;
+ bfin_write_WPIA5(breakinfo[breakno].addr);
+ bfin_write_WPIACNT5(breakinfo[breakno].count
+ + breakinfo->skip);
+ break;
+ case 6:
+ wpdactl |= WPDAEN0|WPDCNTEN0|WPDSRC0;
+ wpdactl |= breakinfo[breakno].dataacc
+ << WPDACC0_OFFSET;
+ bfin_write_WPDA0(breakinfo[breakno].addr);
+ bfin_write_WPDACNT0(breakinfo[breakno].count
+ + breakinfo->skip);
+ break;
+ case 7:
+ wpdactl |= WPDAEN1|WPDCNTEN1|WPDSRC1;
+ wpdactl |= breakinfo[breakno].dataacc
+ << WPDACC1_OFFSET;
+ bfin_write_WPDA1(breakinfo[breakno].addr);
+ bfin_write_WPDACNT1(breakinfo[breakno].count
+ + breakinfo->skip);
break;
}
}
- }
- if (correctit) {
- wpdactl &= ~WPAND;
- wpdactl |= WPPWR;
- /*printk("correct_hw_break: wpdactl=0x%x\n", wpdactl);*/
+
+ /* Should enable WPPWR bit first before set any other
+ * WPIACTL and WPDACTL bits */
+ if (enable_wp) {
+ bfin_write_WPIACTL(WPPWR);
+ CSYNC();
+ bfin_write_WPIACTL(wpiactl|WPPWR);
bfin_write_WPDACTL(wpdactl);
CSYNC();
- /*kgdb_show_info();*/
}
}
void kgdb_disable_hw_debug(struct pt_regs *regs)
{
/* Disable hardware debugging while we are in kgdb */
- bfin_write_WPIACTL(bfin_read_WPIACTL() & ~0x1);
+ bfin_write_WPIACTL(0);
+ bfin_write_WPDACTL(0);
CSYNC();
}
-void kgdb_post_master_code(struct pt_regs *regs, int eVector, int err_code)
+#ifdef CONFIG_SMP
+void kgdb_passive_cpu_callback(void *info)
{
- /* Master processor is completely in the debugger */
- gdb_bf533vector = eVector;
- gdb_bf533errcode = err_code;
+ kgdb_nmicallback(raw_smp_processor_id(), get_irq_regs());
}
-int kgdb_arch_handle_exception(int exceptionVector, int signo,
+void kgdb_roundup_cpus(unsigned long flags)
+{
+ smp_call_function(kgdb_passive_cpu_callback, NULL, 0, 0);
+}
+
+void kgdb_roundup_cpu(int cpu, unsigned long flags)
+{
+ smp_call_function_single(cpu, kgdb_passive_cpu_callback, NULL, 0, 0);
+}
+#endif
+
+void kgdb_post_primary_code(struct pt_regs *regs, int eVector, int err_code)
+{
+ /* Master processor is completely in the debugger */
+ gdb_bfin_vector = eVector;
+ gdb_bfin_errcode = err_code;
+}
+
+int kgdb_arch_handle_exception(int vector, int signo,
int err_code, char *remcom_in_buffer,
char *remcom_out_buffer,
- struct pt_regs *linux_regs)
+ struct pt_regs *regs)
{
long addr;
long breakno;
@@ -385,44 +404,40 @@
/* try to read optional parameter, pc unchanged if no parm */
ptr = &remcom_in_buffer[1];
if (kgdb_hex2long(&ptr, &addr)) {
- linux_regs->retx = addr;
+ regs->retx = addr;
}
- newPC = linux_regs->retx;
+ newPC = regs->retx;
/* clear the trace bit */
- linux_regs->syscfg &= 0xfffffffe;
+ regs->syscfg &= 0xfffffffe;
/* set the trace bit if we're stepping */
if (remcom_in_buffer[0] == 's') {
- linux_regs->syscfg |= 0x1;
- debugger_step = linux_regs->ipend;
- debugger_step >>= 6;
- for (i = 10; i > 0; i--, debugger_step >>= 1)
- if (debugger_step & 1)
+ regs->syscfg |= 0x1;
+ kgdb_single_step = regs->ipend;
+ kgdb_single_step >>= 6;
+ for (i = 10; i > 0; i--, kgdb_single_step >>= 1)
+ if (kgdb_single_step & 1)
break;
/* i indicate event priority of current stopped instruction
* user space instruction is 0, IVG15 is 1, IVTMR is 10.
- * debugger_step > 0 means in single step mode
+ * kgdb_single_step > 0 means in single step mode
*/
- debugger_step = i + 1;
- } else {
- debugger_step = 0;
+ kgdb_single_step = i + 1;
}
- wp_status = bfin_read_WPSTAT();
- CSYNC();
-
- if (exceptionVector == VEC_WATCH) {
- for (breakno = 0; breakno < 6; ++breakno) {
+ if (vector == VEC_WATCH) {
+ wp_status = bfin_read_WPSTAT();
+ for (breakno = 0; breakno < HW_WATCHPOINT_NUM; breakno++) {
if (wp_status & (1 << breakno)) {
breakinfo->skip = 1;
break;
}
}
+ bfin_write_WPSTAT(0);
}
- kgdb_correct_hw_break();
- bfin_write_WPSTAT(0);
+ bfin_correct_hw_break();
return 0;
} /* switch */
@@ -431,5 +446,385 @@
struct kgdb_arch arch_kgdb_ops = {
.gdb_bpt_instr = {0xa1},
+#ifdef CONFIG_SMP
+ .flags = KGDB_HW_BREAKPOINT|KGDB_THR_PROC_SWAP,
+#else
.flags = KGDB_HW_BREAKPOINT,
+#endif
+ .set_hw_breakpoint = bfin_set_hw_break,
+ .remove_hw_breakpoint = bfin_remove_hw_break,
+ .remove_all_hw_break = bfin_remove_all_hw_break,
+ .correct_hw_break = bfin_correct_hw_break,
};
+
+static int hex(char ch)
+{
+ if ((ch >= 'a') && (ch <= 'f'))
+ return ch - 'a' + 10;
+ if ((ch >= '0') && (ch <= '9'))
+ return ch - '0';
+ if ((ch >= 'A') && (ch <= 'F'))
+ return ch - 'A' + 10;
+ return -1;
+}
+
+static int validate_memory_access_address(unsigned long addr, int size)
+{
+ int cpu = raw_smp_processor_id();
+
+ if (size < 0)
+ return EFAULT;
+ if (addr >= 0x1000 && (addr + size) <= physical_mem_end)
+ return 0;
+ if (addr >= SYSMMR_BASE)
+ return 0;
+ if (addr >= ASYNC_BANK0_BASE
+ && addr + size <= ASYNC_BANK3_BASE + ASYNC_BANK3_SIZE)
+ return 0;
+ if (cpu == 0) {
+ if (addr >= L1_SCRATCH_START
+ && (addr + size <= L1_SCRATCH_START + L1_SCRATCH_LENGTH))
+ return 0;
+#if L1_CODE_LENGTH != 0
+ if (addr >= L1_CODE_START
+ && (addr + size <= L1_CODE_START + L1_CODE_LENGTH))
+ return 0;
+#endif
+#if L1_DATA_A_LENGTH != 0
+ if (addr >= L1_DATA_A_START
+ && (addr + size <= L1_DATA_A_START + L1_DATA_A_LENGTH))
+ return 0;
+#endif
+#if L1_DATA_B_LENGTH != 0
+ if (addr >= L1_DATA_B_START
+ && (addr + size <= L1_DATA_B_START + L1_DATA_B_LENGTH))
+ return 0;
+#endif
+#ifdef CONFIG_SMP
+ } else if (cpu == 1) {
+ if (addr >= COREB_L1_SCRATCH_START
+ && (addr + size <= COREB_L1_SCRATCH_START
+ + L1_SCRATCH_LENGTH))
+ return 0;
+# if L1_CODE_LENGTH != 0
+ if (addr >= COREB_L1_CODE_START
+ && (addr + size <= COREB_L1_CODE_START + L1_CODE_LENGTH))
+ return 0;
+# endif
+# if L1_DATA_A_LENGTH != 0
+ if (addr >= COREB_L1_DATA_A_START
+ && (addr + size <= COREB_L1_DATA_A_START + L1_DATA_A_LENGTH))
+ return 0;
+# endif
+# if L1_DATA_B_LENGTH != 0
+ if (addr >= COREB_L1_DATA_B_START
+ && (addr + size <= COREB_L1_DATA_B_START + L1_DATA_B_LENGTH))
+ return 0;
+# endif
+#endif
+ }
+
+#if L2_LENGTH != 0
+ if (addr >= L2_START
+ && addr + size <= L2_START + L2_LENGTH)
+ return 0;
+#endif
+
+ return EFAULT;
+}
+
+/*
+ * Convert the memory pointed to by mem into hex, placing result in buf.
+ * Return a pointer to the last char put in buf (null). May return an error.
+ */
+int kgdb_mem2hex(char *mem, char *buf, int count)
+{
+ char *tmp;
+ int err = 0;
+ unsigned char *pch;
+ unsigned short mmr16;
+ unsigned long mmr32;
+ int cpu = raw_smp_processor_id();
+
+ if (validate_memory_access_address((unsigned long)mem, count))
+ return EFAULT;
+
+ /*
+ * We use the upper half of buf as an intermediate buffer for the
+ * raw memory copy. Hex conversion will work against this one.
+ */
+ tmp = buf + count;
+
+ if ((unsigned int)mem >= SYSMMR_BASE) { /*access MMR registers*/
+ switch (count) {
+ case 2:
+ if ((unsigned int)mem % 2 == 0) {
+ mmr16 = *(unsigned short *)mem;
+ pch = (unsigned char *)&mmr16;
+ *tmp++ = *pch++;
+ *tmp++ = *pch++;
+ tmp -= 2;
+ } else
+ err = EFAULT;
+ break;
+ case 4:
+ if ((unsigned int)mem % 4 == 0) {
+ mmr32 = *(unsigned long *)mem;
+ pch = (unsigned char *)&mmr32;
+ *tmp++ = *pch++;
+ *tmp++ = *pch++;
+ *tmp++ = *pch++;
+ *tmp++ = *pch++;
+ tmp -= 4;
+ } else
+ err = EFAULT;
+ break;
+ default:
+ err = EFAULT;
+ }
+ } else if (cpu == 0 && (unsigned int)mem >= L1_CODE_START &&
+ (unsigned int)(mem + count) <= L1_CODE_START + L1_CODE_LENGTH
+#ifdef CONFIG_SMP
+ || cpu == 1 && (unsigned int)mem >= COREB_L1_CODE_START &&
+ (unsigned int)(mem + count) <=
+ COREB_L1_CODE_START + L1_CODE_LENGTH
+#endif
+ ) {
+ /* access L1 instruction SRAM*/
+ if (dma_memcpy(tmp, mem, count) == NULL)
+ err = EFAULT;
+ } else
+ err = probe_kernel_read(tmp, mem, count);
+
+ if (!err) {
+ while (count > 0) {
+ buf = pack_hex_byte(buf, *tmp);
+ tmp++;
+ count--;
+ }
+
+ *buf = 0;
+ }
+
+ return err;
+}
+
+/*
+ * Copy the binary array pointed to by buf into mem. Fix $, #, and
+ * 0x7d escaped with 0x7d. Return a pointer to the character after
+ * the last byte written.
+ */
+int kgdb_ebin2mem(char *buf, char *mem, int count)
+{
+ char *tmp_old;
+ char *tmp_new;
+ unsigned short *mmr16;
+ unsigned long *mmr32;
+ int err = 0;
+ int size = 0;
+ int cpu = raw_smp_processor_id();
+
+ tmp_old = tmp_new = buf;
+
+ while (count-- > 0) {
+ if (*tmp_old == 0x7d)
+ *tmp_new = *(++tmp_old) ^ 0x20;
+ else
+ *tmp_new = *tmp_old;
+ tmp_new++;
+ tmp_old++;
+ size++;
+ }
+
+ if (validate_memory_access_address((unsigned long)mem, size))
+ return EFAULT;
+
+ if ((unsigned int)mem >= SYSMMR_BASE) { /*access MMR registers*/
+ switch (size) {
+ case 2:
+ if ((unsigned int)mem % 2 == 0) {
+ mmr16 = (unsigned short *)buf;
+ *(unsigned short *)mem = *mmr16;
+ } else
+ return EFAULT;
+ break;
+ case 4:
+ if ((unsigned int)mem % 4 == 0) {
+ mmr32 = (unsigned long *)buf;
+ *(unsigned long *)mem = *mmr32;
+ } else
+ return EFAULT;
+ break;
+ default:
+ return EFAULT;
+ }
+ } else if (cpu == 0 && (unsigned int)mem >= L1_CODE_START &&
+ (unsigned int)(mem + count) < L1_CODE_START + L1_CODE_LENGTH
+#ifdef CONFIG_SMP
+ || cpu == 1 && (unsigned int)mem >= COREB_L1_CODE_START &&
+ (unsigned int)(mem + count) <=
+ COREB_L1_CODE_START + L1_CODE_LENGTH
+#endif
+ ) {
+ /* access L1 instruction SRAM */
+ if (dma_memcpy(mem, buf, size) == NULL)
+ err = EFAULT;
+ } else
+ err = probe_kernel_write(mem, buf, size);
+
+ return err;
+}
+
+/*
+ * Convert the hex array pointed to by buf into binary to be placed in mem.
+ * Return a pointer to the character AFTER the last byte written.
+ * May return an error.
+ */
+int kgdb_hex2mem(char *buf, char *mem, int count)
+{
+ char *tmp_raw;
+ char *tmp_hex;
+ unsigned short *mmr16;
+ unsigned long *mmr32;
+ int cpu = raw_smp_processor_id();
+
+ if (validate_memory_access_address((unsigned long)mem, count))
+ return EFAULT;
+
+ /*
+ * We use the upper half of buf as an intermediate buffer for the
+ * raw memory that is converted from hex.
+ */
+ tmp_raw = buf + count * 2;
+
+ tmp_hex = tmp_raw - 1;
+ while (tmp_hex >= buf) {
+ tmp_raw--;
+ *tmp_raw = hex(*tmp_hex--);
+ *tmp_raw |= hex(*tmp_hex--) << 4;
+ }
+
+ if ((unsigned int)mem >= SYSMMR_BASE) { /*access MMR registers*/
+ switch (count) {
+ case 2:
+ if ((unsigned int)mem % 2 == 0) {
+ mmr16 = (unsigned short *)tmp_raw;
+ *(unsigned short *)mem = *mmr16;
+ } else
+ return EFAULT;
+ break;
+ case 4:
+ if ((unsigned int)mem % 4 == 0) {
+ mmr32 = (unsigned long *)tmp_raw;
+ *(unsigned long *)mem = *mmr32;
+ } else
+ return EFAULT;
+ break;
+ default:
+ return EFAULT;
+ }
+ } else if (cpu == 0 && (unsigned int)mem >= L1_CODE_START &&
+ (unsigned int)(mem + count) <= L1_CODE_START + L1_CODE_LENGTH
+#ifdef CONFIG_SMP
+ || cpu == 1 && (unsigned int)mem >= COREB_L1_CODE_START &&
+ (unsigned int)(mem + count) <=
+ COREB_L1_CODE_START + L1_CODE_LENGTH
+#endif
+ ) {
+ /* access L1 instruction SRAM */
+ if (dma_memcpy(mem, tmp_raw, count) == NULL)
+ return EFAULT;
+ } else
+ return probe_kernel_write(mem, tmp_raw, count);
+ return 0;
+}
+
+int kgdb_validate_break_address(unsigned long addr)
+{
+ int cpu = raw_smp_processor_id();
+
+ if (addr >= 0x1000 && (addr + BREAK_INSTR_SIZE) <= physical_mem_end)
+ return 0;
+ if (addr >= ASYNC_BANK0_BASE
+ && addr + BREAK_INSTR_SIZE <= ASYNC_BANK3_BASE + ASYNC_BANK3_BASE)
+ return 0;
+#if L1_CODE_LENGTH != 0
+ if (cpu == 0 && addr >= L1_CODE_START
+ && addr + BREAK_INSTR_SIZE <= L1_CODE_START + L1_CODE_LENGTH)
+ return 0;
+# ifdef CONFIG_SMP
+ else if (cpu == 1 && addr >= COREB_L1_CODE_START
+ && addr + BREAK_INSTR_SIZE <= COREB_L1_CODE_START + L1_CODE_LENGTH)
+ return 0;
+# endif
+#endif
+#if L2_LENGTH != 0
+ if (addr >= L2_START
+ && addr + BREAK_INSTR_SIZE <= L2_START + L2_LENGTH)
+ return 0;
+#endif
+
+ return EFAULT;
+}
+
+int kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr)
+{
+ int err;
+ int cpu = raw_smp_processor_id();
+
+ if ((cpu == 0 && (unsigned int)addr >= L1_CODE_START
+ && (unsigned int)(addr + BREAK_INSTR_SIZE)
+ < L1_CODE_START + L1_CODE_LENGTH)
+#ifdef CONFIG_SMP
+ || (cpu == 1 && (unsigned int)addr >= COREB_L1_CODE_START
+ && (unsigned int)(addr + BREAK_INSTR_SIZE)
+ < COREB_L1_CODE_START + L1_CODE_LENGTH)
+#endif
+ ) {
+ /* access L1 instruction SRAM */
+ if (dma_memcpy(saved_instr, (void *)addr, BREAK_INSTR_SIZE)
+ == NULL)
+ return -EFAULT;
+
+ if (dma_memcpy((void *)addr, arch_kgdb_ops.gdb_bpt_instr,
+ BREAK_INSTR_SIZE) == NULL)
+ return -EFAULT;
+
+ return 0;
+ } else {
+ err = probe_kernel_read(saved_instr, (char *)addr,
+ BREAK_INSTR_SIZE);
+ if (err)
+ return err;
+
+ return probe_kernel_write((char *)addr,
+ arch_kgdb_ops.gdb_bpt_instr, BREAK_INSTR_SIZE);
+ }
+}
+
+int kgdb_arch_remove_breakpoint(unsigned long addr, char *bundle)
+{
+ if ((unsigned int)addr >= L1_CODE_START &&
+ (unsigned int)(addr + BREAK_INSTR_SIZE) <
+ L1_CODE_START + L1_CODE_LENGTH) {
+ /* access L1 instruction SRAM */
+ if (dma_memcpy((void *)addr, bundle, BREAK_INSTR_SIZE) == NULL)
+ return -EFAULT;
+
+ return 0;
+ } else
+ return probe_kernel_write((char *)addr,
+ (char *)bundle, BREAK_INSTR_SIZE);
+}
+
+int kgdb_arch_init(void)
+{
+ kgdb_single_step = 0;
+
+ bfin_remove_all_hw_break();
+ return 0;
+}
+
+void kgdb_arch_exit(void)
+{
+}
diff --git a/arch/blackfin/kernel/ptrace.c b/arch/blackfin/kernel/ptrace.c
index bf1a51d..140bf00 100644
--- a/arch/blackfin/kernel/ptrace.c
+++ b/arch/blackfin/kernel/ptrace.c
@@ -46,7 +46,6 @@
#include <asm/dma.h>
#include <asm/fixed_code.h>
-#define MAX_SHARED_LIBS 3
#define TEXT_OFFSET 0
/*
* does not yet catch signals sent when the child dies.
@@ -161,21 +160,32 @@
struct vm_list_struct *vml;
struct sram_list_struct *sraml;
+ /* overflow */
+ if (start + len < start)
+ return -EIO;
+
for (vml = child->mm->context.vmlist; vml; vml = vml->next)
- if (start >= vml->vma->vm_start && start + len <= vml->vma->vm_end)
+ if (start >= vml->vma->vm_start && start + len < vml->vma->vm_end)
return 0;
for (sraml = child->mm->context.sram_list; sraml; sraml = sraml->next)
if (start >= (unsigned long)sraml->addr
- && start + len <= (unsigned long)sraml->addr + sraml->length)
+ && start + len < (unsigned long)sraml->addr + sraml->length)
return 0;
- if (start >= FIXED_CODE_START && start + len <= FIXED_CODE_END)
+ if (start >= FIXED_CODE_START && start + len < FIXED_CODE_END)
return 0;
return -EIO;
}
+void ptrace_enable(struct task_struct *child)
+{
+ unsigned long tmp;
+ tmp = get_reg(child, PT_SYSCFG) | (TRACE_BITS);
+ put_reg(child, PT_SYSCFG, tmp);
+}
+
/*
* Called by kernel/ptrace.c when detaching..
*
@@ -192,14 +202,12 @@
long arch_ptrace(struct task_struct *child, long request, long addr, long data)
{
int ret;
- int add = 0;
unsigned long __user *datap = (unsigned long __user *)data;
switch (request) {
/* when I and D space are separate, these will need to be fixed. */
case PTRACE_PEEKDATA:
pr_debug("ptrace: PEEKDATA\n");
- add = MAX_SHARED_LIBS * 4; /* space between text and data */
/* fall through */
case PTRACE_PEEKTEXT: /* read word at location addr. */
{
@@ -207,40 +215,35 @@
int copied;
ret = -EIO;
- pr_debug("ptrace: PEEKTEXT at addr 0x%08lx + add %d %ld\n", addr, add,
- sizeof(data));
- if (is_user_addr_valid(child, addr + add, sizeof(tmp)) < 0)
+ pr_debug("ptrace: PEEKTEXT at addr 0x%08lx + %ld\n", addr, sizeof(data));
+ if (is_user_addr_valid(child, addr, sizeof(tmp)) < 0)
break;
pr_debug("ptrace: user address is valid\n");
-#if L1_CODE_LENGTH != 0
- if (addr + add >= L1_CODE_START
- && addr + add + sizeof(tmp) <= L1_CODE_START + L1_CODE_LENGTH) {
- safe_dma_memcpy (&tmp, (const void *)(addr + add), sizeof(tmp));
+ if (L1_CODE_LENGTH != 0 && addr >= L1_CODE_START
+ && addr + sizeof(tmp) <= L1_CODE_START + L1_CODE_LENGTH) {
+ safe_dma_memcpy (&tmp, (const void *)(addr), sizeof(tmp));
copied = sizeof(tmp);
- } else
-#endif
-#if L1_DATA_A_LENGTH != 0
- if (addr + add >= L1_DATA_A_START
- && addr + add + sizeof(tmp) <= L1_DATA_A_START + L1_DATA_A_LENGTH) {
- memcpy(&tmp, (const void *)(addr + add), sizeof(tmp));
+
+ } else if (L1_DATA_A_LENGTH != 0 && addr >= L1_DATA_A_START
+ && addr + sizeof(tmp) <= L1_DATA_A_START + L1_DATA_A_LENGTH) {
+ memcpy(&tmp, (const void *)(addr), sizeof(tmp));
copied = sizeof(tmp);
- } else
-#endif
-#if L1_DATA_B_LENGTH != 0
- if (addr + add >= L1_DATA_B_START
- && addr + add + sizeof(tmp) <= L1_DATA_B_START + L1_DATA_B_LENGTH) {
- memcpy(&tmp, (const void *)(addr + add), sizeof(tmp));
+
+ } else if (L1_DATA_B_LENGTH != 0 && addr >= L1_DATA_B_START
+ && addr + sizeof(tmp) <= L1_DATA_B_START + L1_DATA_B_LENGTH) {
+ memcpy(&tmp, (const void *)(addr), sizeof(tmp));
copied = sizeof(tmp);
- } else
-#endif
- if (addr + add >= FIXED_CODE_START
- && addr + add + sizeof(tmp) <= FIXED_CODE_END) {
- memcpy(&tmp, (const void *)(addr + add), sizeof(tmp));
+
+ } else if (addr >= FIXED_CODE_START
+ && addr + sizeof(tmp) <= FIXED_CODE_END) {
+ memcpy(&tmp, (const void *)(addr), sizeof(tmp));
copied = sizeof(tmp);
+
} else
- copied = access_process_vm(child, addr + add, &tmp,
+ copied = access_process_vm(child, addr, &tmp,
sizeof(tmp), 0);
+
pr_debug("ptrace: copied size %d [0x%08lx]\n", copied, tmp);
if (copied != sizeof(tmp))
break;
@@ -284,47 +287,43 @@
/* when I and D space are separate, this will have to be fixed. */
case PTRACE_POKEDATA:
- printk(KERN_NOTICE "ptrace: PTRACE_PEEKDATA\n");
+ pr_debug("ptrace: PTRACE_PEEKDATA\n");
/* fall through */
case PTRACE_POKETEXT: /* write the word at location addr. */
{
int copied;
ret = -EIO;
- pr_debug("ptrace: POKETEXT at addr 0x%08lx + add %d %ld bytes %lx\n",
- addr, add, sizeof(data), data);
- if (is_user_addr_valid(child, addr + add, sizeof(data)) < 0)
+ pr_debug("ptrace: POKETEXT at addr 0x%08lx + %ld bytes %lx\n",
+ addr, sizeof(data), data);
+ if (is_user_addr_valid(child, addr, sizeof(data)) < 0)
break;
pr_debug("ptrace: user address is valid\n");
-#if L1_CODE_LENGTH != 0
- if (addr + add >= L1_CODE_START
- && addr + add + sizeof(data) <= L1_CODE_START + L1_CODE_LENGTH) {
- safe_dma_memcpy ((void *)(addr + add), &data, sizeof(data));
+ if (L1_CODE_LENGTH != 0 && addr >= L1_CODE_START
+ && addr + sizeof(data) <= L1_CODE_START + L1_CODE_LENGTH) {
+ safe_dma_memcpy ((void *)(addr), &data, sizeof(data));
copied = sizeof(data);
- } else
-#endif
-#if L1_DATA_A_LENGTH != 0
- if (addr + add >= L1_DATA_A_START
- && addr + add + sizeof(data) <= L1_DATA_A_START + L1_DATA_A_LENGTH) {
- memcpy((void *)(addr + add), &data, sizeof(data));
+
+ } else if (L1_DATA_A_LENGTH != 0 && addr >= L1_DATA_A_START
+ && addr + sizeof(data) <= L1_DATA_A_START + L1_DATA_A_LENGTH) {
+ memcpy((void *)(addr), &data, sizeof(data));
copied = sizeof(data);
- } else
-#endif
-#if L1_DATA_B_LENGTH != 0
- if (addr + add >= L1_DATA_B_START
- && addr + add + sizeof(data) <= L1_DATA_B_START + L1_DATA_B_LENGTH) {
- memcpy((void *)(addr + add), &data, sizeof(data));
+
+ } else if (L1_DATA_B_LENGTH != 0 && addr >= L1_DATA_B_START
+ && addr + sizeof(data) <= L1_DATA_B_START + L1_DATA_B_LENGTH) {
+ memcpy((void *)(addr), &data, sizeof(data));
copied = sizeof(data);
- } else
-#endif
- if (addr + add >= FIXED_CODE_START
- && addr + add + sizeof(data) <= FIXED_CODE_END) {
- memcpy((void *)(addr + add), &data, sizeof(data));
+
+ } else if (addr >= FIXED_CODE_START
+ && addr + sizeof(data) <= FIXED_CODE_END) {
+ memcpy((void *)(addr), &data, sizeof(data));
copied = sizeof(data);
+
} else
- copied = access_process_vm(child, addr + add, &data,
+ copied = access_process_vm(child, addr, &data,
sizeof(data), 1);
+
pr_debug("ptrace: copied size %d\n", copied);
if (copied != sizeof(data))
break;
@@ -351,29 +350,22 @@
break;
case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
- case PTRACE_CONT:
- { /* restart after signal. */
- long tmp;
+ case PTRACE_CONT: /* restart after signal. */
+ pr_debug("ptrace: syscall/cont\n");
- pr_debug("ptrace_cont\n");
-
- ret = -EIO;
- if (!valid_signal(data))
- break;
- if (request == PTRACE_SYSCALL)
- set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- else
- clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
-
- child->exit_code = data;
- /* make sure the single step bit is not set. */
- tmp = get_reg(child, PT_SYSCFG) & ~(TRACE_BITS);
- put_reg(child, PT_SYSCFG, tmp);
- pr_debug("before wake_up_process\n");
- wake_up_process(child);
- ret = 0;
+ ret = -EIO;
+ if (!valid_signal(data))
break;
- }
+ if (request == PTRACE_SYSCALL)
+ set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+ else
+ clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+ child->exit_code = data;
+ ptrace_disable(child);
+ pr_debug("ptrace: before wake_up_process\n");
+ wake_up_process(child);
+ ret = 0;
+ break;
/*
* make the child exit. Best I can do is send it a sigkill.
@@ -381,55 +373,37 @@
* exit.
*/
case PTRACE_KILL:
- {
- long tmp;
- ret = 0;
- if (child->exit_state == EXIT_ZOMBIE) /* already dead */
- break;
- child->exit_code = SIGKILL;
- /* make sure the single step bit is not set. */
- tmp = get_reg(child, PT_SYSCFG) & ~(TRACE_BITS);
- put_reg(child, PT_SYSCFG, tmp);
- wake_up_process(child);
+ ret = 0;
+ if (child->exit_state == EXIT_ZOMBIE) /* already dead */
break;
- }
+ child->exit_code = SIGKILL;
+ ptrace_disable(child);
+ wake_up_process(child);
+ break;
- case PTRACE_SINGLESTEP:
- { /* set the trap flag. */
- long tmp;
-
- pr_debug("single step\n");
- ret = -EIO;
- if (!valid_signal(data))
- break;
- clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
-
- tmp = get_reg(child, PT_SYSCFG) | (TRACE_BITS);
- put_reg(child, PT_SYSCFG, tmp);
-
- child->exit_code = data;
- /* give it a chance to run. */
- wake_up_process(child);
- ret = 0;
+ case PTRACE_SINGLESTEP: /* set the trap flag. */
+ pr_debug("ptrace: single step\n");
+ ret = -EIO;
+ if (!valid_signal(data))
break;
- }
+ clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+ ptrace_enable(child);
+ child->exit_code = data;
+ wake_up_process(child);
+ ret = 0;
+ break;
case PTRACE_GETREGS:
- {
-
- /* Get all gp regs from the child. */
- ret = ptrace_getregs(child, datap);
- break;
- }
+ /* Get all gp regs from the child. */
+ ret = ptrace_getregs(child, datap);
+ break;
case PTRACE_SETREGS:
- {
- printk(KERN_NOTICE
- "ptrace: SETREGS: **** NOT IMPLEMENTED ***\n");
- /* Set all gp regs in the child. */
- ret = 0;
- break;
- }
+ printk(KERN_WARNING "ptrace: SETREGS: **** NOT IMPLEMENTED ***\n");
+ /* Set all gp regs in the child. */
+ ret = 0;
+ break;
+
default:
ret = ptrace_request(child, request, addr, data);
break;
@@ -440,7 +414,6 @@
asmlinkage void syscall_trace(void)
{
-
if (!test_thread_flag(TIF_SYSCALL_TRACE))
return;
diff --git a/arch/blackfin/kernel/reboot.c b/arch/blackfin/kernel/reboot.c
index 367e2dc..ae97ca4 100644
--- a/arch/blackfin/kernel/reboot.c
+++ b/arch/blackfin/kernel/reboot.c
@@ -10,6 +10,7 @@
#include <asm/bfin-global.h>
#include <asm/reboot.h>
#include <asm/system.h>
+#include <asm/bfrom.h>
/* A system soft reset makes external memory unusable so force
* this function into L1. We use the compiler ssync here rather
@@ -20,7 +21,7 @@
* the core reset.
*/
__attribute__((l1_text))
-void bfin_reset(void)
+static void bfin_reset(void)
{
/* Wait for completion of "system" events such as cache line
* line fills so that we avoid infinite stalls later on as
@@ -34,15 +35,15 @@
bfin_write_SWRST(0x7);
/* Due to the way reset is handled in the hardware, we need
- * to delay for 7 SCLKS. The only reliable way to do this is
- * to calculate the CCLK/SCLK ratio and multiply 7. For now,
+ * to delay for 10 SCLKS. The only reliable way to do this is
+ * to calculate the CCLK/SCLK ratio and multiply 10. For now,
* we'll assume worse case which is a 1:15 ratio.
*/
asm(
"LSETUP (1f, 1f) LC0 = %0\n"
"1: nop;"
:
- : "a" (15 * 7)
+ : "a" (15 * 10)
: "LC0", "LB0", "LT0"
);
@@ -74,7 +75,14 @@
{
native_machine_restart(cmd);
local_irq_disable();
- bfin_reset();
+ if (ANOMALY_05000353 || ANOMALY_05000386)
+ bfin_reset();
+ else
+ /* the bootrom checks to see how it was reset and will
+ * automatically perform a software reset for us when
+ * it starts executing boot
+ */
+ asm("raise 1;");
}
__attribute__((weak))
diff --git a/arch/blackfin/kernel/setup.c b/arch/blackfin/kernel/setup.c
index 7a82d10..7f35d10 100644
--- a/arch/blackfin/kernel/setup.c
+++ b/arch/blackfin/kernel/setup.c
@@ -42,6 +42,7 @@
EXPORT_SYMBOL(memory_end);
EXPORT_SYMBOL(physical_mem_end);
EXPORT_SYMBOL(_ramend);
+EXPORT_SYMBOL(reserved_mem_dcache_on);
#ifdef CONFIG_MTD_UCLINUX
unsigned long memory_mtd_end, memory_mtd_start, mtd_size;
@@ -52,7 +53,8 @@
#endif
char __initdata command_line[COMMAND_LINE_SIZE];
-unsigned int __initdata *__retx;
+void __initdata *init_retx, *init_saved_retx, *init_saved_seqstat,
+ *init_saved_icplb_fault_addr, *init_saved_dcplb_fault_addr;
/* boot memmap, for parsing "memmap=" */
#define BFIN_MEMMAP_MAX 128 /* number of entries in bfin_memmap */
@@ -77,10 +79,10 @@
static struct bfin_memmap_entry *overlap_list[BFIN_MEMMAP_MAX] __initdata;
static struct bfin_memmap_entry new_map[BFIN_MEMMAP_MAX] __initdata;
-void __init bf53x_cache_init(void)
+void __init bfin_cache_init(void)
{
#if defined(CONFIG_BFIN_DCACHE) || defined(CONFIG_BFIN_ICACHE)
- generate_cpl_tables();
+ generate_cplb_tables();
#endif
#ifdef CONFIG_BFIN_ICACHE
@@ -100,7 +102,7 @@
#endif
}
-void __init bf53x_relocate_l1_mem(void)
+void __init bfin_relocate_l1_mem(void)
{
unsigned long l1_code_length;
unsigned long l1_data_a_length;
@@ -410,7 +412,7 @@
* [_rambase, _ramstart]: kernel image
* [memory_start, memory_end]: dynamic memory managed by kernel
* [memory_end, _ramend]: reserved memory
- * [meory_mtd_start(memory_end),
+ * [memory_mtd_start(memory_end),
* memory_mtd_start + mtd_size]: rootfs (if any)
* [_ramend - DMA_UNCACHED_REGION,
* _ramend]: uncached DMA region
@@ -782,16 +784,25 @@
_bfin_swrst = bfin_read_SWRST();
- /* If we double fault, reset the system - otherwise we hang forever */
- bfin_write_SWRST(DOUBLE_FAULT);
+#ifdef CONFIG_DEBUG_DOUBLEFAULT_PRINT
+ bfin_write_SWRST(_bfin_swrst & ~DOUBLE_FAULT);
+#endif
+#ifdef CONFIG_DEBUG_DOUBLEFAULT_RESET
+ bfin_write_SWRST(_bfin_swrst | DOUBLE_FAULT);
+#endif
- if (_bfin_swrst & RESET_DOUBLE)
- /*
- * don't decode the address, since you don't know if this
- * kernel's symbol map is the same as the crashing kernel
- */
- printk(KERN_INFO "Recovering from Double Fault event at %pF\n", __retx);
- else if (_bfin_swrst & RESET_WDOG)
+ if (_bfin_swrst & RESET_DOUBLE) {
+ printk(KERN_EMERG "Recovering from DOUBLE FAULT event\n");
+#ifdef CONFIG_DEBUG_DOUBLEFAULT
+ /* We assume the crashing kernel, and the current symbol table match */
+ printk(KERN_EMERG " While handling exception (EXCAUSE = 0x%x) at %pF\n",
+ (int)init_saved_seqstat & SEQSTAT_EXCAUSE, init_saved_retx);
+ printk(KERN_NOTICE " DCPLB_FAULT_ADDR: %pF\n", init_saved_dcplb_fault_addr);
+ printk(KERN_NOTICE " ICPLB_FAULT_ADDR: %pF\n", init_saved_icplb_fault_addr);
+#endif
+ printk(KERN_NOTICE " The instruction at %pF caused a double exception\n",
+ init_retx);
+ } else if (_bfin_swrst & RESET_WDOG)
printk(KERN_INFO "Recovering from Watchdog event\n");
else if (_bfin_swrst & RESET_SOFTWARE)
printk(KERN_NOTICE "Reset caused by Software reset\n");
@@ -803,17 +814,24 @@
printk(KERN_INFO "Compiled for ADSP-%s Rev none\n", CPU);
else
printk(KERN_INFO "Compiled for ADSP-%s Rev 0.%d\n", CPU, bfin_compiled_revid());
- if (bfin_revid() != bfin_compiled_revid()) {
- if (bfin_compiled_revid() == -1)
- printk(KERN_ERR "Warning: Compiled for Rev none, but running on Rev %d\n",
- bfin_revid());
- else if (bfin_compiled_revid() != 0xffff)
- printk(KERN_ERR "Warning: Compiled for Rev %d, but running on Rev %d\n",
- bfin_compiled_revid(), bfin_revid());
+
+ if (unlikely(CPUID != bfin_cpuid()))
+ printk(KERN_ERR "ERROR: Not running on ADSP-%s: unknown CPUID 0x%04x Rev 0.%d\n",
+ CPU, bfin_cpuid(), bfin_revid());
+ else {
+ if (bfin_revid() != bfin_compiled_revid()) {
+ if (bfin_compiled_revid() == -1)
+ printk(KERN_ERR "Warning: Compiled for Rev none, but running on Rev %d\n",
+ bfin_revid());
+ else if (bfin_compiled_revid() != 0xffff)
+ printk(KERN_ERR "Warning: Compiled for Rev %d, but running on Rev %d\n",
+ bfin_compiled_revid(), bfin_revid());
+ }
+ if (bfin_revid() <= CONFIG_BF_REV_MIN || bfin_revid() > CONFIG_BF_REV_MAX)
+ printk(KERN_ERR "Warning: Unsupported Chip Revision ADSP-%s Rev 0.%d detected\n",
+ CPU, bfin_revid());
}
- if (bfin_revid() < SUPPORTED_REVID)
- printk(KERN_ERR "Warning: Unsupported Chip Revision ADSP-%s Rev 0.%d detected\n",
- CPU, bfin_revid());
+
printk(KERN_INFO "Blackfin Linux support by http://blackfin.uclinux.org/\n");
printk(KERN_INFO "Processor Speed: %lu MHz core clock and %lu MHz System Clock\n",
@@ -850,7 +868,7 @@
!= SAFE_USER_INSTRUCTION - FIXED_CODE_START);
init_exception_vectors();
- bf53x_cache_init();
+ bfin_cache_init();
}
static int __init topology_init(void)
@@ -986,13 +1004,18 @@
}
seq_printf(m, "processor\t: %d\n"
- "vendor_id\t: %s\n"
- "cpu family\t: 0x%x\n"
- "model name\t: ADSP-%s %lu(MHz CCLK) %lu(MHz SCLK) (%s)\n"
+ "vendor_id\t: %s\n",
+ *(unsigned int *)v,
+ vendor);
+
+ if (CPUID == bfin_cpuid())
+ seq_printf(m, "cpu family\t: 0x%04x\n", CPUID);
+ else
+ seq_printf(m, "cpu family\t: Compiled for:0x%04x, running on:0x%04x\n",
+ CPUID, bfin_cpuid());
+
+ seq_printf(m, "model name\t: ADSP-%s %lu(MHz CCLK) %lu(MHz SCLK) (%s)\n"
"stepping\t: %d\n",
- 0,
- vendor,
- (bfin_read_CHIPID() & CHIPID_FAMILY),
cpu, cclk/1000000, sclk/1000000,
#ifdef CONFIG_MPU
"mpu on",
@@ -1038,7 +1061,7 @@
if ((bfin_read_DMEM_CONTROL() & (ENDCPLB | DMC_ENABLE)) != (ENDCPLB | DMC_ENABLE))
dcache_size = 0;
- if ((bfin_read_IMEM_CONTROL() & (IMC | ENICPLB)) == (IMC | ENICPLB))
+ if ((bfin_read_IMEM_CONTROL() & (IMC | ENICPLB)) != (IMC | ENICPLB))
icache_size = 0;
seq_printf(m, "cache size\t: %d KB(L1 icache) "
@@ -1127,12 +1150,18 @@
static void *c_start(struct seq_file *m, loff_t *pos)
{
- return *pos < NR_CPUS ? ((void *)0x12345678) : NULL;
+ if (*pos == 0)
+ *pos = first_cpu(cpu_online_map);
+ if (*pos >= num_online_cpus())
+ return NULL;
+
+ return pos;
}
static void *c_next(struct seq_file *m, void *v, loff_t *pos)
{
- ++*pos;
+ *pos = next_cpu(*pos, cpu_online_map);
+
return c_start(m, pos);
}
diff --git a/arch/blackfin/kernel/traps.c b/arch/blackfin/kernel/traps.c
index 9a9d508..1aa2c78 100644
--- a/arch/blackfin/kernel/traps.c
+++ b/arch/blackfin/kernel/traps.c
@@ -34,20 +34,19 @@
#include <linux/fs.h>
#include <asm/traps.h>
#include <asm/cacheflush.h>
+#include <asm/cplb.h>
#include <asm/blackfin.h>
#include <asm/irq_handler.h>
#include <linux/irq.h>
#include <asm/trace.h>
#include <asm/fixed_code.h>
-#include <asm/dma.h>
#ifdef CONFIG_KGDB
-# include <linux/debugger.h>
# include <linux/kgdb.h>
# define CHK_DEBUGGER_TRAP() \
do { \
- CHK_DEBUGGER(trapnr, sig, info.si_code, fp, ); \
+ kgdb_handle_exception(trapnr, sig, info.si_code, fp); \
} while (0)
# define CHK_DEBUGGER_TRAP_MAYBE() \
do { \
@@ -59,6 +58,15 @@
# define CHK_DEBUGGER_TRAP_MAYBE() do { } while (0)
#endif
+
+#ifdef CONFIG_VERBOSE_DEBUG
+#define verbose_printk(fmt, arg...) \
+ printk(fmt, ##arg)
+#else
+#define verbose_printk(fmt, arg...) \
+ ({ if (0) printk(fmt, ##arg); 0; })
+#endif
+
/* Initiate the event table handler */
void __init trap_init(void)
{
@@ -67,10 +75,19 @@
CSYNC();
}
-unsigned long saved_icplb_fault_addr, saved_dcplb_fault_addr;
+/*
+ * Used to save the RETX, SEQSTAT, I/D CPLB FAULT ADDR
+ * values across the transition from exception to IRQ5.
+ * We put these in L1, so they are going to be in a valid
+ * location during exception context
+ */
+__attribute__((l1_data))
+unsigned long saved_retx, saved_seqstat,
+ saved_icplb_fault_addr, saved_dcplb_fault_addr;
static void decode_address(char *buf, unsigned long address)
{
+#ifdef CONFIG_DEBUG_VERBOSE
struct vm_list_struct *vml;
struct task_struct *p;
struct mm_struct *mm;
@@ -178,16 +195,39 @@
done:
write_unlock_irqrestore(&tasklist_lock, flags);
+#else
+ sprintf(buf, " ");
+#endif
}
asmlinkage void double_fault_c(struct pt_regs *fp)
{
console_verbose();
oops_in_progress = 1;
+#ifdef CONFIG_DEBUG_VERBOSE
printk(KERN_EMERG "\n" KERN_EMERG "Double Fault\n");
- dump_bfin_process(fp);
- dump_bfin_mem(fp);
- show_regs(fp);
+#ifdef CONFIG_DEBUG_DOUBLEFAULT_PRINT
+ if (((long)fp->seqstat & SEQSTAT_EXCAUSE) == VEC_UNCOV) {
+ char buf[150];
+ decode_address(buf, saved_retx);
+ printk(KERN_EMERG "While handling exception (EXCAUSE = 0x%x) at %s:\n",
+ (int)saved_seqstat & SEQSTAT_EXCAUSE, buf);
+ decode_address(buf, saved_dcplb_fault_addr);
+ printk(KERN_NOTICE " DCPLB_FAULT_ADDR: %s\n", buf);
+ decode_address(buf, saved_icplb_fault_addr);
+ printk(KERN_NOTICE " ICPLB_FAULT_ADDR: %s\n", buf);
+
+ decode_address(buf, fp->retx);
+ printk(KERN_NOTICE "The instruction at %s caused a double exception\n",
+ buf);
+ } else
+#endif
+ {
+ dump_bfin_process(fp);
+ dump_bfin_mem(fp);
+ show_regs(fp);
+ }
+#endif
panic("Double Fault - unrecoverable event\n");
}
@@ -259,34 +299,42 @@
return;
else
break;
-#ifdef CONFIG_KGDB
- case VEC_EXCPT02 : /* gdb connection */
- info.si_code = TRAP_ILLTRAP;
- sig = SIGTRAP;
- CHK_DEBUGGER_TRAP();
- return;
-#else
- /* 0x02 - User Defined, Caught by default */
-#endif
/* 0x03 - User Defined, userspace stack overflow */
case VEC_EXCPT03:
info.si_code = SEGV_STACKFLOW;
sig = SIGSEGV;
- printk(KERN_NOTICE EXC_0x03(KERN_NOTICE));
- CHK_DEBUGGER_TRAP();
+ verbose_printk(KERN_NOTICE EXC_0x03(KERN_NOTICE));
+ CHK_DEBUGGER_TRAP_MAYBE();
break;
- /* 0x04 - User Defined, Caught by default */
- /* 0x05 - User Defined, Caught by default */
- /* 0x06 - User Defined, Caught by default */
- /* 0x07 - User Defined, Caught by default */
- /* 0x08 - User Defined, Caught by default */
- /* 0x09 - User Defined, Caught by default */
- /* 0x0A - User Defined, Caught by default */
- /* 0x0B - User Defined, Caught by default */
- /* 0x0C - User Defined, Caught by default */
- /* 0x0D - User Defined, Caught by default */
- /* 0x0E - User Defined, Caught by default */
- /* 0x0F - User Defined, Caught by default */
+ /* 0x02 - KGDB initial connection and break signal trap */
+ case VEC_EXCPT02:
+#ifdef CONFIG_KGDB
+ info.si_code = TRAP_ILLTRAP;
+ sig = SIGTRAP;
+ CHK_DEBUGGER_TRAP();
+ return;
+#endif
+ /* 0x04 - User Defined */
+ /* 0x05 - User Defined */
+ /* 0x06 - User Defined */
+ /* 0x07 - User Defined */
+ /* 0x08 - User Defined */
+ /* 0x09 - User Defined */
+ /* 0x0A - User Defined */
+ /* 0x0B - User Defined */
+ /* 0x0C - User Defined */
+ /* 0x0D - User Defined */
+ /* 0x0E - User Defined */
+ /* 0x0F - User Defined */
+ /* If we got here, it is most likely that someone was trying to use a
+ * custom exception handler, and it is not actually installed properly
+ */
+ case VEC_EXCPT04 ... VEC_EXCPT15:
+ info.si_code = ILL_ILLPARAOP;
+ sig = SIGILL;
+ verbose_printk(KERN_NOTICE EXC_0x04(KERN_NOTICE));
+ CHK_DEBUGGER_TRAP_MAYBE();
+ break;
/* 0x10 HW Single step, handled here */
case VEC_STEP:
info.si_code = TRAP_STEP;
@@ -301,8 +349,8 @@
case VEC_OVFLOW:
info.si_code = TRAP_TRACEFLOW;
sig = SIGTRAP;
- printk(KERN_NOTICE EXC_0x11(KERN_NOTICE));
- CHK_DEBUGGER_TRAP();
+ verbose_printk(KERN_NOTICE EXC_0x11(KERN_NOTICE));
+ CHK_DEBUGGER_TRAP_MAYBE();
break;
/* 0x12 - Reserved, Caught by default */
/* 0x13 - Reserved, Caught by default */
@@ -323,44 +371,43 @@
case VEC_UNDEF_I:
info.si_code = ILL_ILLOPC;
sig = SIGILL;
- printk(KERN_NOTICE EXC_0x21(KERN_NOTICE));
- CHK_DEBUGGER_TRAP();
+ verbose_printk(KERN_NOTICE EXC_0x21(KERN_NOTICE));
+ CHK_DEBUGGER_TRAP_MAYBE();
break;
/* 0x22 - Illegal Instruction Combination, handled here */
case VEC_ILGAL_I:
info.si_code = ILL_ILLPARAOP;
sig = SIGILL;
- printk(KERN_NOTICE EXC_0x22(KERN_NOTICE));
- CHK_DEBUGGER_TRAP();
+ verbose_printk(KERN_NOTICE EXC_0x22(KERN_NOTICE));
+ CHK_DEBUGGER_TRAP_MAYBE();
break;
/* 0x23 - Data CPLB protection violation, handled here */
case VEC_CPLB_VL:
info.si_code = ILL_CPLB_VI;
sig = SIGBUS;
- printk(KERN_NOTICE EXC_0x23(KERN_NOTICE));
- CHK_DEBUGGER_TRAP();
+ verbose_printk(KERN_NOTICE EXC_0x23(KERN_NOTICE));
+ CHK_DEBUGGER_TRAP_MAYBE();
break;
/* 0x24 - Data access misaligned, handled here */
case VEC_MISALI_D:
info.si_code = BUS_ADRALN;
sig = SIGBUS;
- printk(KERN_NOTICE EXC_0x24(KERN_NOTICE));
- CHK_DEBUGGER_TRAP();
+ verbose_printk(KERN_NOTICE EXC_0x24(KERN_NOTICE));
+ CHK_DEBUGGER_TRAP_MAYBE();
break;
/* 0x25 - Unrecoverable Event, handled here */
case VEC_UNCOV:
info.si_code = ILL_ILLEXCPT;
sig = SIGILL;
- printk(KERN_NOTICE EXC_0x25(KERN_NOTICE));
- CHK_DEBUGGER_TRAP();
+ verbose_printk(KERN_NOTICE EXC_0x25(KERN_NOTICE));
+ CHK_DEBUGGER_TRAP_MAYBE();
break;
/* 0x26 - Data CPLB Miss, normal case is handled in _cplb_hdr,
error case is handled here */
case VEC_CPLB_M:
info.si_code = BUS_ADRALN;
sig = SIGBUS;
- printk(KERN_NOTICE EXC_0x26(KERN_NOTICE));
- CHK_DEBUGGER_TRAP();
+ verbose_printk(KERN_NOTICE EXC_0x26(KERN_NOTICE));
break;
/* 0x27 - Data CPLB Multiple Hits - Linux Trap Zero, handled here */
case VEC_CPLB_MHIT:
@@ -368,11 +415,11 @@
sig = SIGSEGV;
#ifdef CONFIG_DEBUG_HUNT_FOR_ZERO
if (saved_dcplb_fault_addr < FIXED_CODE_START)
- printk(KERN_NOTICE "NULL pointer access\n");
+ verbose_printk(KERN_NOTICE "NULL pointer access\n");
else
#endif
- printk(KERN_NOTICE EXC_0x27(KERN_NOTICE));
- CHK_DEBUGGER_TRAP();
+ verbose_printk(KERN_NOTICE EXC_0x27(KERN_NOTICE));
+ CHK_DEBUGGER_TRAP_MAYBE();
break;
/* 0x28 - Emulation Watchpoint, handled here */
case VEC_WATCH:
@@ -390,8 +437,8 @@
case VEC_ISTRU_VL: /* ADSP-BF535 only (MH) */
info.si_code = BUS_OPFETCH;
sig = SIGBUS;
- printk(KERN_NOTICE "BF535: VEC_ISTRU_VL\n");
- CHK_DEBUGGER_TRAP();
+ verbose_printk(KERN_NOTICE "BF535: VEC_ISTRU_VL\n");
+ CHK_DEBUGGER_TRAP_MAYBE();
break;
#else
/* 0x29 - Reserved, Caught by default */
@@ -400,22 +447,21 @@
case VEC_MISALI_I:
info.si_code = BUS_ADRALN;
sig = SIGBUS;
- printk(KERN_NOTICE EXC_0x2A(KERN_NOTICE));
- CHK_DEBUGGER_TRAP();
+ verbose_printk(KERN_NOTICE EXC_0x2A(KERN_NOTICE));
+ CHK_DEBUGGER_TRAP_MAYBE();
break;
/* 0x2B - Instruction CPLB protection violation, handled here */
case VEC_CPLB_I_VL:
info.si_code = ILL_CPLB_VI;
sig = SIGBUS;
- printk(KERN_NOTICE EXC_0x2B(KERN_NOTICE));
- CHK_DEBUGGER_TRAP();
+ verbose_printk(KERN_NOTICE EXC_0x2B(KERN_NOTICE));
+ CHK_DEBUGGER_TRAP_MAYBE();
break;
/* 0x2C - Instruction CPLB miss, handled in _cplb_hdr */
case VEC_CPLB_I_M:
info.si_code = ILL_CPLB_MISS;
sig = SIGBUS;
- printk(KERN_NOTICE EXC_0x2C(KERN_NOTICE));
- CHK_DEBUGGER_TRAP();
+ verbose_printk(KERN_NOTICE EXC_0x2C(KERN_NOTICE));
break;
/* 0x2D - Instruction CPLB Multiple Hits, handled here */
case VEC_CPLB_I_MHIT:
@@ -423,18 +469,18 @@
sig = SIGSEGV;
#ifdef CONFIG_DEBUG_HUNT_FOR_ZERO
if (saved_icplb_fault_addr < FIXED_CODE_START)
- printk(KERN_NOTICE "Jump to NULL address\n");
+ verbose_printk(KERN_NOTICE "Jump to NULL address\n");
else
#endif
- printk(KERN_NOTICE EXC_0x2D(KERN_NOTICE));
- CHK_DEBUGGER_TRAP();
+ verbose_printk(KERN_NOTICE EXC_0x2D(KERN_NOTICE));
+ CHK_DEBUGGER_TRAP_MAYBE();
break;
/* 0x2E - Illegal use of Supervisor Resource, handled here */
case VEC_ILL_RES:
info.si_code = ILL_PRVOPC;
sig = SIGILL;
- printk(KERN_NOTICE EXC_0x2E(KERN_NOTICE));
- CHK_DEBUGGER_TRAP();
+ verbose_printk(KERN_NOTICE EXC_0x2E(KERN_NOTICE));
+ CHK_DEBUGGER_TRAP_MAYBE();
break;
/* 0x2F - Reserved, Caught by default */
/* 0x30 - Reserved, Caught by default */
@@ -461,17 +507,17 @@
case (SEQSTAT_HWERRCAUSE_SYSTEM_MMR):
info.si_code = BUS_ADRALN;
sig = SIGBUS;
- printk(KERN_NOTICE HWC_x2(KERN_NOTICE));
+ verbose_printk(KERN_NOTICE HWC_x2(KERN_NOTICE));
break;
/* External Memory Addressing Error */
case (SEQSTAT_HWERRCAUSE_EXTERN_ADDR):
info.si_code = BUS_ADRERR;
sig = SIGBUS;
- printk(KERN_NOTICE HWC_x3(KERN_NOTICE));
+ verbose_printk(KERN_NOTICE HWC_x3(KERN_NOTICE));
break;
/* Performance Monitor Overflow */
case (SEQSTAT_HWERRCAUSE_PERF_FLOW):
- printk(KERN_NOTICE HWC_x12(KERN_NOTICE));
+ verbose_printk(KERN_NOTICE HWC_x12(KERN_NOTICE));
break;
/* RAISE 5 instruction */
case (SEQSTAT_HWERRCAUSE_RAISE_5):
@@ -481,21 +527,25 @@
printk(KERN_NOTICE HWC_default(KERN_NOTICE));
break;
}
- CHK_DEBUGGER_TRAP();
+ CHK_DEBUGGER_TRAP_MAYBE();
break;
+ /*
+ * We should be handling all known exception types above,
+ * if we get here we hit a reserved one, so panic
+ */
default:
- info.si_code = TRAP_ILLTRAP;
- sig = SIGTRAP;
- printk(KERN_EMERG "Caught Unhandled Exception, code = %08lx\n",
+ oops_in_progress = 1;
+ info.si_code = ILL_ILLPARAOP;
+ sig = SIGILL;
+ verbose_printk(KERN_EMERG "Caught Unhandled Exception, code = %08lx\n",
(fp->seqstat & SEQSTAT_EXCAUSE));
- CHK_DEBUGGER_TRAP();
+ CHK_DEBUGGER_TRAP_MAYBE();
break;
}
BUG_ON(sig == 0);
if (sig != SIGTRAP) {
- unsigned long *stack;
dump_bfin_process(fp);
dump_bfin_mem(fp);
show_regs(fp);
@@ -503,7 +553,7 @@
/* Print out the trace buffer if it makes sense */
#ifndef CONFIG_DEBUG_BFIN_NO_KERN_HWTRACE
if (trapnr == VEC_CPLB_I_M || trapnr == VEC_CPLB_M)
- printk(KERN_NOTICE "No trace since you do not have "
+ verbose_printk(KERN_NOTICE "No trace since you do not have "
"CONFIG_DEBUG_BFIN_NO_KERN_HWTRACE enabled\n"
KERN_NOTICE "\n");
else
@@ -512,20 +562,22 @@
if (oops_in_progress) {
/* Dump the current kernel stack */
- printk(KERN_NOTICE "\n" KERN_NOTICE "Kernel Stack\n");
+ verbose_printk(KERN_NOTICE "\n" KERN_NOTICE "Kernel Stack\n");
show_stack(current, NULL);
-
print_modules();
#ifndef CONFIG_ACCESS_CHECK
- printk(KERN_EMERG "Please turn on "
+ verbose_printk(KERN_EMERG "Please turn on "
"CONFIG_ACCESS_CHECK\n");
#endif
panic("Kernel exception");
} else {
+#ifdef CONFIG_VERBOSE_DEBUG
+ unsigned long *stack;
/* Dump the user space stack */
stack = (unsigned long *)rdusp();
- printk(KERN_NOTICE "Userspace Stack\n");
+ verbose_printk(KERN_NOTICE "Userspace Stack\n");
show_stack(NULL, stack);
+#endif
}
}
@@ -546,7 +598,7 @@
* Similar to get_user, do some address checking, then dereference
* Return true on sucess, false on bad address
*/
-bool get_instruction(unsigned short *val, unsigned short *address)
+static bool get_instruction(unsigned short *val, unsigned short *address)
{
unsigned long addr;
@@ -592,7 +644,7 @@
#if L1_CODE_LENGTH != 0
if (addr >= L1_CODE_START && (addr + 2) <= (L1_CODE_START + L1_CODE_LENGTH)) {
- dma_memcpy(val, address, 2);
+ isram_memcpy(val, address, 2);
return true;
}
#endif
@@ -607,45 +659,48 @@
* These are the normal instructions which cause change of flow, which
* would be at the source of the trace buffer
*/
-void decode_instruction(unsigned short *address)
+#ifdef CONFIG_DEBUG_VERBOSE
+static void decode_instruction(unsigned short *address)
{
unsigned short opcode;
if (get_instruction(&opcode, address)) {
if (opcode == 0x0010)
- printk("RTS");
+ verbose_printk("RTS");
else if (opcode == 0x0011)
- printk("RTI");
+ verbose_printk("RTI");
else if (opcode == 0x0012)
- printk("RTX");
+ verbose_printk("RTX");
else if (opcode >= 0x0050 && opcode <= 0x0057)
- printk("JUMP (P%i)", opcode & 7);
+ verbose_printk("JUMP (P%i)", opcode & 7);
else if (opcode >= 0x0060 && opcode <= 0x0067)
- printk("CALL (P%i)", opcode & 7);
+ verbose_printk("CALL (P%i)", opcode & 7);
else if (opcode >= 0x0070 && opcode <= 0x0077)
- printk("CALL (PC+P%i)", opcode & 7);
+ verbose_printk("CALL (PC+P%i)", opcode & 7);
else if (opcode >= 0x0080 && opcode <= 0x0087)
- printk("JUMP (PC+P%i)", opcode & 7);
+ verbose_printk("JUMP (PC+P%i)", opcode & 7);
else if ((opcode >= 0x1000 && opcode <= 0x13FF) || (opcode >= 0x1800 && opcode <= 0x1BFF))
- printk("IF !CC JUMP");
+ verbose_printk("IF !CC JUMP");
else if ((opcode >= 0x1400 && opcode <= 0x17ff) || (opcode >= 0x1c00 && opcode <= 0x1fff))
- printk("IF CC JUMP");
+ verbose_printk("IF CC JUMP");
else if (opcode >= 0x2000 && opcode <= 0x2fff)
- printk("JUMP.S");
+ verbose_printk("JUMP.S");
else if (opcode >= 0xe080 && opcode <= 0xe0ff)
- printk("LSETUP");
+ verbose_printk("LSETUP");
else if (opcode >= 0xe200 && opcode <= 0xe2ff)
- printk("JUMP.L");
+ verbose_printk("JUMP.L");
else if (opcode >= 0xe300 && opcode <= 0xe3ff)
- printk("CALL pcrel");
+ verbose_printk("CALL pcrel");
else
- printk("0x%04x", opcode);
+ verbose_printk("0x%04x", opcode);
}
}
+#endif
void dump_bfin_trace_buffer(void)
{
+#ifdef CONFIG_DEBUG_VERBOSE
#ifdef CONFIG_DEBUG_BFIN_HWTRACE_ON
int tflags, i = 0;
char buf[150];
@@ -701,6 +756,7 @@
trace_buffer_restore(tflags);
#endif
+#endif
}
EXPORT_SYMBOL(dump_bfin_trace_buffer);
@@ -708,7 +764,7 @@
* Checks to see if the address pointed to is either a
* 16-bit CALL instruction, or a 32-bit CALL instruction
*/
-bool is_bfin_call(unsigned short *addr)
+static bool is_bfin_call(unsigned short *addr)
{
unsigned short opcode = 0, *ins_addr;
ins_addr = (unsigned short *)addr;
@@ -730,8 +786,10 @@
return false;
}
+
void show_stack(struct task_struct *task, unsigned long *stack)
{
+#ifdef CONFIG_PRINTK
unsigned int *addr, *endstack, *fp = 0, *frame;
unsigned short *ins_addr;
char buf[150];
@@ -756,8 +814,10 @@
} else
endstack = (unsigned int *)PAGE_ALIGN((unsigned int)stack);
+ printk(KERN_NOTICE "Stack info:\n");
decode_address(buf, (unsigned int)stack);
- printk(KERN_NOTICE "Stack info:\n" KERN_NOTICE " SP: [0x%p] %s\n", stack, buf);
+ printk(KERN_NOTICE " SP: [0x%p] %s\n", stack, buf);
+
addr = (unsigned int *)((unsigned int)stack & ~0x3F);
/* First thing is to look for a frame pointer */
@@ -848,7 +908,7 @@
if (!j)
printk("\n");
}
-
+#endif
}
void dump_stack(void)
@@ -866,38 +926,39 @@
void dump_bfin_process(struct pt_regs *fp)
{
+#ifdef CONFIG_DEBUG_VERBOSE
/* We should be able to look at fp->ipend, but we don't push it on the
* stack all the time, so do this until we fix that */
unsigned int context = bfin_read_IPEND();
if (oops_in_progress)
- printk(KERN_EMERG "Kernel OOPS in progress\n");
+ verbose_printk(KERN_EMERG "Kernel OOPS in progress\n");
if (context & 0x0020 && (fp->seqstat & SEQSTAT_EXCAUSE) == VEC_HWERR)
- printk(KERN_NOTICE "HW Error context\n");
+ verbose_printk(KERN_NOTICE "HW Error context\n");
else if (context & 0x0020)
- printk(KERN_NOTICE "Deferred Exception context\n");
+ verbose_printk(KERN_NOTICE "Deferred Exception context\n");
else if (context & 0x3FC0)
- printk(KERN_NOTICE "Interrupt context\n");
+ verbose_printk(KERN_NOTICE "Interrupt context\n");
else if (context & 0x4000)
- printk(KERN_NOTICE "Deferred Interrupt context\n");
+ verbose_printk(KERN_NOTICE "Deferred Interrupt context\n");
else if (context & 0x8000)
- printk(KERN_NOTICE "Kernel process context\n");
+ verbose_printk(KERN_NOTICE "Kernel process context\n");
/* Because we are crashing, and pointers could be bad, we check things
* pretty closely before we use them
*/
if ((unsigned long)current >= FIXED_CODE_START &&
!((unsigned long)current & 0x3) && current->pid) {
- printk(KERN_NOTICE "CURRENT PROCESS:\n");
+ verbose_printk(KERN_NOTICE "CURRENT PROCESS:\n");
if (current->comm >= (char *)FIXED_CODE_START)
- printk(KERN_NOTICE "COMM=%s PID=%d\n",
+ verbose_printk(KERN_NOTICE "COMM=%s PID=%d\n",
current->comm, current->pid);
else
- printk(KERN_NOTICE "COMM= invalid\n");
+ verbose_printk(KERN_NOTICE "COMM= invalid\n");
if (!((unsigned long)current->mm & 0x3) && (unsigned long)current->mm >= FIXED_CODE_START)
- printk(KERN_NOTICE "TEXT = 0x%p-0x%p DATA = 0x%p-0x%p\n"
+ verbose_printk(KERN_NOTICE "TEXT = 0x%p-0x%p DATA = 0x%p-0x%p\n"
KERN_NOTICE " BSS = 0x%p-0x%p USER-STACK = 0x%p\n"
KERN_NOTICE "\n",
(void *)current->mm->start_code,
@@ -908,38 +969,40 @@
(void *)current->mm->brk,
(void *)current->mm->start_stack);
else
- printk(KERN_NOTICE "invalid mm\n");
+ verbose_printk(KERN_NOTICE "invalid mm\n");
} else
- printk(KERN_NOTICE "\n" KERN_NOTICE
+ verbose_printk(KERN_NOTICE "\n" KERN_NOTICE
"No Valid process in current context\n");
+#endif
}
void dump_bfin_mem(struct pt_regs *fp)
{
+#ifdef CONFIG_DEBUG_VERBOSE
unsigned short *addr, *erraddr, val = 0, err = 0;
char sti = 0, buf[6];
erraddr = (void *)fp->pc;
- printk(KERN_NOTICE "return address: [0x%p]; contents of:", erraddr);
+ verbose_printk(KERN_NOTICE "return address: [0x%p]; contents of:", erraddr);
for (addr = (unsigned short *)((unsigned long)erraddr & ~0xF) - 0x10;
addr < (unsigned short *)((unsigned long)erraddr & ~0xF) + 0x10;
addr++) {
if (!((unsigned long)addr & 0xF))
- printk("\n" KERN_NOTICE "0x%p: ", addr);
+ verbose_printk("\n" KERN_NOTICE "0x%p: ", addr);
- if (get_instruction(&val, addr)) {
+ if (!get_instruction(&val, addr)) {
val = 0;
sprintf(buf, "????");
} else
sprintf(buf, "%04x", val);
if (addr == erraddr) {
- printk("[%s]", buf);
+ verbose_printk("[%s]", buf);
err = val;
} else
- printk(" %s ", buf);
+ verbose_printk(" %s ", buf);
/* Do any previous instructions turn on interrupts? */
if (addr <= erraddr && /* in the past */
@@ -948,14 +1011,14 @@
sti = 1;
}
- printk("\n");
+ verbose_printk("\n");
/* Hardware error interrupts can be deferred */
if (unlikely(sti && (fp->seqstat & SEQSTAT_EXCAUSE) == VEC_HWERR &&
oops_in_progress)){
- printk(KERN_NOTICE "Looks like this was a deferred error - sorry\n");
+ verbose_printk(KERN_NOTICE "Looks like this was a deferred error - sorry\n");
#ifndef CONFIG_DEBUG_HWERR
- printk(KERN_NOTICE "The remaining message may be meaningless\n"
+ verbose_printk(KERN_NOTICE "The remaining message may be meaningless\n"
KERN_NOTICE "You should enable CONFIG_DEBUG_HWERR to get a"
" better idea where it came from\n");
#else
@@ -969,34 +1032,47 @@
/* And the last RETI points to the current userspace context */
if ((fp + 1)->pc >= current->mm->start_code &&
(fp + 1)->pc <= current->mm->end_code) {
- printk(KERN_NOTICE "It might be better to look around here : \n");
- printk(KERN_NOTICE "-------------------------------------------\n");
+ verbose_printk(KERN_NOTICE "It might be better to look around here : \n");
+ verbose_printk(KERN_NOTICE "-------------------------------------------\n");
show_regs(fp + 1);
- printk(KERN_NOTICE "-------------------------------------------\n");
+ verbose_printk(KERN_NOTICE "-------------------------------------------\n");
}
}
#endif
}
+#endif
}
void show_regs(struct pt_regs *fp)
{
+#ifdef CONFIG_DEBUG_VERBOSE
char buf [150];
struct irqaction *action;
unsigned int i;
unsigned long flags;
- printk(KERN_NOTICE "\n" KERN_NOTICE "SEQUENCER STATUS:\t\t%s\n", print_tainted());
- printk(KERN_NOTICE " SEQSTAT: %08lx IPEND: %04lx SYSCFG: %04lx\n",
+ verbose_printk(KERN_NOTICE "\n" KERN_NOTICE "SEQUENCER STATUS:\t\t%s\n", print_tainted());
+ verbose_printk(KERN_NOTICE " SEQSTAT: %08lx IPEND: %04lx SYSCFG: %04lx\n",
(long)fp->seqstat, fp->ipend, fp->syscfg);
- printk(KERN_NOTICE " HWERRCAUSE: 0x%lx\n",
- (fp->seqstat & SEQSTAT_HWERRCAUSE) >> 14);
- printk(KERN_NOTICE " EXCAUSE : 0x%lx\n",
+ if ((fp->seqstat & SEQSTAT_EXCAUSE) == VEC_HWERR) {
+ verbose_printk(KERN_NOTICE " HWERRCAUSE: 0x%lx\n",
+ (fp->seqstat & SEQSTAT_HWERRCAUSE) >> 14);
+#ifdef EBIU_ERRMST
+ /* If the error was from the EBIU, print it out */
+ if (bfin_read_EBIU_ERRMST() & CORE_ERROR) {
+ verbose_printk(KERN_NOTICE " EBIU Error Reason : 0x%04x\n",
+ bfin_read_EBIU_ERRMST());
+ verbose_printk(KERN_NOTICE " EBIU Error Address : 0x%08x\n",
+ bfin_read_EBIU_ERRADD());
+ }
+#endif
+ }
+ verbose_printk(KERN_NOTICE " EXCAUSE : 0x%lx\n",
fp->seqstat & SEQSTAT_EXCAUSE);
for (i = 6; i <= 15 ; i++) {
if (fp->ipend & (1 << i)) {
decode_address(buf, bfin_read32(EVT0 + 4*i));
- printk(KERN_NOTICE " physical IVG%i asserted : %s\n", i, buf);
+ verbose_printk(KERN_NOTICE " physical IVG%i asserted : %s\n", i, buf);
}
}
@@ -1009,64 +1085,65 @@
goto unlock;
decode_address(buf, (unsigned int)action->handler);
- printk(KERN_NOTICE " logical irq %3d mapped : %s", i, buf);
+ verbose_printk(KERN_NOTICE " logical irq %3d mapped : %s", i, buf);
for (action = action->next; action; action = action->next) {
decode_address(buf, (unsigned int)action->handler);
- printk(", %s", buf);
+ verbose_printk(", %s", buf);
}
- printk("\n");
+ verbose_printk("\n");
unlock:
spin_unlock_irqrestore(&irq_desc[i].lock, flags);
}
}
decode_address(buf, fp->rete);
- printk(KERN_NOTICE " RETE: %s\n", buf);
+ verbose_printk(KERN_NOTICE " RETE: %s\n", buf);
decode_address(buf, fp->retn);
- printk(KERN_NOTICE " RETN: %s\n", buf);
+ verbose_printk(KERN_NOTICE " RETN: %s\n", buf);
decode_address(buf, fp->retx);
- printk(KERN_NOTICE " RETX: %s\n", buf);
+ verbose_printk(KERN_NOTICE " RETX: %s\n", buf);
decode_address(buf, fp->rets);
- printk(KERN_NOTICE " RETS: %s\n", buf);
+ verbose_printk(KERN_NOTICE " RETS: %s\n", buf);
decode_address(buf, fp->pc);
- printk(KERN_NOTICE " PC : %s\n", buf);
+ verbose_printk(KERN_NOTICE " PC : %s\n", buf);
if (((long)fp->seqstat & SEQSTAT_EXCAUSE) &&
(((long)fp->seqstat & SEQSTAT_EXCAUSE) != VEC_HWERR)) {
decode_address(buf, saved_dcplb_fault_addr);
- printk(KERN_NOTICE "DCPLB_FAULT_ADDR: %s\n", buf);
+ verbose_printk(KERN_NOTICE "DCPLB_FAULT_ADDR: %s\n", buf);
decode_address(buf, saved_icplb_fault_addr);
- printk(KERN_NOTICE "ICPLB_FAULT_ADDR: %s\n", buf);
+ verbose_printk(KERN_NOTICE "ICPLB_FAULT_ADDR: %s\n", buf);
}
- printk(KERN_NOTICE "\n" KERN_NOTICE "PROCESSOR STATE:\n");
- printk(KERN_NOTICE " R0 : %08lx R1 : %08lx R2 : %08lx R3 : %08lx\n",
+ verbose_printk(KERN_NOTICE "\n" KERN_NOTICE "PROCESSOR STATE:\n");
+ verbose_printk(KERN_NOTICE " R0 : %08lx R1 : %08lx R2 : %08lx R3 : %08lx\n",
fp->r0, fp->r1, fp->r2, fp->r3);
- printk(KERN_NOTICE " R4 : %08lx R5 : %08lx R6 : %08lx R7 : %08lx\n",
+ verbose_printk(KERN_NOTICE " R4 : %08lx R5 : %08lx R6 : %08lx R7 : %08lx\n",
fp->r4, fp->r5, fp->r6, fp->r7);
- printk(KERN_NOTICE " P0 : %08lx P1 : %08lx P2 : %08lx P3 : %08lx\n",
+ verbose_printk(KERN_NOTICE " P0 : %08lx P1 : %08lx P2 : %08lx P3 : %08lx\n",
fp->p0, fp->p1, fp->p2, fp->p3);
- printk(KERN_NOTICE " P4 : %08lx P5 : %08lx FP : %08lx SP : %08lx\n",
+ verbose_printk(KERN_NOTICE " P4 : %08lx P5 : %08lx FP : %08lx SP : %08lx\n",
fp->p4, fp->p5, fp->fp, (long)fp);
- printk(KERN_NOTICE " LB0: %08lx LT0: %08lx LC0: %08lx\n",
+ verbose_printk(KERN_NOTICE " LB0: %08lx LT0: %08lx LC0: %08lx\n",
fp->lb0, fp->lt0, fp->lc0);
- printk(KERN_NOTICE " LB1: %08lx LT1: %08lx LC1: %08lx\n",
+ verbose_printk(KERN_NOTICE " LB1: %08lx LT1: %08lx LC1: %08lx\n",
fp->lb1, fp->lt1, fp->lc1);
- printk(KERN_NOTICE " B0 : %08lx L0 : %08lx M0 : %08lx I0 : %08lx\n",
+ verbose_printk(KERN_NOTICE " B0 : %08lx L0 : %08lx M0 : %08lx I0 : %08lx\n",
fp->b0, fp->l0, fp->m0, fp->i0);
- printk(KERN_NOTICE " B1 : %08lx L1 : %08lx M1 : %08lx I1 : %08lx\n",
+ verbose_printk(KERN_NOTICE " B1 : %08lx L1 : %08lx M1 : %08lx I1 : %08lx\n",
fp->b1, fp->l1, fp->m1, fp->i1);
- printk(KERN_NOTICE " B2 : %08lx L2 : %08lx M2 : %08lx I2 : %08lx\n",
+ verbose_printk(KERN_NOTICE " B2 : %08lx L2 : %08lx M2 : %08lx I2 : %08lx\n",
fp->b2, fp->l2, fp->m2, fp->i2);
- printk(KERN_NOTICE " B3 : %08lx L3 : %08lx M3 : %08lx I3 : %08lx\n",
+ verbose_printk(KERN_NOTICE " B3 : %08lx L3 : %08lx M3 : %08lx I3 : %08lx\n",
fp->b3, fp->l3, fp->m3, fp->i3);
- printk(KERN_NOTICE "A0.w: %08lx A0.x: %08lx A1.w: %08lx A1.x: %08lx\n",
+ verbose_printk(KERN_NOTICE "A0.w: %08lx A0.x: %08lx A1.w: %08lx A1.x: %08lx\n",
fp->a0w, fp->a0x, fp->a1w, fp->a1x);
- printk(KERN_NOTICE "USP : %08lx ASTAT: %08lx\n",
+ verbose_printk(KERN_NOTICE "USP : %08lx ASTAT: %08lx\n",
rdusp(), fp->astat);
- printk(KERN_NOTICE "\n");
+ verbose_printk(KERN_NOTICE "\n");
+#endif
}
#ifdef CONFIG_SYS_BFIN_SPINLOCK_L1
diff --git a/arch/blackfin/mach-bf527/boards/Kconfig b/arch/blackfin/mach-bf527/boards/Kconfig
index 8bf9e58..df224d0 100644
--- a/arch/blackfin/mach-bf527/boards/Kconfig
+++ b/arch/blackfin/mach-bf527/boards/Kconfig
@@ -14,4 +14,9 @@
help
CM-BF527 support for EVAL- and DEV-Board.
+config BFIN526_EZBRD
+ bool "BF526-EZBRD"
+ help
+ BF526-EZBRD/EZKIT Lite board support.
+
endchoice
diff --git a/arch/blackfin/mach-bf527/boards/Makefile b/arch/blackfin/mach-bf527/boards/Makefile
index 7ba7d25..eb6ed33 100644
--- a/arch/blackfin/mach-bf527/boards/Makefile
+++ b/arch/blackfin/mach-bf527/boards/Makefile
@@ -4,3 +4,4 @@
obj-$(CONFIG_BFIN527_EZKIT) += ezkit.o
obj-$(CONFIG_BFIN527_BLUETECHNIX_CM) += cm_bf527.o
+obj-$(CONFIG_BFIN526_EZBRD) += ezbrd.o
diff --git a/arch/blackfin/mach-bf527/boards/cm_bf527.c b/arch/blackfin/mach-bf527/boards/cm_bf527.c
index d22bc77..9ea440b 100644
--- a/arch/blackfin/mach-bf527/boards/cm_bf527.c
+++ b/arch/blackfin/mach-bf527/boards/cm_bf527.c
@@ -43,10 +43,7 @@
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/usb/sl811.h>
-#if defined(CONFIG_USB_MUSB_HDRC) || defined(CONFIG_USB_MUSB_HDRC_MODULE)
#include <linux/usb/musb.h>
-#endif
-#include <asm/cplb.h>
#include <asm/dma.h>
#include <asm/bfin5xx_spi.h>
#include <asm/reboot.h>
@@ -130,6 +127,16 @@
},
};
+static struct musb_hdrc_config musb_config = {
+ .multipoint = 0,
+ .dyn_fifo = 0,
+ .soft_con = 1,
+ .dma = 1,
+ .num_eps = 7,
+ .dma_channels = 7,
+ .gpio_vrsel = GPIO_PF11,
+};
+
static struct musb_hdrc_platform_data musb_plat = {
#if defined(CONFIG_USB_MUSB_OTG)
.mode = MUSB_OTG,
@@ -138,7 +145,7 @@
#elif defined(CONFIG_USB_GADGET_MUSB_HDRC)
.mode = MUSB_PERIPHERAL,
#endif
- .multipoint = 0,
+ .config = &musb_config,
};
static u64 musb_dmamask = ~(u32)0;
@@ -201,7 +208,7 @@
{
.name = "linux kernel(nand)",
.offset = 0,
- .size = 4 * SIZE_1M,
+ .size = 4 * 1024 * 1024,
},
{
.name = "file system(nand)",
diff --git a/arch/blackfin/mach-bf527/boards/ezbrd.c b/arch/blackfin/mach-bf527/boards/ezbrd.c
new file mode 100644
index 0000000..36c87b6
--- /dev/null
+++ b/arch/blackfin/mach-bf527/boards/ezbrd.c
@@ -0,0 +1,734 @@
+/*
+ * File: arch/blackfin/mach-bf527/boards/ezbrd.c
+ * Based on: arch/blackfin/mach-bf537/boards/stamp.c
+ * Author: Aidan Williams <aidan@nicta.com.au>
+ *
+ * Created:
+ * Description:
+ *
+ * Modified:
+ * Copyright 2005 National ICT Australia (NICTA)
+ * Copyright 2004-2008 Analog Devices Inc.
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/physmap.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/flash.h>
+
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/usb/musb.h>
+#include <asm/dma.h>
+#include <asm/bfin5xx_spi.h>
+#include <asm/reboot.h>
+#include <asm/nand.h>
+#include <asm/portmux.h>
+#include <asm/dpmc.h>
+#include <linux/spi/ad7877.h>
+
+/*
+ * Name the Board for the /proc/cpuinfo
+ */
+const char bfin_board_name[] = "BF526-EZBRD";
+
+/*
+ * Driver needs to know address, irq and flag pin.
+ */
+
+#if defined(CONFIG_USB_MUSB_HDRC) || defined(CONFIG_USB_MUSB_HDRC_MODULE)
+static struct resource musb_resources[] = {
+ [0] = {
+ .start = 0xffc03800,
+ .end = 0xffc03cff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = { /* general IRQ */
+ .start = IRQ_USB_INT0,
+ .end = IRQ_USB_INT0,
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
+ },
+ [2] = { /* DMA IRQ */
+ .start = IRQ_USB_DMA,
+ .end = IRQ_USB_DMA,
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
+ },
+};
+
+static struct musb_hdrc_config musb_config = {
+ .multipoint = 0,
+ .dyn_fifo = 0,
+ .soft_con = 1,
+ .dma = 1,
+ .num_eps = 7,
+ .dma_channels = 7,
+ .gpio_vrsel = GPIO_PG13,
+};
+
+static struct musb_hdrc_platform_data musb_plat = {
+#if defined(CONFIG_USB_MUSB_OTG)
+ .mode = MUSB_OTG,
+#elif defined(CONFIG_USB_MUSB_HDRC_HCD)
+ .mode = MUSB_HOST,
+#elif defined(CONFIG_USB_GADGET_MUSB_HDRC)
+ .mode = MUSB_PERIPHERAL,
+#endif
+ .config = &musb_config,
+};
+
+static u64 musb_dmamask = ~(u32)0;
+
+static struct platform_device musb_device = {
+ .name = "musb_hdrc",
+ .id = 0,
+ .dev = {
+ .dma_mask = &musb_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ .platform_data = &musb_plat,
+ },
+ .num_resources = ARRAY_SIZE(musb_resources),
+ .resource = musb_resources,
+};
+#endif
+
+#if defined(CONFIG_MTD_PHYSMAP) || defined(CONFIG_MTD_PHYSMAP_MODULE)
+static struct mtd_partition ezbrd_partitions[] = {
+ {
+ .name = "bootloader(nor)",
+ .size = 0x40000,
+ .offset = 0,
+ }, {
+ .name = "linux kernel(nor)",
+ .size = 0x1C0000,
+ .offset = MTDPART_OFS_APPEND,
+ }, {
+ .name = "file system(nor)",
+ .size = MTDPART_SIZ_FULL,
+ .offset = MTDPART_OFS_APPEND,
+ }
+};
+
+static struct physmap_flash_data ezbrd_flash_data = {
+ .width = 2,
+ .parts = ezbrd_partitions,
+ .nr_parts = ARRAY_SIZE(ezbrd_partitions),
+};
+
+static struct resource ezbrd_flash_resource = {
+ .start = 0x20000000,
+ .end = 0x203fffff,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct platform_device ezbrd_flash_device = {
+ .name = "physmap-flash",
+ .id = 0,
+ .dev = {
+ .platform_data = &ezbrd_flash_data,
+ },
+ .num_resources = 1,
+ .resource = &ezbrd_flash_resource,
+};
+#endif
+
+#if defined(CONFIG_MTD_NAND_BF5XX) || defined(CONFIG_MTD_NAND_BF5XX_MODULE)
+static struct mtd_partition partition_info[] = {
+ {
+ .name = "linux kernel(nand)",
+ .offset = 0,
+ .size = 4 * 1024 * 1024,
+ },
+ {
+ .name = "file system(nand)",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL,
+ },
+};
+
+static struct bf5xx_nand_platform bf5xx_nand_platform = {
+ .page_size = NFC_PG_SIZE_256,
+ .data_width = NFC_NWIDTH_8,
+ .partitions = partition_info,
+ .nr_partitions = ARRAY_SIZE(partition_info),
+ .rd_dly = 3,
+ .wr_dly = 3,
+};
+
+static struct resource bf5xx_nand_resources[] = {
+ {
+ .start = NFC_CTL,
+ .end = NFC_DATA_RD + 2,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = CH_NFC,
+ .end = CH_NFC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device bf5xx_nand_device = {
+ .name = "bf5xx-nand",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(bf5xx_nand_resources),
+ .resource = bf5xx_nand_resources,
+ .dev = {
+ .platform_data = &bf5xx_nand_platform,
+ },
+};
+#endif
+
+#if defined(CONFIG_RTC_DRV_BFIN) || defined(CONFIG_RTC_DRV_BFIN_MODULE)
+static struct platform_device rtc_device = {
+ .name = "rtc-bfin",
+ .id = -1,
+};
+#endif
+
+
+#if defined(CONFIG_BFIN_MAC) || defined(CONFIG_BFIN_MAC_MODULE)
+static struct platform_device bfin_mac_device = {
+ .name = "bfin_mac",
+};
+#endif
+
+#if defined(CONFIG_MTD_M25P80) \
+ || defined(CONFIG_MTD_M25P80_MODULE)
+static struct mtd_partition bfin_spi_flash_partitions[] = {
+ {
+ .name = "bootloader(spi)",
+ .size = 0x00040000,
+ .offset = 0,
+ .mask_flags = MTD_CAP_ROM
+ }, {
+ .name = "linux kernel(spi)",
+ .size = MTDPART_SIZ_FULL,
+ .offset = MTDPART_OFS_APPEND,
+ }
+};
+
+static struct flash_platform_data bfin_spi_flash_data = {
+ .name = "m25p80",
+ .parts = bfin_spi_flash_partitions,
+ .nr_parts = ARRAY_SIZE(bfin_spi_flash_partitions),
+ .type = "m25p16",
+};
+
+/* SPI flash chip (m25p64) */
+static struct bfin5xx_spi_chip spi_flash_chip_info = {
+ .enable_dma = 0, /* use dma transfer with this chip*/
+ .bits_per_word = 8,
+};
+#endif
+
+#if defined(CONFIG_SPI_ADC_BF533) \
+ || defined(CONFIG_SPI_ADC_BF533_MODULE)
+/* SPI ADC chip */
+static struct bfin5xx_spi_chip spi_adc_chip_info = {
+ .enable_dma = 1, /* use dma transfer with this chip*/
+ .bits_per_word = 16,
+};
+#endif
+
+#if defined(CONFIG_SPI_MMC) || defined(CONFIG_SPI_MMC_MODULE)
+static struct bfin5xx_spi_chip spi_mmc_chip_info = {
+ .enable_dma = 1,
+ .bits_per_word = 8,
+};
+#endif
+
+#if defined(CONFIG_PBX)
+static struct bfin5xx_spi_chip spi_si3xxx_chip_info = {
+ .ctl_reg = 0x4, /* send zero */
+ .enable_dma = 0,
+ .bits_per_word = 8,
+ .cs_change_per_word = 1,
+};
+#endif
+
+#if defined(CONFIG_TOUCHSCREEN_AD7877) || defined(CONFIG_TOUCHSCREEN_AD7877_MODULE)
+static struct bfin5xx_spi_chip spi_ad7877_chip_info = {
+ .enable_dma = 0,
+ .bits_per_word = 16,
+};
+
+static const struct ad7877_platform_data bfin_ad7877_ts_info = {
+ .model = 7877,
+ .vref_delay_usecs = 50, /* internal, no capacitor */
+ .x_plate_ohms = 419,
+ .y_plate_ohms = 486,
+ .pressure_max = 1000,
+ .pressure_min = 0,
+ .stopacq_polarity = 1,
+ .first_conversion_delay = 3,
+ .acquisition_time = 1,
+ .averaging = 1,
+ .pen_down_acc_interval = 1,
+};
+#endif
+
+#if defined(CONFIG_SND_SOC_WM8731) || defined(CONFIG_SND_SOC_WM8731_MODULE) \
+ && defined(CONFIG_SND_SOC_WM8731_SPI)
+static struct bfin5xx_spi_chip spi_wm8731_chip_info = {
+ .enable_dma = 0,
+ .bits_per_word = 16,
+};
+#endif
+
+#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE)
+static struct bfin5xx_spi_chip spidev_chip_info = {
+ .enable_dma = 0,
+ .bits_per_word = 8,
+};
+#endif
+
+#if defined(CONFIG_FB_BFIN_LQ035Q1) || defined(CONFIG_FB_BFIN_LQ035Q1_MODULE)
+static struct bfin5xx_spi_chip lq035q1_spi_chip_info = {
+ .enable_dma = 0,
+ .bits_per_word = 8,
+};
+#endif
+
+static struct spi_board_info bfin_spi_board_info[] __initdata = {
+#if defined(CONFIG_MTD_M25P80) \
+ || defined(CONFIG_MTD_M25P80_MODULE)
+ {
+ /* the modalias must be the same as spi device driver name */
+ .modalias = "m25p80", /* Name of spi_driver for this device */
+ .max_speed_hz = 25000000, /* max spi clock (SCK) speed in HZ */
+ .bus_num = 0, /* Framework bus number */
+ .chip_select = 1, /* Framework chip select. On STAMP537 it is SPISSEL1*/
+ .platform_data = &bfin_spi_flash_data,
+ .controller_data = &spi_flash_chip_info,
+ .mode = SPI_MODE_3,
+ },
+#endif
+
+#if defined(CONFIG_SPI_ADC_BF533) \
+ || defined(CONFIG_SPI_ADC_BF533_MODULE)
+ {
+ .modalias = "bfin_spi_adc", /* Name of spi_driver for this device */
+ .max_speed_hz = 6250000, /* max spi clock (SCK) speed in HZ */
+ .bus_num = 0, /* Framework bus number */
+ .chip_select = 1, /* Framework chip select. */
+ .platform_data = NULL, /* No spi_driver specific config */
+ .controller_data = &spi_adc_chip_info,
+ },
+#endif
+
+#if defined(CONFIG_SPI_MMC) || defined(CONFIG_SPI_MMC_MODULE)
+ {
+ .modalias = "spi_mmc_dummy",
+ .max_speed_hz = 25000000, /* max spi clock (SCK) speed in HZ */
+ .bus_num = 0,
+ .chip_select = 0,
+ .platform_data = NULL,
+ .controller_data = &spi_mmc_chip_info,
+ .mode = SPI_MODE_3,
+ },
+ {
+ .modalias = "spi_mmc",
+ .max_speed_hz = 25000000, /* max spi clock (SCK) speed in HZ */
+ .bus_num = 0,
+ .chip_select = CONFIG_SPI_MMC_CS_CHAN,
+ .platform_data = NULL,
+ .controller_data = &spi_mmc_chip_info,
+ .mode = SPI_MODE_3,
+ },
+#endif
+#if defined(CONFIG_PBX)
+ {
+ .modalias = "fxs-spi",
+ .max_speed_hz = 12500000, /* max spi clock (SCK) speed in HZ */
+ .bus_num = 0,
+ .chip_select = 8 - CONFIG_J11_JUMPER,
+ .controller_data = &spi_si3xxx_chip_info,
+ .mode = SPI_MODE_3,
+ },
+ {
+ .modalias = "fxo-spi",
+ .max_speed_hz = 12500000, /* max spi clock (SCK) speed in HZ */
+ .bus_num = 0,
+ .chip_select = 8 - CONFIG_J19_JUMPER,
+ .controller_data = &spi_si3xxx_chip_info,
+ .mode = SPI_MODE_3,
+ },
+#endif
+#if defined(CONFIG_TOUCHSCREEN_AD7877) || defined(CONFIG_TOUCHSCREEN_AD7877_MODULE)
+ {
+ .modalias = "ad7877",
+ .platform_data = &bfin_ad7877_ts_info,
+ .irq = IRQ_PF8,
+ .max_speed_hz = 12500000, /* max spi clock (SCK) speed in HZ */
+ .bus_num = 0,
+ .chip_select = 2,
+ .controller_data = &spi_ad7877_chip_info,
+ },
+#endif
+#if defined(CONFIG_SND_SOC_WM8731) || defined(CONFIG_SND_SOC_WM8731_MODULE) \
+ && defined(CONFIG_SND_SOC_WM8731_SPI)
+ {
+ .modalias = "wm8731",
+ .max_speed_hz = 3125000, /* max spi clock (SCK) speed in HZ */
+ .bus_num = 0,
+ .chip_select = 5,
+ .controller_data = &spi_wm8731_chip_info,
+ .mode = SPI_MODE_0,
+ },
+#endif
+#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE)
+ {
+ .modalias = "spidev",
+ .max_speed_hz = 3125000, /* max spi clock (SCK) speed in HZ */
+ .bus_num = 0,
+ .chip_select = 1,
+ .controller_data = &spidev_chip_info,
+ },
+#endif
+#if defined(CONFIG_FB_BFIN_LQ035Q1) || defined(CONFIG_FB_BFIN_LQ035Q1_MODULE)
+ {
+ .modalias = "bfin-lq035q1-spi",
+ .max_speed_hz = 20000000, /* max spi clock (SCK) speed in HZ */
+ .bus_num = 0,
+ .chip_select = 1,
+ .controller_data = &lq035q1_spi_chip_info,
+ .mode = SPI_CPHA | SPI_CPOL,
+ },
+#endif
+};
+
+#if defined(CONFIG_SPI_BFIN) || defined(CONFIG_SPI_BFIN_MODULE)
+/* SPI controller data */
+static struct bfin5xx_spi_master bfin_spi0_info = {
+ .num_chipselect = 8,
+ .enable_dma = 1, /* master has the ability to do dma transfer */
+ .pin_req = {P_SPI0_SCK, P_SPI0_MISO, P_SPI0_MOSI, 0},
+};
+
+/* SPI (0) */
+static struct resource bfin_spi0_resource[] = {
+ [0] = {
+ .start = SPI0_REGBASE,
+ .end = SPI0_REGBASE + 0xFF,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = CH_SPI,
+ .end = CH_SPI,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device bfin_spi0_device = {
+ .name = "bfin-spi",
+ .id = 0, /* Bus number */
+ .num_resources = ARRAY_SIZE(bfin_spi0_resource),
+ .resource = bfin_spi0_resource,
+ .dev = {
+ .platform_data = &bfin_spi0_info, /* Passed to driver */
+ },
+};
+#endif /* spi master and devices */
+
+#if defined(CONFIG_SERIAL_BFIN) || defined(CONFIG_SERIAL_BFIN_MODULE)
+static struct resource bfin_uart_resources[] = {
+#ifdef CONFIG_SERIAL_BFIN_UART0
+ {
+ .start = 0xFFC00400,
+ .end = 0xFFC004FF,
+ .flags = IORESOURCE_MEM,
+ },
+#endif
+#ifdef CONFIG_SERIAL_BFIN_UART1
+ {
+ .start = 0xFFC02000,
+ .end = 0xFFC020FF,
+ .flags = IORESOURCE_MEM,
+ },
+#endif
+};
+
+static struct platform_device bfin_uart_device = {
+ .name = "bfin-uart",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(bfin_uart_resources),
+ .resource = bfin_uart_resources,
+};
+#endif
+
+#if defined(CONFIG_BFIN_SIR) || defined(CONFIG_BFIN_SIR_MODULE)
+static struct resource bfin_sir_resources[] = {
+#ifdef CONFIG_BFIN_SIR0
+ {
+ .start = 0xFFC00400,
+ .end = 0xFFC004FF,
+ .flags = IORESOURCE_MEM,
+ },
+#endif
+#ifdef CONFIG_BFIN_SIR1
+ {
+ .start = 0xFFC02000,
+ .end = 0xFFC020FF,
+ .flags = IORESOURCE_MEM,
+ },
+#endif
+};
+
+static struct platform_device bfin_sir_device = {
+ .name = "bfin_sir",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(bfin_sir_resources),
+ .resource = bfin_sir_resources,
+};
+#endif
+
+#if defined(CONFIG_I2C_BLACKFIN_TWI) || defined(CONFIG_I2C_BLACKFIN_TWI_MODULE)
+static struct resource bfin_twi0_resource[] = {
+ [0] = {
+ .start = TWI0_REGBASE,
+ .end = TWI0_REGBASE,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_TWI,
+ .end = IRQ_TWI,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device i2c_bfin_twi_device = {
+ .name = "i2c-bfin-twi",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(bfin_twi0_resource),
+ .resource = bfin_twi0_resource,
+};
+#endif
+
+#ifdef CONFIG_I2C_BOARDINFO
+static struct i2c_board_info __initdata bfin_i2c_board_info[] = {
+#if defined(CONFIG_TWI_LCD) || defined(CONFIG_TWI_LCD_MODULE)
+ {
+ I2C_BOARD_INFO("pcf8574_lcd", 0x22),
+ },
+#endif
+#if defined(CONFIG_TWI_KEYPAD) || defined(CONFIG_TWI_KEYPAD_MODULE)
+ {
+ I2C_BOARD_INFO("pcf8574_keypad", 0x27),
+ .irq = IRQ_PF8,
+ },
+#endif
+};
+#endif
+
+#if defined(CONFIG_SERIAL_BFIN_SPORT) || defined(CONFIG_SERIAL_BFIN_SPORT_MODULE)
+static struct platform_device bfin_sport0_uart_device = {
+ .name = "bfin-sport-uart",
+ .id = 0,
+};
+
+static struct platform_device bfin_sport1_uart_device = {
+ .name = "bfin-sport-uart",
+ .id = 1,
+};
+#endif
+
+#if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE)
+#include <linux/input.h>
+#include <linux/gpio_keys.h>
+
+static struct gpio_keys_button bfin_gpio_keys_table[] = {
+ {BTN_0, GPIO_PG0, 1, "gpio-keys: BTN0"},
+ {BTN_1, GPIO_PG13, 1, "gpio-keys: BTN1"},
+};
+
+static struct gpio_keys_platform_data bfin_gpio_keys_data = {
+ .buttons = bfin_gpio_keys_table,
+ .nbuttons = ARRAY_SIZE(bfin_gpio_keys_table),
+};
+
+static struct platform_device bfin_device_gpiokeys = {
+ .name = "gpio-keys",
+ .dev = {
+ .platform_data = &bfin_gpio_keys_data,
+ },
+};
+#endif
+
+static struct resource bfin_gpios_resources = {
+ .start = 0,
+ .end = MAX_BLACKFIN_GPIOS - 1,
+ .flags = IORESOURCE_IRQ,
+};
+
+static struct platform_device bfin_gpios_device = {
+ .name = "simple-gpio",
+ .id = -1,
+ .num_resources = 1,
+ .resource = &bfin_gpios_resources,
+};
+
+static const unsigned int cclk_vlev_datasheet[] =
+{
+ VRPAIR(VLEV_100, 400000000),
+ VRPAIR(VLEV_105, 426000000),
+ VRPAIR(VLEV_110, 500000000),
+ VRPAIR(VLEV_115, 533000000),
+ VRPAIR(VLEV_120, 600000000),
+};
+
+static struct bfin_dpmc_platform_data bfin_dmpc_vreg_data = {
+ .tuple_tab = cclk_vlev_datasheet,
+ .tabsize = ARRAY_SIZE(cclk_vlev_datasheet),
+ .vr_settling_time = 25 /* us */,
+};
+
+static struct platform_device bfin_dpmc = {
+ .name = "bfin dpmc",
+ .dev = {
+ .platform_data = &bfin_dmpc_vreg_data,
+ },
+};
+
+#if defined(CONFIG_FB_BFIN_LQ035Q1) || defined(CONFIG_FB_BFIN_LQ035Q1_MODULE)
+#include <asm/bfin-lq035q1.h>
+
+static struct bfin_lq035q1fb_disp_info bfin_lq035q1_data = {
+ .mode = LQ035_NORM | LQ035_RGB | LQ035_RL | LQ035_TB,
+ .use_bl = 1,
+ .gpio_bl = GPIO_PG12,
+};
+
+static struct resource bfin_lq035q1_resources[] = {
+ {
+ .start = IRQ_PPI_ERROR,
+ .end = IRQ_PPI_ERROR,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device bfin_lq035q1_device = {
+ .name = "bfin-lq035q1",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(bfin_lq035q1_resources),
+ .resource = bfin_lq035q1_resources,
+ .dev = {
+ .platform_data = &bfin_lq035q1_data,
+ },
+};
+#endif
+
+static struct platform_device *stamp_devices[] __initdata = {
+
+ &bfin_dpmc,
+
+#if defined(CONFIG_MTD_NAND_BF5XX) || defined(CONFIG_MTD_NAND_BF5XX_MODULE)
+ &bf5xx_nand_device,
+#endif
+
+#if defined(CONFIG_RTC_DRV_BFIN) || defined(CONFIG_RTC_DRV_BFIN_MODULE)
+ &rtc_device,
+#endif
+
+#if defined(CONFIG_USB_MUSB_HDRC) || defined(CONFIG_USB_MUSB_HDRC_MODULE)
+ &musb_device,
+#endif
+
+#if defined(CONFIG_BFIN_MAC) || defined(CONFIG_BFIN_MAC_MODULE)
+ &bfin_mac_device,
+#endif
+
+#if defined(CONFIG_SPI_BFIN) || defined(CONFIG_SPI_BFIN_MODULE)
+ &bfin_spi0_device,
+#endif
+
+#if defined(CONFIG_SERIAL_BFIN) || defined(CONFIG_SERIAL_BFIN_MODULE)
+ &bfin_uart_device,
+#endif
+
+#if defined(CONFIG_FB_BFIN_LQ035Q1) || defined(CONFIG_FB_BFIN_LQ035Q1_MODULE)
+ &bfin_lq035q1_device,
+#endif
+
+#if defined(CONFIG_BFIN_SIR) || defined(CONFIG_BFIN_SIR_MODULE)
+ &bfin_sir_device,
+#endif
+
+#if defined(CONFIG_I2C_BLACKFIN_TWI) || defined(CONFIG_I2C_BLACKFIN_TWI_MODULE)
+ &i2c_bfin_twi_device,
+#endif
+
+#if defined(CONFIG_SERIAL_BFIN_SPORT) || defined(CONFIG_SERIAL_BFIN_SPORT_MODULE)
+ &bfin_sport0_uart_device,
+ &bfin_sport1_uart_device,
+#endif
+
+#if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE)
+ &bfin_device_gpiokeys,
+#endif
+
+#if defined(CONFIG_MTD_PHYSMAP) || defined(CONFIG_MTD_PHYSMAP_MODULE)
+ &ezbrd_flash_device,
+#endif
+
+ &bfin_gpios_device,
+};
+
+static int __init stamp_init(void)
+{
+ printk(KERN_INFO "%s(): registering device resources\n", __func__);
+
+#ifdef CONFIG_I2C_BOARDINFO
+ i2c_register_board_info(0, bfin_i2c_board_info,
+ ARRAY_SIZE(bfin_i2c_board_info));
+#endif
+
+ platform_add_devices(stamp_devices, ARRAY_SIZE(stamp_devices));
+ spi_register_board_info(bfin_spi_board_info, ARRAY_SIZE(bfin_spi_board_info));
+ return 0;
+}
+
+arch_initcall(stamp_init);
+
+void native_machine_restart(char *cmd)
+{
+ /* workaround reboot hang when booting from SPI */
+ if ((bfin_read_SYSCR() & 0x7) == 0x3)
+ bfin_gpio_reset_spi0_ssel1();
+}
+
+void bfin_get_ether_addr(char *addr)
+{
+ /* the MAC is stored in OTP memory page 0xDF */
+ u32 ret;
+ u64 otp_mac;
+ u32 (*otp_read)(u32 page, u32 flags, u64 *page_content) = (void *)0xEF00001A;
+
+ ret = otp_read(0xDF, 0x00, &otp_mac);
+ if (!(ret & 0x1)) {
+ char *otp_mac_p = (char *)&otp_mac;
+ for (ret = 0; ret < 6; ++ret)
+ addr[ret] = otp_mac_p[5 - ret];
+ }
+}
+EXPORT_SYMBOL(bfin_get_ether_addr);
diff --git a/arch/blackfin/mach-bf527/boards/ezkit.c b/arch/blackfin/mach-bf527/boards/ezkit.c
index 762f754..8ee2b74 100644
--- a/arch/blackfin/mach-bf527/boards/ezkit.c
+++ b/arch/blackfin/mach-bf527/boards/ezkit.c
@@ -42,10 +42,7 @@
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/usb/sl811.h>
-#if defined(CONFIG_USB_MUSB_HDRC) || defined(CONFIG_USB_MUSB_HDRC_MODULE)
#include <linux/usb/musb.h>
-#endif
-#include <asm/cplb.h>
#include <asm/dma.h>
#include <asm/bfin5xx_spi.h>
#include <asm/reboot.h>
@@ -129,6 +126,16 @@
},
};
+static struct musb_hdrc_config musb_config = {
+ .multipoint = 0,
+ .dyn_fifo = 0,
+ .soft_con = 1,
+ .dma = 1,
+ .num_eps = 7,
+ .dma_channels = 7,
+ .gpio_vrsel = GPIO_PG13,
+};
+
static struct musb_hdrc_platform_data musb_plat = {
#if defined(CONFIG_USB_MUSB_OTG)
.mode = MUSB_OTG,
@@ -137,7 +144,7 @@
#elif defined(CONFIG_USB_GADGET_MUSB_HDRC)
.mode = MUSB_PERIPHERAL,
#endif
- .multipoint = 0,
+ .config = &musb_config,
};
static u64 musb_dmamask = ~(u32)0;
@@ -218,7 +225,7 @@
{
.name = "linux kernel(nand)",
.offset = 0,
- .size = 4 * SIZE_1M,
+ .size = 4 * 1024 * 1024,
},
{
.name = "file system(nand)",
@@ -846,6 +853,38 @@
};
#endif
+#if defined(CONFIG_JOYSTICK_BFIN_ROTARY) || defined(CONFIG_JOYSTICK_BFIN_ROTARY_MODULE)
+#include <linux/input.h>
+#include <asm/bfin_rotary.h>
+
+static struct bfin_rotary_platform_data bfin_rotary_data = {
+ /*.rotary_up_key = KEY_UP,*/
+ /*.rotary_down_key = KEY_DOWN,*/
+ .rotary_rel_code = REL_WHEEL,
+ .rotary_button_key = KEY_ENTER,
+ .debounce = 10, /* 0..17 */
+ .mode = ROT_QUAD_ENC | ROT_DEBE,
+};
+
+static struct resource bfin_rotary_resources[] = {
+ {
+ .start = IRQ_CNT,
+ .end = IRQ_CNT,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device bfin_rotary_device = {
+ .name = "bfin-rotary",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(bfin_rotary_resources),
+ .resource = bfin_rotary_resources,
+ .dev = {
+ .platform_data = &bfin_rotary_data,
+ },
+};
+#endif
+
static struct resource bfin_gpios_resources = {
.start = 0,
.end = MAX_BLACKFIN_GPIOS - 1,
@@ -962,6 +1001,10 @@
&bfin_device_gpiokeys,
#endif
+#if defined(CONFIG_JOYSTICK_BFIN_ROTARY) || defined(CONFIG_JOYSTICK_BFIN_ROTARY_MODULE)
+ &bfin_rotary_device,
+#endif
+
#if defined(CONFIG_MTD_PHYSMAP) || defined(CONFIG_MTD_PHYSMAP_MODULE)
&ezkit_flash_device,
#endif
diff --git a/arch/blackfin/mach-bf527/head.S b/arch/blackfin/mach-bf527/head.S
index 28c4861..6588170 100644
--- a/arch/blackfin/mach-bf527/head.S
+++ b/arch/blackfin/mach-bf527/head.S
@@ -87,6 +87,9 @@
r1 = PLL_BYPASS; /* Bypass the PLL? */
r1 = r1 << 8; /* Shift it over */
r0 = r1 | r0; /* add them all together */
+#ifdef ANOMALY_05000265
+ r0 = BITSET(r0, 15); /* Add 250 mV of hysteresis to SPORT input pins */
+#endif
p0.h = hi(PLL_CTL);
p0.l = lo(PLL_CTL); /* Load the address */
diff --git a/arch/blackfin/mach-bf527/include/mach/anomaly.h b/arch/blackfin/mach-bf527/include/mach/anomaly.h
index b7b166f..62373e6 100644
--- a/arch/blackfin/mach-bf527/include/mach/anomaly.h
+++ b/arch/blackfin/mach-bf527/include/mach/anomaly.h
@@ -7,12 +7,24 @@
*/
/* This file shoule be up to date with:
- * - Revision C, 01/25/2008; ADSP-BF527 Blackfin Processor Anomaly List
+ * - Revision B, 08/12/2008; ADSP-BF526 Blackfin Processor Anomaly List
+ * - Revision E, 08/18/2008; ADSP-BF527 Blackfin Processor Anomaly List
*/
#ifndef _MACH_ANOMALY_H_
#define _MACH_ANOMALY_H_
+#if defined(__ADSPBF522__) || defined(__ADSPBF524__) || defined(__ADSPBF526__)
+# define ANOMALY_BF526 1
+#else
+# define ANOMALY_BF526 0
+#endif
+#if defined(__ADSPBF523__) || defined(__ADSPBF525__) || defined(__ADSPBF527__)
+# define ANOMALY_BF527 1
+#else
+# define ANOMALY_BF527 0
+#endif
+
/* Multi-Issue Instruction with dsp32shiftimm in slot1 and P-reg Store in slot2 Not Supported */
#define ANOMALY_05000074 (1)
/* DMA_RUN Bit Is Not Valid after a Peripheral Receive Channel DMA Stops */
@@ -23,68 +35,124 @@
#define ANOMALY_05000245 (1)
/* Sensitivity To Noise with Slow Input Edge Rates on External SPORT TX and RX Clocks */
#define ANOMALY_05000265 (1)
-/* New Feature: EMAC TX DMA Word Alignment */
-#define ANOMALY_05000285 (1)
+/* False Hardware Errors Caused by Fetches at the Boundary of Reserved Memory */
+#define ANOMALY_05000310 (1)
/* Errors when SSYNC, CSYNC, or Loads to LT, LB and LC Registers Are Interrupted */
-#define ANOMALY_05000312 (1)
+#define ANOMALY_05000312 (ANOMALY_BF527)
+/* PPI Is Level-Sensitive on First Transfer In Single Frame Sync Modes */
+#define ANOMALY_05000313 (__SILICON_REVISION__ < 2)
/* Incorrect Access of OTP_STATUS During otp_write() Function */
-#define ANOMALY_05000328 (1)
+#define ANOMALY_05000328 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
/* Disallowed Configuration Prevents Subsequent Allowed Configuration on Host DMA Port */
-#define ANOMALY_05000337 (1)
+#define ANOMALY_05000337 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
/* Ethernet MAC MDIO Reads Do Not Meet IEEE Specification */
-#define ANOMALY_05000341 (1)
+#define ANOMALY_05000341 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
/* TWI May Not Operate Correctly Under Certain Signal Termination Conditions */
-#define ANOMALY_05000342 (1)
+#define ANOMALY_05000342 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
/* USB Calibration Value Is Not Initialized */
-#define ANOMALY_05000346 (1)
+#define ANOMALY_05000346 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+/* USB Calibration Value to use */
+#define ANOMALY_05000346_value 0xE510
/* Preboot Routine Incorrectly Alters Reset Value of USB Register */
-#define ANOMALY_05000347 (1)
+#define ANOMALY_05000347 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
/* Security Features Are Not Functional */
-#define ANOMALY_05000348 (__SILICON_REVISION__ < 1)
+#define ANOMALY_05000348 (ANOMALY_BF527 && __SILICON_REVISION__ < 1)
+/* bfrom_SysControl() Firmware Function Performs Improper System Reset */
+#define ANOMALY_05000353 (ANOMALY_BF526)
/* Regulator Programming Blocked when Hibernate Wakeup Source Remains Active */
-#define ANOMALY_05000355 (1)
+#define ANOMALY_05000355 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
/* Serial Port (SPORT) Multichannel Transmit Failure when Channel 0 Is Disabled */
-#define ANOMALY_05000357 (1)
+#define ANOMALY_05000357 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
/* Incorrect Revision Number in DSPID Register */
-#define ANOMALY_05000364 (__SILICON_REVISION__ > 0)
+#define ANOMALY_05000364 (ANOMALY_BF527 && __SILICON_REVISION__ == 1)
/* PPI Underflow Error Goes Undetected in ITU-R 656 Mode */
#define ANOMALY_05000366 (1)
-/* New Feature: Higher Default CCLK Rate */
-#define ANOMALY_05000368 (1)
+/* Incorrect Default CSEL Value in PLL_DIV */
+#define ANOMALY_05000368 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
/* Possible RETS Register Corruption when Subroutine Is under 5 Cycles in Duration */
-#define ANOMALY_05000371 (1)
+#define ANOMALY_05000371 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
/* Authentication Fails To Initiate */
-#define ANOMALY_05000376 (__SILICON_REVISION__ > 0)
+#define ANOMALY_05000376 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
/* Data Read From L3 Memory by USB DMA May be Corrupted */
-#define ANOMALY_05000380 (1)
-/* USB Full-speed Mode not Fully Tested */
-#define ANOMALY_05000381 (1)
-/* New Feature: Boot from OTP Memory */
-#define ANOMALY_05000385 (1)
-/* New Feature: bfrom_SysControl() Routine */
-#define ANOMALY_05000386 (1)
-/* New Feature: Programmable Preboot Settings */
-#define ANOMALY_05000387 (1)
+#define ANOMALY_05000380 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+/* 8-Bit NAND Flash Boot Mode Not Functional */
+#define ANOMALY_05000382 (__SILICON_REVISION__ < 2)
+/* Host Must Not Read Back During Host DMA Boot */
+#define ANOMALY_05000384 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+/* Boot from OTP Memory Not Functional */
+#define ANOMALY_05000385 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+/* bfrom_SysControl() Firmware Routine Not Functional */
+#define ANOMALY_05000386 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+/* Programmable Preboot Settings Not Functional */
+#define ANOMALY_05000387 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+/* CRC32 Checksum Support Not Functional */
+#define ANOMALY_05000388 (__SILICON_REVISION__ < 2)
/* Reset Vector Must Not Be in SDRAM Memory Space */
-#define ANOMALY_05000389 (1)
-/* New Feature: pTempCurrent Added to ADI_BOOT_DATA Structure */
-#define ANOMALY_05000392 (1)
-/* New Feature: dTempByteCount Value Increased in ADI_BOOT_DATA Structure */
-#define ANOMALY_05000393 (1)
-/* New Feature: Log Buffer Functionality */
-#define ANOMALY_05000394 (1)
-/* New Feature: Hook Routine Functionality */
-#define ANOMALY_05000395 (1)
-/* New Feature: Header Indirect Bit */
-#define ANOMALY_05000396 (1)
-/* New Feature: BK_ONES, BK_ZEROS, and BK_DATECODE Constants */
-#define ANOMALY_05000397 (1)
-/* New Feature: SWRESET, DFRESET and WDRESET Bits Added to SYSCR Register */
-#define ANOMALY_05000398 (1)
-/* New Feature: BCODE_NOBOOT Added to BCODE Field of SYSCR Register */
-#define ANOMALY_05000399 (1)
+#define ANOMALY_05000389 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+/* pTempCurrent Not Present in ADI_BOOT_DATA Structure */
+#define ANOMALY_05000392 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+/* Deprecated Value of dTempByteCount in ADI_BOOT_DATA Structure */
+#define ANOMALY_05000393 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+/* Log Buffer Not Functional */
+#define ANOMALY_05000394 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+/* Hook Routine Not Functional */
+#define ANOMALY_05000395 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+/* Header Indirect Bit Not Functional */
+#define ANOMALY_05000396 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+/* BK_ONES, BK_ZEROS, and BK_DATECODE Constants Not Functional */
+#define ANOMALY_05000397 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+/* SWRESET, DFRESET and WDRESET Bits in the SYSCR Register Not Functional */
+#define ANOMALY_05000398 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+/* BCODE_NOBOOT in BCODE Field of SYSCR Register Not Functional */
+#define ANOMALY_05000399 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
/* PPI Data Signals D0 and D8 do not Tristate After Disabling PPI */
-#define ANOMALY_05000401 (1)
+#define ANOMALY_05000401 (__SILICON_REVISION__ < 2)
+/* Level-Sensitive External GPIO Wakeups May Cause Indefinite Stall */
+#define ANOMALY_05000403 (__SILICON_REVISION__ < 2)
+/* Lockbox SESR Disallows Certain User Interrupts */
+#define ANOMALY_05000404 (__SILICON_REVISION__ < 2)
+/* Lockbox SESR Firmware Does Not Save/Restore Full Context */
+#define ANOMALY_05000405 (1)
+/* Lockbox SESR Firmware Arguments Are Not Retained After First Initialization */
+#define ANOMALY_05000407 (__SILICON_REVISION__ < 2)
+/* Lockbox Firmware Memory Cleanup Routine Does not Clear Registers */
+#define ANOMALY_05000408 (1)
+/* Lockbox firmware leaves MDMA0 channel enabled */
+#define ANOMALY_05000409 (__SILICON_REVISION__ < 2)
+/* Incorrect Default Internal Voltage Regulator Setting */
+#define ANOMALY_05000410 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+/* bfrom_SysControl() Firmware Function Cannot be Used to Enter Power Saving Modes */
+#define ANOMALY_05000411 (__SILICON_REVISION__ < 2)
+/* OTP_CHECK_FOR_PREV_WRITE Bit is Not Functional in bfrom_OtpWrite() API */
+#define ANOMALY_05000414 (__SILICON_REVISION__ < 2)
+/* DEB2_URGENT Bit Not Functional */
+#define ANOMALY_05000415 (__SILICON_REVISION__ < 2)
+/* Speculative Fetches Can Cause Undesired External FIFO Operations */
+#define ANOMALY_05000416 (1)
+/* SPORT0 Ignores External TSCLK0 on PG14 When TMR6 is an Output */
+#define ANOMALY_05000417 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+/* tSFSPE and tHFSPE Do Not Meet Data Sheet Specifications */
+#define ANOMALY_05000418 (__SILICON_REVISION__ < 2)
+/* USB PLL_STABLE Bit May Not Accurately Reflect the USB PLL's Status */
+#define ANOMALY_05000420 (__SILICON_REVISION__ < 2)
+/* TWI Fall Time (Tof) May Violate the Minimum I2C Specification */
+#define ANOMALY_05000421 (1)
+/* TWI Input Capacitance (Ci) May Violate the Maximum I2C Specification */
+#define ANOMALY_05000422 (ANOMALY_BF527 && __SILICON_REVISION__ > 1)
+/* Certain Ethernet Frames With Errors are Misclassified in RMII Mode */
+#define ANOMALY_05000423 (__SILICON_REVISION__ < 2)
+/* Internal Voltage Regulator Not Trimmed */
+#define ANOMALY_05000424 (ANOMALY_BF527 && __SILICON_REVISION__ < 2)
+/* Multichannel SPORT Channel Misalignment Under Specific Configuration */
+#define ANOMALY_05000425 (__SILICON_REVISION__ < 2)
+/* Speculative Fetches of Indirect-Pointer Instructions Can Cause Spurious Hardware Errors */
+#define ANOMALY_05000426 (1)
+/* WB_EDGE Bit in NFC_IRQSTAT Incorrectly Reflects Buffer Status Instead of IRQ Status */
+#define ANOMALY_05000429 (__SILICON_REVISION__ < 2)
+/* Software System Reset Corrupts PLL_LOCKCNT Register */
+#define ANOMALY_05000430 (ANOMALY_BF527 && __SILICON_REVISION__ > 1)
+/* bfrom_SysControl() Does Not Clear SIC_IWR1 Before Executing PLL Programming Sequence */
+#define ANOMALY_05000432 (ANOMALY_BF526)
/* Anomalies that don't exist on this proc */
#define ANOMALY_05000125 (0)
@@ -97,6 +165,8 @@
#define ANOMALY_05000263 (0)
#define ANOMALY_05000266 (0)
#define ANOMALY_05000273 (0)
+#define ANOMALY_05000285 (0)
+#define ANOMALY_05000307 (0)
#define ANOMALY_05000311 (0)
#define ANOMALY_05000323 (0)
#define ANOMALY_05000363 (0)
diff --git a/arch/blackfin/mach-bf527/include/mach/bf527.h b/arch/blackfin/mach-bf527/include/mach/bf527.h
index 056eb4b..144f08d 100644
--- a/arch/blackfin/mach-bf527/include/mach/bf527.h
+++ b/arch/blackfin/mach-bf527/include/mach/bf527.h
@@ -30,8 +30,6 @@
#ifndef __MACH_BF527_H__
#define __MACH_BF527_H__
-#define SUPPORTED_REVID 2
-
#define OFFSET_(x) ((x) & 0x0000FFFF)
/*some misc defines*/
@@ -112,16 +110,31 @@
#ifdef CONFIG_BF527
#define CPU "BF527"
+#define CPUID 0x27e4
+#endif
+#ifdef CONFIG_BF526
+#define CPU "BF526"
+#define CPUID 0x27e4
#endif
#ifdef CONFIG_BF525
#define CPU "BF525"
+#define CPUID 0x27e4
+#endif
+#ifdef CONFIG_BF524
+#define CPU "BF524"
+#define CPUID 0x27e4
+#endif
+#ifdef CONFIG_BF523
+#define CPU "BF523"
+#define CPUID 0x27e4
#endif
#ifdef CONFIG_BF522
#define CPU "BF522"
+#define CPUID 0x27e4
#endif
+
#ifndef CPU
-#define CPU "UNKNOWN"
-#define CPUID 0x0
+#error Unknown CPU type - This kernel doesn't seem to be configured properly
#endif
#endif /* __MACH_BF527_H__ */
diff --git a/arch/blackfin/mach-bf527/include/mach/bfin_serial_5xx.h b/arch/blackfin/mach-bf527/include/mach/bfin_serial_5xx.h
index 2526b6e..75722d6 100644
--- a/arch/blackfin/mach-bf527/include/mach/bfin_serial_5xx.h
+++ b/arch/blackfin/mach-bf527/include/mach/bfin_serial_5xx.h
@@ -78,6 +78,9 @@
# define CONFIG_UART1_RTS_PIN -1
# endif
#endif
+
+#define BFIN_UART_TX_FIFO_SIZE 2
+
/*
* The pin configuration is different from schematic
*/
@@ -119,7 +122,6 @@
bfin_write16(uart->port.membase + OFFSET_LSR, -1);
}
-struct bfin_serial_port bfin_serial_ports[BFIN_UART_NR_PORTS];
struct bfin_serial_res {
unsigned long uart_base_addr;
int uart_irq;
@@ -164,8 +166,6 @@
#endif
};
-int nr_ports = ARRAY_SIZE(bfin_serial_resource);
-
#define DRIVER_NAME "bfin-uart"
static void bfin_serial_hw_init(struct bfin_serial_port *uart)
diff --git a/arch/blackfin/mach-bf527/include/mach/defBF52x_base.h b/arch/blackfin/mach-bf527/include/mach/defBF52x_base.h
index 6ac2ed7..68b55d0 100644
--- a/arch/blackfin/mach-bf527/include/mach/defBF52x_base.h
+++ b/arch/blackfin/mach-bf527/include/mach/defBF52x_base.h
@@ -1840,6 +1840,33 @@
#define DPRESCALE 0xf /* Load Counter Register */
+/* CNT_COMMAND bit field options */
+
+#define W1LCNT_ZERO 0x0001 /* write 1 to load CNT_COUNTER with zero */
+#define W1LCNT_MIN 0x0004 /* write 1 to load CNT_COUNTER from CNT_MIN */
+#define W1LCNT_MAX 0x0008 /* write 1 to load CNT_COUNTER from CNT_MAX */
+
+#define W1LMIN_ZERO 0x0010 /* write 1 to load CNT_MIN with zero */
+#define W1LMIN_CNT 0x0020 /* write 1 to load CNT_MIN from CNT_COUNTER */
+#define W1LMIN_MAX 0x0080 /* write 1 to load CNT_MIN from CNT_MAX */
+
+#define W1LMAX_ZERO 0x0100 /* write 1 to load CNT_MAX with zero */
+#define W1LMAX_CNT 0x0200 /* write 1 to load CNT_MAX from CNT_COUNTER */
+#define W1LMAX_MIN 0x0400 /* write 1 to load CNT_MAX from CNT_MIN */
+
+/* CNT_CONFIG bit field options */
+
+#define CNTMODE_QUADENC 0x0000 /* quadrature encoder mode */
+#define CNTMODE_BINENC 0x0100 /* binary encoder mode */
+#define CNTMODE_UDCNT 0x0200 /* up/down counter mode */
+#define CNTMODE_DIRCNT 0x0400 /* direction counter mode */
+#define CNTMODE_DIRTMR 0x0500 /* direction timer mode */
+
+#define BNDMODE_COMP 0x0000 /* boundary compare mode */
+#define BNDMODE_ZERO 0x1000 /* boundary compare and zero mode */
+#define BNDMODE_CAPT 0x2000 /* boundary capture mode */
+#define BNDMODE_AEXT 0x3000 /* boundary auto-extend mode */
+
/* Bit masks for OTP_CONTROL */
#define FUSE_FADDR 0x1ff /* OTP/Fuse Address */
diff --git a/arch/blackfin/mach-bf527/include/mach/portmux.h b/arch/blackfin/mach-bf527/include/mach/portmux.h
index ae4d205..7f6da2c 100644
--- a/arch/blackfin/mach-bf527/include/mach/portmux.h
+++ b/arch/blackfin/mach-bf527/include/mach/portmux.h
@@ -67,6 +67,10 @@
#define P_UART1_RX (P_DEFINED | P_IDENT(GPIO_PG13) | P_FUNCT(1))
#endif
+#define P_CNT_CZM (P_DEFINED | P_IDENT(GPIO_PF11) | P_FUNCT(3))
+#define P_CNT_CDG (P_DEFINED | P_IDENT(GPIO_PF12) | P_FUNCT(3))
+#define P_CNT_CUD (P_DEFINED | P_IDENT(GPIO_PF13) | P_FUNCT(3))
+
#define P_HWAIT (P_DONTCARE)
#define P_SPI0_SS (P_DEFINED | P_IDENT(GPIO_PG1) | P_FUNCT(0))
diff --git a/arch/blackfin/mach-bf533/boards/H8606.c b/arch/blackfin/mach-bf533/boards/H8606.c
index c66a68f..72ac3ac 100644
--- a/arch/blackfin/mach-bf533/boards/H8606.c
+++ b/arch/blackfin/mach-bf533/boards/H8606.c
@@ -9,7 +9,7 @@
* Modified:
* Copyright 2005 National ICT Australia (NICTA)
* Copyright 2004-2006 Analog Devices Inc
- * Copyright 2007 HV Sistemas S.L.
+ * Copyright 2007,2008 HV Sistemas S.L.
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
@@ -64,18 +64,18 @@
static struct resource dm9000_resources[] = {
[0] = {
.start = 0x20300000,
- .end = 0x20300000 + 1,
+ .end = 0x20300002,
.flags = IORESOURCE_MEM,
},
[1] = {
- .start = 0x20300000 + 4,
- .end = 0x20300000 + 5,
+ .start = 0x20300004,
+ .end = 0x20300006,
.flags = IORESOURCE_MEM,
},
[2] = {
.start = IRQ_PF10,
.end = IRQ_PF10,
- .flags = (IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE),
+ .flags = (IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE | IRQF_SHARED | IRQF_TRIGGER_HIGH),
},
};
@@ -140,18 +140,22 @@
#if defined(CONFIG_MTD_M25P80) || defined(CONFIG_MTD_M25P80_MODULE)
static struct mtd_partition bfin_spi_flash_partitions[] = {
{
- .name = "bootloader(spi)",
- .size = 0x00060000,
+ .name = "bootloader (spi)",
+ .size = 0x40000,
.offset = 0,
.mask_flags = MTD_CAP_ROM
}, {
- .name = "linux kernel(spi)",
- .size = 0x100000,
- .offset = 0x60000
+ .name = "fpga (spi)",
+ .size = 0x30000,
+ .offset = 0x40000
}, {
- .name = "file system(spi)",
- .size = 0x6a0000,
- .offset = 0x00160000,
+ .name = "linux kernel (spi)",
+ .size = 0x150000,
+ .offset = 0x70000
+ }, {
+ .name = "jffs2 root file system (spi)",
+ .size = 0x640000,
+ .offset = 0x1c0000,
}
};
@@ -340,7 +344,7 @@
static struct plat_serial8250_port serial8250_platform_data [] = {
{
- .membase = 0x20200000,
+ .membase = (void *)0x20200000,
.mapbase = 0x20200000,
.irq = IRQ_PF8,
.flags = UPF_BOOT_AUTOCONF | UART_CONFIG_TYPE,
@@ -348,7 +352,7 @@
.regshift = 1,
.uartclk = 66666667,
}, {
- .membase = 0x20200010,
+ .membase = (void *)0x20200010,
.mapbase = 0x20200010,
.irq = IRQ_PF8,
.flags = UPF_BOOT_AUTOCONF | UART_CONFIG_TYPE,
diff --git a/arch/blackfin/mach-bf533/head.S b/arch/blackfin/mach-bf533/head.S
index 01b2b7e..619685b 100644
--- a/arch/blackfin/mach-bf533/head.S
+++ b/arch/blackfin/mach-bf533/head.S
@@ -78,6 +78,9 @@
r1 = PLL_BYPASS; /* Bypass the PLL? */
r1 = r1 << 8; /* Shift it over */
r0 = r1 | r0; /* add them all together */
+#ifdef ANOMALY_05000265
+ r0 = BITSET(r0, 15); /* Add 250 mV of hysteresis to SPORT input pins */
+#endif
p0.h = hi(PLL_CTL);
p0.l = lo(PLL_CTL); /* Load the address */
diff --git a/arch/blackfin/mach-bf533/include/mach/anomaly.h b/arch/blackfin/mach-bf533/include/mach/anomaly.h
index 8f7ea11..f544fc5 100644
--- a/arch/blackfin/mach-bf533/include/mach/anomaly.h
+++ b/arch/blackfin/mach-bf533/include/mach/anomaly.h
@@ -7,7 +7,7 @@
*/
/* This file shoule be up to date with:
- * - Revision C, 02/08/2008; ADSP-BF531/BF532/BF533 Blackfin Processor Anomaly List
+ * - Revision D, 06/18/2008; ADSP-BF531/BF532/BF533 Blackfin Processor Anomaly List
*/
#ifndef _MACH_ANOMALY_H_
@@ -97,11 +97,11 @@
/* UART STB Bit Incorrectly Affects Receiver Setting */
#define ANOMALY_05000231 (__SILICON_REVISION__ < 5)
/* PPI_FS3 Is Not Driven in 2 or 3 Internal Frame Sync Transmit Modes */
-#define ANOMALY_05000233 (__SILICON_REVISION__ < 4)
+#define ANOMALY_05000233 (__SILICON_REVISION__ < 6)
/* Incorrect Revision Number in DSPID Register */
#define ANOMALY_05000234 (__SILICON_REVISION__ == 4)
/* DF Bit in PLL_CTL Register Does Not Respond to Hardware Reset */
-#define ANOMALY_05000242 (__SILICON_REVISION__ < 4)
+#define ANOMALY_05000242 (__SILICON_REVISION__ < 5)
/* If I-Cache Is On, CSYNC/SSYNC/IDLE Around Change of Control Causes Failures */
#define ANOMALY_05000244 (__SILICON_REVISION__ < 5)
/* Spurious Hardware Error from an Access in the Shadow of a Conditional Branch */
@@ -131,7 +131,7 @@
/* CSYNC/SSYNC/IDLE Causes Infinite Stall in Penultimate Instruction in Hardware Loop */
#define ANOMALY_05000264 (__SILICON_REVISION__ < 5)
/* Sensitivity To Noise with Slow Input Edge Rates on External SPORT TX and RX Clocks */
-#define ANOMALY_05000265 (__SILICON_REVISION__ < 5)
+#define ANOMALY_05000265 (1)
/* High I/O Activity Causes Output Voltage of Internal Voltage Regulator (Vddint) to Increase */
#define ANOMALY_05000269 (__SILICON_REVISION__ < 5)
/* High I/O Activity Causes Output Voltage of Internal Voltage Regulator (Vddint) to Decrease */
@@ -141,56 +141,59 @@
/* Certain Data Cache Writethrough Modes Fail for Vddint <= 0.9V */
#define ANOMALY_05000272 (1)
/* Writes to Synchronous SDRAM Memory May Be Lost */
-#define ANOMALY_05000273 (1)
+#define ANOMALY_05000273 (__SILICON_REVISION__ < 6)
/* Timing Requirements Change for External Frame Sync PPI Modes with Non-Zero PPI_DELAY */
#define ANOMALY_05000276 (1)
/* Writes to an I/O Data Register One SCLK Cycle after an Edge Is Detected May Clear Interrupt */
-#define ANOMALY_05000277 (1)
+#define ANOMALY_05000277 (__SILICON_REVISION__ < 6)
/* Disabling Peripherals with DMA Running May Cause DMA System Instability */
-#define ANOMALY_05000278 (1)
+#define ANOMALY_05000278 (__SILICON_REVISION__ < 6)
/* False Hardware Error Exception When ISR Context Is Not Restored */
-#define ANOMALY_05000281 (1)
+#define ANOMALY_05000281 (__SILICON_REVISION__ < 6)
/* Memory DMA Corruption with 32-Bit Data and Traffic Control */
-#define ANOMALY_05000282 (1)
+#define ANOMALY_05000282 (__SILICON_REVISION__ < 6)
/* System MMR Write Is Stalled Indefinitely When Killed in a Particular Stage */
-#define ANOMALY_05000283 (1)
+#define ANOMALY_05000283 (__SILICON_REVISION__ < 6)
/* SPORTs May Receive Bad Data If FIFOs Fill Up */
-#define ANOMALY_05000288 (1)
+#define ANOMALY_05000288 (__SILICON_REVISION__ < 6)
/* Memory-To-Memory DMA Source/Destination Descriptors Must Be in Same Memory Space */
-#define ANOMALY_05000301 (1)
+#define ANOMALY_05000301 (__SILICON_REVISION__ < 6)
/* SSYNCs After Writes To DMA MMR Registers May Not Be Handled Correctly */
#define ANOMALY_05000302 (__SILICON_REVISION__ < 5)
/* New Feature: Additional Hysteresis on SPORT Input Pins (Not Available On Older Silicon) */
#define ANOMALY_05000305 (__SILICON_REVISION__ < 5)
/* New Feature: Additional PPI Frame Sync Sampling Options (Not Available On Older Silicon) */
#define ANOMALY_05000306 (__SILICON_REVISION__ < 5)
+/* SCKELOW Bit Does Not Maintain State Through Hibernate */
+#define ANOMALY_05000307 (1)
/* False Hardware Errors Caused by Fetches at the Boundary of Reserved Memory */
#define ANOMALY_05000310 (1)
/* Erroneous Flag (GPIO) Pin Operations under Specific Sequences */
-#define ANOMALY_05000311 (1)
+#define ANOMALY_05000311 (__SILICON_REVISION__ < 6)
/* Errors When SSYNC, CSYNC, or Loads to LT, LB and LC Registers Are Interrupted */
-#define ANOMALY_05000312 (1)
+#define ANOMALY_05000312 (__SILICON_REVISION__ < 6)
/* PPI Is Level-Sensitive on First Transfer */
-#define ANOMALY_05000313 (1)
+#define ANOMALY_05000313 (__SILICON_REVISION__ < 6)
/* Killed System MMR Write Completes Erroneously On Next System MMR Access */
-#define ANOMALY_05000315 (1)
+#define ANOMALY_05000315 (__SILICON_REVISION__ < 6)
/* Internal Voltage Regulator Values of 1.05V, 1.10V and 1.15V Not Allowed for LQFP Packages */
-#define ANOMALY_05000319 (ANOMALY_BF531 || ANOMALY_BF532)
+#define ANOMALY_05000319 ((ANOMALY_BF531 || ANOMALY_BF532) && __SILICON_REVISION__ < 6)
/* Serial Port (SPORT) Multichannel Transmit Failure when Channel 0 Is Disabled */
-#define ANOMALY_05000357 (1)
+#define ANOMALY_05000357 (__SILICON_REVISION__ < 6)
/* UART Break Signal Issues */
#define ANOMALY_05000363 (__SILICON_REVISION__ < 5)
/* PPI Underflow Error Goes Undetected in ITU-R 656 Mode */
#define ANOMALY_05000366 (1)
/* Possible RETS Register Corruption when Subroutine Is under 5 Cycles in Duration */
-#define ANOMALY_05000371 (1)
+#define ANOMALY_05000371 (__SILICON_REVISION__ < 6)
/* PPI Does Not Start Properly In Specific Mode */
-#define ANOMALY_05000400 (__SILICON_REVISION__ >= 5)
+#define ANOMALY_05000400 (__SILICON_REVISION__ == 5)
/* SSYNC Stalls Processor when Executed from Non-Cacheable Memory */
-#define ANOMALY_05000402 (__SILICON_REVISION__ >= 5)
+#define ANOMALY_05000402 (__SILICON_REVISION__ == 5)
/* Level-Sensitive External GPIO Wakeups May Cause Indefinite Stall */
#define ANOMALY_05000403 (1)
-
+/* Speculative Fetches Can Cause Undesired External FIFO Operations */
+#define ANOMALY_05000416 (1)
/* These anomalies have been "phased" out of analog.com anomaly sheets and are
* here to show running on older silicon just isn't feasible.
@@ -268,5 +271,7 @@
/* Anomalies that don't exist on this proc */
#define ANOMALY_05000266 (0)
#define ANOMALY_05000323 (0)
+#define ANOMALY_05000353 (1)
+#define ANOMALY_05000386 (1)
#endif
diff --git a/arch/blackfin/mach-bf533/include/mach/bf533.h b/arch/blackfin/mach-bf533/include/mach/bf533.h
index 12a4169..dfc8c1a 100644
--- a/arch/blackfin/mach-bf533/include/mach/bf533.h
+++ b/arch/blackfin/mach-bf533/include/mach/bf533.h
@@ -30,8 +30,6 @@
#ifndef __MACH_BF533_H__
#define __MACH_BF533_H__
-#define SUPPORTED_REVID 2
-
#define OFFSET_(x) ((x) & 0x0000FFFF)
/*some misc defines*/
@@ -143,19 +141,19 @@
#ifdef CONFIG_BF533
#define CPU "BF533"
-#define CPUID 0x027a5000
+#define CPUID 0x27a5
#endif
#ifdef CONFIG_BF532
#define CPU "BF532"
-#define CPUID 0x0275A000
+#define CPUID 0x275A
#endif
#ifdef CONFIG_BF531
#define CPU "BF531"
-#define CPUID 0x027a5000
+#define CPUID 0x27a5
#endif
+
#ifndef CPU
-#define CPU "UNKNOWN"
-#define CPUID 0x0
+#error Unknown CPU type - This kernel doesn't seem to be configured properly
#endif
#endif /* __MACH_BF533_H__ */
diff --git a/arch/blackfin/mach-bf533/include/mach/bfin_serial_5xx.h b/arch/blackfin/mach-bf533/include/mach/bfin_serial_5xx.h
index ebf592b..34ab0e4 100644
--- a/arch/blackfin/mach-bf533/include/mach/bfin_serial_5xx.h
+++ b/arch/blackfin/mach-bf533/include/mach/bfin_serial_5xx.h
@@ -69,6 +69,8 @@
# endif
#endif
+#define BFIN_UART_TX_FIFO_SIZE 2
+
struct bfin_serial_port {
struct uart_port port;
unsigned int old_status;
@@ -83,7 +85,7 @@
unsigned int rx_dma_channel;
struct work_struct tx_dma_workqueue;
#else
-# if ANOMALY_05000230
+# if ANOMALY_05000363
unsigned int anomaly_threshold;
# endif
#endif
@@ -111,7 +113,6 @@
bfin_write16(uart->port.membase + OFFSET_LSR, -1);
}
-struct bfin_serial_port bfin_serial_ports[BFIN_UART_NR_PORTS];
struct bfin_serial_res {
unsigned long uart_base_addr;
int uart_irq;
@@ -142,7 +143,6 @@
#define DRIVER_NAME "bfin-uart"
-int nr_ports = BFIN_UART_NR_PORTS;
static void bfin_serial_hw_init(struct bfin_serial_port *uart)
{
diff --git a/arch/blackfin/mach-bf537/boards/stamp.c b/arch/blackfin/mach-bf537/boards/stamp.c
index 8482d22..dc5a308 100644
--- a/arch/blackfin/mach-bf537/boards/stamp.c
+++ b/arch/blackfin/mach-bf537/boards/stamp.c
@@ -51,7 +51,6 @@
#include <asm/reboot.h>
#include <asm/portmux.h>
#include <asm/dpmc.h>
-#include <linux/spi/ad7877.h>
/*
* Name the Board for the /proc/cpuinfo
@@ -555,6 +554,7 @@
#endif
#if defined(CONFIG_TOUCHSCREEN_AD7877) || defined(CONFIG_TOUCHSCREEN_AD7877_MODULE)
+#include <linux/spi/ad7877.h>
static struct bfin5xx_spi_chip spi_ad7877_chip_info = {
.enable_dma = 0,
.bits_per_word = 16,
@@ -575,6 +575,28 @@
};
#endif
+#if defined(CONFIG_TOUCHSCREEN_AD7879) || defined(CONFIG_TOUCHSCREEN_AD7879_MODULE)
+#include <linux/spi/ad7879.h>
+static struct bfin5xx_spi_chip spi_ad7879_chip_info = {
+ .enable_dma = 0,
+ .bits_per_word = 16,
+};
+
+static const struct ad7879_platform_data bfin_ad7879_ts_info = {
+ .model = 7879, /* Model = AD7879 */
+ .x_plate_ohms = 620, /* 620 Ohm from the touch datasheet */
+ .pressure_max = 10000,
+ .pressure_min = 0,
+ .first_conversion_delay = 3, /* wait 512us before do a first conversion */
+ .acquisition_time = 1, /* 4us acquisition time per sample */
+ .median = 2, /* do 8 measurements */
+ .averaging = 1, /* take the average of 4 middle samples */
+ .pen_down_acc_interval = 255, /* 9.4 ms */
+ .gpio_output = 1, /* configure AUX/VBAT/GPIO as GPIO output */
+ .gpio_default = 1, /* During initialization set GPIO = HIGH */
+};
+#endif
+
#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE)
static struct bfin5xx_spi_chip spidev_chip_info = {
.enable_dma = 0,
@@ -582,6 +604,13 @@
};
#endif
+#if defined(CONFIG_FB_BFIN_LQ035Q1) || defined(CONFIG_FB_BFIN_LQ035Q1_MODULE)
+static struct bfin5xx_spi_chip lq035q1_spi_chip_info = {
+ .enable_dma = 0,
+ .bits_per_word = 8,
+};
+#endif
+
#if defined(CONFIG_MTD_DATAFLASH) \
|| defined(CONFIG_MTD_DATAFLASH_MODULE)
@@ -721,6 +750,18 @@
.controller_data = &spi_ad7877_chip_info,
},
#endif
+#if defined(CONFIG_TOUCHSCREEN_AD7879) || defined(CONFIG_TOUCHSCREEN_AD7879_MODULE)
+ {
+ .modalias = "ad7879",
+ .platform_data = &bfin_ad7879_ts_info,
+ .irq = IRQ_PF7,
+ .max_speed_hz = 5000000, /* max spi clock (SCK) speed in HZ */
+ .bus_num = 0,
+ .chip_select = 1,
+ .controller_data = &spi_ad7879_chip_info,
+ .mode = SPI_CPHA | SPI_CPOL,
+ },
+#endif
#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE)
{
.modalias = "spidev",
@@ -730,6 +771,16 @@
.controller_data = &spidev_chip_info,
},
#endif
+#if defined(CONFIG_FB_BFIN_LQ035Q1) || defined(CONFIG_FB_BFIN_LQ035Q1_MODULE)
+ {
+ .modalias = "bfin-lq035q1-spi",
+ .max_speed_hz = 20000000, /* max spi clock (SCK) speed in HZ */
+ .bus_num = 0,
+ .chip_select = 2,
+ .controller_data = &lq035q1_spi_chip_info,
+ .mode = SPI_CPHA | SPI_CPOL,
+ },
+#endif
};
#if defined(CONFIG_SPI_BFIN) || defined(CONFIG_SPI_BFIN_MODULE)
@@ -777,6 +828,34 @@
};
#endif
+#if defined(CONFIG_FB_BFIN_LQ035Q1) || defined(CONFIG_FB_BFIN_LQ035Q1_MODULE)
+#include <asm/bfin-lq035q1.h>
+
+static struct bfin_lq035q1fb_disp_info bfin_lq035q1_data = {
+ .mode = LQ035_NORM | LQ035_RGB | LQ035_RL | LQ035_TB,
+ .use_bl = 0, /* let something else control the LCD Blacklight */
+ .gpio_bl = GPIO_PF7,
+};
+
+static struct resource bfin_lq035q1_resources[] = {
+ {
+ .start = IRQ_PPI_ERROR,
+ .end = IRQ_PPI_ERROR,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device bfin_lq035q1_device = {
+ .name = "bfin-lq035q1",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(bfin_lq035q1_resources),
+ .resource = bfin_lq035q1_resources,
+ .dev = {
+ .platform_data = &bfin_lq035q1_data,
+ },
+};
+#endif
+
#if defined(CONFIG_SERIAL_BFIN) || defined(CONFIG_SERIAL_BFIN_MODULE)
static struct resource bfin_uart_resources[] = {
#ifdef CONFIG_SERIAL_BFIN_UART0
@@ -997,6 +1076,10 @@
&bfin_fb_device,
#endif
+#if defined(CONFIG_FB_BFIN_LQ035Q1) || defined(CONFIG_FB_BFIN_LQ035Q1_MODULE)
+ &bfin_lq035q1_device,
+#endif
+
#if defined(CONFIG_FB_BFIN_7393) || defined(CONFIG_FB_BFIN_7393_MODULE)
&bfin_fb_adv7393_device,
#endif
diff --git a/arch/blackfin/mach-bf537/head.S b/arch/blackfin/mach-bf537/head.S
index 12eb5cc..559a7ee 100644
--- a/arch/blackfin/mach-bf537/head.S
+++ b/arch/blackfin/mach-bf537/head.S
@@ -87,6 +87,9 @@
r1 = PLL_BYPASS; /* Bypass the PLL? */
r1 = r1 << 8; /* Shift it over */
r0 = r1 | r0; /* add them all together */
+#ifdef ANOMALY_05000265
+ r0 = BITSET(r0, 15); /* Add 250 mV of hysteresis to SPORT input pins */
+#endif
p0.h = hi(PLL_CTL);
p0.l = lo(PLL_CTL); /* Load the address */
diff --git a/arch/blackfin/mach-bf537/include/mach/anomaly.h b/arch/blackfin/mach-bf537/include/mach/anomaly.h
index 8460ab9..c689924 100644
--- a/arch/blackfin/mach-bf537/include/mach/anomaly.h
+++ b/arch/blackfin/mach-bf537/include/mach/anomaly.h
@@ -158,6 +158,8 @@
#define ANOMALY_05000266 (0)
#define ANOMALY_05000311 (0)
#define ANOMALY_05000323 (0)
+#define ANOMALY_05000353 (1)
#define ANOMALY_05000363 (0)
+#define ANOMALY_05000386 (1)
#endif
diff --git a/arch/blackfin/mach-bf537/include/mach/bf537.h b/arch/blackfin/mach-bf537/include/mach/bf537.h
index cfe2a22..24d5c9d 100644
--- a/arch/blackfin/mach-bf537/include/mach/bf537.h
+++ b/arch/blackfin/mach-bf537/include/mach/bf537.h
@@ -30,8 +30,6 @@
#ifndef __MACH_BF537_H__
#define __MACH_BF537_H__
-#define SUPPORTED_REVID 2
-
/* Masks for generic ERROR IRQ demultiplexing used in int-priority-sc.c */
#define SPI_ERR_MASK (TXCOL | RBSY | MODF | TXE) /* SPI_STAT */
@@ -123,19 +121,19 @@
#ifdef CONFIG_BF537
#define CPU "BF537"
-#define CPUID 0x027c8000
+#define CPUID 0x27c8
#endif
#ifdef CONFIG_BF536
#define CPU "BF536"
-#define CPUID 0x027c8000
+#define CPUID 0x27c8
#endif
#ifdef CONFIG_BF534
#define CPU "BF534"
-#define CPUID 0x027c6000
+#define CPUID 0x27c6
#endif
+
#ifndef CPU
-#define CPU "UNKNOWN"
-#define CPUID 0x0
+#error Unknown CPU type - This kernel doesn't seem to be configured properly
#endif
#endif /* __MACH_BF537_H__ */
diff --git a/arch/blackfin/mach-bf537/include/mach/bfin_serial_5xx.h b/arch/blackfin/mach-bf537/include/mach/bfin_serial_5xx.h
index 1bf56ff..b3f87e1 100644
--- a/arch/blackfin/mach-bf537/include/mach/bfin_serial_5xx.h
+++ b/arch/blackfin/mach-bf537/include/mach/bfin_serial_5xx.h
@@ -78,6 +78,9 @@
# define CONFIG_UART1_RTS_PIN -1
# endif
#endif
+
+#define BFIN_UART_TX_FIFO_SIZE 2
+
/*
* The pin configuration is different from schematic
*/
@@ -119,7 +122,6 @@
bfin_write16(uart->port.membase + OFFSET_LSR, -1);
}
-struct bfin_serial_port bfin_serial_ports[BFIN_UART_NR_PORTS];
struct bfin_serial_res {
unsigned long uart_base_addr;
int uart_irq;
@@ -164,8 +166,6 @@
#endif
};
-int nr_ports = ARRAY_SIZE(bfin_serial_resource);
-
#define DRIVER_NAME "bfin-uart"
static void bfin_serial_hw_init(struct bfin_serial_port *uart)
diff --git a/arch/blackfin/mach-bf548/boards/cm_bf548.c b/arch/blackfin/mach-bf548/boards/cm_bf548.c
index ce934ee..24192aa 100644
--- a/arch/blackfin/mach-bf548/boards/cm_bf548.c
+++ b/arch/blackfin/mach-bf548/boards/cm_bf548.c
@@ -36,11 +36,8 @@
#include <linux/spi/flash.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
-#if defined(CONFIG_USB_MUSB_HDRC) || defined(CONFIG_USB_MUSB_HDRC_MODULE)
#include <linux/usb/musb.h>
-#endif
#include <asm/bfin5xx_spi.h>
-#include <asm/cplb.h>
#include <asm/dma.h>
#include <asm/gpio.h>
#include <asm/nand.h>
@@ -175,6 +172,7 @@
{
.start = 0xFFC03100,
.end = 0xFFC031FF,
+ .flags = IORESOURCE_MEM,
},
#endif
};
@@ -268,6 +266,16 @@
},
};
+static struct musb_hdrc_config musb_config = {
+ .multipoint = 0,
+ .dyn_fifo = 0,
+ .soft_con = 1,
+ .dma = 1,
+ .num_eps = 7,
+ .dma_channels = 7,
+ .gpio_vrsel = GPIO_PH6,
+};
+
static struct musb_hdrc_platform_data musb_plat = {
#if defined(CONFIG_USB_MUSB_OTG)
.mode = MUSB_OTG,
@@ -276,7 +284,7 @@
#elif defined(CONFIG_USB_GADGET_MUSB_HDRC)
.mode = MUSB_PERIPHERAL,
#endif
- .multipoint = 0,
+ .config = &musb_config,
};
static u64 musb_dmamask = ~(u32)0;
@@ -321,12 +329,12 @@
{
.name = "linux kernel(nand)",
.offset = 0,
- .size = 4 * SIZE_1M,
+ .size = 4 * 1024 * 1024,
},
{
.name = "file system(nand)",
- .offset = 4 * SIZE_1M,
- .size = (256 - 4) * SIZE_1M,
+ .offset = 4 * 1024 * 1024,
+ .size = (256 - 4) * 1024 * 1024,
},
};
diff --git a/arch/blackfin/mach-bf548/boards/ezkit.c b/arch/blackfin/mach-bf548/boards/ezkit.c
index 3935769..5288187 100644
--- a/arch/blackfin/mach-bf548/boards/ezkit.c
+++ b/arch/blackfin/mach-bf548/boards/ezkit.c
@@ -38,11 +38,8 @@
#include <linux/irq.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
-#if defined(CONFIG_USB_MUSB_HDRC) || defined(CONFIG_USB_MUSB_HDRC_MODULE)
#include <linux/usb/musb.h>
-#endif
#include <asm/bfin5xx_spi.h>
-#include <asm/cplb.h>
#include <asm/dma.h>
#include <asm/gpio.h>
#include <asm/nand.h>
@@ -186,6 +183,37 @@
};
#endif
+#if defined(CONFIG_JOYSTICK_BFIN_ROTARY) || defined(CONFIG_JOYSTICK_BFIN_ROTARY_MODULE)
+#include <asm/bfin_rotary.h>
+
+static struct bfin_rotary_platform_data bfin_rotary_data = {
+ /*.rotary_up_key = KEY_UP,*/
+ /*.rotary_down_key = KEY_DOWN,*/
+ .rotary_rel_code = REL_WHEEL,
+ .rotary_button_key = KEY_ENTER,
+ .debounce = 10, /* 0..17 */
+ .mode = ROT_QUAD_ENC | ROT_DEBE,
+};
+
+static struct resource bfin_rotary_resources[] = {
+ {
+ .start = IRQ_CNT,
+ .end = IRQ_CNT,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device bfin_rotary_device = {
+ .name = "bfin-rotary",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(bfin_rotary_resources),
+ .resource = bfin_rotary_resources,
+ .dev = {
+ .platform_data = &bfin_rotary_data,
+ },
+};
+#endif
+
#if defined(CONFIG_RTC_DRV_BFIN) || defined(CONFIG_RTC_DRV_BFIN_MODULE)
static struct platform_device rtc_device = {
.name = "rtc-bfin",
@@ -314,6 +342,16 @@
},
};
+static struct musb_hdrc_config musb_config = {
+ .multipoint = 0,
+ .dyn_fifo = 0,
+ .soft_con = 1,
+ .dma = 1,
+ .num_eps = 7,
+ .dma_channels = 7,
+ .gpio_vrsel = GPIO_PE7,
+};
+
static struct musb_hdrc_platform_data musb_plat = {
#if defined(CONFIG_USB_MUSB_OTG)
.mode = MUSB_OTG,
@@ -322,7 +360,7 @@
#elif defined(CONFIG_USB_GADGET_MUSB_HDRC)
.mode = MUSB_PERIPHERAL,
#endif
- .multipoint = 0,
+ .config = &musb_config,
};
static u64 musb_dmamask = ~(u32)0;
@@ -367,7 +405,7 @@
{
.name = "linux kernel(nand)",
.offset = 0,
- .size = 4 * SIZE_1M,
+ .size = 4 * 1024 * 1024,
},
{
.name = "file system(nand)",
@@ -424,7 +462,7 @@
.offset = 0,
}, {
.name = "linux kernel(nor)",
- .size = 0x1C0000,
+ .size = 0x400000,
.offset = MTDPART_OFS_APPEND,
}, {
.name = "file system(nor)",
@@ -441,7 +479,7 @@
static struct resource ezkit_flash_resource = {
.start = 0x20000000,
- .end = 0x20ffffff,
+ .end = 0x21ffffff,
.flags = IORESOURCE_MEM,
};
@@ -551,7 +589,7 @@
{
.modalias = "ad7877",
.platform_data = &bfin_ad7877_ts_info,
- .irq = IRQ_PJ11,
+ .irq = IRQ_PJ11, /* newer boards (Rev 1.4+) use IRQ_PB4 */
.max_speed_hz = 12500000, /* max spi clock (SCK) speed in HZ */
.bus_num = 0,
.chip_select = 2,
@@ -810,6 +848,10 @@
&bf54x_kpad_device,
#endif
+#if defined(CONFIG_JOYSTICK_BFIN_ROTARY) || defined(CONFIG_JOYSTICK_BFIN_ROTARY_MODULE)
+ &bfin_rotary_device,
+#endif
+
#if defined(CONFIG_I2C_BLACKFIN_TWI) || defined(CONFIG_I2C_BLACKFIN_TWI_MODULE)
&i2c_bfin_twi0_device,
#if !defined(CONFIG_BF542)
diff --git a/arch/blackfin/mach-bf548/head.S b/arch/blackfin/mach-bf548/head.S
index 4d5cfea..051b05c 100644
--- a/arch/blackfin/mach-bf548/head.S
+++ b/arch/blackfin/mach-bf548/head.S
@@ -73,25 +73,19 @@
w[p0] = r0.l;
ssync;
-#if defined(CONFIG_BF54x)
+ /* enable self refresh via SRREQ */
P2.H = hi(EBIU_RSTCTL);
P2.L = lo(EBIU_RSTCTL);
R0 = [P2];
BITSET (R0, 3);
-#else
- P2.H = hi(EBIU_SDGCTL);
- P2.L = lo(EBIU_SDGCTL);
- R0 = [P2];
- BITSET (R0, 24);
-#endif
[P2] = R0;
SSYNC;
-#if defined(CONFIG_BF54x)
+
+ /* wait for SRACK bit to be set */
.LSRR_MODE:
R0 = [P2];
CC = BITTST(R0, 4);
if !CC JUMP .LSRR_MODE;
-#endif
r0 = CONFIG_VCO_MULT & 63; /* Load the VCO multiplier */
r0 = r0 << 9; /* Shift it over, */
@@ -100,6 +94,9 @@
r1 = PLL_BYPASS; /* Bypass the PLL? */
r1 = r1 << 8; /* Shift it over */
r0 = r1 | r0; /* add them all together */
+#ifdef ANOMALY_05000265
+ r0 = BITSET(r0, 15); /* Add 250 mV of hysteresis to SPORT input pins */
+#endif
p0.h = hi(PLL_CTL);
p0.l = lo(PLL_CTL); /* Load the address */
@@ -123,7 +120,7 @@
w[p0] = r0.l;
ssync;
-#if defined(CONFIG_BF54x)
+ /* disable self refresh by clearing SRREQ */
P2.H = hi(EBIU_RSTCTL);
P2.L = lo(EBIU_RSTCTL);
R0 = [P2];
@@ -155,41 +152,6 @@
r0.h = hi(mem_DDRCTL2);
[p0] = r0;
ssync;
-#else
- p0.l = lo(EBIU_SDRRC);
- p0.h = hi(EBIU_SDRRC);
- r0 = mem_SDRRC;
- w[p0] = r0.l;
- ssync;
-
- p0.l = LO(EBIU_SDBCTL);
- p0.h = HI(EBIU_SDBCTL); /* SDRAM Memory Bank Control Register */
- r0 = mem_SDBCTL;
- w[p0] = r0.l;
- ssync;
-
- P2.H = hi(EBIU_SDGCTL);
- P2.L = lo(EBIU_SDGCTL);
- R0 = [P2];
- BITCLR (R0, 24);
- p0.h = hi(EBIU_SDSTAT);
- p0.l = lo(EBIU_SDSTAT);
- r2.l = w[p0];
- cc = bittst(r2,3);
- if !cc jump .Lskip;
- NOP;
- BITSET (R0, 23);
-.Lskip:
- [P2] = R0;
- SSYNC;
-
- R0.L = lo(mem_SDGCTL);
- R0.H = hi(mem_SDGCTL);
- R1 = [p2];
- R1 = R1 | R0;
- [P2] = R1;
- SSYNC;
-#endif
RTS;
ENDPROC(_start_dma_code)
diff --git a/arch/blackfin/mach-bf548/include/mach/anomaly.h b/arch/blackfin/mach-bf548/include/mach/anomaly.h
index 3ad5965..816b092 100644
--- a/arch/blackfin/mach-bf548/include/mach/anomaly.h
+++ b/arch/blackfin/mach-bf548/include/mach/anomaly.h
@@ -2,18 +2,18 @@
* File: include/asm-blackfin/mach-bf548/anomaly.h
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
- * Copyright (C) 2004-2007 Analog Devices Inc.
+ * Copyright (C) 2004-2008 Analog Devices Inc.
* Licensed under the GPL-2 or later.
*/
/* This file shoule be up to date with:
- * - Revision E, 11/28/2007; ADSP-BF542/BF544/BF547/BF548/BF549 Blackfin Processor Anomaly List
+ * - Revision G, 08/07/2008; ADSP-BF542/BF544/BF547/BF548/BF549 Blackfin Processor Anomaly List
*/
#ifndef _MACH_ANOMALY_H_
#define _MACH_ANOMALY_H_
-/* Multi-Issue Instruction with dsp32shiftimm in slot1 and P-reg Store in slot 2 Not Supported */
+/* Multi-Issue Instruction with dsp32shiftimm in slot1 and P-reg Store in slot2 Not Supported */
#define ANOMALY_05000074 (1)
/* DMA_RUN Bit Is Not Valid after a Peripheral Receive Channel DMA Stops */
#define ANOMALY_05000119 (1)
@@ -36,14 +36,14 @@
/* TWI Slave Boot Mode Is Not Functional */
#define ANOMALY_05000324 (__SILICON_REVISION__ < 1)
/* External FIFO Boot Mode Is Not Functional */
-#define ANOMALY_05000325 (__SILICON_REVISION__ < 1)
+#define ANOMALY_05000325 (__SILICON_REVISION__ < 2)
/* Data Lost When Core and DMA Accesses Are Made to the USB FIFO Simultaneously */
#define ANOMALY_05000327 (__SILICON_REVISION__ < 1)
/* Incorrect Access of OTP_STATUS During otp_write() Function */
#define ANOMALY_05000328 (__SILICON_REVISION__ < 1)
/* Synchronous Burst Flash Boot Mode Is Not Functional */
#define ANOMALY_05000329 (__SILICON_REVISION__ < 1)
-/* Host DMA Boot Mode Is Not Functional */
+/* Host DMA Boot Modes Are Not Functional */
#define ANOMALY_05000330 (__SILICON_REVISION__ < 1)
/* Inadequate Timing Margins on DDR DQS to DQ and DQM Skew */
#define ANOMALY_05000334 (__SILICON_REVISION__ < 1)
@@ -61,26 +61,102 @@
#define ANOMALY_05000344 (__SILICON_REVISION__ < 1)
/* USB Calibration Value Is Not Intialized */
#define ANOMALY_05000346 (__SILICON_REVISION__ < 1)
-/* Boot ROM Kernel Incorrectly Alters Reset Value of USB Register */
+/* USB Calibration Value to use */
+#define ANOMALY_05000346_value 0x5411
+/* Preboot Routine Incorrectly Alters Reset Value of USB Register */
#define ANOMALY_05000347 (__SILICON_REVISION__ < 1)
/* Data Lost when Core Reads SDH Data FIFO */
#define ANOMALY_05000349 (__SILICON_REVISION__ < 1)
/* PLL Status Register Is Inaccurate */
#define ANOMALY_05000351 (__SILICON_REVISION__ < 1)
+/* bfrom_SysControl() Firmware Function Performs Improper System Reset */
+#define ANOMALY_05000353 (__SILICON_REVISION__ < 2)
+/* Regulator Programming Blocked when Hibernate Wakeup Source Remains Active */
+#define ANOMALY_05000355 (__SILICON_REVISION__ < 1)
+/* System Stalled During A Core Access To AMC While A Core Access To NFC FIFO Is Required */
+#define ANOMALY_05000356 (__SILICON_REVISION__ < 1)
/* Serial Port (SPORT) Multichannel Transmit Failure when Channel 0 Is Disabled */
#define ANOMALY_05000357 (1)
/* External Memory Read Access Hangs Core With PLL Bypass */
#define ANOMALY_05000360 (1)
/* DMAs that Go Urgent during Tight Core Writes to External Memory Are Blocked */
#define ANOMALY_05000365 (1)
+/* WURESET Bit In SYSCR Register Does Not Properly Indicate Hibernate Wake-Up */
+#define ANOMALY_05000367 (__SILICON_REVISION__ < 1)
/* Addressing Conflict between Boot ROM and Asynchronous Memory */
#define ANOMALY_05000369 (1)
+/* Default PLL MSEL and SSEL Settings Can Cause 400MHz Product To Violate Specifications */
+#define ANOMALY_05000370 (__SILICON_REVISION__ < 1)
/* Possible RETS Register Corruption when Subroutine Is under 5 Cycles in Duration */
-#define ANOMALY_05000371 (1)
+#define ANOMALY_05000371 (__SILICON_REVISION__ < 2)
+/* USB DP/DM Data Pins May Lose State When Entering Hibernate */
+#define ANOMALY_05000372 (__SILICON_REVISION__ < 1)
/* Mobile DDR Operation Not Functional */
#define ANOMALY_05000377 (1)
/* Security/Authentication Speedpath Causes Authentication To Fail To Initiate */
-#define ANOMALY_05000378 (1)
+#define ANOMALY_05000378 (__SILICON_REVISION__ < 2)
+/* 16-Bit NAND FLASH Boot Mode Is Not Functional */
+#define ANOMALY_05000379 (1)
+/* 8-Bit NAND Flash Boot Mode Not Functional */
+#define ANOMALY_05000382 (__SILICON_REVISION__ < 1)
+/* Some ATAPI Modes Are Not Functional */
+#define ANOMALY_05000383 (1)
+/* Boot from OTP Memory Not Functional */
+#define ANOMALY_05000385 (__SILICON_REVISION__ < 1)
+/* bfrom_SysControl() Firmware Routine Not Functional */
+#define ANOMALY_05000386 (__SILICON_REVISION__ < 1)
+/* Programmable Preboot Settings Not Functional */
+#define ANOMALY_05000387 (__SILICON_REVISION__ < 1)
+/* CRC32 Checksum Support Not Functional */
+#define ANOMALY_05000388 (__SILICON_REVISION__ < 1)
+/* Reset Vector Must Not Be in SDRAM Memory Space */
+#define ANOMALY_05000389 (__SILICON_REVISION__ < 1)
+/* Changed Meaning of BCODE Field in SYSCR Register */
+#define ANOMALY_05000390 (__SILICON_REVISION__ < 1)
+/* Repeated Boot from Page-Mode or Burst-Mode Flash Memory May Fail */
+#define ANOMALY_05000391 (__SILICON_REVISION__ < 1)
+/* pTempCurrent Not Present in ADI_BOOT_DATA Structure */
+#define ANOMALY_05000392 (__SILICON_REVISION__ < 1)
+/* Deprecated Value of dTempByteCount in ADI_BOOT_DATA Structure */
+#define ANOMALY_05000393 (__SILICON_REVISION__ < 1)
+/* Log Buffer Not Functional */
+#define ANOMALY_05000394 (__SILICON_REVISION__ < 1)
+/* Hook Routine Not Functional */
+#define ANOMALY_05000395 (__SILICON_REVISION__ < 1)
+/* Header Indirect Bit Not Functional */
+#define ANOMALY_05000396 (__SILICON_REVISION__ < 1)
+/* BK_ONES, BK_ZEROS, and BK_DATECODE Constants Not Functional */
+#define ANOMALY_05000397 (__SILICON_REVISION__ < 1)
+/* Lockbox SESR Disallows Certain User Interrupts */
+#define ANOMALY_05000404 (__SILICON_REVISION__ < 2)
+/* Lockbox SESR Firmware Does Not Save/Restore Full Context */
+#define ANOMALY_05000405 (1)
+/* Lockbox SESR Argument Checking Does Not Check L2 Memory Protection Range */
+#define ANOMALY_05000406 (__SILICON_REVISION__ < 2)
+/* Lockbox SESR Firmware Arguments Are Not Retained After First Initialization */
+#define ANOMALY_05000407 (__SILICON_REVISION__ < 2)
+/* Lockbox Firmware Memory Cleanup Routine Does not Clear Registers */
+#define ANOMALY_05000408 (1)
+/* Lockbox firmware leaves MDMA0 channel enabled */
+#define ANOMALY_05000409 (__SILICON_REVISION__ < 2)
+/* bfrom_SysControl() Firmware Function Cannot be Used to Enter Power Saving Modes */
+#define ANOMALY_05000411 (__SILICON_REVISION__ < 2)
+/* NAND Boot Mode Not Compatible With Some NAND Flash Devices */
+#define ANOMALY_05000413 (__SILICON_REVISION__ < 2)
+/* OTP_CHECK_FOR_PREV_WRITE Bit is Not Functional in bfrom_OtpWrite() API */
+#define ANOMALY_05000414 (__SILICON_REVISION__ < 2)
+/* Speculative Fetches Can Cause Undesired External FIFO Operations */
+#define ANOMALY_05000416 (1)
+/* Multichannel SPORT Channel Misalignment Under Specific Configuration */
+#define ANOMALY_05000425 (1)
+/* Speculative Fetches of Indirect-Pointer Instructions Can Cause Spurious Hardware Errors */
+#define ANOMALY_05000426 (1)
+/* CORE_EPPI_PRIO bit and SYS_EPPI_PRIO bit in the HMDMA1_CONTROL register are not functional */
+#define ANOMALY_05000427 (__SILICON_REVISION__ < 2)
+/* WB_EDGE Bit in NFC_IRQSTAT Incorrectly Behaves as a Buffer Status Bit Instead of an IRQ Status Bit */
+#define ANOMALY_05000429 (__SILICON_REVISION__ < 2)
+/* Software System Reset Corrupts PLL_LOCKCNT Register */
+#define ANOMALY_05000430 (__SILICON_REVISION__ >= 2)
/* Anomalies that don't exist on this proc */
#define ANOMALY_05000125 (0)
@@ -93,6 +169,7 @@
#define ANOMALY_05000263 (0)
#define ANOMALY_05000266 (0)
#define ANOMALY_05000273 (0)
+#define ANOMALY_05000307 (0)
#define ANOMALY_05000311 (0)
#define ANOMALY_05000323 (0)
#define ANOMALY_05000363 (0)
diff --git a/arch/blackfin/mach-bf548/include/mach/bf548.h b/arch/blackfin/mach-bf548/include/mach/bf548.h
index e748588..49f9b40 100644
--- a/arch/blackfin/mach-bf548/include/mach/bf548.h
+++ b/arch/blackfin/mach-bf548/include/mach/bf548.h
@@ -30,8 +30,6 @@
#ifndef __MACH_BF548_H__
#define __MACH_BF548_H__
-#define SUPPORTED_REVID 0
-
#define OFFSET_(x) ((x) & 0x0000FFFF)
/*some misc defines*/
@@ -108,20 +106,23 @@
#if defined(CONFIG_BF542)
# define CPU "BF542"
-# define CPUID 0x027c8000
+# define CPUID 0x27de
#elif defined(CONFIG_BF544)
-# define CPU "BF544"
-# define CPUID 0x027c8000
+# define CPU "BF544"
+# define CPUID 0x27de
#elif defined(CONFIG_BF547)
-# define CPU "BF547"
+# define CPU "BF547"
+# define CPUID 0x27de
#elif defined(CONFIG_BF548)
-# define CPU "BF548"
-# define CPUID 0x027c6000
+# define CPU "BF548"
+# define CPUID 0x27de
#elif defined(CONFIG_BF549)
-# define CPU "BF549"
-#else
-# define CPU "UNKNOWN"
-# define CPUID 0x0
+# define CPU "BF549"
+# define CPUID 0x27de
+#endif
+
+#ifndef CPU
+#error Unknown CPU type - This kernel doesn't seem to be configured properly
#endif
#endif /* __MACH_BF48_H__ */
diff --git a/arch/blackfin/mach-bf548/include/mach/bfin_serial_5xx.h b/arch/blackfin/mach-bf548/include/mach/bfin_serial_5xx.h
index 5e29446..e4cf35e 100644
--- a/arch/blackfin/mach-bf548/include/mach/bfin_serial_5xx.h
+++ b/arch/blackfin/mach-bf548/include/mach/bfin_serial_5xx.h
@@ -82,6 +82,9 @@
# define CONFIG_UART1_RTS_PIN -1
# endif
#endif
+
+#define BFIN_UART_TX_FIFO_SIZE 2
+
/*
* The pin configuration is different from schematic
*/
@@ -105,7 +108,6 @@
#endif
};
-struct bfin_serial_port bfin_serial_ports[BFIN_UART_NR_PORTS];
struct bfin_serial_res {
unsigned long uart_base_addr;
int uart_irq;
@@ -170,8 +172,6 @@
#endif
};
-int nr_ports = ARRAY_SIZE(bfin_serial_resource);
-
#define DRIVER_NAME "bfin-uart"
static void bfin_serial_hw_init(struct bfin_serial_port *uart)
diff --git a/arch/blackfin/mach-bf548/include/mach/mem_map.h b/arch/blackfin/mach-bf548/include/mach/mem_map.h
index f99f47b..a222842 100644
--- a/arch/blackfin/mach-bf548/include/mach/mem_map.h
+++ b/arch/blackfin/mach-bf548/include/mach/mem_map.h
@@ -94,13 +94,13 @@
#endif /*CONFIG_BFIN_DCACHE*/
/* Level 2 Memory */
-#if !defined(CONFIG_BF542)
-# define L2_START 0xFEB00000
-# if defined(CONFIG_BF544)
-# define L2_LENGTH 0x10000
-# else
-# define L2_LENGTH 0x20000
-# endif
+#define L2_START 0xFEB00000
+#if defined(CONFIG_BF542)
+# define L2_LENGTH 0
+#elif defined(CONFIG_BF544)
+# define L2_LENGTH 0x10000
+#else
+# define L2_LENGTH 0x20000
#endif
/* Scratch Pad Memory */
diff --git a/arch/blackfin/mach-bf561/head.S b/arch/blackfin/mach-bf561/head.S
index 75ea6a9..0b28137 100644
--- a/arch/blackfin/mach-bf561/head.S
+++ b/arch/blackfin/mach-bf561/head.S
@@ -77,6 +77,9 @@
r1 = PLL_BYPASS; /* Bypass the PLL? */
r1 = r1 << 8; /* Shift it over */
r0 = r1 | r0; /* add them all together */
+#ifdef ANOMALY_05000265
+ r0 = BITSET(r0, 15); /* Add 250 mV of hysteresis to SPORT input pins */
+#endif
p0.h = hi(PLL_CTL);
p0.l = lo(PLL_CTL); /* Load the address */
diff --git a/arch/blackfin/mach-bf561/include/mach/anomaly.h b/arch/blackfin/mach-bf561/include/mach/anomaly.h
index 5c5d7d7..22990df 100644
--- a/arch/blackfin/mach-bf561/include/mach/anomaly.h
+++ b/arch/blackfin/mach-bf561/include/mach/anomaly.h
@@ -270,5 +270,7 @@
#define ANOMALY_05000183 (0)
#define ANOMALY_05000273 (0)
#define ANOMALY_05000311 (0)
+#define ANOMALY_05000353 (1)
+#define ANOMALY_05000386 (1)
#endif
diff --git a/arch/blackfin/mach-bf561/include/mach/bf561.h b/arch/blackfin/mach-bf561/include/mach/bf561.h
index 3ef9e5f..18b1b3a 100644
--- a/arch/blackfin/mach-bf561/include/mach/bf561.h
+++ b/arch/blackfin/mach-bf561/include/mach/bf561.h
@@ -30,8 +30,6 @@
#ifndef __MACH_BF561_H__
#define __MACH_BF561_H__
-#define SUPPORTED_REVID 0x3
-
#define OFFSET_(x) ((x) & 0x0000FFFF)
/*some misc defines*/
@@ -213,11 +211,11 @@
#ifdef CONFIG_BF561
#define CPU "BF561"
-#define CPUID 0x027bb000
+#define CPUID 0x27bb
#endif
+
#ifndef CPU
-#define CPU "UNKNOWN"
-#define CPUID 0x0
+#error Unknown CPU type - This kernel doesn't seem to be configured properly
#endif
#endif /* __MACH_BF561_H__ */
diff --git a/arch/blackfin/mach-bf561/include/mach/bfin_serial_5xx.h b/arch/blackfin/mach-bf561/include/mach/bfin_serial_5xx.h
index 8aa0278..f532726 100644
--- a/arch/blackfin/mach-bf561/include/mach/bfin_serial_5xx.h
+++ b/arch/blackfin/mach-bf561/include/mach/bfin_serial_5xx.h
@@ -69,6 +69,8 @@
# endif
#endif
+#define BFIN_UART_TX_FIFO_SIZE 2
+
struct bfin_serial_port {
struct uart_port port;
unsigned int old_status;
@@ -83,7 +85,7 @@
unsigned int rx_dma_channel;
struct work_struct tx_dma_workqueue;
#else
-# if ANOMALY_05000230
+# if ANOMALY_05000363
unsigned int anomaly_threshold;
# endif
#endif
@@ -111,7 +113,6 @@
bfin_write16(uart->port.membase + OFFSET_LSR, -1);
}
-struct bfin_serial_port bfin_serial_ports[BFIN_UART_NR_PORTS];
struct bfin_serial_res {
unsigned long uart_base_addr;
int uart_irq;
@@ -142,7 +143,6 @@
#define DRIVER_NAME "bfin-uart"
-int nr_ports = BFIN_UART_NR_PORTS;
static void bfin_serial_hw_init(struct bfin_serial_port *uart)
{
diff --git a/arch/blackfin/mach-bf561/include/mach/mem_map.h b/arch/blackfin/mach-bf561/include/mach/mem_map.h
index c26d848..f1d4c06 100644
--- a/arch/blackfin/mach-bf561/include/mach/mem_map.h
+++ b/arch/blackfin/mach-bf561/include/mach/mem_map.h
@@ -35,9 +35,16 @@
/* Memory Map for ADSP-BF561 processors */
#ifdef CONFIG_BF561
-#define L1_CODE_START 0xFFA00000
-#define L1_DATA_A_START 0xFF800000
-#define L1_DATA_B_START 0xFF900000
+#define COREA_L1_CODE_START 0xFFA00000
+#define COREA_L1_DATA_A_START 0xFF800000
+#define COREA_L1_DATA_B_START 0xFF900000
+#define COREB_L1_CODE_START 0xFF600000
+#define COREB_L1_DATA_A_START 0xFF400000
+#define COREB_L1_DATA_B_START 0xFF500000
+
+#define L1_CODE_START COREA_L1_CODE_START
+#define L1_DATA_A_START COREA_L1_DATA_A_START
+#define L1_DATA_B_START COREA_L1_DATA_B_START
#define L1_CODE_LENGTH 0x4000
@@ -72,7 +79,10 @@
/* Scratch Pad Memory */
-#define L1_SCRATCH_START 0xFFB00000
+#define COREA_L1_SCRATCH_START 0xFFB00000
+#define COREB_L1_SCRATCH_START 0xFF700000
+
+#define L1_SCRATCH_START COREA_L1_SCRATCH_START
#define L1_SCRATCH_LENGTH 0x1000
#endif /* _MEM_MAP_533_H_ */
diff --git a/arch/blackfin/mach-common/entry.S b/arch/blackfin/mach-common/entry.S
index 847c172..c13fa8d 100644
--- a/arch/blackfin/mach-common/entry.S
+++ b/arch/blackfin/mach-common/entry.S
@@ -129,6 +129,18 @@
#else
call __cplb_hdr;
#endif
+
+#ifdef CONFIG_DEBUG_DOUBLEFAULT
+ /* While we were processing this, did we double fault? */
+ r7 = SEQSTAT; /* reason code is in bit 5:0 */
+ r6.l = lo(SEQSTAT_EXCAUSE);
+ r6.h = hi(SEQSTAT_EXCAUSE);
+ r7 = r7 & r6;
+ r6 = 0x25;
+ CC = R7 == R6;
+ if CC JUMP _double_fault;
+#endif
+
DEBUG_HWTRACE_RESTORE(p5, r7)
RESTORE_ALL_SYS
SP = EX_SCRATCH_REG;
@@ -136,11 +148,8 @@
ENDPROC(_ex_icplb_miss)
ENTRY(_ex_syscall)
- (R7:6,P5:4) = [sp++];
- ASTAT = [sp++];
raise 15; /* invoked by TRAP #0, for sys call */
- sp = EX_SCRATCH_REG;
- rtx
+ jump.s _bfin_return_from_exception;
ENDPROC(_ex_syscall)
ENTRY(_ex_soft_bp)
@@ -181,8 +190,8 @@
if cc jump .Lfind_priority_done;
jump.s .Lfind_priority_start;
.Lfind_priority_done:
- p4.l = _debugger_step;
- p4.h = _debugger_step;
+ p4.l = _kgdb_single_step;
+ p4.h = _kgdb_single_step;
r6 = [p4];
cc = r6 == 0;
if cc jump .Ldo_single_step;
@@ -250,6 +259,29 @@
R7=LC1;
LC1=R7;
#endif
+
+#ifdef CONFIG_DEBUG_DOUBLEFAULT
+ /* While we were processing the current exception,
+ * did we cause another, and double fault?
+ */
+ r7 = SEQSTAT; /* reason code is in bit 5:0 */
+ r6.l = lo(SEQSTAT_EXCAUSE);
+ r6.h = hi(SEQSTAT_EXCAUSE);
+ r7 = r7 & r6;
+ r6 = 0x25;
+ CC = R7 == R6;
+ if CC JUMP _double_fault;
+
+ /* Did we cause a HW error? */
+ p5.l = lo(ILAT);
+ p5.h = hi(ILAT);
+ r6 = [p5];
+ r7 = 0x20; /* Did I just cause anther HW error? */
+ r7 = r7 & r1;
+ CC = R7 == R6;
+ if CC JUMP _double_fault;
+#endif
+
(R7:6,P5:4) = [sp++];
ASTAT = [sp++];
sp = EX_SCRATCH_REG;
@@ -292,6 +324,14 @@
[p4] = p5;
csync;
+#ifndef CONFIG_DEBUG_DOUBLEFAULT
+ /*
+ * Save these registers, as they are only valid in exception context
+ * (where we are now - as soon as we defer to IRQ5, they can change)
+ * DCPLB_STATUS and ICPLB_STATUS are also only valid in EVT3,
+ * but they are not very interesting, so don't save them
+ */
+
p4.l = lo(DCPLB_FAULT_ADDR);
p4.h = hi(DCPLB_FAULT_ADDR);
r7 = [p4];
@@ -304,12 +344,11 @@
p5.l = _saved_icplb_fault_addr;
[p5] = r7;
- p4.l = _excpt_saved_stuff;
- p4.h = _excpt_saved_stuff;
-
r6 = retx;
+ p4.l = _saved_retx;
+ p4.h = _saved_retx;
[p4] = r6;
-
+#endif
r6 = SYSCFG;
[p4 + 4] = r6;
BITCLR(r6, 0);
@@ -327,59 +366,56 @@
r6 = 0x3f;
sti r6;
- (R7:6,P5:4) = [sp++];
- ASTAT = [sp++];
- SP = EX_SCRATCH_REG;
raise 5;
- rtx;
+ jump.s _bfin_return_from_exception;
ENDPROC(_ex_trap_c)
/* We just realized we got an exception, while we were processing a different
* exception. This is a unrecoverable event, so crash
*/
ENTRY(_double_fault)
- /* Turn caches & protection off, to ensure we don't get any more
- * double exceptions
- */
+ /* Turn caches & protection off, to ensure we don't get any more
+ * double exceptions
+ */
- P4.L = LO(IMEM_CONTROL);
- P4.H = HI(IMEM_CONTROL);
+ P4.L = LO(IMEM_CONTROL);
+ P4.H = HI(IMEM_CONTROL);
- R5 = [P4]; /* Control Register*/
- BITCLR(R5,ENICPLB_P);
- SSYNC; /* SSYNC required before writing to IMEM_CONTROL. */
- .align 8;
- [P4] = R5;
- SSYNC;
+ R5 = [P4]; /* Control Register*/
+ BITCLR(R5,ENICPLB_P);
+ SSYNC; /* SSYNC required before writing to IMEM_CONTROL. */
+ .align 8;
+ [P4] = R5;
+ SSYNC;
- P4.L = LO(DMEM_CONTROL);
- P4.H = HI(DMEM_CONTROL);
- R5 = [P4];
- BITCLR(R5,ENDCPLB_P);
- SSYNC; /* SSYNC required before writing to DMEM_CONTROL. */
- .align 8;
- [P4] = R5;
- SSYNC;
+ P4.L = LO(DMEM_CONTROL);
+ P4.H = HI(DMEM_CONTROL);
+ R5 = [P4];
+ BITCLR(R5,ENDCPLB_P);
+ SSYNC; /* SSYNC required before writing to DMEM_CONTROL. */
+ .align 8;
+ [P4] = R5;
+ SSYNC;
- /* Fix up the stack */
- (R7:6,P5:4) = [sp++];
- ASTAT = [sp++];
- SP = EX_SCRATCH_REG;
+ /* Fix up the stack */
+ (R7:6,P5:4) = [sp++];
+ ASTAT = [sp++];
+ SP = EX_SCRATCH_REG;
- /* We should be out of the exception stack, and back down into
- * kernel or user space stack
- */
- SAVE_ALL_SYS
+ /* We should be out of the exception stack, and back down into
+ * kernel or user space stack
+ */
+ SAVE_ALL_SYS
/* The dumping functions expect the return address in the RETI
* slot. */
r6 = retx;
[sp + PT_PC] = r6;
- r0 = sp; /* stack frame pt_regs pointer argument ==> r0 */
- SP += -12;
- call _double_fault_c;
- SP += 12;
+ r0 = sp; /* stack frame pt_regs pointer argument ==> r0 */
+ SP += -12;
+ call _double_fault_c;
+ SP += 12;
.L_double_fault_panic:
JUMP .L_double_fault_panic
@@ -388,8 +424,8 @@
ENTRY(_exception_to_level5)
SAVE_ALL_SYS
- p4.l = _excpt_saved_stuff;
- p4.h = _excpt_saved_stuff;
+ p4.l = _saved_retx;
+ p4.h = _saved_retx;
r6 = [p4];
[sp + PT_PC] = r6;
@@ -420,6 +456,17 @@
call _trap_c;
SP += 12;
+#ifdef CONFIG_DEBUG_DOUBLEFAULT
+ /* Grab ILAT */
+ p2.l = lo(ILAT);
+ p2.h = hi(ILAT);
+ r0 = [p2];
+ r1 = 0x20; /* Did I just cause anther HW error? */
+ r0 = r0 & r1;
+ CC = R0 == R1;
+ if CC JUMP _double_fault;
+#endif
+
call _ret_from_exception;
RESTORE_ALL_SYS
rti;
@@ -436,7 +483,48 @@
/* Try to deal with syscalls quickly. */
[--sp] = ASTAT;
[--sp] = (R7:6,P5:4);
+
+#if ANOMALY_05000283 || ANOMALY_05000315
+ cc = r7 == r7;
+ p5.h = HI(CHIPID);
+ p5.l = LO(CHIPID);
+ if cc jump 1f;
+ r7.l = W[p5];
+1:
+#endif
+
+#ifdef CONFIG_DEBUG_DOUBLEFAULT
+ /*
+ * Save these registers, as they are only valid in exception context
+ * (where we are now - as soon as we defer to IRQ5, they can change)
+ * DCPLB_STATUS and ICPLB_STATUS are also only valid in EVT3,
+ * but they are not very interesting, so don't save them
+ */
+
+ p4.l = lo(DCPLB_FAULT_ADDR);
+ p4.h = hi(DCPLB_FAULT_ADDR);
+ r7 = [p4];
+ p5.h = _saved_dcplb_fault_addr;
+ p5.l = _saved_dcplb_fault_addr;
+ [p5] = r7;
+
+ r7 = [p4 + (ICPLB_FAULT_ADDR - DCPLB_FAULT_ADDR)];
+ p5.h = _saved_icplb_fault_addr;
+ p5.l = _saved_icplb_fault_addr;
+ [p5] = r7;
+
+ p4.l = _saved_retx;
+ p4.h = _saved_retx;
+ r6 = retx;
+ [p4] = r6;
+
r7 = SEQSTAT; /* reason code is in bit 5:0 */
+ p4.l = _saved_seqstat;
+ p4.h = _saved_seqstat;
+ [p4] = r7;
+#else
+ r7 = SEQSTAT; /* reason code is in bit 5:0 */
+#endif
r6.l = lo(SEQSTAT_EXCAUSE);
r6.h = hi(SEQSTAT_EXCAUSE);
r7 = r7 & r6;
@@ -616,6 +704,9 @@
rts;
ENDPROC(_system_call)
+/* Do not mark as ENTRY() to avoid error in assembler ...
+ * this symbol need not be global anyways, so ...
+ */
_sys_trace:
call _syscall_trace;
@@ -941,6 +1032,15 @@
SAVE_ALL_SYS
trace_buffer_stop(p0,r0);
+#if ANOMALY_05000283 || ANOMALY_05000315
+ cc = r5 == r5;
+ p4.h = HI(CHIPID);
+ p4.l = LO(CHIPID);
+ if cc jump 1f;
+ r5.l = W[p4];
+1:
+#endif
+
/* Turn caches off, to ensure we don't get double exceptions */
P4.L = LO(IMEM_CONTROL);
@@ -992,7 +1092,12 @@
*/
.long _ex_syscall /* 0x00 - User Defined - Linux Syscall */
.long _ex_soft_bp /* 0x01 - User Defined - Software breakpoint */
+#ifdef CONFIG_KGDB
+ .long _ex_trap_c /* 0x02 - User Defined - KGDB initial connection
+ and break signal trap */
+#else
.long _ex_replaceable /* 0x02 - User Defined */
+#endif
.long _ex_trap_c /* 0x03 - User Defined - userspace stack overflow */
.long _ex_trap_c /* 0x04 - User Defined - dump trace buffer */
.long _ex_replaceable /* 0x05 - User Defined */
@@ -1432,15 +1537,7 @@
.rept NR_syscalls-(.-_sys_call_table)/4
.long _sys_ni_syscall
.endr
-
- /*
- * Used to save the real RETX, IMASK and SYSCFG when temporarily
- * storing safe values across the transition from exception to IRQ5.
- */
-_excpt_saved_stuff:
- .long 0;
- .long 0;
- .long 0;
+END(_sys_call_table)
_exception_stack:
.rept 1024
diff --git a/arch/blackfin/mach-common/head.S b/arch/blackfin/mach-common/head.S
index 191b4e9..3069df5 100644
--- a/arch/blackfin/mach-common/head.S
+++ b/arch/blackfin/mach-common/head.S
@@ -68,6 +68,16 @@
M2 = r0;
M3 = r0;
+ /*
+ * Clear ITEST_COMMAND and DTEST_COMMAND registers,
+ * Leaving these as non-zero can confuse the emulator
+ */
+ p0.L = LO(DTEST_COMMAND);
+ p0.H = HI(DTEST_COMMAND);
+ [p0] = R0;
+ [p0 + (ITEST_COMMAND - DTEST_COMMAND)] = R0;
+ CSYNC;
+
trace_buffer_init(p0,r0);
P0 = R1;
R0 = R1;
@@ -90,12 +100,46 @@
[p0] = R0;
SSYNC;
- /* Save RETX, in case of doublefault */
- p0.l = ___retx;
- p0.h = ___retx;
+ /* in case of double faults, save a few things */
+ p0.l = _init_retx;
+ p0.h = _init_retx;
R0 = RETX;
[P0] = R0;
+#ifdef CONFIG_DEBUG_DOUBLEFAULT
+ /* Only save these if we are storing them,
+ * This happens here, since L1 gets clobbered
+ * below
+ */
+ p0.l = _saved_retx;
+ p0.h = _saved_retx;
+ p1.l = _init_saved_retx;
+ p1.h = _init_saved_retx;
+ r0 = [p0];
+ [p1] = r0;
+
+ p0.l = _saved_dcplb_fault_addr;
+ p0.h = _saved_dcplb_fault_addr;
+ p1.l = _init_saved_dcplb_fault_addr;
+ p1.h = _init_saved_dcplb_fault_addr;
+ r0 = [p0];
+ [p1] = r0;
+
+ p0.l = _saved_icplb_fault_addr;
+ p0.h = _saved_icplb_fault_addr;
+ p1.l = _init_saved_icplb_fault_addr;
+ p1.h = _init_saved_icplb_fault_addr;
+ r0 = [p0];
+ [p1] = r0;
+
+ p0.l = _saved_seqstat;
+ p0.h = _saved_seqstat;
+ p1.l = _init_saved_seqstat;
+ p1.h = _init_saved_seqstat;
+ r0 = [p0];
+ [p1] = r0;
+#endif
+
/* Initialize stack pointer */
sp.l = lo(INITIAL_STACK);
sp.h = hi(INITIAL_STACK);
@@ -107,7 +151,7 @@
#endif
/* Put The Code for PLL Programming and SDRAM Programming in L1 ISRAM */
- call _bf53x_relocate_l1_mem;
+ call _bfin_relocate_l1_mem;
#ifdef CONFIG_BFIN_KERNEL_CLOCK
call _start_dma_code;
#endif
diff --git a/arch/blackfin/mach-common/interrupt.S b/arch/blackfin/mach-common/interrupt.S
index b27e59d..4a2ec7a 100644
--- a/arch/blackfin/mach-common/interrupt.S
+++ b/arch/blackfin/mach-common/interrupt.S
@@ -143,7 +143,7 @@
fp = 0;
#endif
-#if ANOMALY_05000283
+#if ANOMALY_05000283 || ANOMALY_05000315
cc = r7 == r7;
p5.h = HI(CHIPID);
p5.l = LO(CHIPID);
@@ -179,7 +179,16 @@
call _trap_c;
SP += 12;
+#ifdef EBIU_ERRMST
+ /* make sure EBIU_ERRMST is clear */
+ p0.l = LO(EBIU_ERRMST);
+ p0.h = HI(EBIU_ERRMST);
+ r0.l = (CORE_ERROR | CORE_MERROR);
+ w[p0] = r0.l;
+#endif
+
call _ret_from_exception;
+
.Lcommon_restore_all_sys:
RESTORE_ALL_SYS
rti;
diff --git a/arch/blackfin/mach-common/ints-priority.c b/arch/blackfin/mach-common/ints-priority.c
index 5fa5367..34e8a72 100644
--- a/arch/blackfin/mach-common/ints-priority.c
+++ b/arch/blackfin/mach-common/ints-priority.c
@@ -243,12 +243,14 @@
#endif
static struct irq_chip bfin_core_irqchip = {
+ .name = "CORE",
.ack = bfin_ack_noop,
.mask = bfin_core_mask_irq,
.unmask = bfin_core_unmask_irq,
};
static struct irq_chip bfin_internal_irqchip = {
+ .name = "INTN",
.ack = bfin_ack_noop,
.mask = bfin_internal_mask_irq,
.unmask = bfin_internal_unmask_irq,
@@ -278,6 +280,7 @@
}
static struct irq_chip bfin_generic_error_irqchip = {
+ .name = "ERROR",
.ack = bfin_ack_noop,
.mask_ack = bfin_generic_error_mask_irq,
.mask = bfin_generic_error_mask_irq,
@@ -361,6 +364,14 @@
}
#endif /* BF537_GENERIC_ERROR_INT_DEMUX */
+static inline void bfin_set_irq_handler(unsigned irq, irq_flow_handler_t handle)
+{
+ struct irq_desc *desc = irq_desc + irq;
+ /* May not call generic set_irq_handler() due to spinlock
+ recursion. */
+ desc->handle_irq = handle;
+}
+
#if !defined(CONFIG_BF54x)
static unsigned short gpio_enabled[gpio_bank(MAX_BLACKFIN_GPIOS)];
@@ -473,9 +484,9 @@
SSYNC();
if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING))
- set_irq_handler(irq, handle_edge_irq);
+ bfin_set_irq_handler(irq, handle_edge_irq);
else
- set_irq_handler(irq, handle_level_irq);
+ bfin_set_irq_handler(irq, handle_level_irq);
return 0;
}
@@ -495,6 +506,7 @@
#endif
static struct irq_chip bfin_gpio_irqchip = {
+ .name = "GPIO",
.ack = bfin_gpio_ack_irq,
.mask = bfin_gpio_mask_irq,
.mask_ack = bfin_gpio_mask_ack_irq,
@@ -804,10 +816,10 @@
if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) {
pint[bank]->edge_set = pintbit;
- set_irq_handler(irq, handle_edge_irq);
+ bfin_set_irq_handler(irq, handle_edge_irq);
} else {
pint[bank]->edge_clear = pintbit;
- set_irq_handler(irq, handle_level_irq);
+ bfin_set_irq_handler(irq, handle_level_irq);
}
SSYNC();
@@ -884,6 +896,7 @@
#endif
static struct irq_chip bfin_gpio_irqchip = {
+ .name = "GPIO",
.ack = bfin_gpio_ack_irq,
.mask = bfin_gpio_mask_irq,
.mask_ack = bfin_gpio_mask_ack_irq,
@@ -1136,8 +1149,4 @@
vec = ivg->irqno;
}
asm_do_IRQ(vec, fp);
-
-#ifdef CONFIG_KGDB
- kgdb_process_breakpoint();
-#endif
}
diff --git a/arch/blackfin/mm/Makefile b/arch/blackfin/mm/Makefile
index 2a7202c..d489f89 100644
--- a/arch/blackfin/mm/Makefile
+++ b/arch/blackfin/mm/Makefile
@@ -2,4 +2,4 @@
# arch/blackfin/mm/Makefile
#
-obj-y := blackfin_sram.o init.o
+obj-y := sram-alloc.o isram-driver.o init.o
diff --git a/arch/blackfin/mm/isram-driver.c b/arch/blackfin/mm/isram-driver.c
new file mode 100644
index 0000000..22913e7
--- /dev/null
+++ b/arch/blackfin/mm/isram-driver.c
@@ -0,0 +1,201 @@
+/*
+ * Description: Instruction SRAM accessor functions for the Blackfin
+ *
+ * Copyright 2008 Analog Devices Inc.
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+
+#include <asm/blackfin.h>
+
+/*
+ * IMPORTANT WARNING ABOUT THESE FUNCTIONS
+ *
+ * The emulator will not function correctly if a write command is left in
+ * ITEST_COMMAND or DTEST_COMMAND AND access to cache memory is needed by
+ * the emulator. To avoid such problems, ensure that both ITEST_COMMAND
+ * and DTEST_COMMAND are zero when exiting these functions.
+ */
+
+
+/*
+ * On the Blackfin, L1 instruction sram (which operates at core speeds) can not
+ * be accessed by a normal core load, so we need to go through a few hoops to
+ * read/write it.
+ * To try to make it easier - we export a memcpy interface, where either src or
+ * dest can be in this special L1 memory area.
+ * The low level read/write functions should not be exposed to the rest of the
+ * kernel, since they operate on 64-bit data, and need specific address alignment
+ */
+
+static DEFINE_SPINLOCK(dtest_lock);
+
+/* Takes a void pointer */
+#define IADDR2DTEST(x) \
+ ({ unsigned long __addr = (unsigned long)(x); \
+ (__addr & 0x47F8) | /* address bits 14 & 10:3 */ \
+ (__addr & 0x0800) << 15 | /* address bit 11 */ \
+ (__addr & 0x3000) << 4 | /* address bits 13:12 */ \
+ (__addr & 0x8000) << 8 | /* address bit 15 */ \
+ (0x1000004); /* isram access */ \
+ })
+
+/* Takes a pointer, and returns the offset (in bits) which things should be shifted */
+#define ADDR2OFFSET(x) ((((unsigned long)(x)) & 0x7) * 8)
+
+/* Takes a pointer, determines if it is the last byte in the isram 64-bit data type */
+#define ADDR2LAST(x) ((((unsigned long)x) & 0x7) == 0x7)
+
+static void isram_write(const void *addr, uint64_t data)
+{
+ uint32_t cmd;
+ unsigned long flags;
+
+ if (addr >= (void *)(L1_CODE_START + L1_CODE_LENGTH))
+ return;
+
+ cmd = IADDR2DTEST(addr) | 1; /* write */
+
+ /*
+ * Writes to DTEST_DATA[0:1] need to be atomic with write to DTEST_COMMAND
+ * While in exception context - atomicity is guaranteed or double fault
+ */
+ spin_lock_irqsave(&dtest_lock, flags);
+
+ bfin_write_DTEST_DATA0(data & 0xFFFFFFFF);
+ bfin_write_DTEST_DATA1(data >> 32);
+
+ /* use the builtin, since interrupts are already turned off */
+ __builtin_bfin_csync();
+ bfin_write_DTEST_COMMAND(cmd);
+ __builtin_bfin_csync();
+
+ bfin_write_DTEST_COMMAND(0);
+ __builtin_bfin_csync();
+
+ spin_unlock_irqrestore(&dtest_lock, flags);
+}
+
+static uint64_t isram_read(const void *addr)
+{
+ uint32_t cmd;
+ unsigned long flags;
+ uint64_t ret;
+
+ if (addr > (void *)(L1_CODE_START + L1_CODE_LENGTH))
+ return 0;
+
+ cmd = IADDR2DTEST(addr) | 0; /* read */
+
+ /*
+ * Reads of DTEST_DATA[0:1] need to be atomic with write to DTEST_COMMAND
+ * While in exception context - atomicity is guaranteed or double fault
+ */
+ spin_lock_irqsave(&dtest_lock, flags);
+ /* use the builtin, since interrupts are already turned off */
+ __builtin_bfin_csync();
+ bfin_write_DTEST_COMMAND(cmd);
+ __builtin_bfin_csync();
+ ret = bfin_read_DTEST_DATA0() | ((uint64_t)bfin_read_DTEST_DATA1() << 32);
+
+ bfin_write_DTEST_COMMAND(0);
+ __builtin_bfin_csync();
+ spin_unlock_irqrestore(&dtest_lock, flags);
+
+ return ret;
+}
+
+static bool isram_check_addr(const void *addr, size_t n)
+{
+ if ((addr >= (void *)L1_CODE_START) &&
+ (addr < (void *)(L1_CODE_START + L1_CODE_LENGTH))) {
+ if ((addr + n) >= (void *)(L1_CODE_START + L1_CODE_LENGTH)) {
+ show_stack(NULL, NULL);
+ printk(KERN_ERR "isram_memcpy: copy involving %p length "
+ "(%zu) too long\n", addr, n);
+ }
+ return true;
+ }
+ return false;
+}
+
+/*
+ * The isram_memcpy() function copies n bytes from memory area src to memory area dest.
+ * The isram_memcpy() function returns a pointer to dest.
+ * Either dest or src can be in L1 instruction sram.
+ */
+void *isram_memcpy(void *dest, const void *src, size_t n)
+{
+ uint64_t data_in = 0, data_out = 0;
+ size_t count;
+ bool dest_in_l1, src_in_l1, need_data, put_data;
+ unsigned char byte, *src_byte, *dest_byte;
+
+ src_byte = (unsigned char *)src;
+ dest_byte = (unsigned char *)dest;
+
+ dest_in_l1 = isram_check_addr(dest, n);
+ src_in_l1 = isram_check_addr(src, n);
+
+ need_data = true;
+ put_data = true;
+ for (count = 0; count < n; count++) {
+ if (src_in_l1) {
+ if (need_data) {
+ data_in = isram_read(src + count);
+ need_data = false;
+ }
+
+ if (ADDR2LAST(src + count))
+ need_data = true;
+
+ byte = (unsigned char)((data_in >> ADDR2OFFSET(src + count)) & 0xff);
+
+ } else {
+ /* src is in L2 or L3 - so just dereference*/
+ byte = src_byte[count];
+ }
+
+ if (dest_in_l1) {
+ if (put_data) {
+ data_out = isram_read(dest + count);
+ put_data = false;
+ }
+
+ data_out &= ~((uint64_t)0xff << ADDR2OFFSET(dest + count));
+ data_out |= ((uint64_t)byte << ADDR2OFFSET(dest + count));
+
+ if (ADDR2LAST(dest + count)) {
+ put_data = true;
+ isram_write(dest + count, data_out);
+ }
+ } else {
+ /* dest in L2 or L3 - so just dereference */
+ dest_byte[count] = byte;
+ }
+ }
+
+ /* make sure we dump the last byte if necessary */
+ if (dest_in_l1 && !put_data)
+ isram_write(dest + count, data_out);
+
+ return dest;
+}
+EXPORT_SYMBOL(isram_memcpy);
+
diff --git a/arch/blackfin/mm/blackfin_sram.c b/arch/blackfin/mm/sram-alloc.c
similarity index 96%
rename from arch/blackfin/mm/blackfin_sram.c
rename to arch/blackfin/mm/sram-alloc.c
index 4f5e887..0f1ca69 100644
--- a/arch/blackfin/mm/blackfin_sram.c
+++ b/arch/blackfin/mm/sram-alloc.c
@@ -1,13 +1,13 @@
/*
- * File: arch/blackfin/mm/blackfin_sram.c
+ * File: arch/blackfin/mm/sram-alloc.c
* Based on:
* Author:
*
* Created:
- * Description: SRAM driver for Blackfin ADSP-BF5xx
+ * Description: SRAM allocator for Blackfin L1 and L2 memory
*
* Modified:
- * Copyright 2004-2007 Analog Devices Inc.
+ * Copyright 2004-2008 Analog Devices Inc.
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
@@ -78,7 +78,7 @@
free_l1_ssram_head.next =
kmem_cache_alloc(sram_piece_cache, GFP_KERNEL);
if (!free_l1_ssram_head.next) {
- printk(KERN_INFO"Fail to initialize Scratchpad data SRAM.\n");
+ printk(KERN_INFO "Failed to initialize Scratchpad data SRAM\n");
return;
}
@@ -102,7 +102,7 @@
free_l1_data_A_sram_head.next =
kmem_cache_alloc(sram_piece_cache, GFP_KERNEL);
if (!free_l1_data_A_sram_head.next) {
- printk(KERN_INFO"Fail to initialize L1 Data A SRAM.\n");
+ printk(KERN_INFO "Failed to initialize L1 Data A SRAM\n");
return;
}
@@ -123,7 +123,7 @@
free_l1_data_B_sram_head.next =
kmem_cache_alloc(sram_piece_cache, GFP_KERNEL);
if (!free_l1_data_B_sram_head.next) {
- printk(KERN_INFO"Fail to initialize L1 Data B SRAM.\n");
+ printk(KERN_INFO "Failed to initialize L1 Data B SRAM\n");
return;
}
@@ -151,7 +151,7 @@
free_l1_inst_sram_head.next =
kmem_cache_alloc(sram_piece_cache, GFP_KERNEL);
if (!free_l1_inst_sram_head.next) {
- printk(KERN_INFO"Fail to initialize L1 Instruction SRAM.\n");
+ printk(KERN_INFO "Failed to initialize L1 Instruction SRAM\n");
return;
}
@@ -179,7 +179,7 @@
free_l2_sram_head.next =
kmem_cache_alloc(sram_piece_cache, GFP_KERNEL);
if (!free_l2_sram_head.next) {
- printk(KERN_INFO"Fail to initialize L2 SRAM.\n");
+ printk(KERN_INFO "Failed to initialize L2 SRAM\n");
return;
}
@@ -351,28 +351,31 @@
int sram_free(const void *addr)
{
- if (0) {}
+
#if L1_CODE_LENGTH != 0
- else if (addr >= (void *)L1_CODE_START
+ if (addr >= (void *)L1_CODE_START
&& addr < (void *)(L1_CODE_START + L1_CODE_LENGTH))
return l1_inst_sram_free(addr);
+ else
#endif
#if L1_DATA_A_LENGTH != 0
- else if (addr >= (void *)L1_DATA_A_START
+ if (addr >= (void *)L1_DATA_A_START
&& addr < (void *)(L1_DATA_A_START + L1_DATA_A_LENGTH))
return l1_data_A_sram_free(addr);
+ else
#endif
#if L1_DATA_B_LENGTH != 0
- else if (addr >= (void *)L1_DATA_B_START
+ if (addr >= (void *)L1_DATA_B_START
&& addr < (void *)(L1_DATA_B_START + L1_DATA_B_LENGTH))
return l1_data_B_sram_free(addr);
+ else
#endif
#if L2_LENGTH != 0
- else if (addr >= (void *)L2_START
+ if (addr >= (void *)L2_START
&& addr < (void *)(L2_START + L2_LENGTH))
return l2_sram_free(addr);
-#endif
else
+#endif
return -1;
}
EXPORT_SYMBOL(sram_free);
diff --git a/arch/cris/arch-v10/boot/tools/build.c b/arch/cris/arch-v10/boot/tools/build.c
index 2f9bbb2..c8adef3 100644
--- a/arch/cris/arch-v10/boot/tools/build.c
+++ b/arch/cris/arch-v10/boot/tools/build.c
@@ -30,7 +30,6 @@
#include <sys/sysmacros.h>
#include <unistd.h> /* contains read/write */
#include <fcntl.h>
-#include <linux/a.out.h>
#include <errno.h>
#define MINIX_HEADER 32
diff --git a/arch/h8300/Kconfig b/arch/h8300/Kconfig
index 396ab05..107cb5b 100644
--- a/arch/h8300/Kconfig
+++ b/arch/h8300/Kconfig
@@ -66,9 +66,6 @@
bool
default y
-config ARCH_SUPPORTS_AOUT
- def_bool y
-
config NO_IOPORT
def_bool y
diff --git a/arch/h8300/include/asm/a.out.h b/arch/h8300/include/asm/a.out.h
deleted file mode 100644
index ded780f..0000000
--- a/arch/h8300/include/asm/a.out.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef __H8300_A_OUT_H__
-#define __H8300_A_OUT_H__
-
-struct exec
-{
- unsigned long a_info; /* Use macros N_MAGIC, etc for access */
- unsigned a_text; /* length of text, in bytes */
- unsigned a_data; /* length of data, in bytes */
- unsigned a_bss; /* length of uninitialized data area for file, in bytes */
- unsigned a_syms; /* length of symbol table data in file, in bytes */
- unsigned a_entry; /* start address */
- unsigned a_trsize; /* length of relocation info for text, in bytes */
- unsigned a_drsize; /* length of relocation info for data, in bytes */
-};
-
-#define N_TRSIZE(a) ((a).a_trsize)
-#define N_DRSIZE(a) ((a).a_drsize)
-#define N_SYMSIZE(a) ((a).a_syms)
-
-#endif /* __H8300_A_OUT_H__ */
diff --git a/arch/h8300/kernel/process.c b/arch/h8300/kernel/process.c
index dfbe7ab..a8ef654 100644
--- a/arch/h8300/kernel/process.c
+++ b/arch/h8300/kernel/process.c
@@ -34,7 +34,6 @@
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/user.h>
-#include <linux/a.out.h>
#include <linux/interrupt.h>
#include <linux/reboot.h>
#include <linux/fs.h>
diff --git a/arch/ia64/include/asm/a.out.h b/arch/ia64/include/asm/a.out.h
deleted file mode 100644
index 193dcfb..0000000
--- a/arch/ia64/include/asm/a.out.h
+++ /dev/null
@@ -1,32 +0,0 @@
-#ifndef _ASM_IA64_A_OUT_H
-#define _ASM_IA64_A_OUT_H
-
-/*
- * No a.out format has been (or should be) defined so this file is
- * just a dummy that allows us to get binfmt_elf compiled. It
- * probably would be better to clean up binfmt_elf.c so it does not
- * necessarily depend on there being a.out support.
- *
- * Modified 1998-2002
- * David Mosberger-Tang <davidm@hpl.hp.com>, Hewlett-Packard Co.
- */
-
-#include <linux/types.h>
-
-struct exec {
- unsigned long a_info;
- unsigned long a_text;
- unsigned long a_data;
- unsigned long a_bss;
- unsigned long a_entry;
-};
-
-#define N_TXTADDR(x) 0
-#define N_DATADDR(x) 0
-#define N_BSSADDR(x) 0
-#define N_DRSIZE(x) 0
-#define N_TRSIZE(x) 0
-#define N_SYMSIZE(x) 0
-#define N_TXTOFF(x) 0
-
-#endif /* _ASM_IA64_A_OUT_H */
diff --git a/arch/ia64/include/asm/siginfo.h b/arch/ia64/include/asm/siginfo.h
index 9294e4b..118d429 100644
--- a/arch/ia64/include/asm/siginfo.h
+++ b/arch/ia64/include/asm/siginfo.h
@@ -113,11 +113,6 @@
#undef NSIGSEGV
#define NSIGSEGV 3
-/*
- * SIGTRAP si_codes
- */
-#define TRAP_BRANCH (__SI_FAULT|3) /* process taken branch trap */
-#define TRAP_HWBKPT (__SI_FAULT|4) /* hardware breakpoint or watchpoint */
#undef NSIGTRAP
#define NSIGTRAP 4
diff --git a/arch/ia64/include/asm/statfs.h b/arch/ia64/include/asm/statfs.h
index 8110979..1e58966 100644
--- a/arch/ia64/include/asm/statfs.h
+++ b/arch/ia64/include/asm/statfs.h
@@ -8,55 +8,13 @@
* David Mosberger-Tang <davidm@hpl.hp.com>, Hewlett-Packard Co
*/
-#ifndef __KERNEL_STRICT_NAMES
-# include <linux/types.h>
-typedef __kernel_fsid_t fsid_t;
-#endif
-
/*
- * This is ugly --- we're already 64-bit, so just duplicate the definitions
+ * We need compat_statfs64 to be packed, because the i386 ABI won't
+ * add padding at the end to bring it to a multiple of 8 bytes, but
+ * the IA64 ABI will.
*/
-struct statfs {
- long f_type;
- long f_bsize;
- long f_blocks;
- long f_bfree;
- long f_bavail;
- long f_files;
- long f_ffree;
- __kernel_fsid_t f_fsid;
- long f_namelen;
- long f_frsize;
- long f_spare[5];
-};
+#define ARCH_PACK_COMPAT_STATFS64 __attribute__((packed,aligned(4)))
-
-struct statfs64 {
- long f_type;
- long f_bsize;
- long f_blocks;
- long f_bfree;
- long f_bavail;
- long f_files;
- long f_ffree;
- __kernel_fsid_t f_fsid;
- long f_namelen;
- long f_frsize;
- long f_spare[5];
-};
-
-struct compat_statfs64 {
- __u32 f_type;
- __u32 f_bsize;
- __u64 f_blocks;
- __u64 f_bfree;
- __u64 f_bavail;
- __u64 f_files;
- __u64 f_ffree;
- __kernel_fsid_t f_fsid;
- __u32 f_namelen;
- __u32 f_frsize;
- __u32 f_spare[5];
-} __attribute__((packed));
+#include <asm-generic/statfs.h>
#endif /* _ASM_IA64_STATFS_H */
diff --git a/arch/ia64/mm/init.c b/arch/ia64/mm/init.c
index 200100e..f482a90 100644
--- a/arch/ia64/mm/init.c
+++ b/arch/ia64/mm/init.c
@@ -21,7 +21,6 @@
#include <linux/bitops.h>
#include <linux/kexec.h>
-#include <asm/a.out.h>
#include <asm/dma.h>
#include <asm/ia32.h>
#include <asm/io.h>
diff --git a/arch/m32r/Kconfig b/arch/m32r/Kconfig
index f57113f..00289c1 100644
--- a/arch/m32r/Kconfig
+++ b/arch/m32r/Kconfig
@@ -36,9 +36,6 @@
config NO_DMA
def_bool y
-config ARCH_SUPPORTS_AOUT
- def_bool y
-
config HZ
int
default 100
diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig
index 8c5e1de..677c93a 100644
--- a/arch/m68k/Kconfig
+++ b/arch/m68k/Kconfig
@@ -5,6 +5,7 @@
config M68K
bool
default y
+ select HAVE_AOUT
select HAVE_IDE
config MMU
@@ -53,9 +54,6 @@
config NO_DMA
def_bool SUN3
-config ARCH_SUPPORTS_AOUT
- def_bool y
-
config HZ
int
default 100
@@ -107,21 +105,9 @@
To compile this driver as modules, choose M here: the
modules will be called pcmcia_core and ds.
-config SUN3
- bool "Sun3 support"
- select M68020
- select MMU_SUN3 if MMU
- help
- This option enables support for the Sun 3 series of workstations
- (3/50, 3/60, 3/1xx, 3/2xx systems). Enabling this option requires
- that all other hardware types must be disabled, as Sun 3 kernels
- are incompatible with all other m68k targets (including Sun 3x!).
-
- If you don't want to compile a kernel exclusively for a Sun 3, say N.
-
config AMIGA
bool "Amiga support"
- depends on !MMU_SUN3
+ select MMU_MOTOROLA if MMU
help
This option enables support for the Amiga series of computers. If
you plan to use this kernel on an Amiga, say Y here and browse the
@@ -129,33 +115,16 @@
config ATARI
bool "Atari support"
- depends on !MMU_SUN3
+ select MMU_MOTOROLA if MMU
help
This option enables support for the 68000-based Atari series of
computers (including the TT, Falcon and Medusa). If you plan to use
this kernel on an Atari, say Y here and browse the material
available in <file:Documentation/m68k>; otherwise say N.
-config HADES
- bool "Hades support"
- depends on ATARI && BROKEN
- help
- This option enables support for the Hades Atari clone. If you plan
- to use this kernel on a Hades, say Y here; otherwise say N.
-
-config PCI
- bool
- depends on HADES
- default y
- help
- Find out whether you have a PCI motherboard. PCI is the name of a
- bus system, i.e. the way the CPU talks to the other stuff inside
- your box. Other bus systems are ISA, EISA, MicroChannel (MCA) or
- VESA. If you have PCI, say Y, otherwise N.
-
config MAC
bool "Macintosh support"
- depends on !MMU_SUN3
+ select MMU_MOTOROLA if MMU
help
This option enables support for the Apple Macintosh series of
computers (yes, there is experimental support now, at least for part
@@ -176,14 +145,14 @@
config APOLLO
bool "Apollo support"
- depends on !MMU_SUN3
+ select MMU_MOTOROLA if MMU
help
Say Y here if you want to run Linux on an MC680x0-based Apollo
Domain workstation such as the DN3500.
config VME
bool "VME (Motorola and BVM) support"
- depends on !MMU_SUN3
+ select MMU_MOTOROLA if MMU
help
Say Y here if you want to build a kernel for a 680x0 based VME
board. Boards currently supported include Motorola boards MVME147,
@@ -220,7 +189,7 @@
config HP300
bool "HP9000/300 and HP9000/400 support"
- depends on !MMU_SUN3
+ select MMU_MOTOROLA if MMU
help
This option enables support for the HP9000/300 and HP9000/400 series
of workstations. Support for these machines is still somewhat
@@ -239,7 +208,7 @@
config SUN3X
bool "Sun3x support"
- depends on !MMU_SUN3
+ select MMU_MOTOROLA if MMU
select M68030
help
This option enables support for the Sun 3x series of workstations.
@@ -252,7 +221,7 @@
config Q40
bool "Q40/Q60 support"
- depends on !MMU_SUN3
+ select MMU_MOTOROLA if MMU
help
The Q40 is a Motorola 68040-based successor to the Sinclair QL
manufactured in Germany. There is an official Q40 home page at
@@ -260,6 +229,19 @@
Q60. Select your CPU below. For 68LC060 don't forget to enable FPU
emulation.
+config SUN3
+ bool "Sun3 support"
+ depends on !MMU_MOTOROLA
+ select MMU_SUN3 if MMU
+ select M68020
+ help
+ This option enables support for the Sun 3 series of workstations
+ (3/50, 3/60, 3/1xx, 3/2xx systems). Enabling this option requires
+ that all other hardware types must be disabled, as Sun 3 kernels
+ are incompatible with all other m68k targets (including Sun 3x!).
+
+ If you don't want to compile a kernel exclusively for a Sun 3, say N.
+
comment "Processor type"
config M68020
@@ -297,10 +279,10 @@
config MMU_MOTOROLA
bool
depends on MMU && !MMU_SUN3
- default y
config MMU_SUN3
bool
+ depends on MMU && !MMU_MOTOROLA
config M68KFPU_EMU
bool "Math emulation support (EXPERIMENTAL)"
diff --git a/arch/m68k/amiga/config.c b/arch/m68k/amiga/config.c
index df679d9..0a3f9e8 100644
--- a/arch/m68k/amiga/config.c
+++ b/arch/m68k/amiga/config.c
@@ -24,6 +24,7 @@
#include <linux/interrupt.h>
#include <linux/zorro.h>
#include <linux/module.h>
+#include <linux/keyboard.h>
#include <asm/bootinfo.h>
#include <asm/setup.h>
@@ -984,3 +985,11 @@
return len;
}
+
+/*
+ * The Amiga keyboard driver needs key_maps, but we cannot export it in
+ * drivers/char/defkeymap.c, as it is autogenerated
+ */
+#ifdef CONFIG_HW_CONSOLE
+EXPORT_SYMBOL_GPL(key_maps);
+#endif
diff --git a/arch/m68k/atari/Makefile b/arch/m68k/atari/Makefile
index 2cd905e..0cac723 100644
--- a/arch/m68k/atari/Makefile
+++ b/arch/m68k/atari/Makefile
@@ -5,7 +5,4 @@
obj-y := config.o time.o debug.o ataints.o stdma.o \
atasound.o stram.o
-ifeq ($(CONFIG_PCI),y)
-obj-$(CONFIG_HADES) += hades-pci.o
-endif
obj-$(CONFIG_ATARI_KBD_CORE) += atakeyb.o
diff --git a/arch/m68k/atari/ataints.c b/arch/m68k/atari/ataints.c
index b45593a..dba4afa 100644
--- a/arch/m68k/atari/ataints.c
+++ b/arch/m68k/atari/ataints.c
@@ -407,10 +407,8 @@
* gets overruns)
*/
- if (!MACH_IS_HADES) {
- vectors[VEC_INT2] = falcon_hblhandler;
- vectors[VEC_INT4] = falcon_hblhandler;
- }
+ vectors[VEC_INT2] = falcon_hblhandler;
+ vectors[VEC_INT4] = falcon_hblhandler;
}
if (ATARIHW_PRESENT(PCM_8BIT) && ATARIHW_PRESENT(MICROWIRE)) {
diff --git a/arch/m68k/atari/atakeyb.c b/arch/m68k/atari/atakeyb.c
index bb959fb..c038b7c 100644
--- a/arch/m68k/atari/atakeyb.c
+++ b/arch/m68k/atari/atakeyb.c
@@ -635,15 +635,3 @@
return 0;
}
EXPORT_SYMBOL_GPL(atari_keyb_init);
-
-int atari_kbd_translate(unsigned char keycode, unsigned char *keycodep, char raw_mode)
-{
-#ifdef CONFIG_MAGIC_SYSRQ
- /* ALT+HELP pressed? */
- if ((keycode == 98) && ((shift_state & 0xff) == 8))
- *keycodep = 0xff;
- else
-#endif
- *keycodep = keycode;
- return 1;
-}
diff --git a/arch/m68k/atari/config.c b/arch/m68k/atari/config.c
index 5945e15..af03185 100644
--- a/arch/m68k/atari/config.c
+++ b/arch/m68k/atari/config.c
@@ -231,7 +231,7 @@
*/
printk("Atari hardware found: ");
- if (MACH_IS_MEDUSA || MACH_IS_HADES) {
+ if (MACH_IS_MEDUSA) {
/* There's no Atari video hardware on the Medusa, but all the
* addresses below generate a DTACK so no bus error occurs! */
} else if (hwreg_present(f030_xreg)) {
@@ -269,10 +269,6 @@
ATARIHW_SET(SCSI_DMA);
printk("TT_SCSI_DMA ");
}
- if (!MACH_IS_HADES && hwreg_present(&st_dma.dma_hi)) {
- ATARIHW_SET(STND_DMA);
- printk("STND_DMA ");
- }
/*
* The ST-DMA address registers aren't readable
* on all Medusas, so the test below may fail
@@ -294,12 +290,11 @@
ATARIHW_SET(YM_2149);
printk("YM2149 ");
}
- if (!MACH_IS_MEDUSA && !MACH_IS_HADES &&
- hwreg_present(&tt_dmasnd.ctrl)) {
+ if (!MACH_IS_MEDUSA && hwreg_present(&tt_dmasnd.ctrl)) {
ATARIHW_SET(PCM_8BIT);
printk("PCM ");
}
- if (!MACH_IS_HADES && hwreg_present(&falcon_codec.unused5)) {
+ if (hwreg_present(&falcon_codec.unused5)) {
ATARIHW_SET(CODEC);
printk("CODEC ");
}
@@ -313,7 +308,7 @@
(tt_scc_dma.dma_ctrl = 0x01, (tt_scc_dma.dma_ctrl & 1) == 1) &&
(tt_scc_dma.dma_ctrl = 0x00, (tt_scc_dma.dma_ctrl & 1) == 0)
#else
- !MACH_IS_MEDUSA && !MACH_IS_HADES
+ !MACH_IS_MEDUSA
#endif
) {
ATARIHW_SET(SCC_DMA);
@@ -327,10 +322,7 @@
ATARIHW_SET(ST_ESCC);
printk("ST_ESCC ");
}
- if (MACH_IS_HADES) {
- ATARIHW_SET(VME);
- printk("VME ");
- } else if (hwreg_present(&tt_scu.sys_mask)) {
+ if (hwreg_present(&tt_scu.sys_mask)) {
ATARIHW_SET(SCU);
/* Assume a VME bus if there's a SCU */
ATARIHW_SET(VME);
@@ -340,7 +332,7 @@
ATARIHW_SET(ANALOG_JOY);
printk("ANALOG_JOY ");
}
- if (!MACH_IS_HADES && hwreg_present(blitter.halftone)) {
+ if (hwreg_present(blitter.halftone)) {
ATARIHW_SET(BLITTER);
printk("BLITTER ");
}
@@ -349,8 +341,7 @@
printk("IDE ");
}
#if 1 /* This maybe wrong */
- if (!MACH_IS_MEDUSA && !MACH_IS_HADES &&
- hwreg_present(&tt_microwire.data) &&
+ if (!MACH_IS_MEDUSA && hwreg_present(&tt_microwire.data) &&
hwreg_present(&tt_microwire.mask) &&
(tt_microwire.mask = 0x7ff,
udelay(1),
@@ -369,19 +360,18 @@
mach_hwclk = atari_tt_hwclk;
mach_set_clock_mmss = atari_tt_set_clock_mmss;
}
- if (!MACH_IS_HADES && hwreg_present(&mste_rtc.sec_ones)) {
+ if (hwreg_present(&mste_rtc.sec_ones)) {
ATARIHW_SET(MSTE_CLK);
printk("MSTE_CLK ");
mach_hwclk = atari_mste_hwclk;
mach_set_clock_mmss = atari_mste_set_clock_mmss;
}
- if (!MACH_IS_MEDUSA && !MACH_IS_HADES &&
- hwreg_present(&dma_wd.fdc_speed) &&
+ if (!MACH_IS_MEDUSA && hwreg_present(&dma_wd.fdc_speed) &&
hwreg_write(&dma_wd.fdc_speed, 0)) {
ATARIHW_SET(FDCSPEED);
printk("FDC_SPEED ");
}
- if (!MACH_IS_HADES && !ATARIHW_PRESENT(ST_SCSI)) {
+ if (!ATARIHW_PRESENT(ST_SCSI)) {
ATARIHW_SET(ACSI);
printk("ACSI ");
}
@@ -449,7 +439,7 @@
* 0xFFxxxxxx -> 0x00xxxxxx, so that the first 16MB is accessible
* in the last 16MB of the address space.
*/
- tos_version = (MACH_IS_MEDUSA || MACH_IS_HADES) ?
+ tos_version = (MACH_IS_MEDUSA) ?
0xfff : *(unsigned short *)0xff000002;
atari_rtc_year_offset = (tos_version < 0x306) ? 70 : 68;
}
@@ -511,8 +501,7 @@
* On the Medusa, phys. 0x4 may contain garbage because it's no
* ROM. See above for explanation why we cannot use PTOV(4).
*/
- reset_addr = MACH_IS_HADES ? 0x7fe00030 :
- MACH_IS_MEDUSA || MACH_IS_AB40 ? 0xe00030 :
+ reset_addr = MACH_IS_MEDUSA || MACH_IS_AB40 ? 0xe00030 :
*(unsigned long *) 0xff000004;
/* reset ACIA for switch off OverScan, if it's active */
@@ -606,8 +595,6 @@
if (MACH_IS_MEDUSA)
/* Medusa has TT _MCH cookie */
strcat(model, "Medusa");
- else if (MACH_IS_HADES)
- strcat(model, "Hades");
else
strcat(model, "TT");
break;
diff --git a/arch/m68k/atari/hades-pci.c b/arch/m68k/atari/hades-pci.c
deleted file mode 100644
index 2bbabc0..0000000
--- a/arch/m68k/atari/hades-pci.c
+++ /dev/null
@@ -1,440 +0,0 @@
-/*
- * hades-pci.c - Hardware specific PCI BIOS functions the Hades Atari clone.
- *
- * Written by Wout Klaren.
- */
-
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <asm/io.h>
-
-#if 0
-# define DBG_DEVS(args) printk args
-#else
-# define DBG_DEVS(args)
-#endif
-
-#if defined(CONFIG_PCI) && defined(CONFIG_HADES)
-
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/pci.h>
-
-#include <asm/atarihw.h>
-#include <asm/atariints.h>
-#include <asm/byteorder.h>
-#include <asm/pci.h>
-
-#define HADES_MEM_BASE 0x80000000
-#define HADES_MEM_SIZE 0x20000000
-#define HADES_CONFIG_BASE 0xA0000000
-#define HADES_CONFIG_SIZE 0x10000000
-#define HADES_IO_BASE 0xB0000000
-#define HADES_IO_SIZE 0x10000000
-#define HADES_VIRT_IO_SIZE 0x00010000 /* Only 64k is remapped and actually used. */
-
-#define N_SLOTS 4 /* Number of PCI slots. */
-
-static const char pci_mem_name[] = "PCI memory space";
-static const char pci_io_name[] = "PCI I/O space";
-static const char pci_config_name[] = "PCI config space";
-
-static struct resource config_space = {
- .name = pci_config_name,
- .start = HADES_CONFIG_BASE,
- .end = HADES_CONFIG_BASE + HADES_CONFIG_SIZE - 1
-};
-static struct resource io_space = {
- .name = pci_io_name,
- .start = HADES_IO_BASE,
- .end = HADES_IO_BASE + HADES_IO_SIZE - 1
-};
-
-static const unsigned long pci_conf_base_phys[] = {
- 0xA0080000, 0xA0040000, 0xA0020000, 0xA0010000
-};
-static unsigned long pci_conf_base_virt[N_SLOTS];
-static unsigned long pci_io_base_virt;
-
-/*
- * static void *mk_conf_addr(unsigned char bus, unsigned char device_fn,
- * unsigned char where)
- *
- * Calculate the address of the PCI configuration area of the given
- * device.
- *
- * BUG: boards with multiple functions are probably not correctly
- * supported.
- */
-
-static void *mk_conf_addr(struct pci_dev *dev, int where)
-{
- int device = dev->devfn >> 3, function = dev->devfn & 7;
- void *result;
-
- DBG_DEVS(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, pci_addr=0x%p)\n",
- dev->bus->number, dev->devfn, where, pci_addr));
-
- if (device > 3)
- {
- DBG_DEVS(("mk_conf_addr: device (%d) > 3, returning NULL\n", device));
- return NULL;
- }
-
- if (dev->bus->number != 0)
- {
- DBG_DEVS(("mk_conf_addr: bus (%d) > 0, returning NULL\n", device));
- return NULL;
- }
-
- result = (void *) (pci_conf_base_virt[device] | (function << 8) | (where));
- DBG_DEVS(("mk_conf_addr: returning pci_addr 0x%lx\n", (unsigned long) result));
- return result;
-}
-
-static int hades_read_config_byte(struct pci_dev *dev, int where, u8 *value)
-{
- volatile unsigned char *pci_addr;
-
- *value = 0xff;
-
- if ((pci_addr = (unsigned char *) mk_conf_addr(dev, where)) == NULL)
- return PCIBIOS_DEVICE_NOT_FOUND;
-
- *value = *pci_addr;
-
- return PCIBIOS_SUCCESSFUL;
-}
-
-static int hades_read_config_word(struct pci_dev *dev, int where, u16 *value)
-{
- volatile unsigned short *pci_addr;
-
- *value = 0xffff;
-
- if (where & 0x1)
- return PCIBIOS_BAD_REGISTER_NUMBER;
-
- if ((pci_addr = (unsigned short *) mk_conf_addr(dev, where)) == NULL)
- return PCIBIOS_DEVICE_NOT_FOUND;
-
- *value = le16_to_cpu(*pci_addr);
-
- return PCIBIOS_SUCCESSFUL;
-}
-
-static int hades_read_config_dword(struct pci_dev *dev, int where, u32 *value)
-{
- volatile unsigned int *pci_addr;
- unsigned char header_type;
- int result;
-
- *value = 0xffffffff;
-
- if (where & 0x3)
- return PCIBIOS_BAD_REGISTER_NUMBER;
-
- if ((pci_addr = (unsigned int *) mk_conf_addr(dev, where)) == NULL)
- return PCIBIOS_DEVICE_NOT_FOUND;
-
- *value = le32_to_cpu(*pci_addr);
-
- /*
- * Check if the value is an address on the bus. If true, add the
- * base address of the PCI memory or PCI I/O area on the Hades.
- */
-
- if ((result = hades_read_config_byte(dev, PCI_HEADER_TYPE,
- &header_type)) != PCIBIOS_SUCCESSFUL)
- return result;
-
- if (((where >= PCI_BASE_ADDRESS_0) && (where <= PCI_BASE_ADDRESS_1)) ||
- ((header_type != PCI_HEADER_TYPE_BRIDGE) && ((where >= PCI_BASE_ADDRESS_2) &&
- (where <= PCI_BASE_ADDRESS_5))))
- {
- if ((*value & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO)
- {
- /*
- * Base address register that contains an I/O address. If the
- * address is valid on the Hades (0 <= *value < HADES_VIRT_IO_SIZE),
- * add 'pci_io_base_virt' to the value.
- */
-
- if (*value < HADES_VIRT_IO_SIZE)
- *value += pci_io_base_virt;
- }
- else
- {
- /*
- * Base address register that contains an memory address. If the
- * address is valid on the Hades (0 <= *value < HADES_MEM_SIZE),
- * add HADES_MEM_BASE to the value.
- */
-
- if (*value == 0)
- {
- /*
- * Base address is 0. Test if this base
- * address register is used.
- */
-
- *pci_addr = 0xffffffff;
- if (*pci_addr != 0)
- {
- *pci_addr = *value;
- if (*value < HADES_MEM_SIZE)
- *value += HADES_MEM_BASE;
- }
- }
- else
- {
- if (*value < HADES_MEM_SIZE)
- *value += HADES_MEM_BASE;
- }
- }
- }
-
- return PCIBIOS_SUCCESSFUL;
-}
-
-static int hades_write_config_byte(struct pci_dev *dev, int where, u8 value)
-{
- volatile unsigned char *pci_addr;
-
- if ((pci_addr = (unsigned char *) mk_conf_addr(dev, where)) == NULL)
- return PCIBIOS_DEVICE_NOT_FOUND;
-
- *pci_addr = value;
-
- return PCIBIOS_SUCCESSFUL;
-}
-
-static int hades_write_config_word(struct pci_dev *dev, int where, u16 value)
-{
- volatile unsigned short *pci_addr;
-
- if ((pci_addr = (unsigned short *) mk_conf_addr(dev, where)) == NULL)
- return PCIBIOS_DEVICE_NOT_FOUND;
-
- *pci_addr = cpu_to_le16(value);
-
- return PCIBIOS_SUCCESSFUL;
-}
-
-static int hades_write_config_dword(struct pci_dev *dev, int where, u32 value)
-{
- volatile unsigned int *pci_addr;
- unsigned char header_type;
- int result;
-
- if ((pci_addr = (unsigned int *) mk_conf_addr(dev, where)) == NULL)
- return PCIBIOS_DEVICE_NOT_FOUND;
-
- /*
- * Check if the value is an address on the bus. If true, subtract the
- * base address of the PCI memory or PCI I/O area on the Hades.
- */
-
- if ((result = hades_read_config_byte(dev, PCI_HEADER_TYPE,
- &header_type)) != PCIBIOS_SUCCESSFUL)
- return result;
-
- if (((where >= PCI_BASE_ADDRESS_0) && (where <= PCI_BASE_ADDRESS_1)) ||
- ((header_type != PCI_HEADER_TYPE_BRIDGE) && ((where >= PCI_BASE_ADDRESS_2) &&
- (where <= PCI_BASE_ADDRESS_5))))
- {
- if ((value & PCI_BASE_ADDRESS_SPACE) ==
- PCI_BASE_ADDRESS_SPACE_IO)
- {
- /*
- * I/O address. Check if the address is valid address on
- * the Hades (pci_io_base_virt <= value < pci_io_base_virt +
- * HADES_VIRT_IO_SIZE) or if the value is 0xffffffff. If not
- * true do not write the base address register. If it is a
- * valid base address subtract 'pci_io_base_virt' from the value.
- */
-
- if ((value >= pci_io_base_virt) && (value < (pci_io_base_virt +
- HADES_VIRT_IO_SIZE)))
- value -= pci_io_base_virt;
- else
- {
- if (value != 0xffffffff)
- return PCIBIOS_SET_FAILED;
- }
- }
- else
- {
- /*
- * Memory address. Check if the address is valid address on
- * the Hades (HADES_MEM_BASE <= value < HADES_MEM_BASE + HADES_MEM_SIZE) or
- * if the value is 0xffffffff. If not true do not write
- * the base address register. If it is a valid base address
- * subtract HADES_MEM_BASE from the value.
- */
-
- if ((value >= HADES_MEM_BASE) && (value < (HADES_MEM_BASE + HADES_MEM_SIZE)))
- value -= HADES_MEM_BASE;
- else
- {
- if (value != 0xffffffff)
- return PCIBIOS_SET_FAILED;
- }
- }
- }
-
- *pci_addr = cpu_to_le32(value);
-
- return PCIBIOS_SUCCESSFUL;
-}
-
-/*
- * static inline void hades_fixup(void)
- *
- * Assign IRQ numbers as used by Linux to the interrupt pins
- * of the PCI cards.
- */
-
-static void __init hades_fixup(int pci_modify)
-{
- char irq_tab[4] = {
- [0] = IRQ_TT_MFP_IO0, /* Slot 0. */
- [1] = IRQ_TT_MFP_IO1, /* Slot 1. */
- [2] = IRQ_TT_MFP_SCC, /* Slot 2. */
- [3] = IRQ_TT_MFP_SCSIDMA /* Slot 3. */
- };
- struct pci_dev *dev = NULL;
- unsigned char slot;
-
- /*
- * Go through all devices, fixing up irqs as we see fit:
- */
-
- while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL)
- {
- if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE)
- {
- slot = PCI_SLOT(dev->devfn); /* Determine slot number. */
- dev->irq = irq_tab[slot];
- if (pci_modify)
- pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
- }
- }
-}
-
-/*
- * static void hades_conf_device(struct pci_dev *dev)
- *
- * Machine dependent Configure the given device.
- *
- * Parameters:
- *
- * dev - the pci device.
- */
-
-static void __init hades_conf_device(struct pci_dev *dev)
-{
- pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0);
-}
-
-static struct pci_ops hades_pci_ops = {
- .read_byte = hades_read_config_byte,
- .read_word = hades_read_config_word,
- .read_dword = hades_read_config_dword,
- .write_byte = hades_write_config_byte,
- .write_word = hades_write_config_word,
- .write_dword = hades_write_config_dword
-};
-
-/*
- * struct pci_bus_info *init_hades_pci(void)
- *
- * Machine specific initialisation:
- *
- * - Allocate and initialise a 'pci_bus_info' structure
- * - Initialise hardware
- *
- * Result: pointer to 'pci_bus_info' structure.
- */
-
-struct pci_bus_info * __init init_hades_pci(void)
-{
- struct pci_bus_info *bus;
- int i;
-
- /*
- * Remap I/O and configuration space.
- */
-
- pci_io_base_virt = (unsigned long) ioremap(HADES_IO_BASE, HADES_VIRT_IO_SIZE);
-
- for (i = 0; i < N_SLOTS; i++)
- pci_conf_base_virt[i] = (unsigned long) ioremap(pci_conf_base_phys[i], 0x10000);
-
- /*
- * Allocate memory for bus info structure.
- */
-
- bus = kzalloc(sizeof(struct pci_bus_info), GFP_KERNEL);
- if (unlikely(!bus))
- goto iounmap_base_virt;
-
- /*
- * Claim resources. The m68k has no separate I/O space, both
- * PCI memory space and PCI I/O space are in memory space. Therefore
- * the I/O resources are requested in memory space as well.
- */
-
- if (unlikely(request_resource(&iomem_resource, &config_space) != 0))
- goto free_bus;
-
- if (unlikely(request_resource(&iomem_resource, &io_space) != 0))
- goto release_config_space;
-
- bus->mem_space.start = HADES_MEM_BASE;
- bus->mem_space.end = HADES_MEM_BASE + HADES_MEM_SIZE - 1;
- bus->mem_space.name = pci_mem_name;
-#if 1
- if (unlikely(request_resource(&iomem_resource, &bus->mem_space) != 0))
- goto release_io_space;
-#endif
- bus->io_space.start = pci_io_base_virt;
- bus->io_space.end = pci_io_base_virt + HADES_VIRT_IO_SIZE - 1;
- bus->io_space.name = pci_io_name;
-#if 1
- if (unlikely(request_resource(&ioport_resource, &bus->io_space) != 0))
- goto release_bus_mem_space;
-#endif
- /*
- * Set hardware dependent functions.
- */
-
- bus->m68k_pci_ops = &hades_pci_ops;
- bus->fixup = hades_fixup;
- bus->conf_device = hades_conf_device;
-
- /*
- * Select high to low edge for PCI interrupts.
- */
-
- tt_mfp.active_edge &= ~0x27;
-
- return bus;
-
-release_bus_mem_space:
- release_resource(&bus->mem_space);
-release_io_space:
- release_resource(&io_space);
-release_config_space:
- release_resource(&config_space);
-free_bus:
- kfree(bus);
-iounmap_base_virt:
- iounmap((void *)pci_io_base_virt);
-
- for (i = 0; i < N_SLOTS; i++)
- iounmap((void *)pci_conf_base_virt[i]);
-
- return NULL;
-}
-#endif
diff --git a/arch/m68k/atari/time.c b/arch/m68k/atari/time.c
index e0d3c8b..1edde27 100644
--- a/arch/m68k/atari/time.c
+++ b/arch/m68k/atari/time.c
@@ -20,6 +20,9 @@
#include <asm/atariints.h>
+DEFINE_SPINLOCK(rtc_lock);
+EXPORT_SYMBOL_GPL(rtc_lock);
+
void __init
atari_sched_init(irq_handler_t timer_routine)
{
@@ -191,13 +194,14 @@
}
if (!(ctrl & RTC_DM_BINARY)) {
- BIN_TO_BCD(sec);
- BIN_TO_BCD(min);
- BIN_TO_BCD(hour);
- BIN_TO_BCD(day);
- BIN_TO_BCD(mon);
- BIN_TO_BCD(year);
- if (wday >= 0) BIN_TO_BCD(wday);
+ sec = bin2bcd(sec);
+ min = bin2bcd(min);
+ hour = bin2bcd(hour);
+ day = bin2bcd(day);
+ mon = bin2bcd(mon);
+ year = bin2bcd(year);
+ if (wday >= 0)
+ wday = bin2bcd(wday);
}
}
@@ -252,13 +256,13 @@
}
if (!(ctrl & RTC_DM_BINARY)) {
- BCD_TO_BIN(sec);
- BCD_TO_BIN(min);
- BCD_TO_BIN(hour);
- BCD_TO_BIN(day);
- BCD_TO_BIN(mon);
- BCD_TO_BIN(year);
- BCD_TO_BIN(wday);
+ sec = bcd2bin(sec);
+ min = bcd2bin(min);
+ hour = bcd2bin(hour);
+ day = bcd2bin(day);
+ mon = bcd2bin(mon);
+ year = bcd2bin(year);
+ wday = bcd2bin(wday);
}
if (!(ctrl & RTC_24H)) {
@@ -318,7 +322,7 @@
rtc_minutes = RTC_READ (RTC_MINUTES);
if (!(save_control & RTC_DM_BINARY))
- BCD_TO_BIN (rtc_minutes);
+ rtc_minutes = bcd2bin(rtc_minutes);
/* Since we're only adjusting minutes and seconds, don't interfere
with hour overflow. This avoids messing with unknown time zones
@@ -329,8 +333,8 @@
{
if (!(save_control & RTC_DM_BINARY))
{
- BIN_TO_BCD (real_seconds);
- BIN_TO_BCD (real_minutes);
+ real_seconds = bin2bcd(real_seconds);
+ real_minutes = bin2bcd(real_minutes);
}
RTC_WRITE (RTC_SECONDS, real_seconds);
RTC_WRITE (RTC_MINUTES, real_minutes);
diff --git a/arch/m68k/bvme6000/config.c b/arch/m68k/bvme6000/config.c
index 9433a88..65c9204 100644
--- a/arch/m68k/bvme6000/config.c
+++ b/arch/m68k/bvme6000/config.c
@@ -25,6 +25,7 @@
#include <linux/genhd.h>
#include <linux/rtc.h>
#include <linux/interrupt.h>
+#include <linux/bcd.h>
#include <asm/bootinfo.h>
#include <asm/system.h>
@@ -46,9 +47,6 @@
extern void bvme6000_waitbut(void);
void bvme6000_set_vectors (void);
-static unsigned char bcd2bin (unsigned char b);
-static unsigned char bin2bcd (unsigned char b);
-
/* Save tick handler routine pointer, will point to do_timer() in
* kernel/sched.c, called via bvme6000_process_int() */
@@ -264,17 +262,6 @@
return v;
}
-static unsigned char bcd2bin (unsigned char b)
-{
- return ((b>>4)*10 + (b&15));
-}
-
-static unsigned char bin2bcd (unsigned char b)
-{
- return (((b/10)*16) + (b%10));
-}
-
-
/*
* Looks like op is non-zero for setting the clock, and zero for
* reading the clock.
diff --git a/arch/m68k/bvme6000/rtc.c b/arch/m68k/bvme6000/rtc.c
index e8ac3f7..808c901 100644
--- a/arch/m68k/bvme6000/rtc.c
+++ b/arch/m68k/bvme6000/rtc.c
@@ -57,16 +57,16 @@
rtc->msr = 0x40;
memset(&wtime, 0, sizeof(struct rtc_time));
do {
- wtime.tm_sec = BCD2BIN(rtc->bcd_sec);
- wtime.tm_min = BCD2BIN(rtc->bcd_min);
- wtime.tm_hour = BCD2BIN(rtc->bcd_hr);
- wtime.tm_mday = BCD2BIN(rtc->bcd_dom);
- wtime.tm_mon = BCD2BIN(rtc->bcd_mth)-1;
- wtime.tm_year = BCD2BIN(rtc->bcd_year);
+ wtime.tm_sec = bcd2bin(rtc->bcd_sec);
+ wtime.tm_min = bcd2bin(rtc->bcd_min);
+ wtime.tm_hour = bcd2bin(rtc->bcd_hr);
+ wtime.tm_mday = bcd2bin(rtc->bcd_dom);
+ wtime.tm_mon = bcd2bin(rtc->bcd_mth)-1;
+ wtime.tm_year = bcd2bin(rtc->bcd_year);
if (wtime.tm_year < 70)
wtime.tm_year += 100;
- wtime.tm_wday = BCD2BIN(rtc->bcd_dow)-1;
- } while (wtime.tm_sec != BCD2BIN(rtc->bcd_sec));
+ wtime.tm_wday = bcd2bin(rtc->bcd_dow)-1;
+ } while (wtime.tm_sec != bcd2bin(rtc->bcd_sec));
rtc->msr = msr;
local_irq_restore(flags);
return copy_to_user(argp, &wtime, sizeof wtime) ?
@@ -114,14 +114,14 @@
rtc->t0cr_rtmr = yrs%4;
rtc->bcd_tenms = 0;
- rtc->bcd_sec = BIN2BCD(sec);
- rtc->bcd_min = BIN2BCD(min);
- rtc->bcd_hr = BIN2BCD(hrs);
- rtc->bcd_dom = BIN2BCD(day);
- rtc->bcd_mth = BIN2BCD(mon);
- rtc->bcd_year = BIN2BCD(yrs%100);
+ rtc->bcd_sec = bin2bcd(sec);
+ rtc->bcd_min = bin2bcd(min);
+ rtc->bcd_hr = bin2bcd(hrs);
+ rtc->bcd_dom = bin2bcd(day);
+ rtc->bcd_mth = bin2bcd(mon);
+ rtc->bcd_year = bin2bcd(yrs%100);
if (rtc_tm.tm_wday >= 0)
- rtc->bcd_dow = BIN2BCD(rtc_tm.tm_wday+1);
+ rtc->bcd_dow = bin2bcd(rtc_tm.tm_wday+1);
rtc->t0cr_rtmr = yrs%4 | 0x08;
rtc->msr = msr;
diff --git a/arch/m68k/kernel/Makefile b/arch/m68k/kernel/Makefile
index 3a7f622..55d5d6b 100644
--- a/arch/m68k/kernel/Makefile
+++ b/arch/m68k/kernel/Makefile
@@ -14,5 +14,4 @@
devres-y = ../../../kernel/irq/devres.o
-obj-$(CONFIG_PCI) += bios32.o
obj-y$(CONFIG_MMU_SUN3) += dma.o # no, it's not a typo
diff --git a/arch/m68k/kernel/bios32.c b/arch/m68k/kernel/bios32.c
deleted file mode 100644
index af170c2..0000000
--- a/arch/m68k/kernel/bios32.c
+++ /dev/null
@@ -1,514 +0,0 @@
-/*
- * bios32.c - PCI BIOS functions for m68k systems.
- *
- * Written by Wout Klaren.
- *
- * Based on the DEC Alpha bios32.c by Dave Rusling and David Mosberger.
- */
-
-#include <linux/init.h>
-#include <linux/kernel.h>
-
-#if 0
-# define DBG_DEVS(args) printk args
-#else
-# define DBG_DEVS(args)
-#endif
-
-#ifdef CONFIG_PCI
-
-/*
- * PCI support for Linux/m68k. Currently only the Hades is supported.
- *
- * The support for PCI bridges in the DEC Alpha version has
- * been removed in this version.
- */
-
-#include <linux/pci.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-
-#include <asm/io.h>
-#include <asm/pci.h>
-#include <asm/uaccess.h>
-
-#define KB 1024
-#define MB (1024*KB)
-#define GB (1024*MB)
-
-#define MAJOR_REV 0
-#define MINOR_REV 5
-
-/*
- * Align VAL to ALIGN, which must be a power of two.
- */
-
-#define ALIGN(val,align) (((val) + ((align) - 1)) & ~((align) - 1))
-
-/*
- * Offsets relative to the I/O and memory base addresses from where resources
- * are allocated.
- */
-
-#define IO_ALLOC_OFFSET 0x00004000
-#define MEM_ALLOC_OFFSET 0x04000000
-
-/*
- * Declarations of hardware specific initialisation functions.
- */
-
-extern struct pci_bus_info *init_hades_pci(void);
-
-/*
- * Bus info structure of the PCI bus. A pointer to this structure is
- * put in the sysdata member of the pci_bus structure.
- */
-
-static struct pci_bus_info *bus_info;
-
-static int pci_modify = 1; /* If set, layout the PCI bus ourself. */
-static int skip_vga; /* If set do not modify base addresses
- of vga cards.*/
-static int disable_pci_burst; /* If set do not allow PCI bursts. */
-
-static unsigned int io_base;
-static unsigned int mem_base;
-
-/*
- * static void disable_dev(struct pci_dev *dev)
- *
- * Disable PCI device DEV so that it does not respond to I/O or memory
- * accesses.
- *
- * Parameters:
- *
- * dev - device to disable.
- */
-
-static void __init disable_dev(struct pci_dev *dev)
-{
- unsigned short cmd;
-
- if (((dev->class >> 8 == PCI_CLASS_NOT_DEFINED_VGA) ||
- (dev->class >> 8 == PCI_CLASS_DISPLAY_VGA) ||
- (dev->class >> 8 == PCI_CLASS_DISPLAY_XGA)) && skip_vga)
- return;
-
- pci_read_config_word(dev, PCI_COMMAND, &cmd);
-
- cmd &= (~PCI_COMMAND_IO & ~PCI_COMMAND_MEMORY & ~PCI_COMMAND_MASTER);
- pci_write_config_word(dev, PCI_COMMAND, cmd);
-}
-
-/*
- * static void layout_dev(struct pci_dev *dev)
- *
- * Layout memory and I/O for a device.
- *
- * Parameters:
- *
- * device - device to layout memory and I/O for.
- */
-
-static void __init layout_dev(struct pci_dev *dev)
-{
- unsigned short cmd;
- unsigned int base, mask, size, reg;
- unsigned int alignto;
- int i;
-
- /*
- * Skip video cards if requested.
- */
-
- if (((dev->class >> 8 == PCI_CLASS_NOT_DEFINED_VGA) ||
- (dev->class >> 8 == PCI_CLASS_DISPLAY_VGA) ||
- (dev->class >> 8 == PCI_CLASS_DISPLAY_XGA)) && skip_vga)
- return;
-
- pci_read_config_word(dev, PCI_COMMAND, &cmd);
-
- for (reg = PCI_BASE_ADDRESS_0, i = 0; reg <= PCI_BASE_ADDRESS_5; reg += 4, i++)
- {
- /*
- * Figure out how much space and of what type this
- * device wants.
- */
-
- pci_write_config_dword(dev, reg, 0xffffffff);
- pci_read_config_dword(dev, reg, &base);
-
- if (!base)
- {
- /* this base-address register is unused */
- dev->resource[i].start = 0;
- dev->resource[i].end = 0;
- dev->resource[i].flags = 0;
- continue;
- }
-
- /*
- * We've read the base address register back after
- * writing all ones and so now we must decode it.
- */
-
- if (base & PCI_BASE_ADDRESS_SPACE_IO)
- {
- /*
- * I/O space base address register.
- */
-
- cmd |= PCI_COMMAND_IO;
-
- base &= PCI_BASE_ADDRESS_IO_MASK;
- mask = (~base << 1) | 0x1;
- size = (mask & base) & 0xffffffff;
-
- /*
- * Align to multiple of size of minimum base.
- */
-
- alignto = max_t(unsigned int, 0x040, size);
- base = ALIGN(io_base, alignto);
- io_base = base + size;
- pci_write_config_dword(dev, reg, base | PCI_BASE_ADDRESS_SPACE_IO);
-
- dev->resource[i].start = base;
- dev->resource[i].end = dev->resource[i].start + size - 1;
- dev->resource[i].flags = IORESOURCE_IO | PCI_BASE_ADDRESS_SPACE_IO;
-
- DBG_DEVS(("layout_dev: IO address: %lX\n", base));
- }
- else
- {
- unsigned int type;
-
- /*
- * Memory space base address register.
- */
-
- cmd |= PCI_COMMAND_MEMORY;
- type = base & PCI_BASE_ADDRESS_MEM_TYPE_MASK;
- base &= PCI_BASE_ADDRESS_MEM_MASK;
- mask = (~base << 1) | 0x1;
- size = (mask & base) & 0xffffffff;
- switch (type)
- {
- case PCI_BASE_ADDRESS_MEM_TYPE_32:
- case PCI_BASE_ADDRESS_MEM_TYPE_64:
- break;
-
- case PCI_BASE_ADDRESS_MEM_TYPE_1M:
- printk("bios32 WARNING: slot %d, function %d "
- "requests memory below 1MB---don't "
- "know how to do that.\n",
- PCI_SLOT(dev->devfn),
- PCI_FUNC(dev->devfn));
- continue;
- }
-
- /*
- * Align to multiple of size of minimum base.
- */
-
- alignto = max_t(unsigned int, 0x1000, size);
- base = ALIGN(mem_base, alignto);
- mem_base = base + size;
- pci_write_config_dword(dev, reg, base);
-
- dev->resource[i].start = base;
- dev->resource[i].end = dev->resource[i].start + size - 1;
- dev->resource[i].flags = IORESOURCE_MEM;
-
- if (type == PCI_BASE_ADDRESS_MEM_TYPE_64)
- {
- /*
- * 64-bit address, set the highest 32 bits
- * to zero.
- */
-
- reg += 4;
- pci_write_config_dword(dev, reg, 0);
-
- i++;
- dev->resource[i].start = 0;
- dev->resource[i].end = 0;
- dev->resource[i].flags = 0;
- }
- }
- }
-
- /*
- * Enable device:
- */
-
- if (dev->class >> 8 == PCI_CLASS_NOT_DEFINED ||
- dev->class >> 8 == PCI_CLASS_NOT_DEFINED_VGA ||
- dev->class >> 8 == PCI_CLASS_DISPLAY_VGA ||
- dev->class >> 8 == PCI_CLASS_DISPLAY_XGA)
- {
- /*
- * All of these (may) have I/O scattered all around
- * and may not use i/o-base address registers at all.
- * So we just have to always enable I/O to these
- * devices.
- */
- cmd |= PCI_COMMAND_IO;
- }
-
- pci_write_config_word(dev, PCI_COMMAND, cmd | PCI_COMMAND_MASTER);
-
- pci_write_config_byte(dev, PCI_LATENCY_TIMER, (disable_pci_burst) ? 0 : 32);
-
- if (bus_info != NULL)
- bus_info->conf_device(dev); /* Machine dependent configuration. */
-
- DBG_DEVS(("layout_dev: bus %d slot 0x%x VID 0x%x DID 0x%x class 0x%x\n",
- dev->bus->number, PCI_SLOT(dev->devfn), dev->vendor, dev->device, dev->class));
-}
-
-/*
- * static void layout_bus(struct pci_bus *bus)
- *
- * Layout memory and I/O for all devices on the given bus.
- *
- * Parameters:
- *
- * bus - bus.
- */
-
-static void __init layout_bus(struct pci_bus *bus)
-{
- unsigned int bio, bmem;
- struct pci_dev *dev;
-
- DBG_DEVS(("layout_bus: starting bus %d\n", bus->number));
-
- if (!bus->devices && !bus->children)
- return;
-
- /*
- * Align the current bases on appropriate boundaries (4K for
- * IO and 1MB for memory).
- */
-
- bio = io_base = ALIGN(io_base, 4*KB);
- bmem = mem_base = ALIGN(mem_base, 1*MB);
-
- /*
- * PCI devices might have been setup by a PCI BIOS emulation
- * running under TOS. In these cases there is a
- * window during which two devices may have an overlapping
- * address range. To avoid this causing trouble, we first
- * turn off the I/O and memory address decoders for all PCI
- * devices. They'll be re-enabled only once all address
- * decoders are programmed consistently.
- */
-
- DBG_DEVS(("layout_bus: disable_dev for bus %d\n", bus->number));
-
- for (dev = bus->devices; dev; dev = dev->sibling)
- {
- if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) ||
- (dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA))
- disable_dev(dev);
- }
-
- /*
- * Allocate space to each device:
- */
-
- DBG_DEVS(("layout_bus: starting bus %d devices\n", bus->number));
-
- for (dev = bus->devices; dev; dev = dev->sibling)
- {
- if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) ||
- (dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA))
- layout_dev(dev);
- }
-
- DBG_DEVS(("layout_bus: bus %d finished\n", bus->number));
-}
-
-/*
- * static void pcibios_fixup(void)
- *
- * Layout memory and I/O of all devices on the PCI bus if 'pci_modify' is
- * true. This might be necessary because not every m68k machine with a PCI
- * bus has a PCI BIOS. This function should be called right after
- * pci_scan_bus() in pcibios_init().
- */
-
-static void __init pcibios_fixup(void)
-{
- if (pci_modify)
- {
- /*
- * Set base addresses for allocation of I/O and memory space.
- */
-
- io_base = bus_info->io_space.start + IO_ALLOC_OFFSET;
- mem_base = bus_info->mem_space.start + MEM_ALLOC_OFFSET;
-
- /*
- * Scan the tree, allocating PCI memory and I/O space.
- */
-
- layout_bus(pci_bus_b(pci_root.next));
- }
-
- /*
- * Fix interrupt assignments, etc.
- */
-
- bus_info->fixup(pci_modify);
-}
-
-/*
- * static void pcibios_claim_resources(struct pci_bus *bus)
- *
- * Claim all resources that are assigned to devices on the given bus.
- *
- * Parameters:
- *
- * bus - bus.
- */
-
-static void __init pcibios_claim_resources(struct pci_bus *bus)
-{
- struct pci_dev *dev;
- int i;
-
- while (bus)
- {
- for (dev = bus->devices; (dev != NULL); dev = dev->sibling)
- {
- for (i = 0; i < PCI_NUM_RESOURCES; i++)
- {
- struct resource *r = &dev->resource[i];
- struct resource *pr;
- struct pci_bus_info *bus_info = (struct pci_bus_info *) dev->sysdata;
-
- if ((r->start == 0) || (r->parent != NULL))
- continue;
-#if 1
- if (r->flags & IORESOURCE_IO)
- pr = &bus_info->io_space;
- else
- pr = &bus_info->mem_space;
-#else
- if (r->flags & IORESOURCE_IO)
- pr = &ioport_resource;
- else
- pr = &iomem_resource;
-#endif
- if (request_resource(pr, r) < 0)
- {
- printk(KERN_ERR "PCI: Address space collision on region %d of device %s\n", i, dev->name);
- }
- }
- }
-
- if (bus->children)
- pcibios_claim_resources(bus->children);
-
- bus = bus->next;
- }
-}
-
-/*
- * int pcibios_assign_resource(struct pci_dev *dev, int i)
- *
- * Assign a new address to a PCI resource.
- *
- * Parameters:
- *
- * dev - device.
- * i - resource.
- *
- * Result: 0 if successful.
- */
-
-int __init pcibios_assign_resource(struct pci_dev *dev, int i)
-{
- struct resource *r = &dev->resource[i];
- struct resource *pr = pci_find_parent_resource(dev, r);
- unsigned long size = r->end + 1;
-
- if (!pr)
- return -EINVAL;
-
- if (r->flags & IORESOURCE_IO)
- {
- if (size > 0x100)
- return -EFBIG;
-
- if (allocate_resource(pr, r, size, bus_info->io_space.start +
- IO_ALLOC_OFFSET, bus_info->io_space.end, 1024))
- return -EBUSY;
- }
- else
- {
- if (allocate_resource(pr, r, size, bus_info->mem_space.start +
- MEM_ALLOC_OFFSET, bus_info->mem_space.end, size))
- return -EBUSY;
- }
-
- if (i < 6)
- pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + 4 * i, r->start);
-
- return 0;
-}
-
-void __init pcibios_fixup_bus(struct pci_bus *bus)
-{
- struct pci_dev *dev;
- void *sysdata;
-
- sysdata = (bus->parent) ? bus->parent->sysdata : bus->sysdata;
-
- for (dev = bus->devices; (dev != NULL); dev = dev->sibling)
- dev->sysdata = sysdata;
-}
-
-void __init pcibios_init(void)
-{
- printk("Linux/m68k PCI BIOS32 revision %x.%02x\n", MAJOR_REV, MINOR_REV);
-
- bus_info = NULL;
-#ifdef CONFIG_HADES
- if (MACH_IS_HADES)
- bus_info = init_hades_pci();
-#endif
- if (bus_info != NULL)
- {
- printk("PCI: Probing PCI hardware\n");
- pci_scan_bus(0, bus_info->m68k_pci_ops, bus_info);
- pcibios_fixup();
- pcibios_claim_resources(pci_root);
- }
- else
- printk("PCI: No PCI bus detected\n");
-}
-
-char * __init pcibios_setup(char *str)
-{
- if (!strcmp(str, "nomodify"))
- {
- pci_modify = 0;
- return NULL;
- }
- else if (!strcmp(str, "skipvga"))
- {
- skip_vga = 1;
- return NULL;
- }
- else if (!strcmp(str, "noburst"))
- {
- disable_pci_burst = 1;
- return NULL;
- }
-
- return str;
-}
-#endif /* CONFIG_PCI */
diff --git a/arch/m68k/kernel/dma.c b/arch/m68k/kernel/dma.c
index 6f8c080..2bb4245 100644
--- a/arch/m68k/kernel/dma.c
+++ b/arch/m68k/kernel/dma.c
@@ -66,8 +66,8 @@
}
EXPORT_SYMBOL(dma_free_coherent);
-inline void dma_sync_single_for_device(struct device *dev, dma_addr_t handle, size_t size,
- enum dma_data_direction dir)
+void dma_sync_single_for_device(struct device *dev, dma_addr_t handle,
+ size_t size, enum dma_data_direction dir)
{
switch (dir) {
case DMA_TO_DEVICE:
diff --git a/arch/m68k/kernel/ints.c b/arch/m68k/kernel/ints.c
index ded7dd2..7e8a0d3 100644
--- a/arch/m68k/kernel/ints.c
+++ b/arch/m68k/kernel/ints.c
@@ -429,8 +429,9 @@
return 0;
}
+#ifdef CONFIG_PROC_FS
void init_irq_proc(void)
{
/* Insert /proc/irq driver here */
}
-
+#endif
diff --git a/arch/m68k/kernel/process.c b/arch/m68k/kernel/process.c
index 7888cdf..3042c2b 100644
--- a/arch/m68k/kernel/process.c
+++ b/arch/m68k/kernel/process.c
@@ -78,7 +78,7 @@
static void default_idle(void)
{
if (!need_resched())
-#if defined(MACH_ATARI_ONLY) && !defined(CONFIG_HADES)
+#if defined(MACH_ATARI_ONLY)
/* block out HSYNC on the atari (falcon) */
__asm__("stop #0x2200" : : : "cc");
#else
diff --git a/arch/m68k/kernel/traps.c b/arch/m68k/kernel/traps.c
index 75b8340..6d813de 100644
--- a/arch/m68k/kernel/traps.c
+++ b/arch/m68k/kernel/traps.c
@@ -883,8 +883,7 @@
if (i % 5 == 0)
printk("\n ");
#endif
- printk(" [<%08lx>]", addr);
- print_symbol(" %s\n", addr);
+ printk(" [<%08lx>] %pS\n", addr, (void *)addr);
i++;
}
}
@@ -900,10 +899,8 @@
int i;
print_modules();
- printk("PC: [<%08lx>]",regs->pc);
- print_symbol(" %s", regs->pc);
- printk("\nSR: %04x SP: %p a2: %08lx\n",
- regs->sr, regs, regs->a2);
+ printk("PC: [<%08lx>] %pS\n", regs->pc, (void *)regs->pc);
+ printk("SR: %04x SP: %p a2: %08lx\n", regs->sr, regs, regs->a2);
printk("d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n",
regs->d0, regs->d1, regs->d2, regs->d3);
printk("d4: %08lx d5: %08lx a0: %08lx a1: %08lx\n",
diff --git a/arch/m68k/kernel/vmlinux-std.lds b/arch/m68k/kernel/vmlinux-std.lds
index 99b0784..f846d4e 100644
--- a/arch/m68k/kernel/vmlinux-std.lds
+++ b/arch/m68k/kernel/vmlinux-std.lds
@@ -34,10 +34,10 @@
CONSTRUCTORS
}
- .bss : { *(.bss) } /* BSS */
-
. = ALIGN(16);
- .data.cacheline_aligned : { *(.data.cacheline_aligned) } :data
+ .data.cacheline_aligned : { *(.data.cacheline_aligned) }
+
+ .bss : { *(.bss) } /* BSS */
_edata = .; /* End of data section */
@@ -48,7 +48,7 @@
_sinittext = .;
INIT_TEXT
_einittext = .;
- }
+ } :data
.init.data : { INIT_DATA }
. = ALIGN(16);
__setup_start = .;
@@ -74,6 +74,7 @@
.init.ramfs : { *(.init.ramfs) }
__initramfs_end = .;
#endif
+ NOTES
. = ALIGN(8192);
__init_end = .;
diff --git a/arch/m68k/mm/kmap.c b/arch/m68k/mm/kmap.c
index 46b7d60..df620ac 100644
--- a/arch/m68k/mm/kmap.c
+++ b/arch/m68k/mm/kmap.c
@@ -66,8 +66,10 @@
for (p = &iolist; (tmp = *p) ; p = &tmp->next) {
if (size + addr < (unsigned long)tmp->addr)
break;
- if (addr > KMAP_END-size)
+ if (addr > KMAP_END-size) {
+ kfree(area);
return NULL;
+ }
addr = tmp->size + (unsigned long)tmp->addr;
}
area->addr = (void *)addr;
diff --git a/arch/m68k/mvme16x/rtc.c b/arch/m68k/mvme16x/rtc.c
index 432a9f1..cea5e3e 100644
--- a/arch/m68k/mvme16x/rtc.c
+++ b/arch/m68k/mvme16x/rtc.c
@@ -52,15 +52,15 @@
/* Ensure clock and real-time-mode-register are accessible */
rtc->ctrl = RTC_READ;
memset(&wtime, 0, sizeof(struct rtc_time));
- wtime.tm_sec = BCD2BIN(rtc->bcd_sec);
- wtime.tm_min = BCD2BIN(rtc->bcd_min);
- wtime.tm_hour = BCD2BIN(rtc->bcd_hr);
- wtime.tm_mday = BCD2BIN(rtc->bcd_dom);
- wtime.tm_mon = BCD2BIN(rtc->bcd_mth)-1;
- wtime.tm_year = BCD2BIN(rtc->bcd_year);
+ wtime.tm_sec = bcd2bin(rtc->bcd_sec);
+ wtime.tm_min = bcd2bin(rtc->bcd_min);
+ wtime.tm_hour = bcd2bin(rtc->bcd_hr);
+ wtime.tm_mday = bcd2bin(rtc->bcd_dom);
+ wtime.tm_mon = bcd2bin(rtc->bcd_mth)-1;
+ wtime.tm_year = bcd2bin(rtc->bcd_year);
if (wtime.tm_year < 70)
wtime.tm_year += 100;
- wtime.tm_wday = BCD2BIN(rtc->bcd_dow)-1;
+ wtime.tm_wday = bcd2bin(rtc->bcd_dow)-1;
rtc->ctrl = 0;
local_irq_restore(flags);
return copy_to_user(argp, &wtime, sizeof wtime) ?
@@ -104,12 +104,12 @@
local_irq_save(flags);
rtc->ctrl = RTC_WRITE;
- rtc->bcd_sec = BIN2BCD(sec);
- rtc->bcd_min = BIN2BCD(min);
- rtc->bcd_hr = BIN2BCD(hrs);
- rtc->bcd_dom = BIN2BCD(day);
- rtc->bcd_mth = BIN2BCD(mon);
- rtc->bcd_year = BIN2BCD(yrs%100);
+ rtc->bcd_sec = bin2bcd(sec);
+ rtc->bcd_min = bin2bcd(min);
+ rtc->bcd_hr = bin2bcd(hrs);
+ rtc->bcd_dom = bin2bcd(day);
+ rtc->bcd_mth = bin2bcd(mon);
+ rtc->bcd_year = bin2bcd(yrs%100);
rtc->ctrl = 0;
local_irq_restore(flags);
diff --git a/arch/m68k/q40/config.c b/arch/m68k/q40/config.c
index be9de2f..9c7eefa 100644
--- a/arch/m68k/q40/config.c
+++ b/arch/m68k/q40/config.c
@@ -23,6 +23,7 @@
#include <linux/serial_reg.h>
#include <linux/rtc.h>
#include <linux/vt_kern.h>
+#include <linux/bcd.h>
#include <asm/io.h>
#include <asm/rtc.h>
@@ -216,17 +217,6 @@
}
-static inline unsigned char bcd2bin(unsigned char b)
-{
- return (b >> 4) * 10 + (b & 15);
-}
-
-static inline unsigned char bin2bcd(unsigned char b)
-{
- return (b / 10) * 16 + (b % 10);
-}
-
-
static unsigned long q40_gettimeoffset(void)
{
return 5000 * (ql_ticks != 0);
diff --git a/arch/m68k/sun3x/time.c b/arch/m68k/sun3x/time.c
index f5eaafb..536a04a 100644
--- a/arch/m68k/sun3x/time.c
+++ b/arch/m68k/sun3x/time.c
@@ -47,23 +47,23 @@
if(set) {
h->csr |= C_WRITE;
- h->sec = BIN2BCD(t->tm_sec);
- h->min = BIN2BCD(t->tm_min);
- h->hour = BIN2BCD(t->tm_hour);
- h->wday = BIN2BCD(t->tm_wday);
- h->mday = BIN2BCD(t->tm_mday);
- h->month = BIN2BCD(t->tm_mon);
- h->year = BIN2BCD(t->tm_year);
+ h->sec = bin2bcd(t->tm_sec);
+ h->min = bin2bcd(t->tm_min);
+ h->hour = bin2bcd(t->tm_hour);
+ h->wday = bin2bcd(t->tm_wday);
+ h->mday = bin2bcd(t->tm_mday);
+ h->month = bin2bcd(t->tm_mon);
+ h->year = bin2bcd(t->tm_year);
h->csr &= ~C_WRITE;
} else {
h->csr |= C_READ;
- t->tm_sec = BCD2BIN(h->sec);
- t->tm_min = BCD2BIN(h->min);
- t->tm_hour = BCD2BIN(h->hour);
- t->tm_wday = BCD2BIN(h->wday);
- t->tm_mday = BCD2BIN(h->mday);
- t->tm_mon = BCD2BIN(h->month);
- t->tm_year = BCD2BIN(h->year);
+ t->tm_sec = bcd2bin(h->sec);
+ t->tm_min = bcd2bin(h->min);
+ t->tm_hour = bcd2bin(h->hour);
+ t->tm_wday = bcd2bin(h->wday);
+ t->tm_mday = bcd2bin(h->mday);
+ t->tm_mon = bcd2bin(h->month);
+ t->tm_year = bcd2bin(h->year);
h->csr &= ~C_READ;
}
diff --git a/arch/m68knommu/Kconfig b/arch/m68knommu/Kconfig
index 2e7515e..0a89983 100644
--- a/arch/m68knommu/Kconfig
+++ b/arch/m68knommu/Kconfig
@@ -73,9 +73,6 @@
config NO_IOPORT
def_bool y
-config ARCH_SUPPORTS_AOUT
- def_bool y
-
source "init/Kconfig"
menu "Processor type and features"
diff --git a/arch/m68knommu/include/asm/a.out.h b/arch/m68knommu/include/asm/a.out.h
deleted file mode 100644
index ce18ef9..0000000
--- a/arch/m68knommu/include/asm/a.out.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-m68k/a.out.h>
diff --git a/arch/m68knommu/kernel/process.c b/arch/m68knommu/kernel/process.c
index 47502d5..3f2d774 100644
--- a/arch/m68knommu/kernel/process.c
+++ b/arch/m68knommu/kernel/process.c
@@ -25,7 +25,6 @@
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/user.h>
-#include <linux/a.out.h>
#include <linux/interrupt.h>
#include <linux/reboot.h>
#include <linux/fs.h>
diff --git a/arch/m68knommu/kernel/traps.c b/arch/m68knommu/kernel/traps.c
index 46f8f9d..5d5d56b 100644
--- a/arch/m68knommu/kernel/traps.c
+++ b/arch/m68knommu/kernel/traps.c
@@ -22,7 +22,6 @@
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/types.h>
-#include <linux/a.out.h>
#include <linux/user.h>
#include <linux/string.h>
#include <linux/linkage.h>
diff --git a/arch/mips/include/asm/a.out.h b/arch/mips/include/asm/a.out.h
deleted file mode 100644
index cad8371..0000000
--- a/arch/mips/include/asm/a.out.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 1994 - 1999, 2003 by Ralf Baechle
- */
-#ifndef _ASM_A_OUT_H
-#define _ASM_A_OUT_H
-
-#ifdef __KERNEL__
-
-
-#endif
-
-struct exec
-{
- unsigned long a_info; /* Use macros N_MAGIC, etc for access */
- unsigned a_text; /* length of text, in bytes */
- unsigned a_data; /* length of data, in bytes */
- unsigned a_bss; /* length of uninitialized data area for
- file, in bytes */
- unsigned a_syms; /* length of symbol table data in file,
- in bytes */
- unsigned a_entry; /* start address */
- unsigned a_trsize; /* length of relocation info for text, in
- bytes */
- unsigned a_drsize; /* length of relocation info for data, in bytes */
-};
-
-#define N_TRSIZE(a) ((a).a_trsize)
-#define N_DRSIZE(a) ((a).a_drsize)
-#define N_SYMSIZE(a) ((a).a_syms)
-
-#endif /* _ASM_A_OUT_H */
diff --git a/arch/mips/include/asm/mach-generic/ide.h b/arch/mips/include/asm/mach-generic/ide.h
index 73008f7..9c93a5b 100644
--- a/arch/mips/include/asm/mach-generic/ide.h
+++ b/arch/mips/include/asm/mach-generic/ide.h
@@ -19,35 +19,6 @@
#include <linux/stddef.h>
#include <asm/processor.h>
-static __inline__ int ide_probe_legacy(void)
-{
-#ifdef CONFIG_PCI
- struct pci_dev *dev;
- /*
- * This can be called on the ide_setup() path, super-early in
- * boot. But the down_read() will enable local interrupts,
- * which can cause some machines to crash. So here we detect
- * and flag that situation and bail out early.
- */
- if (no_pci_devices())
- return 0;
- dev = pci_get_class(PCI_CLASS_BRIDGE_EISA << 8, NULL);
- if (dev)
- goto found;
- dev = pci_get_class(PCI_CLASS_BRIDGE_ISA << 8, NULL);
- if (dev)
- goto found;
- return 0;
-found:
- pci_dev_put(dev);
- return 1;
-#elif defined(CONFIG_EISA) || defined(CONFIG_ISA)
- return 1;
-#else
- return 0;
-#endif
-}
-
/* MIPS port and memory-mapped I/O string operations. */
static inline void __ide_flush_prologue(void)
{
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index 22fc19b..ca2e402 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -22,7 +22,6 @@
#include <linux/personality.h>
#include <linux/sys.h>
#include <linux/user.h>
-#include <linux/a.out.h>
#include <linux/init.h>
#include <linux/completion.h>
#include <linux/kallsyms.h>
diff --git a/arch/mips/kernel/syscall.c b/arch/mips/kernel/syscall.c
index 343015a..37970d9 100644
--- a/arch/mips/kernel/syscall.c
+++ b/arch/mips/kernel/syscall.c
@@ -7,7 +7,6 @@
* Copyright (C) 1999, 2000 Silicon Graphics, Inc.
* Copyright (C) 2001 MIPS Technologies, Inc.
*/
-#include <linux/a.out.h>
#include <linux/capability.h>
#include <linux/errno.h>
#include <linux/linkage.h>
diff --git a/arch/mn10300/Kconfig b/arch/mn10300/Kconfig
index e856218..dd557c9 100644
--- a/arch/mn10300/Kconfig
+++ b/arch/mn10300/Kconfig
@@ -53,9 +53,6 @@
config ARCH_HAS_ILOG2_U32
def_bool y
-config ARCH_SUPPORTS_AOUT
- def_bool n
-
# Use the generic interrupt handling code in kernel/irq/
config GENERIC_HARDIRQS
def_bool y
diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig
index a7d4fd35..8313fcc 100644
--- a/arch/parisc/Kconfig
+++ b/arch/parisc/Kconfig
@@ -76,9 +76,6 @@
bool
default y
-config ARCH_SUPPORTS_AOUT
- def_bool y
-
# unless you want to implement ACPI on PA-RISC ... ;-)
config PM
bool
diff --git a/arch/powerpc/include/asm/a.out.h b/arch/powerpc/include/asm/a.out.h
deleted file mode 100644
index 89cead6..0000000
--- a/arch/powerpc/include/asm/a.out.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef _ASM_POWERPC_A_OUT_H
-#define _ASM_POWERPC_A_OUT_H
-
-struct exec
-{
- unsigned long a_info; /* Use macros N_MAGIC, etc for access */
- unsigned a_text; /* length of text, in bytes */
- unsigned a_data; /* length of data, in bytes */
- unsigned a_bss; /* length of uninitialized data area for file, in bytes */
- unsigned a_syms; /* length of symbol table data in file, in bytes */
- unsigned a_entry; /* start address */
- unsigned a_trsize; /* length of relocation info for text, in bytes */
- unsigned a_drsize; /* length of relocation info for data, in bytes */
-};
-
-#define N_TRSIZE(a) ((a).a_trsize)
-#define N_DRSIZE(a) ((a).a_drsize)
-#define N_SYMSIZE(a) ((a).a_syms)
-
-#endif /* _ASM_POWERPC_A_OUT_H */
diff --git a/arch/powerpc/include/asm/siginfo.h b/arch/powerpc/include/asm/siginfo.h
index 12f1bce..49495b0 100644
--- a/arch/powerpc/include/asm/siginfo.h
+++ b/arch/powerpc/include/asm/siginfo.h
@@ -15,11 +15,6 @@
#include <asm-generic/siginfo.h>
-/*
- * SIGTRAP si_codes
- */
-#define TRAP_BRANCH (__SI_FAULT|3) /* process taken branch trap */
-#define TRAP_HWBKPT (__SI_FAULT|4) /* hardware breakpoint or watchpoint */
#undef NSIGTRAP
#define NSIGTRAP 4
diff --git a/arch/powerpc/include/asm/statfs.h b/arch/powerpc/include/asm/statfs.h
index 6702402..5244834 100644
--- a/arch/powerpc/include/asm/statfs.h
+++ b/arch/powerpc/include/asm/statfs.h
@@ -1,60 +1,6 @@
#ifndef _ASM_POWERPC_STATFS_H
#define _ASM_POWERPC_STATFS_H
-/* For ppc32 we just use the generic definitions, not so simple on ppc64 */
-
-#ifndef __powerpc64__
#include <asm-generic/statfs.h>
-#else
-#ifndef __KERNEL_STRICT_NAMES
-#include <linux/types.h>
-typedef __kernel_fsid_t fsid_t;
-#endif
-
-/*
- * We're already 64-bit, so duplicate the definition
- */
-struct statfs {
- long f_type;
- long f_bsize;
- long f_blocks;
- long f_bfree;
- long f_bavail;
- long f_files;
- long f_ffree;
- __kernel_fsid_t f_fsid;
- long f_namelen;
- long f_frsize;
- long f_spare[5];
-};
-
-struct statfs64 {
- long f_type;
- long f_bsize;
- long f_blocks;
- long f_bfree;
- long f_bavail;
- long f_files;
- long f_ffree;
- __kernel_fsid_t f_fsid;
- long f_namelen;
- long f_frsize;
- long f_spare[5];
-};
-
-struct compat_statfs64 {
- __u32 f_type;
- __u32 f_bsize;
- __u64 f_blocks;
- __u64 f_bfree;
- __u64 f_bavail;
- __u64 f_files;
- __u64 f_ffree;
- __kernel_fsid_t f_fsid;
- __u32 f_namelen;
- __u32 f_frsize;
- __u32 f_spare[5];
-};
-#endif /* ! __powerpc64__ */
#endif
diff --git a/arch/powerpc/kernel/softemu8xx.c b/arch/powerpc/kernel/softemu8xx.c
index c906c4b..23c8c5e 100644
--- a/arch/powerpc/kernel/softemu8xx.c
+++ b/arch/powerpc/kernel/softemu8xx.c
@@ -23,7 +23,6 @@
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/user.h>
-#include <linux/a.out.h>
#include <linux/interrupt.h>
#include <asm/pgtable.h>
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index 81ccb8dd..f5def6c 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -23,7 +23,6 @@
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/user.h>
-#include <linux/a.out.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/module.h>
diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c
index 690ca7b..2c8b809 100644
--- a/arch/powerpc/platforms/cell/spufs/inode.c
+++ b/arch/powerpc/platforms/cell/spufs/inode.c
@@ -659,7 +659,7 @@
Opt_uid, Opt_gid, Opt_mode, Opt_debug, Opt_err,
};
-static match_table_t spufs_tokens = {
+static const match_table_t spufs_tokens = {
{ Opt_uid, "uid=%d" },
{ Opt_gid, "gid=%d" },
{ Opt_mode, "mode=%o" },
diff --git a/arch/powerpc/platforms/chrp/setup.c b/arch/powerpc/platforms/chrp/setup.c
index 1ba7ce5..272d79a 100644
--- a/arch/powerpc/platforms/chrp/setup.c
+++ b/arch/powerpc/platforms/chrp/setup.c
@@ -17,7 +17,6 @@
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/user.h>
-#include <linux/a.out.h>
#include <linux/tty.h>
#include <linux/major.h>
#include <linux/interrupt.h>
diff --git a/arch/powerpc/platforms/maple/setup.c b/arch/powerpc/platforms/maple/setup.c
index 3647147..d4c61c3 100644
--- a/arch/powerpc/platforms/maple/setup.c
+++ b/arch/powerpc/platforms/maple/setup.c
@@ -23,7 +23,6 @@
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/user.h>
-#include <linux/a.out.h>
#include <linux/tty.h>
#include <linux/string.h>
#include <linux/delay.h>
diff --git a/arch/powerpc/platforms/powermac/setup.c b/arch/powerpc/platforms/powermac/setup.c
index 88ccf3a..82c14d2 100644
--- a/arch/powerpc/platforms/powermac/setup.c
+++ b/arch/powerpc/platforms/powermac/setup.c
@@ -33,7 +33,6 @@
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/user.h>
-#include <linux/a.out.h>
#include <linux/tty.h>
#include <linux/string.h>
#include <linux/delay.h>
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index 7b01d67..ec34170 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -25,7 +25,6 @@
#include <linux/unistd.h>
#include <linux/slab.h>
#include <linux/user.h>
-#include <linux/a.out.h>
#include <linux/tty.h>
#include <linux/major.h>
#include <linux/interrupt.h>
diff --git a/arch/s390/hypfs/inode.c b/arch/s390/hypfs/inode.c
index 7383781..3631380 100644
--- a/arch/s390/hypfs/inode.c
+++ b/arch/s390/hypfs/inode.c
@@ -219,7 +219,7 @@
enum { opt_uid, opt_gid, opt_err };
-static match_table_t hypfs_tokens = {
+static const match_table_t hypfs_tokens = {
{opt_uid, "uid=%u"},
{opt_gid, "gid=%u"},
{opt_err, NULL}
diff --git a/arch/s390/include/asm/statfs.h b/arch/s390/include/asm/statfs.h
index 099a455..06cc703 100644
--- a/arch/s390/include/asm/statfs.h
+++ b/arch/s390/include/asm/statfs.h
@@ -12,19 +12,16 @@
#ifndef __s390x__
#include <asm-generic/statfs.h>
#else
+/*
+ * We can't use <asm-generic/statfs.h> because in 64-bit mode
+ * we mix ints of different sizes in our struct statfs.
+ */
#ifndef __KERNEL_STRICT_NAMES
-
#include <linux/types.h>
-
typedef __kernel_fsid_t fsid_t;
-
#endif
-/*
- * This is ugly -- we're already 64-bit clean, so just duplicate the
- * definitions.
- */
struct statfs {
int f_type;
int f_bsize;
diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig
index a214002..97671da 100644
--- a/arch/sparc/Kconfig
+++ b/arch/sparc/Kconfig
@@ -20,6 +20,11 @@
bool
default y
+config GENERIC_GPIO
+ bool
+ help
+ Generic GPIO API support
+
config ARCH_NO_VIRT_TO_BUS
def_bool y
@@ -69,6 +74,9 @@
select HAVE_OPROFILE
select HAVE_ARCH_KGDB if !SMP
select HAVE_ARCH_TRACEHOOK
+ select ARCH_WANT_OPTIONAL_GPIOLIB
+ select RTC_CLASS
+ select RTC_DRV_M48T59
# Identify this as a Sparc32 build
config SPARC32
@@ -204,17 +212,6 @@
Enable power management and CPU standby features on supported
SPARC platforms.
-config SUN4
- bool "Support for SUN4 machines (disables SUN4[CDM] support)"
- depends on !SMP
- default n
- help
- Say Y here if, and only if, your machine is a sun4. Note that
- a kernel compiled with this option will run only on sun4.
- (And the current version will probably work only on sun4/330.)
-
-if !SUN4
-
config PCI
bool "Support for PCI and PS/2 keyboard/mouse"
help
@@ -227,11 +224,6 @@
source "drivers/pci/Kconfig"
-endif
-
-config NO_DMA
- def_bool !PCI
-
config SUN_OPENPROMFS
tristate "Openprom tree appears in /proc/openprom"
help
@@ -263,9 +255,7 @@
source "drivers/Kconfig"
-if !SUN4
source "drivers/sbus/char/Kconfig"
-endif
# This one must be before the filesystem configs. -DaveM
diff --git a/arch/sparc/include/asm/Kbuild b/arch/sparc/include/asm/Kbuild
index a5f0ce7..2d2769d 100644
--- a/arch/sparc/include/asm/Kbuild
+++ b/arch/sparc/include/asm/Kbuild
@@ -15,14 +15,11 @@
header-y += signal_64.h
header-y += stat_32.h
header-y += stat_64.h
-header-y += statfs_32.h
-header-y += statfs_64.h
header-y += unistd_32.h
header-y += unistd_64.h
header-y += apc.h
header-y += asi.h
-header-y += bpp.h
header-y += display7seg.h
header-y += envctrl.h
header-y += fbio.h
@@ -41,5 +38,4 @@
header-y += traps.h
header-y += uctx.h
header-y += utrap.h
-header-y += vfc_ioctls.h
header-y += watchdog.h
diff --git a/arch/sparc/include/asm/asmmacro.h b/arch/sparc/include/asm/asmmacro.h
index a619a4d..a995bf8 100644
--- a/arch/sparc/include/asm/asmmacro.h
+++ b/arch/sparc/include/asm/asmmacro.h
@@ -34,12 +34,7 @@
/* sun4 probably wants half word accesses to ASI_SEGMAP, while sun4c+
likes byte accesses. These are to avoid ifdef mania. */
-#ifdef CONFIG_SUN4
-#define lduXa lduha
-#define stXa stha
-#else
#define lduXa lduba
#define stXa stba
-#endif
#endif /* !(_SPARC_ASMMACRO_H) */
diff --git a/arch/sparc/include/asm/bpp.h b/arch/sparc/include/asm/bpp.h
deleted file mode 100644
index 31f515e..0000000
--- a/arch/sparc/include/asm/bpp.h
+++ /dev/null
@@ -1,73 +0,0 @@
-#ifndef _SPARC_BPP_H
-#define _SPARC_BPP_H
-
-/*
- * Copyright (c) 1995 Picture Elements
- * Stephen Williams
- * Gus Baldauf
- *
- * Linux/SPARC port by Peter Zaitcev.
- * Integration into SPARC tree by Tom Dyas.
- */
-
-#include <linux/ioctl.h>
-
-/*
- * This is a driver that supports IEEE Std 1284-1994 communications
- * with compliant or compatible devices. It will use whatever features
- * the device supports, prefering those that are typically faster.
- *
- * When the device is opened, it is left in COMPATIBILITY mode, and
- * writes work like any printer device. The driver only attempt to
- * negotiate 1284 modes when needed so that plugs can be pulled,
- * switch boxes switched, etc., without disrupting things. It will
- * also leave the device in compatibility mode when closed.
- */
-
-
-
-/*
- * This driver also supplies ioctls to manually manipulate the
- * pins. This is great for testing devices, or writing code to deal
- * with bizzarro-mode of the ACME Special TurboThingy Plus.
- *
- * NOTE: These ioctl currently do not interact well with
- * read/write. Caveat emptor.
- *
- * PUT_PINS allows us to assign the sense of all the pins, including
- * the data pins if being driven by the host. The GET_PINS returns the
- * pins that the peripheral drives, including data if appropriate.
- */
-
-# define BPP_PUT_PINS _IOW('B', 1, int)
-# define BPP_GET_PINS _IOR('B', 2, char) /* that's bogus - should've been _IO */
-# define BPP_PUT_DATA _IOW('B', 3, int)
-# define BPP_GET_DATA _IOR('B', 4, char) /* ditto */
-
-/*
- * Set the data bus to input mode. Disengage the data bin driver and
- * be prepared to read values from the peripheral. If the arg is 0,
- * then revert the bus to output mode.
- */
-# define BPP_SET_INPUT _IOW('B', 5, int)
-
-/*
- * These bits apply to the PUT operation...
- */
-# define BPP_PP_nStrobe 0x0001
-# define BPP_PP_nAutoFd 0x0002
-# define BPP_PP_nInit 0x0004
-# define BPP_PP_nSelectIn 0x0008
-
-/*
- * These apply to the GET operation, which also reads the current value
- * of the previously put values. A bit mask of these will be returned
- * as a bit mask in the return code of the ioctl().
- */
-# define BPP_GP_nAck 0x0100
-# define BPP_GP_Busy 0x0200
-# define BPP_GP_PError 0x0400
-# define BPP_GP_Select 0x0800
-# define BPP_GP_nFault 0x1000
-
-#endif
diff --git a/arch/sparc/include/asm/bugs.h b/arch/sparc/include/asm/bugs.h
index e179bc1..61d86bb 100644
--- a/arch/sparc/include/asm/bugs.h
+++ b/arch/sparc/include/asm/bugs.h
@@ -7,10 +7,6 @@
#include <asm/cpudata.h>
#endif
-#ifdef CONFIG_SPARC64
-#include <asm/sstate.h>
-#endif
-
extern unsigned long loops_per_jiffy;
static void __init check_bugs(void)
@@ -18,7 +14,4 @@
#if defined(CONFIG_SPARC32) && !defined(CONFIG_SMP)
cpu_data(0).udelay_val = loops_per_jiffy;
#endif
-#ifdef CONFIG_SPARC64
- sstate_running();
-#endif
}
diff --git a/arch/sparc/include/asm/cpudata_64.h b/arch/sparc/include/asm/cpudata_64.h
index 532975e..7da7c13 100644
--- a/arch/sparc/include/asm/cpudata_64.h
+++ b/arch/sparc/include/asm/cpudata_64.h
@@ -86,7 +86,6 @@
extern void init_cur_cpu_trap(struct thread_info *);
extern void setup_tba(void);
extern int ncpus_probed;
-extern void __init cpu_probe(void);
extern const struct seq_operations cpuinfo_op;
extern unsigned long real_hard_smp_processor_id(void);
diff --git a/arch/sparc/include/asm/dma-mapping_32.h b/arch/sparc/include/asm/dma-mapping_32.h
index f3a641e..8a57ea0 100644
--- a/arch/sparc/include/asm/dma-mapping_32.h
+++ b/arch/sparc/include/asm/dma-mapping_32.h
@@ -1,11 +1,60 @@
#ifndef _ASM_SPARC_DMA_MAPPING_H
#define _ASM_SPARC_DMA_MAPPING_H
+#include <linux/types.h>
-#ifdef CONFIG_PCI
-#include <asm-generic/dma-mapping.h>
-#else
-#include <asm-generic/dma-mapping-broken.h>
-#endif /* PCI */
+struct device;
+struct scatterlist;
+struct page;
+
+#define DMA_ERROR_CODE (~(dma_addr_t)0x0)
+
+extern int dma_supported(struct device *dev, u64 mask);
+extern int dma_set_mask(struct device *dev, u64 dma_mask);
+extern void *dma_alloc_coherent(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, gfp_t flag);
+extern void dma_free_coherent(struct device *dev, size_t size,
+ void *cpu_addr, dma_addr_t dma_handle);
+extern dma_addr_t dma_map_single(struct device *dev, void *cpu_addr,
+ size_t size,
+ enum dma_data_direction direction);
+extern void dma_unmap_single(struct device *dev, dma_addr_t dma_addr,
+ size_t size,
+ enum dma_data_direction direction);
+extern dma_addr_t dma_map_page(struct device *dev, struct page *page,
+ unsigned long offset, size_t size,
+ enum dma_data_direction direction);
+extern void dma_unmap_page(struct device *dev, dma_addr_t dma_address,
+ size_t size, enum dma_data_direction direction);
+extern int dma_map_sg(struct device *dev, struct scatterlist *sg,
+ int nents, enum dma_data_direction direction);
+extern void dma_unmap_sg(struct device *dev, struct scatterlist *sg,
+ int nents, enum dma_data_direction direction);
+extern void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle,
+ size_t size,
+ enum dma_data_direction direction);
+extern void dma_sync_single_for_device(struct device *dev,
+ dma_addr_t dma_handle,
+ size_t size,
+ enum dma_data_direction direction);
+extern void dma_sync_single_range_for_cpu(struct device *dev,
+ dma_addr_t dma_handle,
+ unsigned long offset,
+ size_t size,
+ enum dma_data_direction direction);
+extern void dma_sync_single_range_for_device(struct device *dev,
+ dma_addr_t dma_handle,
+ unsigned long offset, size_t size,
+ enum dma_data_direction direction);
+extern void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg,
+ int nelems, enum dma_data_direction direction);
+extern void dma_sync_sg_for_device(struct device *dev,
+ struct scatterlist *sg, int nelems,
+ enum dma_data_direction direction);
+extern int dma_mapping_error(struct device *dev, dma_addr_t dma_addr);
+extern int dma_get_cache_alignment(void);
+
+#define dma_alloc_noncoherent dma_alloc_coherent
+#define dma_free_noncoherent dma_free_coherent
#endif /* _ASM_SPARC_DMA_MAPPING_H */
diff --git a/arch/sparc/include/asm/dma.h b/arch/sparc/include/asm/dma.h
index aa1d90a..b554927 100644
--- a/arch/sparc/include/asm/dma.h
+++ b/arch/sparc/include/asm/dma.h
@@ -1,8 +1,139 @@
-#ifndef ___ASM_SPARC_DMA_H
-#define ___ASM_SPARC_DMA_H
-#if defined(__sparc__) && defined(__arch64__)
-#include <asm/dma_64.h>
+#ifndef _ASM_SPARC_DMA_H
+#define _ASM_SPARC_DMA_H
+
+/* These are irrelevant for Sparc DMA, but we leave it in so that
+ * things can compile.
+ */
+#define MAX_DMA_CHANNELS 8
+#define DMA_MODE_READ 1
+#define DMA_MODE_WRITE 2
+#define MAX_DMA_ADDRESS (~0UL)
+
+/* Useful constants */
+#define SIZE_16MB (16*1024*1024)
+#define SIZE_64K (64*1024)
+
+/* SBUS DMA controller reg offsets */
+#define DMA_CSR 0x00UL /* rw DMA control/status register 0x00 */
+#define DMA_ADDR 0x04UL /* rw DMA transfer address register 0x04 */
+#define DMA_COUNT 0x08UL /* rw DMA transfer count register 0x08 */
+#define DMA_TEST 0x0cUL /* rw DMA test/debug register 0x0c */
+
+/* Fields in the cond_reg register */
+/* First, the version identification bits */
+#define DMA_DEVICE_ID 0xf0000000 /* Device identification bits */
+#define DMA_VERS0 0x00000000 /* Sunray DMA version */
+#define DMA_ESCV1 0x40000000 /* DMA ESC Version 1 */
+#define DMA_VERS1 0x80000000 /* DMA rev 1 */
+#define DMA_VERS2 0xa0000000 /* DMA rev 2 */
+#define DMA_VERHME 0xb0000000 /* DMA hme gate array */
+#define DMA_VERSPLUS 0x90000000 /* DMA rev 1 PLUS */
+
+#define DMA_HNDL_INTR 0x00000001 /* An IRQ needs to be handled */
+#define DMA_HNDL_ERROR 0x00000002 /* We need to take an error */
+#define DMA_FIFO_ISDRAIN 0x0000000c /* The DMA FIFO is draining */
+#define DMA_INT_ENAB 0x00000010 /* Turn on interrupts */
+#define DMA_FIFO_INV 0x00000020 /* Invalidate the FIFO */
+#define DMA_ACC_SZ_ERR 0x00000040 /* The access size was bad */
+#define DMA_FIFO_STDRAIN 0x00000040 /* DMA_VERS1 Drain the FIFO */
+#define DMA_RST_SCSI 0x00000080 /* Reset the SCSI controller */
+#define DMA_RST_ENET DMA_RST_SCSI /* Reset the ENET controller */
+#define DMA_ST_WRITE 0x00000100 /* write from device to memory */
+#define DMA_ENABLE 0x00000200 /* Fire up DMA, handle requests */
+#define DMA_PEND_READ 0x00000400 /* DMA_VERS1/0/PLUS Pending Read */
+#define DMA_ESC_BURST 0x00000800 /* 1=16byte 0=32byte */
+#define DMA_READ_AHEAD 0x00001800 /* DMA read ahead partial longword */
+#define DMA_DSBL_RD_DRN 0x00001000 /* No EC drain on slave reads */
+#define DMA_BCNT_ENAB 0x00002000 /* If on, use the byte counter */
+#define DMA_TERM_CNTR 0x00004000 /* Terminal counter */
+#define DMA_SCSI_SBUS64 0x00008000 /* HME: Enable 64-bit SBUS mode. */
+#define DMA_CSR_DISAB 0x00010000 /* No FIFO drains during csr */
+#define DMA_SCSI_DISAB 0x00020000 /* No FIFO drains during reg */
+#define DMA_DSBL_WR_INV 0x00020000 /* No EC inval. on slave writes */
+#define DMA_ADD_ENABLE 0x00040000 /* Special ESC DVMA optimization */
+#define DMA_E_BURSTS 0x000c0000 /* ENET: SBUS r/w burst mask */
+#define DMA_E_BURST32 0x00040000 /* ENET: SBUS 32 byte r/w burst */
+#define DMA_E_BURST16 0x00000000 /* ENET: SBUS 16 byte r/w burst */
+#define DMA_BRST_SZ 0x000c0000 /* SCSI: SBUS r/w burst size */
+#define DMA_BRST64 0x000c0000 /* SCSI: 64byte bursts (HME on UltraSparc only) */
+#define DMA_BRST32 0x00040000 /* SCSI: 32byte bursts */
+#define DMA_BRST16 0x00000000 /* SCSI: 16byte bursts */
+#define DMA_BRST0 0x00080000 /* SCSI: no bursts (non-HME gate arrays) */
+#define DMA_ADDR_DISAB 0x00100000 /* No FIFO drains during addr */
+#define DMA_2CLKS 0x00200000 /* Each transfer = 2 clock ticks */
+#define DMA_3CLKS 0x00400000 /* Each transfer = 3 clock ticks */
+#define DMA_EN_ENETAUI DMA_3CLKS /* Put lance into AUI-cable mode */
+#define DMA_CNTR_DISAB 0x00800000 /* No IRQ when DMA_TERM_CNTR set */
+#define DMA_AUTO_NADDR 0x01000000 /* Use "auto nxt addr" feature */
+#define DMA_SCSI_ON 0x02000000 /* Enable SCSI dma */
+#define DMA_PARITY_OFF 0x02000000 /* HME: disable parity checking */
+#define DMA_LOADED_ADDR 0x04000000 /* Address has been loaded */
+#define DMA_LOADED_NADDR 0x08000000 /* Next address has been loaded */
+#define DMA_RESET_FAS366 0x08000000 /* HME: Assert RESET to FAS366 */
+
+/* Values describing the burst-size property from the PROM */
+#define DMA_BURST1 0x01
+#define DMA_BURST2 0x02
+#define DMA_BURST4 0x04
+#define DMA_BURST8 0x08
+#define DMA_BURST16 0x10
+#define DMA_BURST32 0x20
+#define DMA_BURST64 0x40
+#define DMA_BURSTBITS 0x7f
+
+/* From PCI */
+
+#ifdef CONFIG_PCI
+extern int isa_dma_bridge_buggy;
#else
-#include <asm/dma_32.h>
+#define isa_dma_bridge_buggy (0)
#endif
+
+#ifdef CONFIG_SPARC32
+
+/* Routines for data transfer buffers. */
+BTFIXUPDEF_CALL(char *, mmu_lockarea, char *, unsigned long)
+BTFIXUPDEF_CALL(void, mmu_unlockarea, char *, unsigned long)
+
+#define mmu_lockarea(vaddr,len) BTFIXUP_CALL(mmu_lockarea)(vaddr,len)
+#define mmu_unlockarea(vaddr,len) BTFIXUP_CALL(mmu_unlockarea)(vaddr,len)
+
+struct page;
+struct device;
+struct scatterlist;
+
+/* These are implementations for sbus_map_sg/sbus_unmap_sg... collapse later */
+BTFIXUPDEF_CALL(__u32, mmu_get_scsi_one, struct device *, char *, unsigned long)
+BTFIXUPDEF_CALL(void, mmu_get_scsi_sgl, struct device *, struct scatterlist *, int)
+BTFIXUPDEF_CALL(void, mmu_release_scsi_one, struct device *, __u32, unsigned long)
+BTFIXUPDEF_CALL(void, mmu_release_scsi_sgl, struct device *, struct scatterlist *, int)
+
+#define mmu_get_scsi_one(dev,vaddr,len) BTFIXUP_CALL(mmu_get_scsi_one)(dev,vaddr,len)
+#define mmu_get_scsi_sgl(dev,sg,sz) BTFIXUP_CALL(mmu_get_scsi_sgl)(dev,sg,sz)
+#define mmu_release_scsi_one(dev,vaddr,len) BTFIXUP_CALL(mmu_release_scsi_one)(dev,vaddr,len)
+#define mmu_release_scsi_sgl(dev,sg,sz) BTFIXUP_CALL(mmu_release_scsi_sgl)(dev,sg,sz)
+
+/*
+ * mmu_map/unmap are provided by iommu/iounit; Invalid to call on IIep.
+ *
+ * The mmu_map_dma_area establishes two mappings in one go.
+ * These mappings point to pages normally mapped at 'va' (linear address).
+ * First mapping is for CPU visible address at 'a', uncached.
+ * This is an alias, but it works because it is an uncached mapping.
+ * Second mapping is for device visible address, or "bus" address.
+ * The bus address is returned at '*pba'.
+ *
+ * These functions seem distinct, but are hard to split. On sun4c,
+ * at least for now, 'a' is equal to bus address, and retured in *pba.
+ * On sun4m, page attributes depend on the CPU type, so we have to
+ * know if we are mapping RAM or I/O, so it has to be an additional argument
+ * to a separate mapping function for CPU visible mappings.
+ */
+BTFIXUPDEF_CALL(int, mmu_map_dma_area, struct device *, dma_addr_t *, unsigned long, unsigned long, int len)
+BTFIXUPDEF_CALL(void, mmu_unmap_dma_area, struct device *, unsigned long busa, int len)
+
+#define mmu_map_dma_area(dev,pba,va,a,len) BTFIXUP_CALL(mmu_map_dma_area)(dev,pba,va,a,len)
+#define mmu_unmap_dma_area(dev,ba,len) BTFIXUP_CALL(mmu_unmap_dma_area)(dev,ba,len)
#endif
+
+#endif /* !(_ASM_SPARC_DMA_H) */
diff --git a/arch/sparc/include/asm/dma_32.h b/arch/sparc/include/asm/dma_32.h
deleted file mode 100644
index cf7189c..0000000
--- a/arch/sparc/include/asm/dma_32.h
+++ /dev/null
@@ -1,288 +0,0 @@
-/* include/asm/dma.h
- *
- * Copyright 1995 (C) David S. Miller (davem@davemloft.net)
- */
-
-#ifndef _ASM_SPARC_DMA_H
-#define _ASM_SPARC_DMA_H
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-
-#include <asm/vac-ops.h> /* for invalidate's, etc. */
-#include <asm/sbus.h>
-#include <asm/delay.h>
-#include <asm/oplib.h>
-#include <asm/system.h>
-#include <asm/io.h>
-#include <linux/spinlock.h>
-
-struct page;
-extern spinlock_t dma_spin_lock;
-
-static inline unsigned long claim_dma_lock(void)
-{
- unsigned long flags;
- spin_lock_irqsave(&dma_spin_lock, flags);
- return flags;
-}
-
-static inline void release_dma_lock(unsigned long flags)
-{
- spin_unlock_irqrestore(&dma_spin_lock, flags);
-}
-
-/* These are irrelevant for Sparc DMA, but we leave it in so that
- * things can compile.
- */
-#define MAX_DMA_CHANNELS 8
-#define MAX_DMA_ADDRESS (~0UL)
-#define DMA_MODE_READ 1
-#define DMA_MODE_WRITE 2
-
-/* Useful constants */
-#define SIZE_16MB (16*1024*1024)
-#define SIZE_64K (64*1024)
-
-/* SBUS DMA controller reg offsets */
-#define DMA_CSR 0x00UL /* rw DMA control/status register 0x00 */
-#define DMA_ADDR 0x04UL /* rw DMA transfer address register 0x04 */
-#define DMA_COUNT 0x08UL /* rw DMA transfer count register 0x08 */
-#define DMA_TEST 0x0cUL /* rw DMA test/debug register 0x0c */
-
-/* DVMA chip revisions */
-enum dvma_rev {
- dvmarev0,
- dvmaesc1,
- dvmarev1,
- dvmarev2,
- dvmarev3,
- dvmarevplus,
- dvmahme
-};
-
-#define DMA_HASCOUNT(rev) ((rev)==dvmaesc1)
-
-/* Linux DMA information structure, filled during probe. */
-struct sbus_dma {
- struct sbus_dma *next;
- struct sbus_dev *sdev;
- void __iomem *regs;
-
- /* Status, misc info */
- int node; /* Prom node for this DMA device */
- int running; /* Are we doing DMA now? */
- int allocated; /* Are we "owned" by anyone yet? */
-
- /* Transfer information. */
- unsigned long addr; /* Start address of current transfer */
- int nbytes; /* Size of current transfer */
- int realbytes; /* For splitting up large transfers, etc. */
-
- /* DMA revision */
- enum dvma_rev revision;
-};
-
-extern struct sbus_dma *dma_chain;
-
-/* Broken hardware... */
-#ifdef CONFIG_SUN4
-/* Have to sort this out. Does rev0 work fine on sun4[cmd] without isbroken?
- * Or is rev0 present only on sun4 boxes? -jj */
-#define DMA_ISBROKEN(dma) ((dma)->revision == dvmarev0 || (dma)->revision == dvmarev1)
-#else
-#define DMA_ISBROKEN(dma) ((dma)->revision == dvmarev1)
-#endif
-#define DMA_ISESC1(dma) ((dma)->revision == dvmaesc1)
-
-/* Main routines in dma.c */
-extern void dvma_init(struct sbus_bus *);
-
-/* Fields in the cond_reg register */
-/* First, the version identification bits */
-#define DMA_DEVICE_ID 0xf0000000 /* Device identification bits */
-#define DMA_VERS0 0x00000000 /* Sunray DMA version */
-#define DMA_ESCV1 0x40000000 /* DMA ESC Version 1 */
-#define DMA_VERS1 0x80000000 /* DMA rev 1 */
-#define DMA_VERS2 0xa0000000 /* DMA rev 2 */
-#define DMA_VERHME 0xb0000000 /* DMA hme gate array */
-#define DMA_VERSPLUS 0x90000000 /* DMA rev 1 PLUS */
-
-#define DMA_HNDL_INTR 0x00000001 /* An IRQ needs to be handled */
-#define DMA_HNDL_ERROR 0x00000002 /* We need to take an error */
-#define DMA_FIFO_ISDRAIN 0x0000000c /* The DMA FIFO is draining */
-#define DMA_INT_ENAB 0x00000010 /* Turn on interrupts */
-#define DMA_FIFO_INV 0x00000020 /* Invalidate the FIFO */
-#define DMA_ACC_SZ_ERR 0x00000040 /* The access size was bad */
-#define DMA_FIFO_STDRAIN 0x00000040 /* DMA_VERS1 Drain the FIFO */
-#define DMA_RST_SCSI 0x00000080 /* Reset the SCSI controller */
-#define DMA_RST_ENET DMA_RST_SCSI /* Reset the ENET controller */
-#define DMA_RST_BPP DMA_RST_SCSI /* Reset the BPP controller */
-#define DMA_ST_WRITE 0x00000100 /* write from device to memory */
-#define DMA_ENABLE 0x00000200 /* Fire up DMA, handle requests */
-#define DMA_PEND_READ 0x00000400 /* DMA_VERS1/0/PLUS Pending Read */
-#define DMA_ESC_BURST 0x00000800 /* 1=16byte 0=32byte */
-#define DMA_READ_AHEAD 0x00001800 /* DMA read ahead partial longword */
-#define DMA_DSBL_RD_DRN 0x00001000 /* No EC drain on slave reads */
-#define DMA_BCNT_ENAB 0x00002000 /* If on, use the byte counter */
-#define DMA_TERM_CNTR 0x00004000 /* Terminal counter */
-#define DMA_SCSI_SBUS64 0x00008000 /* HME: Enable 64-bit SBUS mode. */
-#define DMA_CSR_DISAB 0x00010000 /* No FIFO drains during csr */
-#define DMA_SCSI_DISAB 0x00020000 /* No FIFO drains during reg */
-#define DMA_DSBL_WR_INV 0x00020000 /* No EC inval. on slave writes */
-#define DMA_ADD_ENABLE 0x00040000 /* Special ESC DVMA optimization */
-#define DMA_E_BURSTS 0x000c0000 /* ENET: SBUS r/w burst mask */
-#define DMA_E_BURST32 0x00040000 /* ENET: SBUS 32 byte r/w burst */
-#define DMA_E_BURST16 0x00000000 /* ENET: SBUS 16 byte r/w burst */
-#define DMA_BRST_SZ 0x000c0000 /* SCSI: SBUS r/w burst size */
-#define DMA_BRST64 0x00080000 /* SCSI: 64byte bursts (HME on UltraSparc only) */
-#define DMA_BRST32 0x00040000 /* SCSI/BPP: 32byte bursts */
-#define DMA_BRST16 0x00000000 /* SCSI/BPP: 16byte bursts */
-#define DMA_BRST0 0x00080000 /* SCSI: no bursts (non-HME gate arrays) */
-#define DMA_ADDR_DISAB 0x00100000 /* No FIFO drains during addr */
-#define DMA_2CLKS 0x00200000 /* Each transfer = 2 clock ticks */
-#define DMA_3CLKS 0x00400000 /* Each transfer = 3 clock ticks */
-#define DMA_EN_ENETAUI DMA_3CLKS /* Put lance into AUI-cable mode */
-#define DMA_CNTR_DISAB 0x00800000 /* No IRQ when DMA_TERM_CNTR set */
-#define DMA_AUTO_NADDR 0x01000000 /* Use "auto nxt addr" feature */
-#define DMA_SCSI_ON 0x02000000 /* Enable SCSI dma */
-#define DMA_BPP_ON DMA_SCSI_ON /* Enable BPP dma */
-#define DMA_PARITY_OFF 0x02000000 /* HME: disable parity checking */
-#define DMA_LOADED_ADDR 0x04000000 /* Address has been loaded */
-#define DMA_LOADED_NADDR 0x08000000 /* Next address has been loaded */
-#define DMA_RESET_FAS366 0x08000000 /* HME: Assert RESET to FAS366 */
-
-/* Values describing the burst-size property from the PROM */
-#define DMA_BURST1 0x01
-#define DMA_BURST2 0x02
-#define DMA_BURST4 0x04
-#define DMA_BURST8 0x08
-#define DMA_BURST16 0x10
-#define DMA_BURST32 0x20
-#define DMA_BURST64 0x40
-#define DMA_BURSTBITS 0x7f
-
-/* Determine highest possible final transfer address given a base */
-#define DMA_MAXEND(addr) (0x01000000UL-(((unsigned long)(addr))&0x00ffffffUL))
-
-/* Yes, I hack a lot of elisp in my spare time... */
-#define DMA_ERROR_P(regs) ((((regs)->cond_reg) & DMA_HNDL_ERROR))
-#define DMA_IRQ_P(regs) ((((regs)->cond_reg) & (DMA_HNDL_INTR | DMA_HNDL_ERROR)))
-#define DMA_WRITE_P(regs) ((((regs)->cond_reg) & DMA_ST_WRITE))
-#define DMA_OFF(regs) ((((regs)->cond_reg) &= (~DMA_ENABLE)))
-#define DMA_INTSOFF(regs) ((((regs)->cond_reg) &= (~DMA_INT_ENAB)))
-#define DMA_INTSON(regs) ((((regs)->cond_reg) |= (DMA_INT_ENAB)))
-#define DMA_PUNTFIFO(regs) ((((regs)->cond_reg) |= DMA_FIFO_INV))
-#define DMA_SETSTART(regs, addr) ((((regs)->st_addr) = (char *) addr))
-#define DMA_BEGINDMA_W(regs) \
- ((((regs)->cond_reg |= (DMA_ST_WRITE|DMA_ENABLE|DMA_INT_ENAB))))
-#define DMA_BEGINDMA_R(regs) \
- ((((regs)->cond_reg |= ((DMA_ENABLE|DMA_INT_ENAB)&(~DMA_ST_WRITE)))))
-
-/* For certain DMA chips, we need to disable ints upon irq entry
- * and turn them back on when we are done. So in any ESP interrupt
- * handler you *must* call DMA_IRQ_ENTRY upon entry and DMA_IRQ_EXIT
- * when leaving the handler. You have been warned...
- */
-#define DMA_IRQ_ENTRY(dma, dregs) do { \
- if(DMA_ISBROKEN(dma)) DMA_INTSOFF(dregs); \
- } while (0)
-
-#define DMA_IRQ_EXIT(dma, dregs) do { \
- if(DMA_ISBROKEN(dma)) DMA_INTSON(dregs); \
- } while(0)
-
-#if 0 /* P3 this stuff is inline in ledma.c:init_restart_ledma() */
-/* Pause until counter runs out or BIT isn't set in the DMA condition
- * register.
- */
-static inline void sparc_dma_pause(struct sparc_dma_registers *regs,
- unsigned long bit)
-{
- int ctr = 50000; /* Let's find some bugs ;) */
-
- /* Busy wait until the bit is not set any more */
- while((regs->cond_reg&bit) && (ctr>0)) {
- ctr--;
- __delay(5);
- }
-
- /* Check for bogus outcome. */
- if(!ctr)
- panic("DMA timeout");
-}
-
-/* Reset the friggin' thing... */
-#define DMA_RESET(dma) do { \
- struct sparc_dma_registers *regs = dma->regs; \
- /* Let the current FIFO drain itself */ \
- sparc_dma_pause(regs, (DMA_FIFO_ISDRAIN)); \
- /* Reset the logic */ \
- regs->cond_reg |= (DMA_RST_SCSI); /* assert */ \
- __delay(400); /* let the bits set ;) */ \
- regs->cond_reg &= ~(DMA_RST_SCSI); /* de-assert */ \
- sparc_dma_enable_interrupts(regs); /* Re-enable interrupts */ \
- /* Enable FAST transfers if available */ \
- if(dma->revision>dvmarev1) regs->cond_reg |= DMA_3CLKS; \
- dma->running = 0; \
-} while(0)
-#endif
-
-#define for_each_dvma(dma) \
- for((dma) = dma_chain; (dma); (dma) = (dma)->next)
-
-extern int get_dma_list(char *);
-extern int request_dma(unsigned int, __const__ char *);
-extern void free_dma(unsigned int);
-
-/* From PCI */
-
-#ifdef CONFIG_PCI
-extern int isa_dma_bridge_buggy;
-#else
-#define isa_dma_bridge_buggy (0)
-#endif
-
-/* Routines for data transfer buffers. */
-BTFIXUPDEF_CALL(char *, mmu_lockarea, char *, unsigned long)
-BTFIXUPDEF_CALL(void, mmu_unlockarea, char *, unsigned long)
-
-#define mmu_lockarea(vaddr,len) BTFIXUP_CALL(mmu_lockarea)(vaddr,len)
-#define mmu_unlockarea(vaddr,len) BTFIXUP_CALL(mmu_unlockarea)(vaddr,len)
-
-/* These are implementations for sbus_map_sg/sbus_unmap_sg... collapse later */
-BTFIXUPDEF_CALL(__u32, mmu_get_scsi_one, char *, unsigned long, struct sbus_bus *sbus)
-BTFIXUPDEF_CALL(void, mmu_get_scsi_sgl, struct scatterlist *, int, struct sbus_bus *sbus)
-BTFIXUPDEF_CALL(void, mmu_release_scsi_one, __u32, unsigned long, struct sbus_bus *sbus)
-BTFIXUPDEF_CALL(void, mmu_release_scsi_sgl, struct scatterlist *, int, struct sbus_bus *sbus)
-
-#define mmu_get_scsi_one(vaddr,len,sbus) BTFIXUP_CALL(mmu_get_scsi_one)(vaddr,len,sbus)
-#define mmu_get_scsi_sgl(sg,sz,sbus) BTFIXUP_CALL(mmu_get_scsi_sgl)(sg,sz,sbus)
-#define mmu_release_scsi_one(vaddr,len,sbus) BTFIXUP_CALL(mmu_release_scsi_one)(vaddr,len,sbus)
-#define mmu_release_scsi_sgl(sg,sz,sbus) BTFIXUP_CALL(mmu_release_scsi_sgl)(sg,sz,sbus)
-
-/*
- * mmu_map/unmap are provided by iommu/iounit; Invalid to call on IIep.
- *
- * The mmu_map_dma_area establishes two mappings in one go.
- * These mappings point to pages normally mapped at 'va' (linear address).
- * First mapping is for CPU visible address at 'a', uncached.
- * This is an alias, but it works because it is an uncached mapping.
- * Second mapping is for device visible address, or "bus" address.
- * The bus address is returned at '*pba'.
- *
- * These functions seem distinct, but are hard to split. On sun4c,
- * at least for now, 'a' is equal to bus address, and retured in *pba.
- * On sun4m, page attributes depend on the CPU type, so we have to
- * know if we are mapping RAM or I/O, so it has to be an additional argument
- * to a separate mapping function for CPU visible mappings.
- */
-BTFIXUPDEF_CALL(int, mmu_map_dma_area, dma_addr_t *, unsigned long, unsigned long, int len)
-BTFIXUPDEF_CALL(struct page *, mmu_translate_dvma, unsigned long busa)
-BTFIXUPDEF_CALL(void, mmu_unmap_dma_area, unsigned long busa, int len)
-
-#define mmu_map_dma_area(pba,va,a,len) BTFIXUP_CALL(mmu_map_dma_area)(pba,va,a,len)
-#define mmu_unmap_dma_area(ba,len) BTFIXUP_CALL(mmu_unmap_dma_area)(ba,len)
-#define mmu_translate_dvma(ba) BTFIXUP_CALL(mmu_translate_dvma)(ba)
-
-#endif /* !(_ASM_SPARC_DMA_H) */
diff --git a/arch/sparc/include/asm/dma_64.h b/arch/sparc/include/asm/dma_64.h
deleted file mode 100644
index 46a8aec..0000000
--- a/arch/sparc/include/asm/dma_64.h
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * include/asm/dma.h
- *
- * Copyright 1996 (C) David S. Miller (davem@caip.rutgers.edu)
- */
-
-#ifndef _ASM_SPARC64_DMA_H
-#define _ASM_SPARC64_DMA_H
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/spinlock.h>
-
-#include <asm/sbus.h>
-#include <asm/delay.h>
-#include <asm/oplib.h>
-
-/* These are irrelevant for Sparc DMA, but we leave it in so that
- * things can compile.
- */
-#define MAX_DMA_CHANNELS 8
-#define DMA_MODE_READ 1
-#define DMA_MODE_WRITE 2
-#define MAX_DMA_ADDRESS (~0UL)
-
-/* Useful constants */
-#define SIZE_16MB (16*1024*1024)
-#define SIZE_64K (64*1024)
-
-/* SBUS DMA controller reg offsets */
-#define DMA_CSR 0x00UL /* rw DMA control/status register 0x00 */
-#define DMA_ADDR 0x04UL /* rw DMA transfer address register 0x04 */
-#define DMA_COUNT 0x08UL /* rw DMA transfer count register 0x08 */
-#define DMA_TEST 0x0cUL /* rw DMA test/debug register 0x0c */
-
-/* DVMA chip revisions */
-enum dvma_rev {
- dvmarev0,
- dvmaesc1,
- dvmarev1,
- dvmarev2,
- dvmarev3,
- dvmarevplus,
- dvmahme
-};
-
-#define DMA_HASCOUNT(rev) ((rev)==dvmaesc1)
-
-/* Linux DMA information structure, filled during probe. */
-struct sbus_dma {
- struct sbus_dma *next;
- struct sbus_dev *sdev;
- void __iomem *regs;
-
- /* Status, misc info */
- int node; /* Prom node for this DMA device */
- int running; /* Are we doing DMA now? */
- int allocated; /* Are we "owned" by anyone yet? */
-
- /* Transfer information. */
- u32 addr; /* Start address of current transfer */
- int nbytes; /* Size of current transfer */
- int realbytes; /* For splitting up large transfers, etc. */
-
- /* DMA revision */
- enum dvma_rev revision;
-};
-
-extern struct sbus_dma *dma_chain;
-
-/* Broken hardware... */
-#define DMA_ISBROKEN(dma) ((dma)->revision == dvmarev1)
-#define DMA_ISESC1(dma) ((dma)->revision == dvmaesc1)
-
-/* Main routines in dma.c */
-extern void dvma_init(struct sbus_bus *);
-
-/* Fields in the cond_reg register */
-/* First, the version identification bits */
-#define DMA_DEVICE_ID 0xf0000000 /* Device identification bits */
-#define DMA_VERS0 0x00000000 /* Sunray DMA version */
-#define DMA_ESCV1 0x40000000 /* DMA ESC Version 1 */
-#define DMA_VERS1 0x80000000 /* DMA rev 1 */
-#define DMA_VERS2 0xa0000000 /* DMA rev 2 */
-#define DMA_VERHME 0xb0000000 /* DMA hme gate array */
-#define DMA_VERSPLUS 0x90000000 /* DMA rev 1 PLUS */
-
-#define DMA_HNDL_INTR 0x00000001 /* An IRQ needs to be handled */
-#define DMA_HNDL_ERROR 0x00000002 /* We need to take an error */
-#define DMA_FIFO_ISDRAIN 0x0000000c /* The DMA FIFO is draining */
-#define DMA_INT_ENAB 0x00000010 /* Turn on interrupts */
-#define DMA_FIFO_INV 0x00000020 /* Invalidate the FIFO */
-#define DMA_ACC_SZ_ERR 0x00000040 /* The access size was bad */
-#define DMA_FIFO_STDRAIN 0x00000040 /* DMA_VERS1 Drain the FIFO */
-#define DMA_RST_SCSI 0x00000080 /* Reset the SCSI controller */
-#define DMA_RST_ENET DMA_RST_SCSI /* Reset the ENET controller */
-#define DMA_ST_WRITE 0x00000100 /* write from device to memory */
-#define DMA_ENABLE 0x00000200 /* Fire up DMA, handle requests */
-#define DMA_PEND_READ 0x00000400 /* DMA_VERS1/0/PLUS Pending Read */
-#define DMA_ESC_BURST 0x00000800 /* 1=16byte 0=32byte */
-#define DMA_READ_AHEAD 0x00001800 /* DMA read ahead partial longword */
-#define DMA_DSBL_RD_DRN 0x00001000 /* No EC drain on slave reads */
-#define DMA_BCNT_ENAB 0x00002000 /* If on, use the byte counter */
-#define DMA_TERM_CNTR 0x00004000 /* Terminal counter */
-#define DMA_SCSI_SBUS64 0x00008000 /* HME: Enable 64-bit SBUS mode. */
-#define DMA_CSR_DISAB 0x00010000 /* No FIFO drains during csr */
-#define DMA_SCSI_DISAB 0x00020000 /* No FIFO drains during reg */
-#define DMA_DSBL_WR_INV 0x00020000 /* No EC inval. on slave writes */
-#define DMA_ADD_ENABLE 0x00040000 /* Special ESC DVMA optimization */
-#define DMA_E_BURSTS 0x000c0000 /* ENET: SBUS r/w burst mask */
-#define DMA_E_BURST32 0x00040000 /* ENET: SBUS 32 byte r/w burst */
-#define DMA_E_BURST16 0x00000000 /* ENET: SBUS 16 byte r/w burst */
-#define DMA_BRST_SZ 0x000c0000 /* SCSI: SBUS r/w burst size */
-#define DMA_BRST64 0x000c0000 /* SCSI: 64byte bursts (HME on UltraSparc only) */
-#define DMA_BRST32 0x00040000 /* SCSI: 32byte bursts */
-#define DMA_BRST16 0x00000000 /* SCSI: 16byte bursts */
-#define DMA_BRST0 0x00080000 /* SCSI: no bursts (non-HME gate arrays) */
-#define DMA_ADDR_DISAB 0x00100000 /* No FIFO drains during addr */
-#define DMA_2CLKS 0x00200000 /* Each transfer = 2 clock ticks */
-#define DMA_3CLKS 0x00400000 /* Each transfer = 3 clock ticks */
-#define DMA_EN_ENETAUI DMA_3CLKS /* Put lance into AUI-cable mode */
-#define DMA_CNTR_DISAB 0x00800000 /* No IRQ when DMA_TERM_CNTR set */
-#define DMA_AUTO_NADDR 0x01000000 /* Use "auto nxt addr" feature */
-#define DMA_SCSI_ON 0x02000000 /* Enable SCSI dma */
-#define DMA_PARITY_OFF 0x02000000 /* HME: disable parity checking */
-#define DMA_LOADED_ADDR 0x04000000 /* Address has been loaded */
-#define DMA_LOADED_NADDR 0x08000000 /* Next address has been loaded */
-#define DMA_RESET_FAS366 0x08000000 /* HME: Assert RESET to FAS366 */
-
-/* Values describing the burst-size property from the PROM */
-#define DMA_BURST1 0x01
-#define DMA_BURST2 0x02
-#define DMA_BURST4 0x04
-#define DMA_BURST8 0x08
-#define DMA_BURST16 0x10
-#define DMA_BURST32 0x20
-#define DMA_BURST64 0x40
-#define DMA_BURSTBITS 0x7f
-
-/* Determine highest possible final transfer address given a base */
-#define DMA_MAXEND(addr) (0x01000000UL-(((unsigned long)(addr))&0x00ffffffUL))
-
-/* Yes, I hack a lot of elisp in my spare time... */
-#define DMA_ERROR_P(regs) ((sbus_readl((regs) + DMA_CSR) & DMA_HNDL_ERROR))
-#define DMA_IRQ_P(regs) ((sbus_readl((regs) + DMA_CSR)) & (DMA_HNDL_INTR | DMA_HNDL_ERROR))
-#define DMA_WRITE_P(regs) ((sbus_readl((regs) + DMA_CSR) & DMA_ST_WRITE))
-#define DMA_OFF(__regs) \
-do { u32 tmp = sbus_readl((__regs) + DMA_CSR); \
- tmp &= ~DMA_ENABLE; \
- sbus_writel(tmp, (__regs) + DMA_CSR); \
-} while(0)
-#define DMA_INTSOFF(__regs) \
-do { u32 tmp = sbus_readl((__regs) + DMA_CSR); \
- tmp &= ~DMA_INT_ENAB; \
- sbus_writel(tmp, (__regs) + DMA_CSR); \
-} while(0)
-#define DMA_INTSON(__regs) \
-do { u32 tmp = sbus_readl((__regs) + DMA_CSR); \
- tmp |= DMA_INT_ENAB; \
- sbus_writel(tmp, (__regs) + DMA_CSR); \
-} while(0)
-#define DMA_PUNTFIFO(__regs) \
-do { u32 tmp = sbus_readl((__regs) + DMA_CSR); \
- tmp |= DMA_FIFO_INV; \
- sbus_writel(tmp, (__regs) + DMA_CSR); \
-} while(0)
-#define DMA_SETSTART(__regs, __addr) \
- sbus_writel((u32)(__addr), (__regs) + DMA_ADDR);
-#define DMA_BEGINDMA_W(__regs) \
-do { u32 tmp = sbus_readl((__regs) + DMA_CSR); \
- tmp |= (DMA_ST_WRITE|DMA_ENABLE|DMA_INT_ENAB); \
- sbus_writel(tmp, (__regs) + DMA_CSR); \
-} while(0)
-#define DMA_BEGINDMA_R(__regs) \
-do { u32 tmp = sbus_readl((__regs) + DMA_CSR); \
- tmp |= (DMA_ENABLE|DMA_INT_ENAB); \
- tmp &= ~DMA_ST_WRITE; \
- sbus_writel(tmp, (__regs) + DMA_CSR); \
-} while(0)
-
-/* For certain DMA chips, we need to disable ints upon irq entry
- * and turn them back on when we are done. So in any ESP interrupt
- * handler you *must* call DMA_IRQ_ENTRY upon entry and DMA_IRQ_EXIT
- * when leaving the handler. You have been warned...
- */
-#define DMA_IRQ_ENTRY(dma, dregs) do { \
- if(DMA_ISBROKEN(dma)) DMA_INTSOFF(dregs); \
- } while (0)
-
-#define DMA_IRQ_EXIT(dma, dregs) do { \
- if(DMA_ISBROKEN(dma)) DMA_INTSON(dregs); \
- } while(0)
-
-#define for_each_dvma(dma) \
- for((dma) = dma_chain; (dma); (dma) = (dma)->next)
-
-/* From PCI */
-
-#ifdef CONFIG_PCI
-extern int isa_dma_bridge_buggy;
-#else
-#define isa_dma_bridge_buggy (0)
-#endif
-
-#endif /* !(_ASM_SPARC64_DMA_H) */
diff --git a/arch/sparc/include/asm/ebus.h b/arch/sparc/include/asm/ebus.h
deleted file mode 100644
index 83a6d16..0000000
--- a/arch/sparc/include/asm/ebus.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef ___ASM_SPARC_EBUS_H
-#define ___ASM_SPARC_EBUS_H
-#if defined(__sparc__) && defined(__arch64__)
-#include <asm/ebus_64.h>
-#else
-#include <asm/ebus_32.h>
-#endif
-#endif
diff --git a/arch/sparc/include/asm/ebus_32.h b/arch/sparc/include/asm/ebus_32.h
deleted file mode 100644
index f91f0b2..0000000
--- a/arch/sparc/include/asm/ebus_32.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * ebus.h: PCI to Ebus pseudo driver software state.
- *
- * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
- *
- * Adopted for sparc by V. Roganov and G. Raiko.
- */
-
-#ifndef __SPARC_EBUS_H
-#define __SPARC_EBUS_H
-
-#ifndef _LINUX_IOPORT_H
-#include <linux/ioport.h>
-#endif
-#include <linux/of_device.h>
-#include <asm/oplib.h>
-#include <asm/prom.h>
-
-struct linux_ebus_child {
- struct linux_ebus_child *next;
- struct linux_ebus_device *parent;
- struct linux_ebus *bus;
- struct device_node *prom_node;
- struct resource resource[PROMREG_MAX];
- int num_addrs;
- unsigned int irqs[PROMINTR_MAX];
- int num_irqs;
-};
-
-struct linux_ebus_device {
- struct of_device ofdev;
- struct linux_ebus_device *next;
- struct linux_ebus_child *children;
- struct linux_ebus *bus;
- struct device_node *prom_node;
- struct resource resource[PROMREG_MAX];
- int num_addrs;
- unsigned int irqs[PROMINTR_MAX];
- int num_irqs;
-};
-#define to_ebus_device(d) container_of(d, struct linux_ebus_device, ofdev.dev)
-
-struct linux_ebus {
- struct of_device ofdev;
- struct linux_ebus *next;
- struct linux_ebus_device *devices;
- struct linux_pbm_info *parent;
- struct pci_dev *self;
- struct device_node *prom_node;
-};
-#define to_ebus(d) container_of(d, struct linux_ebus, ofdev.dev)
-
-struct linux_ebus_dma {
- unsigned int dcsr;
- unsigned int dacr;
- unsigned int dbcr;
-};
-
-#define EBUS_DCSR_INT_PEND 0x00000001
-#define EBUS_DCSR_ERR_PEND 0x00000002
-#define EBUS_DCSR_DRAIN 0x00000004
-#define EBUS_DCSR_INT_EN 0x00000010
-#define EBUS_DCSR_RESET 0x00000080
-#define EBUS_DCSR_WRITE 0x00000100
-#define EBUS_DCSR_EN_DMA 0x00000200
-#define EBUS_DCSR_CYC_PEND 0x00000400
-#define EBUS_DCSR_DIAG_RD_DONE 0x00000800
-#define EBUS_DCSR_DIAG_WR_DONE 0x00001000
-#define EBUS_DCSR_EN_CNT 0x00002000
-#define EBUS_DCSR_TC 0x00004000
-#define EBUS_DCSR_DIS_CSR_DRN 0x00010000
-#define EBUS_DCSR_BURST_SZ_MASK 0x000c0000
-#define EBUS_DCSR_BURST_SZ_1 0x00080000
-#define EBUS_DCSR_BURST_SZ_4 0x00000000
-#define EBUS_DCSR_BURST_SZ_8 0x00040000
-#define EBUS_DCSR_BURST_SZ_16 0x000c0000
-#define EBUS_DCSR_DIAG_EN 0x00100000
-#define EBUS_DCSR_DIS_ERR_PEND 0x00400000
-#define EBUS_DCSR_TCI_DIS 0x00800000
-#define EBUS_DCSR_EN_NEXT 0x01000000
-#define EBUS_DCSR_DMA_ON 0x02000000
-#define EBUS_DCSR_A_LOADED 0x04000000
-#define EBUS_DCSR_NA_LOADED 0x08000000
-#define EBUS_DCSR_DEV_ID_MASK 0xf0000000
-
-extern struct linux_ebus *ebus_chain;
-
-extern void ebus_init(void);
-
-#define for_each_ebus(bus) \
- for((bus) = ebus_chain; (bus); (bus) = (bus)->next)
-
-#define for_each_ebusdev(dev, bus) \
- for((dev) = (bus)->devices; (dev); (dev) = (dev)->next)
-
-#define for_each_edevchild(dev, child) \
- for((child) = (dev)->children; (child); (child) = (child)->next)
-
-#endif /* !(__SPARC_EBUS_H) */
diff --git a/arch/sparc/include/asm/ebus_64.h b/arch/sparc/include/asm/ebus_64.h
deleted file mode 100644
index 14c6a11..0000000
--- a/arch/sparc/include/asm/ebus_64.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * ebus.h: PCI to Ebus pseudo driver software state.
- *
- * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
- * Copyright (C) 1999 David S. Miller (davem@redhat.com)
- */
-
-#ifndef __SPARC64_EBUS_H
-#define __SPARC64_EBUS_H
-
-#include <linux/of_device.h>
-
-#include <asm/oplib.h>
-#include <asm/prom.h>
-
-struct linux_ebus_child {
- struct linux_ebus_child *next;
- struct linux_ebus_device *parent;
- struct linux_ebus *bus;
- struct device_node *prom_node;
- struct resource resource[PROMREG_MAX];
- int num_addrs;
- unsigned int irqs[PROMINTR_MAX];
- int num_irqs;
-};
-
-struct linux_ebus_device {
- struct of_device ofdev;
- struct linux_ebus_device *next;
- struct linux_ebus_child *children;
- struct linux_ebus *bus;
- struct device_node *prom_node;
- struct resource resource[PROMREG_MAX];
- int num_addrs;
- unsigned int irqs[PROMINTR_MAX];
- int num_irqs;
-};
-#define to_ebus_device(d) container_of(d, struct linux_ebus_device, ofdev.dev)
-
-struct linux_ebus {
- struct of_device ofdev;
- struct linux_ebus *next;
- struct linux_ebus_device *devices;
- struct pci_dev *self;
- int index;
- int is_rio;
- struct device_node *prom_node;
-};
-#define to_ebus(d) container_of(d, struct linux_ebus, ofdev.dev)
-
-struct ebus_dma_info {
- spinlock_t lock;
- void __iomem *regs;
-
- unsigned int flags;
-#define EBUS_DMA_FLAG_USE_EBDMA_HANDLER 0x00000001
-#define EBUS_DMA_FLAG_TCI_DISABLE 0x00000002
-
- /* These are only valid is EBUS_DMA_FLAG_USE_EBDMA_HANDLER is
- * set.
- */
- void (*callback)(struct ebus_dma_info *p, int event, void *cookie);
- void *client_cookie;
- unsigned int irq;
-#define EBUS_DMA_EVENT_ERROR 1
-#define EBUS_DMA_EVENT_DMA 2
-#define EBUS_DMA_EVENT_DEVICE 4
-
- unsigned char name[64];
-};
-
-extern int ebus_dma_register(struct ebus_dma_info *p);
-extern int ebus_dma_irq_enable(struct ebus_dma_info *p, int on);
-extern void ebus_dma_unregister(struct ebus_dma_info *p);
-extern int ebus_dma_request(struct ebus_dma_info *p, dma_addr_t bus_addr,
- size_t len);
-extern void ebus_dma_prepare(struct ebus_dma_info *p, int write);
-extern unsigned int ebus_dma_residue(struct ebus_dma_info *p);
-extern unsigned int ebus_dma_addr(struct ebus_dma_info *p);
-extern void ebus_dma_enable(struct ebus_dma_info *p, int on);
-
-extern struct linux_ebus *ebus_chain;
-
-extern void ebus_init(void);
-
-#define for_each_ebus(bus) \
- for((bus) = ebus_chain; (bus); (bus) = (bus)->next)
-
-#define for_each_ebusdev(dev, bus) \
- for((dev) = (bus)->devices; (dev); (dev) = (dev)->next)
-
-#define for_each_edevchild(dev, child) \
- for((child) = (dev)->children; (child); (child) = (child)->next)
-
-#endif /* !(__SPARC64_EBUS_H) */
diff --git a/arch/sparc/include/asm/ebus_dma.h b/arch/sparc/include/asm/ebus_dma.h
new file mode 100644
index 0000000..f07a5b5
--- /dev/null
+++ b/arch/sparc/include/asm/ebus_dma.h
@@ -0,0 +1,35 @@
+#ifndef __ASM_SPARC_EBUS_DMA_H
+#define __ASM_SPARC_EBUS_DMA_H
+
+struct ebus_dma_info {
+ spinlock_t lock;
+ void __iomem *regs;
+
+ unsigned int flags;
+#define EBUS_DMA_FLAG_USE_EBDMA_HANDLER 0x00000001
+#define EBUS_DMA_FLAG_TCI_DISABLE 0x00000002
+
+ /* These are only valid is EBUS_DMA_FLAG_USE_EBDMA_HANDLER is
+ * set.
+ */
+ void (*callback)(struct ebus_dma_info *p, int event, void *cookie);
+ void *client_cookie;
+ unsigned int irq;
+#define EBUS_DMA_EVENT_ERROR 1
+#define EBUS_DMA_EVENT_DMA 2
+#define EBUS_DMA_EVENT_DEVICE 4
+
+ unsigned char name[64];
+};
+
+extern int ebus_dma_register(struct ebus_dma_info *p);
+extern int ebus_dma_irq_enable(struct ebus_dma_info *p, int on);
+extern void ebus_dma_unregister(struct ebus_dma_info *p);
+extern int ebus_dma_request(struct ebus_dma_info *p, dma_addr_t bus_addr,
+ size_t len);
+extern void ebus_dma_prepare(struct ebus_dma_info *p, int write);
+extern unsigned int ebus_dma_residue(struct ebus_dma_info *p);
+extern unsigned int ebus_dma_addr(struct ebus_dma_info *p);
+extern void ebus_dma_enable(struct ebus_dma_info *p, int on);
+
+#endif /* __ASM_SPARC_EBUS_DMA_H */
diff --git a/arch/sparc/include/asm/elf_32.h b/arch/sparc/include/asm/elf_32.h
index d043f80..b7ab605 100644
--- a/arch/sparc/include/asm/elf_32.h
+++ b/arch/sparc/include/asm/elf_32.h
@@ -105,11 +105,8 @@
#define ELF_DATA ELFDATA2MSB
#define USE_ELF_CORE_DUMP
-#ifndef CONFIG_SUN4
+
#define ELF_EXEC_PAGESIZE 4096
-#else
-#define ELF_EXEC_PAGESIZE 8192
-#endif
/* This is the location that an ET_DYN program is loaded if exec'ed. Typical
@@ -126,7 +123,7 @@
/* Sun4c has none of the capabilities, most sun4m's have them all.
* XXX This is gross, set some global variable at boot time. -DaveM
*/
-#define ELF_HWCAP ((ARCH_SUN4C_SUN4) ? 0 : \
+#define ELF_HWCAP ((ARCH_SUN4C) ? 0 : \
(HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | \
HWCAP_SPARC_SWAP | \
((srmmu_modtype != Cypress && \
diff --git a/arch/sparc/include/asm/fhc.h b/arch/sparc/include/asm/fhc.h
index 788cbc4..57f1b30 100644
--- a/arch/sparc/include/asm/fhc.h
+++ b/arch/sparc/include/asm/fhc.h
@@ -1,5 +1,4 @@
-/*
- * fhc.h: Structures for central/fhc pseudo driver on Sunfire/Starfire/Wildfire.
+/* fhc.h: FHC and Clock board register definitions.
*
* Copyright (C) 1997, 1999 David S. Miller (davem@redhat.com)
*/
@@ -7,14 +6,6 @@
#ifndef _SPARC64_FHC_H
#define _SPARC64_FHC_H
-#include <linux/timer.h>
-
-#include <asm/oplib.h>
-#include <asm/prom.h>
-#include <asm/upa.h>
-
-struct linux_fhc;
-
/* Clock board register offsets. */
#define CLOCK_CTRL 0x00UL /* Main control */
#define CLOCK_STAT1 0x10UL /* Status one */
@@ -29,21 +20,7 @@
#define CLOCK_CTRL_MLED 0x02 /* Mid LED, 1 == on */
#define CLOCK_CTRL_RLED 0x01 /* RIght LED, 1 == on */
-struct linux_central {
- struct linux_fhc *child;
- unsigned long cfreg;
- unsigned long clkregs;
- unsigned long clkver;
- int slots;
- struct device_node *prom_node;
-
- struct linux_prom_ranges central_ranges[PROMREG_MAX];
- int num_central_ranges;
-};
-
/* Firehose controller register offsets */
-struct fhc_regs {
- unsigned long pregs; /* FHC internal regs */
#define FHC_PREGS_ID 0x00UL /* FHC ID */
#define FHC_ID_VERS 0xf0000000 /* Version of this FHC */
#define FHC_ID_PARTID 0x0ffff000 /* Part ID code (0x0f9f == FHC) */
@@ -90,32 +67,14 @@
#define FHC_JTAG_CTRL_MENAB 0x80000000 /* Indicates this is JTAG Master */
#define FHC_JTAG_CTRL_MNONE 0x40000000 /* Indicates no JTAG Master present */
#define FHC_PREGS_JCMD 0x100UL /* FHC JTAG Command Register */
- unsigned long ireg; /* FHC IGN reg */
#define FHC_IREG_IGN 0x00UL /* This FHC's IGN */
- unsigned long ffregs; /* FHC fanfail regs */
#define FHC_FFREGS_IMAP 0x00UL /* FHC Fanfail IMAP */
#define FHC_FFREGS_ICLR 0x10UL /* FHC Fanfail ICLR */
- unsigned long sregs; /* FHC system regs */
#define FHC_SREGS_IMAP 0x00UL /* FHC System IMAP */
#define FHC_SREGS_ICLR 0x10UL /* FHC System ICLR */
- unsigned long uregs; /* FHC uart regs */
#define FHC_UREGS_IMAP 0x00UL /* FHC Uart IMAP */
#define FHC_UREGS_ICLR 0x10UL /* FHC Uart ICLR */
- unsigned long tregs; /* FHC TOD regs */
#define FHC_TREGS_IMAP 0x00UL /* FHC TOD IMAP */
#define FHC_TREGS_ICLR 0x10UL /* FHC TOD ICLR */
-};
-
-struct linux_fhc {
- struct linux_fhc *next;
- struct linux_central *parent; /* NULL if not central FHC */
- struct fhc_regs fhc_regs;
- int board;
- int jtag_master;
- struct device_node *prom_node;
-
- struct linux_prom_ranges fhc_ranges[PROMREG_MAX];
- int num_fhc_ranges;
-};
#endif /* !(_SPARC64_FHC_H) */
diff --git a/arch/sparc/include/asm/floppy_32.h b/arch/sparc/include/asm/floppy_32.h
index ae3f00b..c792830 100644
--- a/arch/sparc/include/asm/floppy_32.h
+++ b/arch/sparc/include/asm/floppy_32.h
@@ -6,6 +6,9 @@
#ifndef __ASM_SPARC_FLOPPY_H
#define __ASM_SPARC_FLOPPY_H
+#include <linux/of.h>
+#include <linux/of_device.h>
+
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/system.h>
@@ -343,7 +346,7 @@
r.flags = fd_regs[0].which_io;
r.start = fd_regs[0].phys_addr;
sun_fdc = (struct sun_flpy_controller *)
- sbus_ioremap(&r, 0, fd_regs[0].reg_size, "floppy");
+ of_ioremap(&r, 0, fd_regs[0].reg_size, "floppy");
/* Last minute sanity check... */
if(sun_fdc->status_82072 == 0xff) {
@@ -385,4 +388,15 @@
#define EXTRA_FLOPPY_PARAMS
+static DEFINE_SPINLOCK(dma_spin_lock);
+
+#define claim_dma_lock() \
+({ unsigned long flags; \
+ spin_lock_irqsave(&dma_spin_lock, flags); \
+ flags; \
+})
+
+#define release_dma_lock(__flags) \
+ spin_unlock_irqrestore(&dma_spin_lock, __flags);
+
#endif /* !(__ASM_SPARC_FLOPPY_H) */
diff --git a/arch/sparc/include/asm/floppy_64.h b/arch/sparc/include/asm/floppy_64.h
index c39db10..36439d6 100644
--- a/arch/sparc/include/asm/floppy_64.h
+++ b/arch/sparc/include/asm/floppy_64.h
@@ -1,6 +1,6 @@
/* floppy.h: Sparc specific parts of the Floppy driver.
*
- * Copyright (C) 1996, 2007 David S. Miller (davem@davemloft.net)
+ * Copyright (C) 1996, 2007, 2008 David S. Miller (davem@davemloft.net)
* Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*
* Ultra/PCI support added: Sep 1997 Eddie C. Dost (ecd@skynet.be)
@@ -9,18 +9,11 @@
#ifndef __ASM_SPARC64_FLOPPY_H
#define __ASM_SPARC64_FLOPPY_H
-#include <linux/init.h>
-#include <linux/pci.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/dma-mapping.h>
-#include <asm/page.h>
-#include <asm/pgtable.h>
-#include <asm/system.h>
-#include <asm/idprom.h>
-#include <asm/oplib.h>
#include <asm/auxio.h>
-#include <asm/sbus.h>
-#include <asm/irq.h>
-
/*
* Define this to enable exchanging drive 0 and 1 if only drive 1 is
@@ -50,7 +43,7 @@
/* You'll only ever find one controller on an Ultra anyways. */
static struct sun_flpy_controller *sun_fdc = (struct sun_flpy_controller *)-1;
unsigned long fdc_status;
-static struct sbus_dev *floppy_sdev = NULL;
+static struct of_device *floppy_op = NULL;
struct sun_floppy_ops {
unsigned char (*fd_inb) (unsigned long port);
@@ -291,12 +284,11 @@
return 0;
}
-#ifdef CONFIG_PCI
-#include <asm/ebus.h>
+#include <asm/ebus_dma.h>
#include <asm/ns87303.h>
static struct ebus_dma_info sun_pci_fd_ebus_dma;
-static struct pci_dev *sun_pci_ebus_dev;
+static struct device *sun_floppy_dev;
static int sun_pci_broken_drive = -1;
struct sun_pci_dma_op {
@@ -377,7 +369,7 @@
sun_pci_dma_pending.addr = -1U;
sun_pci_dma_current.addr =
- pci_map_single(sun_pci_ebus_dev,
+ dma_map_single(sun_floppy_dev,
sun_pci_dma_current.buf,
sun_pci_dma_current.len,
sun_pci_dma_current.direction);
@@ -394,7 +386,7 @@
{
ebus_dma_enable(&sun_pci_fd_ebus_dma, 0);
if (sun_pci_dma_current.addr != -1U)
- pci_unmap_single(sun_pci_ebus_dev,
+ dma_unmap_single(sun_floppy_dev,
sun_pci_dma_current.addr,
sun_pci_dma_current.len,
sun_pci_dma_current.direction);
@@ -404,9 +396,9 @@
static void sun_pci_fd_set_dma_mode(int mode)
{
if (mode == DMA_MODE_WRITE)
- sun_pci_dma_pending.direction = PCI_DMA_TODEVICE;
+ sun_pci_dma_pending.direction = DMA_TO_DEVICE;
else
- sun_pci_dma_pending.direction = PCI_DMA_FROMDEVICE;
+ sun_pci_dma_pending.direction = DMA_FROM_DEVICE;
ebus_dma_prepare(&sun_pci_fd_ebus_dma, mode != DMA_MODE_WRITE);
}
@@ -538,80 +530,84 @@
#undef MSR
#undef DOR
-#endif /* CONFIG_PCI */
-
-#ifdef CONFIG_PCI
-static int __init ebus_fdthree_p(struct linux_ebus_device *edev)
+static int __init ebus_fdthree_p(struct device_node *dp)
{
- if (!strcmp(edev->prom_node->name, "fdthree"))
+ if (!strcmp(dp->name, "fdthree"))
return 1;
- if (!strcmp(edev->prom_node->name, "floppy")) {
+ if (!strcmp(dp->name, "floppy")) {
const char *compat;
- compat = of_get_property(edev->prom_node,
- "compatible", NULL);
+ compat = of_get_property(dp, "compatible", NULL);
if (compat && !strcmp(compat, "fdthree"))
return 1;
}
return 0;
}
-#endif
static unsigned long __init sun_floppy_init(void)
{
- char state[128];
- struct sbus_bus *bus;
- struct sbus_dev *sdev = NULL;
static int initialized = 0;
+ struct device_node *dp;
+ struct of_device *op;
+ const char *prop;
+ char state[128];
if (initialized)
return sun_floppy_types[0];
initialized = 1;
- for_all_sbusdev (sdev, bus) {
- if (!strcmp(sdev->prom_name, "SUNW,fdtwo"))
+ op = NULL;
+
+ for_each_node_by_name(dp, "SUNW,fdtwo") {
+ if (strcmp(dp->parent->name, "sbus"))
+ continue;
+ op = of_find_device_by_node(dp);
+ if (op)
break;
}
- if(sdev) {
- floppy_sdev = sdev;
- FLOPPY_IRQ = sdev->irqs[0];
+ if (op) {
+ floppy_op = op;
+ FLOPPY_IRQ = op->irqs[0];
} else {
-#ifdef CONFIG_PCI
- struct linux_ebus *ebus;
- struct linux_ebus_device *edev = NULL;
- unsigned long config = 0;
+ struct device_node *ebus_dp;
void __iomem *auxio_reg;
const char *state_prop;
+ unsigned long config;
- for_each_ebus(ebus) {
- for_each_ebusdev(edev, ebus) {
- if (ebus_fdthree_p(edev))
- goto ebus_done;
+ dp = NULL;
+ for_each_node_by_name(ebus_dp, "ebus") {
+ for (dp = ebus_dp->child; dp; dp = dp->sibling) {
+ if (ebus_fdthree_p(dp))
+ goto found_fdthree;
}
}
- ebus_done:
- if (!edev)
+ found_fdthree:
+ if (!dp)
return 0;
- state_prop = of_get_property(edev->prom_node, "status", NULL);
+ op = of_find_device_by_node(dp);
+ if (!op)
+ return 0;
+
+ state_prop = of_get_property(op->node, "status", NULL);
if (state_prop && !strncmp(state_prop, "disabled", 8))
return 0;
- FLOPPY_IRQ = edev->irqs[0];
+ FLOPPY_IRQ = op->irqs[0];
/* Make sure the high density bit is set, some systems
* (most notably Ultra5/Ultra10) come up with it clear.
*/
- auxio_reg = (void __iomem *) edev->resource[2].start;
+ auxio_reg = (void __iomem *) op->resource[2].start;
writel(readl(auxio_reg)|0x2, auxio_reg);
- sun_pci_ebus_dev = ebus->self;
+ sun_floppy_dev = &op->dev;
spin_lock_init(&sun_pci_fd_ebus_dma.lock);
/* XXX ioremap */
sun_pci_fd_ebus_dma.regs = (void __iomem *)
- edev->resource[1].start;
+ op->resource[1].start;
if (!sun_pci_fd_ebus_dma.regs)
return 0;
@@ -625,7 +621,7 @@
return 0;
/* XXX ioremap */
- sun_fdc = (struct sun_flpy_controller *)edev->resource[0].start;
+ sun_fdc = (struct sun_flpy_controller *) op->resource[0].start;
sun_fdops.fd_inb = sun_pci_fd_inb;
sun_fdops.fd_outb = sun_pci_fd_outb;
@@ -662,12 +658,15 @@
/*
* Find NS87303 SuperIO config registers (through ecpp).
*/
- for_each_ebus(ebus) {
- for_each_ebusdev(edev, ebus) {
- if (!strcmp(edev->prom_node->name, "ecpp")) {
- config = edev->resource[1].start;
- goto config_done;
- }
+ config = 0;
+ for (dp = ebus_dp->child; dp; dp = dp->sibling) {
+ if (!strcmp(dp->name, "ecpp")) {
+ struct of_device *ecpp_op;
+
+ ecpp_op = of_find_device_by_node(dp);
+ if (ecpp_op)
+ config = ecpp_op->resource[1].start;
+ goto config_done;
}
}
config_done:
@@ -716,26 +715,23 @@
#endif /* PCI_FDC_SWAP_DRIVES */
return sun_floppy_types[0];
-#else
- return 0;
-#endif
}
- prom_getproperty(sdev->prom_node, "status", state, sizeof(state));
- if(!strncmp(state, "disabled", 8))
+ prop = of_get_property(op->node, "status", NULL);
+ if (prop && !strncmp(state, "disabled", 8))
return 0;
/*
- * We cannot do sbus_ioremap here: it does request_region,
+ * We cannot do of_ioremap here: it does request_region,
* which the generic floppy driver tries to do once again.
* But we must use the sdev resource values as they have
* had parent ranges applied.
*/
sun_fdc = (struct sun_flpy_controller *)
- (sdev->resource[0].start +
- ((sdev->resource[0].flags & 0x1ffUL) << 32UL));
+ (op->resource[0].start +
+ ((op->resource[0].flags & 0x1ffUL) << 32UL));
/* Last minute sanity check... */
- if(sbus_readb(&sun_fdc->status1_82077) == 0xff) {
+ if (sbus_readb(&sun_fdc->status1_82077) == 0xff) {
sun_fdc = (struct sun_flpy_controller *)-1;
return 0;
}
diff --git a/arch/sparc/include/asm/gpio.h b/arch/sparc/include/asm/gpio.h
new file mode 100644
index 0000000..a0e3ac0
--- /dev/null
+++ b/arch/sparc/include/asm/gpio.h
@@ -0,0 +1,36 @@
+#ifndef __ASM_SPARC_GPIO_H
+#define __ASM_SPARC_GPIO_H
+
+#include <linux/errno.h>
+#include <asm-generic/gpio.h>
+
+#ifdef CONFIG_GPIOLIB
+
+static inline int gpio_get_value(unsigned int gpio)
+{
+ return __gpio_get_value(gpio);
+}
+
+static inline void gpio_set_value(unsigned int gpio, int value)
+{
+ __gpio_set_value(gpio, value);
+}
+
+static inline int gpio_cansleep(unsigned int gpio)
+{
+ return __gpio_cansleep(gpio);
+}
+
+static inline int gpio_to_irq(unsigned int gpio)
+{
+ return -ENOSYS;
+}
+
+static inline int irq_to_gpio(unsigned int irq)
+{
+ return -EINVAL;
+}
+
+#endif /* CONFIG_GPIOLIB */
+
+#endif /* __ASM_SPARC_GPIO_H */
diff --git a/arch/sparc/include/asm/io-unit.h b/arch/sparc/include/asm/io-unit.h
index 96823b4..01ab2f6 100644
--- a/arch/sparc/include/asm/io-unit.h
+++ b/arch/sparc/include/asm/io-unit.h
@@ -55,8 +55,4 @@
#define IOUNIT_BMAPM_START IOUNIT_BMAP2_END
#define IOUNIT_BMAPM_END ((IOUNIT_DMA_SIZE - IOUNIT_DVMA_SIZE) >> PAGE_SHIFT)
-extern __u32 iounit_map_dma_init(struct sbus_bus *, int);
-#define iounit_map_dma_finish(sbus, addr, len) mmu_release_scsi_one(addr, len, sbus)
-extern __u32 iounit_map_dma_page(__u32, void *, struct sbus_bus *);
-
#endif /* !(_SPARC_IO_UNIT_H) */
diff --git a/arch/sparc/include/asm/io_32.h b/arch/sparc/include/asm/io_32.h
index 10d7da4..93fe21e 100644
--- a/arch/sparc/include/asm/io_32.h
+++ b/arch/sparc/include/asm/io_32.h
@@ -293,14 +293,6 @@
extern void pci_iounmap(struct pci_dev *dev, void __iomem *);
/*
- * Bus number may be in res->flags... somewhere.
- */
-extern void __iomem *sbus_ioremap(struct resource *res, unsigned long offset,
- unsigned long size, char *name);
-extern void sbus_iounmap(volatile void __iomem *vaddr, unsigned long size);
-
-
-/*
* At the moment, we do not use CMOS_READ anywhere outside of rtc.c,
* so rtc_port is static in it. This should not change unless a new
* hardware pops up.
@@ -308,6 +300,17 @@
#define RTC_PORT(x) (rtc_port + (x))
#define RTC_ALWAYS_BCD 0
+static inline int sbus_can_dma_64bit(void)
+{
+ return 0; /* actually, sparc_cpu_model==sun4d */
+}
+static inline int sbus_can_burst64(void)
+{
+ return 0; /* actually, sparc_cpu_model==sun4d */
+}
+struct device;
+extern void sbus_set_sbus64(struct device *, int);
+
#endif
#define __ARCH_HAS_NO_PAGE_ZERO_MAPPED 1
diff --git a/arch/sparc/include/asm/io_64.h b/arch/sparc/include/asm/io_64.h
index 0bff078..4aee21d 100644
--- a/arch/sparc/include/asm/io_64.h
+++ b/arch/sparc/include/asm/io_64.h
@@ -482,18 +482,16 @@
extern void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max);
extern void pci_iounmap(struct pci_dev *dev, void __iomem *);
-/* Similarly for SBUS. */
-#define sbus_ioremap(__res, __offset, __size, __name) \
-({ unsigned long __ret; \
- __ret = (__res)->start + (((__res)->flags & 0x1ffUL) << 32UL); \
- __ret += (unsigned long) (__offset); \
- if (! request_region((__ret), (__size), (__name))) \
- __ret = 0UL; \
- (void __iomem *) __ret; \
-})
-
-#define sbus_iounmap(__addr, __size) \
- release_region((unsigned long)(__addr), (__size))
+static inline int sbus_can_dma_64bit(void)
+{
+ return 1;
+}
+static inline int sbus_can_burst64(void)
+{
+ return 1;
+}
+struct device;
+extern void sbus_set_sbus64(struct device *, int);
/*
* Convert a physical pointer to a virtual kernel pointer for /dev/mem
diff --git a/arch/sparc/include/asm/iommu_64.h b/arch/sparc/include/asm/iommu_64.h
index d7b9afc..caf798b 100644
--- a/arch/sparc/include/asm/iommu_64.h
+++ b/arch/sparc/include/asm/iommu_64.h
@@ -48,6 +48,9 @@
unsigned long strbuf_control;
unsigned long strbuf_pflush;
unsigned long strbuf_fsync;
+ unsigned long strbuf_err_stat;
+ unsigned long strbuf_tag_diag;
+ unsigned long strbuf_line_diag;
unsigned long strbuf_ctxflush;
unsigned long strbuf_ctxmatch_base;
unsigned long strbuf_flushflag_pa;
diff --git a/arch/sparc/include/asm/irq_64.h b/arch/sparc/include/asm/irq_64.h
index e3dd930..71673ec 100644
--- a/arch/sparc/include/asm/irq_64.h
+++ b/arch/sparc/include/asm/irq_64.h
@@ -56,7 +56,6 @@
unsigned long imap_base,
unsigned long iclr_base);
extern void sun4u_destroy_msi(unsigned int virt_irq);
-extern unsigned int sbus_build_irq(void *sbus, unsigned int ino);
extern unsigned char virt_irq_alloc(unsigned int dev_handle,
unsigned int dev_ino);
diff --git a/arch/sparc/include/asm/mc146818rtc_64.h b/arch/sparc/include/asm/mc146818rtc_64.h
index e9c0fcc..7238d17 100644
--- a/arch/sparc/include/asm/mc146818rtc_64.h
+++ b/arch/sparc/include/asm/mc146818rtc_64.h
@@ -7,12 +7,8 @@
#include <asm/io.h>
#ifndef RTC_PORT
-#ifdef CONFIG_PCI
-extern unsigned long ds1287_regs;
-#else
-#define ds1287_regs (0UL)
-#endif
-#define RTC_PORT(x) (ds1287_regs + (x))
+extern unsigned long cmos_regs;
+#define RTC_PORT(x) (cmos_regs + (x))
#define RTC_ALWAYS_BCD 0
#endif
@@ -29,6 +25,4 @@
outb_p((val),RTC_PORT(1)); \
})
-#define RTC_IRQ 8
-
#endif /* __ASM_SPARC64_MC146818RTC_H */
diff --git a/arch/sparc/include/asm/memctrl.h b/arch/sparc/include/asm/memctrl.h
new file mode 100644
index 0000000..4065c56
--- /dev/null
+++ b/arch/sparc/include/asm/memctrl.h
@@ -0,0 +1,9 @@
+#ifndef _SPARC_MEMCTRL_H
+#define _SPARC_MEMCTRL_H
+
+typedef int (*dimm_printer_t)(int synd_code, unsigned long paddr, char *buf, int buflen);
+
+int register_dimm_printer(dimm_printer_t func);
+void unregister_dimm_printer(dimm_printer_t func);
+
+#endif /* _SPARC_MEMCTRL_H */
diff --git a/arch/sparc/include/asm/mostek.h b/arch/sparc/include/asm/mostek.h
deleted file mode 100644
index 433be3e..0000000
--- a/arch/sparc/include/asm/mostek.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef ___ASM_SPARC_MOSTEK_H
-#define ___ASM_SPARC_MOSTEK_H
-#if defined(__sparc__) && defined(__arch64__)
-#include <asm/mostek_64.h>
-#else
-#include <asm/mostek_32.h>
-#endif
-#endif
diff --git a/arch/sparc/include/asm/mostek_32.h b/arch/sparc/include/asm/mostek_32.h
deleted file mode 100644
index a99590c..0000000
--- a/arch/sparc/include/asm/mostek_32.h
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * mostek.h: Describes the various Mostek time of day clock registers.
- *
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu)
- * Added intersil code 05/25/98 Chris Davis (cdavis@cois.on.ca)
- */
-
-#ifndef _SPARC_MOSTEK_H
-#define _SPARC_MOSTEK_H
-
-#include <asm/idprom.h>
-#include <asm/io.h>
-
-/* M48T02 Register Map (adapted from Sun NVRAM/Hostid FAQ)
- *
- * Data
- * Address Function
- * Bit 7 Bit 6 Bit 5 Bit 4Bit 3 Bit 2 Bit 1 Bit 0
- * 7ff - - - - - - - - Year 00-99
- * 7fe 0 0 0 - - - - - Month 01-12
- * 7fd 0 0 - - - - - - Date 01-31
- * 7fc 0 FT 0 0 0 - - - Day 01-07
- * 7fb KS 0 - - - - - - Hours 00-23
- * 7fa 0 - - - - - - - Minutes 00-59
- * 7f9 ST - - - - - - - Seconds 00-59
- * 7f8 W R S - - - - - Control
- *
- * * ST is STOP BIT
- * * W is WRITE BIT
- * * R is READ BIT
- * * S is SIGN BIT
- * * FT is FREQ TEST BIT
- * * KS is KICK START BIT
- */
-
-/* The Mostek 48t02 real time clock and NVRAM chip. The registers
- * other than the control register are in binary coded decimal. Some
- * control bits also live outside the control register.
- */
-#define mostek_read(_addr) readb(_addr)
-#define mostek_write(_addr,_val) writeb(_val, _addr)
-#define MOSTEK_EEPROM 0x0000UL
-#define MOSTEK_IDPROM 0x07d8UL
-#define MOSTEK_CREG 0x07f8UL
-#define MOSTEK_SEC 0x07f9UL
-#define MOSTEK_MIN 0x07faUL
-#define MOSTEK_HOUR 0x07fbUL
-#define MOSTEK_DOW 0x07fcUL
-#define MOSTEK_DOM 0x07fdUL
-#define MOSTEK_MONTH 0x07feUL
-#define MOSTEK_YEAR 0x07ffUL
-
-struct mostek48t02 {
- volatile char eeprom[2008]; /* This is the eeprom, don't touch! */
- struct idprom idprom; /* The idprom lives here. */
- volatile unsigned char creg; /* Control register */
- volatile unsigned char sec; /* Seconds (0-59) */
- volatile unsigned char min; /* Minutes (0-59) */
- volatile unsigned char hour; /* Hour (0-23) */
- volatile unsigned char dow; /* Day of the week (1-7) */
- volatile unsigned char dom; /* Day of the month (1-31) */
- volatile unsigned char month; /* Month of year (1-12) */
- volatile unsigned char year; /* Year (0-99) */
-};
-
-extern spinlock_t mostek_lock;
-extern void __iomem *mstk48t02_regs;
-
-/* Control register values. */
-#define MSTK_CREG_WRITE 0x80 /* Must set this before placing values. */
-#define MSTK_CREG_READ 0x40 /* Stop updates to allow a clean read. */
-#define MSTK_CREG_SIGN 0x20 /* Slow/speed clock in calibration mode. */
-
-/* Control bits that live in the other registers. */
-#define MSTK_STOP 0x80 /* Stop the clock oscillator. (sec) */
-#define MSTK_KICK_START 0x80 /* Kick start the clock chip. (hour) */
-#define MSTK_FREQ_TEST 0x40 /* Frequency test mode. (day) */
-
-#define MSTK_YEAR_ZERO 1968 /* If year reg has zero, it is 1968. */
-#define MSTK_CVT_YEAR(yr) ((yr) + MSTK_YEAR_ZERO)
-
-/* Masks that define how much space each value takes up. */
-#define MSTK_SEC_MASK 0x7f
-#define MSTK_MIN_MASK 0x7f
-#define MSTK_HOUR_MASK 0x3f
-#define MSTK_DOW_MASK 0x07
-#define MSTK_DOM_MASK 0x3f
-#define MSTK_MONTH_MASK 0x1f
-#define MSTK_YEAR_MASK 0xffU
-
-/* Binary coded decimal conversion macros. */
-#define MSTK_REGVAL_TO_DECIMAL(x) (((x) & 0x0F) + 0x0A * ((x) >> 0x04))
-#define MSTK_DECIMAL_TO_REGVAL(x) ((((x) / 0x0A) << 0x04) + ((x) % 0x0A))
-
-/* Generic register set and get macros for internal use. */
-#define MSTK_GET(regs,var,mask) (MSTK_REGVAL_TO_DECIMAL(((struct mostek48t02 *)regs)->var & MSTK_ ## mask ## _MASK))
-#define MSTK_SET(regs,var,value,mask) do { ((struct mostek48t02 *)regs)->var &= ~(MSTK_ ## mask ## _MASK); ((struct mostek48t02 *)regs)->var |= MSTK_DECIMAL_TO_REGVAL(value) & (MSTK_ ## mask ## _MASK); } while (0)
-
-/* Macros to make register access easier on our fingers. These give you
- * the decimal value of the register requested if applicable. You pass
- * the a pointer to a 'struct mostek48t02'.
- */
-#define MSTK_REG_CREG(regs) (((struct mostek48t02 *)regs)->creg)
-#define MSTK_REG_SEC(regs) MSTK_GET(regs,sec,SEC)
-#define MSTK_REG_MIN(regs) MSTK_GET(regs,min,MIN)
-#define MSTK_REG_HOUR(regs) MSTK_GET(regs,hour,HOUR)
-#define MSTK_REG_DOW(regs) MSTK_GET(regs,dow,DOW)
-#define MSTK_REG_DOM(regs) MSTK_GET(regs,dom,DOM)
-#define MSTK_REG_MONTH(regs) MSTK_GET(regs,month,MONTH)
-#define MSTK_REG_YEAR(regs) MSTK_GET(regs,year,YEAR)
-
-#define MSTK_SET_REG_SEC(regs,value) MSTK_SET(regs,sec,value,SEC)
-#define MSTK_SET_REG_MIN(regs,value) MSTK_SET(regs,min,value,MIN)
-#define MSTK_SET_REG_HOUR(regs,value) MSTK_SET(regs,hour,value,HOUR)
-#define MSTK_SET_REG_DOW(regs,value) MSTK_SET(regs,dow,value,DOW)
-#define MSTK_SET_REG_DOM(regs,value) MSTK_SET(regs,dom,value,DOM)
-#define MSTK_SET_REG_MONTH(regs,value) MSTK_SET(regs,month,value,MONTH)
-#define MSTK_SET_REG_YEAR(regs,value) MSTK_SET(regs,year,value,YEAR)
-
-
-/* The Mostek 48t08 clock chip. Found on Sun4m's I think. It has the
- * same (basically) layout of the 48t02 chip except for the extra
- * NVRAM on board (8 KB against the 48t02's 2 KB).
- */
-struct mostek48t08 {
- char offset[6*1024]; /* Magic things may be here, who knows? */
- struct mostek48t02 regs; /* Here is what we are interested in. */
-};
-
-#ifdef CONFIG_SUN4
-enum sparc_clock_type { MSTK48T02, MSTK48T08, \
-INTERSIL, MSTK_INVALID };
-#else
-enum sparc_clock_type { MSTK48T02, MSTK48T08, \
-MSTK_INVALID };
-#endif
-
-#ifdef CONFIG_SUN4
-/* intersil on a sun 4/260 code data from harris doc */
-struct intersil_dt {
- volatile unsigned char int_csec;
- volatile unsigned char int_hour;
- volatile unsigned char int_min;
- volatile unsigned char int_sec;
- volatile unsigned char int_month;
- volatile unsigned char int_day;
- volatile unsigned char int_year;
- volatile unsigned char int_dow;
-};
-
-struct intersil {
- struct intersil_dt clk;
- struct intersil_dt cmp;
- volatile unsigned char int_intr_reg;
- volatile unsigned char int_cmd_reg;
-};
-
-#define INTERSIL_STOP 0x0
-#define INTERSIL_START 0x8
-#define INTERSIL_INTR_DISABLE 0x0
-#define INTERSIL_INTR_ENABLE 0x10
-#define INTERSIL_32K 0x0
-#define INTERSIL_NORMAL 0x0
-#define INTERSIL_24H 0x4
-#define INTERSIL_INT_100HZ 0x2
-
-/* end of intersil info */
-#endif
-
-#endif /* !(_SPARC_MOSTEK_H) */
diff --git a/arch/sparc/include/asm/mostek_64.h b/arch/sparc/include/asm/mostek_64.h
deleted file mode 100644
index c5652de..0000000
--- a/arch/sparc/include/asm/mostek_64.h
+++ /dev/null
@@ -1,143 +0,0 @@
-/* mostek.h: Describes the various Mostek time of day clock registers.
- *
- * Copyright (C) 1995 David S. Miller (davem@davemloft.net)
- * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu)
- */
-
-#ifndef _SPARC64_MOSTEK_H
-#define _SPARC64_MOSTEK_H
-
-#include <asm/idprom.h>
-
-/* M48T02 Register Map (adapted from Sun NVRAM/Hostid FAQ)
- *
- * Data
- * Address Function
- * Bit 7 Bit 6 Bit 5 Bit 4Bit 3 Bit 2 Bit 1 Bit 0
- * 7ff - - - - - - - - Year 00-99
- * 7fe 0 0 0 - - - - - Month 01-12
- * 7fd 0 0 - - - - - - Date 01-31
- * 7fc 0 FT 0 0 0 - - - Day 01-07
- * 7fb KS 0 - - - - - - Hours 00-23
- * 7fa 0 - - - - - - - Minutes 00-59
- * 7f9 ST - - - - - - - Seconds 00-59
- * 7f8 W R S - - - - - Control
- *
- * * ST is STOP BIT
- * * W is WRITE BIT
- * * R is READ BIT
- * * S is SIGN BIT
- * * FT is FREQ TEST BIT
- * * KS is KICK START BIT
- */
-
-/* The Mostek 48t02 real time clock and NVRAM chip. The registers
- * other than the control register are in binary coded decimal. Some
- * control bits also live outside the control register.
- *
- * We now deal with physical addresses for I/O to the chip. -DaveM
- */
-static inline u8 mostek_read(void __iomem *addr)
-{
- u8 ret;
-
- __asm__ __volatile__("lduba [%1] %2, %0"
- : "=r" (ret)
- : "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E));
- return ret;
-}
-
-static inline void mostek_write(void __iomem *addr, u8 val)
-{
- __asm__ __volatile__("stba %0, [%1] %2"
- : /* no outputs */
- : "r" (val), "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E));
-}
-
-#define MOSTEK_EEPROM 0x0000UL
-#define MOSTEK_IDPROM 0x07d8UL
-#define MOSTEK_CREG 0x07f8UL
-#define MOSTEK_SEC 0x07f9UL
-#define MOSTEK_MIN 0x07faUL
-#define MOSTEK_HOUR 0x07fbUL
-#define MOSTEK_DOW 0x07fcUL
-#define MOSTEK_DOM 0x07fdUL
-#define MOSTEK_MONTH 0x07feUL
-#define MOSTEK_YEAR 0x07ffUL
-
-extern spinlock_t mostek_lock;
-extern void __iomem *mstk48t02_regs;
-
-/* Control register values. */
-#define MSTK_CREG_WRITE 0x80 /* Must set this before placing values. */
-#define MSTK_CREG_READ 0x40 /* Stop updates to allow a clean read. */
-#define MSTK_CREG_SIGN 0x20 /* Slow/speed clock in calibration mode. */
-
-/* Control bits that live in the other registers. */
-#define MSTK_STOP 0x80 /* Stop the clock oscillator. (sec) */
-#define MSTK_KICK_START 0x80 /* Kick start the clock chip. (hour) */
-#define MSTK_FREQ_TEST 0x40 /* Frequency test mode. (day) */
-
-#define MSTK_YEAR_ZERO 1968 /* If year reg has zero, it is 1968. */
-#define MSTK_CVT_YEAR(yr) ((yr) + MSTK_YEAR_ZERO)
-
-/* Masks that define how much space each value takes up. */
-#define MSTK_SEC_MASK 0x7f
-#define MSTK_MIN_MASK 0x7f
-#define MSTK_HOUR_MASK 0x3f
-#define MSTK_DOW_MASK 0x07
-#define MSTK_DOM_MASK 0x3f
-#define MSTK_MONTH_MASK 0x1f
-#define MSTK_YEAR_MASK 0xffU
-
-/* Binary coded decimal conversion macros. */
-#define MSTK_REGVAL_TO_DECIMAL(x) (((x) & 0x0F) + 0x0A * ((x) >> 0x04))
-#define MSTK_DECIMAL_TO_REGVAL(x) ((((x) / 0x0A) << 0x04) + ((x) % 0x0A))
-
-/* Generic register set and get macros for internal use. */
-#define MSTK_GET(regs,name) \
- (MSTK_REGVAL_TO_DECIMAL(mostek_read(regs + MOSTEK_ ## name) & MSTK_ ## name ## _MASK))
-#define MSTK_SET(regs,name,value) \
-do { u8 __val = mostek_read(regs + MOSTEK_ ## name); \
- __val &= ~(MSTK_ ## name ## _MASK); \
- __val |= (MSTK_DECIMAL_TO_REGVAL(value) & \
- (MSTK_ ## name ## _MASK)); \
- mostek_write(regs + MOSTEK_ ## name, __val); \
-} while(0)
-
-/* Macros to make register access easier on our fingers. These give you
- * the decimal value of the register requested if applicable. You pass
- * the a pointer to a 'struct mostek48t02'.
- */
-#define MSTK_REG_CREG(regs) (mostek_read((regs) + MOSTEK_CREG))
-#define MSTK_REG_SEC(regs) MSTK_GET(regs,SEC)
-#define MSTK_REG_MIN(regs) MSTK_GET(regs,MIN)
-#define MSTK_REG_HOUR(regs) MSTK_GET(regs,HOUR)
-#define MSTK_REG_DOW(regs) MSTK_GET(regs,DOW)
-#define MSTK_REG_DOM(regs) MSTK_GET(regs,DOM)
-#define MSTK_REG_MONTH(regs) MSTK_GET(regs,MONTH)
-#define MSTK_REG_YEAR(regs) MSTK_GET(regs,YEAR)
-
-#define MSTK_SET_REG_SEC(regs,value) MSTK_SET(regs,SEC,value)
-#define MSTK_SET_REG_MIN(regs,value) MSTK_SET(regs,MIN,value)
-#define MSTK_SET_REG_HOUR(regs,value) MSTK_SET(regs,HOUR,value)
-#define MSTK_SET_REG_DOW(regs,value) MSTK_SET(regs,DOW,value)
-#define MSTK_SET_REG_DOM(regs,value) MSTK_SET(regs,DOM,value)
-#define MSTK_SET_REG_MONTH(regs,value) MSTK_SET(regs,MONTH,value)
-#define MSTK_SET_REG_YEAR(regs,value) MSTK_SET(regs,YEAR,value)
-
-
-/* The Mostek 48t08 clock chip. Found on Sun4m's I think. It has the
- * same (basically) layout of the 48t02 chip except for the extra
- * NVRAM on board (8 KB against the 48t02's 2 KB).
- */
-#define MOSTEK_48T08_OFFSET 0x0000UL /* Lower NVRAM portions */
-#define MOSTEK_48T08_48T02 0x1800UL /* Offset to 48T02 chip */
-
-/* SUN5 systems usually have 48t59 model clock chipsets. But we keep the older
- * clock chip definitions around just in case.
- */
-#define MOSTEK_48T59_OFFSET 0x0000UL /* Lower NVRAM portions */
-#define MOSTEK_48T59_48T02 0x1800UL /* Offset to 48T02 chip */
-
-#endif /* !(_SPARC64_MOSTEK_H) */
diff --git a/arch/sparc/include/asm/obio.h b/arch/sparc/include/asm/obio.h
index 1a7544c..4ade0c8 100644
--- a/arch/sparc/include/asm/obio.h
+++ b/arch/sparc/include/asm/obio.h
@@ -155,17 +155,6 @@
"i" (ASI_M_CTL));
}
-extern unsigned char cpu_leds[32];
-
-static inline void show_leds(int cpuid)
-{
- cpuid &= 0x1e;
- __asm__ __volatile__ ("stba %0, [%1] %2" : :
- "r" ((cpu_leds[cpuid] << 4) | cpu_leds[cpuid+1]),
- "r" (ECSR_BASE(cpuid) | BB_LEDS),
- "i" (ASI_M_CTL));
-}
-
static inline unsigned cc_get_ipen(void)
{
unsigned pending;
diff --git a/arch/sparc/include/asm/of_device.h b/arch/sparc/include/asm/of_device.h
index bba777a..a5d9811 100644
--- a/arch/sparc/include/asm/of_device.h
+++ b/arch/sparc/include/asm/of_device.h
@@ -30,6 +30,8 @@
extern void __iomem *of_ioremap(struct resource *res, unsigned long offset, unsigned long size, char *name);
extern void of_iounmap(struct resource *res, void __iomem *base, unsigned long size);
+extern void of_propagate_archdata(struct of_device *bus);
+
/* This is just here during the transition */
#include <linux/of_platform.h>
diff --git a/arch/sparc/include/asm/of_platform.h b/arch/sparc/include/asm/of_platform.h
index 2348ab9..90da990 100644
--- a/arch/sparc/include/asm/of_platform.h
+++ b/arch/sparc/include/asm/of_platform.h
@@ -13,9 +13,6 @@
*
*/
-extern struct bus_type ebus_bus_type;
-extern struct bus_type sbus_bus_type;
-
#define of_bus_type of_platform_bus_type /* for compatibility */
#endif
diff --git a/arch/sparc/include/asm/oplib_32.h b/arch/sparc/include/asm/oplib_32.h
index b2631da..699da05 100644
--- a/arch/sparc/include/asm/oplib_32.h
+++ b/arch/sparc/include/asm/oplib_32.h
@@ -21,7 +21,6 @@
PROM_V2, /* sun4c and early sun4m V2 prom */
PROM_V3, /* sun4m and later, up to sun4d/sun4e machines V3 */
PROM_P1275, /* IEEE compliant ISA based Sun PROM, only sun4u */
- PROM_SUN4, /* Old sun4 proms are totally different, but we'll shoehorn it to make it fit */
};
extern enum prom_major_version prom_vers;
diff --git a/arch/sparc/include/asm/page_32.h b/arch/sparc/include/asm/page_32.h
index cf5fb70..d1806ed 100644
--- a/arch/sparc/include/asm/page_32.h
+++ b/arch/sparc/include/asm/page_32.h
@@ -8,11 +8,8 @@
#ifndef _SPARC_PAGE_H
#define _SPARC_PAGE_H
-#ifdef CONFIG_SUN4
-#define PAGE_SHIFT 13
-#else
#define PAGE_SHIFT 12
-#endif
+
#ifndef __ASSEMBLY__
/* I have my suspicions... -DaveM */
#define PAGE_SIZE (1UL << PAGE_SHIFT)
diff --git a/arch/sparc/include/asm/page_64.h b/arch/sparc/include/asm/page_64.h
index b579b91..4274ed1 100644
--- a/arch/sparc/include/asm/page_64.h
+++ b/arch/sparc/include/asm/page_64.h
@@ -38,6 +38,8 @@
#ifndef __ASSEMBLY__
+#define WANT_PAGE_VIRTUAL
+
extern void _clear_page(void *page);
#define clear_page(X) _clear_page((void *)(X))
struct page;
diff --git a/arch/sparc/include/asm/parport.h b/arch/sparc/include/asm/parport.h
index d983062..dff3f02 100644
--- a/arch/sparc/include/asm/parport.h
+++ b/arch/sparc/include/asm/parport.h
@@ -8,7 +8,7 @@
#include <linux/of_device.h>
-#include <asm/ebus.h>
+#include <asm/ebus_dma.h>
#include <asm/ns87303.h>
#include <asm/prom.h>
@@ -215,7 +215,7 @@
return 0;
}
-static struct of_device_id ecpp_match[] = {
+static const struct of_device_id ecpp_match[] = {
{
.name = "ecpp",
},
diff --git a/arch/sparc/include/asm/pci_32.h b/arch/sparc/include/asm/pci_32.h
index 0ee949d..b41c4c1 100644
--- a/arch/sparc/include/asm/pci_32.h
+++ b/arch/sparc/include/asm/pci_32.h
@@ -3,6 +3,8 @@
#ifdef __KERNEL__
+#include <linux/dma-mapping.h>
+
/* Can be used to override the logic in pci_scan_bus for skipping
* already-configured bus numbers - to be used for buggy BIOSes
* or architectures with incomplete PCI setup by the loader.
diff --git a/arch/sparc/include/asm/pgtable_32.h b/arch/sparc/include/asm/pgtable_32.h
index 08237fd..e0cabe7 100644
--- a/arch/sparc/include/asm/pgtable_32.h
+++ b/arch/sparc/include/asm/pgtable_32.h
@@ -14,11 +14,7 @@
#include <linux/spinlock.h>
#include <linux/swap.h>
#include <asm/types.h>
-#ifdef CONFIG_SUN4
-#include <asm/pgtsun4.h>
-#else
#include <asm/pgtsun4c.h>
-#endif
#include <asm/pgtsrmmu.h>
#include <asm/vac-ops.h>
#include <asm/oplib.h>
diff --git a/arch/sparc/include/asm/pgtable_64.h b/arch/sparc/include/asm/pgtable_64.h
index bb9ec2c..b049abf 100644
--- a/arch/sparc/include/asm/pgtable_64.h
+++ b/arch/sparc/include/asm/pgtable_64.h
@@ -770,6 +770,8 @@
extern unsigned long cmdline_memory_size;
+extern asmlinkage void do_sparc64_fault(struct pt_regs *regs);
+
#endif /* !(__ASSEMBLY__) */
#endif /* !(_SPARC64_PGTABLE_H) */
diff --git a/arch/sparc/include/asm/prom.h b/arch/sparc/include/asm/prom.h
index fd55522..900d447 100644
--- a/arch/sparc/include/asm/prom.h
+++ b/arch/sparc/include/asm/prom.h
@@ -18,6 +18,7 @@
*/
#include <linux/types.h>
#include <linux/proc_fs.h>
+#include <linux/mutex.h>
#include <asm/atomic.h>
#define OF_ROOT_NODE_ADDR_CELLS_DEFAULT 2
@@ -73,6 +74,7 @@
extern struct device_node *of_find_node_by_cpuid(int cpuid);
extern int of_set_property(struct device_node *node, const char *name, void *val, int len);
+extern struct mutex of_set_property_mutex;
extern int of_getintprop_default(struct device_node *np,
const char *name,
int def);
@@ -94,6 +96,16 @@
{
}
+/* These routines are here to provide compatibility with how powerpc
+ * handles IRQ mapping for OF device nodes. We precompute and permanently
+ * register them in the of_device objects, whereas powerpc computes them
+ * on request.
+ */
+extern unsigned int irq_of_parse_and_map(struct device_node *node, int index);
+static inline void irq_dispose_mapping(unsigned int virq)
+{
+}
+
/*
* NB: This is here while we transition from using asm/prom.h
* to linux/of.h
diff --git a/arch/sparc/include/asm/ptrace_64.h b/arch/sparc/include/asm/ptrace_64.h
index 06e4914..3d3e9c1 100644
--- a/arch/sparc/include/asm/ptrace_64.h
+++ b/arch/sparc/include/asm/ptrace_64.h
@@ -113,6 +113,8 @@
#ifdef __KERNEL__
+#include <linux/threads.h>
+
static inline int pt_regs_trap_type(struct pt_regs *regs)
{
return regs->magic & 0x1ff;
@@ -138,6 +140,7 @@
struct thread_info *thread;
unsigned long pad1;
};
+extern struct global_reg_snapshot global_reg_snapshot[NR_CPUS];
#define __ARCH_WANT_COMPAT_SYS_PTRACE
diff --git a/arch/sparc/include/asm/reboot.h b/arch/sparc/include/asm/reboot.h
deleted file mode 100644
index 3f3f43f..0000000
--- a/arch/sparc/include/asm/reboot.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _SPARC64_REBOOT_H
-#define _SPARC64_REBOOT_H
-
-extern void machine_alt_power_off(void);
-
-#endif /* _SPARC64_REBOOT_H */
diff --git a/arch/sparc/include/asm/rtc.h b/arch/sparc/include/asm/rtc.h
deleted file mode 100644
index f9ecb1f..0000000
--- a/arch/sparc/include/asm/rtc.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * rtc.h: Definitions for access to the Mostek real time clock
- *
- * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu)
- */
-
-#ifndef _RTC_H
-#define _RTC_H
-
-#include <linux/ioctl.h>
-
-struct rtc_time
-{
- int sec; /* Seconds (0-59) */
- int min; /* Minutes (0-59) */
- int hour; /* Hour (0-23) */
- int dow; /* Day of the week (1-7) */
- int dom; /* Day of the month (1-31) */
- int month; /* Month of year (1-12) */
- int year; /* Year (0-99) */
-};
-
-#define RTCGET _IOR('p', 20, struct rtc_time)
-#define RTCSET _IOW('p', 21, struct rtc_time)
-
-#endif
diff --git a/arch/sparc/include/asm/sbus.h b/arch/sparc/include/asm/sbus.h
deleted file mode 100644
index f82481a..0000000
--- a/arch/sparc/include/asm/sbus.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef ___ASM_SPARC_SBUS_H
-#define ___ASM_SPARC_SBUS_H
-#if defined(__sparc__) && defined(__arch64__)
-#include <asm/sbus_64.h>
-#else
-#include <asm/sbus_32.h>
-#endif
-#endif
diff --git a/arch/sparc/include/asm/sbus_32.h b/arch/sparc/include/asm/sbus_32.h
deleted file mode 100644
index a7b4fa2..0000000
--- a/arch/sparc/include/asm/sbus_32.h
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * sbus.h: Defines for the Sun SBus.
- *
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- */
-
-#ifndef _SPARC_SBUS_H
-#define _SPARC_SBUS_H
-
-#include <linux/dma-mapping.h>
-#include <linux/ioport.h>
-#include <linux/of_device.h>
-
-#include <asm/oplib.h>
-#include <asm/prom.h>
-#include <asm/scatterlist.h>
-
-/* We scan which devices are on the SBus using the PROM node device
- * tree. SBus devices are described in two different ways. You can
- * either get an absolute address at which to access the device, or
- * you can get a SBus 'slot' number and an offset within that slot.
- */
-
-/* The base address at which to calculate device OBIO addresses. */
-#define SUN_SBUS_BVADDR 0xf8000000
-#define SBUS_OFF_MASK 0x01ffffff
-
-/* These routines are used to calculate device address from slot
- * numbers + offsets, and vice versa.
- */
-
-static inline unsigned long sbus_devaddr(int slotnum, unsigned long offset)
-{
- return (unsigned long) (SUN_SBUS_BVADDR+((slotnum)<<25)+(offset));
-}
-
-static inline int sbus_dev_slot(unsigned long dev_addr)
-{
- return (int) (((dev_addr)-SUN_SBUS_BVADDR)>>25);
-}
-
-struct sbus_bus;
-
-/* Linux SBUS device tables */
-struct sbus_dev {
- struct of_device ofdev;
- struct sbus_bus *bus;
- struct sbus_dev *next;
- struct sbus_dev *child;
- struct sbus_dev *parent;
- int prom_node;
- char prom_name[64];
- int slot;
-
- struct resource resource[PROMREG_MAX];
-
- struct linux_prom_registers reg_addrs[PROMREG_MAX];
- int num_registers;
-
- struct linux_prom_ranges device_ranges[PROMREG_MAX];
- int num_device_ranges;
-
- unsigned int irqs[4];
- int num_irqs;
-};
-#define to_sbus_device(d) container_of(d, struct sbus_dev, ofdev.dev)
-
-/* This struct describes the SBus(s) found on this machine. */
-struct sbus_bus {
- struct of_device ofdev;
- struct sbus_dev *devices; /* Link to devices on this SBus */
- struct sbus_bus *next; /* next SBus, if more than one SBus */
- int prom_node; /* PROM device tree node for this SBus */
- char prom_name[64]; /* Usually "sbus" or "sbi" */
- int clock_freq;
-
- struct linux_prom_ranges sbus_ranges[PROMREG_MAX];
- int num_sbus_ranges;
-
- int devid;
- int board;
-};
-#define to_sbus(d) container_of(d, struct sbus_bus, ofdev.dev)
-
-extern struct sbus_bus *sbus_root;
-
-static inline int
-sbus_is_slave(struct sbus_dev *dev)
-{
- /* XXX Have to write this for sun4c's */
- return 0;
-}
-
-/* Device probing routines could find these handy */
-#define for_each_sbus(bus) \
- for((bus) = sbus_root; (bus); (bus)=(bus)->next)
-
-#define for_each_sbusdev(device, bus) \
- for((device) = (bus)->devices; (device); (device)=(device)->next)
-
-#define for_all_sbusdev(device, bus) \
- for ((bus) = sbus_root; (bus); (bus) = (bus)->next) \
- for ((device) = (bus)->devices; (device); (device) = (device)->next)
-
-/* Driver DVMA interfaces. */
-#define sbus_can_dma_64bit(sdev) (0) /* actually, sparc_cpu_model==sun4d */
-#define sbus_can_burst64(sdev) (0) /* actually, sparc_cpu_model==sun4d */
-extern void sbus_set_sbus64(struct sbus_dev *, int);
-extern void sbus_fill_device_irq(struct sbus_dev *);
-
-/* These yield IOMMU mappings in consistent mode. */
-extern void *sbus_alloc_consistent(struct sbus_dev *, long, u32 *dma_addrp);
-extern void sbus_free_consistent(struct sbus_dev *, long, void *, u32);
-void prom_adjust_ranges(struct linux_prom_ranges *, int,
- struct linux_prom_ranges *, int);
-
-#define SBUS_DMA_BIDIRECTIONAL DMA_BIDIRECTIONAL
-#define SBUS_DMA_TODEVICE DMA_TO_DEVICE
-#define SBUS_DMA_FROMDEVICE DMA_FROM_DEVICE
-#define SBUS_DMA_NONE DMA_NONE
-
-/* All the rest use streaming mode mappings. */
-extern dma_addr_t sbus_map_single(struct sbus_dev *, void *, size_t, int);
-extern void sbus_unmap_single(struct sbus_dev *, dma_addr_t, size_t, int);
-extern int sbus_map_sg(struct sbus_dev *, struct scatterlist *, int, int);
-extern void sbus_unmap_sg(struct sbus_dev *, struct scatterlist *, int, int);
-
-/* Finally, allow explicit synchronization of streamable mappings. */
-extern void sbus_dma_sync_single_for_cpu(struct sbus_dev *, dma_addr_t, size_t, int);
-#define sbus_dma_sync_single sbus_dma_sync_single_for_cpu
-extern void sbus_dma_sync_single_for_device(struct sbus_dev *, dma_addr_t, size_t, int);
-extern void sbus_dma_sync_sg_for_cpu(struct sbus_dev *, struct scatterlist *, int, int);
-#define sbus_dma_sync_sg sbus_dma_sync_sg_for_cpu
-extern void sbus_dma_sync_sg_for_device(struct sbus_dev *, struct scatterlist *, int, int);
-
-/* Eric Brower (ebrower@usa.net)
- * Translate SBus interrupt levels to ino values--
- * this is used when converting sbus "interrupts" OBP
- * node values to "intr" node values, and is platform
- * dependent. If only we could call OBP with
- * "sbus-intr>cpu (sbint -- ino)" from kernel...
- * See .../drivers/sbus/sbus.c for details.
- */
-BTFIXUPDEF_CALL(unsigned int, sbint_to_irq, struct sbus_dev *sdev, unsigned int)
-#define sbint_to_irq(sdev, sbint) BTFIXUP_CALL(sbint_to_irq)(sdev, sbint)
-
-extern void sbus_arch_bus_ranges_init(struct device_node *, struct sbus_bus *);
-extern void sbus_setup_iommu(struct sbus_bus *, struct device_node *);
-extern void sbus_setup_arch_props(struct sbus_bus *, struct device_node *);
-extern int sbus_arch_preinit(void);
-extern void sbus_arch_postinit(void);
-
-#endif /* !(_SPARC_SBUS_H) */
diff --git a/arch/sparc/include/asm/sbus_64.h b/arch/sparc/include/asm/sbus_64.h
deleted file mode 100644
index b606c14..0000000
--- a/arch/sparc/include/asm/sbus_64.h
+++ /dev/null
@@ -1,190 +0,0 @@
-/* sbus.h: Defines for the Sun SBus.
- *
- * Copyright (C) 1996, 1999, 2007 David S. Miller (davem@davemloft.net)
- */
-
-#ifndef _SPARC64_SBUS_H
-#define _SPARC64_SBUS_H
-
-#include <linux/dma-mapping.h>
-#include <linux/ioport.h>
-#include <linux/of_device.h>
-
-#include <asm/oplib.h>
-#include <asm/prom.h>
-#include <asm/iommu.h>
-#include <asm/scatterlist.h>
-
-/* We scan which devices are on the SBus using the PROM node device
- * tree. SBus devices are described in two different ways. You can
- * either get an absolute address at which to access the device, or
- * you can get a SBus 'slot' number and an offset within that slot.
- */
-
-/* The base address at which to calculate device OBIO addresses. */
-#define SUN_SBUS_BVADDR 0x00000000
-#define SBUS_OFF_MASK 0x0fffffff
-
-/* These routines are used to calculate device address from slot
- * numbers + offsets, and vice versa.
- */
-
-static inline unsigned long sbus_devaddr(int slotnum, unsigned long offset)
-{
- return (unsigned long) (SUN_SBUS_BVADDR+((slotnum)<<28)+(offset));
-}
-
-static inline int sbus_dev_slot(unsigned long dev_addr)
-{
- return (int) (((dev_addr)-SUN_SBUS_BVADDR)>>28);
-}
-
-struct sbus_bus;
-
-/* Linux SBUS device tables */
-struct sbus_dev {
- struct of_device ofdev;
- struct sbus_bus *bus;
- struct sbus_dev *next;
- struct sbus_dev *child;
- struct sbus_dev *parent;
- int prom_node;
- char prom_name[64];
- int slot;
-
- struct resource resource[PROMREG_MAX];
-
- struct linux_prom_registers reg_addrs[PROMREG_MAX];
- int num_registers;
-
- struct linux_prom_ranges device_ranges[PROMREG_MAX];
- int num_device_ranges;
-
- unsigned int irqs[4];
- int num_irqs;
-};
-#define to_sbus_device(d) container_of(d, struct sbus_dev, ofdev.dev)
-
-/* This struct describes the SBus(s) found on this machine. */
-struct sbus_bus {
- struct of_device ofdev;
- struct sbus_dev *devices; /* Tree of SBUS devices */
- struct sbus_bus *next; /* Next SBUS in system */
- int prom_node; /* OBP node of SBUS */
- char prom_name[64]; /* Usually "sbus" or "sbi" */
- int clock_freq;
-
- struct linux_prom_ranges sbus_ranges[PROMREG_MAX];
- int num_sbus_ranges;
-
- int portid;
-};
-#define to_sbus(d) container_of(d, struct sbus_bus, ofdev.dev)
-
-extern struct sbus_bus *sbus_root;
-
-/* Device probing routines could find these handy */
-#define for_each_sbus(bus) \
- for((bus) = sbus_root; (bus); (bus)=(bus)->next)
-
-#define for_each_sbusdev(device, bus) \
- for((device) = (bus)->devices; (device); (device)=(device)->next)
-
-#define for_all_sbusdev(device, bus) \
- for ((bus) = sbus_root; (bus); (bus) = (bus)->next) \
- for ((device) = (bus)->devices; (device); (device) = (device)->next)
-
-/* Driver DVMA interfaces. */
-#define sbus_can_dma_64bit(sdev) (1)
-#define sbus_can_burst64(sdev) (1)
-extern void sbus_set_sbus64(struct sbus_dev *, int);
-extern void sbus_fill_device_irq(struct sbus_dev *);
-
-static inline void *sbus_alloc_consistent(struct sbus_dev *sdev , size_t size,
- dma_addr_t *dma_handle)
-{
- return dma_alloc_coherent(&sdev->ofdev.dev, size,
- dma_handle, GFP_ATOMIC);
-}
-
-static inline void sbus_free_consistent(struct sbus_dev *sdev, size_t size,
- void *vaddr, dma_addr_t dma_handle)
-{
- return dma_free_coherent(&sdev->ofdev.dev, size, vaddr, dma_handle);
-}
-
-#define SBUS_DMA_BIDIRECTIONAL DMA_BIDIRECTIONAL
-#define SBUS_DMA_TODEVICE DMA_TO_DEVICE
-#define SBUS_DMA_FROMDEVICE DMA_FROM_DEVICE
-#define SBUS_DMA_NONE DMA_NONE
-
-/* All the rest use streaming mode mappings. */
-static inline dma_addr_t sbus_map_single(struct sbus_dev *sdev, void *ptr,
- size_t size, int direction)
-{
- return dma_map_single(&sdev->ofdev.dev, ptr, size,
- (enum dma_data_direction) direction);
-}
-
-static inline void sbus_unmap_single(struct sbus_dev *sdev,
- dma_addr_t dma_addr, size_t size,
- int direction)
-{
- dma_unmap_single(&sdev->ofdev.dev, dma_addr, size,
- (enum dma_data_direction) direction);
-}
-
-static inline int sbus_map_sg(struct sbus_dev *sdev, struct scatterlist *sg,
- int nents, int direction)
-{
- return dma_map_sg(&sdev->ofdev.dev, sg, nents,
- (enum dma_data_direction) direction);
-}
-
-static inline void sbus_unmap_sg(struct sbus_dev *sdev, struct scatterlist *sg,
- int nents, int direction)
-{
- dma_unmap_sg(&sdev->ofdev.dev, sg, nents,
- (enum dma_data_direction) direction);
-}
-
-/* Finally, allow explicit synchronization of streamable mappings. */
-static inline void sbus_dma_sync_single_for_cpu(struct sbus_dev *sdev,
- dma_addr_t dma_handle,
- size_t size, int direction)
-{
- dma_sync_single_for_cpu(&sdev->ofdev.dev, dma_handle, size,
- (enum dma_data_direction) direction);
-}
-#define sbus_dma_sync_single sbus_dma_sync_single_for_cpu
-
-static inline void sbus_dma_sync_single_for_device(struct sbus_dev *sdev,
- dma_addr_t dma_handle,
- size_t size, int direction)
-{
- /* No flushing needed to sync cpu writes to the device. */
-}
-
-static inline void sbus_dma_sync_sg_for_cpu(struct sbus_dev *sdev,
- struct scatterlist *sg,
- int nents, int direction)
-{
- dma_sync_sg_for_cpu(&sdev->ofdev.dev, sg, nents,
- (enum dma_data_direction) direction);
-}
-#define sbus_dma_sync_sg sbus_dma_sync_sg_for_cpu
-
-static inline void sbus_dma_sync_sg_for_device(struct sbus_dev *sdev,
- struct scatterlist *sg,
- int nents, int direction)
-{
- /* No flushing needed to sync cpu writes to the device. */
-}
-
-extern void sbus_arch_bus_ranges_init(struct device_node *, struct sbus_bus *);
-extern void sbus_setup_iommu(struct sbus_bus *, struct device_node *);
-extern void sbus_setup_arch_props(struct sbus_bus *, struct device_node *);
-extern int sbus_arch_preinit(void);
-extern void sbus_arch_postinit(void);
-
-#endif /* !(_SPARC64_SBUS_H) */
diff --git a/arch/sparc/include/asm/serial.h b/arch/sparc/include/asm/serial.h
new file mode 100644
index 0000000..f90d61c
--- /dev/null
+++ b/arch/sparc/include/asm/serial.h
@@ -0,0 +1,6 @@
+#ifndef __SPARC_SERIAL_H
+#define __SPARC_SERIAL_H
+
+#define BASE_BAUD ( 1843200 / 16 )
+
+#endif /* __SPARC_SERIAL_H */
diff --git a/arch/sparc/include/asm/spinlock_32.h b/arch/sparc/include/asm/spinlock_32.h
index de2249b..bf2d532 100644
--- a/arch/sparc/include/asm/spinlock_32.h
+++ b/arch/sparc/include/asm/spinlock_32.h
@@ -6,8 +6,6 @@
#ifndef __SPARC_SPINLOCK_H
#define __SPARC_SPINLOCK_H
-#include <linux/threads.h> /* For NR_CPUS */
-
#ifndef __ASSEMBLY__
#include <asm/psr.h>
diff --git a/arch/sparc/include/asm/spinlock_64.h b/arch/sparc/include/asm/spinlock_64.h
index 0006fe9..120cfe4 100644
--- a/arch/sparc/include/asm/spinlock_64.h
+++ b/arch/sparc/include/asm/spinlock_64.h
@@ -6,8 +6,6 @@
#ifndef __SPARC64_SPINLOCK_H
#define __SPARC64_SPINLOCK_H
-#include <linux/threads.h> /* For NR_CPUS */
-
#ifndef __ASSEMBLY__
/* To get debugging spinlocks which detect and catch
diff --git a/arch/sparc/include/asm/sstate.h b/arch/sparc/include/asm/sstate.h
deleted file mode 100644
index a7c35db..0000000
--- a/arch/sparc/include/asm/sstate.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef _SPARC64_SSTATE_H
-#define _SPARC64_SSTATE_H
-
-extern void sstate_booting(void);
-extern void sstate_running(void);
-extern void sstate_halt(void);
-extern void sstate_poweroff(void);
-extern void sstate_panic(void);
-extern void sstate_reboot(void);
-
-extern void sun4v_sstate_init(void);
-
-#endif /* _SPARC64_SSTATE_H */
diff --git a/arch/sparc/include/asm/starfire.h b/arch/sparc/include/asm/starfire.h
index 07bafd3..d56ce60a 100644
--- a/arch/sparc/include/asm/starfire.h
+++ b/arch/sparc/include/asm/starfire.h
@@ -12,7 +12,6 @@
extern int this_is_starfire;
extern void check_if_starfire(void);
-extern void starfire_cpu_setup(void);
extern int starfire_hard_smp_processor_id(void);
extern void starfire_hookup(int);
extern unsigned int starfire_translate(unsigned long imap, unsigned int upaid);
diff --git a/arch/sparc/include/asm/statfs.h b/arch/sparc/include/asm/statfs.h
index 5e937a7..55e607a 100644
--- a/arch/sparc/include/asm/statfs.h
+++ b/arch/sparc/include/asm/statfs.h
@@ -1,8 +1,6 @@
#ifndef ___ASM_SPARC_STATFS_H
#define ___ASM_SPARC_STATFS_H
-#if defined(__sparc__) && defined(__arch64__)
-#include <asm/statfs_64.h>
-#else
-#include <asm/statfs_32.h>
-#endif
+
+#include <asm-generic/statfs.h>
+
#endif
diff --git a/arch/sparc/include/asm/statfs_32.h b/arch/sparc/include/asm/statfs_32.h
deleted file mode 100644
index 304520f..0000000
--- a/arch/sparc/include/asm/statfs_32.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _SPARC_STATFS_H
-#define _SPARC_STATFS_H
-
-#include <asm-generic/statfs.h>
-
-#endif
diff --git a/arch/sparc/include/asm/statfs_64.h b/arch/sparc/include/asm/statfs_64.h
deleted file mode 100644
index 79b3c89..0000000
--- a/arch/sparc/include/asm/statfs_64.h
+++ /dev/null
@@ -1,54 +0,0 @@
-#ifndef _SPARC64_STATFS_H
-#define _SPARC64_STATFS_H
-
-#ifndef __KERNEL_STRICT_NAMES
-
-#include <linux/types.h>
-
-typedef __kernel_fsid_t fsid_t;
-
-#endif
-
-struct statfs {
- long f_type;
- long f_bsize;
- long f_blocks;
- long f_bfree;
- long f_bavail;
- long f_files;
- long f_ffree;
- __kernel_fsid_t f_fsid;
- long f_namelen;
- long f_frsize;
- long f_spare[5];
-};
-
-struct statfs64 {
- long f_type;
- long f_bsize;
- long f_blocks;
- long f_bfree;
- long f_bavail;
- long f_files;
- long f_ffree;
- __kernel_fsid_t f_fsid;
- long f_namelen;
- long f_frsize;
- long f_spare[5];
-};
-
-struct compat_statfs64 {
- __u32 f_type;
- __u32 f_bsize;
- __u64 f_blocks;
- __u64 f_bfree;
- __u64 f_bavail;
- __u64 f_files;
- __u64 f_ffree;
- __kernel_fsid_t f_fsid;
- __u32 f_namelen;
- __u32 f_frsize;
- __u32 f_spare[5];
-};
-
-#endif
diff --git a/arch/sparc/include/asm/sun4paddr.h b/arch/sparc/include/asm/sun4paddr.h
deleted file mode 100644
index d52985f..0000000
--- a/arch/sparc/include/asm/sun4paddr.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * sun4paddr.h: Various physical addresses on sun4 machines
- *
- * Copyright (C) 1997 Anton Blanchard (anton@progsoc.uts.edu.au)
- * Copyright (C) 1998 Chris Davis (cdavis@cois.on.ca)
- *
- * Now supports more sun4's
- */
-
-#ifndef _SPARC_SUN4PADDR_H
-#define _SPARC_SUN4PADDR_H
-
-#define SUN4_IE_PHYSADDR 0xf5000000
-#define SUN4_UNUSED_PHYSADDR 0
-
-/* these work for me */
-#define SUN4_200_MEMREG_PHYSADDR 0xf4000000
-#define SUN4_200_CLOCK_PHYSADDR 0xf3000000
-#define SUN4_200_BWTWO_PHYSADDR 0xfd000000
-#define SUN4_200_ETH_PHYSADDR 0xf6000000
-#define SUN4_200_SI_PHYSADDR 0xff200000
-
-/* these were here before */
-#define SUN4_300_MEMREG_PHYSADDR 0xf4000000
-#define SUN4_300_CLOCK_PHYSADDR 0xf2000000
-#define SUN4_300_TIMER_PHYSADDR 0xef000000
-#define SUN4_300_ETH_PHYSADDR 0xf9000000
-#define SUN4_300_BWTWO_PHYSADDR 0xfb400000
-#define SUN4_300_DMA_PHYSADDR 0xfa001000
-#define SUN4_300_ESP_PHYSADDR 0xfa000000
-
-/* Are these right? */
-#define SUN4_400_MEMREG_PHYSADDR 0xf4000000
-#define SUN4_400_CLOCK_PHYSADDR 0xf2000000
-#define SUN4_400_TIMER_PHYSADDR 0xef000000
-#define SUN4_400_ETH_PHYSADDR 0xf9000000
-#define SUN4_400_BWTWO_PHYSADDR 0xfb400000
-#define SUN4_400_DMA_PHYSADDR 0xfa001000
-#define SUN4_400_ESP_PHYSADDR 0xfa000000
-
-/*
- these are the actual values set and used in the code. Unused items set
- to SUN_UNUSED_PHYSADDR
- */
-
-extern int sun4_memreg_physaddr; /* memory register (ecc?) */
-extern int sun4_clock_physaddr; /* system clock */
-extern int sun4_timer_physaddr; /* timer, where applicable */
-extern int sun4_eth_physaddr; /* onboard ethernet (ie/le) */
-extern int sun4_si_physaddr; /* sun3 scsi adapter */
-extern int sun4_bwtwo_physaddr; /* onboard bw2 */
-extern int sun4_dma_physaddr; /* scsi dma */
-extern int sun4_esp_physaddr; /* esp scsi */
-extern int sun4_ie_physaddr; /* interrupt enable */
-
-#endif /* !(_SPARC_SUN4PADDR_H) */
diff --git a/arch/sparc/include/asm/sun4prom.h b/arch/sparc/include/asm/sun4prom.h
deleted file mode 100644
index 9c8b4cb..0000000
--- a/arch/sparc/include/asm/sun4prom.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * sun4prom.h -- interface to sun4 PROM monitor. We don't use most of this,
- * so most of these are just placeholders.
- */
-
-#ifndef _SUN4PROM_H_
-#define _SUN4PROM_H_
-
-/*
- * Although this looks similar to an romvec for a OpenProm machine, it is
- * actually closer to what was used in the Sun2 and Sun3.
- *
- * V2 entries exist only in version 2 PROMs and later, V3 in version 3 and later.
- *
- * Many of the function prototypes are guesses. Some are certainly wrong.
- * Use with care.
- */
-
-typedef struct {
- char *initSP; /* Initial system stack ptr */
- void (*startmon)(void); /* Initial PC for hardware */
- int *diagberr; /* Bus err handler for diags */
- struct linux_arguments_v0 **bootParam; /* Info for bootstrapped pgm */
- unsigned int *memorysize; /* Usable memory in bytes */
- unsigned char (*getchar)(void); /* Get char from input device */
- void (*putchar)(char); /* Put char to output device */
- int (*mayget)(void); /* Maybe get char, or -1 */
- int (*mayput)(int); /* Maybe put char, or -1 */
- unsigned char *echo; /* Should getchar echo? */
- unsigned char *insource; /* Input source selector */
- unsigned char *outsink; /* Output sink selector */
- int (*getkey)(void); /* Get next key if one exists */
- void (*initgetkey)(void); /* Initialize get key */
- unsigned int *translation; /* Kbd translation selector */
- unsigned char *keybid; /* Keyboard ID byte */
- int *screen_x; /* V2: Screen x pos (r/o) */
- int *screen_y; /* V2: Screen y pos (r/o) */
- struct keybuf *keybuf; /* Up/down keycode buffer */
- char *monid; /* Monitor version ID */
- void (*fbwritechar)(char); /* Write a character to FB */
- int *fbAddr; /* Address of frame buffer */
- char **font; /* Font table for FB */
- void (*fbwritestr)(char *); /* Write string to FB */
- void (*reboot)(char *); /* e.g. reboot("sd()vmlinux") */
- unsigned char *linebuf; /* The line input buffer */
- unsigned char **lineptr; /* Cur pointer into linebuf */
- int *linesize; /* length of line in linebuf */
- void (*getline)(char *); /* Get line from user */
- unsigned char (*getnextchar)(void); /* Get next char from linebuf */
- unsigned char (*peeknextchar)(void); /* Peek at next char */
- int *fbthere; /* =1 if frame buffer there */
- int (*getnum)(void); /* Grab hex num from line */
- int (*printf)(char *, ...); /* See prom_printf() instead */
- void (*printhex)(int); /* Format N digits in hex */
- unsigned char *leds; /* RAM copy of LED register */
- void (*setLEDs)(unsigned char *); /* Sets LED's and RAM copy */
- void (*NMIaddr)(void *); /* Addr for level 7 vector */
- void (*abortentry)(void); /* Entry for keyboard abort */
- int *nmiclock; /* Counts up in msec */
- int *FBtype; /* Frame buffer type */
- unsigned int romvecversion; /* Version number for this romvec */
- struct globram *globram; /* monitor global variables ??? */
- void * kbdaddr; /* Addr of keyboard in use */
- int *keyrinit; /* ms before kbd repeat */
- unsigned char *keyrtick; /* ms between repetitions */
- unsigned int *memoryavail; /* V1: Main mem usable size */
- long *resetaddr; /* where to jump on a reset */
- long *resetmap; /* pgmap entry for resetaddr */
- void (*exittomon)(void); /* Exit from user program */
- unsigned char **memorybitmap; /* V1: &{0 or &bits} */
- void (*setcxsegmap)(int ctxt, char *va, int pmeg); /* Set seg in any context */
- void (**vector_cmd)(void *); /* V2: Handler for 'v' cmd */
- unsigned long *expectedtrapsig; /* V3: Location of the expected trap signal */
- unsigned long *trapvectorbasetable; /* V3: Address of the trap vector table */
- int unused1;
- int unused2;
- int unused3;
- int unused4;
-} linux_sun4_romvec;
-
-extern linux_sun4_romvec *sun4_romvec;
-
-#endif /* _SUN4PROM_H_ */
diff --git a/arch/sparc/include/asm/system_32.h b/arch/sparc/include/asm/system_32.h
index b4b0244..8623fc4 100644
--- a/arch/sparc/include/asm/system_32.h
+++ b/arch/sparc/include/asm/system_32.h
@@ -34,13 +34,7 @@
extern enum sparc_cpu sparc_cpu_model;
-#ifndef CONFIG_SUN4
-#define ARCH_SUN4C_SUN4 (sparc_cpu_model==sun4c)
-#define ARCH_SUN4 0
-#else
-#define ARCH_SUN4C_SUN4 1
-#define ARCH_SUN4 1
-#endif
+#define ARCH_SUN4C (sparc_cpu_model==sun4c)
#define SUN4M_NCPUS 4 /* Architectural limit of sun4m. */
@@ -55,6 +49,7 @@
extern void sun_do_break(void);
extern int serial_console;
extern int stop_a_enabled;
+extern int scons_pwroff;
static inline int con_is_present(void)
{
diff --git a/arch/sparc/include/asm/system_64.h b/arch/sparc/include/asm/system_64.h
index db9e742..8759f2a 100644
--- a/arch/sparc/include/asm/system_64.h
+++ b/arch/sparc/include/asm/system_64.h
@@ -26,9 +26,8 @@
#define sparc_cpu_model sun4u
-/* This cannot ever be a sun4c nor sun4 :) That's just history. */
-#define ARCH_SUN4C_SUN4 0
-#define ARCH_SUN4 0
+/* This cannot ever be a sun4c :) That's just history. */
+#define ARCH_SUN4C 0
extern char reboot_command[];
@@ -118,6 +117,7 @@
extern void sun_do_break(void);
extern int stop_a_enabled;
+extern int scons_pwroff;
extern void fault_in_user_windows(void);
extern void synchronize_user_stack(void);
diff --git a/arch/sparc/include/asm/thread_info_32.h b/arch/sparc/include/asm/thread_info_32.h
index cbb892d..29899fd 100644
--- a/arch/sparc/include/asm/thread_info_32.h
+++ b/arch/sparc/include/asm/thread_info_32.h
@@ -80,11 +80,7 @@
/*
* thread information allocation
*/
-#if PAGE_SHIFT == 13
-#define THREAD_INFO_ORDER 0
-#else /* PAGE_SHIFT */
#define THREAD_INFO_ORDER 1
-#endif
#define __HAVE_ARCH_THREAD_INFO_ALLOCATOR
diff --git a/arch/sparc/include/asm/timer_32.h b/arch/sparc/include/asm/timer_32.h
index 361e538..2ec030e 100644
--- a/arch/sparc/include/asm/timer_32.h
+++ b/arch/sparc/include/asm/timer_32.h
@@ -9,96 +9,9 @@
#define _SPARC_TIMER_H
#include <asm/system.h> /* For SUN4M_NCPUS */
-#include <asm/sun4paddr.h>
#include <asm/btfixup.h>
-/* Timer structures. The interrupt timer has two properties which
- * are the counter (which is handled in do_timer in sched.c) and the limit.
- * This limit is where the timer's counter 'wraps' around. Oddly enough,
- * the sun4c timer when it hits the limit wraps back to 1 and not zero
- * thus when calculating the value at which it will fire a microsecond you
- * must adjust by one. Thanks SUN for designing such great hardware ;(
- */
-
-/* Note that I am only going to use the timer that interrupts at
- * Sparc IRQ 10. There is another one available that can fire at
- * IRQ 14. Currently it is left untouched, we keep the PROM's limit
- * register value and let the prom take these interrupts. This allows
- * L1-A to work.
- */
-
-struct sun4c_timer_info {
- __volatile__ unsigned int cur_count10;
- __volatile__ unsigned int timer_limit10;
- __volatile__ unsigned int cur_count14;
- __volatile__ unsigned int timer_limit14;
-};
-
-#define SUN4C_TIMER_PHYSADDR 0xf3000000
-#ifdef CONFIG_SUN4
-#define SUN_TIMER_PHYSADDR SUN4_300_TIMER_PHYSADDR
-#else
-#define SUN_TIMER_PHYSADDR SUN4C_TIMER_PHYSADDR
-#endif
-
-/* A sun4m has two blocks of registers which are probably of the same
- * structure. LSI Logic's L64851 is told to _decrement_ from the limit
- * value. Aurora behaves similarly but its limit value is compacted in
- * other fashion (it's wider). Documented fields are defined here.
- */
-
-/* As with the interrupt register, we have two classes of timer registers
- * which are per-cpu and master. Per-cpu timers only hit that cpu and are
- * only level 14 ticks, master timer hits all cpus and is level 10.
- */
-
-#define SUN4M_PRM_CNT_L 0x80000000
-#define SUN4M_PRM_CNT_LVALUE 0x7FFFFC00
-
-struct sun4m_timer_percpu_info {
- __volatile__ unsigned int l14_timer_limit; /* Initial value is 0x009c4000 */
- __volatile__ unsigned int l14_cur_count;
-
- /* This register appears to be write only and/or inaccessible
- * on Uni-Processor sun4m machines.
- */
- __volatile__ unsigned int l14_limit_noclear; /* Data access error is here */
-
- __volatile__ unsigned int cntrl; /* =1 after POST on Aurora */
- __volatile__ unsigned char space[PAGE_SIZE - 16];
-};
-
-struct sun4m_timer_regs {
- struct sun4m_timer_percpu_info cpu_timers[SUN4M_NCPUS];
- volatile unsigned int l10_timer_limit;
- volatile unsigned int l10_cur_count;
-
- /* Again, this appears to be write only and/or inaccessible
- * on uni-processor sun4m machines.
- */
- volatile unsigned int l10_limit_noclear;
-
- /* This register too, it must be magic. */
- volatile unsigned int foobar;
-
- volatile unsigned int cfg; /* equals zero at boot time... */
-};
-
-#define SUN4D_PRM_CNT_L 0x80000000
-#define SUN4D_PRM_CNT_LVALUE 0x7FFFFC00
-
-struct sun4d_timer_regs {
- volatile unsigned int l10_timer_limit;
- volatile unsigned int l10_cur_countx;
- volatile unsigned int l10_limit_noclear;
- volatile unsigned int ctrl;
- volatile unsigned int l10_cur_count;
-};
-
-extern struct sun4d_timer_regs *sun4d_timers;
-
extern __volatile__ unsigned int *master_l10_counter;
-extern __volatile__ unsigned int *master_l10_limit;
/* FIXME: Make do_[gs]ettimeofday btfixup calls */
BTFIXUPDEF_CALL(int, bus_do_settimeofday, struct timespec *tv)
diff --git a/arch/sparc/include/asm/vac-ops.h b/arch/sparc/include/asm/vac-ops.h
index d105276..a63e88e 100644
--- a/arch/sparc/include/asm/vac-ops.h
+++ b/arch/sparc/include/asm/vac-ops.h
@@ -76,11 +76,7 @@
* cacheable bit in the pte's of all such pages.
*/
-#ifdef CONFIG_SUN4
-#define S4CVAC_BADBITS 0x0001e000
-#else
#define S4CVAC_BADBITS 0x0000f000
-#endif
/* The following is true if vaddr1 and vaddr2 would cause
* a 'bad alias'.
@@ -94,10 +90,7 @@
*/
struct sun4c_vac_props {
unsigned int num_bytes; /* Size of the cache */
- unsigned int num_lines; /* Number of cache lines */
unsigned int do_hwflushes; /* Hardware flushing available? */
- enum { VAC_NONE, VAC_WRITE_THROUGH,
- VAC_WRITE_BACK } type; /* What type of VAC? */
unsigned int linesize; /* Size of each line in bytes */
unsigned int log2lsize; /* log2(linesize) */
unsigned int on; /* VAC is enabled */
diff --git a/arch/sparc/include/asm/vfc_ioctls.h b/arch/sparc/include/asm/vfc_ioctls.h
deleted file mode 100644
index af8b690..0000000
--- a/arch/sparc/include/asm/vfc_ioctls.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/* Copyright (c) 1996 by Manish Vachharajani */
-
-#ifndef _LINUX_VFC_IOCTLS_H_
-#define _LINUX_VFC_IOCTLS_H_
-
- /* IOCTLs */
-#define VFC_IOCTL(a) (('j' << 8) | a)
-#define VFCGCTRL (VFC_IOCTL (0)) /* get vfc attributes */
-#define VFCSCTRL (VFC_IOCTL (1)) /* set vfc attributes */
-#define VFCGVID (VFC_IOCTL (2)) /* get video decoder attributes */
-#define VFCSVID (VFC_IOCTL (3)) /* set video decoder attributes */
-#define VFCHUE (VFC_IOCTL (4)) /* set hue */
-#define VFCPORTCHG (VFC_IOCTL (5)) /* change port */
-#define VFCRDINFO (VFC_IOCTL (6)) /* read info */
-
- /* Options for setting the vfc attributes and status */
-#define MEMPRST 0x1 /* reset FIFO ptr. */
-#define CAPTRCMD 0x2 /* start capture and wait */
-#define DIAGMODE 0x3 /* diag mode */
-#define NORMMODE 0x4 /* normal mode */
-#define CAPTRSTR 0x5 /* start capture */
-#define CAPTRWAIT 0x6 /* wait for capture to finish */
-
-
- /* Options for the decoder */
-#define STD_NTSC 0x1 /* NTSC mode */
-#define STD_PAL 0x2 /* PAL mode */
-#define COLOR_ON 0x3 /* force color ON */
-#define MONO 0x4 /* force color OFF */
-
- /* Values returned by ioctl 2 */
-
-#define NO_LOCK 1
-#define NTSC_COLOR 2
-#define NTSC_NOCOLOR 3
-#define PAL_COLOR 4
-#define PAL_NOCOLOR 5
-
-/* Not too sure what this does yet */
- /* Options for setting Field number */
-#define ODD_FIELD 0x1
-#define EVEN_FIELD 0x0
-#define ACTIVE_ONLY 0x2
-#define NON_ACTIVE 0x0
-
-/* Debug options */
-#define VFC_I2C_SEND 0
-#define VFC_I2C_RECV 1
-
-struct vfc_debug_inout
-{
- unsigned long addr;
- unsigned long ret;
- unsigned long len;
- unsigned char __user *buffer;
-};
-
-#endif /* _LINUX_VFC_IOCTLS_H_ */
diff --git a/arch/sparc/include/asm/visasm.h b/arch/sparc/include/asm/visasm.h
index de797b9..39ca301 100644
--- a/arch/sparc/include/asm/visasm.h
+++ b/arch/sparc/include/asm/visasm.h
@@ -57,6 +57,7 @@
" " : : "i" (FPRS_FEF|FPRS_DU) :
"o5", "g1", "g2", "g3", "g7", "cc");
}
+extern int vis_emul(struct pt_regs *, unsigned int);
#endif
#endif /* _SPARC64_ASI_H */
diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile
index 6e03a2a..2d65820 100644
--- a/arch/sparc/kernel/Makefile
+++ b/arch/sparc/kernel/Makefile
@@ -13,15 +13,13 @@
time.o windows.o cpu.o devices.o \
tadpole.o tick14.o ptrace.o \
unaligned.o una_asm.o muldiv.o \
- prom.o of_device.o devres.o
+ prom.o of_device.o devres.o dma.o
devres-y = ../../../kernel/irq/devres.o
obj-$(CONFIG_PCI) += pcic.o
-obj-$(CONFIG_SUN4) += sun4setup.o
obj-$(CONFIG_SMP) += trampoline.o smp.o sun4m_smp.o sun4d_smp.o
obj-$(CONFIG_SUN_AUXIO) += auxio.o
-obj-$(CONFIG_PCI) += ebus.o
obj-$(CONFIG_SUN_PM) += apc.o pmc.o
obj-$(CONFIG_MODULES) += module.o sparc_ksyms.o
obj-$(CONFIG_SPARC_LED) += led.o
diff --git a/arch/sparc/kernel/apc.c b/arch/sparc/kernel/apc.c
index 5267d48..4dd1ba7 100644
--- a/arch/sparc/kernel/apc.c
+++ b/arch/sparc/kernel/apc.c
@@ -12,9 +12,10 @@
#include <linux/miscdevice.h>
#include <linux/smp_lock.h>
#include <linux/pm.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <asm/io.h>
-#include <asm/sbus.h>
#include <asm/oplib.h>
#include <asm/uaccess.h>
#include <asm/auxio.h>
@@ -29,11 +30,10 @@
#define APC_OBPNAME "power-management"
#define APC_DEVNAME "apc"
-volatile static u8 __iomem *regs;
-static int apc_regsize;
+static u8 __iomem *regs;
static int apc_no_idle __initdata = 0;
-#define apc_readb(offs) (sbus_readb(regs+offs))
+#define apc_readb(offs) (sbus_readb(regs+offs))
#define apc_writeb(val, offs) (sbus_writeb(val, regs+offs))
/* Specify "apc=noidle" on the kernel command line to
@@ -69,9 +69,9 @@
#endif
}
-static inline void apc_free(void)
+static inline void apc_free(struct of_device *op)
{
- sbus_iounmap(regs, apc_regsize);
+ of_iounmap(&op->resource[0], regs, resource_size(&op->resource[0]));
}
static int apc_open(struct inode *inode, struct file *f)
@@ -153,52 +153,56 @@
static struct miscdevice apc_miscdev = { APC_MINOR, APC_DEVNAME, &apc_fops };
-static int __init apc_probe(void)
+static int __devinit apc_probe(struct of_device *op,
+ const struct of_device_id *match)
{
- struct sbus_bus *sbus = NULL;
- struct sbus_dev *sdev = NULL;
- int iTmp = 0;
+ int err;
- for_each_sbus(sbus) {
- for_each_sbusdev(sdev, sbus) {
- if (!strcmp(sdev->prom_name, APC_OBPNAME)) {
- goto sbus_done;
- }
- }
- }
-
-sbus_done:
- if (!sdev) {
- return -ENODEV;
- }
-
- apc_regsize = sdev->reg_addrs[0].reg_size;
- regs = sbus_ioremap(&sdev->resource[0], 0,
- apc_regsize, APC_OBPNAME);
- if(!regs) {
+ regs = of_ioremap(&op->resource[0], 0,
+ resource_size(&op->resource[0]), APC_OBPNAME);
+ if (!regs) {
printk(KERN_ERR "%s: unable to map registers\n", APC_DEVNAME);
return -ENODEV;
}
- iTmp = misc_register(&apc_miscdev);
- if (iTmp != 0) {
+ err = misc_register(&apc_miscdev);
+ if (err) {
printk(KERN_ERR "%s: unable to register device\n", APC_DEVNAME);
- apc_free();
+ apc_free(op);
return -ENODEV;
}
/* Assign power management IDLE handler */
- if(!apc_no_idle)
+ if (!apc_no_idle)
pm_idle = apc_swift_idle;
printk(KERN_INFO "%s: power management initialized%s\n",
- APC_DEVNAME, apc_no_idle ? " (CPU idle disabled)" : "");
+ APC_DEVNAME, apc_no_idle ? " (CPU idle disabled)" : "");
+
return 0;
}
+static struct of_device_id __initdata apc_match[] = {
+ {
+ .name = APC_OBPNAME,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, apc_match);
+
+static struct of_platform_driver apc_driver = {
+ .name = "apc",
+ .match_table = apc_match,
+ .probe = apc_probe,
+};
+
+static int __init apc_init(void)
+{
+ return of_register_driver(&apc_driver, &of_bus_type);
+}
+
/* This driver is not critical to the boot process
* and is easiest to ioremap when SBus is already
* initialized, so we install ourselves thusly:
*/
-__initcall(apc_probe);
-
+__initcall(apc_init);
diff --git a/arch/sparc/kernel/auxio.c b/arch/sparc/kernel/auxio.c
index baf4ed3..09c8572 100644
--- a/arch/sparc/kernel/auxio.c
+++ b/arch/sparc/kernel/auxio.c
@@ -6,6 +6,8 @@
#include <linux/stddef.h>
#include <linux/init.h>
#include <linux/spinlock.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <asm/oplib.h>
#include <asm/io.h>
#include <asm/auxio.h>
@@ -59,7 +61,7 @@
r.flags = auxregs[0].which_io & 0xF;
r.start = auxregs[0].phys_addr;
r.end = auxregs[0].phys_addr + auxregs[0].reg_size - 1;
- auxio_register = sbus_ioremap(&r, 0, auxregs[0].reg_size, "auxio");
+ auxio_register = of_ioremap(&r, 0, auxregs[0].reg_size, "auxio");
/* Fix the address on sun4m and sun4c. */
if((((unsigned long) auxregs[0].phys_addr) & 3) == 3 ||
sparc_cpu_model == sun4c)
@@ -128,7 +130,7 @@
r.flags = regs.which_io & 0xF;
r.start = regs.phys_addr;
r.end = regs.phys_addr + regs.reg_size - 1;
- auxio_power_register = (unsigned char *) sbus_ioremap(&r, 0,
+ auxio_power_register = (unsigned char *) of_ioremap(&r, 0,
regs.reg_size, "auxpower");
/* Display a quick message on the console. */
diff --git a/arch/sparc/kernel/devices.c b/arch/sparc/kernel/devices.c
index b240b88..ad656b0 100644
--- a/arch/sparc/kernel/devices.c
+++ b/arch/sparc/kernel/devices.c
@@ -143,7 +143,7 @@
#endif
clock_stop_probe();
- if (ARCH_SUN4C_SUN4)
+ if (ARCH_SUN4C)
sun4c_probe_memerr_reg();
return;
diff --git a/arch/sparc/kernel/dma.c b/arch/sparc/kernel/dma.c
new file mode 100644
index 0000000..ebc8403
--- /dev/null
+++ b/arch/sparc/kernel/dma.c
@@ -0,0 +1,227 @@
+/* dma.c: PCI and SBUS DMA accessors for 32-bit sparc.
+ *
+ * Copyright (C) 2008 David S. Miller <davem@davemloft.net>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/scatterlist.h>
+#include <linux/mm.h>
+
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+#endif
+
+#include "dma.h"
+
+int dma_supported(struct device *dev, u64 mask)
+{
+#ifdef CONFIG_PCI
+ if (dev->bus == &pci_bus_type)
+ return pci_dma_supported(to_pci_dev(dev), mask);
+#endif
+ return 0;
+}
+EXPORT_SYMBOL(dma_supported);
+
+int dma_set_mask(struct device *dev, u64 dma_mask)
+{
+#ifdef CONFIG_PCI
+ if (dev->bus == &pci_bus_type)
+ return pci_set_dma_mask(to_pci_dev(dev), dma_mask);
+#endif
+ return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL(dma_set_mask);
+
+void *dma_alloc_coherent(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, gfp_t flag)
+{
+#ifdef CONFIG_PCI
+ if (dev->bus == &pci_bus_type)
+ return pci_alloc_consistent(to_pci_dev(dev), size, dma_handle);
+#endif
+ return sbus_alloc_consistent(dev, size, dma_handle);
+}
+EXPORT_SYMBOL(dma_alloc_coherent);
+
+void dma_free_coherent(struct device *dev, size_t size,
+ void *cpu_addr, dma_addr_t dma_handle)
+{
+#ifdef CONFIG_PCI
+ if (dev->bus == &pci_bus_type) {
+ pci_free_consistent(to_pci_dev(dev), size,
+ cpu_addr, dma_handle);
+ return;
+ }
+#endif
+ sbus_free_consistent(dev, size, cpu_addr, dma_handle);
+}
+EXPORT_SYMBOL(dma_free_coherent);
+
+dma_addr_t dma_map_single(struct device *dev, void *cpu_addr,
+ size_t size, enum dma_data_direction direction)
+{
+#ifdef CONFIG_PCI
+ if (dev->bus == &pci_bus_type)
+ return pci_map_single(to_pci_dev(dev), cpu_addr,
+ size, (int)direction);
+#endif
+ return sbus_map_single(dev, cpu_addr, size, (int)direction);
+}
+EXPORT_SYMBOL(dma_map_single);
+
+void dma_unmap_single(struct device *dev, dma_addr_t dma_addr,
+ size_t size,
+ enum dma_data_direction direction)
+{
+#ifdef CONFIG_PCI
+ if (dev->bus == &pci_bus_type) {
+ pci_unmap_single(to_pci_dev(dev), dma_addr,
+ size, (int)direction);
+ return;
+ }
+#endif
+ sbus_unmap_single(dev, dma_addr, size, (int)direction);
+}
+EXPORT_SYMBOL(dma_unmap_single);
+
+dma_addr_t dma_map_page(struct device *dev, struct page *page,
+ unsigned long offset, size_t size,
+ enum dma_data_direction direction)
+{
+#ifdef CONFIG_PCI
+ if (dev->bus == &pci_bus_type)
+ return pci_map_page(to_pci_dev(dev), page, offset,
+ size, (int)direction);
+#endif
+ return sbus_map_single(dev, page_address(page) + offset,
+ size, (int)direction);
+}
+EXPORT_SYMBOL(dma_map_page);
+
+void dma_unmap_page(struct device *dev, dma_addr_t dma_address,
+ size_t size, enum dma_data_direction direction)
+{
+#ifdef CONFIG_PCI
+ if (dev->bus == &pci_bus_type) {
+ pci_unmap_page(to_pci_dev(dev), dma_address,
+ size, (int)direction);
+ return;
+ }
+#endif
+ sbus_unmap_single(dev, dma_address, size, (int)direction);
+}
+EXPORT_SYMBOL(dma_unmap_page);
+
+int dma_map_sg(struct device *dev, struct scatterlist *sg,
+ int nents, enum dma_data_direction direction)
+{
+#ifdef CONFIG_PCI
+ if (dev->bus == &pci_bus_type)
+ return pci_map_sg(to_pci_dev(dev), sg, nents, (int)direction);
+#endif
+ return sbus_map_sg(dev, sg, nents, direction);
+}
+EXPORT_SYMBOL(dma_map_sg);
+
+void dma_unmap_sg(struct device *dev, struct scatterlist *sg,
+ int nents, enum dma_data_direction direction)
+{
+#ifdef CONFIG_PCI
+ if (dev->bus == &pci_bus_type) {
+ pci_unmap_sg(to_pci_dev(dev), sg, nents, (int)direction);
+ return;
+ }
+#endif
+ sbus_unmap_sg(dev, sg, nents, (int)direction);
+}
+EXPORT_SYMBOL(dma_unmap_sg);
+
+void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle,
+ size_t size, enum dma_data_direction direction)
+{
+#ifdef CONFIG_PCI
+ if (dev->bus == &pci_bus_type) {
+ pci_dma_sync_single_for_cpu(to_pci_dev(dev), dma_handle,
+ size, (int)direction);
+ return;
+ }
+#endif
+ sbus_dma_sync_single_for_cpu(dev, dma_handle, size, (int) direction);
+}
+EXPORT_SYMBOL(dma_sync_single_for_cpu);
+
+void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle,
+ size_t size, enum dma_data_direction direction)
+{
+#ifdef CONFIG_PCI
+ if (dev->bus == &pci_bus_type) {
+ pci_dma_sync_single_for_device(to_pci_dev(dev), dma_handle,
+ size, (int)direction);
+ return;
+ }
+#endif
+ sbus_dma_sync_single_for_device(dev, dma_handle, size, (int) direction);
+}
+EXPORT_SYMBOL(dma_sync_single_for_device);
+
+void dma_sync_single_range_for_cpu(struct device *dev,
+ dma_addr_t dma_handle,
+ unsigned long offset,
+ size_t size,
+ enum dma_data_direction direction)
+{
+ dma_sync_single_for_cpu(dev, dma_handle+offset, size, direction);
+}
+EXPORT_SYMBOL(dma_sync_single_range_for_cpu);
+
+void dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle,
+ unsigned long offset, size_t size,
+ enum dma_data_direction direction)
+{
+ dma_sync_single_for_device(dev, dma_handle+offset, size, direction);
+}
+EXPORT_SYMBOL(dma_sync_single_range_for_device);
+
+void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg,
+ int nelems, enum dma_data_direction direction)
+{
+#ifdef CONFIG_PCI
+ if (dev->bus == &pci_bus_type) {
+ pci_dma_sync_sg_for_cpu(to_pci_dev(dev), sg,
+ nelems, (int)direction);
+ return;
+ }
+#endif
+ BUG();
+}
+EXPORT_SYMBOL(dma_sync_sg_for_cpu);
+
+void dma_sync_sg_for_device(struct device *dev,
+ struct scatterlist *sg, int nelems,
+ enum dma_data_direction direction)
+{
+#ifdef CONFIG_PCI
+ if (dev->bus == &pci_bus_type) {
+ pci_dma_sync_sg_for_device(to_pci_dev(dev), sg,
+ nelems, (int)direction);
+ return;
+ }
+#endif
+ BUG();
+}
+EXPORT_SYMBOL(dma_sync_sg_for_device);
+
+int dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
+{
+ return (dma_addr == DMA_ERROR_CODE);
+}
+EXPORT_SYMBOL(dma_mapping_error);
+
+int dma_get_cache_alignment(void)
+{
+ return 32;
+}
+EXPORT_SYMBOL(dma_get_cache_alignment);
diff --git a/arch/sparc/kernel/dma.h b/arch/sparc/kernel/dma.h
new file mode 100644
index 0000000..f8d8951
--- /dev/null
+++ b/arch/sparc/kernel/dma.h
@@ -0,0 +1,14 @@
+void *sbus_alloc_consistent(struct device *dev, long len, u32 *dma_addrp);
+void sbus_free_consistent(struct device *dev, long n, void *p, u32 ba);
+dma_addr_t sbus_map_single(struct device *dev, void *va,
+ size_t len, int direction);
+void sbus_unmap_single(struct device *dev, dma_addr_t ba,
+ size_t n, int direction);
+int sbus_map_sg(struct device *dev, struct scatterlist *sg,
+ int n, int direction);
+void sbus_unmap_sg(struct device *dev, struct scatterlist *sg,
+ int n, int direction);
+void sbus_dma_sync_single_for_cpu(struct device *dev, dma_addr_t ba,
+ size_t size, int direction);
+void sbus_dma_sync_single_for_device(struct device *dev, dma_addr_t ba,
+ size_t size, int direction);
diff --git a/arch/sparc/kernel/ebus.c b/arch/sparc/kernel/ebus.c
deleted file mode 100644
index 9729423..0000000
--- a/arch/sparc/kernel/ebus.c
+++ /dev/null
@@ -1,393 +0,0 @@
-/*
- * ebus.c: PCI to EBus bridge device.
- *
- * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
- *
- * Adopted for sparc by V. Roganov and G. Raiko.
- * Fixes for different platforms by Pete Zaitcev.
- */
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-
-#include <asm/system.h>
-#include <asm/page.h>
-#include <asm/pbm.h>
-#include <asm/ebus.h>
-#include <asm/io.h>
-#include <asm/oplib.h>
-#include <asm/prom.h>
-#include <asm/bpp.h>
-
-struct linux_ebus *ebus_chain = NULL;
-
-/* We are together with pcic.c under CONFIG_PCI. */
-extern unsigned int pcic_pin_to_irq(unsigned int, const char *name);
-
-/*
- * IRQ Blacklist
- * Here we list PROMs and systems that are known to supply crap as IRQ numbers.
- */
-struct ebus_device_irq {
- char *name;
- unsigned int pin;
-};
-
-struct ebus_system_entry {
- char *esname;
- struct ebus_device_irq *ipt;
-};
-
-static struct ebus_device_irq je1_1[] = {
- { "8042", 3 },
- { "SUNW,CS4231", 0 },
- { "parallel", 0 },
- { "se", 2 },
- { NULL, 0 }
-};
-
-/*
- * Gleb's JE1 supplied reasonable pin numbers, but mine did not (OBP 2.32).
- * Blacklist the sucker... Note that Gleb's system will work.
- */
-static struct ebus_system_entry ebus_blacklist[] = {
- { "SUNW,JavaEngine1", je1_1 },
- { NULL, NULL }
-};
-
-static struct ebus_device_irq *ebus_blackp = NULL;
-
-/*
- */
-static inline unsigned long ebus_alloc(size_t size)
-{
- return (unsigned long)kmalloc(size, GFP_ATOMIC);
-}
-
-/*
- */
-static int __init ebus_blacklist_irq(const char *name)
-{
- struct ebus_device_irq *dp;
-
- if ((dp = ebus_blackp) != NULL) {
- for (; dp->name != NULL; dp++) {
- if (strcmp(name, dp->name) == 0) {
- return pcic_pin_to_irq(dp->pin, name);
- }
- }
- }
- return 0;
-}
-
-static void __init fill_ebus_child(struct device_node *dp,
- struct linux_ebus_child *dev)
-{
- const int *regs;
- const int *irqs;
- int i, len;
-
- dev->prom_node = dp;
- regs = of_get_property(dp, "reg", &len);
- if (!regs)
- len = 0;
- dev->num_addrs = len / sizeof(regs[0]);
-
- for (i = 0; i < dev->num_addrs; i++) {
- if (regs[i] >= dev->parent->num_addrs) {
- prom_printf("UGH: property for %s was %d, need < %d\n",
- dev->prom_node->name, len,
- dev->parent->num_addrs);
- panic(__func__);
- }
-
- /* XXX resource */
- dev->resource[i].start =
- dev->parent->resource[regs[i]].start;
- }
-
- for (i = 0; i < PROMINTR_MAX; i++)
- dev->irqs[i] = PCI_IRQ_NONE;
-
- if ((dev->irqs[0] = ebus_blacklist_irq(dev->prom_node->name)) != 0) {
- dev->num_irqs = 1;
- } else {
- irqs = of_get_property(dp, "interrupts", &len);
- if (!irqs) {
- dev->num_irqs = 0;
- dev->irqs[0] = 0;
- if (dev->parent->num_irqs != 0) {
- dev->num_irqs = 1;
- dev->irqs[0] = dev->parent->irqs[0];
- }
- } else {
- dev->num_irqs = len / sizeof(irqs[0]);
- if (irqs[0] == 0 || irqs[0] >= 8) {
- /*
- * XXX Zero is a valid pin number...
- * This works as long as Ebus is not wired
- * to INTA#.
- */
- printk("EBUS: %s got bad irq %d from PROM\n",
- dev->prom_node->name, irqs[0]);
- dev->num_irqs = 0;
- dev->irqs[0] = 0;
- } else {
- dev->irqs[0] =
- pcic_pin_to_irq(irqs[0],
- dev->prom_node->name);
- }
- }
- }
-}
-
-static void __init fill_ebus_device(struct device_node *dp,
- struct linux_ebus_device *dev)
-{
- const struct linux_prom_registers *regs;
- struct linux_ebus_child *child;
- struct dev_archdata *sd;
- const int *irqs;
- int i, n, len;
- unsigned long baseaddr;
-
- dev->prom_node = dp;
-
- regs = of_get_property(dp, "reg", &len);
- if (!regs)
- len = 0;
- if (len % sizeof(struct linux_prom_registers)) {
- prom_printf("UGH: proplen for %s was %d, need multiple of %d\n",
- dev->prom_node->name, len,
- (int)sizeof(struct linux_prom_registers));
- panic(__func__);
- }
- dev->num_addrs = len / sizeof(struct linux_prom_registers);
-
- for (i = 0; i < dev->num_addrs; i++) {
- /*
- * XXX Collect JE-1 PROM
- *
- * Example - JS-E with 3.11:
- * /ebus
- * regs
- * 0x00000000, 0x0, 0x00000000, 0x0, 0x00000000,
- * 0x82000010, 0x0, 0xf0000000, 0x0, 0x01000000,
- * 0x82000014, 0x0, 0x38800000, 0x0, 0x00800000,
- * ranges
- * 0x00, 0x00000000, 0x02000010, 0x0, 0x0, 0x01000000,
- * 0x01, 0x01000000, 0x02000014, 0x0, 0x0, 0x00800000,
- * /ebus/8042
- * regs
- * 0x00000001, 0x00300060, 0x00000008,
- * 0x00000001, 0x00300060, 0x00000008,
- */
- n = regs[i].which_io;
- if (n >= 4) {
- /* XXX This is copied from old JE-1 by Gleb. */
- n = (regs[i].which_io - 0x10) >> 2;
- } else {
- ;
- }
-
-/*
- * XXX Now as we have regions, why don't we make an on-demand allocation...
- */
- dev->resource[i].start = 0;
- if ((baseaddr = dev->bus->self->resource[n].start +
- regs[i].phys_addr) != 0) {
- /* dev->resource[i].name = dev->prom_name; */
- if ((baseaddr = (unsigned long) ioremap(baseaddr,
- regs[i].reg_size)) == 0) {
- panic("ebus: unable to remap dev %s",
- dev->prom_node->name);
- }
- }
- dev->resource[i].start = baseaddr; /* XXX Unaligned */
- }
-
- for (i = 0; i < PROMINTR_MAX; i++)
- dev->irqs[i] = PCI_IRQ_NONE;
-
- if ((dev->irqs[0] = ebus_blacklist_irq(dev->prom_node->name)) != 0) {
- dev->num_irqs = 1;
- } else {
- irqs = of_get_property(dp, "interrupts", &len);
- if (!irqs) {
- dev->num_irqs = 0;
- if ((dev->irqs[0] = dev->bus->self->irq) != 0) {
- dev->num_irqs = 1;
-/* P3 */ /* printk("EBUS: child %s irq %d from parent\n", dev->prom_name, dev->irqs[0]); */
- }
- } else {
- dev->num_irqs = 1; /* dev->num_irqs = len / sizeof(irqs[0]); */
- if (irqs[0] == 0 || irqs[0] >= 8) {
- /* See above for the parent. XXX */
- printk("EBUS: %s got bad irq %d from PROM\n",
- dev->prom_node->name, irqs[0]);
- dev->num_irqs = 0;
- dev->irqs[0] = 0;
- } else {
- dev->irqs[0] =
- pcic_pin_to_irq(irqs[0],
- dev->prom_node->name);
- }
- }
- }
-
- sd = &dev->ofdev.dev.archdata;
- sd->prom_node = dp;
- sd->op = &dev->ofdev;
- sd->iommu = dev->bus->ofdev.dev.parent->archdata.iommu;
-
- dev->ofdev.node = dp;
- dev->ofdev.dev.parent = &dev->bus->ofdev.dev;
- dev->ofdev.dev.bus = &ebus_bus_type;
- sprintf(dev->ofdev.dev.bus_id, "ebus[%08x]", dp->node);
-
- /* Register with core */
- if (of_device_register(&dev->ofdev) != 0)
- printk(KERN_DEBUG "ebus: device registration error for %s!\n",
- dp->path_component_name);
-
- if ((dp = dp->child) != NULL) {
- dev->children = (struct linux_ebus_child *)
- ebus_alloc(sizeof(struct linux_ebus_child));
-
- child = dev->children;
- child->next = NULL;
- child->parent = dev;
- child->bus = dev->bus;
- fill_ebus_child(dp, child);
-
- while ((dp = dp->sibling) != NULL) {
- child->next = (struct linux_ebus_child *)
- ebus_alloc(sizeof(struct linux_ebus_child));
-
- child = child->next;
- child->next = NULL;
- child->parent = dev;
- child->bus = dev->bus;
- fill_ebus_child(dp, child);
- }
- }
-}
-
-void __init ebus_init(void)
-{
- const struct linux_prom_pci_registers *regs;
- struct linux_pbm_info *pbm;
- struct linux_ebus_device *dev;
- struct linux_ebus *ebus;
- struct ebus_system_entry *sp;
- struct pci_dev *pdev;
- struct pcidev_cookie *cookie;
- struct device_node *dp;
- struct resource *p;
- unsigned short pci_command;
- int len, reg, nreg;
- int num_ebus = 0;
-
- dp = of_find_node_by_path("/");
- for (sp = ebus_blacklist; sp->esname != NULL; sp++) {
- if (strcmp(dp->name, sp->esname) == 0) {
- ebus_blackp = sp->ipt;
- break;
- }
- }
-
- pdev = pci_get_device(PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_EBUS, NULL);
- if (!pdev)
- return;
-
- cookie = pdev->sysdata;
- dp = cookie->prom_node;
-
- ebus_chain = ebus = (struct linux_ebus *)
- ebus_alloc(sizeof(struct linux_ebus));
- ebus->next = NULL;
-
- while (dp) {
- struct device_node *nd;
-
- ebus->prom_node = dp;
- ebus->self = pdev;
- ebus->parent = pbm = cookie->pbm;
-
- /* Enable BUS Master. */
- pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
- pci_command |= PCI_COMMAND_MASTER;
- pci_write_config_word(pdev, PCI_COMMAND, pci_command);
-
- regs = of_get_property(dp, "reg", &len);
- if (!regs) {
- prom_printf("%s: can't find reg property\n",
- __func__);
- prom_halt();
- }
- nreg = len / sizeof(struct linux_prom_pci_registers);
-
- p = &ebus->self->resource[0];
- for (reg = 0; reg < nreg; reg++) {
- if (!(regs[reg].which_io & 0x03000000))
- continue;
-
- (p++)->start = regs[reg].phys_lo;
- }
-
- ebus->ofdev.node = dp;
- ebus->ofdev.dev.parent = &pdev->dev;
- ebus->ofdev.dev.bus = &ebus_bus_type;
- sprintf(ebus->ofdev.dev.bus_id, "ebus%d", num_ebus);
-
- /* Register with core */
- if (of_device_register(&ebus->ofdev) != 0)
- printk(KERN_DEBUG "ebus: device registration error for %s!\n",
- dp->path_component_name);
-
-
- nd = dp->child;
- if (!nd)
- goto next_ebus;
-
- ebus->devices = (struct linux_ebus_device *)
- ebus_alloc(sizeof(struct linux_ebus_device));
-
- dev = ebus->devices;
- dev->next = NULL;
- dev->children = NULL;
- dev->bus = ebus;
- fill_ebus_device(nd, dev);
-
- while ((nd = nd->sibling) != NULL) {
- dev->next = (struct linux_ebus_device *)
- ebus_alloc(sizeof(struct linux_ebus_device));
-
- dev = dev->next;
- dev->next = NULL;
- dev->children = NULL;
- dev->bus = ebus;
- fill_ebus_device(nd, dev);
- }
-
- next_ebus:
- pdev = pci_get_device(PCI_VENDOR_ID_SUN,
- PCI_DEVICE_ID_SUN_EBUS, pdev);
- if (!pdev)
- break;
-
- cookie = pdev->sysdata;
- dp = cookie->prom_node;
-
- ebus->next = (struct linux_ebus *)
- ebus_alloc(sizeof(struct linux_ebus));
- ebus = ebus->next;
- ebus->next = NULL;
- ++num_ebus;
- }
- if (pdev)
- pci_dev_put(pdev);
-}
diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S
index e8cdf71..faf9ccd 100644
--- a/arch/sparc/kernel/entry.S
+++ b/arch/sparc/kernel/entry.S
@@ -20,11 +20,7 @@
#include <asm/memreg.h>
#include <asm/page.h>
#include <asm/pgtable.h>
-#ifdef CONFIG_SUN4
-#include <asm/pgtsun4.h>
-#else
#include <asm/pgtsun4c.h>
-#endif
#include <asm/winmacro.h>
#include <asm/signal.h>
#include <asm/obio.h>
@@ -276,17 +272,18 @@
*/
maybe_smp4m_msg:
GET_PROCESSOR4M_ID(o3)
- set sun4m_interrupts, %l5
- ld [%l5], %o5
+ sethi %hi(sun4m_irq_percpu), %l5
+ sll %o3, 2, %o3
+ or %l5, %lo(sun4m_irq_percpu), %o5
sethi %hi(0x40000000), %o2
- sll %o3, 12, %o3
ld [%o5 + %o3], %o1
- andcc %o1, %o2, %g0
+ ld [%o1 + 0x00], %o3 ! sun4m_irq_percpu[cpu]->pending
+ andcc %o3, %o2, %g0
be,a smp4m_ticker
cmp %l7, 14
- st %o2, [%o5 + 0x4]
+ st %o2, [%o1 + 0x04] ! sun4m_irq_percpu[cpu]->clear=0x40000000
WRITE_PAUSE
- ld [%o5], %g0
+ ld [%o1 + 0x00], %g0 ! sun4m_irq_percpu[cpu]->pending
WRITE_PAUSE
or %l0, PSR_PIL, %l4
wr %l4, 0x0, %psr
@@ -304,16 +301,16 @@
SAVE_ALL
sethi %hi(0x80000000), %o2
GET_PROCESSOR4M_ID(o0)
- set sun4m_interrupts, %l5
- ld [%l5], %o5
- sll %o0, 12, %o0
- add %o5, %o0, %o5
- ld [%o5], %o3
+ sethi %hi(sun4m_irq_percpu), %l5
+ or %l5, %lo(sun4m_irq_percpu), %o5
+ sll %o0, 2, %o0
+ ld [%o5 + %o0], %o5
+ ld [%o5 + 0x00], %o3 ! sun4m_irq_percpu[cpu]->pending
andcc %o3, %o2, %g0
be 1f ! Must be an NMI async memory error
- st %o2, [%o5 + 4]
+ st %o2, [%o5 + 0x04] ! sun4m_irq_percpu[cpu]->clear=0x80000000
WRITE_PAUSE
- ld [%o5], %g0
+ ld [%o5 + 0x00], %g0 ! sun4m_irq_percpu[cpu]->pending
WRITE_PAUSE
or %l0, PSR_PIL, %l4
wr %l4, 0x0, %psr
@@ -327,12 +324,11 @@
1:
/* NMI async memory error handling. */
sethi %hi(0x80000000), %l4
- sethi %hi(0x4000), %o3
- sub %o5, %o0, %o5
- add %o5, %o3, %l5
- st %l4, [%l5 + 0xc]
+ sethi %hi(sun4m_irq_global), %o5
+ ld [%o5 + %lo(sun4m_irq_global)], %l5
+ st %l4, [%l5 + 0x0c] ! sun4m_irq_global->mask_set=0x80000000
WRITE_PAUSE
- ld [%l5], %g0
+ ld [%l5 + 0x00], %g0 ! sun4m_irq_global->pending
WRITE_PAUSE
or %l0, PSR_PIL, %l4
wr %l4, 0x0, %psr
@@ -341,9 +337,9 @@
WRITE_PAUSE
call sun4m_nmi
nop
- st %l4, [%l5 + 0x8]
+ st %l4, [%l5 + 0x08] ! sun4m_irq_global->mask_clear=0x80000000
WRITE_PAUSE
- ld [%l5], %g0
+ ld [%l5 + 0x00], %g0 ! sun4m_irq_global->pending
WRITE_PAUSE
RESTORE_ALL
@@ -775,11 +771,7 @@
* Ugly, but we cant use hardware flushing on the sun4 and we'd require
* two instructions (Anton)
*/
-#ifdef CONFIG_SUN4
-vac_hwflush_patch1_on: nop
-#else
vac_hwflush_patch1_on: addcc %l7, -PAGE_SIZE, %l7
-#endif
vac_hwflush_patch2_on: sta %g0, [%l3 + %l7] ASI_HWFLUSHSEG
@@ -798,42 +790,10 @@
! %l7 = 1 for textfault
! We want error in %l5, vaddr in %l6
sun4c_fault:
-#ifdef CONFIG_SUN4
- sethi %hi(sun4c_memerr_reg), %l4
- ld [%l4+%lo(sun4c_memerr_reg)], %l4 ! memerr ctrl reg addr
- ld [%l4], %l6 ! memerr ctrl reg
- ld [%l4 + 4], %l5 ! memerr vaddr reg
- andcc %l6, 0x80, %g0 ! check for error type
- st %g0, [%l4 + 4] ! clear the error
- be 0f ! normal error
- sethi %hi(AC_BUS_ERROR), %l4 ! bus err reg addr
-
- call prom_halt ! something weird happened
- ! what exactly did happen?
- ! what should we do here?
-
-0: or %l4, %lo(AC_BUS_ERROR), %l4 ! bus err reg addr
- lduba [%l4] ASI_CONTROL, %l6 ! bus err reg
-
- cmp %l7, 1 ! text fault?
- be 1f ! yes
- nop
-
- ld [%l1], %l4 ! load instruction that caused fault
- srl %l4, 21, %l4
- andcc %l4, 1, %g0 ! store instruction?
-
- be 1f ! no
- sethi %hi(SUN4C_SYNC_BADWRITE), %l4 ! yep
- ! %lo(SUN4C_SYNC_BADWRITE) = 0
- or %l4, %l6, %l6 ! set write bit to emulate sun4c
-1:
-#else
sethi %hi(AC_SYNC_ERR), %l4
add %l4, 0x4, %l6 ! AC_SYNC_VA in %l6
lda [%l6] ASI_CONTROL, %l5 ! Address
lda [%l4] ASI_CONTROL, %l6 ! Error, retained for a bit
-#endif
andn %l5, 0xfff, %l5 ! Encode all info into l7
srl %l6, 14, %l4
@@ -880,12 +840,7 @@
or %l4, %lo(swapper_pg_dir), %l4
sll %l6, 2, %l6
ld [%l4 + %l6], %l4
-#ifdef CONFIG_SUN4
- sethi %hi(PAGE_MASK), %l6
- andcc %l4, %l6, %g0
-#else
andcc %l4, PAGE_MASK, %g0
-#endif
be sun4c_fault_fromuser
lduXa [%l5] ASI_SEGMAP, %l4
@@ -937,11 +892,7 @@
ld [%l6 + 0x08], %l3 ! tmp = entry->vaddr
! Flush segment from the cache.
-#ifdef CONFIG_SUN4
- sethi %hi((128 * 1024)), %l7
-#else
sethi %hi((64 * 1024)), %l7
-#endif
9:
vac_hwflush_patch1:
vac_linesize_patch:
@@ -1029,12 +980,7 @@
or %l4, %lo(swapper_pg_dir), %l4
sll %l3, 2, %l3
ld [%l4 + %l3], %l4
-#ifndef CONFIG_SUN4
and %l4, PAGE_MASK, %l4
-#else
- sethi %hi(PAGE_MASK), %l6
- and %l4, %l6, %l4
-#endif
srl %l5, (PAGE_SHIFT - 2), %l6
and %l6, ((SUN4C_PTRS_PER_PTE - 1) << 2), %l6
diff --git a/arch/sparc/kernel/head.S b/arch/sparc/kernel/head.S
index 50d9a16..2d325fd 100644
--- a/arch/sparc/kernel/head.S
+++ b/arch/sparc/kernel/head.S
@@ -63,15 +63,9 @@
.align 4
-#ifndef CONFIG_SUN4
sun4_notsup:
- .asciz "Sparc-Linux sun4 needs a specially compiled kernel, turn CONFIG_SUN4 on.\n\n"
+ .asciz "Sparc-Linux sun4 support does no longer exist.\n\n"
.align 4
-#else
-sun4cdm_notsup:
- .asciz "Kernel compiled with CONFIG_SUN4 cannot run on SUN4C/SUN4M/SUN4D\nTurn CONFIG_SUN4 off.\n\n"
- .align 4
-#endif
sun4e_notsup:
.asciz "Sparc-Linux sun4e support does not exist\n\n"
@@ -780,15 +774,6 @@
nop
found_version:
-#ifdef CONFIG_SUN4
-/* For people who try sun4 kernels, even if Configure.help advises them. */
- ld [%g7 + 0x68], %o1
- set sun4cdm_notsup, %o0
- call %o1
- nop
- b halt_me
- nop
-#endif
/* Get the machine type via the mysterious romvec node operations. */
add %g7, 0x1c, %l1
@@ -1150,15 +1135,6 @@
nop
sun4_init:
-#ifdef CONFIG_SUN4
-/* There, happy now Adrian? */
- set cputypval, %o2 ! Let everyone know we
- set ' ', %o0 ! are a "sun4 " architecture
- stb %o0, [%o2 + 0x4]
-
- b got_prop
- nop
-#else
sethi %hi(SUN4_PROM_VECTOR+0x84), %o1
ld [%o1 + %lo(SUN4_PROM_VECTOR+0x84)], %o1
set sun4_notsup, %o0
@@ -1170,7 +1146,7 @@
nop
1: ba 1b ! Cannot exit into KMON
nop
-#endif
+
no_sun4e_here:
ld [%g7 + 0x68], %o1
set sun4e_notsup, %o0
diff --git a/arch/sparc/kernel/idprom.c b/arch/sparc/kernel/idprom.c
index fc511f3..223a658 100644
--- a/arch/sparc/kernel/idprom.c
+++ b/arch/sparc/kernel/idprom.c
@@ -12,10 +12,6 @@
#include <asm/oplib.h>
#include <asm/idprom.h>
#include <asm/machines.h> /* Fun with Sun released architectures. */
-#ifdef CONFIG_SUN4
-#include <asm/sun4paddr.h>
-extern void sun4setup(void);
-#endif
struct idprom *idprom;
static struct idprom idprom_buffer;
@@ -101,7 +97,4 @@
idprom->id_ethaddr[0], idprom->id_ethaddr[1],
idprom->id_ethaddr[2], idprom->id_ethaddr[3],
idprom->id_ethaddr[4], idprom->id_ethaddr[5]);
-#ifdef CONFIG_SUN4
- sun4setup();
-#endif
}
diff --git a/arch/sparc/kernel/ioport.c b/arch/sparc/kernel/ioport.c
index 2a8a847..4f025b3 100644
--- a/arch/sparc/kernel/ioport.c
+++ b/arch/sparc/kernel/ioport.c
@@ -42,10 +42,13 @@
#include <asm/vaddrs.h>
#include <asm/oplib.h>
#include <asm/prom.h>
-#include <asm/sbus.h>
#include <asm/page.h>
#include <asm/pgalloc.h>
#include <asm/dma.h>
+#include <asm/iommu.h>
+#include <asm/io-unit.h>
+
+#include "dma.h"
#define mmu_inval_dma_area(p, l) /* Anton pulled it out for 2.4.0-xx */
@@ -139,15 +142,6 @@
}
}
-/*
- */
-void __iomem *sbus_ioremap(struct resource *phyres, unsigned long offset,
- unsigned long size, char *name)
-{
- return _sparc_alloc_io(phyres->flags & 0xF,
- phyres->start + offset, size, name);
-}
-
void __iomem *of_ioremap(struct resource *res, unsigned long offset,
unsigned long size, char *name)
{
@@ -164,13 +158,6 @@
EXPORT_SYMBOL(of_iounmap);
/*
- */
-void sbus_iounmap(volatile void __iomem *addr, unsigned long size)
-{
- iounmap(addr);
-}
-
-/*
* Meat of mapping
*/
static void __iomem *_sparc_alloc_io(unsigned int busno, unsigned long phys,
@@ -246,63 +233,19 @@
#ifdef CONFIG_SBUS
-void sbus_set_sbus64(struct sbus_dev *sdev, int x)
+void sbus_set_sbus64(struct device *dev, int x)
{
printk("sbus_set_sbus64: unsupported\n");
}
-extern unsigned int sun4d_build_irq(struct sbus_dev *sdev, int irq);
-void __init sbus_fill_device_irq(struct sbus_dev *sdev)
-{
- struct linux_prom_irqs irqs[PROMINTR_MAX];
- int len;
-
- len = prom_getproperty(sdev->prom_node, "intr",
- (char *)irqs, sizeof(irqs));
- if (len != -1) {
- sdev->num_irqs = len / 8;
- if (sdev->num_irqs == 0) {
- sdev->irqs[0] = 0;
- } else if (sparc_cpu_model == sun4d) {
- for (len = 0; len < sdev->num_irqs; len++)
- sdev->irqs[len] =
- sun4d_build_irq(sdev, irqs[len].pri);
- } else {
- for (len = 0; len < sdev->num_irqs; len++)
- sdev->irqs[len] = irqs[len].pri;
- }
- } else {
- int interrupts[PROMINTR_MAX];
-
- /* No "intr" node found-- check for "interrupts" node.
- * This node contains SBus interrupt levels, not IPLs
- * as in "intr", and no vector values. We convert
- * SBus interrupt levels to PILs (platform specific).
- */
- len = prom_getproperty(sdev->prom_node, "interrupts",
- (char *)interrupts, sizeof(interrupts));
- if (len == -1) {
- sdev->irqs[0] = 0;
- sdev->num_irqs = 0;
- } else {
- sdev->num_irqs = len / sizeof(int);
- for (len = 0; len < sdev->num_irqs; len++) {
- sdev->irqs[len] =
- sbint_to_irq(sdev, interrupts[len]);
- }
- }
- }
-}
-
/*
* Allocate a chunk of memory suitable for DMA.
* Typically devices use them for control blocks.
* CPU may access them without any explicit flushing.
- *
- * XXX Some clever people know that sdev is not used and supply NULL. Watch.
*/
-void *sbus_alloc_consistent(struct sbus_dev *sdev, long len, u32 *dma_addrp)
+void *sbus_alloc_consistent(struct device *dev, long len, u32 *dma_addrp)
{
+ struct of_device *op = to_of_device(dev);
unsigned long len_total = (len + PAGE_SIZE-1) & PAGE_MASK;
unsigned long va;
struct resource *res;
@@ -336,13 +279,10 @@
* XXX That's where sdev would be used. Currently we load
* all iommu tables with the same translations.
*/
- if (mmu_map_dma_area(dma_addrp, va, res->start, len_total) != 0)
+ if (mmu_map_dma_area(dev, dma_addrp, va, res->start, len_total) != 0)
goto err_noiommu;
- /* Set the resource name, if known. */
- if (sdev) {
- res->name = sdev->prom_name;
- }
+ res->name = op->node->name;
return (void *)(unsigned long)res->start;
@@ -356,7 +296,7 @@
return NULL;
}
-void sbus_free_consistent(struct sbus_dev *sdev, long n, void *p, u32 ba)
+void sbus_free_consistent(struct device *dev, long n, void *p, u32 ba)
{
struct resource *res;
struct page *pgv;
@@ -383,8 +323,8 @@
kfree(res);
/* mmu_inval_dma_area(va, n); */ /* it's consistent, isn't it */
- pgv = mmu_translate_dvma(ba);
- mmu_unmap_dma_area(ba, n);
+ pgv = virt_to_page(p);
+ mmu_unmap_dma_area(dev, ba, n);
__free_pages(pgv, get_order(n));
}
@@ -394,7 +334,7 @@
* CPU view of this memory may be inconsistent with
* a device view and explicit flushing is necessary.
*/
-dma_addr_t sbus_map_single(struct sbus_dev *sdev, void *va, size_t len, int direction)
+dma_addr_t sbus_map_single(struct device *dev, void *va, size_t len, int direction)
{
/* XXX why are some lengths signed, others unsigned? */
if (len <= 0) {
@@ -404,17 +344,17 @@
if (len > 256*1024) { /* __get_free_pages() limit */
return 0;
}
- return mmu_get_scsi_one(va, len, sdev->bus);
+ return mmu_get_scsi_one(dev, va, len);
}
-void sbus_unmap_single(struct sbus_dev *sdev, dma_addr_t ba, size_t n, int direction)
+void sbus_unmap_single(struct device *dev, dma_addr_t ba, size_t n, int direction)
{
- mmu_release_scsi_one(ba, n, sdev->bus);
+ mmu_release_scsi_one(dev, ba, n);
}
-int sbus_map_sg(struct sbus_dev *sdev, struct scatterlist *sg, int n, int direction)
+int sbus_map_sg(struct device *dev, struct scatterlist *sg, int n, int direction)
{
- mmu_get_scsi_sgl(sg, n, sdev->bus);
+ mmu_get_scsi_sgl(dev, sg, n);
/*
* XXX sparc64 can return a partial length here. sun4c should do this
@@ -423,145 +363,28 @@
return n;
}
-void sbus_unmap_sg(struct sbus_dev *sdev, struct scatterlist *sg, int n, int direction)
+void sbus_unmap_sg(struct device *dev, struct scatterlist *sg, int n, int direction)
{
- mmu_release_scsi_sgl(sg, n, sdev->bus);
+ mmu_release_scsi_sgl(dev, sg, n);
}
-/*
- */
-void sbus_dma_sync_single_for_cpu(struct sbus_dev *sdev, dma_addr_t ba, size_t size, int direction)
+void sbus_dma_sync_single_for_cpu(struct device *dev, dma_addr_t ba, size_t size, int direction)
{
-#if 0
- unsigned long va;
- struct resource *res;
-
- /* We do not need the resource, just print a message if invalid. */
- res = _sparc_find_resource(&_sparc_dvma, ba);
- if (res == NULL)
- panic("sbus_dma_sync_single: 0x%x\n", ba);
-
- va = page_address(mmu_translate_dvma(ba)); /* XXX higmem */
- /*
- * XXX This bogosity will be fixed with the iommu rewrite coming soon
- * to a kernel near you. - Anton
- */
- /* mmu_inval_dma_area(va, (size + PAGE_SIZE-1) & PAGE_MASK); */
-#endif
}
-void sbus_dma_sync_single_for_device(struct sbus_dev *sdev, dma_addr_t ba, size_t size, int direction)
+void sbus_dma_sync_single_for_device(struct device *dev, dma_addr_t ba, size_t size, int direction)
{
-#if 0
- unsigned long va;
- struct resource *res;
-
- /* We do not need the resource, just print a message if invalid. */
- res = _sparc_find_resource(&_sparc_dvma, ba);
- if (res == NULL)
- panic("sbus_dma_sync_single: 0x%x\n", ba);
-
- va = page_address(mmu_translate_dvma(ba)); /* XXX higmem */
- /*
- * XXX This bogosity will be fixed with the iommu rewrite coming soon
- * to a kernel near you. - Anton
- */
- /* mmu_inval_dma_area(va, (size + PAGE_SIZE-1) & PAGE_MASK); */
-#endif
}
-void sbus_dma_sync_sg_for_cpu(struct sbus_dev *sdev, struct scatterlist *sg, int n, int direction)
-{
- printk("sbus_dma_sync_sg_for_cpu: not implemented yet\n");
-}
-
-void sbus_dma_sync_sg_for_device(struct sbus_dev *sdev, struct scatterlist *sg, int n, int direction)
-{
- printk("sbus_dma_sync_sg_for_device: not implemented yet\n");
-}
-
-/* Support code for sbus_init(). */
-/*
- * XXX This functions appears to be a distorted version of
- * prom_sbus_ranges_init(), with all sun4d stuff cut away.
- * Ask DaveM what is going on here, how is sun4d supposed to work... XXX
- */
-/* added back sun4d patch from Thomas Bogendoerfer - should be OK (crn) */
-void __init sbus_arch_bus_ranges_init(struct device_node *pn, struct sbus_bus *sbus)
-{
- int parent_node = pn->node;
-
- if (sparc_cpu_model == sun4d) {
- struct linux_prom_ranges iounit_ranges[PROMREG_MAX];
- int num_iounit_ranges, len;
-
- len = prom_getproperty(parent_node, "ranges",
- (char *) iounit_ranges,
- sizeof (iounit_ranges));
- if (len != -1) {
- num_iounit_ranges =
- (len / sizeof(struct linux_prom_ranges));
- prom_adjust_ranges(sbus->sbus_ranges,
- sbus->num_sbus_ranges,
- iounit_ranges, num_iounit_ranges);
- }
- }
-}
-
-void __init sbus_setup_iommu(struct sbus_bus *sbus, struct device_node *dp)
-{
-#ifndef CONFIG_SUN4
- struct device_node *parent = dp->parent;
-
- if (sparc_cpu_model != sun4d &&
- parent != NULL &&
- !strcmp(parent->name, "iommu")) {
- extern void iommu_init(int iommu_node, struct sbus_bus *sbus);
-
- iommu_init(parent->node, sbus);
- }
-
- if (sparc_cpu_model == sun4d) {
- extern void iounit_init(int sbi_node, int iounit_node,
- struct sbus_bus *sbus);
-
- iounit_init(dp->node, parent->node, sbus);
- }
-#endif
-}
-
-void __init sbus_setup_arch_props(struct sbus_bus *sbus, struct device_node *dp)
-{
- if (sparc_cpu_model == sun4d) {
- struct device_node *parent = dp->parent;
-
- sbus->devid = of_getintprop_default(parent, "device-id", 0);
- sbus->board = of_getintprop_default(parent, "board#", 0);
- }
-}
-
-int __init sbus_arch_preinit(void)
+static int __init sparc_register_ioport(void)
{
register_proc_sparc_ioport();
-#ifdef CONFIG_SUN4
- {
- extern void sun4_dvma_init(void);
- sun4_dvma_init();
- }
- return 1;
-#else
return 0;
-#endif
}
-void __init sbus_arch_postinit(void)
-{
- if (sparc_cpu_model == sun4d) {
- extern void sun4d_init_sbi_irq(void);
- sun4d_init_sbi_irq();
- }
-}
+arch_initcall(sparc_register_ioport);
+
#endif /* CONFIG_SBUS */
#ifdef CONFIG_PCI
diff --git a/arch/sparc/kernel/irq.h b/arch/sparc/kernel/irq.h
index 32ef3eb..db75138 100644
--- a/arch/sparc/kernel/irq.h
+++ b/arch/sparc/kernel/irq.h
@@ -13,7 +13,6 @@
BTFIXUPDEF_CALL(void, disable_pil_irq, unsigned int)
BTFIXUPDEF_CALL(void, enable_pil_irq, unsigned int)
BTFIXUPDEF_CALL(void, clear_clock_irq, void)
-BTFIXUPDEF_CALL(void, clear_profile_irq, int)
BTFIXUPDEF_CALL(void, load_profile_irq, int, unsigned int)
static inline void __disable_irq(unsigned int irq)
@@ -41,11 +40,6 @@
BTFIXUP_CALL(clear_clock_irq)();
}
-static inline void clear_profile_irq(int irq)
-{
- BTFIXUP_CALL(clear_profile_irq)(irq);
-}
-
static inline void load_profile_irq(int cpu, int limit)
{
BTFIXUP_CALL(load_profile_irq)(cpu, limit);
diff --git a/arch/sparc/kernel/of_device.c b/arch/sparc/kernel/of_device.c
index f58c537..0837bd5 100644
--- a/arch/sparc/kernel/of_device.c
+++ b/arch/sparc/kernel/of_device.c
@@ -29,15 +29,38 @@
}
EXPORT_SYMBOL(of_find_device_by_node);
-#ifdef CONFIG_PCI
-struct bus_type ebus_bus_type;
-EXPORT_SYMBOL(ebus_bus_type);
-#endif
+unsigned int irq_of_parse_and_map(struct device_node *node, int index)
+{
+ struct of_device *op = of_find_device_by_node(node);
-#ifdef CONFIG_SBUS
-struct bus_type sbus_bus_type;
-EXPORT_SYMBOL(sbus_bus_type);
-#endif
+ if (!op || index >= op->num_irqs)
+ return 0;
+
+ return op->irqs[index];
+}
+EXPORT_SYMBOL(irq_of_parse_and_map);
+
+/* Take the archdata values for IOMMU, STC, and HOSTDATA found in
+ * BUS and propagate to all child of_device objects.
+ */
+void of_propagate_archdata(struct of_device *bus)
+{
+ struct dev_archdata *bus_sd = &bus->dev.archdata;
+ struct device_node *bus_dp = bus->node;
+ struct device_node *dp;
+
+ for (dp = bus_dp->child; dp; dp = dp->sibling) {
+ struct of_device *op = of_find_device_by_node(dp);
+
+ op->dev.archdata.iommu = bus_sd->iommu;
+ op->dev.archdata.stc = bus_sd->stc;
+ op->dev.archdata.host_controller = bus_sd->host_controller;
+ op->dev.archdata.numa_node = bus_sd->numa_node;
+
+ if (dp->child)
+ of_propagate_archdata(op);
+ }
+}
struct bus_type of_platform_bus_type;
EXPORT_SYMBOL(of_platform_bus_type);
@@ -327,6 +350,27 @@
return 1;
}
+static int __init use_1to1_mapping(struct device_node *pp)
+{
+ /* If we have a ranges property in the parent, use it. */
+ if (of_find_property(pp, "ranges", NULL) != NULL)
+ return 0;
+
+ /* Some SBUS devices use intermediate nodes to express
+ * hierarchy within the device itself. These aren't
+ * real bus nodes, and don't have a 'ranges' property.
+ * But, we should still pass the translation work up
+ * to the SBUS itself.
+ */
+ if (!strcmp(pp->name, "dma") ||
+ !strcmp(pp->name, "espdma") ||
+ !strcmp(pp->name, "ledma") ||
+ !strcmp(pp->name, "lebuffer"))
+ return 0;
+
+ return 1;
+}
+
static int of_resource_verbose;
static void __init build_device_resources(struct of_device *op,
@@ -373,10 +417,7 @@
flags = bus->get_flags(reg, 0);
- /* If the immediate parent has no ranges property to apply,
- * just use a 1<->1 mapping.
- */
- if (of_find_property(pp, "ranges", NULL) == NULL) {
+ if (use_1to1_mapping(pp)) {
result = of_read_addr(addr, na);
goto build_res;
}
@@ -565,15 +606,6 @@
int err;
err = of_bus_type_init(&of_platform_bus_type, "of");
-#ifdef CONFIG_PCI
- if (!err)
- err = of_bus_type_init(&ebus_bus_type, "ebus");
-#endif
-#ifdef CONFIG_SBUS
- if (!err)
- err = of_bus_type_init(&sbus_bus_type, "sbus");
-#endif
-
if (!err)
scan_of_devices();
diff --git a/arch/sparc/kernel/pcic.c b/arch/sparc/kernel/pcic.c
index a6a6f98..462584e 100644
--- a/arch/sparc/kernel/pcic.c
+++ b/arch/sparc/kernel/pcic.c
@@ -17,8 +17,6 @@
#include <linux/slab.h>
#include <linux/jiffies.h>
-#include <asm/ebus.h>
-#include <asm/sbus.h> /* for sanity check... */
#include <asm/swift.h> /* for cache flushing. */
#include <asm/io.h>
@@ -430,7 +428,6 @@
pcic_pbm_scan_bus(pcic);
- ebus_init();
return 0;
}
@@ -493,10 +490,6 @@
* do ioremap() before accessing PC-style I/O,
* we supply virtual, ready to access address.
*
- * Ebus devices do not come here even if
- * CheerIO makes a similar conversion.
- * See ebus.c for details.
- *
* Note that request_region()
* works for these devices.
*
@@ -677,7 +670,7 @@
}
/*
- * pcic_pin_to_irq() is exported to ebus.c.
+ * pcic_pin_to_irq() is exported to bus probing code
*/
unsigned int
pcic_pin_to_irq(unsigned int pin, const char *name)
@@ -904,11 +897,6 @@
local_irq_restore(flags);
}
-static void pcic_clear_profile_irq(int cpu)
-{
- printk("PCIC: unimplemented code: FILE=%s LINE=%d", __FILE__, __LINE__);
-}
-
static void pcic_load_profile_irq(int cpu, unsigned int limit)
{
printk("PCIC: unimplemented code: FILE=%s LINE=%d", __FILE__, __LINE__);
@@ -934,7 +922,6 @@
BTFIXUPSET_CALL(enable_pil_irq, pcic_enable_pil_irq, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(disable_pil_irq, pcic_disable_pil_irq, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(clear_clock_irq, pcic_clear_clock_irq, BTFIXUPCALL_NORM);
- BTFIXUPSET_CALL(clear_profile_irq, pcic_clear_profile_irq, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(load_profile_irq, pcic_load_profile_irq, BTFIXUPCALL_NORM);
}
diff --git a/arch/sparc/kernel/pmc.c b/arch/sparc/kernel/pmc.c
index 7eca887..2afcfab 100644
--- a/arch/sparc/kernel/pmc.c
+++ b/arch/sparc/kernel/pmc.c
@@ -8,11 +8,11 @@
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/init.h>
-#include <linux/miscdevice.h>
#include <linux/pm.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <asm/io.h>
-#include <asm/sbus.h>
#include <asm/oplib.h>
#include <asm/uaccess.h>
#include <asm/auxio.h>
@@ -23,17 +23,15 @@
* #define PMC_NO_IDLE
*/
-#define PMC_MINOR MISC_DYNAMIC_MINOR
#define PMC_OBPNAME "SUNW,pmc"
#define PMC_DEVNAME "pmc"
#define PMC_IDLE_REG 0x00
#define PMC_IDLE_ON 0x01
-volatile static u8 __iomem *regs;
-static int pmc_regsize;
+static u8 __iomem *regs;
-#define pmc_readb(offs) (sbus_readb(regs+offs))
+#define pmc_readb(offs) (sbus_readb(regs+offs))
#define pmc_writeb(val, offs) (sbus_writeb(val, regs+offs))
/*
@@ -53,31 +51,11 @@
#endif
}
-static inline void pmc_free(void)
+static int __devinit pmc_probe(struct of_device *op,
+ const struct of_device_id *match)
{
- sbus_iounmap(regs, pmc_regsize);
-}
-
-static int __init pmc_probe(void)
-{
- struct sbus_bus *sbus = NULL;
- struct sbus_dev *sdev = NULL;
- for_each_sbus(sbus) {
- for_each_sbusdev(sdev, sbus) {
- if (!strcmp(sdev->prom_name, PMC_OBPNAME)) {
- goto sbus_done;
- }
- }
- }
-
-sbus_done:
- if (!sdev) {
- return -ENODEV;
- }
-
- pmc_regsize = sdev->reg_addrs[0].reg_size;
- regs = sbus_ioremap(&sdev->resource[0], 0,
- pmc_regsize, PMC_OBPNAME);
+ regs = of_ioremap(&op->resource[0], 0,
+ resource_size(&op->resource[0]), PMC_OBPNAME);
if (!regs) {
printk(KERN_ERR "%s: unable to map registers\n", PMC_DEVNAME);
return -ENODEV;
@@ -92,8 +70,27 @@
return 0;
}
+static struct of_device_id __initdata pmc_match[] = {
+ {
+ .name = PMC_OBPNAME,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, pmc_match);
+
+static struct of_platform_driver pmc_driver = {
+ .name = "pmc",
+ .match_table = pmc_match,
+ .probe = pmc_probe,
+};
+
+static int __init pmc_init(void)
+{
+ return of_register_driver(&pmc_driver, &of_bus_type);
+}
+
/* This driver is not critical to the boot process
* and is easiest to ioremap when SBus is already
* initialized, so we install ourselves thusly:
*/
-__initcall(pmc_probe);
+__initcall(pmc_init);
diff --git a/arch/sparc/kernel/process.c b/arch/sparc/kernel/process.c
index 4bb4309..e8c43ff 100644
--- a/arch/sparc/kernel/process.c
+++ b/arch/sparc/kernel/process.c
@@ -75,7 +75,7 @@
{
/* endless idle loop with no priority at all */
for (;;) {
- if (ARCH_SUN4C_SUN4) {
+ if (ARCH_SUN4C) {
static int count = HZ;
static unsigned long last_jiffies;
static unsigned long last_faults;
diff --git a/arch/sparc/kernel/prom.c b/arch/sparc/kernel/prom.c
index cd4fb79..eee5efc 100644
--- a/arch/sparc/kernel/prom.c
+++ b/arch/sparc/kernel/prom.c
@@ -54,6 +54,9 @@
}
EXPORT_SYMBOL(of_getintprop_default);
+DEFINE_MUTEX(of_set_property_mutex);
+EXPORT_SYMBOL(of_set_property_mutex);
+
int of_set_property(struct device_node *dp, const char *name, void *val, int len)
{
struct property **prevp;
@@ -77,7 +80,10 @@
void *old_val = prop->value;
int ret;
+ mutex_lock(&of_set_property_mutex);
ret = prom_setprop(dp->node, (char *) name, val, len);
+ mutex_unlock(&of_set_property_mutex);
+
err = -EINVAL;
if (ret >= 0) {
prop->value = new_val;
@@ -436,7 +442,6 @@
switch (prom_vers) {
case PROM_V0:
- case PROM_SUN4:
skip = 0;
switch (*romvec->pv_stdout) {
case PROMDEV_SCREEN:
diff --git a/arch/sparc/kernel/setup.c b/arch/sparc/kernel/setup.c
index 9e451b2..24fe307 100644
--- a/arch/sparc/kernel/setup.c
+++ b/arch/sparc/kernel/setup.c
@@ -213,23 +213,25 @@
/* Initialize PROM console and command line. */
*cmdline_p = prom_getbootargs();
strcpy(boot_command_line, *cmdline_p);
+ parse_early_param();
/* Set sparc_cpu_model */
sparc_cpu_model = sun_unknown;
- if(!strcmp(&cputypval,"sun4 ")) { sparc_cpu_model=sun4; }
- if(!strcmp(&cputypval,"sun4c")) { sparc_cpu_model=sun4c; }
- if(!strcmp(&cputypval,"sun4m")) { sparc_cpu_model=sun4m; }
- if(!strcmp(&cputypval,"sun4s")) { sparc_cpu_model=sun4m; } /* CP-1200 with PROM 2.30 -E */
- if(!strcmp(&cputypval,"sun4d")) { sparc_cpu_model=sun4d; }
- if(!strcmp(&cputypval,"sun4e")) { sparc_cpu_model=sun4e; }
- if(!strcmp(&cputypval,"sun4u")) { sparc_cpu_model=sun4u; }
+ if (!strcmp(&cputypval,"sun4 "))
+ sparc_cpu_model = sun4;
+ if (!strcmp(&cputypval,"sun4c"))
+ sparc_cpu_model = sun4c;
+ if (!strcmp(&cputypval,"sun4m"))
+ sparc_cpu_model = sun4m;
+ if (!strcmp(&cputypval,"sun4s"))
+ sparc_cpu_model = sun4m; /* CP-1200 with PROM 2.30 -E */
+ if (!strcmp(&cputypval,"sun4d"))
+ sparc_cpu_model = sun4d;
+ if (!strcmp(&cputypval,"sun4e"))
+ sparc_cpu_model = sun4e;
+ if (!strcmp(&cputypval,"sun4u"))
+ sparc_cpu_model = sun4u;
-#ifdef CONFIG_SUN4
- if (sparc_cpu_model != sun4) {
- prom_printf("This kernel is for Sun4 architecture only.\n");
- prom_halt();
- }
-#endif
printk("ARCH: ");
switch(sparc_cpu_model) {
case sun4:
@@ -263,7 +265,7 @@
boot_flags_init(*cmdline_p);
idprom_init();
- if (ARCH_SUN4C_SUN4)
+ if (ARCH_SUN4C)
sun4c_probe_vac();
load_mmu();
diff --git a/arch/sparc/kernel/sparc_ksyms.c b/arch/sparc/kernel/sparc_ksyms.c
index b23cea5..b0dfff8 100644
--- a/arch/sparc/kernel/sparc_ksyms.c
+++ b/arch/sparc/kernel/sparc_ksyms.c
@@ -38,17 +38,12 @@
#include <asm/idprom.h>
#include <asm/head.h>
#include <asm/smp.h>
-#include <asm/mostek.h>
#include <asm/ptrace.h>
#include <asm/uaccess.h>
#include <asm/checksum.h>
#ifdef CONFIG_SBUS
-#include <asm/sbus.h>
#include <asm/dma.h>
#endif
-#ifdef CONFIG_PCI
-#include <asm/ebus.h>
-#endif
#include <asm/io-unit.h>
#include <asm/bug.h>
@@ -127,16 +122,11 @@
EXPORT_SYMBOL(__udelay);
EXPORT_SYMBOL(__ndelay);
EXPORT_SYMBOL(rtc_lock);
-EXPORT_SYMBOL(mostek_lock);
-EXPORT_SYMBOL(mstk48t02_regs);
#ifdef CONFIG_SUN_AUXIO
EXPORT_SYMBOL(set_auxio);
EXPORT_SYMBOL(get_auxio);
#endif
EXPORT_SYMBOL(io_remap_pfn_range);
- /* P3: iounit_xxx may be needed, sun4d users */
-/* EXPORT_SYMBOL(iounit_map_dma_init); */
-/* EXPORT_SYMBOL(iounit_map_dma_page); */
#ifndef CONFIG_SMP
EXPORT_SYMBOL(BTFIXUP_CALL(___xchg32));
@@ -153,24 +143,9 @@
EXPORT_SYMBOL(BTFIXUP_CALL(pgprot_noncached));
#ifdef CONFIG_SBUS
-EXPORT_SYMBOL(sbus_root);
-EXPORT_SYMBOL(dma_chain);
EXPORT_SYMBOL(sbus_set_sbus64);
-EXPORT_SYMBOL(sbus_alloc_consistent);
-EXPORT_SYMBOL(sbus_free_consistent);
-EXPORT_SYMBOL(sbus_map_single);
-EXPORT_SYMBOL(sbus_unmap_single);
-EXPORT_SYMBOL(sbus_map_sg);
-EXPORT_SYMBOL(sbus_unmap_sg);
-EXPORT_SYMBOL(sbus_dma_sync_single_for_cpu);
-EXPORT_SYMBOL(sbus_dma_sync_single_for_device);
-EXPORT_SYMBOL(sbus_dma_sync_sg_for_cpu);
-EXPORT_SYMBOL(sbus_dma_sync_sg_for_device);
-EXPORT_SYMBOL(sbus_iounmap);
-EXPORT_SYMBOL(sbus_ioremap);
#endif
#ifdef CONFIG_PCI
-EXPORT_SYMBOL(ebus_chain);
EXPORT_SYMBOL(insb);
EXPORT_SYMBOL(outsb);
EXPORT_SYMBOL(insw);
diff --git a/arch/sparc/kernel/sun4c_irq.c b/arch/sparc/kernel/sun4c_irq.c
index 340fc39..5dc8a57 100644
--- a/arch/sparc/kernel/sun4c_irq.c
+++ b/arch/sparc/kernel/sun4c_irq.c
@@ -18,6 +18,8 @@
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include "irq.h"
#include <asm/ptrace.h>
@@ -31,15 +33,8 @@
#include <asm/traps.h>
#include <asm/irq.h>
#include <asm/io.h>
-#include <asm/sun4paddr.h>
#include <asm/idprom.h>
#include <asm/machines.h>
-#include <asm/sbus.h>
-
-#if 0
-static struct resource sun4c_timer_eb = { "sun4c_timer" };
-static struct resource sun4c_intr_eb = { "sun4c_intr" };
-#endif
/*
* Bit field defines for the interrupt registers on various
@@ -64,19 +59,7 @@
*
* so don't go making it static, like I tried. sigh.
*/
-unsigned char *interrupt_enable = NULL;
-
-static int sun4c_pil_map[] = { 0, 1, 2, 3, 5, 7, 8, 9 };
-
-static unsigned int sun4c_sbint_to_irq(struct sbus_dev *sdev,
- unsigned int sbint)
-{
- if (sbint >= sizeof(sun4c_pil_map)) {
- printk(KERN_ERR "%s: bogus SBINT %d\n", sdev->prom_name, sbint);
- BUG();
- }
- return sun4c_pil_map[sbint];
-}
+unsigned char __iomem *interrupt_enable = NULL;
static void sun4c_disable_irq(unsigned int irq_nr)
{
@@ -85,7 +68,7 @@
local_irq_save(flags);
irq_nr &= (NR_IRQS - 1);
- current_mask = *interrupt_enable;
+ current_mask = sbus_readb(interrupt_enable);
switch(irq_nr) {
case 1:
new_mask = ((current_mask) & (~(SUN4C_INT_E1)));
@@ -103,7 +86,7 @@
local_irq_restore(flags);
return;
}
- *interrupt_enable = new_mask;
+ sbus_writeb(new_mask, interrupt_enable);
local_irq_restore(flags);
}
@@ -114,7 +97,7 @@
local_irq_save(flags);
irq_nr &= (NR_IRQS - 1);
- current_mask = *interrupt_enable;
+ current_mask = sbus_readb(interrupt_enable);
switch(irq_nr) {
case 1:
new_mask = ((current_mask) | SUN4C_INT_E1);
@@ -132,37 +115,22 @@
local_irq_restore(flags);
return;
}
- *interrupt_enable = new_mask;
+ sbus_writeb(new_mask, interrupt_enable);
local_irq_restore(flags);
}
-#define TIMER_IRQ 10 /* Also at level 14, but we ignore that one. */
-#define PROFILE_IRQ 14 /* Level14 ticker.. used by OBP for polling */
+struct sun4c_timer_info {
+ u32 l10_count;
+ u32 l10_limit;
+ u32 l14_count;
+ u32 l14_limit;
+};
-volatile struct sun4c_timer_info *sun4c_timers;
-
-#ifdef CONFIG_SUN4
-/* This is an ugly hack to work around the
- current timer code, and make it work with
- the sun4/260 intersil
- */
-volatile struct sun4c_timer_info sun4_timer;
-#endif
+static struct sun4c_timer_info __iomem *sun4c_timers;
static void sun4c_clear_clock_irq(void)
{
- volatile unsigned int clear_intr;
-#ifdef CONFIG_SUN4
- if (idprom->id_machtype == (SM_SUN4 | SM_4_260))
- clear_intr = sun4_timer.timer_limit10;
- else
-#endif
- clear_intr = sun4c_timers->timer_limit10;
-}
-
-static void sun4c_clear_profile_irq(int cpu)
-{
- /* Errm.. not sure how to do this.. */
+ sbus_readl(&sun4c_timers->l10_limit);
}
static void sun4c_load_profile_irq(int cpu, unsigned int limit)
@@ -172,41 +140,48 @@
static void __init sun4c_init_timers(irq_handler_t counter_fn)
{
- int irq;
+ const struct linux_prom_irqs *irq;
+ struct device_node *dp;
+ const u32 *addr;
+ int err;
- /* Map the Timer chip, this is implemented in hardware inside
- * the cache chip on the sun4c.
- */
-#ifdef CONFIG_SUN4
- if (idprom->id_machtype == (SM_SUN4 | SM_4_260))
- sun4c_timers = &sun4_timer;
- else
-#endif
- sun4c_timers = ioremap(SUN_TIMER_PHYSADDR,
- sizeof(struct sun4c_timer_info));
+ dp = of_find_node_by_name(NULL, "counter-timer");
+ if (!dp) {
+ prom_printf("sun4c_init_timers: Unable to find counter-timer\n");
+ prom_halt();
+ }
+
+ addr = of_get_property(dp, "address", NULL);
+ if (!addr) {
+ prom_printf("sun4c_init_timers: No address property\n");
+ prom_halt();
+ }
+
+ sun4c_timers = (void __iomem *) (unsigned long) addr[0];
+
+ irq = of_get_property(dp, "intr", NULL);
+ if (!irq) {
+ prom_printf("sun4c_init_timers: No intr property\n");
+ prom_halt();
+ }
/* Have the level 10 timer tick at 100HZ. We don't touch the
* level 14 timer limit since we are letting the prom handle
* them until we have a real console driver so L1-A works.
*/
- sun4c_timers->timer_limit10 = (((1000000/HZ) + 1) << 10);
- master_l10_counter = &sun4c_timers->cur_count10;
- master_l10_limit = &sun4c_timers->timer_limit10;
+ sbus_writel((((1000000/HZ) + 1) << 10), &sun4c_timers->l10_limit);
- irq = request_irq(TIMER_IRQ,
- counter_fn,
+ master_l10_counter = &sun4c_timers->l10_count;
+
+ err = request_irq(irq[0].pri, counter_fn,
(IRQF_DISABLED | SA_STATIC_ALLOC),
"timer", NULL);
- if (irq) {
- prom_printf("time_init: unable to attach IRQ%d\n",TIMER_IRQ);
+ if (err) {
+ prom_printf("sun4c_init_timers: request_irq() fails with %d\n", err);
prom_halt();
}
-#if 0
- /* This does not work on 4/330 */
- sun4c_enable_irq(10);
-#endif
- claim_ticker14(NULL, PROFILE_IRQ, 0);
+ sun4c_disable_irq(irq[1].pri);
}
#ifdef CONFIG_SMP
@@ -215,41 +190,28 @@
void __init sun4c_init_IRQ(void)
{
- struct linux_prom_registers int_regs[2];
- int ie_node;
+ struct device_node *dp;
+ const u32 *addr;
- if (ARCH_SUN4) {
- interrupt_enable = (char *)
- ioremap(sun4_ie_physaddr, PAGE_SIZE);
- } else {
- struct resource phyres;
-
- ie_node = prom_searchsiblings (prom_getchild(prom_root_node),
- "interrupt-enable");
- if(ie_node == 0)
- panic("Cannot find /interrupt-enable node");
-
- /* Depending on the "address" property is bad news... */
- interrupt_enable = NULL;
- if (prom_getproperty(ie_node, "reg", (char *) int_regs,
- sizeof(int_regs)) != -1) {
- memset(&phyres, 0, sizeof(struct resource));
- phyres.flags = int_regs[0].which_io;
- phyres.start = int_regs[0].phys_addr;
- interrupt_enable = (char *) sbus_ioremap(&phyres, 0,
- int_regs[0].reg_size, "sun4c_intr");
- }
+ dp = of_find_node_by_name(NULL, "interrupt-enable");
+ if (!dp) {
+ prom_printf("sun4c_init_IRQ: Unable to find interrupt-enable\n");
+ prom_halt();
}
- if (!interrupt_enable)
- panic("Cannot map interrupt_enable");
- BTFIXUPSET_CALL(sbint_to_irq, sun4c_sbint_to_irq, BTFIXUPCALL_NORM);
+ addr = of_get_property(dp, "address", NULL);
+ if (!addr) {
+ prom_printf("sun4c_init_IRQ: No address property\n");
+ prom_halt();
+ }
+
+ interrupt_enable = (void __iomem *) (unsigned long) addr[0];
+
BTFIXUPSET_CALL(enable_irq, sun4c_enable_irq, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(disable_irq, sun4c_disable_irq, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(enable_pil_irq, sun4c_enable_irq, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(disable_pil_irq, sun4c_disable_irq, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(clear_clock_irq, sun4c_clear_clock_irq, BTFIXUPCALL_NORM);
- BTFIXUPSET_CALL(clear_profile_irq, sun4c_clear_profile_irq, BTFIXUPCALL_NOP);
BTFIXUPSET_CALL(load_profile_irq, sun4c_load_profile_irq, BTFIXUPCALL_NOP);
sparc_init_timers = sun4c_init_timers;
#ifdef CONFIG_SMP
@@ -257,6 +219,6 @@
BTFIXUPSET_CALL(clear_cpu_int, sun4c_nop, BTFIXUPCALL_NOP);
BTFIXUPSET_CALL(set_irq_udt, sun4c_nop, BTFIXUPCALL_NOP);
#endif
- *interrupt_enable = (SUN4C_INT_ENABLE);
+ sbus_writeb(SUN4C_INT_ENABLE, interrupt_enable);
/* Cannot enable interrupts until OBP ticker is disabled. */
}
diff --git a/arch/sparc/kernel/sun4d_irq.c b/arch/sparc/kernel/sun4d_irq.c
index 1290b59..d3cb76c 100644
--- a/arch/sparc/kernel/sun4d_irq.c
+++ b/arch/sparc/kernel/sun4d_irq.c
@@ -19,6 +19,8 @@
#include <linux/smp.h>
#include <linux/spinlock.h>
#include <linux/seq_file.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <asm/ptrace.h>
#include <asm/processor.h>
@@ -34,7 +36,6 @@
#include <asm/io.h>
#include <asm/pgalloc.h>
#include <asm/pgtable.h>
-#include <asm/sbus.h>
#include <asm/sbi.h>
#include <asm/cacheflush.h>
#include <asm/irq_regs.h>
@@ -44,16 +45,22 @@
/* If you trust current SCSI layer to handle different SCSI IRQs, enable this. I don't trust it... -jj */
/* #define DISTRIBUTE_IRQS */
-struct sun4d_timer_regs *sun4d_timers;
+struct sun4d_timer_regs {
+ u32 l10_timer_limit;
+ u32 l10_cur_countx;
+ u32 l10_limit_noclear;
+ u32 ctrl;
+ u32 l10_cur_count;
+};
+
+static struct sun4d_timer_regs __iomem *sun4d_timers;
+
#define TIMER_IRQ 10
#define MAX_STATIC_ALLOC 4
extern struct irqaction static_irqaction[MAX_STATIC_ALLOC];
extern int static_irq_count;
-unsigned char cpu_leds[32];
-#ifdef CONFIG_SMP
static unsigned char sbus_tid[32];
-#endif
static struct irqaction *irq_action[NR_IRQS];
extern spinlock_t irq_action_lock;
@@ -72,9 +79,9 @@
};
static int nsbi;
-#ifdef CONFIG_SMP
+
+/* Exported for sun4d_smp.c */
DEFINE_SPINLOCK(sun4d_imsk_lock);
-#endif
int show_sun4d_interrupts(struct seq_file *p, void *v)
{
@@ -257,26 +264,6 @@
set_irq_regs(old_regs);
}
-unsigned int sun4d_build_irq(struct sbus_dev *sdev, int irq)
-{
- int sbusl = pil_to_sbus[irq];
-
- if (sbusl)
- return ((sdev->bus->board + 1) << 5) + (sbusl << 2) + sdev->slot;
- else
- return irq;
-}
-
-static unsigned int sun4d_sbint_to_irq(struct sbus_dev *sdev,
- unsigned int sbint)
-{
- if (sbint >= sizeof(sbus_to_pil)) {
- printk(KERN_ERR "%s: bogus SBINT %d\n", sdev->prom_name, sbint);
- BUG();
- }
- return sun4d_build_irq(sdev, sbus_to_pil[sbint]);
-}
-
int sun4d_request_irq(unsigned int irq,
irq_handler_t handler,
unsigned long irqflags, const char * devname, void *dev_id)
@@ -360,36 +347,28 @@
static void sun4d_disable_irq(unsigned int irq)
{
-#ifdef CONFIG_SMP
int tid = sbus_tid[(irq >> 5) - 1];
unsigned long flags;
-#endif
- if (irq < NR_IRQS) return;
-#ifdef CONFIG_SMP
+ if (irq < NR_IRQS)
+ return;
+
spin_lock_irqsave(&sun4d_imsk_lock, flags);
cc_set_imsk_other(tid, cc_get_imsk_other(tid) | (1 << sbus_to_pil[(irq >> 2) & 7]));
spin_unlock_irqrestore(&sun4d_imsk_lock, flags);
-#else
- cc_set_imsk(cc_get_imsk() | (1 << sbus_to_pil[(irq >> 2) & 7]));
-#endif
}
static void sun4d_enable_irq(unsigned int irq)
{
-#ifdef CONFIG_SMP
int tid = sbus_tid[(irq >> 5) - 1];
unsigned long flags;
-#endif
- if (irq < NR_IRQS) return;
-#ifdef CONFIG_SMP
+ if (irq < NR_IRQS)
+ return;
+
spin_lock_irqsave(&sun4d_imsk_lock, flags);
cc_set_imsk_other(tid, cc_get_imsk_other(tid) & ~(1 << sbus_to_pil[(irq >> 2) & 7]));
spin_unlock_irqrestore(&sun4d_imsk_lock, flags);
-#else
- cc_set_imsk(cc_get_imsk() & ~(1 << sbus_to_pil[(irq >> 2) & 7]));
-#endif
}
#ifdef CONFIG_SMP
@@ -409,47 +388,55 @@
/* Setup IRQ distribution scheme. */
void __init sun4d_distribute_irqs(void)
{
+ struct device_node *dp;
+
#ifdef DISTRIBUTE_IRQS
- struct sbus_bus *sbus;
- unsigned long sbus_serving_map;
+ cpumask_t sbus_serving_map;
sbus_serving_map = cpu_present_map;
- for_each_sbus(sbus) {
- if ((sbus->board * 2) == boot_cpu_id && (cpu_present_map & (1 << (sbus->board * 2 + 1))))
- sbus_tid[sbus->board] = (sbus->board * 2 + 1);
- else if (cpu_present_map & (1 << (sbus->board * 2)))
- sbus_tid[sbus->board] = (sbus->board * 2);
- else if (cpu_present_map & (1 << (sbus->board * 2 + 1)))
- sbus_tid[sbus->board] = (sbus->board * 2 + 1);
+ for_each_node_by_name(dp, "sbi") {
+ int board = of_getintprop_default(dp, "board#", 0);
+
+ if ((board * 2) == boot_cpu_id && cpu_isset(board * 2 + 1, cpu_present_map))
+ sbus_tid[board] = (board * 2 + 1);
+ else if (cpu_isset(board * 2, cpu_present_map))
+ sbus_tid[board] = (board * 2);
+ else if (cpu_isset(board * 2 + 1, cpu_present_map))
+ sbus_tid[board] = (board * 2 + 1);
else
- sbus_tid[sbus->board] = 0xff;
- if (sbus_tid[sbus->board] != 0xff)
- sbus_serving_map &= ~(1 << sbus_tid[sbus->board]);
+ sbus_tid[board] = 0xff;
+ if (sbus_tid[board] != 0xff)
+ cpu_clear(sbus_tid[board], sbus_serving_map);
}
- for_each_sbus(sbus)
- if (sbus_tid[sbus->board] == 0xff) {
+ for_each_node_by_name(dp, "sbi") {
+ int board = of_getintprop_default(dp, "board#", 0);
+ if (sbus_tid[board] == 0xff) {
int i = 31;
- if (!sbus_serving_map)
+ if (cpus_empty(sbus_serving_map))
sbus_serving_map = cpu_present_map;
- while (!(sbus_serving_map & (1 << i)))
+ while (cpu_isset(i, sbus_serving_map))
i--;
- sbus_tid[sbus->board] = i;
- sbus_serving_map &= ~(1 << i);
+ sbus_tid[board] = i;
+ cpu_clear(i, sbus_serving_map);
}
- for_each_sbus(sbus) {
- printk("sbus%d IRQs directed to CPU%d\n", sbus->board, sbus_tid[sbus->board]);
- set_sbi_tid(sbus->devid, sbus_tid[sbus->board] << 3);
+ }
+ for_each_node_by_name(dp, "sbi") {
+ int devid = of_getintprop_default(dp, "device-id", 0);
+ int board = of_getintprop_default(dp, "board#", 0);
+ printk("sbus%d IRQs directed to CPU%d\n", board, sbus_tid[board]);
+ set_sbi_tid(devid, sbus_tid[board] << 3);
}
#else
- struct sbus_bus *sbus;
int cpuid = cpu_logical_map(1);
if (cpuid == -1)
cpuid = cpu_logical_map(0);
- for_each_sbus(sbus) {
- sbus_tid[sbus->board] = cpuid;
- set_sbi_tid(sbus->devid, cpuid << 3);
+ for_each_node_by_name(dp, "sbi") {
+ int devid = of_getintprop_default(dp, "device-id", 0);
+ int board = of_getintprop_default(dp, "board#", 0);
+ sbus_tid[board] = cpuid;
+ set_sbi_tid(devid, cpuid << 3);
}
printk("All sbus IRQs directed to CPU%d\n", cpuid);
#endif
@@ -458,13 +445,7 @@
static void sun4d_clear_clock_irq(void)
{
- volatile unsigned int clear_intr;
- clear_intr = sun4d_timers->l10_timer_limit;
-}
-
-static void sun4d_clear_profile_irq(int cpu)
-{
- bw_get_prof_limit(cpu);
+ sbus_readl(&sun4d_timers->l10_timer_limit);
}
static void sun4d_load_profile_irq(int cpu, unsigned int limit)
@@ -472,98 +453,121 @@
bw_set_prof_limit(cpu, limit);
}
-static void __init sun4d_init_timers(irq_handler_t counter_fn)
+static void __init sun4d_load_profile_irqs(void)
{
- int irq;
- int cpu;
- struct resource r;
- int mid;
+ int cpu = 0, mid;
- /* Map the User Timer registers. */
- memset(&r, 0, sizeof(r));
-#ifdef CONFIG_SMP
- r.start = CSR_BASE(boot_cpu_id)+BW_TIMER_LIMIT;
-#else
- r.start = CSR_BASE(0)+BW_TIMER_LIMIT;
-#endif
- r.flags = 0xf;
- sun4d_timers = (struct sun4d_timer_regs *) sbus_ioremap(&r, 0,
- PAGE_SIZE, "user timer");
-
- sun4d_timers->l10_timer_limit = (((1000000/HZ) + 1) << 10);
- master_l10_counter = &sun4d_timers->l10_cur_count;
- master_l10_limit = &sun4d_timers->l10_timer_limit;
-
- irq = request_irq(TIMER_IRQ,
- counter_fn,
- (IRQF_DISABLED | SA_STATIC_ALLOC),
- "timer", NULL);
- if (irq) {
- prom_printf("time_init: unable to attach IRQ%d\n",TIMER_IRQ);
- prom_halt();
- }
-
- /* Enable user timer free run for CPU 0 in BW */
- /* bw_set_ctrl(0, bw_get_ctrl(0) | BW_CTRL_USER_TIMER); */
-
- cpu = 0;
while (!cpu_find_by_instance(cpu, NULL, &mid)) {
sun4d_load_profile_irq(mid >> 3, 0);
cpu++;
}
-
+}
+
+static void __init sun4d_fixup_trap_table(void)
+{
#ifdef CONFIG_SMP
- {
- unsigned long flags;
- extern unsigned long lvl14_save[4];
- struct tt_entry *trap_table = &sparc_ttable[SP_TRAP_IRQ1 + (14 - 1)];
- extern unsigned int real_irq_entry[], smp4d_ticker[];
- extern unsigned int patchme_maybe_smp_msg[];
+ unsigned long flags;
+ extern unsigned long lvl14_save[4];
+ struct tt_entry *trap_table = &sparc_ttable[SP_TRAP_IRQ1 + (14 - 1)];
+ extern unsigned int real_irq_entry[], smp4d_ticker[];
+ extern unsigned int patchme_maybe_smp_msg[];
- /* Adjust so that we jump directly to smp4d_ticker */
- lvl14_save[2] += smp4d_ticker - real_irq_entry;
+ /* Adjust so that we jump directly to smp4d_ticker */
+ lvl14_save[2] += smp4d_ticker - real_irq_entry;
- /* For SMP we use the level 14 ticker, however the bootup code
- * has copied the firmware's level 14 vector into the boot cpu's
- * trap table, we must fix this now or we get squashed.
- */
- local_irq_save(flags);
- patchme_maybe_smp_msg[0] = 0x01000000; /* NOP out the branch */
- trap_table->inst_one = lvl14_save[0];
- trap_table->inst_two = lvl14_save[1];
- trap_table->inst_three = lvl14_save[2];
- trap_table->inst_four = lvl14_save[3];
- local_flush_cache_all();
- local_irq_restore(flags);
- }
+ /* For SMP we use the level 14 ticker, however the bootup code
+ * has copied the firmware's level 14 vector into the boot cpu's
+ * trap table, we must fix this now or we get squashed.
+ */
+ local_irq_save(flags);
+ patchme_maybe_smp_msg[0] = 0x01000000; /* NOP out the branch */
+ trap_table->inst_one = lvl14_save[0];
+ trap_table->inst_two = lvl14_save[1];
+ trap_table->inst_three = lvl14_save[2];
+ trap_table->inst_four = lvl14_save[3];
+ local_flush_cache_all();
+ local_irq_restore(flags);
#endif
}
+static void __init sun4d_init_timers(irq_handler_t counter_fn)
+{
+ struct device_node *dp;
+ struct resource res;
+ const u32 *reg;
+ int err;
+
+ dp = of_find_node_by_name(NULL, "cpu-unit");
+ if (!dp) {
+ prom_printf("sun4d_init_timers: Unable to find cpu-unit\n");
+ prom_halt();
+ }
+
+ /* Which cpu-unit we use is arbitrary, we can view the bootbus timer
+ * registers via any cpu's mapping. The first 'reg' property is the
+ * bootbus.
+ */
+ reg = of_get_property(dp, "reg", NULL);
+ if (!reg) {
+ prom_printf("sun4d_init_timers: No reg property\n");
+ prom_halt();
+ }
+
+ res.start = reg[1];
+ res.end = reg[2] - 1;
+ res.flags = reg[0] & 0xff;
+ sun4d_timers = of_ioremap(&res, BW_TIMER_LIMIT,
+ sizeof(struct sun4d_timer_regs), "user timer");
+ if (!sun4d_timers) {
+ prom_printf("sun4d_init_timers: Can't map timer regs\n");
+ prom_halt();
+ }
+
+ sbus_writel((((1000000/HZ) + 1) << 10), &sun4d_timers->l10_timer_limit);
+
+ master_l10_counter = &sun4d_timers->l10_cur_count;
+
+ err = request_irq(TIMER_IRQ, counter_fn,
+ (IRQF_DISABLED | SA_STATIC_ALLOC),
+ "timer", NULL);
+ if (err) {
+ prom_printf("sun4d_init_timers: request_irq() failed with %d\n", err);
+ prom_halt();
+ }
+ sun4d_load_profile_irqs();
+ sun4d_fixup_trap_table();
+}
+
void __init sun4d_init_sbi_irq(void)
{
- struct sbus_bus *sbus;
- unsigned mask;
+ struct device_node *dp;
+ int target_cpu = 0;
+
+#ifdef CONFIG_SMP
+ target_cpu = boot_cpu_id;
+#endif
nsbi = 0;
- for_each_sbus(sbus)
+ for_each_node_by_name(dp, "sbi")
nsbi++;
sbus_actions = kzalloc (nsbi * 8 * 4 * sizeof(struct sbus_action), GFP_ATOMIC);
if (!sbus_actions) {
prom_printf("SUN4D: Cannot allocate sbus_actions, halting.\n");
prom_halt();
}
- for_each_sbus(sbus) {
-#ifdef CONFIG_SMP
- extern unsigned char boot_cpu_id;
-
- set_sbi_tid(sbus->devid, boot_cpu_id << 3);
- sbus_tid[sbus->board] = boot_cpu_id;
-#endif
+ for_each_node_by_name(dp, "sbi") {
+ int devid = of_getintprop_default(dp, "device-id", 0);
+ int board = of_getintprop_default(dp, "board#", 0);
+ unsigned int mask;
+
+ set_sbi_tid(devid, target_cpu << 3);
+ sbus_tid[board] = target_cpu;
+
/* Get rid of pending irqs from PROM */
- mask = acquire_sbi(sbus->devid, 0xffffffff);
+ mask = acquire_sbi(devid, 0xffffffff);
if (mask) {
- printk ("Clearing pending IRQs %08x on SBI %d\n", mask, sbus->board);
- release_sbi(sbus->devid, mask);
+ printk ("Clearing pending IRQs %08x on SBI %d\n", mask, board);
+ release_sbi(devid, mask);
}
}
}
@@ -572,11 +576,9 @@
{
local_irq_disable();
- BTFIXUPSET_CALL(sbint_to_irq, sun4d_sbint_to_irq, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(enable_irq, sun4d_enable_irq, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(disable_irq, sun4d_disable_irq, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(clear_clock_irq, sun4d_clear_clock_irq, BTFIXUPCALL_NORM);
- BTFIXUPSET_CALL(clear_profile_irq, sun4d_clear_profile_irq, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(load_profile_irq, sun4d_load_profile_irq, BTFIXUPCALL_NORM);
sparc_init_timers = sun4d_init_timers;
#ifdef CONFIG_SMP
diff --git a/arch/sparc/kernel/sun4d_smp.c b/arch/sparc/kernel/sun4d_smp.c
index 446767e..7a6a5e7 100644
--- a/arch/sparc/kernel/sun4d_smp.c
+++ b/arch/sparc/kernel/sun4d_smp.c
@@ -20,6 +20,7 @@
#include <linux/swap.h>
#include <linux/profile.h>
#include <linux/delay.h>
+#include <linux/cpu.h>
#include <asm/ptrace.h>
#include <asm/atomic.h>
@@ -30,7 +31,6 @@
#include <asm/pgalloc.h>
#include <asm/pgtable.h>
#include <asm/oplib.h>
-#include <asm/sbus.h>
#include <asm/sbi.h>
#include <asm/tlbflush.h>
#include <asm/cacheflush.h>
@@ -72,6 +72,17 @@
extern void cpu_probe(void);
extern void sun4d_distribute_irqs(void);
+static unsigned char cpu_leds[32];
+
+static inline void show_leds(int cpuid)
+{
+ cpuid &= 0x1e;
+ __asm__ __volatile__ ("stba %0, [%1] %2" : :
+ "r" ((cpu_leds[cpuid] << 4) | cpu_leds[cpuid+1]),
+ "r" (ECSR_BASE(cpuid) | BB_LEDS),
+ "i" (ASI_M_CTL));
+}
+
void __init smp4d_callin(void)
{
int cpuid = hard_smp4d_processor_id();
diff --git a/arch/sparc/kernel/sun4m_irq.c b/arch/sparc/kernel/sun4m_irq.c
index 94e02de..f103171 100644
--- a/arch/sparc/kernel/sun4m_irq.c
+++ b/arch/sparc/kernel/sun4m_irq.c
@@ -20,6 +20,8 @@
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/ioport.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <asm/ptrace.h>
#include <asm/processor.h>
@@ -35,59 +37,27 @@
#include <asm/smp.h>
#include <asm/irq.h>
#include <asm/io.h>
-#include <asm/sbus.h>
#include <asm/cacheflush.h>
#include "irq.h"
-/* On the sun4m, just like the timers, we have both per-cpu and master
- * interrupt registers.
- */
-
-/* These registers are used for sending/receiving irqs from/to
- * different cpu's.
- */
-struct sun4m_intreg_percpu {
- unsigned int tbt; /* Interrupts still pending for this cpu. */
-
- /* These next two registers are WRITE-ONLY and are only
- * "on bit" sensitive, "off bits" written have NO affect.
- */
- unsigned int clear; /* Clear this cpus irqs here. */
- unsigned int set; /* Set this cpus irqs here. */
- unsigned char space[PAGE_SIZE - 12];
+struct sun4m_irq_percpu {
+ u32 pending;
+ u32 clear;
+ u32 set;
};
-/*
- * djhr
- * Actually the clear and set fields in this struct are misleading..
- * according to the SLAVIO manual (and the same applies for the SEC)
- * the clear field clears bits in the mask which will ENABLE that IRQ
- * the set field sets bits in the mask to DISABLE the IRQ.
- *
- * Also the undirected_xx address in the SLAVIO is defined as
- * RESERVED and write only..
- *
- * DAVEM_NOTE: The SLAVIO only specifies behavior on uniprocessor
- * sun4m machines, for MP the layout makes more sense.
- */
-struct sun4m_intregs {
- struct sun4m_intreg_percpu cpu_intregs[SUN4M_NCPUS];
- unsigned int tbt; /* IRQ's that are still pending. */
- unsigned int irqs; /* Master IRQ bits. */
-
- /* Again, like the above, two these registers are WRITE-ONLY. */
- unsigned int clear; /* Clear master IRQ's by setting bits here. */
- unsigned int set; /* Set master IRQ's by setting bits here. */
-
- /* This register is both READ and WRITE. */
- unsigned int undirected_target; /* Which cpu gets undirected irqs. */
+struct sun4m_irq_global {
+ u32 pending;
+ u32 mask;
+ u32 mask_clear;
+ u32 mask_set;
+ u32 interrupt_target;
};
-static unsigned long dummy;
-
-struct sun4m_intregs *sun4m_interrupts;
-unsigned long *irq_rcvreg = &dummy;
+/* Code in entry.S needs to get at these register mappings. */
+struct sun4m_irq_percpu __iomem *sun4m_irq_percpu[SUN4M_NCPUS];
+struct sun4m_irq_global __iomem *sun4m_irq_global;
/* Dave Redman (djhr@tadpole.co.uk)
* The sun4m interrupt registers.
@@ -101,8 +71,9 @@
#define SUN4M_INT_MASKALL 0x80000000 /* mask all interrupts */
#define SUN4M_INT_MODULE_ERR 0x40000000 /* module error */
-#define SUN4M_INT_M2S_WRITE 0x20000000 /* write buffer error */
-#define SUN4M_INT_ECC 0x10000000 /* ecc memory error */
+#define SUN4M_INT_M2S_WRITE_ERR 0x20000000 /* write buffer error */
+#define SUN4M_INT_ECC_ERR 0x10000000 /* ecc memory error */
+#define SUN4M_INT_VME_ERR 0x08000000 /* vme async error */
#define SUN4M_INT_FLOPPY 0x00400000 /* floppy disk */
#define SUN4M_INT_MODULE 0x00200000 /* module interrupt */
#define SUN4M_INT_VIDEO 0x00100000 /* onboard video */
@@ -113,75 +84,126 @@
#define SUN4M_INT_SERIAL 0x00008000 /* serial ports */
#define SUN4M_INT_KBDMS 0x00004000 /* keyboard/mouse */
#define SUN4M_INT_SBUSBITS 0x00003F80 /* sbus int bits */
+#define SUN4M_INT_VMEBITS 0x0000007F /* vme int bits */
+
+#define SUN4M_INT_ERROR (SUN4M_INT_MODULE_ERR | \
+ SUN4M_INT_M2S_WRITE_ERR | \
+ SUN4M_INT_ECC_ERR | \
+ SUN4M_INT_VME_ERR)
#define SUN4M_INT_SBUS(x) (1 << (x+7))
#define SUN4M_INT_VME(x) (1 << (x))
-/* These tables only apply for interrupts greater than 15..
- *
- * any intr value below 0x10 is considered to be a soft-int
- * this may be useful or it may not.. but that's how I've done it.
- * and it won't clash with what OBP is telling us about devices.
+/* Interrupt levels used by OBP */
+#define OBP_INT_LEVEL_SOFT 0x10
+#define OBP_INT_LEVEL_ONBOARD 0x20
+#define OBP_INT_LEVEL_SBUS 0x30
+#define OBP_INT_LEVEL_VME 0x40
+
+/* Interrupt level assignment on sun4m:
*
- * take an encoded intr value and lookup if it's valid
- * then get the mask bits that match from irq_mask
+ * level source
+ * ------------------------------------------------------------
+ * 1 softint-1
+ * 2 softint-2, VME/SBUS level 1
+ * 3 softint-3, VME/SBUS level 2
+ * 4 softint-4, onboard SCSI
+ * 5 softint-5, VME/SBUS level 3
+ * 6 softint-6, onboard ETHERNET
+ * 7 softint-7, VME/SBUS level 4
+ * 8 softint-8, onboard VIDEO
+ * 9 softint-9, VME/SBUS level 5, Module Interrupt
+ * 10 softint-10, system counter/timer
+ * 11 softint-11, VME/SBUS level 6, Floppy
+ * 12 softint-12, Keyboard/Mouse, Serial
+ * 13 softint-13, VME/SBUS level 7, ISDN Audio
+ * 14 softint-14, per-processor counter/timer
+ * 15 softint-15, Asynchronous Errors (broadcast)
*
- * P3: Translation from irq 0x0d to mask 0x2000 is for MrCoffee.
+ * Each interrupt source is masked distinctly in the sun4m interrupt
+ * registers. The PIL level alone is therefore ambiguous, since multiple
+ * interrupt sources map to a single PIL.
+ *
+ * This ambiguity is resolved in the 'intr' property for device nodes
+ * in the OF device tree. Each 'intr' property entry is composed of
+ * two 32-bit words. The first word is the IRQ priority value, which
+ * is what we're intersted in. The second word is the IRQ vector, which
+ * is unused.
+ *
+ * The low 4 bits of the IRQ priority indicate the PIL, and the upper
+ * 4 bits indicate onboard vs. SBUS leveled vs. VME leveled. 0x20
+ * means onboard, 0x30 means SBUS leveled, and 0x40 means VME leveled.
+ *
+ * For example, an 'intr' IRQ priority value of 0x24 is onboard SCSI
+ * whereas a value of 0x33 is SBUS level 2. Here are some sample
+ * 'intr' property IRQ priority values from ss4, ss5, ss10, ss20, and
+ * Tadpole S3 GX systems.
+ *
+ * esp: 0x24 onboard ESP SCSI
+ * le: 0x26 onboard Lance ETHERNET
+ * p9100: 0x32 SBUS level 1 P9100 video
+ * bpp: 0x33 SBUS level 2 BPP parallel port device
+ * DBRI: 0x39 SBUS level 5 DBRI ISDN audio
+ * SUNW,leo: 0x39 SBUS level 5 LEO video
+ * pcmcia: 0x3b SBUS level 6 PCMCIA controller
+ * uctrl: 0x3b SBUS level 6 UCTRL device
+ * modem: 0x3d SBUS level 7 MODEM
+ * zs: 0x2c onboard keyboard/mouse/serial
+ * floppy: 0x2b onboard Floppy
+ * power: 0x22 onboard power device (XXX unknown mask bit XXX)
*/
-static unsigned char irq_xlate[32] = {
- /* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f */
- 0, 0, 0, 0, 1, 0, 2, 0, 3, 0, 4, 5, 6, 14, 0, 7,
- 0, 0, 8, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 0
+
+static unsigned long irq_mask[0x50] = {
+ /* SMP */
+ 0, SUN4M_SOFT_INT(1),
+ SUN4M_SOFT_INT(2), SUN4M_SOFT_INT(3),
+ SUN4M_SOFT_INT(4), SUN4M_SOFT_INT(5),
+ SUN4M_SOFT_INT(6), SUN4M_SOFT_INT(7),
+ SUN4M_SOFT_INT(8), SUN4M_SOFT_INT(9),
+ SUN4M_SOFT_INT(10), SUN4M_SOFT_INT(11),
+ SUN4M_SOFT_INT(12), SUN4M_SOFT_INT(13),
+ SUN4M_SOFT_INT(14), SUN4M_SOFT_INT(15),
+ /* soft */
+ 0, SUN4M_SOFT_INT(1),
+ SUN4M_SOFT_INT(2), SUN4M_SOFT_INT(3),
+ SUN4M_SOFT_INT(4), SUN4M_SOFT_INT(5),
+ SUN4M_SOFT_INT(6), SUN4M_SOFT_INT(7),
+ SUN4M_SOFT_INT(8), SUN4M_SOFT_INT(9),
+ SUN4M_SOFT_INT(10), SUN4M_SOFT_INT(11),
+ SUN4M_SOFT_INT(12), SUN4M_SOFT_INT(13),
+ SUN4M_SOFT_INT(14), SUN4M_SOFT_INT(15),
+ /* onboard */
+ 0, 0, 0, 0,
+ SUN4M_INT_SCSI, 0, SUN4M_INT_ETHERNET, 0,
+ SUN4M_INT_VIDEO, SUN4M_INT_MODULE,
+ SUN4M_INT_REALTIME, SUN4M_INT_FLOPPY,
+ (SUN4M_INT_SERIAL | SUN4M_INT_KBDMS),
+ SUN4M_INT_AUDIO, 0, SUN4M_INT_MODULE_ERR,
+ /* sbus */
+ 0, 0, SUN4M_INT_SBUS(0), SUN4M_INT_SBUS(1),
+ 0, SUN4M_INT_SBUS(2), 0, SUN4M_INT_SBUS(3),
+ 0, SUN4M_INT_SBUS(4), 0, SUN4M_INT_SBUS(5),
+ 0, SUN4M_INT_SBUS(6), 0, 0,
+ /* vme */
+ 0, 0, SUN4M_INT_VME(0), SUN4M_INT_VME(1),
+ 0, SUN4M_INT_VME(2), 0, SUN4M_INT_VME(3),
+ 0, SUN4M_INT_VME(4), 0, SUN4M_INT_VME(5),
+ 0, SUN4M_INT_VME(6), 0, 0
};
-static unsigned long irq_mask[] = {
- 0, /* illegal index */
- SUN4M_INT_SCSI, /* 1 irq 4 */
- SUN4M_INT_ETHERNET, /* 2 irq 6 */
- SUN4M_INT_VIDEO, /* 3 irq 8 */
- SUN4M_INT_REALTIME, /* 4 irq 10 */
- SUN4M_INT_FLOPPY, /* 5 irq 11 */
- (SUN4M_INT_SERIAL | SUN4M_INT_KBDMS), /* 6 irq 12 */
- SUN4M_INT_MODULE_ERR, /* 7 irq 15 */
- SUN4M_INT_SBUS(0), /* 8 irq 2 */
- SUN4M_INT_SBUS(1), /* 9 irq 3 */
- SUN4M_INT_SBUS(2), /* 10 irq 5 */
- SUN4M_INT_SBUS(3), /* 11 irq 7 */
- SUN4M_INT_SBUS(4), /* 12 irq 9 */
- SUN4M_INT_SBUS(5), /* 13 irq 11 */
- SUN4M_INT_SBUS(6) /* 14 irq 13 */
-};
-
-static int sun4m_pil_map[] = { 0, 2, 3, 5, 7, 9, 11, 13 };
-
-static unsigned int sun4m_sbint_to_irq(struct sbus_dev *sdev,
- unsigned int sbint)
-{
- if (sbint >= sizeof(sun4m_pil_map)) {
- printk(KERN_ERR "%s: bogus SBINT %d\n", sdev->prom_name, sbint);
- BUG();
- }
- return sun4m_pil_map[sbint] | 0x30;
-}
-
static unsigned long sun4m_get_irqmask(unsigned int irq)
{
unsigned long mask;
- if (irq > 0x20) {
- /* OBIO/SBUS interrupts */
- irq &= 0x1f;
- mask = irq_mask[irq_xlate[irq]];
- if (!mask)
- printk("sun4m_get_irqmask: IRQ%d has no valid mask!\n",irq);
- } else {
- /* Soft Interrupts will come here.
- * Currently there is no way to trigger them but I'm sure
- * something could be cooked up.
- */
- irq &= 0xf;
- mask = SUN4M_SOFT_INT(irq);
- }
+ if (irq < 0x50)
+ mask = irq_mask[irq];
+ else
+ mask = 0;
+
+ if (!mask)
+ printk(KERN_ERR "sun4m_get_irqmask: IRQ%d has no valid mask!\n",
+ irq);
+
return mask;
}
@@ -193,9 +215,9 @@
mask = sun4m_get_irqmask(irq_nr);
local_irq_save(flags);
if (irq_nr > 15)
- sun4m_interrupts->set = mask;
+ sbus_writel(mask, &sun4m_irq_global->mask_set);
else
- sun4m_interrupts->cpu_intregs[cpu].set = mask;
+ sbus_writel(mask, &sun4m_irq_percpu[cpu]->set);
local_irq_restore(flags);
}
@@ -212,13 +234,13 @@
mask = sun4m_get_irqmask(irq_nr);
local_irq_save(flags);
if (irq_nr > 15)
- sun4m_interrupts->clear = mask;
+ sbus_writel(mask, &sun4m_irq_global->mask_clear);
else
- sun4m_interrupts->cpu_intregs[cpu].clear = mask;
+ sbus_writel(mask, &sun4m_irq_percpu[cpu]->clear);
local_irq_restore(flags);
} else {
local_irq_save(flags);
- sun4m_interrupts->clear = SUN4M_INT_FLOPPY;
+ sbus_writel(SUN4M_INT_FLOPPY, &sun4m_irq_global->mask_clear);
local_irq_restore(flags);
}
}
@@ -236,10 +258,10 @@
/*9*/ SUN4M_INT_SBUS(4) | SUN4M_INT_VME(4) | SUN4M_INT_MODULE_ERR,
/*10*/ SUN4M_INT_REALTIME,
/*11*/ SUN4M_INT_SBUS(5) | SUN4M_INT_VME(5) | SUN4M_INT_FLOPPY,
-/*12*/ SUN4M_INT_SERIAL | SUN4M_INT_KBDMS,
-/*13*/ SUN4M_INT_AUDIO,
+/*12*/ SUN4M_INT_SERIAL | SUN4M_INT_KBDMS,
+/*13*/ SUN4M_INT_SBUS(6) | SUN4M_INT_VME(6) | SUN4M_INT_AUDIO,
/*14*/ SUN4M_INT_E14,
-/*15*/ 0x00000000
+/*15*/ SUN4M_INT_ERROR
};
/* We assume the caller has disabled local interrupts when these are called,
@@ -247,126 +269,141 @@
*/
static void sun4m_disable_pil_irq(unsigned int pil)
{
- sun4m_interrupts->set = cpu_pil_to_imask[pil];
+ sbus_writel(cpu_pil_to_imask[pil], &sun4m_irq_global->mask_set);
}
static void sun4m_enable_pil_irq(unsigned int pil)
{
- sun4m_interrupts->clear = cpu_pil_to_imask[pil];
+ sbus_writel(cpu_pil_to_imask[pil], &sun4m_irq_global->mask_clear);
}
#ifdef CONFIG_SMP
static void sun4m_send_ipi(int cpu, int level)
{
- unsigned long mask;
-
- mask = sun4m_get_irqmask(level);
- sun4m_interrupts->cpu_intregs[cpu].set = mask;
+ unsigned long mask = sun4m_get_irqmask(level);
+ sbus_writel(mask, &sun4m_irq_percpu[cpu]->set);
}
static void sun4m_clear_ipi(int cpu, int level)
{
- unsigned long mask;
-
- mask = sun4m_get_irqmask(level);
- sun4m_interrupts->cpu_intregs[cpu].clear = mask;
+ unsigned long mask = sun4m_get_irqmask(level);
+ sbus_writel(mask, &sun4m_irq_percpu[cpu]->clear);
}
static void sun4m_set_udt(int cpu)
{
- sun4m_interrupts->undirected_target = cpu;
+ sbus_writel(cpu, &sun4m_irq_global->interrupt_target);
}
#endif
-#define OBIO_INTR 0x20
-#define TIMER_IRQ (OBIO_INTR | 10)
-#define PROFILE_IRQ (OBIO_INTR | 14)
+struct sun4m_timer_percpu {
+ u32 l14_limit;
+ u32 l14_count;
+ u32 l14_limit_noclear;
+ u32 user_timer_start_stop;
+};
-static struct sun4m_timer_regs *sun4m_timers;
+static struct sun4m_timer_percpu __iomem *timers_percpu[SUN4M_NCPUS];
+
+struct sun4m_timer_global {
+ u32 l10_limit;
+ u32 l10_count;
+ u32 l10_limit_noclear;
+ u32 reserved;
+ u32 timer_config;
+};
+
+static struct sun4m_timer_global __iomem *timers_global;
+
+#define TIMER_IRQ (OBP_INT_LEVEL_ONBOARD | 10)
+
unsigned int lvl14_resolution = (((1000000/HZ) + 1) << 10);
static void sun4m_clear_clock_irq(void)
{
- volatile unsigned int clear_intr;
- clear_intr = sun4m_timers->l10_timer_limit;
+ sbus_readl(&timers_global->l10_limit);
}
-static void sun4m_clear_profile_irq(int cpu)
+void sun4m_nmi(struct pt_regs *regs)
{
- volatile unsigned int clear;
-
- clear = sun4m_timers->cpu_timers[cpu].l14_timer_limit;
+ unsigned long afsr, afar, si;
+
+ printk(KERN_ERR "Aieee: sun4m NMI received!\n");
+ /* XXX HyperSparc hack XXX */
+ __asm__ __volatile__("mov 0x500, %%g1\n\t"
+ "lda [%%g1] 0x4, %0\n\t"
+ "mov 0x600, %%g1\n\t"
+ "lda [%%g1] 0x4, %1\n\t" :
+ "=r" (afsr), "=r" (afar));
+ printk(KERN_ERR "afsr=%08lx afar=%08lx\n", afsr, afar);
+ si = sbus_readl(&sun4m_irq_global->pending);
+ printk(KERN_ERR "si=%08lx\n", si);
+ if (si & SUN4M_INT_MODULE_ERR)
+ printk(KERN_ERR "Module async error\n");
+ if (si & SUN4M_INT_M2S_WRITE_ERR)
+ printk(KERN_ERR "MBus/SBus async error\n");
+ if (si & SUN4M_INT_ECC_ERR)
+ printk(KERN_ERR "ECC memory error\n");
+ if (si & SUN4M_INT_VME_ERR)
+ printk(KERN_ERR "VME async error\n");
+ printk(KERN_ERR "you lose buddy boy...\n");
+ show_regs(regs);
+ prom_halt();
+}
+
+/* Exported for sun4m_smp.c */
+void sun4m_clear_profile_irq(int cpu)
+{
+ sbus_readl(&timers_percpu[cpu]->l14_limit);
}
static void sun4m_load_profile_irq(int cpu, unsigned int limit)
{
- sun4m_timers->cpu_timers[cpu].l14_timer_limit = limit;
+ sbus_writel(limit, &timers_percpu[cpu]->l14_limit);
}
static void __init sun4m_init_timers(irq_handler_t counter_fn)
{
- int reg_count, irq, cpu;
- struct linux_prom_registers cnt_regs[PROMREG_MAX];
- int obio_node, cnt_node;
- struct resource r;
+ struct device_node *dp = of_find_node_by_name(NULL, "counter");
+ int i, err, len, num_cpu_timers;
+ const u32 *addr;
- cnt_node = 0;
- if((obio_node =
- prom_searchsiblings (prom_getchild(prom_root_node), "obio")) == 0 ||
- (obio_node = prom_getchild (obio_node)) == 0 ||
- (cnt_node = prom_searchsiblings (obio_node, "counter")) == 0) {
- prom_printf("Cannot find /obio/counter node\n");
- prom_halt();
- }
- reg_count = prom_getproperty(cnt_node, "reg",
- (void *) cnt_regs, sizeof(cnt_regs));
- reg_count = (reg_count/sizeof(struct linux_prom_registers));
-
- /* Apply the obio ranges to the timer registers. */
- prom_apply_obio_ranges(cnt_regs, reg_count);
-
- cnt_regs[4].phys_addr = cnt_regs[reg_count-1].phys_addr;
- cnt_regs[4].reg_size = cnt_regs[reg_count-1].reg_size;
- cnt_regs[4].which_io = cnt_regs[reg_count-1].which_io;
- for(obio_node = 1; obio_node < 4; obio_node++) {
- cnt_regs[obio_node].phys_addr =
- cnt_regs[obio_node-1].phys_addr + PAGE_SIZE;
- cnt_regs[obio_node].reg_size = cnt_regs[obio_node-1].reg_size;
- cnt_regs[obio_node].which_io = cnt_regs[obio_node-1].which_io;
+ if (!dp) {
+ printk(KERN_ERR "sun4m_init_timers: No 'counter' node.\n");
+ return;
}
- memset((char*)&r, 0, sizeof(struct resource));
- /* Map the per-cpu Counter registers. */
- r.flags = cnt_regs[0].which_io;
- r.start = cnt_regs[0].phys_addr;
- sun4m_timers = (struct sun4m_timer_regs *) sbus_ioremap(&r, 0,
- PAGE_SIZE*SUN4M_NCPUS, "sun4m_cpu_cnt");
- /* Map the system Counter register. */
- /* XXX Here we expect consequent calls to yeld adjusent maps. */
- r.flags = cnt_regs[4].which_io;
- r.start = cnt_regs[4].phys_addr;
- sbus_ioremap(&r, 0, cnt_regs[4].reg_size, "sun4m_sys_cnt");
-
- sun4m_timers->l10_timer_limit = (((1000000/HZ) + 1) << 10);
- master_l10_counter = &sun4m_timers->l10_cur_count;
- master_l10_limit = &sun4m_timers->l10_timer_limit;
-
- irq = request_irq(TIMER_IRQ,
- counter_fn,
- (IRQF_DISABLED | SA_STATIC_ALLOC),
- "timer", NULL);
- if (irq) {
- prom_printf("time_init: unable to attach IRQ%d\n",TIMER_IRQ);
- prom_halt();
+ addr = of_get_property(dp, "address", &len);
+ if (!addr) {
+ printk(KERN_ERR "sun4m_init_timers: No 'address' prop.\n");
+ return;
}
-
- if (!cpu_find_by_instance(1, NULL, NULL)) {
- for(cpu = 0; cpu < 4; cpu++)
- sun4m_timers->cpu_timers[cpu].l14_timer_limit = 0;
- sun4m_interrupts->set = SUN4M_INT_E14;
- } else {
- sun4m_timers->cpu_timers[0].l14_timer_limit = 0;
+
+ num_cpu_timers = (len / sizeof(u32)) - 1;
+ for (i = 0; i < num_cpu_timers; i++) {
+ timers_percpu[i] = (void __iomem *)
+ (unsigned long) addr[i];
}
+ timers_global = (void __iomem *)
+ (unsigned long) addr[num_cpu_timers];
+
+ sbus_writel((((1000000/HZ) + 1) << 10), &timers_global->l10_limit);
+
+ master_l10_counter = &timers_global->l10_count;
+
+ err = request_irq(TIMER_IRQ, counter_fn,
+ (IRQF_DISABLED | SA_STATIC_ALLOC), "timer", NULL);
+ if (err) {
+ printk(KERN_ERR "sun4m_init_timers: Register IRQ error %d.\n",
+ err);
+ return;
+ }
+
+ for (i = 0; i < num_cpu_timers; i++)
+ sbus_writel(0, &timers_percpu[i]->l14_limit);
+ if (num_cpu_timers == 4)
+ sbus_writel(SUN4M_INT_E14, &sun4m_irq_global->mask_set);
+
#ifdef CONFIG_SMP
{
unsigned long flags;
@@ -390,70 +427,43 @@
void __init sun4m_init_IRQ(void)
{
- int ie_node,i;
- struct linux_prom_registers int_regs[PROMREG_MAX];
- int num_regs;
- struct resource r;
- int mid;
-
+ struct device_node *dp = of_find_node_by_name(NULL, "interrupt");
+ int len, i, mid, num_cpu_iregs;
+ const u32 *addr;
+
+ if (!dp) {
+ printk(KERN_ERR "sun4m_init_IRQ: No 'interrupt' node.\n");
+ return;
+ }
+
+ addr = of_get_property(dp, "address", &len);
+ if (!addr) {
+ printk(KERN_ERR "sun4m_init_IRQ: No 'address' prop.\n");
+ return;
+ }
+
+ num_cpu_iregs = (len / sizeof(u32)) - 1;
+ for (i = 0; i < num_cpu_iregs; i++) {
+ sun4m_irq_percpu[i] = (void __iomem *)
+ (unsigned long) addr[i];
+ }
+ sun4m_irq_global = (void __iomem *)
+ (unsigned long) addr[num_cpu_iregs];
+
local_irq_disable();
- if((ie_node = prom_searchsiblings(prom_getchild(prom_root_node), "obio")) == 0 ||
- (ie_node = prom_getchild (ie_node)) == 0 ||
- (ie_node = prom_searchsiblings (ie_node, "interrupt")) == 0) {
- prom_printf("Cannot find /obio/interrupt node\n");
- prom_halt();
- }
- num_regs = prom_getproperty(ie_node, "reg", (char *) int_regs,
- sizeof(int_regs));
- num_regs = (num_regs/sizeof(struct linux_prom_registers));
-
- /* Apply the obio ranges to these registers. */
- prom_apply_obio_ranges(int_regs, num_regs);
-
- int_regs[4].phys_addr = int_regs[num_regs-1].phys_addr;
- int_regs[4].reg_size = int_regs[num_regs-1].reg_size;
- int_regs[4].which_io = int_regs[num_regs-1].which_io;
- for(ie_node = 1; ie_node < 4; ie_node++) {
- int_regs[ie_node].phys_addr = int_regs[ie_node-1].phys_addr + PAGE_SIZE;
- int_regs[ie_node].reg_size = int_regs[ie_node-1].reg_size;
- int_regs[ie_node].which_io = int_regs[ie_node-1].which_io;
- }
- memset((char *)&r, 0, sizeof(struct resource));
- /* Map the interrupt registers for all possible cpus. */
- r.flags = int_regs[0].which_io;
- r.start = int_regs[0].phys_addr;
- sun4m_interrupts = (struct sun4m_intregs *) sbus_ioremap(&r, 0,
- PAGE_SIZE*SUN4M_NCPUS, "interrupts_percpu");
-
- /* Map the system interrupt control registers. */
- r.flags = int_regs[4].which_io;
- r.start = int_regs[4].phys_addr;
- sbus_ioremap(&r, 0, int_regs[4].reg_size, "interrupts_system");
-
- sun4m_interrupts->set = ~SUN4M_INT_MASKALL;
+ sbus_writel(~SUN4M_INT_MASKALL, &sun4m_irq_global->mask_set);
for (i = 0; !cpu_find_by_instance(i, NULL, &mid); i++)
- sun4m_interrupts->cpu_intregs[mid].clear = ~0x17fff;
+ sbus_writel(~0x17fff, &sun4m_irq_percpu[mid]->clear);
- if (!cpu_find_by_instance(1, NULL, NULL)) {
- /* system wide interrupts go to cpu 0, this should always
- * be safe because it is guaranteed to be fitted or OBP doesn't
- * come up
- *
- * Not sure, but writing here on SLAVIO systems may puke
- * so I don't do it unless there is more than 1 cpu.
- */
- irq_rcvreg = (unsigned long *)
- &sun4m_interrupts->undirected_target;
- sun4m_interrupts->undirected_target = 0;
- }
- BTFIXUPSET_CALL(sbint_to_irq, sun4m_sbint_to_irq, BTFIXUPCALL_NORM);
+ if (num_cpu_iregs == 4)
+ sbus_writel(0, &sun4m_irq_global->interrupt_target);
+
BTFIXUPSET_CALL(enable_irq, sun4m_enable_irq, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(disable_irq, sun4m_disable_irq, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(enable_pil_irq, sun4m_enable_pil_irq, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(disable_pil_irq, sun4m_disable_pil_irq, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(clear_clock_irq, sun4m_clear_clock_irq, BTFIXUPCALL_NORM);
- BTFIXUPSET_CALL(clear_profile_irq, sun4m_clear_profile_irq, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(load_profile_irq, sun4m_load_profile_irq, BTFIXUPCALL_NORM);
sparc_init_timers = sun4m_init_timers;
#ifdef CONFIG_SMP
@@ -461,5 +471,6 @@
BTFIXUPSET_CALL(clear_cpu_int, sun4m_clear_ipi, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(set_irq_udt, sun4m_set_udt, BTFIXUPCALL_NORM);
#endif
+
/* Cannot enable interrupts until OBP ticker is disabled. */
}
diff --git a/arch/sparc/kernel/sun4m_smp.c b/arch/sparc/kernel/sun4m_smp.c
index 9964890..5fc386d 100644
--- a/arch/sparc/kernel/sun4m_smp.c
+++ b/arch/sparc/kernel/sun4m_smp.c
@@ -17,6 +17,7 @@
#include <linux/swap.h>
#include <linux/profile.h>
#include <linux/delay.h>
+#include <linux/cpu.h>
#include <asm/cacheflush.h>
#include <asm/tlbflush.h>
@@ -315,6 +316,8 @@
ccall_info.processors_out[i] = 1;
}
+extern void sun4m_clear_profile_irq(int cpu);
+
void smp4m_percpu_timer_interrupt(struct pt_regs *regs)
{
struct pt_regs *old_regs;
@@ -322,7 +325,7 @@
old_regs = set_irq_regs(regs);
- clear_profile_irq(cpu);
+ sun4m_clear_profile_irq(cpu);
profile_tick(CPU_PROFILING);
diff --git a/arch/sparc/kernel/sun4setup.c b/arch/sparc/kernel/sun4setup.c
deleted file mode 100644
index 229a52f..0000000
--- a/arch/sparc/kernel/sun4setup.c
+++ /dev/null
@@ -1,75 +0,0 @@
-/* sun4setup.c: Setup the hardware address of various items in the sun4
- * architecture. Called from idprom_init
- *
- * Copyright (C) 1998 Chris G. Davis (cdavis@cois.on.ca)
- */
-
-#include <asm/page.h>
-#include <asm/oplib.h>
-#include <asm/idprom.h>
-#include <asm/sun4paddr.h>
-#include <asm/machines.h>
-
-int sun4_memreg_physaddr;
-int sun4_ie_physaddr;
-int sun4_clock_physaddr;
-int sun4_timer_physaddr;
-int sun4_eth_physaddr;
-int sun4_si_physaddr;
-int sun4_bwtwo_physaddr;
-int sun4_zs0_physaddr;
-int sun4_zs1_physaddr;
-int sun4_dma_physaddr;
-int sun4_esp_physaddr;
-int sun4_ie_physaddr;
-
-void __init sun4setup(void)
-{
- printk("Sun4 Hardware Setup v1.0 18/May/98 Chris Davis (cdavis@cois.on.ca). ");
- /*
- setup standard sun4 info
- */
- sun4_ie_physaddr=SUN4_IE_PHYSADDR;
-
- /*
- setup model specific info
- */
- switch(idprom->id_machtype) {
- case (SM_SUN4 | SM_4_260 ):
- printk("Setup for a SUN4/260\n");
- sun4_memreg_physaddr=SUN4_200_MEMREG_PHYSADDR;
- sun4_clock_physaddr=SUN4_200_CLOCK_PHYSADDR;
- sun4_timer_physaddr=SUN4_UNUSED_PHYSADDR;
- sun4_eth_physaddr=SUN4_200_ETH_PHYSADDR;
- sun4_si_physaddr=SUN4_200_SI_PHYSADDR;
- sun4_bwtwo_physaddr=SUN4_200_BWTWO_PHYSADDR;
- sun4_dma_physaddr=SUN4_UNUSED_PHYSADDR;
- sun4_esp_physaddr=SUN4_UNUSED_PHYSADDR;
- break;
- case (SM_SUN4 | SM_4_330 ):
- printk("Setup for a SUN4/330\n");
- sun4_memreg_physaddr=SUN4_300_MEMREG_PHYSADDR;
- sun4_clock_physaddr=SUN4_300_CLOCK_PHYSADDR;
- sun4_timer_physaddr=SUN4_300_TIMER_PHYSADDR;
- sun4_eth_physaddr=SUN4_300_ETH_PHYSADDR;
- sun4_si_physaddr=SUN4_UNUSED_PHYSADDR;
- sun4_bwtwo_physaddr=SUN4_300_BWTWO_PHYSADDR;
- sun4_dma_physaddr=SUN4_300_DMA_PHYSADDR;
- sun4_esp_physaddr=SUN4_300_ESP_PHYSADDR;
- break;
- case (SM_SUN4 | SM_4_470 ):
- printk("Setup for a SUN4/470\n");
- sun4_memreg_physaddr=SUN4_400_MEMREG_PHYSADDR;
- sun4_clock_physaddr=SUN4_400_CLOCK_PHYSADDR;
- sun4_timer_physaddr=SUN4_400_TIMER_PHYSADDR;
- sun4_eth_physaddr=SUN4_400_ETH_PHYSADDR;
- sun4_si_physaddr=SUN4_UNUSED_PHYSADDR;
- sun4_bwtwo_physaddr=SUN4_400_BWTWO_PHYSADDR;
- sun4_dma_physaddr=SUN4_400_DMA_PHYSADDR;
- sun4_esp_physaddr=SUN4_400_ESP_PHYSADDR;
- break;
- default:
- ;
- }
-}
-
diff --git a/arch/sparc/kernel/sys_sparc.c b/arch/sparc/kernel/sys_sparc.c
index 4d73421..03035c8 100644
--- a/arch/sparc/kernel/sys_sparc.c
+++ b/arch/sparc/kernel/sys_sparc.c
@@ -53,7 +53,7 @@
/* See asm-sparc/uaccess.h */
if (len > TASK_SIZE - PAGE_SIZE)
return -ENOMEM;
- if (ARCH_SUN4C_SUN4 && len > 0x20000000)
+ if (ARCH_SUN4C && len > 0x20000000)
return -ENOMEM;
if (!addr)
addr = TASK_UNMAPPED_BASE;
@@ -65,7 +65,7 @@
for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) {
/* At this point: (!vmm || addr < vmm->vm_end). */
- if (ARCH_SUN4C_SUN4 && addr < 0xe0000000 && 0x20000000 - len < addr) {
+ if (ARCH_SUN4C && addr < 0xe0000000 && 0x20000000 - len < addr) {
addr = PAGE_OFFSET;
vmm = find_vma(current->mm, PAGE_OFFSET);
}
@@ -81,7 +81,7 @@
asmlinkage unsigned long sparc_brk(unsigned long brk)
{
- if(ARCH_SUN4C_SUN4) {
+ if(ARCH_SUN4C) {
if ((brk & 0xe0000000) != (current->mm->brk & 0xe0000000))
return current->mm->brk;
}
@@ -221,7 +221,7 @@
int sparc_mmap_check(unsigned long addr, unsigned long len)
{
- if (ARCH_SUN4C_SUN4 &&
+ if (ARCH_SUN4C &&
(len > 0x20000000 ||
(addr < 0xe0000000 && addr + len > 0x20000000)))
return -EINVAL;
diff --git a/arch/sparc/kernel/tick14.c b/arch/sparc/kernel/tick14.c
index 707bfda..138bbf5 100644
--- a/arch/sparc/kernel/tick14.c
+++ b/arch/sparc/kernel/tick14.c
@@ -1,31 +1,12 @@
/* tick14.c
- * linux/arch/sparc/kernel/tick14.c
*
* Copyright (C) 1996 David Redman (djhr@tadpole.co.uk)
*
* This file handles the Sparc specific level14 ticker
* This is really useful for profiling OBP uses it for keyboard
* aborts and other stuff.
- *
- *
*/
-#include <linux/errno.h>
-#include <linux/sched.h>
#include <linux/kernel.h>
-#include <linux/param.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/timex.h>
-#include <linux/interrupt.h>
-
-#include <asm/oplib.h>
-#include <asm/timer.h>
-#include <asm/mostek.h>
-#include <asm/system.h>
-#include <asm/irq.h>
-#include <asm/io.h>
-
-#include "irq.h"
extern unsigned long lvl14_save[5];
static unsigned long *linux_lvl14 = NULL;
@@ -56,31 +37,3 @@
linux_lvl14[2] = obp_lvl14[2];
linux_lvl14[3] = obp_lvl14[3];
}
-
-void claim_ticker14(irq_handler_t handler,
- int irq_nr, unsigned int timeout )
-{
- int cpu = smp_processor_id();
-
- /* first we copy the obp handler instructions
- */
- __disable_irq(irq_nr);
- if (!handler)
- return;
-
- linux_lvl14 = (unsigned long *)lvl14_save[4];
- obp_lvl14[0] = linux_lvl14[0];
- obp_lvl14[1] = linux_lvl14[1];
- obp_lvl14[2] = linux_lvl14[2];
- obp_lvl14[3] = linux_lvl14[3];
-
- if (!request_irq(irq_nr,
- handler,
- (IRQF_DISABLED | SA_STATIC_ALLOC),
- "counter14",
- NULL)) {
- install_linux_ticker();
- load_profile_irq(cpu, timeout);
- __enable_irq(irq_nr);
- }
-}
diff --git a/arch/sparc/kernel/time.c b/arch/sparc/kernel/time.c
index 0762f5d..62c1d94 100644
--- a/arch/sparc/kernel/time.c
+++ b/arch/sparc/kernel/time.c
@@ -23,22 +23,24 @@
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/time.h>
+#include <linux/rtc.h>
+#include <linux/rtc/m48t59.h>
#include <linux/timex.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/ioport.h>
#include <linux/profile.h>
+#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/platform_device.h>
#include <asm/oplib.h>
#include <asm/timer.h>
-#include <asm/mostek.h>
#include <asm/system.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/idprom.h>
#include <asm/machines.h>
-#include <asm/sun4paddr.h>
#include <asm/page.h>
#include <asm/pcic.h>
#include <asm/irq_regs.h>
@@ -46,34 +48,9 @@
#include "irq.h"
DEFINE_SPINLOCK(rtc_lock);
-static enum sparc_clock_type sp_clock_typ;
-DEFINE_SPINLOCK(mostek_lock);
-void __iomem *mstk48t02_regs = NULL;
-static struct mostek48t08 __iomem *mstk48t08_regs = NULL;
static int set_rtc_mmss(unsigned long);
static int sbus_do_settimeofday(struct timespec *tv);
-#ifdef CONFIG_SUN4
-struct intersil *intersil_clock;
-#define intersil_cmd(intersil_reg, intsil_cmd) intersil_reg->int_cmd_reg = \
- (intsil_cmd)
-
-#define intersil_intr(intersil_reg, intsil_cmd) intersil_reg->int_intr_reg = \
- (intsil_cmd)
-
-#define intersil_start(intersil_reg) intersil_cmd(intersil_reg, \
- ( INTERSIL_START | INTERSIL_32K | INTERSIL_NORMAL | INTERSIL_24H |\
- INTERSIL_INTR_ENABLE))
-
-#define intersil_stop(intersil_reg) intersil_cmd(intersil_reg, \
- ( INTERSIL_STOP | INTERSIL_32K | INTERSIL_NORMAL | INTERSIL_24H |\
- INTERSIL_INTR_ENABLE))
-
-#define intersil_read_intr(intersil_reg, towhere) towhere = \
- intersil_reg->int_intr_reg
-
-#endif
-
unsigned long profile_pc(struct pt_regs *regs)
{
extern char __copy_user_begin[], __copy_user_end[];
@@ -96,7 +73,6 @@
EXPORT_SYMBOL(profile_pc);
__volatile__ unsigned int *master_l10_counter;
-__volatile__ unsigned int *master_l10_limit;
/*
* timer_interrupt() needs to keep up the real-time clock,
@@ -116,15 +92,7 @@
/* Protect counter clear so that do_gettimeoffset works */
write_seqlock(&xtime_lock);
-#ifdef CONFIG_SUN4
- if((idprom->id_machtype == (SM_SUN4 | SM_4_260)) ||
- (idprom->id_machtype == (SM_SUN4 | SM_4_110))) {
- int temp;
- intersil_read_intr(intersil_clock, temp);
- /* re-enable the irq */
- enable_pil_irq(10);
- }
-#endif
+
clear_clock_irq();
do_timer(1);
@@ -147,157 +115,56 @@
return IRQ_HANDLED;
}
-/* Kick start a stopped clock (procedure from the Sun NVRAM/hostid FAQ). */
-static void __devinit kick_start_clock(void)
+static unsigned char mostek_read_byte(struct device *dev, u32 ofs)
{
- struct mostek48t02 *regs = (struct mostek48t02 *)mstk48t02_regs;
- unsigned char sec;
- int i, count;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct m48t59_plat_data *pdata = pdev->dev.platform_data;
+ void __iomem *regs = pdata->ioaddr;
+ unsigned char val = readb(regs + ofs);
- prom_printf("CLOCK: Clock was stopped. Kick start ");
-
- spin_lock_irq(&mostek_lock);
-
- /* Turn on the kick start bit to start the oscillator. */
- regs->creg |= MSTK_CREG_WRITE;
- regs->sec &= ~MSTK_STOP;
- regs->hour |= MSTK_KICK_START;
- regs->creg &= ~MSTK_CREG_WRITE;
-
- spin_unlock_irq(&mostek_lock);
-
- /* Delay to allow the clock oscillator to start. */
- sec = MSTK_REG_SEC(regs);
- for (i = 0; i < 3; i++) {
- while (sec == MSTK_REG_SEC(regs))
- for (count = 0; count < 100000; count++)
- /* nothing */ ;
- prom_printf(".");
- sec = regs->sec;
+ /* the year 0 is 1968 */
+ if (ofs == pdata->offset + M48T59_YEAR) {
+ val += 0x68;
+ if ((val & 0xf) > 9)
+ val += 6;
}
- prom_printf("\n");
+ return val;
+}
- spin_lock_irq(&mostek_lock);
+static void mostek_write_byte(struct device *dev, u32 ofs, u8 val)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct m48t59_plat_data *pdata = pdev->dev.platform_data;
+ void __iomem *regs = pdata->ioaddr;
- /* Turn off kick start and set a "valid" time and date. */
- regs->creg |= MSTK_CREG_WRITE;
- regs->hour &= ~MSTK_KICK_START;
- MSTK_SET_REG_SEC(regs,0);
- MSTK_SET_REG_MIN(regs,0);
- MSTK_SET_REG_HOUR(regs,0);
- MSTK_SET_REG_DOW(regs,5);
- MSTK_SET_REG_DOM(regs,1);
- MSTK_SET_REG_MONTH(regs,8);
- MSTK_SET_REG_YEAR(regs,1996 - MSTK_YEAR_ZERO);
- regs->creg &= ~MSTK_CREG_WRITE;
-
- spin_unlock_irq(&mostek_lock);
-
- /* Ensure the kick start bit is off. If it isn't, turn it off. */
- while (regs->hour & MSTK_KICK_START) {
- prom_printf("CLOCK: Kick start still on!\n");
-
- spin_lock_irq(&mostek_lock);
- regs->creg |= MSTK_CREG_WRITE;
- regs->hour &= ~MSTK_KICK_START;
- regs->creg &= ~MSTK_CREG_WRITE;
- spin_unlock_irq(&mostek_lock);
+ if (ofs == pdata->offset + M48T59_YEAR) {
+ if (val < 0x68)
+ val += 0x32;
+ else
+ val -= 0x68;
+ if ((val & 0xf) > 9)
+ val += 6;
+ if ((val & 0xf0) > 0x9A)
+ val += 0x60;
}
-
- prom_printf("CLOCK: Kick start procedure successful.\n");
+ writeb(val, regs + ofs);
}
-/* Return nonzero if the clock chip battery is low. */
-static inline int has_low_battery(void)
-{
- struct mostek48t02 *regs = (struct mostek48t02 *)mstk48t02_regs;
- unsigned char data1, data2;
+static struct m48t59_plat_data m48t59_data = {
+ .read_byte = mostek_read_byte,
+ .write_byte = mostek_write_byte,
+};
- spin_lock_irq(&mostek_lock);
- data1 = regs->eeprom[0]; /* Read some data. */
- regs->eeprom[0] = ~data1; /* Write back the complement. */
- data2 = regs->eeprom[0]; /* Read back the complement. */
- regs->eeprom[0] = data1; /* Restore the original value. */
- spin_unlock_irq(&mostek_lock);
+/* resource is set at runtime */
+static struct platform_device m48t59_rtc = {
+ .name = "rtc-m48t59",
+ .id = 0,
+ .num_resources = 1,
+ .dev = {
+ .platform_data = &m48t59_data,
+ },
+};
- return (data1 == data2); /* Was the write blocked? */
-}
-
-static void __devinit mostek_set_system_time(void)
-{
- unsigned int year, mon, day, hour, min, sec;
- struct mostek48t02 *mregs;
-
- mregs = (struct mostek48t02 *)mstk48t02_regs;
- if(!mregs) {
- prom_printf("Something wrong, clock regs not mapped yet.\n");
- prom_halt();
- }
- spin_lock_irq(&mostek_lock);
- mregs->creg |= MSTK_CREG_READ;
- sec = MSTK_REG_SEC(mregs);
- min = MSTK_REG_MIN(mregs);
- hour = MSTK_REG_HOUR(mregs);
- day = MSTK_REG_DOM(mregs);
- mon = MSTK_REG_MONTH(mregs);
- year = MSTK_CVT_YEAR( MSTK_REG_YEAR(mregs) );
- xtime.tv_sec = mktime(year, mon, day, hour, min, sec);
- xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ);
- set_normalized_timespec(&wall_to_monotonic,
- -xtime.tv_sec, -xtime.tv_nsec);
- mregs->creg &= ~MSTK_CREG_READ;
- spin_unlock_irq(&mostek_lock);
-}
-
-/* Probe for the real time clock chip on Sun4 */
-static inline void sun4_clock_probe(void)
-{
-#ifdef CONFIG_SUN4
- int temp;
- struct resource r;
-
- memset(&r, 0, sizeof(r));
- if( idprom->id_machtype == (SM_SUN4 | SM_4_330) ) {
- sp_clock_typ = MSTK48T02;
- r.start = sun4_clock_physaddr;
- mstk48t02_regs = sbus_ioremap(&r, 0,
- sizeof(struct mostek48t02), NULL);
- mstk48t08_regs = NULL; /* To catch weirdness */
- intersil_clock = NULL; /* just in case */
-
- /* Kick start the clock if it is completely stopped. */
- if (mostek_read(mstk48t02_regs + MOSTEK_SEC) & MSTK_STOP)
- kick_start_clock();
- } else if( idprom->id_machtype == (SM_SUN4 | SM_4_260)) {
- /* intersil setup code */
- printk("Clock: INTERSIL at %8x ",sun4_clock_physaddr);
- sp_clock_typ = INTERSIL;
- r.start = sun4_clock_physaddr;
- intersil_clock = (struct intersil *)
- sbus_ioremap(&r, 0, sizeof(*intersil_clock), "intersil");
- mstk48t02_regs = 0; /* just be sure */
- mstk48t08_regs = NULL; /* ditto */
- /* initialise the clock */
-
- intersil_intr(intersil_clock,INTERSIL_INT_100HZ);
-
- intersil_start(intersil_clock);
-
- intersil_read_intr(intersil_clock, temp);
- while (!(temp & 0x80))
- intersil_read_intr(intersil_clock, temp);
-
- intersil_read_intr(intersil_clock, temp);
- while (!(temp & 0x80))
- intersil_read_intr(intersil_clock, temp);
-
- intersil_stop(intersil_clock);
-
- }
-#endif
-}
-
-#ifndef CONFIG_SUN4
static int __devinit clock_probe(struct of_device *op, const struct of_device_id *match)
{
struct device_node *dp = op->node;
@@ -306,38 +173,26 @@
if (!model)
return -ENODEV;
+ m48t59_rtc.resource = &op->resource[0];
if (!strcmp(model, "mk48t02")) {
- sp_clock_typ = MSTK48T02;
-
/* Map the clock register io area read-only */
- mstk48t02_regs = of_ioremap(&op->resource[0], 0,
- sizeof(struct mostek48t02),
- "mk48t02");
- mstk48t08_regs = NULL; /* To catch weirdness */
+ m48t59_data.ioaddr = of_ioremap(&op->resource[0], 0,
+ 2048, "rtc-m48t59");
+ m48t59_data.type = M48T59RTC_TYPE_M48T02;
} else if (!strcmp(model, "mk48t08")) {
- sp_clock_typ = MSTK48T08;
- mstk48t08_regs = of_ioremap(&op->resource[0], 0,
- sizeof(struct mostek48t08),
- "mk48t08");
-
- mstk48t02_regs = &mstk48t08_regs->regs;
+ m48t59_data.ioaddr = of_ioremap(&op->resource[0], 0,
+ 8192, "rtc-m48t59");
+ m48t59_data.type = M48T59RTC_TYPE_M48T08;
} else
return -ENODEV;
- /* Report a low battery voltage condition. */
- if (has_low_battery())
- printk(KERN_CRIT "NVRAM: Low battery voltage!\n");
-
- /* Kick start the clock if it is completely stopped. */
- if (mostek_read(mstk48t02_regs + MOSTEK_SEC) & MSTK_STOP)
- kick_start_clock();
-
- mostek_set_system_time();
+ if (platform_device_register(&m48t59_rtc) < 0)
+ printk(KERN_ERR "Registering RTC device failed\n");
return 0;
}
-static struct of_device_id clock_match[] = {
+static struct of_device_id __initdata clock_match[] = {
{
.name = "eeprom",
},
@@ -348,7 +203,7 @@
.match_table = clock_match,
.probe = clock_probe,
.driver = {
- .name = "clock",
+ .name = "rtc",
},
};
@@ -364,7 +219,6 @@
* need to see the clock registers.
*/
fs_initcall(clock_init);
-#endif /* !CONFIG_SUN4 */
static void __init sbus_time_init(void)
{
@@ -372,51 +226,8 @@
BTFIXUPSET_CALL(bus_do_settimeofday, sbus_do_settimeofday, BTFIXUPCALL_NORM);
btfixup();
- if (ARCH_SUN4)
- sun4_clock_probe();
-
sparc_init_timers(timer_interrupt);
-#ifdef CONFIG_SUN4
- if(idprom->id_machtype == (SM_SUN4 | SM_4_330)) {
- mostek_set_system_time();
- } else if(idprom->id_machtype == (SM_SUN4 | SM_4_260) ) {
- /* initialise the intersil on sun4 */
- unsigned int year, mon, day, hour, min, sec;
- int temp;
- struct intersil *iregs;
-
- iregs=intersil_clock;
- if(!iregs) {
- prom_printf("Something wrong, clock regs not mapped yet.\n");
- prom_halt();
- }
-
- intersil_intr(intersil_clock,INTERSIL_INT_100HZ);
- disable_pil_irq(10);
- intersil_stop(iregs);
- intersil_read_intr(intersil_clock, temp);
-
- temp = iregs->clk.int_csec;
-
- sec = iregs->clk.int_sec;
- min = iregs->clk.int_min;
- hour = iregs->clk.int_hour;
- day = iregs->clk.int_day;
- mon = iregs->clk.int_month;
- year = MSTK_CVT_YEAR(iregs->clk.int_year);
-
- enable_pil_irq(10);
- intersil_start(iregs);
-
- xtime.tv_sec = mktime(year, mon, day, hour, min, sec);
- xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ);
- set_normalized_timespec(&wall_to_monotonic,
- -xtime.tv_sec, -xtime.tv_nsec);
- printk("%u/%u/%u %u:%u:%u\n",day,mon,year,hour,min,sec);
- }
-#endif
-
/* Now that OBP ticker has been silenced, it is safe to enable IRQ. */
local_irq_enable();
}
@@ -522,80 +333,15 @@
return 0;
}
-/*
- * BUG: This routine does not handle hour overflow properly; it just
- * sets the minutes. Usually you won't notice until after reboot!
- */
-static int set_rtc_mmss(unsigned long nowtime)
+static int set_rtc_mmss(unsigned long secs)
{
- int real_seconds, real_minutes, mostek_minutes;
- struct mostek48t02 *regs = (struct mostek48t02 *)mstk48t02_regs;
- unsigned long flags;
-#ifdef CONFIG_SUN4
- struct intersil *iregs = intersil_clock;
- int temp;
-#endif
+ struct rtc_device *rtc = rtc_class_open("rtc0");
+ int err = -1;
- /* Not having a register set can lead to trouble. */
- if (!regs) {
-#ifdef CONFIG_SUN4
- if(!iregs)
- return -1;
- else {
- temp = iregs->clk.int_csec;
-
- mostek_minutes = iregs->clk.int_min;
-
- real_seconds = nowtime % 60;
- real_minutes = nowtime / 60;
- if (((abs(real_minutes - mostek_minutes) + 15)/30) & 1)
- real_minutes += 30; /* correct for half hour time zone */
- real_minutes %= 60;
-
- if (abs(real_minutes - mostek_minutes) < 30) {
- intersil_stop(iregs);
- iregs->clk.int_sec=real_seconds;
- iregs->clk.int_min=real_minutes;
- intersil_start(iregs);
- } else {
- printk(KERN_WARNING
- "set_rtc_mmss: can't update from %d to %d\n",
- mostek_minutes, real_minutes);
- return -1;
- }
-
- return 0;
- }
-#endif
+ if (rtc) {
+ err = rtc_set_mmss(rtc, secs);
+ rtc_class_close(rtc);
}
- spin_lock_irqsave(&mostek_lock, flags);
- /* Read the current RTC minutes. */
- regs->creg |= MSTK_CREG_READ;
- mostek_minutes = MSTK_REG_MIN(regs);
- regs->creg &= ~MSTK_CREG_READ;
-
- /*
- * since we're only adjusting minutes and seconds,
- * don't interfere with hour overflow. This avoids
- * messing with unknown time zones but requires your
- * RTC not to be off by more than 15 minutes
- */
- real_seconds = nowtime % 60;
- real_minutes = nowtime / 60;
- if (((abs(real_minutes - mostek_minutes) + 15)/30) & 1)
- real_minutes += 30; /* correct for half hour time zone */
- real_minutes %= 60;
-
- if (abs(real_minutes - mostek_minutes) < 30) {
- regs->creg |= MSTK_CREG_WRITE;
- MSTK_SET_REG_SEC(regs,real_seconds);
- MSTK_SET_REG_MIN(regs,real_minutes);
- regs->creg &= ~MSTK_CREG_WRITE;
- spin_unlock_irqrestore(&mostek_lock, flags);
- return 0;
- } else {
- spin_unlock_irqrestore(&mostek_lock, flags);
- return -1;
- }
+ return err;
}
diff --git a/arch/sparc/kernel/traps.c b/arch/sparc/kernel/traps.c
index 5d45d5f..2b7d506 100644
--- a/arch/sparc/kernel/traps.c
+++ b/arch/sparc/kernel/traps.c
@@ -43,23 +43,6 @@
{
}
-void sun4m_nmi(struct pt_regs *regs)
-{
- unsigned long afsr, afar;
-
- printk("Aieee: sun4m NMI received!\n");
- /* XXX HyperSparc hack XXX */
- __asm__ __volatile__("mov 0x500, %%g1\n\t"
- "lda [%%g1] 0x4, %0\n\t"
- "mov 0x600, %%g1\n\t"
- "lda [%%g1] 0x4, %1\n\t" :
- "=r" (afsr), "=r" (afar));
- printk("afsr=%08lx afar=%08lx\n", afsr, afar);
- printk("you lose buddy boy...\n");
- show_regs(regs);
- prom_halt();
-}
-
void sun4d_nmi(struct pt_regs *regs)
{
printk("Aieee: sun4d NMI received!\n");
diff --git a/arch/sparc/mm/Makefile b/arch/sparc/mm/Makefile
index 109c8b2..ea88955 100644
--- a/arch/sparc/mm/Makefile
+++ b/arch/sparc/mm/Makefile
@@ -3,13 +3,8 @@
EXTRA_AFLAGS := -ansi
-obj-y := fault.o init.o loadmmu.o generic.o extable.o btfixup.o
-
-ifeq ($(CONFIG_SUN4),y)
-obj-y += nosrmmu.o
-else
-obj-y += srmmu.o iommu.o io-unit.o hypersparc.o viking.o tsunami.o swift.o
-endif
+obj-y := fault.o init.o loadmmu.o generic.o extable.o btfixup.o \
+ srmmu.o iommu.o io-unit.o hypersparc.o viking.o tsunami.o swift.o
ifdef CONFIG_HIGHMEM
obj-y += highmem.o
diff --git a/arch/sparc/mm/btfixup.c b/arch/sparc/mm/btfixup.c
index a312d12..5175ac2 100644
--- a/arch/sparc/mm/btfixup.c
+++ b/arch/sparc/mm/btfixup.c
@@ -20,11 +20,7 @@
extern char *srmmu_name;
static char version[] __initdata = "Boot time fixup v1.6. 4/Mar/98 Jakub Jelinek (jj@ultra.linux.cz). Patching kernel for ";
-#ifdef CONFIG_SUN4
-static char str_sun4c[] __initdata = "sun4\n";
-#else
static char str_sun4c[] __initdata = "sun4c\n";
-#endif
static char str_srmmu[] __initdata = "srmmu[%s]/";
static char str_iommu[] __initdata = "iommu\n";
static char str_iounit[] __initdata = "io-unit\n";
@@ -86,7 +82,7 @@
if (!visited) {
visited++;
printk(version);
- if (ARCH_SUN4C_SUN4)
+ if (ARCH_SUN4C)
printk(str_sun4c);
else {
printk(str_srmmu, srmmu_name);
diff --git a/arch/sparc/mm/fault.c b/arch/sparc/mm/fault.c
index 3604c2e..a507e11 100644
--- a/arch/sparc/mm/fault.c
+++ b/arch/sparc/mm/fault.c
@@ -191,7 +191,7 @@
* only copy the information from the master page table,
* nothing more.
*/
- if (!ARCH_SUN4C_SUN4 && address >= TASK_SIZE)
+ if (!ARCH_SUN4C && address >= TASK_SIZE)
goto vmalloc_fault;
info.si_code = SEGV_MAPERR;
diff --git a/arch/sparc/mm/init.c b/arch/sparc/mm/init.c
index e103f1b..677c1e1 100644
--- a/arch/sparc/mm/init.c
+++ b/arch/sparc/mm/init.c
@@ -23,6 +23,7 @@
#include <linux/highmem.h>
#include <linux/bootmem.h>
#include <linux/pagemap.h>
+#include <linux/poison.h>
#include <asm/system.h>
#include <asm/vac-ops.h>
@@ -480,6 +481,7 @@
for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) {
struct page *p;
+ memset((void *)addr, POISON_FREE_INITMEM, PAGE_SIZE);
p = virt_to_page(addr);
ClearPageReserved(p);
@@ -488,20 +490,26 @@
totalram_pages++;
num_physpages++;
}
- printk (KERN_INFO "Freeing unused kernel memory: %dk freed\n", (&__init_end - &__init_begin) >> 10);
+ printk(KERN_INFO "Freeing unused kernel memory: %dk freed\n",
+ (&__init_end - &__init_begin) >> 10);
}
#ifdef CONFIG_BLK_DEV_INITRD
void free_initrd_mem(unsigned long start, unsigned long end)
{
if (start < end)
- printk (KERN_INFO "Freeing initrd memory: %ldk freed\n", (end - start) >> 10);
+ printk(KERN_INFO "Freeing initrd memory: %ldk freed\n",
+ (end - start) >> 10);
for (; start < end; start += PAGE_SIZE) {
- struct page *p = virt_to_page(start);
+ struct page *p;
+
+ memset((void *)start, POISON_FREE_INITMEM, PAGE_SIZE);
+ p = virt_to_page(start);
ClearPageReserved(p);
init_page_count(p);
__free_page(p);
+ totalram_pages++;
num_physpages++;
}
}
diff --git a/arch/sparc/mm/io-unit.c b/arch/sparc/mm/io-unit.c
index f167835..daadf5f 100644
--- a/arch/sparc/mm/io-unit.c
+++ b/arch/sparc/mm/io-unit.c
@@ -12,10 +12,11 @@
#include <linux/highmem.h> /* pte_offset_map => kmap_atomic */
#include <linux/bitops.h>
#include <linux/scatterlist.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <asm/pgalloc.h>
#include <asm/pgtable.h>
-#include <asm/sbus.h>
#include <asm/io.h>
#include <asm/io-unit.h>
#include <asm/mxcc.h>
@@ -34,13 +35,10 @@
#define IOPERM (IOUPTE_CACHE | IOUPTE_WRITE | IOUPTE_VALID)
#define MKIOPTE(phys) __iopte((((phys)>>4) & IOUPTE_PAGE) | IOPERM)
-void __init
-iounit_init(int sbi_node, int io_node, struct sbus_bus *sbus)
+static void __init iounit_iommu_init(struct of_device *op)
{
- iopte_t *xpt, *xptend;
struct iounit_struct *iounit;
- struct linux_prom_registers iommu_promregs[PROMREG_MAX];
- struct resource r;
+ iopte_t *xpt, *xptend;
iounit = kzalloc(sizeof(struct iounit_struct), GFP_ATOMIC);
if (!iounit) {
@@ -55,18 +53,13 @@
iounit->rotor[1] = IOUNIT_BMAP2_START;
iounit->rotor[2] = IOUNIT_BMAPM_START;
- xpt = NULL;
- if(prom_getproperty(sbi_node, "reg", (void *) iommu_promregs,
- sizeof(iommu_promregs)) != -1) {
- prom_apply_generic_ranges(io_node, 0, iommu_promregs, 3);
- memset(&r, 0, sizeof(r));
- r.flags = iommu_promregs[2].which_io;
- r.start = iommu_promregs[2].phys_addr;
- xpt = (iopte_t *) sbus_ioremap(&r, 0, PAGE_SIZE * 16, "XPT");
+ xpt = of_ioremap(&op->resource[2], 0, PAGE_SIZE * 16, "XPT");
+ if (!xpt) {
+ prom_printf("SUN4D: Cannot map External Page Table.");
+ prom_halt();
}
- if(!xpt) panic("Cannot map External Page Table.");
- sbus->ofdev.dev.archdata.iommu = iounit;
+ op->dev.archdata.iommu = iounit;
iounit->page_table = xpt;
spin_lock_init(&iounit->lock);
@@ -75,6 +68,25 @@
iopte_val(*xpt++) = 0;
}
+static int __init iounit_init(void)
+{
+ extern void sun4d_init_sbi_irq(void);
+ struct device_node *dp;
+
+ for_each_node_by_name(dp, "sbi") {
+ struct of_device *op = of_find_device_by_node(dp);
+
+ iounit_iommu_init(op);
+ of_propagate_archdata(op);
+ }
+
+ sun4d_init_sbi_irq();
+
+ return 0;
+}
+
+subsys_initcall(iounit_init);
+
/* One has to hold iounit->lock to call this */
static unsigned long iounit_get_area(struct iounit_struct *iounit, unsigned long vaddr, int size)
{
@@ -124,10 +136,10 @@
return vaddr;
}
-static __u32 iounit_get_scsi_one(char *vaddr, unsigned long len, struct sbus_bus *sbus)
+static __u32 iounit_get_scsi_one(struct device *dev, char *vaddr, unsigned long len)
{
+ struct iounit_struct *iounit = dev->archdata.iommu;
unsigned long ret, flags;
- struct iounit_struct *iounit = sbus->ofdev.dev.archdata.iommu;
spin_lock_irqsave(&iounit->lock, flags);
ret = iounit_get_area(iounit, (unsigned long)vaddr, len);
@@ -135,10 +147,10 @@
return ret;
}
-static void iounit_get_scsi_sgl(struct scatterlist *sg, int sz, struct sbus_bus *sbus)
+static void iounit_get_scsi_sgl(struct device *dev, struct scatterlist *sg, int sz)
{
+ struct iounit_struct *iounit = dev->archdata.iommu;
unsigned long flags;
- struct iounit_struct *iounit = sbus->ofdev.dev.archdata.iommu;
/* FIXME: Cache some resolved pages - often several sg entries are to the same page */
spin_lock_irqsave(&iounit->lock, flags);
@@ -151,10 +163,10 @@
spin_unlock_irqrestore(&iounit->lock, flags);
}
-static void iounit_release_scsi_one(__u32 vaddr, unsigned long len, struct sbus_bus *sbus)
+static void iounit_release_scsi_one(struct device *dev, __u32 vaddr, unsigned long len)
{
+ struct iounit_struct *iounit = dev->archdata.iommu;
unsigned long flags;
- struct iounit_struct *iounit = sbus->ofdev.dev.archdata.iommu;
spin_lock_irqsave(&iounit->lock, flags);
len = ((vaddr & ~PAGE_MASK) + len + (PAGE_SIZE-1)) >> PAGE_SHIFT;
@@ -165,11 +177,11 @@
spin_unlock_irqrestore(&iounit->lock, flags);
}
-static void iounit_release_scsi_sgl(struct scatterlist *sg, int sz, struct sbus_bus *sbus)
+static void iounit_release_scsi_sgl(struct device *dev, struct scatterlist *sg, int sz)
{
+ struct iounit_struct *iounit = dev->archdata.iommu;
unsigned long flags;
unsigned long vaddr, len;
- struct iounit_struct *iounit = sbus->ofdev.dev.archdata.iommu;
spin_lock_irqsave(&iounit->lock, flags);
while (sz != 0) {
@@ -185,12 +197,12 @@
}
#ifdef CONFIG_SBUS
-static int iounit_map_dma_area(dma_addr_t *pba, unsigned long va, __u32 addr, int len)
+static int iounit_map_dma_area(struct device *dev, dma_addr_t *pba, unsigned long va, __u32 addr, int len)
{
+ struct iounit_struct *iounit = dev->archdata.iommu;
unsigned long page, end;
pgprot_t dvma_prot;
iopte_t *iopte;
- struct sbus_bus *sbus;
*pba = addr;
@@ -212,12 +224,8 @@
i = ((addr - IOUNIT_DMA_BASE) >> PAGE_SHIFT);
- for_each_sbus(sbus) {
- struct iounit_struct *iounit = sbus->ofdev.dev.archdata.iommu;
-
- iopte = (iopte_t *)(iounit->page_table + i);
- *iopte = MKIOPTE(__pa(page));
- }
+ iopte = (iopte_t *)(iounit->page_table + i);
+ *iopte = MKIOPTE(__pa(page));
}
addr += PAGE_SIZE;
va += PAGE_SIZE;
@@ -228,23 +236,10 @@
return 0;
}
-static void iounit_unmap_dma_area(unsigned long addr, int len)
+static void iounit_unmap_dma_area(struct device *dev, unsigned long addr, int len)
{
/* XXX Somebody please fill this in */
}
-
-/* XXX We do not pass sbus device here, bad. */
-static struct page *iounit_translate_dvma(unsigned long addr)
-{
- struct sbus_bus *sbus = sbus_root; /* They are all the same */
- struct iounit_struct *iounit = sbus->ofdev.dev.archdata.iommu;
- int i;
- iopte_t *iopte;
-
- i = ((addr - IOUNIT_DMA_BASE) >> PAGE_SHIFT);
- iopte = (iopte_t *)(iounit->page_table + i);
- return pfn_to_page(iopte_val(*iopte) >> (PAGE_SHIFT-4)); /* XXX sun4d guru, help */
-}
#endif
static char *iounit_lockarea(char *vaddr, unsigned long len)
@@ -271,54 +266,5 @@
#ifdef CONFIG_SBUS
BTFIXUPSET_CALL(mmu_map_dma_area, iounit_map_dma_area, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(mmu_unmap_dma_area, iounit_unmap_dma_area, BTFIXUPCALL_NORM);
- BTFIXUPSET_CALL(mmu_translate_dvma, iounit_translate_dvma, BTFIXUPCALL_NORM);
#endif
}
-
-__u32 iounit_map_dma_init(struct sbus_bus *sbus, int size)
-{
- int i, j, k, npages;
- unsigned long rotor, scan, limit;
- unsigned long flags;
- __u32 ret;
- struct iounit_struct *iounit = sbus->ofdev.dev.archdata.iommu;
-
- npages = (size + (PAGE_SIZE-1)) >> PAGE_SHIFT;
- i = 0x0213;
- spin_lock_irqsave(&iounit->lock, flags);
-next: j = (i & 15);
- rotor = iounit->rotor[j - 1];
- limit = iounit->limit[j];
- scan = rotor;
-nexti: scan = find_next_zero_bit(iounit->bmap, limit, scan);
- if (scan + npages > limit) {
- if (limit != rotor) {
- limit = rotor;
- scan = iounit->limit[j - 1];
- goto nexti;
- }
- i >>= 4;
- if (!(i & 15))
- panic("iounit_map_dma_init: Couldn't find free iopte slots for %d bytes\n", size);
- goto next;
- }
- for (k = 1, scan++; k < npages; k++)
- if (test_bit(scan++, iounit->bmap))
- goto nexti;
- iounit->rotor[j - 1] = (scan < limit) ? scan : iounit->limit[j - 1];
- scan -= npages;
- ret = IOUNIT_DMA_BASE + (scan << PAGE_SHIFT);
- for (k = 0; k < npages; k++, scan++)
- set_bit(scan, iounit->bmap);
- spin_unlock_irqrestore(&iounit->lock, flags);
- return ret;
-}
-
-__u32 iounit_map_dma_page(__u32 vaddr, void *addr, struct sbus_bus *sbus)
-{
- int scan = (vaddr - IOUNIT_DMA_BASE) >> PAGE_SHIFT;
- struct iounit_struct *iounit = sbus->ofdev.dev.archdata.iommu;
-
- iounit->page_table[scan] = MKIOPTE(__pa(((unsigned long)addr) & PAGE_MASK));
- return vaddr + (((unsigned long)addr) & ~PAGE_MASK);
-}
diff --git a/arch/sparc/mm/iommu.c b/arch/sparc/mm/iommu.c
index 4b93427..e7a499e 100644
--- a/arch/sparc/mm/iommu.c
+++ b/arch/sparc/mm/iommu.c
@@ -13,10 +13,11 @@
#include <linux/slab.h>
#include <linux/highmem.h> /* pte_offset_map => kmap_atomic */
#include <linux/scatterlist.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <asm/pgalloc.h>
#include <asm/pgtable.h>
-#include <asm/sbus.h>
#include <asm/io.h>
#include <asm/mxcc.h>
#include <asm/mbus.h>
@@ -55,30 +56,21 @@
#define IOPERM (IOPTE_CACHE | IOPTE_WRITE | IOPTE_VALID)
#define MKIOPTE(pfn, perm) (((((pfn)<<8) & IOPTE_PAGE) | (perm)) & ~IOPTE_WAZ)
-void __init
-iommu_init(int iommund, struct sbus_bus *sbus)
+static void __init sbus_iommu_init(struct of_device *op)
{
- unsigned int impl, vers;
- unsigned long tmp;
struct iommu_struct *iommu;
- struct linux_prom_registers iommu_promregs[PROMREG_MAX];
- struct resource r;
+ unsigned int impl, vers;
unsigned long *bitmap;
+ unsigned long tmp;
iommu = kmalloc(sizeof(struct iommu_struct), GFP_ATOMIC);
if (!iommu) {
prom_printf("Unable to allocate iommu structure\n");
prom_halt();
}
- iommu->regs = NULL;
- if (prom_getproperty(iommund, "reg", (void *) iommu_promregs,
- sizeof(iommu_promregs)) != -1) {
- memset(&r, 0, sizeof(r));
- r.flags = iommu_promregs[0].which_io;
- r.start = iommu_promregs[0].phys_addr;
- iommu->regs = (struct iommu_regs *)
- sbus_ioremap(&r, 0, PAGE_SIZE * 3, "iommu_regs");
- }
+
+ iommu->regs = of_ioremap(&op->resource[0], 0, PAGE_SIZE * 3,
+ "iommu_regs");
if (!iommu->regs) {
prom_printf("Cannot map IOMMU registers\n");
prom_halt();
@@ -128,13 +120,29 @@
else
iommu->usemap.num_colors = 1;
- printk("IOMMU: impl %d vers %d table 0x%p[%d B] map [%d b]\n",
- impl, vers, iommu->page_table,
- (int)(IOMMU_NPTES*sizeof(iopte_t)), (int)IOMMU_NPTES);
+ printk(KERN_INFO "IOMMU: impl %d vers %d table 0x%p[%d B] map [%d b]\n",
+ impl, vers, iommu->page_table,
+ (int)(IOMMU_NPTES*sizeof(iopte_t)), (int)IOMMU_NPTES);
- sbus->ofdev.dev.archdata.iommu = iommu;
+ op->dev.archdata.iommu = iommu;
}
+static int __init iommu_init(void)
+{
+ struct device_node *dp;
+
+ for_each_node_by_name(dp, "iommu") {
+ struct of_device *op = of_find_device_by_node(dp);
+
+ sbus_iommu_init(op);
+ of_propagate_archdata(op);
+ }
+
+ return 0;
+}
+
+subsys_initcall(iommu_init);
+
/* This begs to be btfixup-ed by srmmu. */
/* Flush the iotlb entries to ram. */
/* This could be better if we didn't have to flush whole pages. */
@@ -164,9 +172,9 @@
}
}
-static u32 iommu_get_one(struct page *page, int npages, struct sbus_bus *sbus)
+static u32 iommu_get_one(struct device *dev, struct page *page, int npages)
{
- struct iommu_struct *iommu = sbus->ofdev.dev.archdata.iommu;
+ struct iommu_struct *iommu = dev->archdata.iommu;
int ioptex;
iopte_t *iopte, *iopte0;
unsigned int busa, busa0;
@@ -194,8 +202,7 @@
return busa0;
}
-static u32 iommu_get_scsi_one(char *vaddr, unsigned int len,
- struct sbus_bus *sbus)
+static u32 iommu_get_scsi_one(struct device *dev, char *vaddr, unsigned int len)
{
unsigned long off;
int npages;
@@ -205,22 +212,22 @@
off = (unsigned long)vaddr & ~PAGE_MASK;
npages = (off + len + PAGE_SIZE-1) >> PAGE_SHIFT;
page = virt_to_page((unsigned long)vaddr & PAGE_MASK);
- busa = iommu_get_one(page, npages, sbus);
+ busa = iommu_get_one(dev, page, npages);
return busa + off;
}
-static __u32 iommu_get_scsi_one_noflush(char *vaddr, unsigned long len, struct sbus_bus *sbus)
+static __u32 iommu_get_scsi_one_noflush(struct device *dev, char *vaddr, unsigned long len)
{
- return iommu_get_scsi_one(vaddr, len, sbus);
+ return iommu_get_scsi_one(dev, vaddr, len);
}
-static __u32 iommu_get_scsi_one_gflush(char *vaddr, unsigned long len, struct sbus_bus *sbus)
+static __u32 iommu_get_scsi_one_gflush(struct device *dev, char *vaddr, unsigned long len)
{
flush_page_for_dma(0);
- return iommu_get_scsi_one(vaddr, len, sbus);
+ return iommu_get_scsi_one(dev, vaddr, len);
}
-static __u32 iommu_get_scsi_one_pflush(char *vaddr, unsigned long len, struct sbus_bus *sbus)
+static __u32 iommu_get_scsi_one_pflush(struct device *dev, char *vaddr, unsigned long len)
{
unsigned long page = ((unsigned long) vaddr) & PAGE_MASK;
@@ -228,23 +235,23 @@
flush_page_for_dma(page);
page += PAGE_SIZE;
}
- return iommu_get_scsi_one(vaddr, len, sbus);
+ return iommu_get_scsi_one(dev, vaddr, len);
}
-static void iommu_get_scsi_sgl_noflush(struct scatterlist *sg, int sz, struct sbus_bus *sbus)
+static void iommu_get_scsi_sgl_noflush(struct device *dev, struct scatterlist *sg, int sz)
{
int n;
while (sz != 0) {
--sz;
n = (sg->length + sg->offset + PAGE_SIZE-1) >> PAGE_SHIFT;
- sg->dvma_address = iommu_get_one(sg_page(sg), n, sbus) + sg->offset;
+ sg->dvma_address = iommu_get_one(dev, sg_page(sg), n) + sg->offset;
sg->dvma_length = (__u32) sg->length;
sg = sg_next(sg);
}
}
-static void iommu_get_scsi_sgl_gflush(struct scatterlist *sg, int sz, struct sbus_bus *sbus)
+static void iommu_get_scsi_sgl_gflush(struct device *dev, struct scatterlist *sg, int sz)
{
int n;
@@ -252,13 +259,13 @@
while (sz != 0) {
--sz;
n = (sg->length + sg->offset + PAGE_SIZE-1) >> PAGE_SHIFT;
- sg->dvma_address = iommu_get_one(sg_page(sg), n, sbus) + sg->offset;
+ sg->dvma_address = iommu_get_one(dev, sg_page(sg), n) + sg->offset;
sg->dvma_length = (__u32) sg->length;
sg = sg_next(sg);
}
}
-static void iommu_get_scsi_sgl_pflush(struct scatterlist *sg, int sz, struct sbus_bus *sbus)
+static void iommu_get_scsi_sgl_pflush(struct device *dev, struct scatterlist *sg, int sz)
{
unsigned long page, oldpage = 0;
int n, i;
@@ -283,15 +290,15 @@
}
}
- sg->dvma_address = iommu_get_one(sg_page(sg), n, sbus) + sg->offset;
+ sg->dvma_address = iommu_get_one(dev, sg_page(sg), n) + sg->offset;
sg->dvma_length = (__u32) sg->length;
sg = sg_next(sg);
}
}
-static void iommu_release_one(u32 busa, int npages, struct sbus_bus *sbus)
+static void iommu_release_one(struct device *dev, u32 busa, int npages)
{
- struct iommu_struct *iommu = sbus->ofdev.dev.archdata.iommu;
+ struct iommu_struct *iommu = dev->archdata.iommu;
int ioptex;
int i;
@@ -305,17 +312,17 @@
bit_map_clear(&iommu->usemap, ioptex, npages);
}
-static void iommu_release_scsi_one(__u32 vaddr, unsigned long len, struct sbus_bus *sbus)
+static void iommu_release_scsi_one(struct device *dev, __u32 vaddr, unsigned long len)
{
unsigned long off;
int npages;
off = vaddr & ~PAGE_MASK;
npages = (off + len + PAGE_SIZE-1) >> PAGE_SHIFT;
- iommu_release_one(vaddr & PAGE_MASK, npages, sbus);
+ iommu_release_one(dev, vaddr & PAGE_MASK, npages);
}
-static void iommu_release_scsi_sgl(struct scatterlist *sg, int sz, struct sbus_bus *sbus)
+static void iommu_release_scsi_sgl(struct device *dev, struct scatterlist *sg, int sz)
{
int n;
@@ -323,18 +330,18 @@
--sz;
n = (sg->length + sg->offset + PAGE_SIZE-1) >> PAGE_SHIFT;
- iommu_release_one(sg->dvma_address & PAGE_MASK, n, sbus);
+ iommu_release_one(dev, sg->dvma_address & PAGE_MASK, n);
sg->dvma_address = 0x21212121;
sg = sg_next(sg);
}
}
#ifdef CONFIG_SBUS
-static int iommu_map_dma_area(dma_addr_t *pba, unsigned long va,
- unsigned long addr, int len)
+static int iommu_map_dma_area(struct device *dev, dma_addr_t *pba, unsigned long va,
+ unsigned long addr, int len)
{
+ struct iommu_struct *iommu = dev->archdata.iommu;
unsigned long page, end;
- struct iommu_struct *iommu = sbus_root->ofdev.dev.archdata.iommu;
iopte_t *iopte = iommu->page_table;
iopte_t *first;
int ioptex;
@@ -397,9 +404,9 @@
return 0;
}
-static void iommu_unmap_dma_area(unsigned long busa, int len)
+static void iommu_unmap_dma_area(struct device *dev, unsigned long busa, int len)
{
- struct iommu_struct *iommu = sbus_root->ofdev.dev.archdata.iommu;
+ struct iommu_struct *iommu = dev->archdata.iommu;
iopte_t *iopte = iommu->page_table;
unsigned long end;
int ioptex = (busa - iommu->start) >> PAGE_SHIFT;
@@ -417,15 +424,6 @@
iommu_invalidate(iommu->regs);
bit_map_clear(&iommu->usemap, ioptex, len >> PAGE_SHIFT);
}
-
-static struct page *iommu_translate_dvma(unsigned long busa)
-{
- struct iommu_struct *iommu = sbus_root->ofdev.dev.archdata.iommu;
- iopte_t *iopte = iommu->page_table;
-
- iopte += ((busa - iommu->start) >> PAGE_SHIFT);
- return pfn_to_page((iopte_val(*iopte) & IOPTE_PAGE) >> (PAGE_SHIFT-4));
-}
#endif
static char *iommu_lockarea(char *vaddr, unsigned long len)
@@ -461,7 +459,6 @@
#ifdef CONFIG_SBUS
BTFIXUPSET_CALL(mmu_map_dma_area, iommu_map_dma_area, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(mmu_unmap_dma_area, iommu_unmap_dma_area, BTFIXUPCALL_NORM);
- BTFIXUPSET_CALL(mmu_translate_dvma, iommu_translate_dvma, BTFIXUPCALL_NORM);
#endif
if (viking_mxcc_present || srmmu_modtype == HyperSparc) {
diff --git a/arch/sparc/mm/nosrmmu.c b/arch/sparc/mm/nosrmmu.c
deleted file mode 100644
index 3701f70..0000000
--- a/arch/sparc/mm/nosrmmu.c
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * nosrmmu.c: This file is a bunch of dummies for sun4 compiles,
- * so that it does not need srmmu and avoid ifdefs.
- *
- * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
- */
-
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/init.h>
-#include <asm/mbus.h>
-#include <asm/sbus.h>
-
-static char shouldnothappen[] __initdata = "SUN4 kernel can only run on SUN4\n";
-
-enum mbus_module srmmu_modtype;
-void *srmmu_nocache_pool;
-
-int vac_cache_size = 0;
-
-static void __init should_not_happen(void)
-{
- prom_printf(shouldnothappen);
- prom_halt();
-}
-
-void __init srmmu_frob_mem_map(unsigned long start_mem)
-{
- should_not_happen();
-}
-
-unsigned long __init srmmu_paging_init(unsigned long start_mem, unsigned long end_mem)
-{
- should_not_happen();
- return 0;
-}
-
-void __init ld_mmu_srmmu(void)
-{
- should_not_happen();
-}
-
-void srmmu_mapioaddr(unsigned long physaddr, unsigned long virt_addr, int bus_type, int rdonly)
-{
-}
-
-void srmmu_unmapioaddr(unsigned long virt_addr)
-{
-}
-
-__u32 iounit_map_dma_init(struct sbus_bus *sbus, int size)
-{
- return 0;
-}
-
-__u32 iounit_map_dma_page(__u32 vaddr, void *addr, struct sbus_bus *sbus)
-{
- return 0;
-}
diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c
index ee30462..6a5d7ca 100644
--- a/arch/sparc/mm/srmmu.c
+++ b/arch/sparc/mm/srmmu.c
@@ -31,7 +31,6 @@
#include <asm/mbus.h>
#include <asm/cache.h>
#include <asm/oplib.h>
-#include <asm/sbus.h>
#include <asm/asi.h>
#include <asm/msi.h>
#include <asm/mmu_context.h>
diff --git a/arch/sparc/mm/sun4c.c b/arch/sparc/mm/sun4c.c
index d1782f6..fe65aee 100644
--- a/arch/sparc/mm/sun4c.c
+++ b/arch/sparc/mm/sun4c.c
@@ -31,7 +31,6 @@
#include <asm/oplib.h>
#include <asm/openprom.h>
#include <asm/mmu_context.h>
-#include <asm/sun4paddr.h>
#include <asm/highmem.h>
#include <asm/btfixup.h>
#include <asm/cacheflush.h>
@@ -52,15 +51,11 @@
extern unsigned long page_kernel;
-#ifdef CONFIG_SUN4
-#define SUN4C_VAC_SIZE sun4c_vacinfo.num_bytes
-#else
/* That's it, we prom_halt() on sun4c if the cache size is something other than 65536.
* So let's save some cycles and just use that everywhere except for that bootup
* sanity check.
*/
#define SUN4C_VAC_SIZE 65536
-#endif
#define SUN4C_KERNEL_BUCKETS 32
@@ -285,75 +280,32 @@
{
sun4c_disable_vac();
- if (ARCH_SUN4) {
- switch (idprom->id_machtype) {
-
- case (SM_SUN4|SM_4_110):
- sun4c_vacinfo.type = VAC_NONE;
- sun4c_vacinfo.num_bytes = 0;
- sun4c_vacinfo.linesize = 0;
- sun4c_vacinfo.do_hwflushes = 0;
- prom_printf("No VAC. Get some bucks and buy a real computer.");
- prom_halt();
- break;
-
- case (SM_SUN4|SM_4_260):
- sun4c_vacinfo.type = VAC_WRITE_BACK;
- sun4c_vacinfo.num_bytes = 128 * 1024;
- sun4c_vacinfo.linesize = 16;
- sun4c_vacinfo.do_hwflushes = 0;
- break;
-
- case (SM_SUN4|SM_4_330):
- sun4c_vacinfo.type = VAC_WRITE_THROUGH;
- sun4c_vacinfo.num_bytes = 128 * 1024;
- sun4c_vacinfo.linesize = 16;
- sun4c_vacinfo.do_hwflushes = 0;
- break;
-
- case (SM_SUN4|SM_4_470):
- sun4c_vacinfo.type = VAC_WRITE_BACK;
- sun4c_vacinfo.num_bytes = 128 * 1024;
- sun4c_vacinfo.linesize = 32;
- sun4c_vacinfo.do_hwflushes = 0;
- break;
-
- default:
- prom_printf("Cannot initialize VAC - weird sun4 model idprom->id_machtype = %d", idprom->id_machtype);
- prom_halt();
- };
+ if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) ||
+ (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) {
+ /* PROM on SS1 lacks this info, to be super safe we
+ * hard code it here since this arch is cast in stone.
+ */
+ sun4c_vacinfo.num_bytes = 65536;
+ sun4c_vacinfo.linesize = 16;
} else {
- sun4c_vacinfo.type = VAC_WRITE_THROUGH;
+ sun4c_vacinfo.num_bytes =
+ prom_getintdefault(prom_root_node, "vac-size", 65536);
+ sun4c_vacinfo.linesize =
+ prom_getintdefault(prom_root_node, "vac-linesize", 16);
+ }
+ sun4c_vacinfo.do_hwflushes =
+ prom_getintdefault(prom_root_node, "vac-hwflush", 0);
- if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) ||
- (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) {
- /* PROM on SS1 lacks this info, to be super safe we
- * hard code it here since this arch is cast in stone.
- */
- sun4c_vacinfo.num_bytes = 65536;
- sun4c_vacinfo.linesize = 16;
- } else {
- sun4c_vacinfo.num_bytes =
- prom_getintdefault(prom_root_node, "vac-size", 65536);
- sun4c_vacinfo.linesize =
- prom_getintdefault(prom_root_node, "vac-linesize", 16);
- }
+ if (sun4c_vacinfo.do_hwflushes == 0)
sun4c_vacinfo.do_hwflushes =
- prom_getintdefault(prom_root_node, "vac-hwflush", 0);
+ prom_getintdefault(prom_root_node, "vac_hwflush", 0);
- if (sun4c_vacinfo.do_hwflushes == 0)
- sun4c_vacinfo.do_hwflushes =
- prom_getintdefault(prom_root_node, "vac_hwflush", 0);
-
- if (sun4c_vacinfo.num_bytes != 65536) {
- prom_printf("WEIRD Sun4C VAC cache size, "
- "tell sparclinux@vger.kernel.org");
- prom_halt();
- }
+ if (sun4c_vacinfo.num_bytes != 65536) {
+ prom_printf("WEIRD Sun4C VAC cache size, "
+ "tell sparclinux@vger.kernel.org");
+ prom_halt();
}
- sun4c_vacinfo.num_lines =
- (sun4c_vacinfo.num_bytes / sun4c_vacinfo.linesize);
switch (sun4c_vacinfo.linesize) {
case 16:
sun4c_vacinfo.log2lsize = 4;
@@ -447,49 +399,18 @@
static void __init sun4c_probe_mmu(void)
{
- if (ARCH_SUN4) {
- switch (idprom->id_machtype) {
- case (SM_SUN4|SM_4_110):
- prom_printf("No support for 4100 yet\n");
- prom_halt();
- num_segmaps = 256;
- num_contexts = 8;
- break;
-
- case (SM_SUN4|SM_4_260):
- /* should be 512 segmaps. when it get fixed */
- num_segmaps = 256;
- num_contexts = 16;
- break;
-
- case (SM_SUN4|SM_4_330):
- num_segmaps = 256;
- num_contexts = 16;
- break;
-
- case (SM_SUN4|SM_4_470):
- /* should be 1024 segmaps. when it get fixed */
- num_segmaps = 256;
- num_contexts = 64;
- break;
- default:
- prom_printf("Invalid SUN4 model\n");
- prom_halt();
- };
+ if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) ||
+ (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) {
+ /* Hardcode these just to be safe, PROM on SS1 does
+ * not have this info available in the root node.
+ */
+ num_segmaps = 128;
+ num_contexts = 8;
} else {
- if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) ||
- (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) {
- /* Hardcode these just to be safe, PROM on SS1 does
- * not have this info available in the root node.
- */
- num_segmaps = 128;
- num_contexts = 8;
- } else {
- num_segmaps =
- prom_getintdefault(prom_root_node, "mmu-npmg", 128);
- num_contexts =
- prom_getintdefault(prom_root_node, "mmu-nctx", 0x8);
- }
+ num_segmaps =
+ prom_getintdefault(prom_root_node, "mmu-npmg", 128);
+ num_contexts =
+ prom_getintdefault(prom_root_node, "mmu-nctx", 0x8);
}
patch_kernel_fault_handler();
}
@@ -501,18 +422,14 @@
int node;
struct linux_prom_registers regs[1];
- if (ARCH_SUN4) {
- sun4c_memerr_reg = ioremap(sun4_memreg_physaddr, PAGE_SIZE);
- } else {
- node = prom_getchild(prom_root_node);
- node = prom_searchsiblings(prom_root_node, "memory-error");
- if (!node)
- return;
- if (prom_getproperty(node, "reg", (char *)regs, sizeof(regs)) <= 0)
- return;
- /* hmm I think regs[0].which_io is zero here anyways */
- sun4c_memerr_reg = ioremap(regs[0].phys_addr, regs[0].reg_size);
- }
+ node = prom_getchild(prom_root_node);
+ node = prom_searchsiblings(prom_root_node, "memory-error");
+ if (!node)
+ return;
+ if (prom_getproperty(node, "reg", (char *)regs, sizeof(regs)) <= 0)
+ return;
+ /* hmm I think regs[0].which_io is zero here anyways */
+ sun4c_memerr_reg = ioremap(regs[0].phys_addr, regs[0].reg_size);
}
static inline void sun4c_init_ss2_cache_bug(void)
@@ -521,7 +438,6 @@
if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS2)) ||
(idprom->id_machtype == (SM_SUN4C | SM_4C_IPX)) ||
- (idprom->id_machtype == (SM_SUN4 | SM_4_330)) ||
(idprom->id_machtype == (SM_SUN4C | SM_4C_ELC))) {
/* Whee.. */
printk("SS2 cache bug detected, uncaching trap table page\n");
@@ -532,8 +448,8 @@
}
/* Addr is always aligned on a page boundary for us already. */
-static int sun4c_map_dma_area(dma_addr_t *pba, unsigned long va,
- unsigned long addr, int len)
+static int sun4c_map_dma_area(struct device *dev, dma_addr_t *pba, unsigned long va,
+ unsigned long addr, int len)
{
unsigned long page, end;
@@ -555,14 +471,7 @@
return 0;
}
-static struct page *sun4c_translate_dvma(unsigned long busa)
-{
- /* Fortunately for us, bus_addr == uncached_virt in sun4c. */
- unsigned long pte = sun4c_get_pte(busa);
- return pfn_to_page(pte & SUN4C_PFN_MASK);
-}
-
-static void sun4c_unmap_dma_area(unsigned long busa, int len)
+static void sun4c_unmap_dma_area(struct device *dev, unsigned long busa, int len)
{
/* Fortunately for us, bus_addr == uncached_virt in sun4c. */
/* XXX Implement this */
@@ -624,11 +533,7 @@
{
unsigned long vaddr;
unsigned char pseg, ctx;
-#ifdef CONFIG_SUN4
- /* sun4/110 and 260 have no kadb. */
- if ((idprom->id_machtype != (SM_SUN4 | SM_4_260)) &&
- (idprom->id_machtype != (SM_SUN4 | SM_4_110))) {
-#endif
+
for (vaddr = KADB_DEBUGGER_BEGVM;
vaddr < LINUX_OPPROM_ENDVM;
vaddr += SUN4C_REAL_PGDIR_SIZE) {
@@ -640,9 +545,7 @@
fix_permissions(vaddr, _SUN4C_PAGE_PRIV, 0);
}
}
-#ifdef CONFIG_SUN4
- }
-#endif
+
for (vaddr = KERNBASE; vaddr < kernel_end; vaddr += SUN4C_REAL_PGDIR_SIZE) {
pseg = sun4c_get_segmap(vaddr);
mmu_entry_pool[pseg].locked = 1;
@@ -1048,14 +951,10 @@
* so we must flush the cache to guarantee consistency.
*/
sun4c_flush_page(pages);
-#ifndef CONFIG_SUN4
sun4c_flush_page(pages + PAGE_SIZE);
-#endif
sun4c_put_pte(addr, BUCKET_PTE(pages));
-#ifndef CONFIG_SUN4
sun4c_put_pte(addr + PAGE_SIZE, BUCKET_PTE(pages + PAGE_SIZE));
-#endif
#ifdef CONFIG_DEBUG_STACK_USAGE
memset((void *)addr, 0, PAGE_SIZE << THREAD_INFO_ORDER);
@@ -1072,13 +971,11 @@
/* We are deleting a mapping, so the flush here is mandatory. */
sun4c_flush_page(tiaddr);
-#ifndef CONFIG_SUN4
sun4c_flush_page(tiaddr + PAGE_SIZE);
-#endif
+
sun4c_put_pte(tiaddr, 0);
-#ifndef CONFIG_SUN4
sun4c_put_pte(tiaddr + PAGE_SIZE, 0);
-#endif
+
sun4c_bucket[entry] = BUCKET_EMPTY;
if (entry < sun4c_lowbucket_avail)
sun4c_lowbucket_avail = entry;
@@ -1211,7 +1108,7 @@
* by implication and fool the page locking code above
* if passed to by mistake.
*/
-static __u32 sun4c_get_scsi_one(char *bufptr, unsigned long len, struct sbus_bus *sbus)
+static __u32 sun4c_get_scsi_one(struct device *dev, char *bufptr, unsigned long len)
{
unsigned long page;
@@ -1223,7 +1120,7 @@
return (__u32)sun4c_lockarea(bufptr, len);
}
-static void sun4c_get_scsi_sgl(struct scatterlist *sg, int sz, struct sbus_bus *sbus)
+static void sun4c_get_scsi_sgl(struct device *dev, struct scatterlist *sg, int sz)
{
while (sz != 0) {
--sz;
@@ -1233,14 +1130,14 @@
}
}
-static void sun4c_release_scsi_one(__u32 bufptr, unsigned long len, struct sbus_bus *sbus)
+static void sun4c_release_scsi_one(struct device *dev, __u32 bufptr, unsigned long len)
{
if (bufptr < sun4c_iobuffer_start)
return; /* On kernel stack or similar, see above */
sun4c_unlockarea((char *)bufptr, len);
}
-static void sun4c_release_scsi_sgl(struct scatterlist *sg, int sz, struct sbus_bus *sbus)
+static void sun4c_release_scsi_sgl(struct device *dev, struct scatterlist *sg, int sz)
{
while (sz != 0) {
--sz;
@@ -2263,7 +2160,6 @@
BTFIXUPSET_CALL(mmu_map_dma_area, sun4c_map_dma_area, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(mmu_unmap_dma_area, sun4c_unmap_dma_area, BTFIXUPCALL_NORM);
- BTFIXUPSET_CALL(mmu_translate_dvma, sun4c_translate_dvma, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(sparc_mapiorange, sun4c_mapiorange, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(sparc_unmapiorange, sun4c_unmapiorange, BTFIXUPCALL_NORM);
diff --git a/arch/sparc/prom/Makefile b/arch/sparc/prom/Makefile
index 7f5eacf..8f7e185 100644
--- a/arch/sparc/prom/Makefile
+++ b/arch/sparc/prom/Makefile
@@ -4,5 +4,3 @@
lib-y := bootstr.o devmap.o devops.o init.o memory.o misc.o mp.o \
palloc.o ranges.o segment.o console.o printf.o tree.o
-
-lib-$(CONFIG_SUN4) += sun4prom.o
diff --git a/arch/sparc/prom/bootstr.c b/arch/sparc/prom/bootstr.c
index 5a35c76..916831d 100644
--- a/arch/sparc/prom/bootstr.c
+++ b/arch/sparc/prom/bootstr.c
@@ -6,15 +6,12 @@
#include <linux/string.h>
#include <asm/oplib.h>
-#include <asm/sun4prom.h>
#include <linux/init.h>
#define BARG_LEN 256
static char barg_buf[BARG_LEN] = { 0 };
static char fetched __initdata = 0;
-extern linux_sun4_romvec *sun4_romvec;
-
char * __init
prom_getbootargs(void)
{
@@ -28,7 +25,6 @@
switch(prom_vers) {
case PROM_V0:
- case PROM_SUN4:
cp = barg_buf;
/* Start from 1 and go over fd(0,0,0)kernel */
for(iter = 1; iter < 8; iter++) {
diff --git a/arch/sparc/prom/console.c b/arch/sparc/prom/console.c
index 790057a..b3075d7 100644
--- a/arch/sparc/prom/console.c
+++ b/arch/sparc/prom/console.c
@@ -10,7 +10,6 @@
#include <linux/kernel.h>
#include <linux/sched.h>
#include <asm/openprom.h>
-#include <asm/sun4prom.h>
#include <asm/oplib.h>
#include <asm/system.h>
#include <linux/string.h>
@@ -30,7 +29,6 @@
spin_lock_irqsave(&prom_lock, flags);
switch(prom_vers) {
case PROM_V0:
- case PROM_SUN4:
i = (*(romvec->pv_nbgetchar))();
break;
case PROM_V2:
@@ -63,7 +61,6 @@
spin_lock_irqsave(&prom_lock, flags);
switch(prom_vers) {
case PROM_V0:
- case PROM_SUN4:
i = (*(romvec->pv_nbputchar))(c);
break;
case PROM_V2:
diff --git a/arch/sparc/prom/init.c b/arch/sparc/prom/init.c
index 729f870..873217c 100644
--- a/arch/sparc/prom/init.c
+++ b/arch/sparc/prom/init.c
@@ -11,12 +11,10 @@
#include <asm/openprom.h>
#include <asm/oplib.h>
-#include <asm/sun4prom.h>
struct linux_romvec *romvec;
enum prom_major_version prom_vers;
unsigned int prom_rev, prom_prev;
-linux_sun4_romvec *sun4_romvec;
/* The root node of the prom device tree. */
int prom_root_node;
@@ -34,10 +32,6 @@
void __init prom_init(struct linux_romvec *rp)
{
-#ifdef CONFIG_SUN4
- extern struct linux_romvec *sun4_prom_init(void);
- rp = sun4_prom_init();
-#endif
romvec = rp;
switch(romvec->pv_romvers) {
@@ -50,9 +44,6 @@
case 3:
prom_vers = PROM_V3;
break;
- case 40:
- prom_vers = PROM_SUN4;
- break;
default:
prom_printf("PROMLIB: Bad PROM version %d\n",
romvec->pv_romvers);
@@ -76,11 +67,8 @@
prom_ranges_init();
-#ifndef CONFIG_SUN4
- /* SUN4 prints this in sun4_prom_init */
printk("PROMLIB: Sun Boot Prom Version %d Revision %d\n",
romvec->pv_romvers, prom_rev);
-#endif
/* Initialization successful. */
return;
diff --git a/arch/sparc/prom/memory.c b/arch/sparc/prom/memory.c
index 947f047..fac7899 100644
--- a/arch/sparc/prom/memory.c
+++ b/arch/sparc/prom/memory.c
@@ -10,7 +10,6 @@
#include <linux/init.h>
#include <asm/openprom.h>
-#include <asm/sun4prom.h>
#include <asm/oplib.h>
#include <asm/page.h>
@@ -46,15 +45,6 @@
return num_ents;
}
-static int __init prom_meminit_sun4(void)
-{
-#ifdef CONFIG_SUN4
- sp_banks[0].base_addr = 0;
- sp_banks[0].num_bytes = *(sun4_romvec->memoryavail);
-#endif
- return 1;
-}
-
static int sp_banks_cmp(const void *a, const void *b)
{
const struct sparc_phys_banks *x = a, *y = b;
@@ -81,10 +71,6 @@
num_ents = prom_meminit_v2();
break;
- case PROM_SUN4:
- num_ents = prom_meminit_sun4();
- break;
-
default:
break;
}
diff --git a/arch/sparc/prom/ranges.c b/arch/sparc/prom/ranges.c
index f9b7def..64579a3 100644
--- a/arch/sparc/prom/ranges.c
+++ b/arch/sparc/prom/ranges.c
@@ -9,7 +9,6 @@
#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/types.h>
-#include <asm/sbus.h>
#include <asm/system.h>
struct linux_prom_ranges promlib_obio_ranges[PROMREG_MAX];
diff --git a/arch/sparc/prom/sun4prom.c b/arch/sparc/prom/sun4prom.c
deleted file mode 100644
index 00390a2..0000000
--- a/arch/sparc/prom/sun4prom.c
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 1996 The Australian National University.
- * Copyright (C) 1996 Fujitsu Laboratories Limited
- * Copyright (C) 1997 Michael A. Griffith (grif@acm.org)
- * Copyright (C) 1997 Sun Weenie (ko@ko.reno.nv.us)
- * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
- *
- * This software may be distributed under the terms of the Gnu
- * Public License version 2 or later
- *
- * fake a really simple Sun prom for the SUN4
- */
-
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <asm/oplib.h>
-#include <asm/idprom.h>
-#include <asm/machines.h>
-#include <asm/sun4prom.h>
-#include <asm/asi.h>
-#include <asm/contregs.h>
-#include <linux/init.h>
-
-static struct linux_romvec sun4romvec;
-static struct idprom sun4_idprom;
-
-struct property {
- char *name;
- char *value;
- int length;
-};
-
-struct node {
- int level;
- struct property *properties;
-};
-
-struct property null_properties = { NULL, NULL, -1 };
-
-struct property root_properties[] = {
- {"device_type", "cpu", 4},
- {"idprom", (char *)&sun4_idprom, sizeof(struct idprom)},
- {NULL, NULL, -1}
-};
-
-struct node nodes[] = {
- { 0, &null_properties },
- { 0, root_properties },
- { -1,&null_properties }
-};
-
-
-static int no_nextnode(int node)
-{
- if (nodes[node].level == nodes[node+1].level)
- return node+1;
- return -1;
-}
-
-static int no_child(int node)
-{
- if (nodes[node].level == nodes[node+1].level-1)
- return node+1;
- return -1;
-}
-
-static struct property *find_property(int node,char *name)
-{
- struct property *prop = &nodes[node].properties[0];
- while (prop && prop->name) {
- if (strcmp(prop->name,name) == 0) return prop;
- prop++;
- }
- return NULL;
-}
-
-static int no_proplen(int node,char *name)
-{
- struct property *prop = find_property(node,name);
- if (prop) return prop->length;
- return -1;
-}
-
-static int no_getprop(int node,char *name,char *value)
-{
- struct property *prop = find_property(node,name);
- if (prop) {
- memcpy(value,prop->value,prop->length);
- return 1;
- }
- return -1;
-}
-
-static int no_setprop(int node,char *name,char *value,int len)
-{
- return -1;
-}
-
-static char *no_nextprop(int node,char *name)
-{
- struct property *prop = find_property(node,name);
- if (prop) return prop[1].name;
- return NULL;
-}
-
-static struct linux_nodeops sun4_nodeops = {
- no_nextnode,
- no_child,
- no_proplen,
- no_getprop,
- no_setprop,
- no_nextprop
-};
-
-static int synch_hook;
-
-struct linux_romvec * __init sun4_prom_init(void)
-{
- int i;
- unsigned char x;
- char *p;
-
- p = (char *)&sun4_idprom;
- for (i = 0; i < sizeof(sun4_idprom); i++) {
- __asm__ __volatile__ ("lduba [%1] %2, %0" : "=r" (x) :
- "r" (AC_IDPROM + i), "i" (ASI_CONTROL));
- *p++ = x;
- }
-
- memset(&sun4romvec,0,sizeof(sun4romvec));
-
- sun4_romvec = (linux_sun4_romvec *) SUN4_PROM_VECTOR;
-
- sun4romvec.pv_romvers = 40;
- sun4romvec.pv_nodeops = &sun4_nodeops;
- sun4romvec.pv_reboot = sun4_romvec->reboot;
- sun4romvec.pv_abort = sun4_romvec->abortentry;
- sun4romvec.pv_halt = sun4_romvec->exittomon;
- sun4romvec.pv_synchook = (void (**)(void))&synch_hook;
- sun4romvec.pv_setctxt = sun4_romvec->setcxsegmap;
- sun4romvec.pv_v0bootargs = sun4_romvec->bootParam;
- sun4romvec.pv_nbgetchar = sun4_romvec->mayget;
- sun4romvec.pv_nbputchar = sun4_romvec->mayput;
- sun4romvec.pv_stdin = sun4_romvec->insource;
- sun4romvec.pv_stdout = sun4_romvec->outsink;
-
- /*
- * We turn on the LEDs to let folks without monitors or
- * terminals know we booted. Nothing too fancy now. They
- * are all on, except for LED 5, which blinks. When we
- * have more time, we can teach the penguin to say "By your
- * command" or "Activating turbo boost, Michael". :-)
- */
- sun4_romvec->setLEDs(NULL);
-
- printk("PROMLIB: Old Sun4 boot PROM monitor %s, romvec version %d\n",
- sun4_romvec->monid,
- sun4_romvec->romvecversion);
-
- return &sun4romvec;
-}
diff --git a/arch/sparc64/Kconfig b/arch/sparc64/Kconfig
index 36b4b7a..5446e2a 100644
--- a/arch/sparc64/Kconfig
+++ b/arch/sparc64/Kconfig
@@ -18,6 +18,13 @@
select HAVE_ARCH_KGDB
select USE_GENERIC_SMP_HELPERS if SMP
select HAVE_ARCH_TRACEHOOK
+ select ARCH_WANT_OPTIONAL_GPIOLIB
+ select RTC_CLASS
+ select RTC_DRV_M48T59
+ select RTC_DRV_CMOS
+ select RTC_DRV_BQ4802
+ select RTC_DRV_SUN4V
+ select RTC_DRV_STARFIRE
config GENERIC_TIME
bool
@@ -31,6 +38,11 @@
bool
default y
+config GENERIC_GPIO
+ bool
+ help
+ Generic GPIO API support
+
config 64BIT
def_bool y
@@ -185,6 +197,17 @@
If in doubt, say N.
+config US3_MC
+ tristate "UltraSPARC-III Memory Controller driver"
+ default y
+ help
+ This adds a driver for the UltraSPARC-III memory controller.
+ Loading this driver allows exact mnemonic strings to be
+ printed in the event of a memory error, so that the faulty DIMM
+ on the motherboard can be matched to the error.
+
+ If in doubt, say Y, as this information can be very useful.
+
# Global things across all Sun machines.
config GENERIC_LOCKBREAK
bool
diff --git a/arch/sparc64/Makefile b/arch/sparc64/Makefile
index b785a39..c7214ab 100644
--- a/arch/sparc64/Makefile
+++ b/arch/sparc64/Makefile
@@ -7,7 +7,7 @@
# Copyright (C) 1998 Jakub Jelinek (jj@ultra.linux.cz)
#
-CHECKFLAGS += -D__sparc__ -D__sparc_v9__ -m64
+CHECKFLAGS += -D__sparc__ -D__sparc_v9__ -D__arch64__ -m64
# Undefine sparc when processing vmlinux.lds - it is used
# And teach CPP we are doing 64 bit builds (for this case)
diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile
index 418b578..c0b8009 100644
--- a/arch/sparc64/kernel/Makefile
+++ b/arch/sparc64/kernel/Makefile
@@ -7,16 +7,16 @@
extra-y := head.o init_task.o vmlinux.lds
-obj-y := process.o setup.o cpu.o idprom.o \
+obj-y := process.o setup.o cpu.o idprom.o reboot.o \
traps.o auxio.o una_asm.o sysfs.o iommu.o \
irq.o ptrace.o time.o sys_sparc.o signal.o \
- unaligned.o central.o pci.o starfire.o \
- power.o sbus.o sparc64_ksyms.o chmc.o \
+ unaligned.o central.o starfire.o \
+ power.o sbus.o sparc64_ksyms.o ebus.o \
visemul.o prom.o of_device.o hvapi.o sstate.o mdesc.o
obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o
-obj-$(CONFIG_PCI) += ebus.o pci_common.o \
+obj-$(CONFIG_PCI) += pci.o pci_common.o psycho_common.o \
pci_psycho.o pci_sabre.o pci_schizo.o \
pci_sun4v.o pci_sun4v_asm.o pci_fire.o
obj-$(CONFIG_PCI_MSI) += pci_msi.o
@@ -25,6 +25,7 @@
obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_US3_FREQ) += us3_cpufreq.o
obj-$(CONFIG_US2E_FREQ) += us2e_cpufreq.o
+obj-$(CONFIG_US3_MC) += chmc.o
obj-$(CONFIG_KPROBES) += kprobes.o
obj-$(CONFIG_SUN_LDOMS) += ldc.o vio.o viohs.o ds.o
obj-$(CONFIG_AUDIT) += audit.o
diff --git a/arch/sparc64/kernel/auxio.c b/arch/sparc64/kernel/auxio.c
index dd5c7bf..858beda 100644
--- a/arch/sparc64/kernel/auxio.c
+++ b/arch/sparc64/kernel/auxio.c
@@ -109,7 +109,7 @@
}
}
-static struct of_device_id auxio_match[] = {
+static struct of_device_id __initdata auxio_match[] = {
{
.name = "auxio",
},
diff --git a/arch/sparc64/kernel/central.c b/arch/sparc64/kernel/central.c
index f2e87d0..05f1c91 100644
--- a/arch/sparc64/kernel/central.c
+++ b/arch/sparc64/kernel/central.c
@@ -1,461 +1,268 @@
/* central.c: Central FHC driver for Sunfire/Starfire/Wildfire.
*
- * Copyright (C) 1997, 1999 David S. Miller (davem@davemloft.net)
+ * Copyright (C) 1997, 1999, 2008 David S. Miller (davem@davemloft.net)
*/
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/sched.h>
-#include <linux/delay.h>
#include <linux/init.h>
-#include <linux/bootmem.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
-#include <asm/page.h>
#include <asm/fhc.h>
-#include <asm/starfire.h>
+#include <asm/upa.h>
-static struct linux_central *central_bus = NULL;
-static struct linux_fhc *fhc_list = NULL;
+struct clock_board {
+ void __iomem *clock_freq_regs;
+ void __iomem *clock_regs;
+ void __iomem *clock_ver_reg;
+ int num_slots;
+ struct resource leds_resource;
+ struct platform_device leds_pdev;
+};
-#define IS_CENTRAL_FHC(__fhc) ((__fhc) == central_bus->child)
+struct fhc {
+ void __iomem *pregs;
+ bool central;
+ bool jtag_master;
+ int board_num;
+ struct resource leds_resource;
+ struct platform_device leds_pdev;
+};
-static void central_probe_failure(int line)
+static int __devinit clock_board_calc_nslots(struct clock_board *p)
{
- prom_printf("CENTRAL: Critical device probe failure at central.c:%d\n",
- line);
- prom_halt();
-}
+ u8 reg = upa_readb(p->clock_regs + CLOCK_STAT1) & 0xc0;
-static void central_ranges_init(struct linux_central *central)
-{
- struct device_node *dp = central->prom_node;
- const void *pval;
- int len;
-
- central->num_central_ranges = 0;
- pval = of_get_property(dp, "ranges", &len);
- if (pval) {
- memcpy(central->central_ranges, pval, len);
- central->num_central_ranges =
- (len / sizeof(struct linux_prom_ranges));
- }
-}
-
-static void fhc_ranges_init(struct linux_fhc *fhc)
-{
- struct device_node *dp = fhc->prom_node;
- const void *pval;
- int len;
-
- fhc->num_fhc_ranges = 0;
- pval = of_get_property(dp, "ranges", &len);
- if (pval) {
- memcpy(fhc->fhc_ranges, pval, len);
- fhc->num_fhc_ranges =
- (len / sizeof(struct linux_prom_ranges));
- }
-}
-
-/* Range application routines are exported to various drivers,
- * so do not __init this.
- */
-static void adjust_regs(struct linux_prom_registers *regp, int nregs,
- struct linux_prom_ranges *rangep, int nranges)
-{
- int regc, rngc;
-
- for (regc = 0; regc < nregs; regc++) {
- for (rngc = 0; rngc < nranges; rngc++)
- if (regp[regc].which_io == rangep[rngc].ot_child_space)
- break; /* Fount it */
- if (rngc == nranges) /* oops */
- central_probe_failure(__LINE__);
- regp[regc].which_io = rangep[rngc].ot_parent_space;
- regp[regc].phys_addr -= rangep[rngc].ot_child_base;
- regp[regc].phys_addr += rangep[rngc].ot_parent_base;
- }
-}
-
-/* Apply probed fhc ranges to registers passed, if no ranges return. */
-static void apply_fhc_ranges(struct linux_fhc *fhc,
- struct linux_prom_registers *regs,
- int nregs)
-{
- if (fhc->num_fhc_ranges)
- adjust_regs(regs, nregs, fhc->fhc_ranges,
- fhc->num_fhc_ranges);
-}
-
-/* Apply probed central ranges to registers passed, if no ranges return. */
-static void apply_central_ranges(struct linux_central *central,
- struct linux_prom_registers *regs, int nregs)
-{
- if (central->num_central_ranges)
- adjust_regs(regs, nregs, central->central_ranges,
- central->num_central_ranges);
-}
-
-static void * __init central_alloc_bootmem(unsigned long size)
-{
- void *ret;
-
- ret = __alloc_bootmem(size, SMP_CACHE_BYTES, 0UL);
- if (ret != NULL)
- memset(ret, 0, size);
-
- return ret;
-}
-
-static unsigned long prom_reg_to_paddr(struct linux_prom_registers *r)
-{
- unsigned long ret = ((unsigned long) r->which_io) << 32;
-
- return ret | (unsigned long) r->phys_addr;
-}
-
-static void __init probe_other_fhcs(void)
-{
- struct device_node *dp;
- const struct linux_prom64_registers *fpregs;
-
- for_each_node_by_name(dp, "fhc") {
- struct linux_fhc *fhc;
- int board;
- u32 tmp;
-
- if (dp->parent &&
- dp->parent->parent != NULL)
- continue;
-
- fhc = (struct linux_fhc *)
- central_alloc_bootmem(sizeof(struct linux_fhc));
- if (fhc == NULL)
- central_probe_failure(__LINE__);
-
- /* Link it into the FHC chain. */
- fhc->next = fhc_list;
- fhc_list = fhc;
-
- /* Toplevel FHCs have no parent. */
- fhc->parent = NULL;
-
- fhc->prom_node = dp;
- fhc_ranges_init(fhc);
-
- /* Non-central FHC's have 64-bit OBP format registers. */
- fpregs = of_get_property(dp, "reg", NULL);
- if (!fpregs)
- central_probe_failure(__LINE__);
-
- /* Only central FHC needs special ranges applied. */
- fhc->fhc_regs.pregs = fpregs[0].phys_addr;
- fhc->fhc_regs.ireg = fpregs[1].phys_addr;
- fhc->fhc_regs.ffregs = fpregs[2].phys_addr;
- fhc->fhc_regs.sregs = fpregs[3].phys_addr;
- fhc->fhc_regs.uregs = fpregs[4].phys_addr;
- fhc->fhc_regs.tregs = fpregs[5].phys_addr;
-
- board = of_getintprop_default(dp, "board#", -1);
- fhc->board = board;
-
- tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_JCTRL);
- if ((tmp & FHC_JTAG_CTRL_MENAB) != 0)
- fhc->jtag_master = 1;
- else
- fhc->jtag_master = 0;
-
- tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_ID);
- printk("FHC(board %d): Version[%x] PartID[%x] Manuf[%x] %s\n",
- board,
- (tmp & FHC_ID_VERS) >> 28,
- (tmp & FHC_ID_PARTID) >> 12,
- (tmp & FHC_ID_MANUF) >> 1,
- (fhc->jtag_master ? "(JTAG Master)" : ""));
-
- /* This bit must be set in all non-central FHC's in
- * the system. When it is clear, this identifies
- * the central board.
- */
- tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL);
- tmp |= FHC_CONTROL_IXIST;
- upa_writel(tmp, fhc->fhc_regs.pregs + FHC_PREGS_CTRL);
- }
-}
-
-static void probe_clock_board(struct linux_central *central,
- struct linux_fhc *fhc,
- struct device_node *fp)
-{
- struct device_node *dp;
- struct linux_prom_registers cregs[3];
- const struct linux_prom_registers *pr;
- int nslots, tmp, nregs;
-
- dp = fp->child;
- while (dp) {
- if (!strcmp(dp->name, "clock-board"))
- break;
- dp = dp->sibling;
- }
- if (!dp)
- central_probe_failure(__LINE__);
-
- pr = of_get_property(dp, "reg", &nregs);
- if (!pr)
- central_probe_failure(__LINE__);
-
- memcpy(cregs, pr, nregs);
- nregs /= sizeof(struct linux_prom_registers);
-
- apply_fhc_ranges(fhc, &cregs[0], nregs);
- apply_central_ranges(central, &cregs[0], nregs);
- central->cfreg = prom_reg_to_paddr(&cregs[0]);
- central->clkregs = prom_reg_to_paddr(&cregs[1]);
-
- if (nregs == 2)
- central->clkver = 0UL;
- else
- central->clkver = prom_reg_to_paddr(&cregs[2]);
-
- tmp = upa_readb(central->clkregs + CLOCK_STAT1);
- tmp &= 0xc0;
- switch(tmp) {
+ switch (reg) {
case 0x40:
- nslots = 16;
- break;
+ return 16;
+
case 0xc0:
- nslots = 8;
- break;
+ return 8;
+
case 0x80:
- if (central->clkver != 0UL &&
- upa_readb(central->clkver) != 0) {
- if ((upa_readb(central->clkver) & 0x80) != 0)
- nslots = 4;
+ reg = 0;
+ if (p->clock_ver_reg)
+ reg = upa_readb(p->clock_ver_reg);
+ if (reg) {
+ if (reg & 0x80)
+ return 4;
else
- nslots = 5;
- break;
+ return 5;
}
+ /* Fallthrough */
default:
- nslots = 4;
- break;
- };
- central->slots = nslots;
- printk("CENTRAL: Detected %d slot Enterprise system. cfreg[%02x] cver[%02x]\n",
- central->slots, upa_readb(central->cfreg),
- (central->clkver ? upa_readb(central->clkver) : 0x00));
+ return 4;
+ }
}
-static void ZAP(unsigned long iclr, unsigned long imap)
+static int __devinit clock_board_probe(struct of_device *op,
+ const struct of_device_id *match)
{
- u32 imap_tmp;
+ struct clock_board *p = kzalloc(sizeof(*p), GFP_KERNEL);
+ int err = -ENOMEM;
- upa_writel(0, iclr);
- upa_readl(iclr);
- imap_tmp = upa_readl(imap);
- imap_tmp &= ~(0x80000000);
- upa_writel(imap_tmp, imap);
- upa_readl(imap);
-}
-
-static void init_all_fhc_hw(void)
-{
- struct linux_fhc *fhc;
-
- for (fhc = fhc_list; fhc != NULL; fhc = fhc->next) {
- u32 tmp;
-
- /* Clear all of the interrupt mapping registers
- * just in case OBP left them in a foul state.
- */
- ZAP(fhc->fhc_regs.ffregs + FHC_FFREGS_ICLR,
- fhc->fhc_regs.ffregs + FHC_FFREGS_IMAP);
- ZAP(fhc->fhc_regs.sregs + FHC_SREGS_ICLR,
- fhc->fhc_regs.sregs + FHC_SREGS_IMAP);
- ZAP(fhc->fhc_regs.uregs + FHC_UREGS_ICLR,
- fhc->fhc_regs.uregs + FHC_UREGS_IMAP);
- ZAP(fhc->fhc_regs.tregs + FHC_TREGS_ICLR,
- fhc->fhc_regs.tregs + FHC_TREGS_IMAP);
-
- /* Setup FHC control register. */
- tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL);
-
- /* All non-central boards have this bit set. */
- if (! IS_CENTRAL_FHC(fhc))
- tmp |= FHC_CONTROL_IXIST;
-
- /* For all FHCs, clear the firmware synchronization
- * line and both low power mode enables.
- */
- tmp &= ~(FHC_CONTROL_AOFF | FHC_CONTROL_BOFF |
- FHC_CONTROL_SLINE);
-
- upa_writel(tmp, fhc->fhc_regs.pregs + FHC_PREGS_CTRL);
- upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL);
+ if (!p) {
+ printk(KERN_ERR "clock_board: Cannot allocate struct clock_board\n");
+ goto out;
}
-}
-
-void __init central_probe(void)
-{
- struct linux_prom_registers fpregs[6];
- const struct linux_prom_registers *pr;
- struct linux_fhc *fhc;
- struct device_node *dp, *fp;
- int err;
-
- dp = of_find_node_by_name(NULL, "central");
- if (!dp) {
- if (this_is_starfire)
- starfire_cpu_setup();
- return;
+ p->clock_freq_regs = of_ioremap(&op->resource[0], 0,
+ resource_size(&op->resource[0]),
+ "clock_board_freq");
+ if (!p->clock_freq_regs) {
+ printk(KERN_ERR "clock_board: Cannot map clock_freq_regs\n");
+ goto out_free;
}
- /* Ok we got one, grab some memory for software state. */
- central_bus = (struct linux_central *)
- central_alloc_bootmem(sizeof(struct linux_central));
- if (central_bus == NULL)
- central_probe_failure(__LINE__);
-
- fhc = (struct linux_fhc *)
- central_alloc_bootmem(sizeof(struct linux_fhc));
- if (fhc == NULL)
- central_probe_failure(__LINE__);
-
- /* First init central. */
- central_bus->child = fhc;
- central_bus->prom_node = dp;
- central_ranges_init(central_bus);
-
- /* And then central's FHC. */
- fhc->next = fhc_list;
- fhc_list = fhc;
-
- fhc->parent = central_bus;
- fp = dp->child;
- while (fp) {
- if (!strcmp(fp->name, "fhc"))
- break;
- fp = fp->sibling;
+ p->clock_regs = of_ioremap(&op->resource[1], 0,
+ resource_size(&op->resource[1]),
+ "clock_board_regs");
+ if (!p->clock_regs) {
+ printk(KERN_ERR "clock_board: Cannot map clock_regs\n");
+ goto out_unmap_clock_freq_regs;
}
- if (!fp)
- central_probe_failure(__LINE__);
- fhc->prom_node = fp;
- fhc_ranges_init(fhc);
+ if (op->resource[2].flags) {
+ p->clock_ver_reg = of_ioremap(&op->resource[2], 0,
+ resource_size(&op->resource[2]),
+ "clock_ver_reg");
+ if (!p->clock_ver_reg) {
+ printk(KERN_ERR "clock_board: Cannot map clock_ver_reg\n");
+ goto out_unmap_clock_regs;
+ }
+ }
- /* Now, map in FHC register set. */
- pr = of_get_property(fp, "reg", NULL);
- if (!pr)
- central_probe_failure(__LINE__);
- memcpy(fpregs, pr, sizeof(fpregs));
+ p->num_slots = clock_board_calc_nslots(p);
- apply_central_ranges(central_bus, &fpregs[0], 6);
-
- fhc->fhc_regs.pregs = prom_reg_to_paddr(&fpregs[0]);
- fhc->fhc_regs.ireg = prom_reg_to_paddr(&fpregs[1]);
- fhc->fhc_regs.ffregs = prom_reg_to_paddr(&fpregs[2]);
- fhc->fhc_regs.sregs = prom_reg_to_paddr(&fpregs[3]);
- fhc->fhc_regs.uregs = prom_reg_to_paddr(&fpregs[4]);
- fhc->fhc_regs.tregs = prom_reg_to_paddr(&fpregs[5]);
+ p->leds_resource.start = (unsigned long)
+ (p->clock_regs + CLOCK_CTRL);
+ p->leds_resource.end = p->leds_resource.end;
+ p->leds_resource.name = "leds";
- /* Obtain board number from board status register, Central's
- * FHC lacks "board#" property.
- */
- err = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_BSR);
- fhc->board = (((err >> 16) & 0x01) |
- ((err >> 12) & 0x0e));
+ p->leds_pdev.name = "sunfire-clockboard-leds";
+ p->leds_pdev.resource = &p->leds_resource;
+ p->leds_pdev.num_resources = 1;
+ p->leds_pdev.dev.parent = &op->dev;
- fhc->jtag_master = 0;
+ err = platform_device_register(&p->leds_pdev);
+ if (err) {
+ printk(KERN_ERR "clock_board: Could not register LEDS "
+ "platform device\n");
+ goto out_unmap_clock_ver_reg;
+ }
- /* Attach the clock board registers for CENTRAL. */
- probe_clock_board(central_bus, fhc, fp);
+ printk(KERN_INFO "clock_board: Detected %d slot Enterprise system.\n",
+ p->num_slots);
- err = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_ID);
- printk("FHC(board %d): Version[%x] PartID[%x] Manuf[%x] (CENTRAL)\n",
- fhc->board,
- ((err & FHC_ID_VERS) >> 28),
- ((err & FHC_ID_PARTID) >> 12),
- ((err & FHC_ID_MANUF) >> 1));
+ err = 0;
+out:
+ return err;
- probe_other_fhcs();
+out_unmap_clock_ver_reg:
+ if (p->clock_ver_reg)
+ of_iounmap(&op->resource[2], p->clock_ver_reg,
+ resource_size(&op->resource[2]));
- init_all_fhc_hw();
+out_unmap_clock_regs:
+ of_iounmap(&op->resource[1], p->clock_regs,
+ resource_size(&op->resource[1]));
+
+out_unmap_clock_freq_regs:
+ of_iounmap(&op->resource[0], p->clock_freq_regs,
+ resource_size(&op->resource[0]));
+
+out_free:
+ kfree(p);
+ goto out;
}
-static inline void fhc_ledblink(struct linux_fhc *fhc, int on)
+static struct of_device_id __initdata clock_board_match[] = {
+ {
+ .name = "clock-board",
+ },
+ {},
+};
+
+static struct of_platform_driver clock_board_driver = {
+ .match_table = clock_board_match,
+ .probe = clock_board_probe,
+ .driver = {
+ .name = "clock_board",
+ },
+};
+
+static int __devinit fhc_probe(struct of_device *op,
+ const struct of_device_id *match)
{
- u32 tmp;
+ struct fhc *p = kzalloc(sizeof(*p), GFP_KERNEL);
+ int err = -ENOMEM;
+ u32 reg;
- tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL);
+ if (!p) {
+ printk(KERN_ERR "fhc: Cannot allocate struct fhc\n");
+ goto out;
+ }
- /* NOTE: reverse logic on this bit */
- if (on)
- tmp &= ~(FHC_CONTROL_RLED);
- else
- tmp |= FHC_CONTROL_RLED;
- tmp &= ~(FHC_CONTROL_AOFF | FHC_CONTROL_BOFF | FHC_CONTROL_SLINE);
+ if (!strcmp(op->node->parent->name, "central"))
+ p->central = true;
- upa_writel(tmp, fhc->fhc_regs.pregs + FHC_PREGS_CTRL);
- upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL);
+ p->pregs = of_ioremap(&op->resource[0], 0,
+ resource_size(&op->resource[0]),
+ "fhc_pregs");
+ if (!p->pregs) {
+ printk(KERN_ERR "fhc: Cannot map pregs\n");
+ goto out_free;
+ }
+
+ if (p->central) {
+ reg = upa_readl(p->pregs + FHC_PREGS_BSR);
+ p->board_num = ((reg >> 16) & 1) | ((reg >> 12) & 0x0e);
+ } else {
+ p->board_num = of_getintprop_default(op->node, "board#", -1);
+ if (p->board_num == -1) {
+ printk(KERN_ERR "fhc: No board# property\n");
+ goto out_unmap_pregs;
+ }
+ if (upa_readl(p->pregs + FHC_PREGS_JCTRL) & FHC_JTAG_CTRL_MENAB)
+ p->jtag_master = true;
+ }
+
+ if (!p->central) {
+ p->leds_resource.start = (unsigned long)
+ (p->pregs + FHC_PREGS_CTRL);
+ p->leds_resource.end = p->leds_resource.end;
+ p->leds_resource.name = "leds";
+
+ p->leds_pdev.name = "sunfire-fhc-leds";
+ p->leds_pdev.resource = &p->leds_resource;
+ p->leds_pdev.num_resources = 1;
+ p->leds_pdev.dev.parent = &op->dev;
+
+ err = platform_device_register(&p->leds_pdev);
+ if (err) {
+ printk(KERN_ERR "fhc: Could not register LEDS "
+ "platform device\n");
+ goto out_unmap_pregs;
+ }
+ }
+ reg = upa_readl(p->pregs + FHC_PREGS_CTRL);
+
+ if (!p->central)
+ reg |= FHC_CONTROL_IXIST;
+
+ reg &= ~(FHC_CONTROL_AOFF |
+ FHC_CONTROL_BOFF |
+ FHC_CONTROL_SLINE);
+
+ upa_writel(reg, p->pregs + FHC_PREGS_CTRL);
+ upa_readl(p->pregs + FHC_PREGS_CTRL);
+
+ reg = upa_readl(p->pregs + FHC_PREGS_ID);
+ printk(KERN_INFO "fhc: Board #%d, Version[%x] PartID[%x] Manuf[%x] %s\n",
+ p->board_num,
+ (reg & FHC_ID_VERS) >> 28,
+ (reg & FHC_ID_PARTID) >> 12,
+ (reg & FHC_ID_MANUF) >> 1,
+ (p->jtag_master ?
+ "(JTAG Master)" :
+ (p->central ? "(Central)" : "")));
+
+ err = 0;
+
+out:
+ return err;
+
+out_unmap_pregs:
+ of_iounmap(&op->resource[0], p->pregs, resource_size(&op->resource[0]));
+
+out_free:
+ kfree(p);
+ goto out;
}
-static inline void central_ledblink(struct linux_central *central, int on)
+static struct of_device_id __initdata fhc_match[] = {
+ {
+ .name = "fhc",
+ },
+ {},
+};
+
+static struct of_platform_driver fhc_driver = {
+ .match_table = fhc_match,
+ .probe = fhc_probe,
+ .driver = {
+ .name = "fhc",
+ },
+};
+
+static int __init sunfire_init(void)
{
- u8 tmp;
-
- tmp = upa_readb(central->clkregs + CLOCK_CTRL);
-
- /* NOTE: reverse logic on this bit */
- if (on)
- tmp &= ~(CLOCK_CTRL_RLED);
- else
- tmp |= CLOCK_CTRL_RLED;
-
- upa_writeb(tmp, central->clkregs + CLOCK_CTRL);
- upa_readb(central->clkregs + CLOCK_CTRL);
+ (void) of_register_driver(&fhc_driver, &of_platform_bus_type);
+ (void) of_register_driver(&clock_board_driver, &of_platform_bus_type);
+ return 0;
}
-static struct timer_list sftimer;
-static int led_state;
-
-static void sunfire_timer(unsigned long __ignored)
-{
- struct linux_fhc *fhc;
-
- central_ledblink(central_bus, led_state);
- for (fhc = fhc_list; fhc != NULL; fhc = fhc->next)
- if (! IS_CENTRAL_FHC(fhc))
- fhc_ledblink(fhc, led_state);
- led_state = ! led_state;
- sftimer.expires = jiffies + (HZ >> 1);
- add_timer(&sftimer);
-}
-
-/* After PCI/SBUS busses have been probed, this is called to perform
- * final initialization of all FireHose Controllers in the system.
- */
-void firetruck_init(void)
-{
- struct linux_central *central = central_bus;
- u8 ctrl;
-
- /* No central bus, nothing to do. */
- if (central == NULL)
- return;
-
- /* OBP leaves it on, turn it off so clock board timer LED
- * is in sync with FHC ones.
- */
- ctrl = upa_readb(central->clkregs + CLOCK_CTRL);
- ctrl &= ~(CLOCK_CTRL_RLED);
- upa_writeb(ctrl, central->clkregs + CLOCK_CTRL);
-
- led_state = 0;
- init_timer(&sftimer);
- sftimer.data = 0;
- sftimer.function = &sunfire_timer;
- sftimer.expires = jiffies + (HZ >> 1);
- add_timer(&sftimer);
-}
+subsys_initcall(sunfire_init);
diff --git a/arch/sparc64/kernel/chmc.c b/arch/sparc64/kernel/chmc.c
index 6d4f02e..3b9f4d6 100644
--- a/arch/sparc64/kernel/chmc.c
+++ b/arch/sparc64/kernel/chmc.c
@@ -1,6 +1,6 @@
-/* memctrlr.c: Driver for UltraSPARC-III memory controller.
+/* chmc.c: Driver for UltraSPARC-III memory controller.
*
- * Copyright (C) 2001, 2007 David S. Miller (davem@davemloft.net)
+ * Copyright (C) 2001, 2007, 2008 David S. Miller (davem@davemloft.net)
*/
#include <linux/module.h>
@@ -13,45 +13,64 @@
#include <linux/smp.h>
#include <linux/errno.h>
#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <asm/spitfire.h>
#include <asm/chmctrl.h>
#include <asm/cpudata.h>
#include <asm/oplib.h>
#include <asm/prom.h>
+#include <asm/head.h>
#include <asm/io.h>
+#include <asm/memctrl.h>
+
+#define DRV_MODULE_NAME "chmc"
+#define PFX DRV_MODULE_NAME ": "
+#define DRV_MODULE_VERSION "0.2"
+
+MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
+MODULE_DESCRIPTION("UltraSPARC-III memory controller driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_MODULE_VERSION);
+
+static int mc_type;
+#define MC_TYPE_SAFARI 1
+#define MC_TYPE_JBUS 2
+
+static dimm_printer_t us3mc_dimm_printer;
#define CHMCTRL_NDGRPS 2
#define CHMCTRL_NDIMMS 4
-#define DIMMS_PER_MC (CHMCTRL_NDGRPS * CHMCTRL_NDIMMS)
+#define CHMC_DIMMS_PER_MC (CHMCTRL_NDGRPS * CHMCTRL_NDIMMS)
/* OBP memory-layout property format. */
-struct obp_map {
+struct chmc_obp_map {
unsigned char dimm_map[144];
unsigned char pin_map[576];
};
#define DIMM_LABEL_SZ 8
-struct obp_mem_layout {
+struct chmc_obp_mem_layout {
/* One max 8-byte string label per DIMM. Usually
* this matches the label on the motherboard where
* that DIMM resides.
*/
- char dimm_labels[DIMMS_PER_MC][DIMM_LABEL_SZ];
+ char dimm_labels[CHMC_DIMMS_PER_MC][DIMM_LABEL_SZ];
/* If symmetric use map[0], else it is
* asymmetric and map[1] should be used.
*/
- char symmetric;
+ char symmetric;
- struct obp_map map[2];
+ struct chmc_obp_map map[2];
};
#define CHMCTRL_NBANKS 4
-struct bank_info {
- struct mctrl_info *mp;
+struct chmc_bank_info {
+ struct chmc *p;
int bank_id;
u64 raw_reg;
@@ -65,28 +84,406 @@
unsigned long size;
};
-struct mctrl_info {
- struct list_head list;
- int portid;
+struct chmc {
+ struct list_head list;
+ int portid;
- struct obp_mem_layout layout_prop;
- int layout_size;
+ struct chmc_obp_mem_layout layout_prop;
+ int layout_size;
- void __iomem *regs;
+ void __iomem *regs;
- u64 timing_control1;
- u64 timing_control2;
- u64 timing_control3;
- u64 timing_control4;
- u64 memaddr_control;
+ u64 timing_control1;
+ u64 timing_control2;
+ u64 timing_control3;
+ u64 timing_control4;
+ u64 memaddr_control;
- struct bank_info logical_banks[CHMCTRL_NBANKS];
+ struct chmc_bank_info logical_banks[CHMCTRL_NBANKS];
};
+#define JBUSMC_REGS_SIZE 8
+
+#define JB_MC_REG1_DIMM2_BANK3 0x8000000000000000UL
+#define JB_MC_REG1_DIMM1_BANK1 0x4000000000000000UL
+#define JB_MC_REG1_DIMM2_BANK2 0x2000000000000000UL
+#define JB_MC_REG1_DIMM1_BANK0 0x1000000000000000UL
+#define JB_MC_REG1_XOR 0x0000010000000000UL
+#define JB_MC_REG1_ADDR_GEN_2 0x000000e000000000UL
+#define JB_MC_REG1_ADDR_GEN_2_SHIFT 37
+#define JB_MC_REG1_ADDR_GEN_1 0x0000001c00000000UL
+#define JB_MC_REG1_ADDR_GEN_1_SHIFT 34
+#define JB_MC_REG1_INTERLEAVE 0x0000000001800000UL
+#define JB_MC_REG1_INTERLEAVE_SHIFT 23
+#define JB_MC_REG1_DIMM2_PTYPE 0x0000000000200000UL
+#define JB_MC_REG1_DIMM2_PTYPE_SHIFT 21
+#define JB_MC_REG1_DIMM1_PTYPE 0x0000000000100000UL
+#define JB_MC_REG1_DIMM1_PTYPE_SHIFT 20
+
+#define PART_TYPE_X8 0
+#define PART_TYPE_X4 1
+
+#define INTERLEAVE_NONE 0
+#define INTERLEAVE_SAME 1
+#define INTERLEAVE_INTERNAL 2
+#define INTERLEAVE_BOTH 3
+
+#define ADDR_GEN_128MB 0
+#define ADDR_GEN_256MB 1
+#define ADDR_GEN_512MB 2
+#define ADDR_GEN_1GB 3
+
+#define JB_NUM_DIMM_GROUPS 2
+#define JB_NUM_DIMMS_PER_GROUP 2
+#define JB_NUM_DIMMS (JB_NUM_DIMM_GROUPS * JB_NUM_DIMMS_PER_GROUP)
+
+struct jbusmc_obp_map {
+ unsigned char dimm_map[18];
+ unsigned char pin_map[144];
+};
+
+struct jbusmc_obp_mem_layout {
+ /* One max 8-byte string label per DIMM. Usually
+ * this matches the label on the motherboard where
+ * that DIMM resides.
+ */
+ char dimm_labels[JB_NUM_DIMMS][DIMM_LABEL_SZ];
+
+ /* If symmetric use map[0], else it is
+ * asymmetric and map[1] should be used.
+ */
+ char symmetric;
+
+ struct jbusmc_obp_map map;
+
+ char _pad;
+};
+
+struct jbusmc_dimm_group {
+ struct jbusmc *controller;
+ int index;
+ u64 base_addr;
+ u64 size;
+};
+
+struct jbusmc {
+ void __iomem *regs;
+ u64 mc_reg_1;
+ u32 portid;
+ struct jbusmc_obp_mem_layout layout;
+ int layout_len;
+ int num_dimm_groups;
+ struct jbusmc_dimm_group dimm_groups[JB_NUM_DIMM_GROUPS];
+ struct list_head list;
+};
+
+static DEFINE_SPINLOCK(mctrl_list_lock);
static LIST_HEAD(mctrl_list);
+static void mc_list_add(struct list_head *list)
+{
+ spin_lock(&mctrl_list_lock);
+ list_add(list, &mctrl_list);
+ spin_unlock(&mctrl_list_lock);
+}
+
+static void mc_list_del(struct list_head *list)
+{
+ spin_lock(&mctrl_list_lock);
+ list_del_init(list);
+ spin_unlock(&mctrl_list_lock);
+}
+
+#define SYNDROME_MIN -1
+#define SYNDROME_MAX 144
+
+/* Covert syndrome code into the way the bits are positioned
+ * on the bus.
+ */
+static int syndrome_to_qword_code(int syndrome_code)
+{
+ if (syndrome_code < 128)
+ syndrome_code += 16;
+ else if (syndrome_code < 128 + 9)
+ syndrome_code -= (128 - 7);
+ else if (syndrome_code < (128 + 9 + 3))
+ syndrome_code -= (128 + 9 - 4);
+ else
+ syndrome_code -= (128 + 9 + 3);
+ return syndrome_code;
+}
+
+/* All this magic has to do with how a cache line comes over the wire
+ * on Safari and JBUS. A 64-bit line comes over in 1 or more quadword
+ * cycles, each of which transmit ECC/MTAG info as well as the actual
+ * data.
+ */
+#define L2_LINE_SIZE 64
+#define L2_LINE_ADDR_MSK (L2_LINE_SIZE - 1)
+#define QW_PER_LINE 4
+#define QW_BYTES (L2_LINE_SIZE / QW_PER_LINE)
+#define QW_BITS 144
+#define SAFARI_LAST_BIT (576 - 1)
+#define JBUS_LAST_BIT (144 - 1)
+
+static void get_pin_and_dimm_str(int syndrome_code, unsigned long paddr,
+ int *pin_p, char **dimm_str_p, void *_prop,
+ int base_dimm_offset)
+{
+ int qword_code = syndrome_to_qword_code(syndrome_code);
+ int cache_line_offset;
+ int offset_inverse;
+ int dimm_map_index;
+ int map_val;
+
+ if (mc_type == MC_TYPE_JBUS) {
+ struct jbusmc_obp_mem_layout *p = _prop;
+
+ /* JBUS */
+ cache_line_offset = qword_code;
+ offset_inverse = (JBUS_LAST_BIT - cache_line_offset);
+ dimm_map_index = offset_inverse / 8;
+ map_val = p->map.dimm_map[dimm_map_index];
+ map_val = ((map_val >> ((7 - (offset_inverse & 7)))) & 1);
+ *dimm_str_p = p->dimm_labels[base_dimm_offset + map_val];
+ *pin_p = p->map.pin_map[cache_line_offset];
+ } else {
+ struct chmc_obp_mem_layout *p = _prop;
+ struct chmc_obp_map *mp;
+ int qword;
+
+ /* Safari */
+ if (p->symmetric)
+ mp = &p->map[0];
+ else
+ mp = &p->map[1];
+
+ qword = (paddr & L2_LINE_ADDR_MSK) / QW_BYTES;
+ cache_line_offset = ((3 - qword) * QW_BITS) + qword_code;
+ offset_inverse = (SAFARI_LAST_BIT - cache_line_offset);
+ dimm_map_index = offset_inverse >> 2;
+ map_val = mp->dimm_map[dimm_map_index];
+ map_val = ((map_val >> ((3 - (offset_inverse & 3)) << 1)) & 0x3);
+ *dimm_str_p = p->dimm_labels[base_dimm_offset + map_val];
+ *pin_p = mp->pin_map[cache_line_offset];
+ }
+}
+
+static struct jbusmc_dimm_group *jbusmc_find_dimm_group(unsigned long phys_addr)
+{
+ struct jbusmc *p;
+
+ list_for_each_entry(p, &mctrl_list, list) {
+ int i;
+
+ for (i = 0; i < p->num_dimm_groups; i++) {
+ struct jbusmc_dimm_group *dp = &p->dimm_groups[i];
+
+ if (phys_addr < dp->base_addr ||
+ (dp->base_addr + dp->size) <= phys_addr)
+ continue;
+
+ return dp;
+ }
+ }
+ return NULL;
+}
+
+static int jbusmc_print_dimm(int syndrome_code,
+ unsigned long phys_addr,
+ char *buf, int buflen)
+{
+ struct jbusmc_obp_mem_layout *prop;
+ struct jbusmc_dimm_group *dp;
+ struct jbusmc *p;
+ int first_dimm;
+
+ dp = jbusmc_find_dimm_group(phys_addr);
+ if (dp == NULL ||
+ syndrome_code < SYNDROME_MIN ||
+ syndrome_code > SYNDROME_MAX) {
+ buf[0] = '?';
+ buf[1] = '?';
+ buf[2] = '?';
+ buf[3] = '\0';
+ }
+ p = dp->controller;
+ prop = &p->layout;
+
+ first_dimm = dp->index * JB_NUM_DIMMS_PER_GROUP;
+
+ if (syndrome_code != SYNDROME_MIN) {
+ char *dimm_str;
+ int pin;
+
+ get_pin_and_dimm_str(syndrome_code, phys_addr, &pin,
+ &dimm_str, prop, first_dimm);
+ sprintf(buf, "%s, pin %3d", dimm_str, pin);
+ } else {
+ int dimm;
+
+ /* Multi-bit error, we just dump out all the
+ * dimm labels associated with this dimm group.
+ */
+ for (dimm = 0; dimm < JB_NUM_DIMMS_PER_GROUP; dimm++) {
+ sprintf(buf, "%s ",
+ prop->dimm_labels[first_dimm + dimm]);
+ buf += strlen(buf);
+ }
+ }
+
+ return 0;
+}
+
+static u64 __devinit jbusmc_dimm_group_size(u64 base,
+ const struct linux_prom64_registers *mem_regs,
+ int num_mem_regs)
+{
+ u64 max = base + (8UL * 1024 * 1024 * 1024);
+ u64 max_seen = base;
+ int i;
+
+ for (i = 0; i < num_mem_regs; i++) {
+ const struct linux_prom64_registers *ent;
+ u64 this_base;
+ u64 this_end;
+
+ ent = &mem_regs[i];
+ this_base = ent->phys_addr;
+ this_end = this_base + ent->reg_size;
+ if (base < this_base || base >= this_end)
+ continue;
+ if (this_end > max)
+ this_end = max;
+ if (this_end > max_seen)
+ max_seen = this_end;
+ }
+
+ return max_seen - base;
+}
+
+static void __devinit jbusmc_construct_one_dimm_group(struct jbusmc *p,
+ unsigned long index,
+ const struct linux_prom64_registers *mem_regs,
+ int num_mem_regs)
+{
+ struct jbusmc_dimm_group *dp = &p->dimm_groups[index];
+
+ dp->controller = p;
+ dp->index = index;
+
+ dp->base_addr = (p->portid * (64UL * 1024 * 1024 * 1024));
+ dp->base_addr += (index * (8UL * 1024 * 1024 * 1024));
+ dp->size = jbusmc_dimm_group_size(dp->base_addr, mem_regs, num_mem_regs);
+}
+
+static void __devinit jbusmc_construct_dimm_groups(struct jbusmc *p,
+ const struct linux_prom64_registers *mem_regs,
+ int num_mem_regs)
+{
+ if (p->mc_reg_1 & JB_MC_REG1_DIMM1_BANK0) {
+ jbusmc_construct_one_dimm_group(p, 0, mem_regs, num_mem_regs);
+ p->num_dimm_groups++;
+ }
+ if (p->mc_reg_1 & JB_MC_REG1_DIMM2_BANK2) {
+ jbusmc_construct_one_dimm_group(p, 1, mem_regs, num_mem_regs);
+ p->num_dimm_groups++;
+ }
+}
+
+static int __devinit jbusmc_probe(struct of_device *op,
+ const struct of_device_id *match)
+{
+ const struct linux_prom64_registers *mem_regs;
+ struct device_node *mem_node;
+ int err, len, num_mem_regs;
+ struct jbusmc *p;
+ const u32 *prop;
+ const void *ml;
+
+ err = -ENODEV;
+ mem_node = of_find_node_by_path("/memory");
+ if (!mem_node) {
+ printk(KERN_ERR PFX "Cannot find /memory node.\n");
+ goto out;
+ }
+ mem_regs = of_get_property(mem_node, "reg", &len);
+ if (!mem_regs) {
+ printk(KERN_ERR PFX "Cannot get reg property of /memory node.\n");
+ goto out;
+ }
+ num_mem_regs = len / sizeof(*mem_regs);
+
+ err = -ENOMEM;
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p) {
+ printk(KERN_ERR PFX "Cannot allocate struct jbusmc.\n");
+ goto out;
+ }
+
+ INIT_LIST_HEAD(&p->list);
+
+ err = -ENODEV;
+ prop = of_get_property(op->node, "portid", &len);
+ if (!prop || len != 4) {
+ printk(KERN_ERR PFX "Cannot find portid.\n");
+ goto out_free;
+ }
+
+ p->portid = *prop;
+
+ prop = of_get_property(op->node, "memory-control-register-1", &len);
+ if (!prop || len != 8) {
+ printk(KERN_ERR PFX "Cannot get memory control register 1.\n");
+ goto out_free;
+ }
+
+ p->mc_reg_1 = ((u64)prop[0] << 32) | (u64) prop[1];
+
+ err = -ENOMEM;
+ p->regs = of_ioremap(&op->resource[0], 0, JBUSMC_REGS_SIZE, "jbusmc");
+ if (!p->regs) {
+ printk(KERN_ERR PFX "Cannot map jbusmc regs.\n");
+ goto out_free;
+ }
+
+ err = -ENODEV;
+ ml = of_get_property(op->node, "memory-layout", &p->layout_len);
+ if (!ml) {
+ printk(KERN_ERR PFX "Cannot get memory layout property.\n");
+ goto out_iounmap;
+ }
+ if (p->layout_len > sizeof(p->layout)) {
+ printk(KERN_ERR PFX "Unexpected memory-layout size %d\n",
+ p->layout_len);
+ goto out_iounmap;
+ }
+ memcpy(&p->layout, ml, p->layout_len);
+
+ jbusmc_construct_dimm_groups(p, mem_regs, num_mem_regs);
+
+ mc_list_add(&p->list);
+
+ printk(KERN_INFO PFX "UltraSPARC-IIIi memory controller at %s\n",
+ op->node->full_name);
+
+ dev_set_drvdata(&op->dev, p);
+
+ err = 0;
+
+out:
+ return err;
+
+out_iounmap:
+ of_iounmap(&op->resource[0], p->regs, JBUSMC_REGS_SIZE);
+
+out_free:
+ kfree(p);
+ goto out;
+}
+
/* Does BANK decode PHYS_ADDR? */
-static int bank_match(struct bank_info *bp, unsigned long phys_addr)
+static int chmc_bank_match(struct chmc_bank_info *bp, unsigned long phys_addr)
{
unsigned long upper_bits = (phys_addr & PA_UPPER_BITS) >> PA_UPPER_BITS_SHIFT;
unsigned long lower_bits = (phys_addr & PA_LOWER_BITS) >> PA_LOWER_BITS_SHIFT;
@@ -118,25 +515,18 @@
}
/* Given PHYS_ADDR, search memory controller banks for a match. */
-static struct bank_info *find_bank(unsigned long phys_addr)
+static struct chmc_bank_info *chmc_find_bank(unsigned long phys_addr)
{
- struct list_head *mctrl_head = &mctrl_list;
- struct list_head *mctrl_entry = mctrl_head->next;
+ struct chmc *p;
- for (;;) {
- struct mctrl_info *mp =
- list_entry(mctrl_entry, struct mctrl_info, list);
+ list_for_each_entry(p, &mctrl_list, list) {
int bank_no;
- if (mctrl_entry == mctrl_head)
- break;
- mctrl_entry = mctrl_entry->next;
-
for (bank_no = 0; bank_no < CHMCTRL_NBANKS; bank_no++) {
- struct bank_info *bp;
+ struct chmc_bank_info *bp;
- bp = &mp->logical_banks[bank_no];
- if (bank_match(bp, phys_addr))
+ bp = &p->logical_banks[bank_no];
+ if (chmc_bank_match(bp, phys_addr))
return bp;
}
}
@@ -145,17 +535,15 @@
}
/* This is the main purpose of this driver. */
-#define SYNDROME_MIN -1
-#define SYNDROME_MAX 144
-int chmc_getunumber(int syndrome_code,
- unsigned long phys_addr,
- char *buf, int buflen)
+static int chmc_print_dimm(int syndrome_code,
+ unsigned long phys_addr,
+ char *buf, int buflen)
{
- struct bank_info *bp;
- struct obp_mem_layout *prop;
+ struct chmc_bank_info *bp;
+ struct chmc_obp_mem_layout *prop;
int bank_in_controller, first_dimm;
- bp = find_bank(phys_addr);
+ bp = chmc_find_bank(phys_addr);
if (bp == NULL ||
syndrome_code < SYNDROME_MIN ||
syndrome_code > SYNDROME_MAX) {
@@ -166,60 +554,18 @@
return 0;
}
- prop = &bp->mp->layout_prop;
+ prop = &bp->p->layout_prop;
bank_in_controller = bp->bank_id & (CHMCTRL_NBANKS - 1);
first_dimm = (bank_in_controller & (CHMCTRL_NDGRPS - 1));
first_dimm *= CHMCTRL_NDIMMS;
if (syndrome_code != SYNDROME_MIN) {
- struct obp_map *map;
- int qword, where_in_line, where, map_index, map_offset;
- unsigned int map_val;
+ char *dimm_str;
+ int pin;
- /* Yaay, single bit error so we can figure out
- * the exact dimm.
- */
- if (prop->symmetric)
- map = &prop->map[0];
- else
- map = &prop->map[1];
-
- /* Covert syndrome code into the way the bits are
- * positioned on the bus.
- */
- if (syndrome_code < 144 - 16)
- syndrome_code += 16;
- else if (syndrome_code < 144)
- syndrome_code -= (144 - 7);
- else if (syndrome_code < (144 + 3))
- syndrome_code -= (144 + 3 - 4);
- else
- syndrome_code -= 144 + 3;
-
- /* All this magic has to do with how a cache line
- * comes over the wire on Safari. A 64-bit line
- * comes over in 4 quadword cycles, each of which
- * transmit ECC/MTAG info as well as the actual
- * data. 144 bits per quadword, 576 total.
- */
-#define LINE_SIZE 64
-#define LINE_ADDR_MSK (LINE_SIZE - 1)
-#define QW_PER_LINE 4
-#define QW_BYTES (LINE_SIZE / QW_PER_LINE)
-#define QW_BITS 144
-#define LAST_BIT (576 - 1)
-
- qword = (phys_addr & LINE_ADDR_MSK) / QW_BYTES;
- where_in_line = ((3 - qword) * QW_BITS) + syndrome_code;
- where = (LAST_BIT - where_in_line);
- map_index = where >> 2;
- map_offset = where & 0x3;
- map_val = map->dimm_map[map_index];
- map_val = ((map_val >> ((3 - map_offset) << 1)) & (2 - 1));
-
- sprintf(buf, "%s, pin %3d",
- prop->dimm_labels[first_dimm + map_val],
- map->pin_map[where_in_line]);
+ get_pin_and_dimm_str(syndrome_code, phys_addr, &pin,
+ &dimm_str, prop, first_dimm);
+ sprintf(buf, "%s, pin %3d", dimm_str, pin);
} else {
int dimm;
@@ -240,7 +586,7 @@
* the code is executing, you must use special ASI load/store else
* you go through the global mapping.
*/
-static u64 read_mcreg(struct mctrl_info *mp, unsigned long offset)
+static u64 chmc_read_mcreg(struct chmc *p, unsigned long offset)
{
unsigned long ret, this_cpu;
@@ -248,14 +594,14 @@
this_cpu = real_hard_smp_processor_id();
- if (mp->portid == this_cpu) {
+ if (p->portid == this_cpu) {
__asm__ __volatile__("ldxa [%1] %2, %0"
: "=r" (ret)
: "r" (offset), "i" (ASI_MCU_CTRL_REG));
} else {
__asm__ __volatile__("ldxa [%1] %2, %0"
: "=r" (ret)
- : "r" (mp->regs + offset),
+ : "r" (p->regs + offset),
"i" (ASI_PHYS_BYPASS_EC_E));
}
@@ -265,178 +611,253 @@
}
#if 0 /* currently unused */
-static void write_mcreg(struct mctrl_info *mp, unsigned long offset, u64 val)
+static void chmc_write_mcreg(struct chmc *p, unsigned long offset, u64 val)
{
- if (mp->portid == smp_processor_id()) {
+ if (p->portid == smp_processor_id()) {
__asm__ __volatile__("stxa %0, [%1] %2"
: : "r" (val),
"r" (offset), "i" (ASI_MCU_CTRL_REG));
} else {
__asm__ __volatile__("ldxa %0, [%1] %2"
: : "r" (val),
- "r" (mp->regs + offset),
+ "r" (p->regs + offset),
"i" (ASI_PHYS_BYPASS_EC_E));
}
}
#endif
-static void interpret_one_decode_reg(struct mctrl_info *mp, int which_bank, u64 val)
+static void chmc_interpret_one_decode_reg(struct chmc *p, int which_bank, u64 val)
{
- struct bank_info *p = &mp->logical_banks[which_bank];
+ struct chmc_bank_info *bp = &p->logical_banks[which_bank];
- p->mp = mp;
- p->bank_id = (CHMCTRL_NBANKS * mp->portid) + which_bank;
- p->raw_reg = val;
- p->valid = (val & MEM_DECODE_VALID) >> MEM_DECODE_VALID_SHIFT;
- p->uk = (val & MEM_DECODE_UK) >> MEM_DECODE_UK_SHIFT;
- p->um = (val & MEM_DECODE_UM) >> MEM_DECODE_UM_SHIFT;
- p->lk = (val & MEM_DECODE_LK) >> MEM_DECODE_LK_SHIFT;
- p->lm = (val & MEM_DECODE_LM) >> MEM_DECODE_LM_SHIFT;
+ bp->p = p;
+ bp->bank_id = (CHMCTRL_NBANKS * p->portid) + which_bank;
+ bp->raw_reg = val;
+ bp->valid = (val & MEM_DECODE_VALID) >> MEM_DECODE_VALID_SHIFT;
+ bp->uk = (val & MEM_DECODE_UK) >> MEM_DECODE_UK_SHIFT;
+ bp->um = (val & MEM_DECODE_UM) >> MEM_DECODE_UM_SHIFT;
+ bp->lk = (val & MEM_DECODE_LK) >> MEM_DECODE_LK_SHIFT;
+ bp->lm = (val & MEM_DECODE_LM) >> MEM_DECODE_LM_SHIFT;
- p->base = (p->um);
- p->base &= ~(p->uk);
- p->base <<= PA_UPPER_BITS_SHIFT;
+ bp->base = (bp->um);
+ bp->base &= ~(bp->uk);
+ bp->base <<= PA_UPPER_BITS_SHIFT;
- switch(p->lk) {
+ switch(bp->lk) {
case 0xf:
default:
- p->interleave = 1;
+ bp->interleave = 1;
break;
case 0xe:
- p->interleave = 2;
+ bp->interleave = 2;
break;
case 0xc:
- p->interleave = 4;
+ bp->interleave = 4;
break;
case 0x8:
- p->interleave = 8;
+ bp->interleave = 8;
break;
case 0x0:
- p->interleave = 16;
+ bp->interleave = 16;
break;
};
/* UK[10] is reserved, and UK[11] is not set for the SDRAM
* bank size definition.
*/
- p->size = (((unsigned long)p->uk &
- ((1UL << 10UL) - 1UL)) + 1UL) << PA_UPPER_BITS_SHIFT;
- p->size /= p->interleave;
+ bp->size = (((unsigned long)bp->uk &
+ ((1UL << 10UL) - 1UL)) + 1UL) << PA_UPPER_BITS_SHIFT;
+ bp->size /= bp->interleave;
}
-static void fetch_decode_regs(struct mctrl_info *mp)
+static void chmc_fetch_decode_regs(struct chmc *p)
{
- if (mp->layout_size == 0)
+ if (p->layout_size == 0)
return;
- interpret_one_decode_reg(mp, 0,
- read_mcreg(mp, CHMCTRL_DECODE1));
- interpret_one_decode_reg(mp, 1,
- read_mcreg(mp, CHMCTRL_DECODE2));
- interpret_one_decode_reg(mp, 2,
- read_mcreg(mp, CHMCTRL_DECODE3));
- interpret_one_decode_reg(mp, 3,
- read_mcreg(mp, CHMCTRL_DECODE4));
+ chmc_interpret_one_decode_reg(p, 0,
+ chmc_read_mcreg(p, CHMCTRL_DECODE1));
+ chmc_interpret_one_decode_reg(p, 1,
+ chmc_read_mcreg(p, CHMCTRL_DECODE2));
+ chmc_interpret_one_decode_reg(p, 2,
+ chmc_read_mcreg(p, CHMCTRL_DECODE3));
+ chmc_interpret_one_decode_reg(p, 3,
+ chmc_read_mcreg(p, CHMCTRL_DECODE4));
}
-static int init_one_mctrl(struct device_node *dp)
+static int __devinit chmc_probe(struct of_device *op,
+ const struct of_device_id *match)
{
- struct mctrl_info *mp = kzalloc(sizeof(*mp), GFP_KERNEL);
- int portid = of_getintprop_default(dp, "portid", -1);
- const struct linux_prom64_registers *regs;
+ struct device_node *dp = op->node;
+ unsigned long ver;
const void *pval;
- int len;
+ int len, portid;
+ struct chmc *p;
+ int err;
- if (!mp)
- return -1;
+ err = -ENODEV;
+ __asm__ ("rdpr %%ver, %0" : "=r" (ver));
+ if ((ver >> 32UL) == __JALAPENO_ID ||
+ (ver >> 32UL) == __SERRANO_ID)
+ goto out;
+
+ portid = of_getintprop_default(dp, "portid", -1);
if (portid == -1)
- goto fail;
+ goto out;
- mp->portid = portid;
pval = of_get_property(dp, "memory-layout", &len);
- mp->layout_size = len;
+ if (pval && len > sizeof(p->layout_prop)) {
+ printk(KERN_ERR PFX "Unexpected memory-layout property "
+ "size %d.\n", len);
+ goto out;
+ }
+
+ err = -ENOMEM;
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p) {
+ printk(KERN_ERR PFX "Could not allocate struct chmc.\n");
+ goto out;
+ }
+
+ p->portid = portid;
+ p->layout_size = len;
if (!pval)
- mp->layout_size = 0;
- else {
- if (mp->layout_size > sizeof(mp->layout_prop))
- goto fail;
- memcpy(&mp->layout_prop, pval, len);
+ p->layout_size = 0;
+ else
+ memcpy(&p->layout_prop, pval, len);
+
+ p->regs = of_ioremap(&op->resource[0], 0, 0x48, "chmc");
+ if (!p->regs) {
+ printk(KERN_ERR PFX "Could not map registers.\n");
+ goto out_free;
}
- regs = of_get_property(dp, "reg", NULL);
- if (!regs || regs->reg_size != 0x48)
- goto fail;
-
- mp->regs = ioremap(regs->phys_addr, regs->reg_size);
- if (mp->regs == NULL)
- goto fail;
-
- if (mp->layout_size != 0UL) {
- mp->timing_control1 = read_mcreg(mp, CHMCTRL_TCTRL1);
- mp->timing_control2 = read_mcreg(mp, CHMCTRL_TCTRL2);
- mp->timing_control3 = read_mcreg(mp, CHMCTRL_TCTRL3);
- mp->timing_control4 = read_mcreg(mp, CHMCTRL_TCTRL4);
- mp->memaddr_control = read_mcreg(mp, CHMCTRL_MACTRL);
+ if (p->layout_size != 0UL) {
+ p->timing_control1 = chmc_read_mcreg(p, CHMCTRL_TCTRL1);
+ p->timing_control2 = chmc_read_mcreg(p, CHMCTRL_TCTRL2);
+ p->timing_control3 = chmc_read_mcreg(p, CHMCTRL_TCTRL3);
+ p->timing_control4 = chmc_read_mcreg(p, CHMCTRL_TCTRL4);
+ p->memaddr_control = chmc_read_mcreg(p, CHMCTRL_MACTRL);
}
- fetch_decode_regs(mp);
+ chmc_fetch_decode_regs(p);
- list_add(&mp->list, &mctrl_list);
+ mc_list_add(&p->list);
- /* Report the device. */
- printk(KERN_INFO "%s: US3 memory controller at %p [%s]\n",
+ printk(KERN_INFO PFX "UltraSPARC-III memory controller at %s [%s]\n",
dp->full_name,
- mp->regs, (mp->layout_size ? "ACTIVE" : "INACTIVE"));
+ (p->layout_size ? "ACTIVE" : "INACTIVE"));
- return 0;
+ dev_set_drvdata(&op->dev, p);
-fail:
- if (mp) {
- if (mp->regs != NULL)
- iounmap(mp->regs);
- kfree(mp);
- }
- return -1;
+ err = 0;
+
+out:
+ return err;
+
+out_free:
+ kfree(p);
+ goto out;
}
-static int __init chmc_init(void)
+static int __devinit us3mc_probe(struct of_device *op,
+ const struct of_device_id *match)
{
- struct device_node *dp;
+ if (mc_type == MC_TYPE_SAFARI)
+ return chmc_probe(op, match);
+ else if (mc_type == MC_TYPE_JBUS)
+ return jbusmc_probe(op, match);
+ return -ENODEV;
+}
- /* This driver is only for cheetah platforms. */
- if (tlb_type != cheetah && tlb_type != cheetah_plus)
+static void __devexit chmc_destroy(struct of_device *op, struct chmc *p)
+{
+ list_del(&p->list);
+ of_iounmap(&op->resource[0], p->regs, 0x48);
+ kfree(p);
+}
+
+static void __devexit jbusmc_destroy(struct of_device *op, struct jbusmc *p)
+{
+ mc_list_del(&p->list);
+ of_iounmap(&op->resource[0], p->regs, JBUSMC_REGS_SIZE);
+ kfree(p);
+}
+
+static int __devexit us3mc_remove(struct of_device *op)
+{
+ void *p = dev_get_drvdata(&op->dev);
+
+ if (p) {
+ if (mc_type == MC_TYPE_SAFARI)
+ chmc_destroy(op, p);
+ else if (mc_type == MC_TYPE_JBUS)
+ jbusmc_destroy(op, p);
+ }
+ return 0;
+}
+
+static const struct of_device_id us3mc_match[] = {
+ {
+ .name = "memory-controller",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, us3mc_match);
+
+static struct of_platform_driver us3mc_driver = {
+ .name = "us3mc",
+ .match_table = us3mc_match,
+ .probe = us3mc_probe,
+ .remove = __devexit_p(us3mc_remove),
+};
+
+static inline bool us3mc_platform(void)
+{
+ if (tlb_type == cheetah || tlb_type == cheetah_plus)
+ return true;
+ return false;
+}
+
+static int __init us3mc_init(void)
+{
+ unsigned long ver;
+ int ret;
+
+ if (!us3mc_platform())
return -ENODEV;
- for_each_node_by_name(dp, "memory-controller")
- init_one_mctrl(dp);
+ __asm__ __volatile__("rdpr %%ver, %0" : "=r" (ver));
+ if ((ver >> 32UL) == __JALAPENO_ID ||
+ (ver >> 32UL) == __SERRANO_ID) {
+ mc_type = MC_TYPE_JBUS;
+ us3mc_dimm_printer = jbusmc_print_dimm;
+ } else {
+ mc_type = MC_TYPE_SAFARI;
+ us3mc_dimm_printer = chmc_print_dimm;
+ }
- for_each_node_by_name(dp, "mc-us3")
- init_one_mctrl(dp);
+ ret = register_dimm_printer(us3mc_dimm_printer);
- return 0;
+ if (!ret) {
+ ret = of_register_driver(&us3mc_driver, &of_bus_type);
+ if (ret)
+ unregister_dimm_printer(us3mc_dimm_printer);
+ }
+ return ret;
}
-static void __exit chmc_cleanup(void)
+static void __exit us3mc_cleanup(void)
{
- struct list_head *head = &mctrl_list;
- struct list_head *tmp = head->next;
-
- for (;;) {
- struct mctrl_info *p =
- list_entry(tmp, struct mctrl_info, list);
- if (tmp == head)
- break;
- tmp = tmp->next;
-
- list_del(&p->list);
- iounmap(p->regs);
- kfree(p);
+ if (us3mc_platform()) {
+ unregister_dimm_printer(us3mc_dimm_printer);
+ of_unregister_driver(&us3mc_driver);
}
}
-module_init(chmc_init);
-module_exit(chmc_cleanup);
+module_init(us3mc_init);
+module_exit(us3mc_cleanup);
diff --git a/arch/sparc64/kernel/cpu.c b/arch/sparc64/kernel/cpu.c
index 0097c08..0c9ac83 100644
--- a/arch/sparc64/kernel/cpu.c
+++ b/arch/sparc64/kernel/cpu.c
@@ -1,7 +1,7 @@
/* cpu.c: Dinky routines to look for the kind of Sparc cpu
* we are on.
*
- * Copyright (C) 1996, 2007 David S. Miller (davem@davemloft.net)
+ * Copyright (C) 1996, 2007, 2008 David S. Miller (davem@davemloft.net)
*/
#include <linux/kernel.h>
@@ -19,53 +19,86 @@
DEFINE_PER_CPU(cpuinfo_sparc, __cpu_data) = { 0 };
-struct cpu_iu_info {
- short manuf;
- short impl;
- char* cpu_name; /* should be enough I hope... */
+struct cpu_chip_info {
+ unsigned short manuf;
+ unsigned short impl;
+ const char *cpu_name;
+ const char *fp_name;
};
-struct cpu_fp_info {
- short manuf;
- short impl;
- char fpu_vers;
- char* fp_name;
+static const struct cpu_chip_info cpu_chips[] = {
+ {
+ .manuf = 0x17,
+ .impl = 0x10,
+ .cpu_name = "TI UltraSparc I (SpitFire)",
+ .fp_name = "UltraSparc I integrated FPU",
+ },
+ {
+ .manuf = 0x22,
+ .impl = 0x10,
+ .cpu_name = "TI UltraSparc I (SpitFire)",
+ .fp_name = "UltraSparc I integrated FPU",
+ },
+ {
+ .manuf = 0x17,
+ .impl = 0x11,
+ .cpu_name = "TI UltraSparc II (BlackBird)",
+ .fp_name = "UltraSparc II integrated FPU",
+ },
+ {
+ .manuf = 0x17,
+ .impl = 0x12,
+ .cpu_name = "TI UltraSparc IIi (Sabre)",
+ .fp_name = "UltraSparc IIi integrated FPU",
+ },
+ {
+ .manuf = 0x17,
+ .impl = 0x13,
+ .cpu_name = "TI UltraSparc IIe (Hummingbird)",
+ .fp_name = "UltraSparc IIe integrated FPU",
+ },
+ {
+ .manuf = 0x3e,
+ .impl = 0x14,
+ .cpu_name = "TI UltraSparc III (Cheetah)",
+ .fp_name = "UltraSparc III integrated FPU",
+ },
+ {
+ .manuf = 0x3e,
+ .impl = 0x15,
+ .cpu_name = "TI UltraSparc III+ (Cheetah+)",
+ .fp_name = "UltraSparc III+ integrated FPU",
+ },
+ {
+ .manuf = 0x3e,
+ .impl = 0x16,
+ .cpu_name = "TI UltraSparc IIIi (Jalapeno)",
+ .fp_name = "UltraSparc IIIi integrated FPU",
+ },
+ {
+ .manuf = 0x3e,
+ .impl = 0x18,
+ .cpu_name = "TI UltraSparc IV (Jaguar)",
+ .fp_name = "UltraSparc IV integrated FPU",
+ },
+ {
+ .manuf = 0x3e,
+ .impl = 0x19,
+ .cpu_name = "TI UltraSparc IV+ (Panther)",
+ .fp_name = "UltraSparc IV+ integrated FPU",
+ },
+ {
+ .manuf = 0x3e,
+ .impl = 0x22,
+ .cpu_name = "TI UltraSparc IIIi+ (Serrano)",
+ .fp_name = "UltraSparc IIIi+ integrated FPU",
+ },
};
-static struct cpu_fp_info linux_sparc_fpu[] = {
- { 0x17, 0x10, 0, "UltraSparc I integrated FPU"},
- { 0x22, 0x10, 0, "UltraSparc I integrated FPU"},
- { 0x17, 0x11, 0, "UltraSparc II integrated FPU"},
- { 0x17, 0x12, 0, "UltraSparc IIi integrated FPU"},
- { 0x17, 0x13, 0, "UltraSparc IIe integrated FPU"},
- { 0x3e, 0x14, 0, "UltraSparc III integrated FPU"},
- { 0x3e, 0x15, 0, "UltraSparc III+ integrated FPU"},
- { 0x3e, 0x16, 0, "UltraSparc IIIi integrated FPU"},
- { 0x3e, 0x18, 0, "UltraSparc IV integrated FPU"},
- { 0x3e, 0x19, 0, "UltraSparc IV+ integrated FPU"},
- { 0x3e, 0x22, 0, "UltraSparc IIIi+ integrated FPU"},
-};
+#define NSPARCCHIPS ARRAY_SIZE(linux_sparc_chips)
-#define NSPARCFPU ARRAY_SIZE(linux_sparc_fpu)
-
-static struct cpu_iu_info linux_sparc_chips[] = {
- { 0x17, 0x10, "TI UltraSparc I (SpitFire)"},
- { 0x22, 0x10, "TI UltraSparc I (SpitFire)"},
- { 0x17, 0x11, "TI UltraSparc II (BlackBird)"},
- { 0x17, 0x12, "TI UltraSparc IIi (Sabre)"},
- { 0x17, 0x13, "TI UltraSparc IIe (Hummingbird)"},
- { 0x3e, 0x14, "TI UltraSparc III (Cheetah)"},
- { 0x3e, 0x15, "TI UltraSparc III+ (Cheetah+)"},
- { 0x3e, 0x16, "TI UltraSparc IIIi (Jalapeno)"},
- { 0x3e, 0x18, "TI UltraSparc IV (Jaguar)"},
- { 0x3e, 0x19, "TI UltraSparc IV+ (Panther)"},
- { 0x3e, 0x22, "TI UltraSparc IIIi+ (Serrano)"},
-};
-
-#define NSPARCCHIPS ARRAY_SIZE(linux_sparc_chips)
-
-char *sparc_cpu_type;
-char *sparc_fpu_type;
+const char *sparc_cpu_type;
+const char *sparc_fpu_type;
static void __init sun4v_cpu_probe(void)
{
@@ -89,68 +122,45 @@
}
}
-void __init cpu_probe(void)
+static const struct cpu_chip_info * __init find_cpu_chip(unsigned short manuf,
+ unsigned short impl)
{
- unsigned long ver, fpu_vers, manuf, impl, fprs;
int i;
-
+
+ for (i = 0; i < ARRAY_SIZE(cpu_chips); i++) {
+ const struct cpu_chip_info *p = &cpu_chips[i];
+
+ if (p->manuf == manuf && p->impl == impl)
+ return p;
+ }
+ return NULL;
+}
+
+static int __init cpu_type_probe(void)
+{
if (tlb_type == hypervisor) {
sun4v_cpu_probe();
- return;
- }
-
- fprs = fprs_read();
- fprs_write(FPRS_FEF);
- __asm__ __volatile__ ("rdpr %%ver, %0; stx %%fsr, [%1]"
- : "=&r" (ver)
- : "r" (&fpu_vers));
- fprs_write(fprs);
+ } else {
+ unsigned long ver, manuf, impl;
+ const struct cpu_chip_info *p;
- manuf = ((ver >> 48) & 0xffff);
- impl = ((ver >> 32) & 0xffff);
+ __asm__ __volatile__("rdpr %%ver, %0" : "=r" (ver));
+
+ manuf = ((ver >> 48) & 0xffff);
+ impl = ((ver >> 32) & 0xffff);
- fpu_vers = ((fpu_vers >> 17) & 0x7);
-
-retry:
- for (i = 0; i < NSPARCCHIPS; i++) {
- if (linux_sparc_chips[i].manuf == manuf) {
- if (linux_sparc_chips[i].impl == impl) {
- sparc_cpu_type =
- linux_sparc_chips[i].cpu_name;
- break;
- }
+ p = find_cpu_chip(manuf, impl);
+ if (p) {
+ sparc_cpu_type = p->cpu_name;
+ sparc_fpu_type = p->fp_name;
+ } else {
+ printk(KERN_ERR "CPU: Unknown chip, manuf[%lx] impl[%lx]\n",
+ manuf, impl);
+ sparc_cpu_type = "Unknown CPU";
+ sparc_fpu_type = "Unknown FPU";
}
}
-
- if (i == NSPARCCHIPS) {
- /* Maybe it is a cheetah+ derivative, report it as cheetah+
- * in that case until we learn the real names.
- */
- if (manuf == 0x3e &&
- impl > 0x15) {
- impl = 0x15;
- goto retry;
- } else {
- printk("DEBUG: manuf[%lx] impl[%lx]\n",
- manuf, impl);
- }
- sparc_cpu_type = "Unknown CPU";
- }
-
- for (i = 0; i < NSPARCFPU; i++) {
- if (linux_sparc_fpu[i].manuf == manuf &&
- linux_sparc_fpu[i].impl == impl) {
- if (linux_sparc_fpu[i].fpu_vers == fpu_vers) {
- sparc_fpu_type =
- linux_sparc_fpu[i].fp_name;
- break;
- }
- }
- }
-
- if (i == NSPARCFPU) {
- printk("DEBUG: manuf[%lx] impl[%lx] fsr.vers[%lx]\n",
- manuf, impl, fpu_vers);
- sparc_fpu_type = "Unknown FPU";
- }
+ return 0;
}
+
+arch_initcall(cpu_type_probe);
diff --git a/arch/sparc64/kernel/ds.c b/arch/sparc64/kernel/ds.c
index d0fa5aa..f52e053 100644
--- a/arch/sparc64/kernel/ds.c
+++ b/arch/sparc64/kernel/ds.c
@@ -1,6 +1,6 @@
/* ds.c: Domain Services driver for Logical Domains
*
- * Copyright (C) 2007 David S. Miller <davem@davemloft.net>
+ * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net>
*/
#include <linux/kernel.h>
@@ -1217,7 +1217,7 @@
return 0;
}
-static struct vio_device_id ds_match[] = {
+static struct vio_device_id __initdata ds_match[] = {
{
.type = "domain-services-port",
},
diff --git a/arch/sparc64/kernel/ebus.c b/arch/sparc64/kernel/ebus.c
index 60d36d1..77dbf6d 100644
--- a/arch/sparc64/kernel/ebus.c
+++ b/arch/sparc64/kernel/ebus.c
@@ -1,5 +1,4 @@
-/*
- * ebus.c: PCI to EBus bridge device.
+/* ebus.c: EBUS DMA library code.
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
* Copyright (C) 1999 David S. Miller (davem@redhat.com)
@@ -9,24 +8,12 @@
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
-#include <linux/pci.h>
-#include <linux/of_device.h>
-#include <asm/system.h>
-#include <asm/page.h>
-#include <asm/ebus.h>
-#include <asm/oplib.h>
-#include <asm/prom.h>
-#include <asm/bpp.h>
-#include <asm/irq.h>
+#include <asm/ebus_dma.h>
#include <asm/io.h>
-/* EBUS dma library. */
-
#define EBDMA_CSR 0x00UL /* Control/Status */
#define EBDMA_ADDR 0x04UL /* DMA Address */
#define EBDMA_COUNT 0x08UL /* DMA Count */
@@ -268,283 +255,3 @@
spin_unlock_irqrestore(&p->lock, flags);
}
EXPORT_SYMBOL(ebus_dma_enable);
-
-struct linux_ebus *ebus_chain = NULL;
-
-static inline void *ebus_alloc(size_t size)
-{
- void *mem;
-
- mem = kzalloc(size, GFP_ATOMIC);
- if (!mem)
- panic("ebus_alloc: out of memory");
- return mem;
-}
-
-static void __init fill_ebus_child(struct device_node *dp,
- struct linux_ebus_child *dev,
- int non_standard_regs)
-{
- struct of_device *op;
- const int *regs;
- int i, len;
-
- dev->prom_node = dp;
- printk(" (%s)", dp->name);
-
- regs = of_get_property(dp, "reg", &len);
- if (!regs)
- dev->num_addrs = 0;
- else
- dev->num_addrs = len / sizeof(regs[0]);
-
- if (non_standard_regs) {
- /* This is to handle reg properties which are not
- * in the parent relative format. One example are
- * children of the i2c device on CompactPCI systems.
- *
- * So, for such devices we just record the property
- * raw in the child resources.
- */
- for (i = 0; i < dev->num_addrs; i++)
- dev->resource[i].start = regs[i];
- } else {
- for (i = 0; i < dev->num_addrs; i++) {
- int rnum = regs[i];
- if (rnum >= dev->parent->num_addrs) {
- prom_printf("UGH: property for %s was %d, need < %d\n",
- dp->name, len, dev->parent->num_addrs);
- prom_halt();
- }
- dev->resource[i].start = dev->parent->resource[i].start;
- dev->resource[i].end = dev->parent->resource[i].end;
- dev->resource[i].flags = IORESOURCE_MEM;
- dev->resource[i].name = dp->name;
- }
- }
-
- op = of_find_device_by_node(dp);
- if (!op) {
- dev->num_irqs = 0;
- } else {
- dev->num_irqs = op->num_irqs;
- for (i = 0; i < dev->num_irqs; i++)
- dev->irqs[i] = op->irqs[i];
- }
-
- if (!dev->num_irqs) {
- /*
- * Oh, well, some PROMs don't export interrupts
- * property to children of EBus devices...
- *
- * Be smart about PS/2 keyboard and mouse.
- */
- if (!strcmp(dev->parent->prom_node->name, "8042")) {
- if (!strcmp(dev->prom_node->name, "kb_ps2")) {
- dev->num_irqs = 1;
- dev->irqs[0] = dev->parent->irqs[0];
- } else {
- dev->num_irqs = 1;
- dev->irqs[0] = dev->parent->irqs[1];
- }
- }
- }
-}
-
-static int __init child_regs_nonstandard(struct linux_ebus_device *dev)
-{
- if (!strcmp(dev->prom_node->name, "i2c") ||
- !strcmp(dev->prom_node->name, "SUNW,lombus"))
- return 1;
- return 0;
-}
-
-static void __init fill_ebus_device(struct device_node *dp, struct linux_ebus_device *dev)
-{
- struct linux_ebus_child *child;
- struct dev_archdata *sd;
- struct of_device *op;
- int i, len;
-
- dev->prom_node = dp;
-
- printk(" [%s", dp->name);
-
- op = of_find_device_by_node(dp);
- if (!op) {
- dev->num_addrs = 0;
- dev->num_irqs = 0;
- } else {
- const int *regs = of_get_property(dp, "reg", &len);
-
- if (!regs)
- len = 0;
- dev->num_addrs = len / sizeof(struct linux_prom_registers);
-
- for (i = 0; i < dev->num_addrs; i++)
- memcpy(&dev->resource[i],
- &op->resource[i],
- sizeof(struct resource));
-
- dev->num_irqs = op->num_irqs;
- for (i = 0; i < dev->num_irqs; i++)
- dev->irqs[i] = op->irqs[i];
- }
-
- sd = &dev->ofdev.dev.archdata;
- sd->prom_node = dp;
- sd->op = &dev->ofdev;
- sd->iommu = dev->bus->ofdev.dev.parent->archdata.iommu;
- sd->stc = dev->bus->ofdev.dev.parent->archdata.stc;
- sd->numa_node = dev->bus->ofdev.dev.parent->archdata.numa_node;
-
- dev->ofdev.node = dp;
- dev->ofdev.dev.parent = &dev->bus->ofdev.dev;
- dev->ofdev.dev.bus = &ebus_bus_type;
- dev_set_name(&dev->ofdev.dev, "ebus[%08x]", dp->node);
-
- /* Register with core */
- if (of_device_register(&dev->ofdev) != 0)
- printk(KERN_DEBUG "ebus: device registration error for %s!\n",
- dp->path_component_name);
-
- dp = dp->child;
- if (dp) {
- printk(" ->");
- dev->children = ebus_alloc(sizeof(struct linux_ebus_child));
-
- child = dev->children;
- child->next = NULL;
- child->parent = dev;
- child->bus = dev->bus;
- fill_ebus_child(dp, child,
- child_regs_nonstandard(dev));
-
- while ((dp = dp->sibling) != NULL) {
- child->next = ebus_alloc(sizeof(struct linux_ebus_child));
-
- child = child->next;
- child->next = NULL;
- child->parent = dev;
- child->bus = dev->bus;
- fill_ebus_child(dp, child,
- child_regs_nonstandard(dev));
- }
- }
- printk("]");
-}
-
-static struct pci_dev *find_next_ebus(struct pci_dev *start, int *is_rio_p)
-{
- struct pci_dev *pdev = start;
-
- while ((pdev = pci_get_device(PCI_VENDOR_ID_SUN, PCI_ANY_ID, pdev)))
- if (pdev->device == PCI_DEVICE_ID_SUN_EBUS ||
- pdev->device == PCI_DEVICE_ID_SUN_RIO_EBUS)
- break;
-
- *is_rio_p = !!(pdev && (pdev->device == PCI_DEVICE_ID_SUN_RIO_EBUS));
-
- return pdev;
-}
-
-void __init ebus_init(void)
-{
- struct linux_ebus_device *dev;
- struct linux_ebus *ebus;
- struct pci_dev *pdev;
- struct device_node *dp;
- int is_rio;
- int num_ebus = 0;
-
- pdev = find_next_ebus(NULL, &is_rio);
- if (!pdev) {
- printk("ebus: No EBus's found.\n");
- return;
- }
-
- dp = pci_device_to_OF_node(pdev);
-
- ebus_chain = ebus = ebus_alloc(sizeof(struct linux_ebus));
- ebus->next = NULL;
- ebus->is_rio = is_rio;
-
- while (dp) {
- struct device_node *child;
-
- /* SUNW,pci-qfe uses four empty ebuses on it.
- I think we should not consider them here,
- as they have half of the properties this
- code expects and once we do PCI hot-plug,
- we'd have to tweak with the ebus_chain
- in the runtime after initialization. -jj */
- if (!dp->child) {
- pdev = find_next_ebus(pdev, &is_rio);
- if (!pdev) {
- if (ebus == ebus_chain) {
- ebus_chain = NULL;
- printk("ebus: No EBus's found.\n");
- return;
- }
- break;
- }
- ebus->is_rio = is_rio;
- dp = pci_device_to_OF_node(pdev);
- continue;
- }
- printk("ebus%d:", num_ebus);
-
- ebus->index = num_ebus;
- ebus->prom_node = dp;
- ebus->self = pdev;
-
- ebus->ofdev.node = dp;
- ebus->ofdev.dev.parent = &pdev->dev;
- ebus->ofdev.dev.bus = &ebus_bus_type;
- dev_set_name(&ebus->ofdev.dev, "ebus%d", num_ebus);
-
- /* Register with core */
- if (of_device_register(&ebus->ofdev) != 0)
- printk(KERN_DEBUG "ebus: device registration error for %s!\n",
- dp->path_component_name);
-
-
- child = dp->child;
- if (!child)
- goto next_ebus;
-
- ebus->devices = ebus_alloc(sizeof(struct linux_ebus_device));
-
- dev = ebus->devices;
- dev->next = NULL;
- dev->children = NULL;
- dev->bus = ebus;
- fill_ebus_device(child, dev);
-
- while ((child = child->sibling) != NULL) {
- dev->next = ebus_alloc(sizeof(struct linux_ebus_device));
-
- dev = dev->next;
- dev->next = NULL;
- dev->children = NULL;
- dev->bus = ebus;
- fill_ebus_device(child, dev);
- }
-
- next_ebus:
- printk("\n");
-
- pdev = find_next_ebus(pdev, &is_rio);
- if (!pdev)
- break;
-
- dp = pci_device_to_OF_node(pdev);
-
- ebus->next = ebus_alloc(sizeof(struct linux_ebus));
- ebus = ebus->next;
- ebus->next = NULL;
- ebus->is_rio = is_rio;
- ++num_ebus;
- }
- pci_dev_put(pdev); /* XXX for the case, when ebusnd is 0, is it OK? */
-}
diff --git a/arch/sparc64/kernel/entry.h b/arch/sparc64/kernel/entry.h
index fc294a2..34d7ab5 100644
--- a/arch/sparc64/kernel/entry.h
+++ b/arch/sparc64/kernel/entry.h
@@ -5,8 +5,8 @@
#include <linux/types.h>
#include <linux/init.h>
-extern char *sparc_cpu_type;
-extern char *sparc_fpu_type;
+extern const char *sparc_cpu_type;
+extern const char *sparc_fpu_type;
extern void __init per_cpu_patch(void);
extern void __init sun4v_patch(void);
@@ -22,7 +22,8 @@
unsigned long orig_i0,
unsigned long thread_info_flags);
-extern asmlinkage int syscall_trace(struct pt_regs *regs, int syscall_exit_p);
+extern asmlinkage int syscall_trace_enter(struct pt_regs *regs);
+extern asmlinkage void syscall_trace_leave(struct pt_regs *regs);
extern void bad_trap_tl1(struct pt_regs *regs, long lvl);
diff --git a/arch/sparc64/kernel/head.S b/arch/sparc64/kernel/head.S
index c9afef0..353226f 100644
--- a/arch/sparc64/kernel/head.S
+++ b/arch/sparc64/kernel/head.S
@@ -10,6 +10,7 @@
#include <linux/errno.h>
#include <linux/threads.h>
#include <linux/init.h>
+#include <linux/linkage.h>
#include <asm/thread_info.h>
#include <asm/asi.h>
#include <asm/pstate.h>
diff --git a/arch/sparc64/kernel/hvapi.c b/arch/sparc64/kernel/hvapi.c
index 691760b..1d272c3 100644
--- a/arch/sparc64/kernel/hvapi.c
+++ b/arch/sparc64/kernel/hvapi.c
@@ -9,7 +9,6 @@
#include <asm/hypervisor.h>
#include <asm/oplib.h>
-#include <asm/sstate.h>
/* If the hypervisor indicates that the API setting
* calls are unsupported, by returning HV_EBADTRAP or
@@ -184,8 +183,6 @@
if (sun4v_hvapi_register(group, major, &minor))
goto bad;
- sun4v_sstate_init();
-
return;
bad:
diff --git a/arch/sparc64/kernel/hvcalls.S b/arch/sparc64/kernel/hvcalls.S
index a2810f3..e066269 100644
--- a/arch/sparc64/kernel/hvcalls.S
+++ b/arch/sparc64/kernel/hvcalls.S
@@ -3,89 +3,75 @@
*
* returns %o0: sysino
*/
- .globl sun4v_devino_to_sysino
- .type sun4v_devino_to_sysino,#function
-sun4v_devino_to_sysino:
+ENTRY(sun4v_devino_to_sysino)
mov HV_FAST_INTR_DEVINO2SYSINO, %o5
ta HV_FAST_TRAP
retl
mov %o1, %o0
- .size sun4v_devino_to_sysino, .-sun4v_devino_to_sysino
+ENDPROC(sun4v_devino_to_sysino)
/* %o0: sysino
*
* returns %o0: intr_enabled (HV_INTR_{DISABLED,ENABLED})
*/
- .globl sun4v_intr_getenabled
- .type sun4v_intr_getenabled,#function
-sun4v_intr_getenabled:
+ENTRY(sun4v_intr_getenabled)
mov HV_FAST_INTR_GETENABLED, %o5
ta HV_FAST_TRAP
retl
mov %o1, %o0
- .size sun4v_intr_getenabled, .-sun4v_intr_getenabled
+ENDPROC(sun4v_intr_getenabled)
/* %o0: sysino
* %o1: intr_enabled (HV_INTR_{DISABLED,ENABLED})
*/
- .globl sun4v_intr_setenabled
- .type sun4v_intr_setenabled,#function
-sun4v_intr_setenabled:
+ENTRY(sun4v_intr_setenabled)
mov HV_FAST_INTR_SETENABLED, %o5
ta HV_FAST_TRAP
retl
nop
- .size sun4v_intr_setenabled, .-sun4v_intr_setenabled
+ENDPROC(sun4v_intr_setenabled)
/* %o0: sysino
*
* returns %o0: intr_state (HV_INTR_STATE_*)
*/
- .globl sun4v_intr_getstate
- .type sun4v_intr_getstate,#function
-sun4v_intr_getstate:
+ENTRY(sun4v_intr_getstate)
mov HV_FAST_INTR_GETSTATE, %o5
ta HV_FAST_TRAP
retl
mov %o1, %o0
- .size sun4v_intr_getstate, .-sun4v_intr_getstate
+ENDPROC(sun4v_intr_getstate)
/* %o0: sysino
* %o1: intr_state (HV_INTR_STATE_*)
*/
- .globl sun4v_intr_setstate
- .type sun4v_intr_setstate,#function
-sun4v_intr_setstate:
+ENTRY(sun4v_intr_setstate)
mov HV_FAST_INTR_SETSTATE, %o5
ta HV_FAST_TRAP
retl
nop
- .size sun4v_intr_setstate, .-sun4v_intr_setstate
+ENDPROC(sun4v_intr_setstate)
/* %o0: sysino
*
* returns %o0: cpuid
*/
- .globl sun4v_intr_gettarget
- .type sun4v_intr_gettarget,#function
-sun4v_intr_gettarget:
+ENTRY(sun4v_intr_gettarget)
mov HV_FAST_INTR_GETTARGET, %o5
ta HV_FAST_TRAP
retl
mov %o1, %o0
- .size sun4v_intr_gettarget, .-sun4v_intr_gettarget
+ENDPROC(sun4v_intr_gettarget)
/* %o0: sysino
* %o1: cpuid
*/
- .globl sun4v_intr_settarget
- .type sun4v_intr_settarget,#function
-sun4v_intr_settarget:
+ENTRY(sun4v_intr_settarget)
mov HV_FAST_INTR_SETTARGET, %o5
ta HV_FAST_TRAP
retl
nop
- .size sun4v_intr_settarget, .-sun4v_intr_settarget
+ENDPROC(sun4v_intr_settarget)
/* %o0: cpuid
* %o1: pc
@@ -94,37 +80,31 @@
*
* returns %o0: status
*/
- .globl sun4v_cpu_start
- .type sun4v_cpu_start,#function
-sun4v_cpu_start:
+ENTRY(sun4v_cpu_start)
mov HV_FAST_CPU_START, %o5
ta HV_FAST_TRAP
retl
nop
- .size sun4v_cpu_start, .-sun4v_cpu_start
+ENDPROC(sun4v_cpu_start)
/* %o0: cpuid
*
* returns %o0: status
*/
- .globl sun4v_cpu_stop
- .type sun4v_cpu_stop,#function
-sun4v_cpu_stop:
+ENTRY(sun4v_cpu_stop)
mov HV_FAST_CPU_STOP, %o5
ta HV_FAST_TRAP
retl
nop
- .size sun4v_cpu_stop, .-sun4v_cpu_stop
+ENDPROC(sun4v_cpu_stop)
/* returns %o0: status */
- .globl sun4v_cpu_yield
- .type sun4v_cpu_yield, #function
-sun4v_cpu_yield:
+ENTRY(sun4v_cpu_yield)
mov HV_FAST_CPU_YIELD, %o5
ta HV_FAST_TRAP
retl
nop
- .size sun4v_cpu_yield, .-sun4v_cpu_yield
+ENDPROC(sun4v_cpu_yield)
/* %o0: type
* %o1: queue paddr
@@ -132,14 +112,12 @@
*
* returns %o0: status
*/
- .globl sun4v_cpu_qconf
- .type sun4v_cpu_qconf,#function
-sun4v_cpu_qconf:
+ENTRY(sun4v_cpu_qconf)
mov HV_FAST_CPU_QCONF, %o5
ta HV_FAST_TRAP
retl
nop
- .size sun4v_cpu_qconf, .-sun4v_cpu_qconf
+ENDPROC(sun4v_cpu_qconf)
/* %o0: num cpus in cpu list
* %o1: cpu list paddr
@@ -147,23 +125,19 @@
*
* returns %o0: status
*/
- .globl sun4v_cpu_mondo_send
- .type sun4v_cpu_mondo_send,#function
-sun4v_cpu_mondo_send:
+ENTRY(sun4v_cpu_mondo_send)
mov HV_FAST_CPU_MONDO_SEND, %o5
ta HV_FAST_TRAP
retl
nop
- .size sun4v_cpu_mondo_send, .-sun4v_cpu_mondo_send
+ENDPROC(sun4v_cpu_mondo_send)
/* %o0: CPU ID
*
* returns %o0: -status if status non-zero, else
* %o0: cpu state as HV_CPU_STATE_*
*/
- .globl sun4v_cpu_state
- .type sun4v_cpu_state,#function
-sun4v_cpu_state:
+ENTRY(sun4v_cpu_state)
mov HV_FAST_CPU_STATE, %o5
ta HV_FAST_TRAP
brnz,pn %o0, 1f
@@ -171,7 +145,7 @@
mov %o1, %o0
1: retl
nop
- .size sun4v_cpu_state, .-sun4v_cpu_state
+ENDPROC(sun4v_cpu_state)
/* %o0: virtual address
* %o1: must be zero
@@ -180,28 +154,24 @@
*
* returns %o0: status
*/
- .globl sun4v_mmu_map_perm_addr
- .type sun4v_mmu_map_perm_addr,#function
-sun4v_mmu_map_perm_addr:
+ENTRY(sun4v_mmu_map_perm_addr)
mov HV_FAST_MMU_MAP_PERM_ADDR, %o5
ta HV_FAST_TRAP
retl
nop
- .size sun4v_mmu_map_perm_addr, .-sun4v_mmu_map_perm_addr
+ENDPROC(sun4v_mmu_map_perm_addr)
/* %o0: number of TSB descriptions
* %o1: TSB descriptions real address
*
* returns %o0: status
*/
- .globl sun4v_mmu_tsb_ctx0
- .type sun4v_mmu_tsb_ctx0,#function
-sun4v_mmu_tsb_ctx0:
+ENTRY(sun4v_mmu_tsb_ctx0)
mov HV_FAST_MMU_TSB_CTX0, %o5
ta HV_FAST_TRAP
retl
nop
- .size sun4v_mmu_tsb_ctx0, .-sun4v_mmu_tsb_ctx0
+ENDPROC(sun4v_mmu_tsb_ctx0)
/* %o0: API group number
* %o1: pointer to unsigned long major number storage
@@ -209,9 +179,7 @@
*
* returns %o0: status
*/
- .globl sun4v_get_version
- .type sun4v_get_version,#function
-sun4v_get_version:
+ENTRY(sun4v_get_version)
mov HV_CORE_GET_VER, %o5
mov %o1, %o3
mov %o2, %o4
@@ -219,7 +187,7 @@
stx %o1, [%o3]
retl
stx %o2, [%o4]
- .size sun4v_get_version, .-sun4v_get_version
+ENDPROC(sun4v_get_version)
/* %o0: API group number
* %o1: desired major number
@@ -228,51 +196,43 @@
*
* returns %o0: status
*/
- .globl sun4v_set_version
- .type sun4v_set_version,#function
-sun4v_set_version:
+ENTRY(sun4v_set_version)
mov HV_CORE_SET_VER, %o5
mov %o3, %o4
ta HV_CORE_TRAP
retl
stx %o1, [%o4]
- .size sun4v_set_version, .-sun4v_set_version
+ENDPROC(sun4v_set_version)
/* %o0: pointer to unsigned long time
*
* returns %o0: status
*/
- .globl sun4v_tod_get
- .type sun4v_tod_get,#function
-sun4v_tod_get:
+ENTRY(sun4v_tod_get)
mov %o0, %o4
mov HV_FAST_TOD_GET, %o5
ta HV_FAST_TRAP
stx %o1, [%o4]
retl
nop
- .size sun4v_tod_get, .-sun4v_tod_get
+ENDPROC(sun4v_tod_get)
/* %o0: time
*
* returns %o0: status
*/
- .globl sun4v_tod_set
- .type sun4v_tod_set,#function
-sun4v_tod_set:
+ENTRY(sun4v_tod_set)
mov HV_FAST_TOD_SET, %o5
ta HV_FAST_TRAP
retl
nop
- .size sun4v_tod_set, .-sun4v_tod_set
+ENDPROC(sun4v_tod_set)
/* %o0: pointer to unsigned long status
*
* returns %o0: signed character
*/
- .globl sun4v_con_getchar
- .type sun4v_con_getchar,#function
-sun4v_con_getchar:
+ENTRY(sun4v_con_getchar)
mov %o0, %o4
mov HV_FAST_CONS_GETCHAR, %o5
clr %o0
@@ -281,20 +241,18 @@
stx %o0, [%o4]
retl
sra %o1, 0, %o0
- .size sun4v_con_getchar, .-sun4v_con_getchar
+ENDPROC(sun4v_con_getchar)
/* %o0: signed long character
*
* returns %o0: status
*/
- .globl sun4v_con_putchar
- .type sun4v_con_putchar,#function
-sun4v_con_putchar:
+ENTRY(sun4v_con_putchar)
mov HV_FAST_CONS_PUTCHAR, %o5
ta HV_FAST_TRAP
retl
sra %o0, 0, %o0
- .size sun4v_con_putchar, .-sun4v_con_putchar
+ENDPROC(sun4v_con_putchar)
/* %o0: buffer real address
* %o1: buffer size
@@ -302,9 +260,7 @@
*
* returns %o0: status
*/
- .globl sun4v_con_read
- .type sun4v_con_read,#function
-sun4v_con_read:
+ENTRY(sun4v_con_read)
mov %o2, %o4
mov HV_FAST_CONS_READ, %o5
ta HV_FAST_TRAP
@@ -318,7 +274,7 @@
stx %o1, [%o4]
1: retl
nop
- .size sun4v_con_read, .-sun4v_con_read
+ENDPROC(sun4v_con_read)
/* %o0: buffer real address
* %o1: buffer size
@@ -326,43 +282,37 @@
*
* returns %o0: status
*/
- .globl sun4v_con_write
- .type sun4v_con_write,#function
-sun4v_con_write:
+ENTRY(sun4v_con_write)
mov %o2, %o4
mov HV_FAST_CONS_WRITE, %o5
ta HV_FAST_TRAP
stx %o1, [%o4]
retl
nop
- .size sun4v_con_write, .-sun4v_con_write
+ENDPROC(sun4v_con_write)
/* %o0: soft state
* %o1: address of description string
*
* returns %o0: status
*/
- .globl sun4v_mach_set_soft_state
- .type sun4v_mach_set_soft_state,#function
-sun4v_mach_set_soft_state:
+ENTRY(sun4v_mach_set_soft_state)
mov HV_FAST_MACH_SET_SOFT_STATE, %o5
ta HV_FAST_TRAP
retl
nop
- .size sun4v_mach_set_soft_state, .-sun4v_mach_set_soft_state
+ENDPROC(sun4v_mach_set_soft_state)
/* %o0: exit code
*
* Does not return.
*/
- .globl sun4v_mach_exit
- .type sun4v_mach_exit,#function
-sun4v_mach_exit:
+ENTRY(sun4v_mach_exit)
mov HV_FAST_MACH_EXIT, %o5
ta HV_FAST_TRAP
retl
nop
- .size sun4v_mach_exit, .-sun4v_mach_exit
+ENDPROC(sun4v_mach_exit)
/* %o0: buffer real address
* %o1: buffer length
@@ -370,44 +320,38 @@
*
* returns %o0: status
*/
- .globl sun4v_mach_desc
- .type sun4v_mach_desc,#function
-sun4v_mach_desc:
+ENTRY(sun4v_mach_desc)
mov %o2, %o4
mov HV_FAST_MACH_DESC, %o5
ta HV_FAST_TRAP
stx %o1, [%o4]
retl
nop
- .size sun4v_mach_desc, .-sun4v_mach_desc
+ENDPROC(sun4v_mach_desc)
/* %o0: new timeout in milliseconds
* %o1: pointer to unsigned long orig_timeout
*
* returns %o0: status
*/
- .globl sun4v_mach_set_watchdog
- .type sun4v_mach_set_watchdog,#function
-sun4v_mach_set_watchdog:
+ENTRY(sun4v_mach_set_watchdog)
mov %o1, %o4
mov HV_FAST_MACH_SET_WATCHDOG, %o5
ta HV_FAST_TRAP
stx %o1, [%o4]
retl
nop
- .size sun4v_mach_set_watchdog, .-sun4v_mach_set_watchdog
+ENDPROC(sun4v_mach_set_watchdog)
/* No inputs and does not return. */
- .globl sun4v_mach_sir
- .type sun4v_mach_sir,#function
-sun4v_mach_sir:
+ENTRY(sun4v_mach_sir)
mov %o1, %o4
mov HV_FAST_MACH_SIR, %o5
ta HV_FAST_TRAP
stx %o1, [%o4]
retl
nop
- .size sun4v_mach_sir, .-sun4v_mach_sir
+ENDPROC(sun4v_mach_sir)
/* %o0: channel
* %o1: ra
@@ -415,14 +359,12 @@
*
* returns %o0: status
*/
- .globl sun4v_ldc_tx_qconf
- .type sun4v_ldc_tx_qconf,#function
-sun4v_ldc_tx_qconf:
+ENTRY(sun4v_ldc_tx_qconf)
mov HV_FAST_LDC_TX_QCONF, %o5
ta HV_FAST_TRAP
retl
nop
- .size sun4v_ldc_tx_qconf, .-sun4v_ldc_tx_qconf
+ENDPROC(sun4v_ldc_tx_qconf)
/* %o0: channel
* %o1: pointer to unsigned long ra
@@ -430,9 +372,7 @@
*
* returns %o0: status
*/
- .globl sun4v_ldc_tx_qinfo
- .type sun4v_ldc_tx_qinfo,#function
-sun4v_ldc_tx_qinfo:
+ENTRY(sun4v_ldc_tx_qinfo)
mov %o1, %g1
mov %o2, %g2
mov HV_FAST_LDC_TX_QINFO, %o5
@@ -441,7 +381,7 @@
stx %o2, [%g2]
retl
nop
- .size sun4v_ldc_tx_qinfo, .-sun4v_ldc_tx_qinfo
+ENDPROC(sun4v_ldc_tx_qinfo)
/* %o0: channel
* %o1: pointer to unsigned long head_off
@@ -450,9 +390,7 @@
*
* returns %o0: status
*/
- .globl sun4v_ldc_tx_get_state
- .type sun4v_ldc_tx_get_state,#function
-sun4v_ldc_tx_get_state:
+ENTRY(sun4v_ldc_tx_get_state)
mov %o1, %g1
mov %o2, %g2
mov %o3, %g3
@@ -463,21 +401,19 @@
stx %o3, [%g3]
retl
nop
- .size sun4v_ldc_tx_get_state, .-sun4v_ldc_tx_get_state
+ENDPROC(sun4v_ldc_tx_get_state)
/* %o0: channel
* %o1: tail_off
*
* returns %o0: status
*/
- .globl sun4v_ldc_tx_set_qtail
- .type sun4v_ldc_tx_set_qtail,#function
-sun4v_ldc_tx_set_qtail:
+ENTRY(sun4v_ldc_tx_set_qtail)
mov HV_FAST_LDC_TX_SET_QTAIL, %o5
ta HV_FAST_TRAP
retl
nop
- .size sun4v_ldc_tx_set_qtail, .-sun4v_ldc_tx_set_qtail
+ENDPROC(sun4v_ldc_tx_set_qtail)
/* %o0: channel
* %o1: ra
@@ -485,14 +421,12 @@
*
* returns %o0: status
*/
- .globl sun4v_ldc_rx_qconf
- .type sun4v_ldc_rx_qconf,#function
-sun4v_ldc_rx_qconf:
+ENTRY(sun4v_ldc_rx_qconf)
mov HV_FAST_LDC_RX_QCONF, %o5
ta HV_FAST_TRAP
retl
nop
- .size sun4v_ldc_rx_qconf, .-sun4v_ldc_rx_qconf
+ENDPROC(sun4v_ldc_rx_qconf)
/* %o0: channel
* %o1: pointer to unsigned long ra
@@ -500,9 +434,7 @@
*
* returns %o0: status
*/
- .globl sun4v_ldc_rx_qinfo
- .type sun4v_ldc_rx_qinfo,#function
-sun4v_ldc_rx_qinfo:
+ENTRY(sun4v_ldc_rx_qinfo)
mov %o1, %g1
mov %o2, %g2
mov HV_FAST_LDC_RX_QINFO, %o5
@@ -511,7 +443,7 @@
stx %o2, [%g2]
retl
nop
- .size sun4v_ldc_rx_qinfo, .-sun4v_ldc_rx_qinfo
+ENDPROC(sun4v_ldc_rx_qinfo)
/* %o0: channel
* %o1: pointer to unsigned long head_off
@@ -520,9 +452,7 @@
*
* returns %o0: status
*/
- .globl sun4v_ldc_rx_get_state
- .type sun4v_ldc_rx_get_state,#function
-sun4v_ldc_rx_get_state:
+ENTRY(sun4v_ldc_rx_get_state)
mov %o1, %g1
mov %o2, %g2
mov %o3, %g3
@@ -533,21 +463,19 @@
stx %o3, [%g3]
retl
nop
- .size sun4v_ldc_rx_get_state, .-sun4v_ldc_rx_get_state
+ENDPROC(sun4v_ldc_rx_get_state)
/* %o0: channel
* %o1: head_off
*
* returns %o0: status
*/
- .globl sun4v_ldc_rx_set_qhead
- .type sun4v_ldc_rx_set_qhead,#function
-sun4v_ldc_rx_set_qhead:
+ENTRY(sun4v_ldc_rx_set_qhead)
mov HV_FAST_LDC_RX_SET_QHEAD, %o5
ta HV_FAST_TRAP
retl
nop
- .size sun4v_ldc_rx_set_qhead, .-sun4v_ldc_rx_set_qhead
+ENDPROC(sun4v_ldc_rx_set_qhead)
/* %o0: channel
* %o1: ra
@@ -555,14 +483,12 @@
*
* returns %o0: status
*/
- .globl sun4v_ldc_set_map_table
- .type sun4v_ldc_set_map_table,#function
-sun4v_ldc_set_map_table:
+ENTRY(sun4v_ldc_set_map_table)
mov HV_FAST_LDC_SET_MAP_TABLE, %o5
ta HV_FAST_TRAP
retl
nop
- .size sun4v_ldc_set_map_table, .-sun4v_ldc_set_map_table
+ENDPROC(sun4v_ldc_set_map_table)
/* %o0: channel
* %o1: pointer to unsigned long ra
@@ -570,9 +496,7 @@
*
* returns %o0: status
*/
- .globl sun4v_ldc_get_map_table
- .type sun4v_ldc_get_map_table,#function
-sun4v_ldc_get_map_table:
+ENTRY(sun4v_ldc_get_map_table)
mov %o1, %g1
mov %o2, %g2
mov HV_FAST_LDC_GET_MAP_TABLE, %o5
@@ -581,7 +505,7 @@
stx %o2, [%g2]
retl
nop
- .size sun4v_ldc_get_map_table, .-sun4v_ldc_get_map_table
+ENDPROC(sun4v_ldc_get_map_table)
/* %o0: channel
* %o1: dir_code
@@ -592,16 +516,14 @@
*
* returns %o0: status
*/
- .globl sun4v_ldc_copy
- .type sun4v_ldc_copy,#function
-sun4v_ldc_copy:
+ENTRY(sun4v_ldc_copy)
mov %o5, %g1
mov HV_FAST_LDC_COPY, %o5
ta HV_FAST_TRAP
stx %o1, [%g1]
retl
nop
- .size sun4v_ldc_copy, .-sun4v_ldc_copy
+ENDPROC(sun4v_ldc_copy)
/* %o0: channel
* %o1: cookie
@@ -610,9 +532,7 @@
*
* returns %o0: status
*/
- .globl sun4v_ldc_mapin
- .type sun4v_ldc_mapin,#function
-sun4v_ldc_mapin:
+ENTRY(sun4v_ldc_mapin)
mov %o2, %g1
mov %o3, %g2
mov HV_FAST_LDC_MAPIN, %o5
@@ -621,20 +541,18 @@
stx %o2, [%g2]
retl
nop
- .size sun4v_ldc_mapin, .-sun4v_ldc_mapin
+ENDPROC(sun4v_ldc_mapin)
/* %o0: ra
*
* returns %o0: status
*/
- .globl sun4v_ldc_unmap
- .type sun4v_ldc_unmap,#function
-sun4v_ldc_unmap:
+ENTRY(sun4v_ldc_unmap)
mov HV_FAST_LDC_UNMAP, %o5
ta HV_FAST_TRAP
retl
nop
- .size sun4v_ldc_unmap, .-sun4v_ldc_unmap
+ENDPROC(sun4v_ldc_unmap)
/* %o0: channel
* %o1: cookie
@@ -642,14 +560,12 @@
*
* returns %o0: status
*/
- .globl sun4v_ldc_revoke
- .type sun4v_ldc_revoke,#function
-sun4v_ldc_revoke:
+ENTRY(sun4v_ldc_revoke)
mov HV_FAST_LDC_REVOKE, %o5
ta HV_FAST_TRAP
retl
nop
- .size sun4v_ldc_revoke, .-sun4v_ldc_revoke
+ENDPROC(sun4v_ldc_revoke)
/* %o0: device handle
* %o1: device INO
@@ -657,16 +573,14 @@
*
* returns %o0: status
*/
- .globl sun4v_vintr_get_cookie
- .type sun4v_vintr_get_cookie,#function
-sun4v_vintr_get_cookie:
+ENTRY(sun4v_vintr_get_cookie)
mov %o2, %g1
mov HV_FAST_VINTR_GET_COOKIE, %o5
ta HV_FAST_TRAP
stx %o1, [%g1]
retl
nop
- .size sun4v_vintr_get_cookie, .-sun4v_vintr_get_cookie
+ENDPROC(sun4v_vintr_get_cookie)
/* %o0: device handle
* %o1: device INO
@@ -674,14 +588,12 @@
*
* returns %o0: status
*/
- .globl sun4v_vintr_set_cookie
- .type sun4v_vintr_set_cookie,#function
-sun4v_vintr_set_cookie:
+ENTRY(sun4v_vintr_set_cookie)
mov HV_FAST_VINTR_SET_COOKIE, %o5
ta HV_FAST_TRAP
retl
nop
- .size sun4v_vintr_set_cookie, .-sun4v_vintr_set_cookie
+ENDPROC(sun4v_vintr_set_cookie)
/* %o0: device handle
* %o1: device INO
@@ -689,16 +601,14 @@
*
* returns %o0: status
*/
- .globl sun4v_vintr_get_valid
- .type sun4v_vintr_get_valid,#function
-sun4v_vintr_get_valid:
+ENTRY(sun4v_vintr_get_valid)
mov %o2, %g1
mov HV_FAST_VINTR_GET_VALID, %o5
ta HV_FAST_TRAP
stx %o1, [%g1]
retl
nop
- .size sun4v_vintr_get_valid, .-sun4v_vintr_get_valid
+ENDPROC(sun4v_vintr_get_valid)
/* %o0: device handle
* %o1: device INO
@@ -706,14 +616,12 @@
*
* returns %o0: status
*/
- .globl sun4v_vintr_set_valid
- .type sun4v_vintr_set_valid,#function
-sun4v_vintr_set_valid:
+ENTRY(sun4v_vintr_set_valid)
mov HV_FAST_VINTR_SET_VALID, %o5
ta HV_FAST_TRAP
retl
nop
- .size sun4v_vintr_set_valid, .-sun4v_vintr_set_valid
+ENDPROC(sun4v_vintr_set_valid)
/* %o0: device handle
* %o1: device INO
@@ -721,16 +629,14 @@
*
* returns %o0: status
*/
- .globl sun4v_vintr_get_state
- .type sun4v_vintr_get_state,#function
-sun4v_vintr_get_state:
+ENTRY(sun4v_vintr_get_state)
mov %o2, %g1
mov HV_FAST_VINTR_GET_STATE, %o5
ta HV_FAST_TRAP
stx %o1, [%g1]
retl
nop
- .size sun4v_vintr_get_state, .-sun4v_vintr_get_state
+ENDPROC(sun4v_vintr_get_state)
/* %o0: device handle
* %o1: device INO
@@ -738,14 +644,12 @@
*
* returns %o0: status
*/
- .globl sun4v_vintr_set_state
- .type sun4v_vintr_set_state,#function
-sun4v_vintr_set_state:
+ENTRY(sun4v_vintr_set_state)
mov HV_FAST_VINTR_SET_STATE, %o5
ta HV_FAST_TRAP
retl
nop
- .size sun4v_vintr_set_state, .-sun4v_vintr_set_state
+ENDPROC(sun4v_vintr_set_state)
/* %o0: device handle
* %o1: device INO
@@ -753,16 +657,14 @@
*
* returns %o0: status
*/
- .globl sun4v_vintr_get_target
- .type sun4v_vintr_get_target,#function
-sun4v_vintr_get_target:
+ENTRY(sun4v_vintr_get_target)
mov %o2, %g1
mov HV_FAST_VINTR_GET_TARGET, %o5
ta HV_FAST_TRAP
stx %o1, [%g1]
retl
nop
- .size sun4v_vintr_get_target, .-sun4v_vintr_get_target
+ENDPROC(sun4v_vintr_get_target)
/* %o0: device handle
* %o1: device INO
@@ -770,14 +672,12 @@
*
* returns %o0: status
*/
- .globl sun4v_vintr_set_target
- .type sun4v_vintr_set_target,#function
-sun4v_vintr_set_target:
+ENTRY(sun4v_vintr_set_target)
mov HV_FAST_VINTR_SET_TARGET, %o5
ta HV_FAST_TRAP
retl
nop
- .size sun4v_vintr_set_target, .-sun4v_vintr_set_target
+ENDPROC(sun4v_vintr_set_target)
/* %o0: NCS sub-function
* %o1: sub-function arg real-address
@@ -785,18 +685,14 @@
*
* returns %o0: status
*/
- .globl sun4v_ncs_request
- .type sun4v_ncs_request,#function
-sun4v_ncs_request:
+ENTRY(sun4v_ncs_request)
mov HV_FAST_NCS_REQUEST, %o5
ta HV_FAST_TRAP
retl
nop
- .size sun4v_ncs_request, .-sun4v_ncs_request
+ENDPROC(sun4v_ncs_request)
- .globl sun4v_svc_send
- .type sun4v_svc_send,#function
-sun4v_svc_send:
+ENTRY(sun4v_svc_send)
save %sp, -192, %sp
mov %i0, %o0
mov %i1, %o1
@@ -806,11 +702,9 @@
stx %o1, [%i3]
ret
restore
- .size sun4v_svc_send, .-sun4v_svc_send
+ENDPROC(sun4v_svc_send)
- .globl sun4v_svc_recv
- .type sun4v_svc_recv,#function
-sun4v_svc_recv:
+ENTRY(sun4v_svc_recv)
save %sp, -192, %sp
mov %i0, %o0
mov %i1, %o1
@@ -820,62 +714,50 @@
stx %o1, [%i3]
ret
restore
- .size sun4v_svc_recv, .-sun4v_svc_recv
+ENDPROC(sun4v_svc_recv)
- .globl sun4v_svc_getstatus
- .type sun4v_svc_getstatus,#function
-sun4v_svc_getstatus:
+ENTRY(sun4v_svc_getstatus)
mov HV_FAST_SVC_GETSTATUS, %o5
mov %o1, %o4
ta HV_FAST_TRAP
stx %o1, [%o4]
retl
nop
- .size sun4v_svc_getstatus, .-sun4v_svc_getstatus
+ENDPROC(sun4v_svc_getstatus)
- .globl sun4v_svc_setstatus
- .type sun4v_svc_setstatus,#function
-sun4v_svc_setstatus:
+ENTRY(sun4v_svc_setstatus)
mov HV_FAST_SVC_SETSTATUS, %o5
ta HV_FAST_TRAP
retl
nop
- .size sun4v_svc_setstatus, .-sun4v_svc_setstatus
+ENDPROC(sun4v_svc_setstatus)
- .globl sun4v_svc_clrstatus
- .type sun4v_svc_clrstatus,#function
-sun4v_svc_clrstatus:
+ENTRY(sun4v_svc_clrstatus)
mov HV_FAST_SVC_CLRSTATUS, %o5
ta HV_FAST_TRAP
retl
nop
- .size sun4v_svc_clrstatus, .-sun4v_svc_clrstatus
+ENDPROC(sun4v_svc_clrstatus)
- .globl sun4v_mmustat_conf
- .type sun4v_mmustat_conf,#function
-sun4v_mmustat_conf:
+ENTRY(sun4v_mmustat_conf)
mov %o1, %o4
mov HV_FAST_MMUSTAT_CONF, %o5
ta HV_FAST_TRAP
stx %o1, [%o4]
retl
nop
- .size sun4v_mmustat_conf, .-sun4v_mmustat_conf
+ENDPROC(sun4v_mmustat_conf)
- .globl sun4v_mmustat_info
- .type sun4v_mmustat_info,#function
-sun4v_mmustat_info:
+ENTRY(sun4v_mmustat_info)
mov %o0, %o4
mov HV_FAST_MMUSTAT_INFO, %o5
ta HV_FAST_TRAP
stx %o1, [%o4]
retl
nop
- .size sun4v_mmustat_info, .-sun4v_mmustat_info
+ENDPROC(sun4v_mmustat_info)
- .globl sun4v_mmu_demap_all
- .type sun4v_mmu_demap_all,#function
-sun4v_mmu_demap_all:
+ENTRY(sun4v_mmu_demap_all)
clr %o0
clr %o1
mov HV_MMU_ALL, %o2
@@ -883,4 +765,4 @@
ta HV_FAST_TRAP
retl
nop
- .size sun4v_mmu_demap_all, .-sun4v_mmu_demap_all
+ENDPROC(sun4v_mmu_demap_all)
diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c
index 7495bc7..52fc836 100644
--- a/arch/sparc64/kernel/irq.c
+++ b/arch/sparc64/kernel/irq.c
@@ -29,7 +29,6 @@
#include <asm/system.h>
#include <asm/irq.h>
#include <asm/io.h>
-#include <asm/sbus.h>
#include <asm/iommu.h>
#include <asm/upa.h>
#include <asm/oplib.h>
diff --git a/arch/sparc64/kernel/of_device.c b/arch/sparc64/kernel/of_device.c
index 100ebd5..0f616ae 100644
--- a/arch/sparc64/kernel/of_device.c
+++ b/arch/sparc64/kernel/of_device.c
@@ -55,15 +55,38 @@
}
EXPORT_SYMBOL(of_find_device_by_node);
-#ifdef CONFIG_PCI
-struct bus_type ebus_bus_type;
-EXPORT_SYMBOL(ebus_bus_type);
-#endif
+unsigned int irq_of_parse_and_map(struct device_node *node, int index)
+{
+ struct of_device *op = of_find_device_by_node(node);
-#ifdef CONFIG_SBUS
-struct bus_type sbus_bus_type;
-EXPORT_SYMBOL(sbus_bus_type);
-#endif
+ if (!op || index >= op->num_irqs)
+ return 0;
+
+ return op->irqs[index];
+}
+EXPORT_SYMBOL(irq_of_parse_and_map);
+
+/* Take the archdata values for IOMMU, STC, and HOSTDATA found in
+ * BUS and propagate to all child of_device objects.
+ */
+void of_propagate_archdata(struct of_device *bus)
+{
+ struct dev_archdata *bus_sd = &bus->dev.archdata;
+ struct device_node *bus_dp = bus->node;
+ struct device_node *dp;
+
+ for (dp = bus_dp->child; dp; dp = dp->sibling) {
+ struct of_device *op = of_find_device_by_node(dp);
+
+ op->dev.archdata.iommu = bus_sd->iommu;
+ op->dev.archdata.stc = bus_sd->stc;
+ op->dev.archdata.host_controller = bus_sd->host_controller;
+ op->dev.archdata.numa_node = bus_sd->numa_node;
+
+ if (dp->child)
+ of_propagate_archdata(op);
+ }
+}
struct bus_type of_platform_bus_type;
EXPORT_SYMBOL(of_platform_bus_type);
@@ -378,8 +401,7 @@
int na, int ns, int pna)
{
const u32 *ranges;
- unsigned int rlen;
- int rone;
+ int rone, rlen;
ranges = of_get_property(parent, "ranges", &rlen);
if (ranges == NULL || rlen == 0) {
@@ -421,8 +443,17 @@
/* If the parent is the dma node of an ISA bus, pass
* the translation up to the root.
+ *
+ * Some SBUS devices use intermediate nodes to express
+ * hierarchy within the device itself. These aren't
+ * real bus nodes, and don't have a 'ranges' property.
+ * But, we should still pass the translation work up
+ * to the SBUS itself.
*/
- if (!strcmp(pp->name, "dma"))
+ if (!strcmp(pp->name, "dma") ||
+ !strcmp(pp->name, "espdma") ||
+ !strcmp(pp->name, "ledma") ||
+ !strcmp(pp->name, "lebuffer"))
return 0;
/* Similarly for all PCI bridges, if we get this far
@@ -844,15 +875,6 @@
int err;
err = of_bus_type_init(&of_platform_bus_type, "of");
-#ifdef CONFIG_PCI
- if (!err)
- err = of_bus_type_init(&ebus_bus_type, "ebus");
-#endif
-#ifdef CONFIG_SBUS
- if (!err)
- err = of_bus_type_init(&sbus_bus_type, "sbus");
-#endif
-
if (!err)
scan_of_devices();
diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c
index 80dad76..242ac1c 100644
--- a/arch/sparc64/kernel/pci.c
+++ b/arch/sparc64/kernel/pci.c
@@ -18,32 +18,17 @@
#include <linux/msi.h>
#include <linux/irq.h>
#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/irq.h>
-#include <asm/ebus.h>
#include <asm/prom.h>
#include <asm/apb.h>
#include "pci_impl.h"
-#ifndef CONFIG_PCI
-/* A "nop" PCI implementation. */
-asmlinkage int sys_pciconfig_read(unsigned long bus, unsigned long dfn,
- unsigned long off, unsigned long len,
- unsigned char *buf)
-{
- return 0;
-}
-asmlinkage int sys_pciconfig_write(unsigned long bus, unsigned long dfn,
- unsigned long off, unsigned long len,
- unsigned char *buf)
-{
- return 0;
-}
-#else
-
/* List of all PCI controllers found in the system. */
struct pci_pbm_info *pci_pbm_root = NULL;
@@ -179,97 +164,6 @@
spin_unlock_irqrestore(&pci_poke_lock, flags);
}
-/* Probe for all PCI controllers in the system. */
-extern void sabre_init(struct device_node *, const char *);
-extern void psycho_init(struct device_node *, const char *);
-extern void schizo_init(struct device_node *, const char *);
-extern void schizo_plus_init(struct device_node *, const char *);
-extern void tomatillo_init(struct device_node *, const char *);
-extern void sun4v_pci_init(struct device_node *, const char *);
-extern void fire_pci_init(struct device_node *, const char *);
-
-static struct {
- char *model_name;
- void (*init)(struct device_node *, const char *);
-} pci_controller_table[] __initdata = {
- { "SUNW,sabre", sabre_init },
- { "pci108e,a000", sabre_init },
- { "pci108e,a001", sabre_init },
- { "SUNW,psycho", psycho_init },
- { "pci108e,8000", psycho_init },
- { "SUNW,schizo", schizo_init },
- { "pci108e,8001", schizo_init },
- { "SUNW,schizo+", schizo_plus_init },
- { "pci108e,8002", schizo_plus_init },
- { "SUNW,tomatillo", tomatillo_init },
- { "pci108e,a801", tomatillo_init },
- { "SUNW,sun4v-pci", sun4v_pci_init },
- { "pciex108e,80f0", fire_pci_init },
-};
-#define PCI_NUM_CONTROLLER_TYPES ARRAY_SIZE(pci_controller_table)
-
-static int __init pci_controller_init(const char *model_name, int namelen, struct device_node *dp)
-{
- int i;
-
- for (i = 0; i < PCI_NUM_CONTROLLER_TYPES; i++) {
- if (!strncmp(model_name,
- pci_controller_table[i].model_name,
- namelen)) {
- pci_controller_table[i].init(dp, model_name);
- return 1;
- }
- }
-
- return 0;
-}
-
-static int __init pci_controller_scan(int (*handler)(const char *, int, struct device_node *))
-{
- struct device_node *dp;
- int count = 0;
-
- for_each_node_by_name(dp, "pci") {
- struct property *prop;
- int len;
-
- prop = of_find_property(dp, "model", &len);
- if (!prop)
- prop = of_find_property(dp, "compatible", &len);
-
- if (prop) {
- const char *model = prop->value;
- int item_len = 0;
-
- /* Our value may be a multi-valued string in the
- * case of some compatible properties. For sanity,
- * only try the first one.
- */
- while (model[item_len] && len) {
- len--;
- item_len++;
- }
-
- if (handler(model, item_len, dp))
- count++;
- }
- }
-
- return count;
-}
-
-/* Find each controller in the system, attach and initialize
- * software state structure for each and link into the
- * pci_pbm_root. Setup the controller enough such
- * that bus scanning can be done.
- */
-static void __init pci_controller_probe(void)
-{
- printk("PCI: Probing for controllers.\n");
-
- pci_controller_scan(pci_controller_init);
-}
-
static int ofpci_verbose;
static int __init ofpci_debug(char *str)
@@ -348,11 +242,12 @@
}
}
-struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm,
- struct device_node *node,
- struct pci_bus *bus, int devfn)
+static struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm,
+ struct device_node *node,
+ struct pci_bus *bus, int devfn)
{
struct dev_archdata *sd;
+ struct of_device *op;
struct pci_dev *dev;
const char *type;
u32 class;
@@ -366,14 +261,17 @@
sd->stc = &pbm->stc;
sd->host_controller = pbm;
sd->prom_node = node;
- sd->op = of_find_device_by_node(node);
+ sd->op = op = of_find_device_by_node(node);
sd->numa_node = pbm->numa_node;
- sd = &sd->op->dev.archdata;
+ sd = &op->dev.archdata;
sd->iommu = pbm->iommu;
sd->stc = &pbm->stc;
sd->numa_node = pbm->numa_node;
+ if (!strcmp(node->name, "ebus"))
+ of_propagate_archdata(op);
+
type = of_get_property(node, "device_type", NULL);
if (type == NULL)
type = "";
@@ -775,15 +673,15 @@
pci_bus_register_of_sysfs(child_bus);
}
-struct pci_bus * __devinit pci_scan_one_pbm(struct pci_pbm_info *pbm)
+struct pci_bus * __devinit pci_scan_one_pbm(struct pci_pbm_info *pbm,
+ struct device *parent)
{
- struct device_node *node = pbm->prom_node;
+ struct device_node *node = pbm->op->node;
struct pci_bus *bus;
printk("PCI: Scanning PBM %s\n", node->full_name);
- /* XXX parent device? XXX */
- bus = pci_create_bus(NULL, pbm->pci_first_busno, pbm->pci_ops, pbm);
+ bus = pci_create_bus(parent, pbm->pci_first_busno, pbm->pci_ops, pbm);
if (!bus) {
printk(KERN_ERR "Failed to create bus for %s\n",
node->full_name);
@@ -802,32 +700,6 @@
return bus;
}
-static void __init pci_scan_each_controller_bus(void)
-{
- struct pci_pbm_info *pbm;
-
- for (pbm = pci_pbm_root; pbm; pbm = pbm->next)
- pbm->scan_bus(pbm);
-}
-
-extern void power_init(void);
-
-static int __init pcibios_init(void)
-{
- pci_controller_probe();
- if (pci_pbm_root == NULL)
- return 0;
-
- pci_scan_each_controller_bus();
-
- ebus_init();
- power_init();
-
- return 0;
-}
-
-subsys_initcall(pcibios_init);
-
void __devinit pcibios_fixup_bus(struct pci_bus *pbus)
{
struct pci_pbm_info *pbm = pbus->sysdata;
@@ -1105,14 +977,14 @@
EXPORT_SYMBOL(pcibus_to_node);
#endif
-/* Return the domain nuber for this pci bus */
+/* Return the domain number for this pci bus */
int pci_domain_nr(struct pci_bus *pbus)
{
struct pci_pbm_info *pbm = pbus->sysdata;
int ret;
- if (pbm == NULL || pbm->parent == NULL) {
+ if (!pbm) {
ret = -ENXIO;
} else {
ret = pbm->index;
@@ -1126,7 +998,7 @@
int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
{
struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller;
- int virt_irq;
+ unsigned int virt_irq;
if (!pbm->setup_msi_irq)
return -EINVAL;
@@ -1140,10 +1012,8 @@
struct pci_dev *pdev = entry->dev;
struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller;
- if (!pbm->teardown_msi_irq)
- return;
-
- return pbm->teardown_msi_irq(virt_irq, pdev);
+ if (pbm->teardown_msi_irq)
+ pbm->teardown_msi_irq(virt_irq, pdev);
}
#endif /* !(CONFIG_PCI_MSI) */
@@ -1215,5 +1085,3 @@
*start = rp->start - offset;
*end = rp->end - offset;
}
-
-#endif /* !(CONFIG_PCI) */
diff --git a/arch/sparc64/kernel/pci_common.c b/arch/sparc64/kernel/pci_common.c
index 09a5ec2..23b8808 100644
--- a/arch/sparc64/kernel/pci_common.c
+++ b/arch/sparc64/kernel/pci_common.c
@@ -314,12 +314,12 @@
void pci_get_pbm_props(struct pci_pbm_info *pbm)
{
- const u32 *val = of_get_property(pbm->prom_node, "bus-range", NULL);
+ const u32 *val = of_get_property(pbm->op->node, "bus-range", NULL);
pbm->pci_first_busno = val[0];
pbm->pci_last_busno = val[1];
- val = of_get_property(pbm->prom_node, "ino-bitmap", NULL);
+ val = of_get_property(pbm->op->node, "ino-bitmap", NULL);
if (val) {
pbm->ino_bitmap = (((u64)val[1] << 32UL) |
((u64)val[0] << 0UL));
@@ -365,7 +365,7 @@
static void pci_register_iommu_region(struct pci_pbm_info *pbm)
{
- const u32 *vdma = of_get_property(pbm->prom_node, "virtual-dma", NULL);
+ const u32 *vdma = of_get_property(pbm->op->node, "virtual-dma", NULL);
if (vdma) {
struct resource *rp = kmalloc(sizeof(*rp), GFP_KERNEL);
@@ -389,7 +389,7 @@
int num_pbm_ranges;
saw_mem = saw_io = 0;
- pbm_ranges = of_get_property(pbm->prom_node, "ranges", &i);
+ pbm_ranges = of_get_property(pbm->op->node, "ranges", &i);
if (!pbm_ranges) {
prom_printf("PCI: Fatal error, missing PBM ranges property "
" for %s\n",
diff --git a/arch/sparc64/kernel/pci_fire.c b/arch/sparc64/kernel/pci_fire.c
index d23bb6f..9462b68 100644
--- a/arch/sparc64/kernel/pci_fire.c
+++ b/arch/sparc64/kernel/pci_fire.c
@@ -8,34 +8,16 @@
#include <linux/init.h>
#include <linux/msi.h>
#include <linux/irq.h>
+#include <linux/of_device.h>
-#include <asm/oplib.h>
#include <asm/prom.h>
#include <asm/irq.h>
+#include <asm/upa.h>
#include "pci_impl.h"
-#define fire_read(__reg) \
-({ u64 __ret; \
- __asm__ __volatile__("ldxa [%1] %2, %0" \
- : "=r" (__ret) \
- : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \
- : "memory"); \
- __ret; \
-})
-#define fire_write(__reg, __val) \
- __asm__ __volatile__("stxa %0, [%1] %2" \
- : /* no outputs */ \
- : "r" (__val), "r" (__reg), \
- "i" (ASI_PHYS_BYPASS_EC_E) \
- : "memory")
-
-static void __init pci_fire_scan_bus(struct pci_pbm_info *pbm)
-{
- pbm->pci_bus = pci_scan_one_pbm(pbm);
-
- /* XXX register error interrupt handlers XXX */
-}
+#define DRIVER_NAME "fire"
+#define PFX DRIVER_NAME ": "
#define FIRE_IOMMU_CONTROL 0x40000UL
#define FIRE_IOMMU_TSBBASE 0x40008UL
@@ -69,21 +51,21 @@
/*
* Invalidate TLB Entries.
*/
- fire_write(iommu->iommu_flushinv, ~(u64)0);
+ upa_writeq(~(u64)0, iommu->iommu_flushinv);
err = iommu_table_init(iommu, tsbsize * 8 * 1024, vdma[0], dma_mask,
pbm->numa_node);
if (err)
return err;
- fire_write(iommu->iommu_tsbbase, __pa(iommu->page_table) | 0x7UL);
+ upa_writeq(__pa(iommu->page_table) | 0x7UL, iommu->iommu_tsbbase);
- control = fire_read(iommu->iommu_control);
+ control = upa_readq(iommu->iommu_control);
control |= (0x00000400 /* TSB cache snoop enable */ |
0x00000300 /* Cache mode */ |
0x00000002 /* Bypass enable */ |
0x00000001 /* Translation enable */);
- fire_write(iommu->iommu_control, control);
+ upa_writeq(control, iommu->iommu_control);
return 0;
}
@@ -165,7 +147,7 @@
static int pci_fire_get_head(struct pci_pbm_info *pbm, unsigned long msiqid,
unsigned long *head)
{
- *head = fire_read(pbm->pbm_regs + EVENT_QUEUE_HEAD(msiqid));
+ *head = upa_readq(pbm->pbm_regs + EVENT_QUEUE_HEAD(msiqid));
return 0;
}
@@ -191,8 +173,7 @@
*msi = msi_num = ((ep->word0 & MSIQ_WORD0_DATA0) >>
MSIQ_WORD0_DATA0_SHIFT);
- fire_write(pbm->pbm_regs + MSI_CLEAR(msi_num),
- MSI_CLEAR_EQWR_N);
+ upa_writeq(MSI_CLEAR_EQWR_N, pbm->pbm_regs + MSI_CLEAR(msi_num));
/* Clear the entry. */
ep->word0 &= ~MSIQ_WORD0_FMT_TYPE;
@@ -208,7 +189,7 @@
static int pci_fire_set_head(struct pci_pbm_info *pbm, unsigned long msiqid,
unsigned long head)
{
- fire_write(pbm->pbm_regs + EVENT_QUEUE_HEAD(msiqid), head);
+ upa_writeq(head, pbm->pbm_regs + EVENT_QUEUE_HEAD(msiqid));
return 0;
}
@@ -217,17 +198,16 @@
{
u64 val;
- val = fire_read(pbm->pbm_regs + MSI_MAP(msi));
+ val = upa_readq(pbm->pbm_regs + MSI_MAP(msi));
val &= ~(MSI_MAP_EQNUM);
val |= msiqid;
- fire_write(pbm->pbm_regs + MSI_MAP(msi), val);
+ upa_writeq(val, pbm->pbm_regs + MSI_MAP(msi));
- fire_write(pbm->pbm_regs + MSI_CLEAR(msi),
- MSI_CLEAR_EQWR_N);
+ upa_writeq(MSI_CLEAR_EQWR_N, pbm->pbm_regs + MSI_CLEAR(msi));
- val = fire_read(pbm->pbm_regs + MSI_MAP(msi));
+ val = upa_readq(pbm->pbm_regs + MSI_MAP(msi));
val |= MSI_MAP_VALID;
- fire_write(pbm->pbm_regs + MSI_MAP(msi), val);
+ upa_writeq(val, pbm->pbm_regs + MSI_MAP(msi));
return 0;
}
@@ -237,12 +217,12 @@
unsigned long msiqid;
u64 val;
- val = fire_read(pbm->pbm_regs + MSI_MAP(msi));
+ val = upa_readq(pbm->pbm_regs + MSI_MAP(msi));
msiqid = (val & MSI_MAP_EQNUM);
val &= ~MSI_MAP_VALID;
- fire_write(pbm->pbm_regs + MSI_MAP(msi), val);
+ upa_writeq(val, pbm->pbm_regs + MSI_MAP(msi));
return 0;
}
@@ -261,22 +241,19 @@
memset((char *)pages, 0, PAGE_SIZE << order);
pbm->msi_queues = (void *) pages;
- fire_write(pbm->pbm_regs + EVENT_QUEUE_BASE_ADDR_REG,
- (EVENT_QUEUE_BASE_ADDR_ALL_ONES |
- __pa(pbm->msi_queues)));
+ upa_writeq((EVENT_QUEUE_BASE_ADDR_ALL_ONES |
+ __pa(pbm->msi_queues)),
+ pbm->pbm_regs + EVENT_QUEUE_BASE_ADDR_REG);
- fire_write(pbm->pbm_regs + IMONDO_DATA0,
- pbm->portid << 6);
- fire_write(pbm->pbm_regs + IMONDO_DATA1, 0);
+ upa_writeq(pbm->portid << 6, pbm->pbm_regs + IMONDO_DATA0);
+ upa_writeq(0, pbm->pbm_regs + IMONDO_DATA1);
- fire_write(pbm->pbm_regs + MSI_32BIT_ADDR,
- pbm->msi32_start);
- fire_write(pbm->pbm_regs + MSI_64BIT_ADDR,
- pbm->msi64_start);
+ upa_writeq(pbm->msi32_start, pbm->pbm_regs + MSI_32BIT_ADDR);
+ upa_writeq(pbm->msi64_start, pbm->pbm_regs + MSI_64BIT_ADDR);
for (i = 0; i < pbm->msiq_num; i++) {
- fire_write(pbm->pbm_regs + EVENT_QUEUE_HEAD(i), 0);
- fire_write(pbm->pbm_regs + EVENT_QUEUE_TAIL(i), 0);
+ upa_writeq(0, pbm->pbm_regs + EVENT_QUEUE_HEAD(i));
+ upa_writeq(0, pbm->pbm_regs + EVENT_QUEUE_TAIL(i));
}
return 0;
@@ -310,9 +287,9 @@
/* XXX iterate amongst the 4 IRQ controllers XXX */
int_ctrlr = (1UL << 6);
- val = fire_read(imap_reg);
+ val = upa_readq(imap_reg);
val |= (1UL << 63) | int_ctrlr;
- fire_write(imap_reg, val);
+ upa_writeq(val, imap_reg);
fixup = ((pbm->portid << 6) | devino) - int_ctrlr;
@@ -320,9 +297,8 @@
if (!virt_irq)
return -ENOMEM;
- fire_write(pbm->pbm_regs +
- EVENT_QUEUE_CONTROL_SET(msiqid),
- EVENT_QUEUE_CONTROL_SET_EN);
+ upa_writeq(EVENT_QUEUE_CONTROL_SET_EN,
+ pbm->pbm_regs + EVENT_QUEUE_CONTROL_SET(msiqid));
return virt_irq;
}
@@ -390,77 +366,65 @@
{
u64 val;
- fire_write(pbm->controller_regs + FIRE_PARITY_CONTROL,
- FIRE_PARITY_ENAB);
+ upa_writeq(FIRE_PARITY_ENAB,
+ pbm->controller_regs + FIRE_PARITY_CONTROL);
- fire_write(pbm->controller_regs + FIRE_FATAL_RESET_CTL,
- (FIRE_FATAL_RESET_SPARE |
+ upa_writeq((FIRE_FATAL_RESET_SPARE |
FIRE_FATAL_RESET_MB |
FIRE_FATAL_RESET_CPE |
FIRE_FATAL_RESET_APE |
FIRE_FATAL_RESET_PIO |
FIRE_FATAL_RESET_JW |
FIRE_FATAL_RESET_JI |
- FIRE_FATAL_RESET_JR));
+ FIRE_FATAL_RESET_JR),
+ pbm->controller_regs + FIRE_FATAL_RESET_CTL);
- fire_write(pbm->controller_regs + FIRE_CORE_INTR_ENABLE, ~(u64)0);
+ upa_writeq(~(u64)0, pbm->controller_regs + FIRE_CORE_INTR_ENABLE);
- val = fire_read(pbm->pbm_regs + FIRE_TLU_CTRL);
+ val = upa_readq(pbm->pbm_regs + FIRE_TLU_CTRL);
val |= (FIRE_TLU_CTRL_TIM |
FIRE_TLU_CTRL_QDET |
FIRE_TLU_CTRL_CFG);
- fire_write(pbm->pbm_regs + FIRE_TLU_CTRL, val);
- fire_write(pbm->pbm_regs + FIRE_TLU_DEV_CTRL, 0);
- fire_write(pbm->pbm_regs + FIRE_TLU_LINK_CTRL,
- FIRE_TLU_LINK_CTRL_CLK);
+ upa_writeq(val, pbm->pbm_regs + FIRE_TLU_CTRL);
+ upa_writeq(0, pbm->pbm_regs + FIRE_TLU_DEV_CTRL);
+ upa_writeq(FIRE_TLU_LINK_CTRL_CLK,
+ pbm->pbm_regs + FIRE_TLU_LINK_CTRL);
- fire_write(pbm->pbm_regs + FIRE_LPU_RESET, 0);
- fire_write(pbm->pbm_regs + FIRE_LPU_LLCFG,
- FIRE_LPU_LLCFG_VC0);
- fire_write(pbm->pbm_regs + FIRE_LPU_FCTRL_UCTRL,
- (FIRE_LPU_FCTRL_UCTRL_N |
- FIRE_LPU_FCTRL_UCTRL_P));
- fire_write(pbm->pbm_regs + FIRE_LPU_TXL_FIFOP,
- ((0xffff << 16) | (0x0000 << 0)));
- fire_write(pbm->pbm_regs + FIRE_LPU_LTSSM_CFG2, 3000000);
- fire_write(pbm->pbm_regs + FIRE_LPU_LTSSM_CFG3, 500000);
- fire_write(pbm->pbm_regs + FIRE_LPU_LTSSM_CFG4,
- (2 << 16) | (140 << 8));
- fire_write(pbm->pbm_regs + FIRE_LPU_LTSSM_CFG5, 0);
+ upa_writeq(0, pbm->pbm_regs + FIRE_LPU_RESET);
+ upa_writeq(FIRE_LPU_LLCFG_VC0, pbm->pbm_regs + FIRE_LPU_LLCFG);
+ upa_writeq((FIRE_LPU_FCTRL_UCTRL_N | FIRE_LPU_FCTRL_UCTRL_P),
+ pbm->pbm_regs + FIRE_LPU_FCTRL_UCTRL);
+ upa_writeq(((0xffff << 16) | (0x0000 << 0)),
+ pbm->pbm_regs + FIRE_LPU_TXL_FIFOP);
+ upa_writeq(3000000, pbm->pbm_regs + FIRE_LPU_LTSSM_CFG2);
+ upa_writeq(500000, pbm->pbm_regs + FIRE_LPU_LTSSM_CFG3);
+ upa_writeq((2 << 16) | (140 << 8),
+ pbm->pbm_regs + FIRE_LPU_LTSSM_CFG4);
+ upa_writeq(0, pbm->pbm_regs + FIRE_LPU_LTSSM_CFG5);
- fire_write(pbm->pbm_regs + FIRE_DMC_IENAB, ~(u64)0);
- fire_write(pbm->pbm_regs + FIRE_DMC_DBG_SEL_A, 0);
- fire_write(pbm->pbm_regs + FIRE_DMC_DBG_SEL_B, 0);
+ upa_writeq(~(u64)0, pbm->pbm_regs + FIRE_DMC_IENAB);
+ upa_writeq(0, pbm->pbm_regs + FIRE_DMC_DBG_SEL_A);
+ upa_writeq(0, pbm->pbm_regs + FIRE_DMC_DBG_SEL_B);
- fire_write(pbm->pbm_regs + FIRE_PEC_IENAB, ~(u64)0);
+ upa_writeq(~(u64)0, pbm->pbm_regs + FIRE_PEC_IENAB);
}
-static int __init pci_fire_pbm_init(struct pci_controller_info *p,
- struct device_node *dp, u32 portid)
+static int __init pci_fire_pbm_init(struct pci_pbm_info *pbm,
+ struct of_device *op, u32 portid)
{
const struct linux_prom64_registers *regs;
- struct pci_pbm_info *pbm;
+ struct device_node *dp = op->node;
int err;
- if ((portid & 1) == 0)
- pbm = &p->pbm_A;
- else
- pbm = &p->pbm_B;
-
- pbm->next = pci_pbm_root;
- pci_pbm_root = pbm;
-
pbm->numa_node = -1;
- pbm->scan_bus = pci_fire_scan_bus;
pbm->pci_ops = &sun4u_pci_ops;
pbm->config_space_reg_bits = 12;
pbm->index = pci_num_pbms++;
pbm->portid = portid;
- pbm->parent = p;
- pbm->prom_node = dp;
+ pbm->op = op;
pbm->name = dp->full_name;
regs = of_get_property(dp, "reg", NULL);
@@ -481,53 +445,77 @@
pci_fire_msi_init(pbm);
+ pbm->pci_bus = pci_scan_one_pbm(pbm, &op->dev);
+
+ /* XXX register error interrupt handlers XXX */
+
+ pbm->next = pci_pbm_root;
+ pci_pbm_root = pbm;
+
return 0;
}
-static inline int portid_compare(u32 x, u32 y)
+static int __devinit fire_probe(struct of_device *op,
+ const struct of_device_id *match)
{
- if (x == (y ^ 1))
- return 1;
- return 0;
-}
-
-void __init fire_pci_init(struct device_node *dp, const char *model_name)
-{
- struct pci_controller_info *p;
- u32 portid = of_getintprop_default(dp, "portid", 0xff);
- struct iommu *iommu;
+ struct device_node *dp = op->node;
struct pci_pbm_info *pbm;
+ struct iommu *iommu;
+ u32 portid;
+ int err;
- for (pbm = pci_pbm_root; pbm; pbm = pbm->next) {
- if (portid_compare(pbm->portid, portid)) {
- if (pci_fire_pbm_init(pbm->parent, dp, portid))
- goto fatal_memory_error;
- return;
- }
+ portid = of_getintprop_default(dp, "portid", 0xff);
+
+ err = -ENOMEM;
+ pbm = kzalloc(sizeof(*pbm), GFP_KERNEL);
+ if (!pbm) {
+ printk(KERN_ERR PFX "Cannot allocate pci_pbminfo.\n");
+ goto out_err;
}
- p = kzalloc(sizeof(struct pci_controller_info), GFP_ATOMIC);
- if (!p)
- goto fatal_memory_error;
+ iommu = kzalloc(sizeof(struct iommu), GFP_KERNEL);
+ if (!iommu) {
+ printk(KERN_ERR PFX "Cannot allocate PBM iommu.\n");
+ goto out_free_controller;
+ }
- iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC);
- if (!iommu)
- goto fatal_memory_error;
+ pbm->iommu = iommu;
- p->pbm_A.iommu = iommu;
+ err = pci_fire_pbm_init(pbm, op, portid);
+ if (err)
+ goto out_free_iommu;
- iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC);
- if (!iommu)
- goto fatal_memory_error;
+ dev_set_drvdata(&op->dev, pbm);
- p->pbm_B.iommu = iommu;
+ return 0;
- if (pci_fire_pbm_init(p, dp, portid))
- goto fatal_memory_error;
+out_free_iommu:
+ kfree(pbm->iommu);
+
+out_free_controller:
+ kfree(pbm);
- return;
-
-fatal_memory_error:
- prom_printf("PCI_FIRE: Fatal memory allocation error.\n");
- prom_halt();
+out_err:
+ return err;
}
+
+static struct of_device_id __initdata fire_match[] = {
+ {
+ .name = "pci",
+ .compatible = "pciex108e,80f0",
+ },
+ {},
+};
+
+static struct of_platform_driver fire_driver = {
+ .name = DRIVER_NAME,
+ .match_table = fire_match,
+ .probe = fire_probe,
+};
+
+static int __init fire_init(void)
+{
+ return of_register_driver(&fire_driver, &of_bus_type);
+}
+
+subsys_initcall(fire_init);
diff --git a/arch/sparc64/kernel/pci_impl.h b/arch/sparc64/kernel/pci_impl.h
index c385d12..0318682 100644
--- a/arch/sparc64/kernel/pci_impl.h
+++ b/arch/sparc64/kernel/pci_impl.h
@@ -10,6 +10,7 @@
#include <linux/spinlock.h>
#include <linux/pci.h>
#include <linux/msi.h>
+#include <linux/of_device.h>
#include <asm/io.h>
#include <asm/prom.h>
#include <asm/iommu.h>
@@ -56,15 +57,11 @@
};
#endif
-struct pci_controller_info;
-
struct pci_pbm_info {
struct pci_pbm_info *next;
+ struct pci_pbm_info *sibling;
int index;
- /* PCI controller we sit under. */
- struct pci_controller_info *parent;
-
/* Physical address base of controller registers. */
unsigned long controller_regs;
@@ -94,7 +91,7 @@
char *name;
/* OBP specific information. */
- struct device_node *prom_node;
+ struct of_device *op;
u64 ino_bitmap;
/* PBM I/O and Memory space resources. */
@@ -107,6 +104,10 @@
/* This will be 12 on PCI-E controllers, 8 elsewhere. */
unsigned long config_space_reg_bits;
+ unsigned long pci_afsr;
+ unsigned long pci_afar;
+ unsigned long pci_csr;
+
/* State of 66MHz capabilities on this PBM. */
int is_66mhz_capable;
int all_devs_66mhz;
@@ -146,25 +147,19 @@
unsigned int pci_first_busno;
unsigned int pci_last_busno;
struct pci_bus *pci_bus;
- void (*scan_bus)(struct pci_pbm_info *);
struct pci_ops *pci_ops;
int numa_node;
};
-struct pci_controller_info {
- /* The PCI bus modules controlled by us. */
- struct pci_pbm_info pbm_A;
- struct pci_pbm_info pbm_B;
-};
-
extern struct pci_pbm_info *pci_pbm_root;
extern int pci_num_pbms;
/* PCI bus scanning and fixup support. */
extern void pci_get_pbm_props(struct pci_pbm_info *pbm);
-extern struct pci_bus *pci_scan_one_pbm(struct pci_pbm_info *pbm);
+extern struct pci_bus *pci_scan_one_pbm(struct pci_pbm_info *pbm,
+ struct device *parent);
extern void pci_determine_mem_io_space(struct pci_pbm_info *pbm);
/* Error reporting support. */
@@ -183,4 +178,8 @@
extern struct pci_ops sun4u_pci_ops;
extern struct pci_ops sun4v_pci_ops;
+extern volatile int pci_poke_in_progress;
+extern volatile int pci_poke_cpu;
+extern volatile int pci_poke_faulted;
+
#endif /* !(PCI_IMPL_H) */
diff --git a/arch/sparc64/kernel/pci_msi.c b/arch/sparc64/kernel/pci_msi.c
index 60c71e3..2e680f3 100644
--- a/arch/sparc64/kernel/pci_msi.c
+++ b/arch/sparc64/kernel/pci_msi.c
@@ -323,7 +323,7 @@
const u32 *val;
int len;
- val = of_get_property(pbm->prom_node, "#msi-eqs", &len);
+ val = of_get_property(pbm->op->node, "#msi-eqs", &len);
if (!val || len != 4)
goto no_msi;
pbm->msiq_num = *val;
@@ -346,16 +346,16 @@
u32 msi64_len;
} *arng;
- val = of_get_property(pbm->prom_node, "msi-eq-size", &len);
+ val = of_get_property(pbm->op->node, "msi-eq-size", &len);
if (!val || len != 4)
goto no_msi;
pbm->msiq_ent_count = *val;
- mqp = of_get_property(pbm->prom_node,
+ mqp = of_get_property(pbm->op->node,
"msi-eq-to-devino", &len);
if (!mqp)
- mqp = of_get_property(pbm->prom_node,
+ mqp = of_get_property(pbm->op->node,
"msi-eq-devino", &len);
if (!mqp || len != sizeof(struct msiq_prop))
goto no_msi;
@@ -363,27 +363,27 @@
pbm->msiq_first = mqp->first_msiq;
pbm->msiq_first_devino = mqp->first_devino;
- val = of_get_property(pbm->prom_node, "#msi", &len);
+ val = of_get_property(pbm->op->node, "#msi", &len);
if (!val || len != 4)
goto no_msi;
pbm->msi_num = *val;
- mrng = of_get_property(pbm->prom_node, "msi-ranges", &len);
+ mrng = of_get_property(pbm->op->node, "msi-ranges", &len);
if (!mrng || len != sizeof(struct msi_range_prop))
goto no_msi;
pbm->msi_first = mrng->first_msi;
- val = of_get_property(pbm->prom_node, "msi-data-mask", &len);
+ val = of_get_property(pbm->op->node, "msi-data-mask", &len);
if (!val || len != 4)
goto no_msi;
pbm->msi_data_mask = *val;
- val = of_get_property(pbm->prom_node, "msix-data-width", &len);
+ val = of_get_property(pbm->op->node, "msix-data-width", &len);
if (!val || len != 4)
goto no_msi;
pbm->msix_data_width = *val;
- arng = of_get_property(pbm->prom_node, "msi-address-ranges",
+ arng = of_get_property(pbm->op->node, "msi-address-ranges",
&len);
if (!arng || len != sizeof(struct addr_range_prop))
goto no_msi;
diff --git a/arch/sparc64/kernel/pci_psycho.c b/arch/sparc64/kernel/pci_psycho.c
index f85b6be..dfb3ec8 100644
--- a/arch/sparc64/kernel/pci_psycho.c
+++ b/arch/sparc64/kernel/pci_psycho.c
@@ -17,29 +17,14 @@
#include <asm/irq.h>
#include <asm/starfire.h>
#include <asm/prom.h>
-#include <asm/oplib.h>
+#include <asm/upa.h>
#include "pci_impl.h"
#include "iommu_common.h"
+#include "psycho_common.h"
-/* All PSYCHO registers are 64-bits. The following accessor
- * routines are how they are accessed. The REG parameter
- * is a physical address.
- */
-#define psycho_read(__reg) \
-({ u64 __ret; \
- __asm__ __volatile__("ldxa [%1] %2, %0" \
- : "=r" (__ret) \
- : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \
- : "memory"); \
- __ret; \
-})
-#define psycho_write(__reg, __val) \
- __asm__ __volatile__("stxa %0, [%1] %2" \
- : /* no outputs */ \
- : "r" (__val), "r" (__reg), \
- "i" (ASI_PHYS_BYPASS_EC_E) \
- : "memory")
+#define DRIVER_NAME "psycho"
+#define PFX DRIVER_NAME ": "
/* Misc. PSYCHO PCI controller register offsets and definitions. */
#define PSYCHO_CONTROL 0x0010UL
@@ -67,37 +52,7 @@
#define PSYCHO_PCICTRL_RESV4 0x00000000000000c0UL /* Reserved */
#define PSYCHO_PCICTRL_AEN 0x000000000000003fUL /* PCI DVMA Arbitration Enable */
-/* U2P Programmer's Manual, page 13-55, configuration space
- * address format:
- *
- * 32 24 23 16 15 11 10 8 7 2 1 0
- * ---------------------------------------------------------
- * |0 0 0 0 0 0 0 0 1| bus | device | function | reg | 0 0 |
- * ---------------------------------------------------------
- */
-#define PSYCHO_CONFIG_BASE(PBM) \
- ((PBM)->config_space | (1UL << 24))
-#define PSYCHO_CONFIG_ENCODE(BUS, DEVFN, REG) \
- (((unsigned long)(BUS) << 16) | \
- ((unsigned long)(DEVFN) << 8) | \
- ((unsigned long)(REG)))
-
-static void *psycho_pci_config_mkaddr(struct pci_pbm_info *pbm,
- unsigned char bus,
- unsigned int devfn,
- int where)
-{
- if (!pbm)
- return NULL;
- return (void *)
- (PSYCHO_CONFIG_BASE(pbm) |
- PSYCHO_CONFIG_ENCODE(bus, devfn, where));
-}
-
/* PSYCHO error handling support. */
-enum psycho_error_type {
- UE_ERR, CE_ERR, PCI_ERR
-};
/* Helper function of IOMMU error checking, which checks out
* the state of the streaming buffers. The IOMMU lock is
@@ -122,129 +77,10 @@
#define PSYCHO_STC_DATA_B 0xc000UL
#define PSYCHO_STC_ERR_A 0xb400UL
#define PSYCHO_STC_ERR_B 0xc400UL
-#define PSYCHO_STCERR_WRITE 0x0000000000000002UL /* Write Error */
-#define PSYCHO_STCERR_READ 0x0000000000000001UL /* Read Error */
#define PSYCHO_STC_TAG_A 0xb800UL
#define PSYCHO_STC_TAG_B 0xc800UL
-#define PSYCHO_STCTAG_PPN 0x0fffffff00000000UL /* Physical Page Number */
-#define PSYCHO_STCTAG_VPN 0x00000000ffffe000UL /* Virtual Page Number */
-#define PSYCHO_STCTAG_VALID 0x0000000000000002UL /* Valid */
-#define PSYCHO_STCTAG_WRITE 0x0000000000000001UL /* Writable */
#define PSYCHO_STC_LINE_A 0xb900UL
#define PSYCHO_STC_LINE_B 0xc900UL
-#define PSYCHO_STCLINE_LINDX 0x0000000001e00000UL /* LRU Index */
-#define PSYCHO_STCLINE_SPTR 0x00000000001f8000UL /* Dirty Data Start Pointer */
-#define PSYCHO_STCLINE_LADDR 0x0000000000007f00UL /* Line Address */
-#define PSYCHO_STCLINE_EPTR 0x00000000000000fcUL /* Dirty Data End Pointer */
-#define PSYCHO_STCLINE_VALID 0x0000000000000002UL /* Valid */
-#define PSYCHO_STCLINE_FOFN 0x0000000000000001UL /* Fetch Outstanding / Flush Necessary */
-
-static DEFINE_SPINLOCK(stc_buf_lock);
-static unsigned long stc_error_buf[128];
-static unsigned long stc_tag_buf[16];
-static unsigned long stc_line_buf[16];
-
-static void __psycho_check_one_stc(struct pci_pbm_info *pbm,
- int is_pbm_a)
-{
- struct strbuf *strbuf = &pbm->stc;
- unsigned long regbase = pbm->controller_regs;
- unsigned long err_base, tag_base, line_base;
- u64 control;
- int i;
-
- if (is_pbm_a) {
- err_base = regbase + PSYCHO_STC_ERR_A;
- tag_base = regbase + PSYCHO_STC_TAG_A;
- line_base = regbase + PSYCHO_STC_LINE_A;
- } else {
- err_base = regbase + PSYCHO_STC_ERR_B;
- tag_base = regbase + PSYCHO_STC_TAG_B;
- line_base = regbase + PSYCHO_STC_LINE_B;
- }
-
- spin_lock(&stc_buf_lock);
-
- /* This is __REALLY__ dangerous. When we put the
- * streaming buffer into diagnostic mode to probe
- * it's tags and error status, we _must_ clear all
- * of the line tag valid bits before re-enabling
- * the streaming buffer. If any dirty data lives
- * in the STC when we do this, we will end up
- * invalidating it before it has a chance to reach
- * main memory.
- */
- control = psycho_read(strbuf->strbuf_control);
- psycho_write(strbuf->strbuf_control,
- (control | PSYCHO_STRBUF_CTRL_DENAB));
- for (i = 0; i < 128; i++) {
- unsigned long val;
-
- val = psycho_read(err_base + (i * 8UL));
- psycho_write(err_base + (i * 8UL), 0UL);
- stc_error_buf[i] = val;
- }
- for (i = 0; i < 16; i++) {
- stc_tag_buf[i] = psycho_read(tag_base + (i * 8UL));
- stc_line_buf[i] = psycho_read(line_base + (i * 8UL));
- psycho_write(tag_base + (i * 8UL), 0UL);
- psycho_write(line_base + (i * 8UL), 0UL);
- }
-
- /* OK, state is logged, exit diagnostic mode. */
- psycho_write(strbuf->strbuf_control, control);
-
- for (i = 0; i < 16; i++) {
- int j, saw_error, first, last;
-
- saw_error = 0;
- first = i * 8;
- last = first + 8;
- for (j = first; j < last; j++) {
- unsigned long errval = stc_error_buf[j];
- if (errval != 0) {
- saw_error++;
- printk("%s: STC_ERR(%d)[wr(%d)rd(%d)]\n",
- pbm->name,
- j,
- (errval & PSYCHO_STCERR_WRITE) ? 1 : 0,
- (errval & PSYCHO_STCERR_READ) ? 1 : 0);
- }
- }
- if (saw_error != 0) {
- unsigned long tagval = stc_tag_buf[i];
- unsigned long lineval = stc_line_buf[i];
- printk("%s: STC_TAG(%d)[PA(%016lx)VA(%08lx)V(%d)W(%d)]\n",
- pbm->name,
- i,
- ((tagval & PSYCHO_STCTAG_PPN) >> 19UL),
- (tagval & PSYCHO_STCTAG_VPN),
- ((tagval & PSYCHO_STCTAG_VALID) ? 1 : 0),
- ((tagval & PSYCHO_STCTAG_WRITE) ? 1 : 0));
- printk("%s: STC_LINE(%d)[LIDX(%lx)SP(%lx)LADDR(%lx)EP(%lx)"
- "V(%d)FOFN(%d)]\n",
- pbm->name,
- i,
- ((lineval & PSYCHO_STCLINE_LINDX) >> 21UL),
- ((lineval & PSYCHO_STCLINE_SPTR) >> 15UL),
- ((lineval & PSYCHO_STCLINE_LADDR) >> 8UL),
- ((lineval & PSYCHO_STCLINE_EPTR) >> 2UL),
- ((lineval & PSYCHO_STCLINE_VALID) ? 1 : 0),
- ((lineval & PSYCHO_STCLINE_FOFN) ? 1 : 0));
- }
- }
-
- spin_unlock(&stc_buf_lock);
-}
-
-static void __psycho_check_stc_error(struct pci_pbm_info *pbm,
- unsigned long afsr,
- unsigned long afar,
- enum psycho_error_type type)
-{
- __psycho_check_one_stc(pbm,
- (pbm == &pbm->parent->pbm_A));
-}
/* When an Uncorrectable Error or a PCI Error happens, we
* interrogate the IOMMU state to see if it is the cause.
@@ -271,122 +107,7 @@
#define PSYCHO_IOMMU_TSBBASE 0x0208UL
#define PSYCHO_IOMMU_FLUSH 0x0210UL
#define PSYCHO_IOMMU_TAG 0xa580UL
-#define PSYCHO_IOMMU_TAG_ERRSTS (0x3UL << 23UL)
-#define PSYCHO_IOMMU_TAG_ERR (0x1UL << 22UL)
-#define PSYCHO_IOMMU_TAG_WRITE (0x1UL << 21UL)
-#define PSYCHO_IOMMU_TAG_STREAM (0x1UL << 20UL)
-#define PSYCHO_IOMMU_TAG_SIZE (0x1UL << 19UL)
-#define PSYCHO_IOMMU_TAG_VPAGE 0x7ffffUL
#define PSYCHO_IOMMU_DATA 0xa600UL
-#define PSYCHO_IOMMU_DATA_VALID (1UL << 30UL)
-#define PSYCHO_IOMMU_DATA_CACHE (1UL << 28UL)
-#define PSYCHO_IOMMU_DATA_PPAGE 0xfffffffUL
-static void psycho_check_iommu_error(struct pci_pbm_info *pbm,
- unsigned long afsr,
- unsigned long afar,
- enum psycho_error_type type)
-{
- struct iommu *iommu = pbm->iommu;
- unsigned long iommu_tag[16];
- unsigned long iommu_data[16];
- unsigned long flags;
- u64 control;
- int i;
-
- spin_lock_irqsave(&iommu->lock, flags);
- control = psycho_read(iommu->iommu_control);
- if (control & PSYCHO_IOMMU_CTRL_XLTEERR) {
- char *type_string;
-
- /* Clear the error encountered bit. */
- control &= ~PSYCHO_IOMMU_CTRL_XLTEERR;
- psycho_write(iommu->iommu_control, control);
-
- switch((control & PSYCHO_IOMMU_CTRL_XLTESTAT) >> 25UL) {
- case 0:
- type_string = "Protection Error";
- break;
- case 1:
- type_string = "Invalid Error";
- break;
- case 2:
- type_string = "TimeOut Error";
- break;
- case 3:
- default:
- type_string = "ECC Error";
- break;
- };
- printk("%s: IOMMU Error, type[%s]\n",
- pbm->name, type_string);
-
- /* Put the IOMMU into diagnostic mode and probe
- * it's TLB for entries with error status.
- *
- * It is very possible for another DVMA to occur
- * while we do this probe, and corrupt the system
- * further. But we are so screwed at this point
- * that we are likely to crash hard anyways, so
- * get as much diagnostic information to the
- * console as we can.
- */
- psycho_write(iommu->iommu_control,
- control | PSYCHO_IOMMU_CTRL_DENAB);
- for (i = 0; i < 16; i++) {
- unsigned long base = pbm->controller_regs;
-
- iommu_tag[i] =
- psycho_read(base + PSYCHO_IOMMU_TAG + (i * 8UL));
- iommu_data[i] =
- psycho_read(base + PSYCHO_IOMMU_DATA + (i * 8UL));
-
- /* Now clear out the entry. */
- psycho_write(base + PSYCHO_IOMMU_TAG + (i * 8UL), 0);
- psycho_write(base + PSYCHO_IOMMU_DATA + (i * 8UL), 0);
- }
-
- /* Leave diagnostic mode. */
- psycho_write(iommu->iommu_control, control);
-
- for (i = 0; i < 16; i++) {
- unsigned long tag, data;
-
- tag = iommu_tag[i];
- if (!(tag & PSYCHO_IOMMU_TAG_ERR))
- continue;
-
- data = iommu_data[i];
- switch((tag & PSYCHO_IOMMU_TAG_ERRSTS) >> 23UL) {
- case 0:
- type_string = "Protection Error";
- break;
- case 1:
- type_string = "Invalid Error";
- break;
- case 2:
- type_string = "TimeOut Error";
- break;
- case 3:
- default:
- type_string = "ECC Error";
- break;
- };
- printk("%s: IOMMU TAG(%d)[error(%s) wr(%d) str(%d) sz(%dK) vpg(%08lx)]\n",
- pbm->name, i, type_string,
- ((tag & PSYCHO_IOMMU_TAG_WRITE) ? 1 : 0),
- ((tag & PSYCHO_IOMMU_TAG_STREAM) ? 1 : 0),
- ((tag & PSYCHO_IOMMU_TAG_SIZE) ? 64 : 8),
- (tag & PSYCHO_IOMMU_TAG_VPAGE) << IOMMU_PAGE_SHIFT);
- printk("%s: IOMMU DATA(%d)[valid(%d) cache(%d) ppg(%016lx)]\n",
- pbm->name, i,
- ((data & PSYCHO_IOMMU_DATA_VALID) ? 1 : 0),
- ((data & PSYCHO_IOMMU_DATA_CACHE) ? 1 : 0),
- (data & PSYCHO_IOMMU_DATA_PPAGE) << IOMMU_PAGE_SHIFT);
- }
- }
- __psycho_check_stc_error(pbm, afsr, afar, type);
- spin_unlock_irqrestore(&iommu->lock, flags);
-}
/* Uncorrectable Errors. Cause of the error and the address are
* recorded in the UE_AFSR and UE_AFAR of PSYCHO. They are errors
@@ -410,15 +131,14 @@
static irqreturn_t psycho_ue_intr(int irq, void *dev_id)
{
struct pci_pbm_info *pbm = dev_id;
- struct pci_controller_info *p = pbm->parent;
unsigned long afsr_reg = pbm->controller_regs + PSYCHO_UE_AFSR;
unsigned long afar_reg = pbm->controller_regs + PSYCHO_UE_AFAR;
unsigned long afsr, afar, error_bits;
int reported;
/* Latch uncorrectable error status. */
- afar = psycho_read(afar_reg);
- afsr = psycho_read(afsr_reg);
+ afar = upa_readq(afar_reg);
+ afsr = upa_readq(afsr_reg);
/* Clear the primary/secondary error status bits. */
error_bits = afsr &
@@ -426,7 +146,7 @@
PSYCHO_UEAFSR_SPIO | PSYCHO_UEAFSR_SDRD | PSYCHO_UEAFSR_SDWR);
if (!error_bits)
return IRQ_NONE;
- psycho_write(afsr_reg, error_bits);
+ upa_writeq(error_bits, afsr_reg);
/* Log the error. */
printk("%s: Uncorrectable Error, primary error type[%s]\n",
@@ -463,8 +183,9 @@
printk("]\n");
/* Interrogate both IOMMUs for error status. */
- psycho_check_iommu_error(&p->pbm_A, afsr, afar, UE_ERR);
- psycho_check_iommu_error(&p->pbm_B, afsr, afar, UE_ERR);
+ psycho_check_iommu_error(pbm, afsr, afar, UE_ERR);
+ if (pbm->sibling)
+ psycho_check_iommu_error(pbm->sibling, afsr, afar, UE_ERR);
return IRQ_HANDLED;
}
@@ -495,8 +216,8 @@
int reported;
/* Latch error status. */
- afar = psycho_read(afar_reg);
- afsr = psycho_read(afsr_reg);
+ afar = upa_readq(afar_reg);
+ afsr = upa_readq(afsr_reg);
/* Clear primary/secondary error status bits. */
error_bits = afsr &
@@ -504,7 +225,7 @@
PSYCHO_CEAFSR_SPIO | PSYCHO_CEAFSR_SDRD | PSYCHO_CEAFSR_SDWR);
if (!error_bits)
return IRQ_NONE;
- psycho_write(afsr_reg, error_bits);
+ upa_writeq(error_bits, afsr_reg);
/* Log the error. */
printk("%s: Correctable Error, primary error type[%s]\n",
@@ -554,164 +275,9 @@
*/
#define PSYCHO_PCI_AFSR_A 0x2010UL
#define PSYCHO_PCI_AFSR_B 0x4010UL
-#define PSYCHO_PCIAFSR_PMA 0x8000000000000000UL /* Primary Master Abort Error */
-#define PSYCHO_PCIAFSR_PTA 0x4000000000000000UL /* Primary Target Abort Error */
-#define PSYCHO_PCIAFSR_PRTRY 0x2000000000000000UL /* Primary Excessive Retries */
-#define PSYCHO_PCIAFSR_PPERR 0x1000000000000000UL /* Primary Parity Error */
-#define PSYCHO_PCIAFSR_SMA 0x0800000000000000UL /* Secondary Master Abort Error */
-#define PSYCHO_PCIAFSR_STA 0x0400000000000000UL /* Secondary Target Abort Error */
-#define PSYCHO_PCIAFSR_SRTRY 0x0200000000000000UL /* Secondary Excessive Retries */
-#define PSYCHO_PCIAFSR_SPERR 0x0100000000000000UL /* Secondary Parity Error */
-#define PSYCHO_PCIAFSR_RESV1 0x00ff000000000000UL /* Reserved */
-#define PSYCHO_PCIAFSR_BMSK 0x0000ffff00000000UL /* Bytemask of failed transfer */
-#define PSYCHO_PCIAFSR_BLK 0x0000000080000000UL /* Trans was block operation */
-#define PSYCHO_PCIAFSR_RESV2 0x0000000040000000UL /* Reserved */
-#define PSYCHO_PCIAFSR_MID 0x000000003e000000UL /* MID causing the error */
-#define PSYCHO_PCIAFSR_RESV3 0x0000000001ffffffUL /* Reserved */
#define PSYCHO_PCI_AFAR_A 0x2018UL
#define PSYCHO_PCI_AFAR_B 0x4018UL
-static irqreturn_t psycho_pcierr_intr_other(struct pci_pbm_info *pbm, int is_pbm_a)
-{
- unsigned long csr_reg, csr, csr_error_bits;
- irqreturn_t ret = IRQ_NONE;
- u16 stat, *addr;
-
- if (is_pbm_a) {
- csr_reg = pbm->controller_regs + PSYCHO_PCIA_CTRL;
- } else {
- csr_reg = pbm->controller_regs + PSYCHO_PCIB_CTRL;
- }
- csr = psycho_read(csr_reg);
- csr_error_bits =
- csr & (PSYCHO_PCICTRL_SBH_ERR | PSYCHO_PCICTRL_SERR);
- if (csr_error_bits) {
- /* Clear the errors. */
- psycho_write(csr_reg, csr);
-
- /* Log 'em. */
- if (csr_error_bits & PSYCHO_PCICTRL_SBH_ERR)
- printk("%s: PCI streaming byte hole error asserted.\n",
- pbm->name);
- if (csr_error_bits & PSYCHO_PCICTRL_SERR)
- printk("%s: PCI SERR signal asserted.\n", pbm->name);
- ret = IRQ_HANDLED;
- }
- addr = psycho_pci_config_mkaddr(pbm, pbm->pci_first_busno,
- 0, PCI_STATUS);
- pci_config_read16(addr, &stat);
- if (stat & (PCI_STATUS_PARITY |
- PCI_STATUS_SIG_TARGET_ABORT |
- PCI_STATUS_REC_TARGET_ABORT |
- PCI_STATUS_REC_MASTER_ABORT |
- PCI_STATUS_SIG_SYSTEM_ERROR)) {
- printk("%s: PCI bus error, PCI_STATUS[%04x]\n",
- pbm->name, stat);
- pci_config_write16(addr, 0xffff);
- ret = IRQ_HANDLED;
- }
- return ret;
-}
-
-static irqreturn_t psycho_pcierr_intr(int irq, void *dev_id)
-{
- struct pci_pbm_info *pbm = dev_id;
- struct pci_controller_info *p = pbm->parent;
- unsigned long afsr_reg, afar_reg;
- unsigned long afsr, afar, error_bits;
- int is_pbm_a, reported;
-
- is_pbm_a = (pbm == &pbm->parent->pbm_A);
- if (is_pbm_a) {
- afsr_reg = p->pbm_A.controller_regs + PSYCHO_PCI_AFSR_A;
- afar_reg = p->pbm_A.controller_regs + PSYCHO_PCI_AFAR_A;
- } else {
- afsr_reg = p->pbm_A.controller_regs + PSYCHO_PCI_AFSR_B;
- afar_reg = p->pbm_A.controller_regs + PSYCHO_PCI_AFAR_B;
- }
-
- /* Latch error status. */
- afar = psycho_read(afar_reg);
- afsr = psycho_read(afsr_reg);
-
- /* Clear primary/secondary error status bits. */
- error_bits = afsr &
- (PSYCHO_PCIAFSR_PMA | PSYCHO_PCIAFSR_PTA |
- PSYCHO_PCIAFSR_PRTRY | PSYCHO_PCIAFSR_PPERR |
- PSYCHO_PCIAFSR_SMA | PSYCHO_PCIAFSR_STA |
- PSYCHO_PCIAFSR_SRTRY | PSYCHO_PCIAFSR_SPERR);
- if (!error_bits)
- return psycho_pcierr_intr_other(pbm, is_pbm_a);
- psycho_write(afsr_reg, error_bits);
-
- /* Log the error. */
- printk("%s: PCI Error, primary error type[%s]\n",
- pbm->name,
- (((error_bits & PSYCHO_PCIAFSR_PMA) ?
- "Master Abort" :
- ((error_bits & PSYCHO_PCIAFSR_PTA) ?
- "Target Abort" :
- ((error_bits & PSYCHO_PCIAFSR_PRTRY) ?
- "Excessive Retries" :
- ((error_bits & PSYCHO_PCIAFSR_PPERR) ?
- "Parity Error" : "???"))))));
- printk("%s: bytemask[%04lx] UPA_MID[%02lx] was_block(%d)\n",
- pbm->name,
- (afsr & PSYCHO_PCIAFSR_BMSK) >> 32UL,
- (afsr & PSYCHO_PCIAFSR_MID) >> 25UL,
- (afsr & PSYCHO_PCIAFSR_BLK) ? 1 : 0);
- printk("%s: PCI AFAR [%016lx]\n", pbm->name, afar);
- printk("%s: PCI Secondary errors [", pbm->name);
- reported = 0;
- if (afsr & PSYCHO_PCIAFSR_SMA) {
- reported++;
- printk("(Master Abort)");
- }
- if (afsr & PSYCHO_PCIAFSR_STA) {
- reported++;
- printk("(Target Abort)");
- }
- if (afsr & PSYCHO_PCIAFSR_SRTRY) {
- reported++;
- printk("(Excessive Retries)");
- }
- if (afsr & PSYCHO_PCIAFSR_SPERR) {
- reported++;
- printk("(Parity Error)");
- }
- if (!reported)
- printk("(none)");
- printk("]\n");
-
- /* For the error types shown, scan PBM's PCI bus for devices
- * which have logged that error type.
- */
-
- /* If we see a Target Abort, this could be the result of an
- * IOMMU translation error of some sort. It is extremely
- * useful to log this information as usually it indicates
- * a bug in the IOMMU support code or a PCI device driver.
- */
- if (error_bits & (PSYCHO_PCIAFSR_PTA | PSYCHO_PCIAFSR_STA)) {
- psycho_check_iommu_error(pbm, afsr, afar, PCI_ERR);
- pci_scan_for_target_abort(pbm, pbm->pci_bus);
- }
- if (error_bits & (PSYCHO_PCIAFSR_PMA | PSYCHO_PCIAFSR_SMA))
- pci_scan_for_master_abort(pbm, pbm->pci_bus);
-
- /* For excessive retries, PSYCHO/PBM will abort the device
- * and there is no way to specifically check for excessive
- * retries in the config space status registers. So what
- * we hope is that we'll catch it via the master/target
- * abort events.
- */
-
- if (error_bits & (PSYCHO_PCIAFSR_PPERR | PSYCHO_PCIAFSR_SPERR))
- pci_scan_for_parity_error(pbm, pbm->pci_bus);
-
- return IRQ_HANDLED;
-}
-
/* XXX What about PowerFail/PowerManagement??? -DaveM */
#define PSYCHO_ECC_CTRL 0x0020
#define PSYCHO_ECCCTRL_EE 0x8000000000000000UL /* Enable ECC Checking */
@@ -719,7 +285,7 @@
#define PSYCHO_ECCCTRL_CE 0x2000000000000000UL /* Enable CE INterrupts */
static void psycho_register_error_handlers(struct pci_pbm_info *pbm)
{
- struct of_device *op = of_find_device_by_node(pbm->prom_node);
+ struct of_device *op = of_find_device_by_node(pbm->op->node);
unsigned long base = pbm->controller_regs;
u64 tmp;
int err;
@@ -762,27 +328,26 @@
"err=%d\n", pbm->name, err);
/* Enable UE and CE interrupts for controller. */
- psycho_write(base + PSYCHO_ECC_CTRL,
- (PSYCHO_ECCCTRL_EE |
- PSYCHO_ECCCTRL_UE |
- PSYCHO_ECCCTRL_CE));
+ upa_writeq((PSYCHO_ECCCTRL_EE |
+ PSYCHO_ECCCTRL_UE |
+ PSYCHO_ECCCTRL_CE), base + PSYCHO_ECC_CTRL);
/* Enable PCI Error interrupts and clear error
* bits for each PBM.
*/
- tmp = psycho_read(base + PSYCHO_PCIA_CTRL);
+ tmp = upa_readq(base + PSYCHO_PCIA_CTRL);
tmp |= (PSYCHO_PCICTRL_SERR |
PSYCHO_PCICTRL_SBH_ERR |
PSYCHO_PCICTRL_EEN);
tmp &= ~(PSYCHO_PCICTRL_SBH_INT);
- psycho_write(base + PSYCHO_PCIA_CTRL, tmp);
+ upa_writeq(tmp, base + PSYCHO_PCIA_CTRL);
- tmp = psycho_read(base + PSYCHO_PCIB_CTRL);
+ tmp = upa_readq(base + PSYCHO_PCIB_CTRL);
tmp |= (PSYCHO_PCICTRL_SERR |
PSYCHO_PCICTRL_SBH_ERR |
PSYCHO_PCICTRL_EEN);
tmp &= ~(PSYCHO_PCICTRL_SBH_INT);
- psycho_write(base + PSYCHO_PCIB_CTRL, tmp);
+ upa_writeq(tmp, base + PSYCHO_PCIB_CTRL);
}
/* PSYCHO boot time probing and initialization. */
@@ -803,11 +368,12 @@
pci_config_write8(addr, 64);
}
-static void __init psycho_scan_bus(struct pci_pbm_info *pbm)
+static void __init psycho_scan_bus(struct pci_pbm_info *pbm,
+ struct device *parent)
{
pbm_config_busmastering(pbm);
pbm->is_66mhz_capable = 0;
- pbm->pci_bus = pci_scan_one_pbm(pbm);
+ pbm->pci_bus = pci_scan_one_pbm(pbm, parent);
/* After the PCI bus scan is complete, we can register
* the error interrupt handlers.
@@ -815,61 +381,6 @@
psycho_register_error_handlers(pbm);
}
-static int psycho_iommu_init(struct pci_pbm_info *pbm)
-{
- struct iommu *iommu = pbm->iommu;
- unsigned long i;
- u64 control;
- int err;
-
- /* Register addresses. */
- iommu->iommu_control = pbm->controller_regs + PSYCHO_IOMMU_CONTROL;
- iommu->iommu_tsbbase = pbm->controller_regs + PSYCHO_IOMMU_TSBBASE;
- iommu->iommu_flush = pbm->controller_regs + PSYCHO_IOMMU_FLUSH;
- iommu->iommu_tags = iommu->iommu_flush + (0xa580UL - 0x0210UL);
-
- /* PSYCHO's IOMMU lacks ctx flushing. */
- iommu->iommu_ctxflush = 0;
-
- /* We use the main control register of PSYCHO as the write
- * completion register.
- */
- iommu->write_complete_reg = pbm->controller_regs + PSYCHO_CONTROL;
-
- /*
- * Invalidate TLB Entries.
- */
- control = psycho_read(pbm->controller_regs + PSYCHO_IOMMU_CONTROL);
- control |= PSYCHO_IOMMU_CTRL_DENAB;
- psycho_write(pbm->controller_regs + PSYCHO_IOMMU_CONTROL, control);
- for(i = 0; i < 16; i++) {
- psycho_write(pbm->controller_regs + PSYCHO_IOMMU_TAG + (i * 8UL), 0);
- psycho_write(pbm->controller_regs + PSYCHO_IOMMU_DATA + (i * 8UL), 0);
- }
-
- /* Leave diag mode enabled for full-flushing done
- * in pci_iommu.c
- */
- err = iommu_table_init(iommu, IO_TSB_SIZE, 0xc0000000, 0xffffffff,
- pbm->numa_node);
- if (err)
- return err;
-
- psycho_write(pbm->controller_regs + PSYCHO_IOMMU_TSBBASE,
- __pa(iommu->page_table));
-
- control = psycho_read(pbm->controller_regs + PSYCHO_IOMMU_CONTROL);
- control &= ~(PSYCHO_IOMMU_CTRL_TSBSZ | PSYCHO_IOMMU_CTRL_TBWSZ);
- control |= (PSYCHO_IOMMU_TSBSZ_128K | PSYCHO_IOMMU_CTRL_ENAB);
- psycho_write(pbm->controller_regs + PSYCHO_IOMMU_CONTROL, control);
-
- /* If necessary, hook us up for starfire IRQ translations. */
- if (this_is_starfire)
- starfire_hookup(pbm->portid);
-
- return 0;
-}
-
#define PSYCHO_IRQ_RETRY 0x1a00UL
#define PSYCHO_PCIA_DIAG 0x2020UL
#define PSYCHO_PCIB_DIAG 0x4020UL
@@ -886,28 +397,28 @@
{
u64 tmp;
- psycho_write(pbm->controller_regs + PSYCHO_IRQ_RETRY, 5);
+ upa_writeq(5, pbm->controller_regs + PSYCHO_IRQ_RETRY);
/* Enable arbiter for all PCI slots. */
- tmp = psycho_read(pbm->controller_regs + PSYCHO_PCIA_CTRL);
+ tmp = upa_readq(pbm->controller_regs + PSYCHO_PCIA_CTRL);
tmp |= PSYCHO_PCICTRL_AEN;
- psycho_write(pbm->controller_regs + PSYCHO_PCIA_CTRL, tmp);
+ upa_writeq(tmp, pbm->controller_regs + PSYCHO_PCIA_CTRL);
- tmp = psycho_read(pbm->controller_regs + PSYCHO_PCIB_CTRL);
+ tmp = upa_readq(pbm->controller_regs + PSYCHO_PCIB_CTRL);
tmp |= PSYCHO_PCICTRL_AEN;
- psycho_write(pbm->controller_regs + PSYCHO_PCIB_CTRL, tmp);
+ upa_writeq(tmp, pbm->controller_regs + PSYCHO_PCIB_CTRL);
/* Disable DMA write / PIO read synchronization on
* both PCI bus segments.
* [ U2P Erratum 1243770, STP2223BGA data sheet ]
*/
- tmp = psycho_read(pbm->controller_regs + PSYCHO_PCIA_DIAG);
+ tmp = upa_readq(pbm->controller_regs + PSYCHO_PCIA_DIAG);
tmp |= PSYCHO_PCIDIAG_DDWSYNC;
- psycho_write(pbm->controller_regs + PSYCHO_PCIA_DIAG, tmp);
+ upa_writeq(tmp, pbm->controller_regs + PSYCHO_PCIA_DIAG);
- tmp = psycho_read(pbm->controller_regs + PSYCHO_PCIB_DIAG);
+ tmp = upa_readq(pbm->controller_regs + PSYCHO_PCIB_DIAG);
tmp |= PSYCHO_PCIDIAG_DDWSYNC;
- psycho_write(pbm->controller_regs + PSYCHO_PCIB_DIAG, tmp);
+ upa_writeq(tmp, pbm->controller_regs + PSYCHO_PCIB_DIAG);
}
static void psycho_pbm_strbuf_init(struct pci_pbm_info *pbm,
@@ -920,10 +431,16 @@
pbm->stc.strbuf_control = base + PSYCHO_STRBUF_CONTROL_A;
pbm->stc.strbuf_pflush = base + PSYCHO_STRBUF_FLUSH_A;
pbm->stc.strbuf_fsync = base + PSYCHO_STRBUF_FSYNC_A;
+ pbm->stc.strbuf_err_stat = base + PSYCHO_STC_ERR_A;
+ pbm->stc.strbuf_tag_diag = base + PSYCHO_STC_TAG_A;
+ pbm->stc.strbuf_line_diag= base + PSYCHO_STC_LINE_A;
} else {
pbm->stc.strbuf_control = base + PSYCHO_STRBUF_CONTROL_B;
pbm->stc.strbuf_pflush = base + PSYCHO_STRBUF_FLUSH_B;
pbm->stc.strbuf_fsync = base + PSYCHO_STRBUF_FSYNC_B;
+ pbm->stc.strbuf_err_stat = base + PSYCHO_STC_ERR_B;
+ pbm->stc.strbuf_tag_diag = base + PSYCHO_STC_TAG_B;
+ pbm->stc.strbuf_line_diag= base + PSYCHO_STC_LINE_B;
}
/* PSYCHO's streaming buffer lacks ctx flushing. */
pbm->stc.strbuf_ctxflush = 0;
@@ -946,7 +463,7 @@
*/
#undef PSYCHO_STRBUF_RERUN_ENABLE
#undef PSYCHO_STRBUF_RERUN_DISABLE
- control = psycho_read(pbm->stc.strbuf_control);
+ control = upa_readq(pbm->stc.strbuf_control);
control |= PSYCHO_STRBUF_CTRL_ENAB;
control &= ~(PSYCHO_STRBUF_CTRL_LENAB | PSYCHO_STRBUF_CTRL_LPTR);
#ifdef PSYCHO_STRBUF_RERUN_ENABLE
@@ -956,7 +473,7 @@
control |= PSYCHO_STRBUF_CTRL_RRDIS;
#endif
#endif
- psycho_write(pbm->stc.strbuf_control, control);
+ upa_writeq(control, pbm->stc.strbuf_control);
pbm->stc.strbuf_enabled = 1;
}
@@ -968,111 +485,134 @@
#define PSYCHO_MEMSPACE_B 0x180000000UL
#define PSYCHO_MEMSPACE_SIZE 0x07fffffffUL
-static void __init psycho_pbm_init(struct pci_controller_info *p,
- struct device_node *dp, int is_pbm_a)
+static void __init psycho_pbm_init(struct pci_pbm_info *pbm,
+ struct of_device *op, int is_pbm_a)
{
- struct property *prop;
+ psycho_pbm_init_common(pbm, op, "PSYCHO", PBM_CHIP_TYPE_PSYCHO);
+ psycho_pbm_strbuf_init(pbm, is_pbm_a);
+ psycho_scan_bus(pbm, &op->dev);
+}
+
+static struct pci_pbm_info * __devinit psycho_find_sibling(u32 upa_portid)
+{
struct pci_pbm_info *pbm;
- if (is_pbm_a)
- pbm = &p->pbm_A;
- else
- pbm = &p->pbm_B;
-
- pbm->next = pci_pbm_root;
- pci_pbm_root = pbm;
-
- pbm->numa_node = -1;
-
- pbm->scan_bus = psycho_scan_bus;
- pbm->pci_ops = &sun4u_pci_ops;
- pbm->config_space_reg_bits = 8;
-
- pbm->index = pci_num_pbms++;
-
- pbm->chip_type = PBM_CHIP_TYPE_PSYCHO;
- pbm->chip_version = 0;
- prop = of_find_property(dp, "version#", NULL);
- if (prop)
- pbm->chip_version = *(int *) prop->value;
- pbm->chip_revision = 0;
- prop = of_find_property(dp, "module-revision#", NULL);
- if (prop)
- pbm->chip_revision = *(int *) prop->value;
-
- pbm->parent = p;
- pbm->prom_node = dp;
- pbm->name = dp->full_name;
-
- printk("%s: PSYCHO PCI Bus Module ver[%x:%x]\n",
- pbm->name,
- pbm->chip_version, pbm->chip_revision);
-
- pci_determine_mem_io_space(pbm);
-
- pci_get_pbm_props(pbm);
-
- psycho_pbm_strbuf_init(pbm, is_pbm_a);
+ for (pbm = pci_pbm_root; pbm; pbm = pbm->next) {
+ if (pbm->portid == upa_portid)
+ return pbm;
+ }
+ return NULL;
}
#define PSYCHO_CONFIGSPACE 0x001000000UL
-void __init psycho_init(struct device_node *dp, char *model_name)
+static int __devinit psycho_probe(struct of_device *op,
+ const struct of_device_id *match)
{
- struct linux_prom64_registers *pr_regs;
- struct pci_controller_info *p;
+ const struct linux_prom64_registers *pr_regs;
+ struct device_node *dp = op->node;
struct pci_pbm_info *pbm;
struct iommu *iommu;
- struct property *prop;
+ int is_pbm_a, err;
u32 upa_portid;
- int is_pbm_a;
- upa_portid = 0xff;
- prop = of_find_property(dp, "upa-portid", NULL);
- if (prop)
- upa_portid = *(u32 *) prop->value;
+ upa_portid = of_getintprop_default(dp, "upa-portid", 0xff);
- for (pbm = pci_pbm_root; pbm; pbm = pbm->next) {
- struct pci_controller_info *p = pbm->parent;
+ err = -ENOMEM;
+ pbm = kzalloc(sizeof(*pbm), GFP_KERNEL);
+ if (!pbm) {
+ printk(KERN_ERR PFX "Cannot allocate pci_pbm_info.\n");
+ goto out_err;
+ }
- if (p->pbm_A.portid == upa_portid) {
- is_pbm_a = (p->pbm_A.prom_node == NULL);
- psycho_pbm_init(p, dp, is_pbm_a);
- return;
+ pbm->sibling = psycho_find_sibling(upa_portid);
+ if (pbm->sibling) {
+ iommu = pbm->sibling->iommu;
+ } else {
+ iommu = kzalloc(sizeof(struct iommu), GFP_KERNEL);
+ if (!iommu) {
+ printk(KERN_ERR PFX "Cannot allocate PBM iommu.\n");
+ goto out_free_controller;
}
}
- p = kzalloc(sizeof(struct pci_controller_info), GFP_ATOMIC);
- if (!p)
- goto fatal_memory_error;
- iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC);
- if (!iommu)
- goto fatal_memory_error;
+ pbm->iommu = iommu;
+ pbm->portid = upa_portid;
- p->pbm_A.iommu = p->pbm_B.iommu = iommu;
-
- p->pbm_A.portid = upa_portid;
- p->pbm_B.portid = upa_portid;
-
- prop = of_find_property(dp, "reg", NULL);
- pr_regs = prop->value;
-
- p->pbm_A.controller_regs = pr_regs[2].phys_addr;
- p->pbm_B.controller_regs = pr_regs[2].phys_addr;
-
- p->pbm_A.config_space = p->pbm_B.config_space =
- (pr_regs[2].phys_addr + PSYCHO_CONFIGSPACE);
-
- psycho_controller_hwinit(&p->pbm_A);
-
- if (psycho_iommu_init(&p->pbm_A))
- goto fatal_memory_error;
+ pr_regs = of_get_property(dp, "reg", NULL);
+ err = -ENODEV;
+ if (!pr_regs) {
+ printk(KERN_ERR PFX "No reg property.\n");
+ goto out_free_iommu;
+ }
is_pbm_a = ((pr_regs[0].phys_addr & 0x6000) == 0x2000);
- psycho_pbm_init(p, dp, is_pbm_a);
- return;
-fatal_memory_error:
- prom_printf("PSYCHO: Fatal memory allocation error.\n");
- prom_halt();
+ pbm->controller_regs = pr_regs[2].phys_addr;
+ pbm->config_space = (pr_regs[2].phys_addr + PSYCHO_CONFIGSPACE);
+
+ if (is_pbm_a) {
+ pbm->pci_afsr = pbm->controller_regs + PSYCHO_PCI_AFSR_A;
+ pbm->pci_afar = pbm->controller_regs + PSYCHO_PCI_AFAR_A;
+ pbm->pci_csr = pbm->controller_regs + PSYCHO_PCIA_CTRL;
+ } else {
+ pbm->pci_afsr = pbm->controller_regs + PSYCHO_PCI_AFSR_B;
+ pbm->pci_afar = pbm->controller_regs + PSYCHO_PCI_AFAR_B;
+ pbm->pci_csr = pbm->controller_regs + PSYCHO_PCIB_CTRL;
+ }
+
+ psycho_controller_hwinit(pbm);
+ if (!pbm->sibling) {
+ err = psycho_iommu_init(pbm, 128, 0xc0000000,
+ 0xffffffff, PSYCHO_CONTROL);
+ if (err)
+ goto out_free_iommu;
+
+ /* If necessary, hook us up for starfire IRQ translations. */
+ if (this_is_starfire)
+ starfire_hookup(pbm->portid);
+ }
+
+ psycho_pbm_init(pbm, op, is_pbm_a);
+
+ pbm->next = pci_pbm_root;
+ pci_pbm_root = pbm;
+
+ if (pbm->sibling)
+ pbm->sibling->sibling = pbm;
+
+ dev_set_drvdata(&op->dev, pbm);
+
+ return 0;
+
+out_free_iommu:
+ if (!pbm->sibling)
+ kfree(pbm->iommu);
+
+out_free_controller:
+ kfree(pbm);
+
+out_err:
+ return err;
}
+
+static struct of_device_id __initdata psycho_match[] = {
+ {
+ .name = "pci",
+ .compatible = "pci108e,8000",
+ },
+ {},
+};
+
+static struct of_platform_driver psycho_driver = {
+ .name = DRIVER_NAME,
+ .match_table = psycho_match,
+ .probe = psycho_probe,
+};
+
+static int __init psycho_init(void)
+{
+ return of_register_driver(&psycho_driver, &of_bus_type);
+}
+
+subsys_initcall(psycho_init);
diff --git a/arch/sparc64/kernel/pci_sabre.c b/arch/sparc64/kernel/pci_sabre.c
index ade5184..713257b 100644
--- a/arch/sparc64/kernel/pci_sabre.c
+++ b/arch/sparc64/kernel/pci_sabre.c
@@ -16,31 +16,15 @@
#include <asm/apb.h>
#include <asm/iommu.h>
#include <asm/irq.h>
-#include <asm/smp.h>
-#include <asm/oplib.h>
#include <asm/prom.h>
+#include <asm/upa.h>
#include "pci_impl.h"
#include "iommu_common.h"
+#include "psycho_common.h"
-/* All SABRE registers are 64-bits. The following accessor
- * routines are how they are accessed. The REG parameter
- * is a physical address.
- */
-#define sabre_read(__reg) \
-({ u64 __ret; \
- __asm__ __volatile__("ldxa [%1] %2, %0" \
- : "=r" (__ret) \
- : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \
- : "memory"); \
- __ret; \
-})
-#define sabre_write(__reg, __val) \
- __asm__ __volatile__("stxa %0, [%1] %2" \
- : /* no outputs */ \
- : "r" (__val), "r" (__reg), \
- "i" (ASI_PHYS_BYPASS_EC_E) \
- : "memory")
+#define DRIVER_NAME "sabre"
+#define PFX DRIVER_NAME ": "
/* SABRE PCI controller register offsets and definitions. */
#define SABRE_UE_AFSR 0x0030UL
@@ -208,95 +192,6 @@
static int hummingbird_p;
static struct pci_bus *sabre_root_bus;
-/* SABRE error handling support. */
-static void sabre_check_iommu_error(struct pci_pbm_info *pbm,
- unsigned long afsr,
- unsigned long afar)
-{
- struct iommu *iommu = pbm->iommu;
- unsigned long iommu_tag[16];
- unsigned long iommu_data[16];
- unsigned long flags;
- u64 control;
- int i;
-
- spin_lock_irqsave(&iommu->lock, flags);
- control = sabre_read(iommu->iommu_control);
- if (control & SABRE_IOMMUCTRL_ERR) {
- char *type_string;
-
- /* Clear the error encountered bit.
- * NOTE: On Sabre this is write 1 to clear,
- * which is different from Psycho.
- */
- sabre_write(iommu->iommu_control, control);
- switch((control & SABRE_IOMMUCTRL_ERRSTS) >> 25UL) {
- case 1:
- type_string = "Invalid Error";
- break;
- case 3:
- type_string = "ECC Error";
- break;
- default:
- type_string = "Unknown";
- break;
- };
- printk("%s: IOMMU Error, type[%s]\n",
- pbm->name, type_string);
-
- /* Enter diagnostic mode and probe for error'd
- * entries in the IOTLB.
- */
- control &= ~(SABRE_IOMMUCTRL_ERRSTS | SABRE_IOMMUCTRL_ERR);
- sabre_write(iommu->iommu_control,
- (control | SABRE_IOMMUCTRL_DENAB));
- for (i = 0; i < 16; i++) {
- unsigned long base = pbm->controller_regs;
-
- iommu_tag[i] =
- sabre_read(base + SABRE_IOMMU_TAG + (i * 8UL));
- iommu_data[i] =
- sabre_read(base + SABRE_IOMMU_DATA + (i * 8UL));
- sabre_write(base + SABRE_IOMMU_TAG + (i * 8UL), 0);
- sabre_write(base + SABRE_IOMMU_DATA + (i * 8UL), 0);
- }
- sabre_write(iommu->iommu_control, control);
-
- for (i = 0; i < 16; i++) {
- unsigned long tag, data;
-
- tag = iommu_tag[i];
- if (!(tag & SABRE_IOMMUTAG_ERR))
- continue;
-
- data = iommu_data[i];
- switch((tag & SABRE_IOMMUTAG_ERRSTS) >> 23UL) {
- case 1:
- type_string = "Invalid Error";
- break;
- case 3:
- type_string = "ECC Error";
- break;
- default:
- type_string = "Unknown";
- break;
- };
- printk("%s: IOMMU TAG(%d)[RAW(%016lx)error(%s)wr(%d)sz(%dK)vpg(%08lx)]\n",
- pbm->name, i, tag, type_string,
- ((tag & SABRE_IOMMUTAG_WRITE) ? 1 : 0),
- ((tag & SABRE_IOMMUTAG_SIZE) ? 64 : 8),
- ((tag & SABRE_IOMMUTAG_VPN) << IOMMU_PAGE_SHIFT));
- printk("%s: IOMMU DATA(%d)[RAW(%016lx)valid(%d)used(%d)cache(%d)ppg(%016lx)\n",
- pbm->name, i, data,
- ((data & SABRE_IOMMUDATA_VALID) ? 1 : 0),
- ((data & SABRE_IOMMUDATA_USED) ? 1 : 0),
- ((data & SABRE_IOMMUDATA_CACHE) ? 1 : 0),
- ((data & SABRE_IOMMUDATA_PPN) << IOMMU_PAGE_SHIFT));
- }
- }
- spin_unlock_irqrestore(&iommu->lock, flags);
-}
-
static irqreturn_t sabre_ue_intr(int irq, void *dev_id)
{
struct pci_pbm_info *pbm = dev_id;
@@ -306,8 +201,8 @@
int reported;
/* Latch uncorrectable error status. */
- afar = sabre_read(afar_reg);
- afsr = sabre_read(afsr_reg);
+ afar = upa_readq(afar_reg);
+ afsr = upa_readq(afsr_reg);
/* Clear the primary/secondary error status bits. */
error_bits = afsr &
@@ -316,7 +211,7 @@
SABRE_UEAFSR_SDTE | SABRE_UEAFSR_PDTE);
if (!error_bits)
return IRQ_NONE;
- sabre_write(afsr_reg, error_bits);
+ upa_writeq(error_bits, afsr_reg);
/* Log the error. */
printk("%s: Uncorrectable Error, primary error type[%s%s]\n",
@@ -352,7 +247,7 @@
printk("]\n");
/* Interrogate IOMMU for error status. */
- sabre_check_iommu_error(pbm, afsr, afar);
+ psycho_check_iommu_error(pbm, afsr, afar, UE_ERR);
return IRQ_HANDLED;
}
@@ -366,8 +261,8 @@
int reported;
/* Latch error status. */
- afar = sabre_read(afar_reg);
- afsr = sabre_read(afsr_reg);
+ afar = upa_readq(afar_reg);
+ afsr = upa_readq(afsr_reg);
/* Clear primary/secondary error status bits. */
error_bits = afsr &
@@ -375,7 +270,7 @@
SABRE_CEAFSR_SDRD | SABRE_CEAFSR_SDWR);
if (!error_bits)
return IRQ_NONE;
- sabre_write(afsr_reg, error_bits);
+ upa_writeq(error_bits, afsr_reg);
/* Log the error. */
printk("%s: Correctable Error, primary error type[%s]\n",
@@ -413,136 +308,9 @@
return IRQ_HANDLED;
}
-static irqreturn_t sabre_pcierr_intr_other(struct pci_pbm_info *pbm)
-{
- unsigned long csr_reg, csr, csr_error_bits;
- irqreturn_t ret = IRQ_NONE;
- u16 stat;
-
- csr_reg = pbm->controller_regs + SABRE_PCICTRL;
- csr = sabre_read(csr_reg);
- csr_error_bits =
- csr & SABRE_PCICTRL_SERR;
- if (csr_error_bits) {
- /* Clear the errors. */
- sabre_write(csr_reg, csr);
-
- /* Log 'em. */
- if (csr_error_bits & SABRE_PCICTRL_SERR)
- printk("%s: PCI SERR signal asserted.\n",
- pbm->name);
- ret = IRQ_HANDLED;
- }
- pci_bus_read_config_word(sabre_root_bus, 0,
- PCI_STATUS, &stat);
- if (stat & (PCI_STATUS_PARITY |
- PCI_STATUS_SIG_TARGET_ABORT |
- PCI_STATUS_REC_TARGET_ABORT |
- PCI_STATUS_REC_MASTER_ABORT |
- PCI_STATUS_SIG_SYSTEM_ERROR)) {
- printk("%s: PCI bus error, PCI_STATUS[%04x]\n",
- pbm->name, stat);
- pci_bus_write_config_word(sabre_root_bus, 0,
- PCI_STATUS, 0xffff);
- ret = IRQ_HANDLED;
- }
- return ret;
-}
-
-static irqreturn_t sabre_pcierr_intr(int irq, void *dev_id)
-{
- struct pci_pbm_info *pbm = dev_id;
- unsigned long afsr_reg, afar_reg;
- unsigned long afsr, afar, error_bits;
- int reported;
-
- afsr_reg = pbm->controller_regs + SABRE_PIOAFSR;
- afar_reg = pbm->controller_regs + SABRE_PIOAFAR;
-
- /* Latch error status. */
- afar = sabre_read(afar_reg);
- afsr = sabre_read(afsr_reg);
-
- /* Clear primary/secondary error status bits. */
- error_bits = afsr &
- (SABRE_PIOAFSR_PMA | SABRE_PIOAFSR_PTA |
- SABRE_PIOAFSR_PRTRY | SABRE_PIOAFSR_PPERR |
- SABRE_PIOAFSR_SMA | SABRE_PIOAFSR_STA |
- SABRE_PIOAFSR_SRTRY | SABRE_PIOAFSR_SPERR);
- if (!error_bits)
- return sabre_pcierr_intr_other(pbm);
- sabre_write(afsr_reg, error_bits);
-
- /* Log the error. */
- printk("%s: PCI Error, primary error type[%s]\n",
- pbm->name,
- (((error_bits & SABRE_PIOAFSR_PMA) ?
- "Master Abort" :
- ((error_bits & SABRE_PIOAFSR_PTA) ?
- "Target Abort" :
- ((error_bits & SABRE_PIOAFSR_PRTRY) ?
- "Excessive Retries" :
- ((error_bits & SABRE_PIOAFSR_PPERR) ?
- "Parity Error" : "???"))))));
- printk("%s: bytemask[%04lx] was_block(%d)\n",
- pbm->name,
- (afsr & SABRE_PIOAFSR_BMSK) >> 32UL,
- (afsr & SABRE_PIOAFSR_BLK) ? 1 : 0);
- printk("%s: PCI AFAR [%016lx]\n", pbm->name, afar);
- printk("%s: PCI Secondary errors [", pbm->name);
- reported = 0;
- if (afsr & SABRE_PIOAFSR_SMA) {
- reported++;
- printk("(Master Abort)");
- }
- if (afsr & SABRE_PIOAFSR_STA) {
- reported++;
- printk("(Target Abort)");
- }
- if (afsr & SABRE_PIOAFSR_SRTRY) {
- reported++;
- printk("(Excessive Retries)");
- }
- if (afsr & SABRE_PIOAFSR_SPERR) {
- reported++;
- printk("(Parity Error)");
- }
- if (!reported)
- printk("(none)");
- printk("]\n");
-
- /* For the error types shown, scan both PCI buses for devices
- * which have logged that error type.
- */
-
- /* If we see a Target Abort, this could be the result of an
- * IOMMU translation error of some sort. It is extremely
- * useful to log this information as usually it indicates
- * a bug in the IOMMU support code or a PCI device driver.
- */
- if (error_bits & (SABRE_PIOAFSR_PTA | SABRE_PIOAFSR_STA)) {
- sabre_check_iommu_error(pbm, afsr, afar);
- pci_scan_for_target_abort(pbm, pbm->pci_bus);
- }
- if (error_bits & (SABRE_PIOAFSR_PMA | SABRE_PIOAFSR_SMA))
- pci_scan_for_master_abort(pbm, pbm->pci_bus);
-
- /* For excessive retries, SABRE/PBM will abort the device
- * and there is no way to specifically check for excessive
- * retries in the config space status registers. So what
- * we hope is that we'll catch it via the master/target
- * abort events.
- */
-
- if (error_bits & (SABRE_PIOAFSR_PPERR | SABRE_PIOAFSR_SPERR))
- pci_scan_for_parity_error(pbm, pbm->pci_bus);
-
- return IRQ_HANDLED;
-}
-
static void sabre_register_error_handlers(struct pci_pbm_info *pbm)
{
- struct device_node *dp = pbm->prom_node;
+ struct device_node *dp = pbm->op->node;
struct of_device *op;
unsigned long base = pbm->controller_regs;
u64 tmp;
@@ -568,33 +336,34 @@
* registering the handler so that we don't get spurious
* interrupts.
*/
- sabre_write(base + SABRE_UE_AFSR,
- (SABRE_UEAFSR_PDRD | SABRE_UEAFSR_PDWR |
- SABRE_UEAFSR_SDRD | SABRE_UEAFSR_SDWR |
- SABRE_UEAFSR_SDTE | SABRE_UEAFSR_PDTE));
+ upa_writeq((SABRE_UEAFSR_PDRD | SABRE_UEAFSR_PDWR |
+ SABRE_UEAFSR_SDRD | SABRE_UEAFSR_SDWR |
+ SABRE_UEAFSR_SDTE | SABRE_UEAFSR_PDTE),
+ base + SABRE_UE_AFSR);
err = request_irq(op->irqs[1], sabre_ue_intr, 0, "SABRE_UE", pbm);
if (err)
printk(KERN_WARNING "%s: Couldn't register UE, err=%d.\n",
pbm->name, err);
- sabre_write(base + SABRE_CE_AFSR,
- (SABRE_CEAFSR_PDRD | SABRE_CEAFSR_PDWR |
- SABRE_CEAFSR_SDRD | SABRE_CEAFSR_SDWR));
+ upa_writeq((SABRE_CEAFSR_PDRD | SABRE_CEAFSR_PDWR |
+ SABRE_CEAFSR_SDRD | SABRE_CEAFSR_SDWR),
+ base + SABRE_CE_AFSR);
+
err = request_irq(op->irqs[2], sabre_ce_intr, 0, "SABRE_CE", pbm);
if (err)
printk(KERN_WARNING "%s: Couldn't register CE, err=%d.\n",
pbm->name, err);
- err = request_irq(op->irqs[0], sabre_pcierr_intr, 0,
+ err = request_irq(op->irqs[0], psycho_pcierr_intr, 0,
"SABRE_PCIERR", pbm);
if (err)
printk(KERN_WARNING "%s: Couldn't register PCIERR, err=%d.\n",
pbm->name, err);
- tmp = sabre_read(base + SABRE_PCICTRL);
+ tmp = upa_readq(base + SABRE_PCICTRL);
tmp |= SABRE_PCICTRL_ERREN;
- sabre_write(base + SABRE_PCICTRL, tmp);
+ upa_writeq(tmp, base + SABRE_PCICTRL);
}
static void apb_init(struct pci_bus *sabre_bus)
@@ -633,7 +402,8 @@
}
}
-static void __init sabre_scan_bus(struct pci_pbm_info *pbm)
+static void __init sabre_scan_bus(struct pci_pbm_info *pbm,
+ struct device *parent)
{
static int once;
@@ -656,12 +426,12 @@
* to live at bus 0.
*/
if (once != 0) {
- prom_printf("SABRE: Multiple controllers unsupported.\n");
- prom_halt();
+ printk(KERN_ERR PFX "Multiple controllers unsupported.\n");
+ return;
}
once++;
- pbm->pci_bus = pci_scan_one_pbm(pbm);
+ pbm->pci_bus = pci_scan_one_pbm(pbm, parent);
if (!pbm->pci_bus)
return;
@@ -672,133 +442,58 @@
sabre_register_error_handlers(pbm);
}
-static int sabre_iommu_init(struct pci_pbm_info *pbm,
- int tsbsize, unsigned long dvma_offset,
- u32 dma_mask)
+static void __init sabre_pbm_init(struct pci_pbm_info *pbm,
+ struct of_device *op)
{
- struct iommu *iommu = pbm->iommu;
- unsigned long i;
- u64 control;
- int err;
-
- /* Register addresses. */
- iommu->iommu_control = pbm->controller_regs + SABRE_IOMMU_CONTROL;
- iommu->iommu_tsbbase = pbm->controller_regs + SABRE_IOMMU_TSBBASE;
- iommu->iommu_flush = pbm->controller_regs + SABRE_IOMMU_FLUSH;
- iommu->iommu_tags = iommu->iommu_flush + (0xa580UL - 0x0210UL);
- iommu->write_complete_reg = pbm->controller_regs + SABRE_WRSYNC;
- /* Sabre's IOMMU lacks ctx flushing. */
- iommu->iommu_ctxflush = 0;
-
- /* Invalidate TLB Entries. */
- control = sabre_read(pbm->controller_regs + SABRE_IOMMU_CONTROL);
- control |= SABRE_IOMMUCTRL_DENAB;
- sabre_write(pbm->controller_regs + SABRE_IOMMU_CONTROL, control);
-
- for(i = 0; i < 16; i++) {
- sabre_write(pbm->controller_regs + SABRE_IOMMU_TAG + (i * 8UL), 0);
- sabre_write(pbm->controller_regs + SABRE_IOMMU_DATA + (i * 8UL), 0);
- }
-
- /* Leave diag mode enabled for full-flushing done
- * in pci_iommu.c
- */
- err = iommu_table_init(iommu, tsbsize * 1024 * 8,
- dvma_offset, dma_mask, pbm->numa_node);
- if (err)
- return err;
-
- sabre_write(pbm->controller_regs + SABRE_IOMMU_TSBBASE,
- __pa(iommu->page_table));
-
- control = sabre_read(pbm->controller_regs + SABRE_IOMMU_CONTROL);
- control &= ~(SABRE_IOMMUCTRL_TSBSZ | SABRE_IOMMUCTRL_TBWSZ);
- control |= SABRE_IOMMUCTRL_ENAB;
- switch(tsbsize) {
- case 64:
- control |= SABRE_IOMMU_TSBSZ_64K;
- break;
- case 128:
- control |= SABRE_IOMMU_TSBSZ_128K;
- break;
- default:
- prom_printf("iommu_init: Illegal TSB size %d\n", tsbsize);
- prom_halt();
- break;
- }
- sabre_write(pbm->controller_regs + SABRE_IOMMU_CONTROL, control);
-
- return 0;
+ psycho_pbm_init_common(pbm, op, "SABRE", PBM_CHIP_TYPE_SABRE);
+ pbm->pci_afsr = pbm->controller_regs + SABRE_PIOAFSR;
+ pbm->pci_afar = pbm->controller_regs + SABRE_PIOAFAR;
+ pbm->pci_csr = pbm->controller_regs + SABRE_PCICTRL;
+ sabre_scan_bus(pbm, &op->dev);
}
-static void __init sabre_pbm_init(struct pci_controller_info *p,
- struct pci_pbm_info *pbm, struct device_node *dp)
-{
- pbm->name = dp->full_name;
- printk("%s: SABRE PCI Bus Module\n", pbm->name);
-
- pbm->numa_node = -1;
-
- pbm->scan_bus = sabre_scan_bus;
- pbm->pci_ops = &sun4u_pci_ops;
- pbm->config_space_reg_bits = 8;
-
- pbm->index = pci_num_pbms++;
-
- pbm->chip_type = PBM_CHIP_TYPE_SABRE;
- pbm->parent = p;
- pbm->prom_node = dp;
- pci_get_pbm_props(pbm);
-
- pci_determine_mem_io_space(pbm);
-}
-
-void __init sabre_init(struct device_node *dp, char *model_name)
+static int __devinit sabre_probe(struct of_device *op,
+ const struct of_device_id *match)
{
const struct linux_prom64_registers *pr_regs;
- struct pci_controller_info *p;
+ struct device_node *dp = op->node;
struct pci_pbm_info *pbm;
- struct iommu *iommu;
- int tsbsize;
- const u32 *vdma;
u32 upa_portid, dma_mask;
+ struct iommu *iommu;
+ int tsbsize, err;
+ const u32 *vdma;
u64 clear_irq;
- hummingbird_p = 0;
- if (!strcmp(model_name, "pci108e,a001"))
- hummingbird_p = 1;
- else if (!strcmp(model_name, "SUNW,sabre")) {
- const char *compat = of_get_property(dp, "compatible", NULL);
- if (compat && !strcmp(compat, "pci108e,a001"))
- hummingbird_p = 1;
- if (!hummingbird_p) {
- struct device_node *dp;
+ hummingbird_p = (match->data != NULL);
+ if (!hummingbird_p) {
+ struct device_node *cpu_dp;
- /* Of course, Sun has to encode things a thousand
- * different ways, inconsistently.
- */
- for_each_node_by_type(dp, "cpu") {
- if (!strcmp(dp->name, "SUNW,UltraSPARC-IIe"))
- hummingbird_p = 1;
- }
+ /* Of course, Sun has to encode things a thousand
+ * different ways, inconsistently.
+ */
+ for_each_node_by_type(cpu_dp, "cpu") {
+ if (!strcmp(cpu_dp->name, "SUNW,UltraSPARC-IIe"))
+ hummingbird_p = 1;
}
}
- p = kzalloc(sizeof(*p), GFP_ATOMIC);
- if (!p)
- goto fatal_memory_error;
+ err = -ENOMEM;
+ pbm = kzalloc(sizeof(*pbm), GFP_KERNEL);
+ if (!pbm) {
+ printk(KERN_ERR PFX "Cannot allocate pci_pbm_info.\n");
+ goto out_err;
+ }
- iommu = kzalloc(sizeof(*iommu), GFP_ATOMIC);
- if (!iommu)
- goto fatal_memory_error;
- pbm = &p->pbm_A;
+ iommu = kzalloc(sizeof(*iommu), GFP_KERNEL);
+ if (!iommu) {
+ printk(KERN_ERR PFX "Cannot allocate PBM iommu.\n");
+ goto out_free_controller;
+ }
+
pbm->iommu = iommu;
upa_portid = of_getintprop_default(dp, "upa-portid", 0xff);
- pbm->next = pci_pbm_root;
- pci_pbm_root = pbm;
-
pbm->portid = upa_portid;
/*
@@ -806,6 +501,11 @@
*/
pr_regs = of_get_property(dp, "reg", NULL);
+ err = -ENODEV;
+ if (!pr_regs) {
+ printk(KERN_ERR PFX "No reg property\n");
+ goto out_free_iommu;
+ }
/*
* First REG in property is base of entire SABRE register space.
@@ -816,22 +516,25 @@
/* PCI first */
for (clear_irq = SABRE_ICLR_A_SLOT0; clear_irq < SABRE_ICLR_B_SLOT0 + 0x80; clear_irq += 8)
- sabre_write(pbm->controller_regs + clear_irq, 0x0UL);
+ upa_writeq(0x0UL, pbm->controller_regs + clear_irq);
/* Then OBIO */
for (clear_irq = SABRE_ICLR_SCSI; clear_irq < SABRE_ICLR_SCSI + 0x80; clear_irq += 8)
- sabre_write(pbm->controller_regs + clear_irq, 0x0UL);
+ upa_writeq(0x0UL, pbm->controller_regs + clear_irq);
/* Error interrupts are enabled later after the bus scan. */
- sabre_write(pbm->controller_regs + SABRE_PCICTRL,
- (SABRE_PCICTRL_MRLEN | SABRE_PCICTRL_SERR |
- SABRE_PCICTRL_ARBPARK | SABRE_PCICTRL_AEN));
+ upa_writeq((SABRE_PCICTRL_MRLEN | SABRE_PCICTRL_SERR |
+ SABRE_PCICTRL_ARBPARK | SABRE_PCICTRL_AEN),
+ pbm->controller_regs + SABRE_PCICTRL);
/* Now map in PCI config space for entire SABRE. */
- pbm->config_space =
- (pbm->controller_regs + SABRE_CONFIGSPACE);
+ pbm->config_space = pbm->controller_regs + SABRE_CONFIGSPACE;
vdma = of_get_property(dp, "virtual-dma", NULL);
+ if (!vdma) {
+ printk(KERN_ERR PFX "No virtual-dma property\n");
+ goto out_free_iommu;
+ }
dma_mask = vdma[0];
switch(vdma[1]) {
@@ -849,20 +552,58 @@
tsbsize = 128;
break;
default:
- prom_printf("SABRE: strange virtual-dma size.\n");
- prom_halt();
+ printk(KERN_ERR PFX "Strange virtual-dma size.\n");
+ goto out_free_iommu;
}
- if (sabre_iommu_init(pbm, tsbsize, vdma[0], dma_mask))
- goto fatal_memory_error;
+ err = psycho_iommu_init(pbm, tsbsize, vdma[0], dma_mask, SABRE_WRSYNC);
+ if (err)
+ goto out_free_iommu;
/*
* Look for APB underneath.
*/
- sabre_pbm_init(p, pbm, dp);
- return;
+ sabre_pbm_init(pbm, op);
-fatal_memory_error:
- prom_printf("SABRE: Fatal memory allocation error.\n");
- prom_halt();
+ pbm->next = pci_pbm_root;
+ pci_pbm_root = pbm;
+
+ dev_set_drvdata(&op->dev, pbm);
+
+ return 0;
+
+out_free_iommu:
+ kfree(pbm->iommu);
+
+out_free_controller:
+ kfree(pbm);
+
+out_err:
+ return err;
}
+
+static struct of_device_id __initdata sabre_match[] = {
+ {
+ .name = "pci",
+ .compatible = "pci108e,a001",
+ .data = (void *) 1,
+ },
+ {
+ .name = "pci",
+ .compatible = "pci108e,a000",
+ },
+ {},
+};
+
+static struct of_platform_driver sabre_driver = {
+ .name = DRIVER_NAME,
+ .match_table = sabre_match,
+ .probe = sabre_probe,
+};
+
+static int __init sabre_init(void)
+{
+ return of_register_driver(&sabre_driver, &of_bus_type);
+}
+
+subsys_initcall(sabre_init);
diff --git a/arch/sparc64/kernel/pci_schizo.c b/arch/sparc64/kernel/pci_schizo.c
index 9248c67..45d9dba 100644
--- a/arch/sparc64/kernel/pci_schizo.c
+++ b/arch/sparc64/kernel/pci_schizo.c
@@ -1,6 +1,6 @@
/* pci_schizo.c: SCHIZO/TOMATILLO specific PCI controller support.
*
- * Copyright (C) 2001, 2002, 2003, 2007 David S. Miller (davem@davemloft.net)
+ * Copyright (C) 2001, 2002, 2003, 2007, 2008 David S. Miller (davem@davemloft.net)
*/
#include <linux/kernel.h>
@@ -13,32 +13,15 @@
#include <asm/iommu.h>
#include <asm/irq.h>
-#include <asm/upa.h>
#include <asm/pstate.h>
#include <asm/prom.h>
-#include <asm/oplib.h>
+#include <asm/upa.h>
#include "pci_impl.h"
#include "iommu_common.h"
-/* All SCHIZO registers are 64-bits. The following accessor
- * routines are how they are accessed. The REG parameter
- * is a physical address.
- */
-#define schizo_read(__reg) \
-({ u64 __ret; \
- __asm__ __volatile__("ldxa [%1] %2, %0" \
- : "=r" (__ret) \
- : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \
- : "memory"); \
- __ret; \
-})
-#define schizo_write(__reg, __val) \
- __asm__ __volatile__("stxa %0, [%1] %2" \
- : /* no outputs */ \
- : "r" (__val), "r" (__reg), \
- "i" (ASI_PHYS_BYPASS_EC_E) \
- : "memory")
+#define DRIVER_NAME "schizo"
+#define PFX DRIVER_NAME ": "
/* This is a convention that at least Excalibur and Merlin
* follow. I suppose the SCHIZO used in Starcat and friends
@@ -163,25 +146,25 @@
* invalidating it before it has a chance to reach
* main memory.
*/
- control = schizo_read(strbuf->strbuf_control);
- schizo_write(strbuf->strbuf_control,
- (control | SCHIZO_STRBUF_CTRL_DENAB));
+ control = upa_readq(strbuf->strbuf_control);
+ upa_writeq((control | SCHIZO_STRBUF_CTRL_DENAB),
+ strbuf->strbuf_control);
for (i = 0; i < 128; i++) {
unsigned long val;
- val = schizo_read(err_base + (i * 8UL));
- schizo_write(err_base + (i * 8UL), 0UL);
+ val = upa_readq(err_base + (i * 8UL));
+ upa_writeq(0UL, err_base + (i * 8UL));
stc_error_buf[i] = val;
}
for (i = 0; i < 16; i++) {
- stc_tag_buf[i] = schizo_read(tag_base + (i * 8UL));
- stc_line_buf[i] = schizo_read(line_base + (i * 8UL));
- schizo_write(tag_base + (i * 8UL), 0UL);
- schizo_write(line_base + (i * 8UL), 0UL);
+ stc_tag_buf[i] = upa_readq(tag_base + (i * 8UL));
+ stc_line_buf[i] = upa_readq(line_base + (i * 8UL));
+ upa_writeq(0UL, tag_base + (i * 8UL));
+ upa_writeq(0UL, line_base + (i * 8UL));
}
/* OK, state is logged, exit diagnostic mode. */
- schizo_write(strbuf->strbuf_control, control);
+ upa_writeq(control, strbuf->strbuf_control);
for (i = 0; i < 16; i++) {
int j, saw_error, first, last;
@@ -258,14 +241,14 @@
int i;
spin_lock_irqsave(&iommu->lock, flags);
- control = schizo_read(iommu->iommu_control);
+ control = upa_readq(iommu->iommu_control);
if (control & SCHIZO_IOMMU_CTRL_XLTEERR) {
unsigned long base;
char *type_string;
/* Clear the error encountered bit. */
control &= ~SCHIZO_IOMMU_CTRL_XLTEERR;
- schizo_write(iommu->iommu_control, control);
+ upa_writeq(control, iommu->iommu_control);
switch((control & SCHIZO_IOMMU_CTRL_XLTESTAT) >> 25UL) {
case 0:
@@ -295,24 +278,24 @@
* get as much diagnostic information to the
* console as we can.
*/
- schizo_write(iommu->iommu_control,
- control | SCHIZO_IOMMU_CTRL_DENAB);
+ upa_writeq(control | SCHIZO_IOMMU_CTRL_DENAB,
+ iommu->iommu_control);
base = pbm->pbm_regs;
for (i = 0; i < 16; i++) {
iommu_tag[i] =
- schizo_read(base + SCHIZO_IOMMU_TAG + (i * 8UL));
+ upa_readq(base + SCHIZO_IOMMU_TAG + (i * 8UL));
iommu_data[i] =
- schizo_read(base + SCHIZO_IOMMU_DATA + (i * 8UL));
+ upa_readq(base + SCHIZO_IOMMU_DATA + (i * 8UL));
/* Now clear out the entry. */
- schizo_write(base + SCHIZO_IOMMU_TAG + (i * 8UL), 0);
- schizo_write(base + SCHIZO_IOMMU_DATA + (i * 8UL), 0);
+ upa_writeq(0, base + SCHIZO_IOMMU_TAG + (i * 8UL));
+ upa_writeq(0, base + SCHIZO_IOMMU_DATA + (i * 8UL));
}
/* Leave diagnostic mode. */
- schizo_write(iommu->iommu_control, control);
+ upa_writeq(control, iommu->iommu_control);
for (i = 0; i < 16; i++) {
unsigned long tag, data;
@@ -357,11 +340,12 @@
spin_unlock_irqrestore(&iommu->lock, flags);
}
-static void schizo_check_iommu_error(struct pci_controller_info *p,
+static void schizo_check_iommu_error(struct pci_pbm_info *pbm,
enum schizo_error_type type)
{
- schizo_check_iommu_error_pbm(&p->pbm_A, type);
- schizo_check_iommu_error_pbm(&p->pbm_B, type);
+ schizo_check_iommu_error_pbm(pbm, type);
+ if (pbm->sibling)
+ schizo_check_iommu_error_pbm(pbm->sibling, type);
}
/* Uncorrectable ECC error status gathering. */
@@ -386,14 +370,13 @@
static irqreturn_t schizo_ue_intr(int irq, void *dev_id)
{
struct pci_pbm_info *pbm = dev_id;
- struct pci_controller_info *p = pbm->parent;
unsigned long afsr_reg = pbm->controller_regs + SCHIZO_UE_AFSR;
unsigned long afar_reg = pbm->controller_regs + SCHIZO_UE_AFAR;
unsigned long afsr, afar, error_bits;
int reported, limit;
/* Latch uncorrectable error status. */
- afar = schizo_read(afar_reg);
+ afar = upa_readq(afar_reg);
/* If either of the error pending bits are set in the
* AFSR, the error status is being actively updated by
@@ -401,7 +384,7 @@
*/
limit = 1000;
do {
- afsr = schizo_read(afsr_reg);
+ afsr = upa_readq(afsr_reg);
} while ((afsr & SCHIZO_UEAFSR_ERRPNDG) != 0 && --limit);
/* Clear the primary/secondary error status bits. */
@@ -410,7 +393,7 @@
SCHIZO_UEAFSR_SPIO | SCHIZO_UEAFSR_SDMA);
if (!error_bits)
return IRQ_NONE;
- schizo_write(afsr_reg, error_bits);
+ upa_writeq(error_bits, afsr_reg);
/* Log the error. */
printk("%s: Uncorrectable Error, primary error type[%s]\n",
@@ -449,7 +432,7 @@
printk("]\n");
/* Interrogate IOMMU for error status. */
- schizo_check_iommu_error(p, UE_ERR);
+ schizo_check_iommu_error(pbm, UE_ERR);
return IRQ_HANDLED;
}
@@ -481,7 +464,7 @@
int reported, limit;
/* Latch error status. */
- afar = schizo_read(afar_reg);
+ afar = upa_readq(afar_reg);
/* If either of the error pending bits are set in the
* AFSR, the error status is being actively updated by
@@ -489,7 +472,7 @@
*/
limit = 1000;
do {
- afsr = schizo_read(afsr_reg);
+ afsr = upa_readq(afsr_reg);
} while ((afsr & SCHIZO_UEAFSR_ERRPNDG) != 0 && --limit);
/* Clear primary/secondary error status bits. */
@@ -498,7 +481,7 @@
SCHIZO_CEAFSR_SPIO | SCHIZO_CEAFSR_SDMA);
if (!error_bits)
return IRQ_NONE;
- schizo_write(afsr_reg, error_bits);
+ upa_writeq(error_bits, afsr_reg);
/* Log the error. */
printk("%s: Correctable Error, primary error type[%s]\n",
@@ -600,7 +583,7 @@
u16 stat;
csr_reg = pbm->pbm_regs + SCHIZO_PCI_CTRL;
- csr = schizo_read(csr_reg);
+ csr = upa_readq(csr_reg);
csr_error_bits =
csr & (SCHIZO_PCICTRL_BUS_UNUS |
SCHIZO_PCICTRL_TTO_ERR |
@@ -610,7 +593,7 @@
SCHIZO_PCICTRL_SERR);
if (csr_error_bits) {
/* Clear the errors. */
- schizo_write(csr_reg, csr);
+ upa_writeq(csr, csr_reg);
/* Log 'em. */
if (csr_error_bits & SCHIZO_PCICTRL_BUS_UNUS)
@@ -650,7 +633,6 @@
static irqreturn_t schizo_pcierr_intr(int irq, void *dev_id)
{
struct pci_pbm_info *pbm = dev_id;
- struct pci_controller_info *p = pbm->parent;
unsigned long afsr_reg, afar_reg, base;
unsigned long afsr, afar, error_bits;
int reported;
@@ -661,8 +643,8 @@
afar_reg = base + SCHIZO_PCI_AFAR;
/* Latch error status. */
- afar = schizo_read(afar_reg);
- afsr = schizo_read(afsr_reg);
+ afar = upa_readq(afar_reg);
+ afsr = upa_readq(afsr_reg);
/* Clear primary/secondary error status bits. */
error_bits = afsr &
@@ -674,7 +656,7 @@
SCHIZO_PCIAFSR_STTO | SCHIZO_PCIAFSR_SUNUS);
if (!error_bits)
return schizo_pcierr_intr_other(pbm);
- schizo_write(afsr_reg, error_bits);
+ upa_writeq(error_bits, afsr_reg);
/* Log the error. */
printk("%s: PCI Error, primary error type[%s]\n",
@@ -744,7 +726,7 @@
* a bug in the IOMMU support code or a PCI device driver.
*/
if (error_bits & (SCHIZO_PCIAFSR_PTA | SCHIZO_PCIAFSR_STA)) {
- schizo_check_iommu_error(p, PCI_ERR);
+ schizo_check_iommu_error(pbm, PCI_ERR);
pci_scan_for_target_abort(pbm, pbm->pci_bus);
}
if (error_bits & (SCHIZO_PCIAFSR_PMA | SCHIZO_PCIAFSR_SMA))
@@ -805,12 +787,11 @@
static irqreturn_t schizo_safarierr_intr(int irq, void *dev_id)
{
struct pci_pbm_info *pbm = dev_id;
- struct pci_controller_info *p = pbm->parent;
u64 errlog;
- errlog = schizo_read(pbm->controller_regs + SCHIZO_SAFARI_ERRLOG);
- schizo_write(pbm->controller_regs + SCHIZO_SAFARI_ERRLOG,
- errlog & ~(SAFARI_ERRLOG_ERROUT));
+ errlog = upa_readq(pbm->controller_regs + SCHIZO_SAFARI_ERRLOG);
+ upa_writeq(errlog & ~(SAFARI_ERRLOG_ERROUT),
+ pbm->controller_regs + SCHIZO_SAFARI_ERRLOG);
if (!(errlog & BUS_ERROR_UNMAP)) {
printk("%s: Unexpected Safari/JBUS error interrupt, errlog[%016lx]\n",
@@ -821,7 +802,7 @@
printk("%s: Safari/JBUS interrupt, UNMAPPED error, interrogating IOMMUs.\n",
pbm->name);
- schizo_check_iommu_error(p, SAFARI_ERR);
+ schizo_check_iommu_error(pbm, SAFARI_ERR);
return IRQ_HANDLED;
}
@@ -863,7 +844,7 @@
*/
static void tomatillo_register_error_handlers(struct pci_pbm_info *pbm)
{
- struct of_device *op = of_find_device_by_node(pbm->prom_node);
+ struct of_device *op = of_find_device_by_node(pbm->op->node);
u64 tmp, err_mask, err_no_mask;
int err;
@@ -910,10 +891,9 @@
}
/* Enable UE and CE interrupts for controller. */
- schizo_write(pbm->controller_regs + SCHIZO_ECC_CTRL,
- (SCHIZO_ECCCTRL_EE |
- SCHIZO_ECCCTRL_UE |
- SCHIZO_ECCCTRL_CE));
+ upa_writeq((SCHIZO_ECCCTRL_EE |
+ SCHIZO_ECCCTRL_UE |
+ SCHIZO_ECCCTRL_CE), pbm->controller_regs + SCHIZO_ECC_CTRL);
/* Enable PCI Error interrupts and clear error
* bits.
@@ -926,10 +906,10 @@
err_no_mask = SCHIZO_PCICTRL_DTO_ERR;
- tmp = schizo_read(pbm->pbm_regs + SCHIZO_PCI_CTRL);
+ tmp = upa_readq(pbm->pbm_regs + SCHIZO_PCI_CTRL);
tmp |= err_mask;
tmp &= ~err_no_mask;
- schizo_write(pbm->pbm_regs + SCHIZO_PCI_CTRL, tmp);
+ upa_writeq(tmp, pbm->pbm_regs + SCHIZO_PCI_CTRL);
err_mask = (SCHIZO_PCIAFSR_PMA | SCHIZO_PCIAFSR_PTA |
SCHIZO_PCIAFSR_PRTRY | SCHIZO_PCIAFSR_PPERR |
@@ -938,7 +918,7 @@
SCHIZO_PCIAFSR_SRTRY | SCHIZO_PCIAFSR_SPERR |
SCHIZO_PCIAFSR_STTO);
- schizo_write(pbm->pbm_regs + SCHIZO_PCI_AFSR, err_mask);
+ upa_writeq(err_mask, pbm->pbm_regs + SCHIZO_PCI_AFSR);
err_mask = (BUS_ERROR_BADCMD | BUS_ERROR_SNOOP_GR |
BUS_ERROR_SNOOP_PCI | BUS_ERROR_SNOOP_RD |
@@ -950,16 +930,16 @@
BUS_ERROR_APERR | BUS_ERROR_UNMAP |
BUS_ERROR_BUSERR | BUS_ERROR_TIMEOUT);
- schizo_write(pbm->controller_regs + SCHIZO_SAFARI_ERRCTRL,
- (SCHIZO_SAFERRCTRL_EN | err_mask));
+ upa_writeq((SCHIZO_SAFERRCTRL_EN | err_mask),
+ pbm->controller_regs + SCHIZO_SAFARI_ERRCTRL);
- schizo_write(pbm->controller_regs + SCHIZO_SAFARI_IRQCTRL,
- (SCHIZO_SAFIRQCTRL_EN | (BUS_ERROR_UNMAP)));
+ upa_writeq((SCHIZO_SAFIRQCTRL_EN | (BUS_ERROR_UNMAP)),
+ pbm->controller_regs + SCHIZO_SAFARI_IRQCTRL);
}
static void schizo_register_error_handlers(struct pci_pbm_info *pbm)
{
- struct of_device *op = of_find_device_by_node(pbm->prom_node);
+ struct of_device *op = of_find_device_by_node(pbm->op->node);
u64 tmp, err_mask, err_no_mask;
int err;
@@ -1006,10 +986,9 @@
}
/* Enable UE and CE interrupts for controller. */
- schizo_write(pbm->controller_regs + SCHIZO_ECC_CTRL,
- (SCHIZO_ECCCTRL_EE |
- SCHIZO_ECCCTRL_UE |
- SCHIZO_ECCCTRL_CE));
+ upa_writeq((SCHIZO_ECCCTRL_EE |
+ SCHIZO_ECCCTRL_UE |
+ SCHIZO_ECCCTRL_CE), pbm->controller_regs + SCHIZO_ECC_CTRL);
err_mask = (SCHIZO_PCICTRL_BUS_UNUS |
SCHIZO_PCICTRL_ESLCK |
@@ -1025,18 +1004,18 @@
/* Enable PCI Error interrupts and clear error
* bits for each PBM.
*/
- tmp = schizo_read(pbm->pbm_regs + SCHIZO_PCI_CTRL);
+ tmp = upa_readq(pbm->pbm_regs + SCHIZO_PCI_CTRL);
tmp |= err_mask;
tmp &= ~err_no_mask;
- schizo_write(pbm->pbm_regs + SCHIZO_PCI_CTRL, tmp);
+ upa_writeq(tmp, pbm->pbm_regs + SCHIZO_PCI_CTRL);
- schizo_write(pbm->pbm_regs + SCHIZO_PCI_AFSR,
- (SCHIZO_PCIAFSR_PMA | SCHIZO_PCIAFSR_PTA |
- SCHIZO_PCIAFSR_PRTRY | SCHIZO_PCIAFSR_PPERR |
- SCHIZO_PCIAFSR_PTTO | SCHIZO_PCIAFSR_PUNUS |
- SCHIZO_PCIAFSR_SMA | SCHIZO_PCIAFSR_STA |
- SCHIZO_PCIAFSR_SRTRY | SCHIZO_PCIAFSR_SPERR |
- SCHIZO_PCIAFSR_STTO | SCHIZO_PCIAFSR_SUNUS));
+ upa_writeq((SCHIZO_PCIAFSR_PMA | SCHIZO_PCIAFSR_PTA |
+ SCHIZO_PCIAFSR_PRTRY | SCHIZO_PCIAFSR_PPERR |
+ SCHIZO_PCIAFSR_PTTO | SCHIZO_PCIAFSR_PUNUS |
+ SCHIZO_PCIAFSR_SMA | SCHIZO_PCIAFSR_STA |
+ SCHIZO_PCIAFSR_SRTRY | SCHIZO_PCIAFSR_SPERR |
+ SCHIZO_PCIAFSR_STTO | SCHIZO_PCIAFSR_SUNUS),
+ pbm->pbm_regs + SCHIZO_PCI_AFSR);
/* Make all Safari error conditions fatal except unmapped
* errors which we make generate interrupts.
@@ -1063,8 +1042,8 @@
BUS_ERROR_CPU0PS | BUS_ERROR_CPU0PB);
#endif
- schizo_write(pbm->controller_regs + SCHIZO_SAFARI_ERRCTRL,
- (SCHIZO_SAFERRCTRL_EN | err_mask));
+ upa_writeq((SCHIZO_SAFERRCTRL_EN | err_mask),
+ pbm->controller_regs + SCHIZO_SAFARI_ERRCTRL);
}
static void pbm_config_busmastering(struct pci_pbm_info *pbm)
@@ -1084,14 +1063,15 @@
pci_config_write8(addr, 64);
}
-static void __init schizo_scan_bus(struct pci_pbm_info *pbm)
+static void __devinit schizo_scan_bus(struct pci_pbm_info *pbm,
+ struct device *parent)
{
pbm_config_busmastering(pbm);
pbm->is_66mhz_capable =
- (of_find_property(pbm->prom_node, "66mhz-capable", NULL)
+ (of_find_property(pbm->op->node, "66mhz-capable", NULL)
!= NULL);
- pbm->pci_bus = pci_scan_one_pbm(pbm);
+ pbm->pci_bus = pci_scan_one_pbm(pbm, parent);
if (pbm->chip_type == PBM_CHIP_TYPE_TOMATILLO)
tomatillo_register_error_handlers(pbm);
@@ -1133,12 +1113,12 @@
* streaming buffer and leave the rerun-disable
* setting however OBP set it.
*/
- control = schizo_read(pbm->stc.strbuf_control);
+ control = upa_readq(pbm->stc.strbuf_control);
control &= ~(SCHIZO_STRBUF_CTRL_LPTR |
SCHIZO_STRBUF_CTRL_LENAB |
SCHIZO_STRBUF_CTRL_DENAB);
control |= SCHIZO_STRBUF_CTRL_ENAB;
- schizo_write(pbm->stc.strbuf_control, control);
+ upa_writeq(control, pbm->stc.strbuf_control);
pbm->stc.strbuf_enabled = 1;
}
@@ -1150,24 +1130,17 @@
static int schizo_pbm_iommu_init(struct pci_pbm_info *pbm)
{
- struct iommu *iommu = pbm->iommu;
+ static const u32 vdma_default[] = { 0xc0000000, 0x40000000 };
unsigned long i, tagbase, database;
- struct property *prop;
- u32 vdma[2], dma_mask;
+ struct iommu *iommu = pbm->iommu;
int tsbsize, err;
+ const u32 *vdma;
+ u32 dma_mask;
u64 control;
- prop = of_find_property(pbm->prom_node, "virtual-dma", NULL);
- if (prop) {
- u32 *val = prop->value;
-
- vdma[0] = val[0];
- vdma[1] = val[1];
- } else {
- /* No property, use default values. */
- vdma[0] = 0xc0000000;
- vdma[1] = 0x40000000;
- }
+ vdma = of_get_property(pbm->op->node, "virtual-dma", NULL);
+ if (!vdma)
+ vdma = vdma_default;
dma_mask = vdma[0];
switch (vdma[1]) {
@@ -1187,9 +1160,9 @@
break;
default:
- prom_printf("SCHIZO: strange virtual-dma size.\n");
- prom_halt();
- };
+ printk(KERN_ERR PFX "Strange virtual-dma size.\n");
+ return -EINVAL;
+ }
/* Register addresses, SCHIZO has iommu ctx flushing. */
iommu->iommu_control = pbm->pbm_regs + SCHIZO_IOMMU_CONTROL;
@@ -1206,15 +1179,15 @@
/*
* Invalidate TLB Entries.
*/
- control = schizo_read(iommu->iommu_control);
+ control = upa_readq(iommu->iommu_control);
control |= SCHIZO_IOMMU_CTRL_DENAB;
- schizo_write(iommu->iommu_control, control);
+ upa_writeq(control, iommu->iommu_control);
tagbase = SCHIZO_IOMMU_TAG, database = SCHIZO_IOMMU_DATA;
- for(i = 0; i < 16; i++) {
- schizo_write(pbm->pbm_regs + tagbase + (i * 8UL), 0);
- schizo_write(pbm->pbm_regs + database + (i * 8UL), 0);
+ for (i = 0; i < 16; i++) {
+ upa_writeq(0, pbm->pbm_regs + tagbase + (i * 8UL));
+ upa_writeq(0, pbm->pbm_regs + database + (i * 8UL));
}
/* Leave diag mode enabled for full-flushing done
@@ -1222,12 +1195,14 @@
*/
err = iommu_table_init(iommu, tsbsize * 8 * 1024, vdma[0], dma_mask,
pbm->numa_node);
- if (err)
+ if (err) {
+ printk(KERN_ERR PFX "iommu_table_init() fails with %d\n", err);
return err;
+ }
- schizo_write(iommu->iommu_tsbbase, __pa(iommu->page_table));
+ upa_writeq(__pa(iommu->page_table), iommu->iommu_tsbbase);
- control = schizo_read(iommu->iommu_control);
+ control = upa_readq(iommu->iommu_control);
control &= ~(SCHIZO_IOMMU_CTRL_TSBSZ | SCHIZO_IOMMU_CTRL_TBWSZ);
switch (tsbsize) {
case 64:
@@ -1236,10 +1211,10 @@
case 128:
control |= SCHIZO_IOMMU_TSBSZ_128K;
break;
- };
+ }
control |= SCHIZO_IOMMU_CTRL_ENAB;
- schizo_write(iommu->iommu_control, control);
+ upa_writeq(control, iommu->iommu_control);
return 0;
}
@@ -1280,12 +1255,11 @@
static void schizo_pbm_hw_init(struct pci_pbm_info *pbm)
{
- struct property *prop;
u64 tmp;
- schizo_write(pbm->pbm_regs + SCHIZO_PCI_IRQ_RETRY, 5);
+ upa_writeq(5, pbm->pbm_regs + SCHIZO_PCI_IRQ_RETRY);
- tmp = schizo_read(pbm->pbm_regs + SCHIZO_PCI_CTRL);
+ tmp = upa_readq(pbm->pbm_regs + SCHIZO_PCI_CTRL);
/* Enable arbiter for all PCI slots. */
tmp |= 0xff;
@@ -1294,8 +1268,7 @@
pbm->chip_version >= 0x2)
tmp |= 0x3UL << SCHIZO_PCICTRL_PTO_SHIFT;
- prop = of_find_property(pbm->prom_node, "no-bus-parking", NULL);
- if (!prop)
+ if (!of_find_property(pbm->op->node, "no-bus-parking", NULL))
tmp |= SCHIZO_PCICTRL_PARK;
else
tmp &= ~SCHIZO_PCICTRL_PARK;
@@ -1311,13 +1284,13 @@
SCHIZO_PCICTRL_RDO_PREF |
SCHIZO_PCICTRL_RDL_PREF);
- schizo_write(pbm->pbm_regs + SCHIZO_PCI_CTRL, tmp);
+ upa_writeq(tmp, pbm->pbm_regs + SCHIZO_PCI_CTRL);
- tmp = schizo_read(pbm->pbm_regs + SCHIZO_PCI_DIAG);
+ tmp = upa_readq(pbm->pbm_regs + SCHIZO_PCI_DIAG);
tmp &= ~(SCHIZO_PCIDIAG_D_RTRYARB |
SCHIZO_PCIDIAG_D_RETRY |
SCHIZO_PCIDIAG_D_INTSYNC);
- schizo_write(pbm->pbm_regs + SCHIZO_PCI_DIAG, tmp);
+ upa_writeq(tmp, pbm->pbm_regs + SCHIZO_PCI_DIAG);
if (pbm->chip_type == PBM_CHIP_TYPE_TOMATILLO) {
/* Clear prefetch lengths to workaround a bug in
@@ -1329,17 +1302,16 @@
TOMATILLO_IOC_RDONE_CPENAB |
TOMATILLO_IOC_RDLINE_CPENAB);
- schizo_write(pbm->pbm_regs + TOMATILLO_PCI_IOC_CSR,
- tmp);
+ upa_writeq(tmp, pbm->pbm_regs + TOMATILLO_PCI_IOC_CSR);
}
}
-static int __init schizo_pbm_init(struct pci_controller_info *p,
- struct device_node *dp, u32 portid,
- int chip_type)
+static int __devinit schizo_pbm_init(struct pci_pbm_info *pbm,
+ struct of_device *op, u32 portid,
+ int chip_type)
{
const struct linux_prom64_registers *regs;
- struct pci_pbm_info *pbm;
+ struct device_node *dp = op->node;
const char *chipset_name;
int is_pbm_a, err;
@@ -1372,25 +1344,19 @@
regs = of_get_property(dp, "reg", NULL);
is_pbm_a = ((regs[0].phys_addr & 0x00700000) == 0x00600000);
- if (is_pbm_a)
- pbm = &p->pbm_A;
- else
- pbm = &p->pbm_B;
pbm->next = pci_pbm_root;
pci_pbm_root = pbm;
pbm->numa_node = -1;
- pbm->scan_bus = schizo_scan_bus;
pbm->pci_ops = &sun4u_pci_ops;
pbm->config_space_reg_bits = 8;
pbm->index = pci_num_pbms++;
pbm->portid = portid;
- pbm->parent = p;
- pbm->prom_node = dp;
+ pbm->op = op;
pbm->chip_type = chip_type;
pbm->chip_version = of_getintprop_default(dp, "version#", 0);
@@ -1420,6 +1386,8 @@
schizo_pbm_strbuf_init(pbm);
+ schizo_scan_bus(pbm, &op->dev);
+
return 0;
}
@@ -1433,62 +1401,104 @@
return (x == y);
}
-static void __init __schizo_init(struct device_node *dp, char *model_name,
- int chip_type)
+static struct pci_pbm_info * __devinit schizo_find_sibling(u32 portid,
+ int chip_type)
{
- struct pci_controller_info *p;
+ struct pci_pbm_info *pbm;
+
+ for (pbm = pci_pbm_root; pbm; pbm = pbm->next) {
+ if (portid_compare(pbm->portid, portid, chip_type))
+ return pbm;
+ }
+ return NULL;
+}
+
+static int __devinit __schizo_init(struct of_device *op, unsigned long chip_type)
+{
+ struct device_node *dp = op->node;
struct pci_pbm_info *pbm;
struct iommu *iommu;
u32 portid;
+ int err;
portid = of_getintprop_default(dp, "portid", 0xff);
- for (pbm = pci_pbm_root; pbm; pbm = pbm->next) {
- if (portid_compare(pbm->portid, portid, chip_type)) {
- if (schizo_pbm_init(pbm->parent, dp,
- portid, chip_type))
- goto fatal_memory_error;
- return;
- }
+ err = -ENOMEM;
+ pbm = kzalloc(sizeof(*pbm), GFP_KERNEL);
+ if (!pbm) {
+ printk(KERN_ERR PFX "Cannot allocate pci_pbm_info.\n");
+ goto out_err;
}
- p = kzalloc(sizeof(struct pci_controller_info), GFP_ATOMIC);
- if (!p)
- goto fatal_memory_error;
+ pbm->sibling = schizo_find_sibling(portid, chip_type);
- iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC);
- if (!iommu)
- goto fatal_memory_error;
+ iommu = kzalloc(sizeof(struct iommu), GFP_KERNEL);
+ if (!iommu) {
+ printk(KERN_ERR PFX "Cannot allocate PBM A iommu.\n");
+ goto out_free_pbm;
+ }
- p->pbm_A.iommu = iommu;
+ pbm->iommu = iommu;
- iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC);
- if (!iommu)
- goto fatal_memory_error;
+ if (schizo_pbm_init(pbm, op, portid, chip_type))
+ goto out_free_iommu;
- p->pbm_B.iommu = iommu;
+ if (pbm->sibling)
+ pbm->sibling->sibling = pbm;
- if (schizo_pbm_init(p, dp, portid, chip_type))
- goto fatal_memory_error;
+ dev_set_drvdata(&op->dev, pbm);
- return;
+ return 0;
-fatal_memory_error:
- prom_printf("SCHIZO: Fatal memory allocation error.\n");
- prom_halt();
+out_free_iommu:
+ kfree(pbm->iommu);
+
+out_free_pbm:
+ kfree(pbm);
+
+out_err:
+ return err;
}
-void __init schizo_init(struct device_node *dp, char *model_name)
+static int __devinit schizo_probe(struct of_device *op,
+ const struct of_device_id *match)
{
- __schizo_init(dp, model_name, PBM_CHIP_TYPE_SCHIZO);
+ return __schizo_init(op, (unsigned long) match->data);
}
-void __init schizo_plus_init(struct device_node *dp, char *model_name)
+/* The ordering of this table is very important. Some Tomatillo
+ * nodes announce that they are compatible with both pci108e,a801
+ * and pci108e,8001. So list the chips in reverse chronological
+ * order.
+ */
+static struct of_device_id __initdata schizo_match[] = {
+ {
+ .name = "pci",
+ .compatible = "pci108e,a801",
+ .data = (void *) PBM_CHIP_TYPE_TOMATILLO,
+ },
+ {
+ .name = "pci",
+ .compatible = "pci108e,8002",
+ .data = (void *) PBM_CHIP_TYPE_SCHIZO_PLUS,
+ },
+ {
+ .name = "pci",
+ .compatible = "pci108e,8001",
+ .data = (void *) PBM_CHIP_TYPE_SCHIZO,
+ },
+ {},
+};
+
+static struct of_platform_driver schizo_driver = {
+ .name = DRIVER_NAME,
+ .match_table = schizo_match,
+ .probe = schizo_probe,
+};
+
+static int __init schizo_init(void)
{
- __schizo_init(dp, model_name, PBM_CHIP_TYPE_SCHIZO_PLUS);
+ return of_register_driver(&schizo_driver, &of_bus_type);
}
-void __init tomatillo_init(struct device_node *dp, char *model_name)
-{
- __schizo_init(dp, model_name, PBM_CHIP_TYPE_TOMATILLO);
-}
+subsys_initcall(schizo_init);
diff --git a/arch/sparc64/kernel/pci_sun4v.c b/arch/sparc64/kernel/pci_sun4v.c
index a104c80..e86c73e 100644
--- a/arch/sparc64/kernel/pci_sun4v.c
+++ b/arch/sparc64/kernel/pci_sun4v.c
@@ -13,12 +13,10 @@
#include <linux/irq.h>
#include <linux/msi.h>
#include <linux/log2.h>
+#include <linux/of_device.h>
#include <asm/iommu.h>
#include <asm/irq.h>
-#include <asm/upa.h>
-#include <asm/pstate.h>
-#include <asm/oplib.h>
#include <asm/hypervisor.h>
#include <asm/prom.h>
@@ -27,6 +25,9 @@
#include "pci_sun4v.h"
+#define DRIVER_NAME "pci_sun4v"
+#define PFX DRIVER_NAME ": "
+
static unsigned long vpci_major = 1;
static unsigned long vpci_minor = 1;
@@ -41,6 +42,7 @@
};
static DEFINE_PER_CPU(struct iommu_batch, iommu_batch);
+static int iommu_batch_initialized;
/* Interrupts must be disabled. */
static inline void iommu_batch_start(struct device *dev, unsigned long prot, unsigned long entry)
@@ -542,15 +544,16 @@
.sync_sg_for_cpu = dma_4v_sync_sg_for_cpu,
};
-static void __init pci_sun4v_scan_bus(struct pci_pbm_info *pbm)
+static void __init pci_sun4v_scan_bus(struct pci_pbm_info *pbm,
+ struct device *parent)
{
struct property *prop;
struct device_node *dp;
- dp = pbm->prom_node;
+ dp = pbm->op->node;
prop = of_find_property(dp, "66mhz-capable", NULL);
pbm->is_66mhz_capable = (prop != NULL);
- pbm->pci_bus = pci_scan_one_pbm(pbm);
+ pbm->pci_bus = pci_scan_one_pbm(pbm, parent);
/* XXX register error interrupt handlers XXX */
}
@@ -583,29 +586,22 @@
return cnt;
}
-static void __init pci_sun4v_iommu_init(struct pci_pbm_info *pbm)
+static int __init pci_sun4v_iommu_init(struct pci_pbm_info *pbm)
{
+ static const u32 vdma_default[] = { 0x80000000, 0x80000000 };
struct iommu *iommu = pbm->iommu;
- struct property *prop;
unsigned long num_tsb_entries, sz, tsbsize;
- u32 vdma[2], dma_mask, dma_offset;
+ u32 dma_mask, dma_offset;
+ const u32 *vdma;
- prop = of_find_property(pbm->prom_node, "virtual-dma", NULL);
- if (prop) {
- u32 *val = prop->value;
-
- vdma[0] = val[0];
- vdma[1] = val[1];
- } else {
- /* No property, use default values. */
- vdma[0] = 0x80000000;
- vdma[1] = 0x80000000;
- }
+ vdma = of_get_property(pbm->op->node, "virtual-dma", NULL);
+ if (!vdma)
+ vdma = vdma_default;
if ((vdma[0] | vdma[1]) & ~IO_PAGE_MASK) {
- prom_printf("PCI-SUN4V: strange virtual-dma[%08x:%08x].\n",
- vdma[0], vdma[1]);
- prom_halt();
+ printk(KERN_ERR PFX "Strange virtual-dma[%08x:%08x].\n",
+ vdma[0], vdma[1]);
+ return -EINVAL;
};
dma_mask = (roundup_pow_of_two(vdma[1]) - 1UL);
@@ -625,8 +621,8 @@
sz = (sz + 7UL) & ~7UL;
iommu->arena.map = kzalloc(sz, GFP_KERNEL);
if (!iommu->arena.map) {
- prom_printf("PCI_IOMMU: Error, kmalloc(arena.map) failed.\n");
- prom_halt();
+ printk(KERN_ERR PFX "Error, kmalloc(arena.map) failed.\n");
+ return -ENOMEM;
}
iommu->arena.limit = num_tsb_entries;
@@ -634,6 +630,8 @@
if (sz)
printk("%s: Imported %lu TSB entries from OBP\n",
pbm->name, sz);
+
+ return 0;
}
#ifdef CONFIG_PCI_MSI
@@ -890,29 +888,20 @@
}
#endif /* !(CONFIG_PCI_MSI) */
-static void __init pci_sun4v_pbm_init(struct pci_controller_info *p,
- struct device_node *dp, u32 devhandle)
+static int __init pci_sun4v_pbm_init(struct pci_pbm_info *pbm,
+ struct of_device *op, u32 devhandle)
{
- struct pci_pbm_info *pbm;
-
- if (devhandle & 0x40)
- pbm = &p->pbm_B;
- else
- pbm = &p->pbm_A;
-
- pbm->next = pci_pbm_root;
- pci_pbm_root = pbm;
+ struct device_node *dp = op->node;
+ int err;
pbm->numa_node = of_node_to_nid(dp);
- pbm->scan_bus = pci_sun4v_scan_bus;
pbm->pci_ops = &sun4v_pci_ops;
pbm->config_space_reg_bits = 12;
pbm->index = pci_num_pbms++;
- pbm->parent = p;
- pbm->prom_node = dp;
+ pbm->op = op;
pbm->devhandle = devhandle;
@@ -924,82 +913,120 @@
pci_determine_mem_io_space(pbm);
pci_get_pbm_props(pbm);
- pci_sun4v_iommu_init(pbm);
+
+ err = pci_sun4v_iommu_init(pbm);
+ if (err)
+ return err;
+
pci_sun4v_msi_init(pbm);
+
+ pci_sun4v_scan_bus(pbm, &op->dev);
+
+ pbm->next = pci_pbm_root;
+ pci_pbm_root = pbm;
+
+ return 0;
}
-void __init sun4v_pci_init(struct device_node *dp, char *model_name)
+static int __devinit pci_sun4v_probe(struct of_device *op,
+ const struct of_device_id *match)
{
+ const struct linux_prom64_registers *regs;
static int hvapi_negotiated = 0;
- struct pci_controller_info *p;
struct pci_pbm_info *pbm;
+ struct device_node *dp;
struct iommu *iommu;
- struct property *prop;
- struct linux_prom64_registers *regs;
u32 devhandle;
- int i;
+ int i, err;
+
+ dp = op->node;
if (!hvapi_negotiated++) {
- int err = sun4v_hvapi_register(HV_GRP_PCI,
- vpci_major,
- &vpci_minor);
+ err = sun4v_hvapi_register(HV_GRP_PCI,
+ vpci_major,
+ &vpci_minor);
if (err) {
- prom_printf("SUN4V_PCI: Could not register hvapi, "
- "err=%d\n", err);
- prom_halt();
+ printk(KERN_ERR PFX "Could not register hvapi, "
+ "err=%d\n", err);
+ return err;
}
- printk("SUN4V_PCI: Registered hvapi major[%lu] minor[%lu]\n",
+ printk(KERN_INFO PFX "Registered hvapi major[%lu] minor[%lu]\n",
vpci_major, vpci_minor);
dma_ops = &sun4v_dma_ops;
}
- prop = of_find_property(dp, "reg", NULL);
- if (!prop) {
- prom_printf("SUN4V_PCI: Could not find config registers\n");
- prom_halt();
+ regs = of_get_property(dp, "reg", NULL);
+ err = -ENODEV;
+ if (!regs) {
+ printk(KERN_ERR PFX "Could not find config registers\n");
+ goto out_err;
}
- regs = prop->value;
-
devhandle = (regs->phys_addr >> 32UL) & 0x0fffffff;
- for (pbm = pci_pbm_root; pbm; pbm = pbm->next) {
- if (pbm->devhandle == (devhandle ^ 0x40)) {
- pci_sun4v_pbm_init(pbm->parent, dp, devhandle);
- return;
+ err = -ENOMEM;
+ if (!iommu_batch_initialized) {
+ for_each_possible_cpu(i) {
+ unsigned long page = get_zeroed_page(GFP_KERNEL);
+
+ if (!page)
+ goto out_err;
+
+ per_cpu(iommu_batch, i).pglist = (u64 *) page;
}
+ iommu_batch_initialized = 1;
}
- for_each_possible_cpu(i) {
- unsigned long page = get_zeroed_page(GFP_ATOMIC);
-
- if (!page)
- goto fatal_memory_error;
-
- per_cpu(iommu_batch, i).pglist = (u64 *) page;
+ pbm = kzalloc(sizeof(*pbm), GFP_KERNEL);
+ if (!pbm) {
+ printk(KERN_ERR PFX "Could not allocate pci_pbm_info\n");
+ goto out_err;
}
- p = kzalloc(sizeof(struct pci_controller_info), GFP_ATOMIC);
- if (!p)
- goto fatal_memory_error;
+ iommu = kzalloc(sizeof(struct iommu), GFP_KERNEL);
+ if (!iommu) {
+ printk(KERN_ERR PFX "Could not allocate pbm iommu\n");
+ goto out_free_controller;
+ }
- iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC);
- if (!iommu)
- goto fatal_memory_error;
+ pbm->iommu = iommu;
- p->pbm_A.iommu = iommu;
+ err = pci_sun4v_pbm_init(pbm, op, devhandle);
+ if (err)
+ goto out_free_iommu;
- iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC);
- if (!iommu)
- goto fatal_memory_error;
+ dev_set_drvdata(&op->dev, pbm);
- p->pbm_B.iommu = iommu;
+ return 0;
- pci_sun4v_pbm_init(p, dp, devhandle);
- return;
+out_free_iommu:
+ kfree(pbm->iommu);
-fatal_memory_error:
- prom_printf("SUN4V_PCI: Fatal memory allocation error.\n");
- prom_halt();
+out_free_controller:
+ kfree(pbm);
+
+out_err:
+ return err;
}
+
+static struct of_device_id __initdata pci_sun4v_match[] = {
+ {
+ .name = "pci",
+ .compatible = "SUNW,sun4v-pci",
+ },
+ {},
+};
+
+static struct of_platform_driver pci_sun4v_driver = {
+ .name = DRIVER_NAME,
+ .match_table = pci_sun4v_match,
+ .probe = pci_sun4v_probe,
+};
+
+static int __init pci_sun4v_init(void)
+{
+ return of_register_driver(&pci_sun4v_driver, &of_bus_type);
+}
+
+subsys_initcall(pci_sun4v_init);
diff --git a/arch/sparc64/kernel/pci_sun4v_asm.S b/arch/sparc64/kernel/pci_sun4v_asm.S
index ecb81f3..e606d46 100644
--- a/arch/sparc64/kernel/pci_sun4v_asm.S
+++ b/arch/sparc64/kernel/pci_sun4v_asm.S
@@ -1,8 +1,9 @@
/* pci_sun4v_asm: Hypervisor calls for PCI support.
*
- * Copyright (C) 2006 David S. Miller <davem@davemloft.net>
+ * Copyright (C) 2006, 2008 David S. Miller <davem@davemloft.net>
*/
+#include <linux/linkage.h>
#include <asm/hypervisor.h>
/* %o0: devhandle
@@ -14,8 +15,7 @@
* returns %o0: -status if status was non-zero, else
* %o0: num pages mapped
*/
- .globl pci_sun4v_iommu_map
-pci_sun4v_iommu_map:
+ENTRY(pci_sun4v_iommu_map)
mov %o5, %g1
mov HV_FAST_PCI_IOMMU_MAP, %o5
ta HV_FAST_TRAP
@@ -24,6 +24,7 @@
mov %o1, %o0
1: retl
nop
+ENDPROC(pci_sun4v_iommu_map)
/* %o0: devhandle
* %o1: tsbid
@@ -31,12 +32,12 @@
*
* returns %o0: num ttes demapped
*/
- .globl pci_sun4v_iommu_demap
-pci_sun4v_iommu_demap:
+ENTRY(pci_sun4v_iommu_demap)
mov HV_FAST_PCI_IOMMU_DEMAP, %o5
ta HV_FAST_TRAP
retl
mov %o1, %o0
+ENDPROC(pci_sun4v_iommu_demap)
/* %o0: devhandle
* %o1: tsbid
@@ -45,8 +46,7 @@
*
* returns %o0: status
*/
- .globl pci_sun4v_iommu_getmap
-pci_sun4v_iommu_getmap:
+ENTRY(pci_sun4v_iommu_getmap)
mov %o2, %o4
mov HV_FAST_PCI_IOMMU_GETMAP, %o5
ta HV_FAST_TRAP
@@ -54,6 +54,7 @@
stx %o2, [%o3]
retl
mov %o0, %o0
+ENDPROC(pci_sun4v_iommu_getmap)
/* %o0: devhandle
* %o1: pci_device
@@ -65,14 +66,14 @@
* If there is an error, the data will be returned
* as all 1's.
*/
- .globl pci_sun4v_config_get
-pci_sun4v_config_get:
+ENTRY(pci_sun4v_config_get)
mov HV_FAST_PCI_CONFIG_GET, %o5
ta HV_FAST_TRAP
brnz,a,pn %o1, 1f
mov -1, %o2
1: retl
mov %o2, %o0
+ENDPROC(pci_sun4v_config_get)
/* %o0: devhandle
* %o1: pci_device
@@ -85,14 +86,14 @@
* status will be zero if the operation completed
* successfully, else -1 if not
*/
- .globl pci_sun4v_config_put
-pci_sun4v_config_put:
+ENTRY(pci_sun4v_config_put)
mov HV_FAST_PCI_CONFIG_PUT, %o5
ta HV_FAST_TRAP
brnz,a,pn %o1, 1f
mov -1, %o1
1: retl
mov %o1, %o0
+ENDPROC(pci_sun4v_config_put)
/* %o0: devhandle
* %o1: msiqid
@@ -104,12 +105,12 @@
* status will be zero if the operation completed
* successfully, else -1 if not
*/
- .globl pci_sun4v_msiq_conf
-pci_sun4v_msiq_conf:
+ENTRY(pci_sun4v_msiq_conf)
mov HV_FAST_PCI_MSIQ_CONF, %o5
ta HV_FAST_TRAP
retl
mov %o0, %o0
+ENDPROC(pci_sun4v_msiq_conf)
/* %o0: devhandle
* %o1: msiqid
@@ -118,8 +119,7 @@
*
* returns %o0: status
*/
- .globl pci_sun4v_msiq_info
-pci_sun4v_msiq_info:
+ENTRY(pci_sun4v_msiq_info)
mov %o2, %o4
mov HV_FAST_PCI_MSIQ_INFO, %o5
ta HV_FAST_TRAP
@@ -127,6 +127,7 @@
stx %o2, [%o3]
retl
mov %o0, %o0
+ENDPROC(pci_sun4v_msiq_info)
/* %o0: devhandle
* %o1: msiqid
@@ -134,13 +135,13 @@
*
* returns %o0: status
*/
- .globl pci_sun4v_msiq_getvalid
-pci_sun4v_msiq_getvalid:
+ENTRY(pci_sun4v_msiq_getvalid)
mov HV_FAST_PCI_MSIQ_GETVALID, %o5
ta HV_FAST_TRAP
stx %o1, [%o2]
retl
mov %o0, %o0
+ENDPROC(pci_sun4v_msiq_getvalid)
/* %o0: devhandle
* %o1: msiqid
@@ -148,12 +149,12 @@
*
* returns %o0: status
*/
- .globl pci_sun4v_msiq_setvalid
-pci_sun4v_msiq_setvalid:
+ENTRY(pci_sun4v_msiq_setvalid)
mov HV_FAST_PCI_MSIQ_SETVALID, %o5
ta HV_FAST_TRAP
retl
mov %o0, %o0
+ENDPROC(pci_sun4v_msiq_setvalid)
/* %o0: devhandle
* %o1: msiqid
@@ -161,13 +162,13 @@
*
* returns %o0: status
*/
- .globl pci_sun4v_msiq_getstate
-pci_sun4v_msiq_getstate:
+ENTRY(pci_sun4v_msiq_getstate)
mov HV_FAST_PCI_MSIQ_GETSTATE, %o5
ta HV_FAST_TRAP
stx %o1, [%o2]
retl
mov %o0, %o0
+ENDPROC(pci_sun4v_msiq_getstate)
/* %o0: devhandle
* %o1: msiqid
@@ -175,12 +176,12 @@
*
* returns %o0: status
*/
- .globl pci_sun4v_msiq_setstate
-pci_sun4v_msiq_setstate:
+ENTRY(pci_sun4v_msiq_setstate)
mov HV_FAST_PCI_MSIQ_SETSTATE, %o5
ta HV_FAST_TRAP
retl
mov %o0, %o0
+ENDPROC(pci_sun4v_msiq_setstate)
/* %o0: devhandle
* %o1: msiqid
@@ -188,13 +189,13 @@
*
* returns %o0: status
*/
- .globl pci_sun4v_msiq_gethead
-pci_sun4v_msiq_gethead:
+ENTRY(pci_sun4v_msiq_gethead)
mov HV_FAST_PCI_MSIQ_GETHEAD, %o5
ta HV_FAST_TRAP
stx %o1, [%o2]
retl
mov %o0, %o0
+ENDPROC(pci_sun4v_msiq_gethead)
/* %o0: devhandle
* %o1: msiqid
@@ -202,12 +203,12 @@
*
* returns %o0: status
*/
- .globl pci_sun4v_msiq_sethead
-pci_sun4v_msiq_sethead:
+ENTRY(pci_sun4v_msiq_sethead)
mov HV_FAST_PCI_MSIQ_SETHEAD, %o5
ta HV_FAST_TRAP
retl
mov %o0, %o0
+ENDPROC(pci_sun4v_msiq_sethead)
/* %o0: devhandle
* %o1: msiqid
@@ -215,13 +216,13 @@
*
* returns %o0: status
*/
- .globl pci_sun4v_msiq_gettail
-pci_sun4v_msiq_gettail:
+ENTRY(pci_sun4v_msiq_gettail)
mov HV_FAST_PCI_MSIQ_GETTAIL, %o5
ta HV_FAST_TRAP
stx %o1, [%o2]
retl
mov %o0, %o0
+ENDPROC(pci_sun4v_msiq_gettail)
/* %o0: devhandle
* %o1: msinum
@@ -229,13 +230,13 @@
*
* returns %o0: status
*/
- .globl pci_sun4v_msi_getvalid
-pci_sun4v_msi_getvalid:
+ENTRY(pci_sun4v_msi_getvalid)
mov HV_FAST_PCI_MSI_GETVALID, %o5
ta HV_FAST_TRAP
stx %o1, [%o2]
retl
mov %o0, %o0
+ENDPROC(pci_sun4v_msi_getvalid)
/* %o0: devhandle
* %o1: msinum
@@ -243,12 +244,12 @@
*
* returns %o0: status
*/
- .globl pci_sun4v_msi_setvalid
-pci_sun4v_msi_setvalid:
+ENTRY(pci_sun4v_msi_setvalid)
mov HV_FAST_PCI_MSI_SETVALID, %o5
ta HV_FAST_TRAP
retl
mov %o0, %o0
+ENDPROC(pci_sun4v_msi_setvalid)
/* %o0: devhandle
* %o1: msinum
@@ -256,13 +257,13 @@
*
* returns %o0: status
*/
- .globl pci_sun4v_msi_getmsiq
-pci_sun4v_msi_getmsiq:
+ENTRY(pci_sun4v_msi_getmsiq)
mov HV_FAST_PCI_MSI_GETMSIQ, %o5
ta HV_FAST_TRAP
stx %o1, [%o2]
retl
mov %o0, %o0
+ENDPROC(pci_sun4v_msi_getmsiq)
/* %o0: devhandle
* %o1: msinum
@@ -271,12 +272,12 @@
*
* returns %o0: status
*/
- .globl pci_sun4v_msi_setmsiq
-pci_sun4v_msi_setmsiq:
+ENTRY(pci_sun4v_msi_setmsiq)
mov HV_FAST_PCI_MSI_SETMSIQ, %o5
ta HV_FAST_TRAP
retl
mov %o0, %o0
+ENDPROC(pci_sun4v_msi_setmsiq)
/* %o0: devhandle
* %o1: msinum
@@ -284,13 +285,13 @@
*
* returns %o0: status
*/
- .globl pci_sun4v_msi_getstate
-pci_sun4v_msi_getstate:
+ENTRY(pci_sun4v_msi_getstate)
mov HV_FAST_PCI_MSI_GETSTATE, %o5
ta HV_FAST_TRAP
stx %o1, [%o2]
retl
mov %o0, %o0
+ENDPROC(pci_sun4v_msi_getstate)
/* %o0: devhandle
* %o1: msinum
@@ -298,12 +299,12 @@
*
* returns %o0: status
*/
- .globl pci_sun4v_msi_setstate
-pci_sun4v_msi_setstate:
+ENTRY(pci_sun4v_msi_setstate)
mov HV_FAST_PCI_MSI_SETSTATE, %o5
ta HV_FAST_TRAP
retl
mov %o0, %o0
+ENDPROC(pci_sun4v_msi_setstate)
/* %o0: devhandle
* %o1: msinum
@@ -311,13 +312,13 @@
*
* returns %o0: status
*/
- .globl pci_sun4v_msg_getmsiq
-pci_sun4v_msg_getmsiq:
+ENTRY(pci_sun4v_msg_getmsiq)
mov HV_FAST_PCI_MSG_GETMSIQ, %o5
ta HV_FAST_TRAP
stx %o1, [%o2]
retl
mov %o0, %o0
+ENDPROC(pci_sun4v_msg_getmsiq)
/* %o0: devhandle
* %o1: msinum
@@ -325,12 +326,12 @@
*
* returns %o0: status
*/
- .globl pci_sun4v_msg_setmsiq
-pci_sun4v_msg_setmsiq:
+ENTRY(pci_sun4v_msg_setmsiq)
mov HV_FAST_PCI_MSG_SETMSIQ, %o5
ta HV_FAST_TRAP
retl
mov %o0, %o0
+ENDPROC(pci_sun4v_msg_setmsiq)
/* %o0: devhandle
* %o1: msinum
@@ -338,13 +339,13 @@
*
* returns %o0: status
*/
- .globl pci_sun4v_msg_getvalid
-pci_sun4v_msg_getvalid:
+ENTRY(pci_sun4v_msg_getvalid)
mov HV_FAST_PCI_MSG_GETVALID, %o5
ta HV_FAST_TRAP
stx %o1, [%o2]
retl
mov %o0, %o0
+ENDPROC(pci_sun4v_msg_getvalid)
/* %o0: devhandle
* %o1: msinum
@@ -352,10 +353,10 @@
*
* returns %o0: status
*/
- .globl pci_sun4v_msg_setvalid
-pci_sun4v_msg_setvalid:
+ENTRY(pci_sun4v_msg_setvalid)
mov HV_FAST_PCI_MSG_SETVALID, %o5
ta HV_FAST_TRAP
retl
mov %o0, %o0
+ENDPROC(pci_sun4v_msg_setvalid)
diff --git a/arch/sparc64/kernel/power.c b/arch/sparc64/kernel/power.c
index 3bb987a..076cad7 100644
--- a/arch/sparc64/kernel/power.c
+++ b/arch/sparc64/kernel/power.c
@@ -1,34 +1,17 @@
/* power.c: Power management driver.
*
- * Copyright (C) 1999, 2007 David S. Miller (davem@davemloft.net)
+ * Copyright (C) 1999, 2007, 2008 David S. Miller (davem@davemloft.net)
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/signal.h>
-#include <linux/delay.h>
#include <linux/interrupt.h>
-#include <linux/pm.h>
-#include <linux/syscalls.h>
#include <linux/reboot.h>
#include <linux/of_device.h>
-#include <asm/system.h>
-#include <asm/auxio.h>
#include <asm/prom.h>
#include <asm/io.h>
-#include <asm/sstate.h>
-#include <asm/reboot.h>
-
-#include <linux/unistd.h>
-
-/*
- * sysctl - toggle power-off restriction for serial console
- * systems in machine_power_off()
- */
-int scons_pwroff = 1;
static void __iomem *power_reg;
@@ -40,31 +23,6 @@
return IRQ_HANDLED;
}
-static void (*poweroff_method)(void) = machine_alt_power_off;
-
-void machine_power_off(void)
-{
- sstate_poweroff();
- if (strcmp(of_console_device->type, "serial") || scons_pwroff) {
- if (power_reg) {
- /* Both register bits seem to have the
- * same effect, so until I figure out
- * what the difference is...
- */
- writel(AUXIO_PCIO_CPWR_OFF | AUXIO_PCIO_SPWR_OFF, power_reg);
- } else {
- if (poweroff_method != NULL) {
- poweroff_method();
- /* not reached */
- }
- }
- }
- machine_halt();
-}
-
-void (*pm_power_off)(void) = machine_power_off;
-EXPORT_SYMBOL(pm_power_off);
-
static int __init has_button_interrupt(unsigned int irq, struct device_node *dp)
{
if (irq == 0xffffffff)
@@ -85,8 +43,6 @@
printk(KERN_INFO "%s: Control reg at %lx\n",
op->node->name, res->start);
- poweroff_method = machine_halt; /* able to use the standard halt */
-
if (has_button_interrupt(irq, op->node)) {
if (request_irq(irq,
power_handler, 0, "power", NULL) < 0)
@@ -96,7 +52,7 @@
return 0;
}
-static struct of_device_id power_match[] = {
+static struct of_device_id __initdata power_match[] = {
{
.name = "power",
},
@@ -111,8 +67,9 @@
},
};
-void __init power_init(void)
+static int __init power_init(void)
{
- of_register_driver(&power_driver, &of_platform_bus_type);
- return;
+ return of_register_driver(&power_driver, &of_platform_bus_type);
}
+
+device_initcall(power_init);
diff --git a/arch/sparc64/kernel/process.c b/arch/sparc64/kernel/process.c
index 15f4178..d5e2ace 100644
--- a/arch/sparc64/kernel/process.c
+++ b/arch/sparc64/kernel/process.c
@@ -22,7 +22,6 @@
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/user.h>
-#include <linux/reboot.h>
#include <linux/delay.h>
#include <linux/compat.h>
#include <linux/tick.h>
@@ -31,7 +30,6 @@
#include <linux/elfcore.h>
#include <linux/sysrq.h>
-#include <asm/oplib.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/page.h>
@@ -46,8 +44,6 @@
#include <asm/mmu_context.h>
#include <asm/unistd.h>
#include <asm/hypervisor.h>
-#include <asm/sstate.h>
-#include <asm/reboot.h>
#include <asm/syscalls.h>
#include <asm/irq_regs.h>
#include <asm/smp.h>
@@ -115,35 +111,6 @@
}
}
-void machine_halt(void)
-{
- sstate_halt();
- prom_halt();
- panic("Halt failed!");
-}
-
-void machine_alt_power_off(void)
-{
- sstate_poweroff();
- prom_halt_power_off();
- panic("Power-off failed!");
-}
-
-void machine_restart(char * cmd)
-{
- char *p;
-
- sstate_reboot();
- p = strchr (reboot_command, '\n');
- if (p) *p = 0;
- if (cmd)
- prom_reboot(cmd);
- if (*reboot_command)
- prom_reboot(reboot_command);
- prom_reboot("");
- panic("Reboot failed!");
-}
-
#ifdef CONFIG_COMPAT
static void show_regwindow32(struct pt_regs *regs)
{
@@ -248,7 +215,6 @@
global_reg_snapshot[this_cpu].o7 = regs->u_regs[UREG_I7];
if (regs->tstate & TSTATE_PRIV) {
- struct thread_info *tp = current_thread_info();
struct reg_window *rw;
rw = (struct reg_window *)
@@ -304,7 +270,6 @@
for_each_online_cpu(cpu) {
struct global_reg_snapshot *gp = &global_reg_snapshot[cpu];
- struct thread_info *tp;
__global_reg_poll(gp);
diff --git a/arch/sparc64/kernel/prom.c b/arch/sparc64/kernel/prom.c
index 7151513..dbba82f 100644
--- a/arch/sparc64/kernel/prom.c
+++ b/arch/sparc64/kernel/prom.c
@@ -38,7 +38,7 @@
{
struct device_node *np;
- for (np = allnodes; np != 0; np = np->allnext)
+ for (np = allnodes; np; np = np->allnext)
if (np->node == handle)
break;
@@ -59,6 +59,9 @@
}
EXPORT_SYMBOL(of_getintprop_default);
+DEFINE_MUTEX(of_set_property_mutex);
+EXPORT_SYMBOL(of_set_property_mutex);
+
int of_set_property(struct device_node *dp, const char *name, void *val, int len)
{
struct property **prevp;
@@ -82,7 +85,10 @@
void *old_val = prop->value;
int ret;
+ mutex_lock(&of_set_property_mutex);
ret = prom_setprop(dp->node, name, val, len);
+ mutex_unlock(&of_set_property_mutex);
+
err = -EINVAL;
if (ret >= 0) {
prop->value = new_val;
@@ -945,22 +951,30 @@
for (i = 0; i < ARRAY_SIZE(pci_irq_trans_table); i++) {
struct irq_trans *t = &pci_irq_trans_table[i];
- if (!strcmp(model, t->name))
- return t->init(dp);
+ if (!strcmp(model, t->name)) {
+ t->init(dp);
+ return;
+ }
}
}
#endif
#ifdef CONFIG_SBUS
if (!strcmp(dp->name, "sbus") ||
- !strcmp(dp->name, "sbi"))
- return sbus_irq_trans_init(dp);
+ !strcmp(dp->name, "sbi")) {
+ sbus_irq_trans_init(dp);
+ return;
+ }
#endif
if (!strcmp(dp->name, "fhc") &&
- !strcmp(dp->parent->name, "central"))
- return central_irq_trans_init(dp);
+ !strcmp(dp->parent->name, "central")) {
+ central_irq_trans_init(dp);
+ return;
+ }
if (!strcmp(dp->name, "virtual-devices") ||
- !strcmp(dp->name, "niu"))
- return sun4v_vdev_irq_trans_init(dp);
+ !strcmp(dp->name, "niu")) {
+ sun4v_vdev_irq_trans_init(dp);
+ return;
+ }
}
static int is_root_node(const struct device_node *dp)
@@ -1231,32 +1245,49 @@
if (parent != NULL) {
if (!strcmp(parent->type, "pci") ||
- !strcmp(parent->type, "pciex"))
- return pci_path_component(dp, tmp_buf);
- if (!strcmp(parent->type, "sbus"))
- return sbus_path_component(dp, tmp_buf);
- if (!strcmp(parent->type, "upa"))
- return upa_path_component(dp, tmp_buf);
- if (!strcmp(parent->type, "ebus"))
- return ebus_path_component(dp, tmp_buf);
+ !strcmp(parent->type, "pciex")) {
+ pci_path_component(dp, tmp_buf);
+ return;
+ }
+ if (!strcmp(parent->type, "sbus")) {
+ sbus_path_component(dp, tmp_buf);
+ return;
+ }
+ if (!strcmp(parent->type, "upa")) {
+ upa_path_component(dp, tmp_buf);
+ return;
+ }
+ if (!strcmp(parent->type, "ebus")) {
+ ebus_path_component(dp, tmp_buf);
+ return;
+ }
if (!strcmp(parent->name, "usb") ||
- !strcmp(parent->name, "hub"))
- return usb_path_component(dp, tmp_buf);
- if (!strcmp(parent->type, "i2c"))
- return i2c_path_component(dp, tmp_buf);
- if (!strcmp(parent->type, "firewire"))
- return ieee1394_path_component(dp, tmp_buf);
- if (!strcmp(parent->type, "virtual-devices"))
- return vdev_path_component(dp, tmp_buf);
-
+ !strcmp(parent->name, "hub")) {
+ usb_path_component(dp, tmp_buf);
+ return;
+ }
+ if (!strcmp(parent->type, "i2c")) {
+ i2c_path_component(dp, tmp_buf);
+ return;
+ }
+ if (!strcmp(parent->type, "firewire")) {
+ ieee1394_path_component(dp, tmp_buf);
+ return;
+ }
+ if (!strcmp(parent->type, "virtual-devices")) {
+ vdev_path_component(dp, tmp_buf);
+ return;
+ }
/* "isa" is handled with platform naming */
}
/* Use platform naming convention. */
- if (tlb_type == hypervisor)
- return sun4v_path_component(dp, tmp_buf);
- else
- return sun4u_path_component(dp, tmp_buf);
+ if (tlb_type == hypervisor) {
+ sun4v_path_component(dp, tmp_buf);
+ return;
+ } else {
+ sun4u_path_component(dp, tmp_buf);
+ }
}
static char * __init build_path_component(struct device_node *dp)
diff --git a/arch/sparc64/kernel/psycho_common.c b/arch/sparc64/kernel/psycho_common.c
new file mode 100644
index 0000000..7909964
--- /dev/null
+++ b/arch/sparc64/kernel/psycho_common.c
@@ -0,0 +1,470 @@
+/* psycho_common.c: Code common to PSYCHO and derivative PCI controllers.
+ *
+ * Copyright (C) 2008 David S. Miller <davem@davemloft.net>
+ */
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+
+#include <asm/upa.h>
+
+#include "pci_impl.h"
+#include "iommu_common.h"
+#include "psycho_common.h"
+
+#define PSYCHO_STRBUF_CTRL_DENAB 0x0000000000000002UL
+#define PSYCHO_STCERR_WRITE 0x0000000000000002UL
+#define PSYCHO_STCERR_READ 0x0000000000000001UL
+#define PSYCHO_STCTAG_PPN 0x0fffffff00000000UL
+#define PSYCHO_STCTAG_VPN 0x00000000ffffe000UL
+#define PSYCHO_STCTAG_VALID 0x0000000000000002UL
+#define PSYCHO_STCTAG_WRITE 0x0000000000000001UL
+#define PSYCHO_STCLINE_LINDX 0x0000000001e00000UL
+#define PSYCHO_STCLINE_SPTR 0x00000000001f8000UL
+#define PSYCHO_STCLINE_LADDR 0x0000000000007f00UL
+#define PSYCHO_STCLINE_EPTR 0x00000000000000fcUL
+#define PSYCHO_STCLINE_VALID 0x0000000000000002UL
+#define PSYCHO_STCLINE_FOFN 0x0000000000000001UL
+
+static DEFINE_SPINLOCK(stc_buf_lock);
+static unsigned long stc_error_buf[128];
+static unsigned long stc_tag_buf[16];
+static unsigned long stc_line_buf[16];
+
+static void psycho_check_stc_error(struct pci_pbm_info *pbm)
+{
+ unsigned long err_base, tag_base, line_base;
+ struct strbuf *strbuf = &pbm->stc;
+ u64 control;
+ int i;
+
+ if (!strbuf->strbuf_control)
+ return;
+
+ err_base = strbuf->strbuf_err_stat;
+ tag_base = strbuf->strbuf_tag_diag;
+ line_base = strbuf->strbuf_line_diag;
+
+ spin_lock(&stc_buf_lock);
+
+ /* This is __REALLY__ dangerous. When we put the streaming
+ * buffer into diagnostic mode to probe it's tags and error
+ * status, we _must_ clear all of the line tag valid bits
+ * before re-enabling the streaming buffer. If any dirty data
+ * lives in the STC when we do this, we will end up
+ * invalidating it before it has a chance to reach main
+ * memory.
+ */
+ control = upa_readq(strbuf->strbuf_control);
+ upa_writeq(control | PSYCHO_STRBUF_CTRL_DENAB, strbuf->strbuf_control);
+ for (i = 0; i < 128; i++) {
+ u64 val;
+
+ val = upa_readq(err_base + (i * 8UL));
+ upa_writeq(0UL, err_base + (i * 8UL));
+ stc_error_buf[i] = val;
+ }
+ for (i = 0; i < 16; i++) {
+ stc_tag_buf[i] = upa_readq(tag_base + (i * 8UL));
+ stc_line_buf[i] = upa_readq(line_base + (i * 8UL));
+ upa_writeq(0UL, tag_base + (i * 8UL));
+ upa_writeq(0UL, line_base + (i * 8UL));
+ }
+
+ /* OK, state is logged, exit diagnostic mode. */
+ upa_writeq(control, strbuf->strbuf_control);
+
+ for (i = 0; i < 16; i++) {
+ int j, saw_error, first, last;
+
+ saw_error = 0;
+ first = i * 8;
+ last = first + 8;
+ for (j = first; j < last; j++) {
+ u64 errval = stc_error_buf[j];
+ if (errval != 0) {
+ saw_error++;
+ printk(KERN_ERR "%s: STC_ERR(%d)[wr(%d)"
+ "rd(%d)]\n",
+ pbm->name,
+ j,
+ (errval & PSYCHO_STCERR_WRITE) ? 1 : 0,
+ (errval & PSYCHO_STCERR_READ) ? 1 : 0);
+ }
+ }
+ if (saw_error != 0) {
+ u64 tagval = stc_tag_buf[i];
+ u64 lineval = stc_line_buf[i];
+ printk(KERN_ERR "%s: STC_TAG(%d)[PA(%016lx)VA(%08lx)"
+ "V(%d)W(%d)]\n",
+ pbm->name,
+ i,
+ ((tagval & PSYCHO_STCTAG_PPN) >> 19UL),
+ (tagval & PSYCHO_STCTAG_VPN),
+ ((tagval & PSYCHO_STCTAG_VALID) ? 1 : 0),
+ ((tagval & PSYCHO_STCTAG_WRITE) ? 1 : 0));
+ printk(KERN_ERR "%s: STC_LINE(%d)[LIDX(%lx)SP(%lx)"
+ "LADDR(%lx)EP(%lx)V(%d)FOFN(%d)]\n",
+ pbm->name,
+ i,
+ ((lineval & PSYCHO_STCLINE_LINDX) >> 21UL),
+ ((lineval & PSYCHO_STCLINE_SPTR) >> 15UL),
+ ((lineval & PSYCHO_STCLINE_LADDR) >> 8UL),
+ ((lineval & PSYCHO_STCLINE_EPTR) >> 2UL),
+ ((lineval & PSYCHO_STCLINE_VALID) ? 1 : 0),
+ ((lineval & PSYCHO_STCLINE_FOFN) ? 1 : 0));
+ }
+ }
+
+ spin_unlock(&stc_buf_lock);
+}
+
+#define PSYCHO_IOMMU_TAG 0xa580UL
+#define PSYCHO_IOMMU_DATA 0xa600UL
+
+static void psycho_record_iommu_tags_and_data(struct pci_pbm_info *pbm,
+ u64 *tag, u64 *data)
+{
+ int i;
+
+ for (i = 0; i < 16; i++) {
+ unsigned long base = pbm->controller_regs;
+ unsigned long off = i * 8UL;
+
+ tag[i] = upa_readq(base + PSYCHO_IOMMU_TAG+off);
+ data[i] = upa_readq(base + PSYCHO_IOMMU_DATA+off);
+
+ /* Now clear out the entry. */
+ upa_writeq(0, base + PSYCHO_IOMMU_TAG + off);
+ upa_writeq(0, base + PSYCHO_IOMMU_DATA + off);
+ }
+}
+
+#define PSYCHO_IOMMU_TAG_ERRSTS (0x3UL << 23UL)
+#define PSYCHO_IOMMU_TAG_ERR (0x1UL << 22UL)
+#define PSYCHO_IOMMU_TAG_WRITE (0x1UL << 21UL)
+#define PSYCHO_IOMMU_TAG_STREAM (0x1UL << 20UL)
+#define PSYCHO_IOMMU_TAG_SIZE (0x1UL << 19UL)
+#define PSYCHO_IOMMU_TAG_VPAGE 0x7ffffUL
+#define PSYCHO_IOMMU_DATA_VALID (1UL << 30UL)
+#define PSYCHO_IOMMU_DATA_CACHE (1UL << 28UL)
+#define PSYCHO_IOMMU_DATA_PPAGE 0xfffffffUL
+
+static void psycho_dump_iommu_tags_and_data(struct pci_pbm_info *pbm,
+ u64 *tag, u64 *data)
+{
+ int i;
+
+ for (i = 0; i < 16; i++) {
+ u64 tag_val, data_val;
+ const char *type_str;
+ tag_val = tag[i];
+ if (!(tag_val & PSYCHO_IOMMU_TAG_ERR))
+ continue;
+
+ data_val = data[i];
+ switch((tag_val & PSYCHO_IOMMU_TAG_ERRSTS) >> 23UL) {
+ case 0:
+ type_str = "Protection Error";
+ break;
+ case 1:
+ type_str = "Invalid Error";
+ break;
+ case 2:
+ type_str = "TimeOut Error";
+ break;
+ case 3:
+ default:
+ type_str = "ECC Error";
+ break;
+ }
+
+ printk(KERN_ERR "%s: IOMMU TAG(%d)[error(%s) wr(%d) "
+ "str(%d) sz(%dK) vpg(%08lx)]\n",
+ pbm->name, i, type_str,
+ ((tag_val & PSYCHO_IOMMU_TAG_WRITE) ? 1 : 0),
+ ((tag_val & PSYCHO_IOMMU_TAG_STREAM) ? 1 : 0),
+ ((tag_val & PSYCHO_IOMMU_TAG_SIZE) ? 64 : 8),
+ (tag_val & PSYCHO_IOMMU_TAG_VPAGE) << IOMMU_PAGE_SHIFT);
+ printk(KERN_ERR "%s: IOMMU DATA(%d)[valid(%d) cache(%d) "
+ "ppg(%016lx)]\n",
+ pbm->name, i,
+ ((data_val & PSYCHO_IOMMU_DATA_VALID) ? 1 : 0),
+ ((data_val & PSYCHO_IOMMU_DATA_CACHE) ? 1 : 0),
+ (data_val & PSYCHO_IOMMU_DATA_PPAGE)<<IOMMU_PAGE_SHIFT);
+ }
+}
+
+#define PSYCHO_IOMMU_CTRL_XLTESTAT 0x0000000006000000UL
+#define PSYCHO_IOMMU_CTRL_XLTEERR 0x0000000001000000UL
+
+void psycho_check_iommu_error(struct pci_pbm_info *pbm,
+ unsigned long afsr,
+ unsigned long afar,
+ enum psycho_error_type type)
+{
+ u64 control, iommu_tag[16], iommu_data[16];
+ struct iommu *iommu = pbm->iommu;
+ unsigned long flags;
+
+ spin_lock_irqsave(&iommu->lock, flags);
+ control = upa_readq(iommu->iommu_control);
+ if (control & PSYCHO_IOMMU_CTRL_XLTEERR) {
+ const char *type_str;
+
+ control &= ~PSYCHO_IOMMU_CTRL_XLTEERR;
+ upa_writeq(control, iommu->iommu_control);
+
+ switch ((control & PSYCHO_IOMMU_CTRL_XLTESTAT) >> 25UL) {
+ case 0:
+ type_str = "Protection Error";
+ break;
+ case 1:
+ type_str = "Invalid Error";
+ break;
+ case 2:
+ type_str = "TimeOut Error";
+ break;
+ case 3:
+ default:
+ type_str = "ECC Error";
+ break;
+ };
+ printk(KERN_ERR "%s: IOMMU Error, type[%s]\n",
+ pbm->name, type_str);
+
+ /* It is very possible for another DVMA to occur while
+ * we do this probe, and corrupt the system further.
+ * But we are so screwed at this point that we are
+ * likely to crash hard anyways, so get as much
+ * diagnostic information to the console as we can.
+ */
+ psycho_record_iommu_tags_and_data(pbm, iommu_tag, iommu_data);
+ psycho_dump_iommu_tags_and_data(pbm, iommu_tag, iommu_data);
+ }
+ psycho_check_stc_error(pbm);
+ spin_unlock_irqrestore(&iommu->lock, flags);
+}
+
+#define PSYCHO_PCICTRL_SBH_ERR 0x0000000800000000UL
+#define PSYCHO_PCICTRL_SERR 0x0000000400000000UL
+
+static irqreturn_t psycho_pcierr_intr_other(struct pci_pbm_info *pbm)
+{
+ irqreturn_t ret = IRQ_NONE;
+ u64 csr, csr_error_bits;
+ u16 stat, *addr;
+
+ csr = upa_readq(pbm->pci_csr);
+ csr_error_bits = csr & (PSYCHO_PCICTRL_SBH_ERR | PSYCHO_PCICTRL_SERR);
+ if (csr_error_bits) {
+ /* Clear the errors. */
+ upa_writeq(csr, pbm->pci_csr);
+
+ /* Log 'em. */
+ if (csr_error_bits & PSYCHO_PCICTRL_SBH_ERR)
+ printk(KERN_ERR "%s: PCI streaming byte hole "
+ "error asserted.\n", pbm->name);
+ if (csr_error_bits & PSYCHO_PCICTRL_SERR)
+ printk(KERN_ERR "%s: PCI SERR signal asserted.\n",
+ pbm->name);
+ ret = IRQ_HANDLED;
+ }
+ addr = psycho_pci_config_mkaddr(pbm, pbm->pci_first_busno,
+ 0, PCI_STATUS);
+ pci_config_read16(addr, &stat);
+ if (stat & (PCI_STATUS_PARITY |
+ PCI_STATUS_SIG_TARGET_ABORT |
+ PCI_STATUS_REC_TARGET_ABORT |
+ PCI_STATUS_REC_MASTER_ABORT |
+ PCI_STATUS_SIG_SYSTEM_ERROR)) {
+ printk(KERN_ERR "%s: PCI bus error, PCI_STATUS[%04x]\n",
+ pbm->name, stat);
+ pci_config_write16(addr, 0xffff);
+ ret = IRQ_HANDLED;
+ }
+ return ret;
+}
+
+#define PSYCHO_PCIAFSR_PMA 0x8000000000000000UL
+#define PSYCHO_PCIAFSR_PTA 0x4000000000000000UL
+#define PSYCHO_PCIAFSR_PRTRY 0x2000000000000000UL
+#define PSYCHO_PCIAFSR_PPERR 0x1000000000000000UL
+#define PSYCHO_PCIAFSR_SMA 0x0800000000000000UL
+#define PSYCHO_PCIAFSR_STA 0x0400000000000000UL
+#define PSYCHO_PCIAFSR_SRTRY 0x0200000000000000UL
+#define PSYCHO_PCIAFSR_SPERR 0x0100000000000000UL
+#define PSYCHO_PCIAFSR_RESV1 0x00ff000000000000UL
+#define PSYCHO_PCIAFSR_BMSK 0x0000ffff00000000UL
+#define PSYCHO_PCIAFSR_BLK 0x0000000080000000UL
+#define PSYCHO_PCIAFSR_RESV2 0x0000000040000000UL
+#define PSYCHO_PCIAFSR_MID 0x000000003e000000UL
+#define PSYCHO_PCIAFSR_RESV3 0x0000000001ffffffUL
+
+irqreturn_t psycho_pcierr_intr(int irq, void *dev_id)
+{
+ struct pci_pbm_info *pbm = dev_id;
+ u64 afsr, afar, error_bits;
+ int reported;
+
+ afsr = upa_readq(pbm->pci_afsr);
+ afar = upa_readq(pbm->pci_afar);
+ error_bits = afsr &
+ (PSYCHO_PCIAFSR_PMA | PSYCHO_PCIAFSR_PTA |
+ PSYCHO_PCIAFSR_PRTRY | PSYCHO_PCIAFSR_PPERR |
+ PSYCHO_PCIAFSR_SMA | PSYCHO_PCIAFSR_STA |
+ PSYCHO_PCIAFSR_SRTRY | PSYCHO_PCIAFSR_SPERR);
+ if (!error_bits)
+ return psycho_pcierr_intr_other(pbm);
+ upa_writeq(error_bits, pbm->pci_afsr);
+ printk(KERN_ERR "%s: PCI Error, primary error type[%s]\n",
+ pbm->name,
+ (((error_bits & PSYCHO_PCIAFSR_PMA) ?
+ "Master Abort" :
+ ((error_bits & PSYCHO_PCIAFSR_PTA) ?
+ "Target Abort" :
+ ((error_bits & PSYCHO_PCIAFSR_PRTRY) ?
+ "Excessive Retries" :
+ ((error_bits & PSYCHO_PCIAFSR_PPERR) ?
+ "Parity Error" : "???"))))));
+ printk(KERN_ERR "%s: bytemask[%04lx] UPA_MID[%02lx] was_block(%d)\n",
+ pbm->name,
+ (afsr & PSYCHO_PCIAFSR_BMSK) >> 32UL,
+ (afsr & PSYCHO_PCIAFSR_MID) >> 25UL,
+ (afsr & PSYCHO_PCIAFSR_BLK) ? 1 : 0);
+ printk(KERN_ERR "%s: PCI AFAR [%016lx]\n", pbm->name, afar);
+ printk(KERN_ERR "%s: PCI Secondary errors [", pbm->name);
+ reported = 0;
+ if (afsr & PSYCHO_PCIAFSR_SMA) {
+ reported++;
+ printk("(Master Abort)");
+ }
+ if (afsr & PSYCHO_PCIAFSR_STA) {
+ reported++;
+ printk("(Target Abort)");
+ }
+ if (afsr & PSYCHO_PCIAFSR_SRTRY) {
+ reported++;
+ printk("(Excessive Retries)");
+ }
+ if (afsr & PSYCHO_PCIAFSR_SPERR) {
+ reported++;
+ printk("(Parity Error)");
+ }
+ if (!reported)
+ printk("(none)");
+ printk("]\n");
+
+ if (error_bits & (PSYCHO_PCIAFSR_PTA | PSYCHO_PCIAFSR_STA)) {
+ psycho_check_iommu_error(pbm, afsr, afar, PCI_ERR);
+ pci_scan_for_target_abort(pbm, pbm->pci_bus);
+ }
+ if (error_bits & (PSYCHO_PCIAFSR_PMA | PSYCHO_PCIAFSR_SMA))
+ pci_scan_for_master_abort(pbm, pbm->pci_bus);
+
+ if (error_bits & (PSYCHO_PCIAFSR_PPERR | PSYCHO_PCIAFSR_SPERR))
+ pci_scan_for_parity_error(pbm, pbm->pci_bus);
+
+ return IRQ_HANDLED;
+}
+
+static void psycho_iommu_flush(struct pci_pbm_info *pbm)
+{
+ int i;
+
+ for (i = 0; i < 16; i++) {
+ unsigned long off = i * 8;
+
+ upa_writeq(0, pbm->controller_regs + PSYCHO_IOMMU_TAG + off);
+ upa_writeq(0, pbm->controller_regs + PSYCHO_IOMMU_DATA + off);
+ }
+}
+
+#define PSYCHO_IOMMU_CONTROL 0x0200UL
+#define PSYCHO_IOMMU_CTRL_TSBSZ 0x0000000000070000UL
+#define PSYCHO_IOMMU_TSBSZ_1K 0x0000000000000000UL
+#define PSYCHO_IOMMU_TSBSZ_2K 0x0000000000010000UL
+#define PSYCHO_IOMMU_TSBSZ_4K 0x0000000000020000UL
+#define PSYCHO_IOMMU_TSBSZ_8K 0x0000000000030000UL
+#define PSYCHO_IOMMU_TSBSZ_16K 0x0000000000040000UL
+#define PSYCHO_IOMMU_TSBSZ_32K 0x0000000000050000UL
+#define PSYCHO_IOMMU_TSBSZ_64K 0x0000000000060000UL
+#define PSYCHO_IOMMU_TSBSZ_128K 0x0000000000070000UL
+#define PSYCHO_IOMMU_CTRL_TBWSZ 0x0000000000000004UL
+#define PSYCHO_IOMMU_CTRL_DENAB 0x0000000000000002UL
+#define PSYCHO_IOMMU_CTRL_ENAB 0x0000000000000001UL
+#define PSYCHO_IOMMU_FLUSH 0x0210UL
+#define PSYCHO_IOMMU_TSBBASE 0x0208UL
+
+int psycho_iommu_init(struct pci_pbm_info *pbm, int tsbsize,
+ u32 dvma_offset, u32 dma_mask,
+ unsigned long write_complete_offset)
+{
+ struct iommu *iommu = pbm->iommu;
+ u64 control;
+ int err;
+
+ iommu->iommu_control = pbm->controller_regs + PSYCHO_IOMMU_CONTROL;
+ iommu->iommu_tsbbase = pbm->controller_regs + PSYCHO_IOMMU_TSBBASE;
+ iommu->iommu_flush = pbm->controller_regs + PSYCHO_IOMMU_FLUSH;
+ iommu->iommu_tags = pbm->controller_regs + PSYCHO_IOMMU_TAG;
+ iommu->write_complete_reg = (pbm->controller_regs +
+ write_complete_offset);
+
+ iommu->iommu_ctxflush = 0;
+
+ control = upa_readq(iommu->iommu_control);
+ control |= PSYCHO_IOMMU_CTRL_DENAB;
+ upa_writeq(control, iommu->iommu_control);
+
+ psycho_iommu_flush(pbm);
+
+ /* Leave diag mode enabled for full-flushing done in pci_iommu.c */
+ err = iommu_table_init(iommu, tsbsize * 1024 * 8,
+ dvma_offset, dma_mask, pbm->numa_node);
+ if (err)
+ return err;
+
+ upa_writeq(__pa(iommu->page_table), iommu->iommu_tsbbase);
+
+ control = upa_readq(iommu->iommu_control);
+ control &= ~(PSYCHO_IOMMU_CTRL_TSBSZ | PSYCHO_IOMMU_CTRL_TBWSZ);
+ control |= PSYCHO_IOMMU_CTRL_ENAB;
+
+ switch (tsbsize) {
+ case 64:
+ control |= PSYCHO_IOMMU_TSBSZ_64K;
+ break;
+ case 128:
+ control |= PSYCHO_IOMMU_TSBSZ_128K;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ upa_writeq(control, iommu->iommu_control);
+
+ return 0;
+
+}
+
+void psycho_pbm_init_common(struct pci_pbm_info *pbm, struct of_device *op,
+ const char *chip_name, int chip_type)
+{
+ struct device_node *dp = op->node;
+
+ pbm->name = dp->full_name;
+ pbm->numa_node = -1;
+ pbm->chip_type = chip_type;
+ pbm->chip_version = of_getintprop_default(dp, "version#", 0);
+ pbm->chip_revision = of_getintprop_default(dp, "module-revision#", 0);
+ pbm->op = op;
+ pbm->pci_ops = &sun4u_pci_ops;
+ pbm->config_space_reg_bits = 8;
+ pbm->index = pci_num_pbms++;
+ pci_get_pbm_props(pbm);
+ pci_determine_mem_io_space(pbm);
+
+ printk(KERN_INFO "%s: %s PCI Bus Module ver[%x:%x]\n",
+ pbm->name, chip_name,
+ pbm->chip_version, pbm->chip_revision);
+}
diff --git a/arch/sparc64/kernel/psycho_common.h b/arch/sparc64/kernel/psycho_common.h
new file mode 100644
index 0000000..092c278
--- /dev/null
+++ b/arch/sparc64/kernel/psycho_common.h
@@ -0,0 +1,48 @@
+#ifndef _PSYCHO_COMMON_H
+#define _PSYCHO_COMMON_H
+
+/* U2P Programmer's Manual, page 13-55, configuration space
+ * address format:
+ *
+ * 32 24 23 16 15 11 10 8 7 2 1 0
+ * ---------------------------------------------------------
+ * |0 0 0 0 0 0 0 0 1| bus | device | function | reg | 0 0 |
+ * ---------------------------------------------------------
+ */
+#define PSYCHO_CONFIG_BASE(PBM) \
+ ((PBM)->config_space | (1UL << 24))
+#define PSYCHO_CONFIG_ENCODE(BUS, DEVFN, REG) \
+ (((unsigned long)(BUS) << 16) | \
+ ((unsigned long)(DEVFN) << 8) | \
+ ((unsigned long)(REG)))
+
+static inline void *psycho_pci_config_mkaddr(struct pci_pbm_info *pbm,
+ unsigned char bus,
+ unsigned int devfn,
+ int where)
+{
+ return (void *)
+ (PSYCHO_CONFIG_BASE(pbm) |
+ PSYCHO_CONFIG_ENCODE(bus, devfn, where));
+}
+
+enum psycho_error_type {
+ UE_ERR, CE_ERR, PCI_ERR
+};
+
+extern void psycho_check_iommu_error(struct pci_pbm_info *pbm,
+ unsigned long afsr,
+ unsigned long afar,
+ enum psycho_error_type type);
+
+extern irqreturn_t psycho_pcierr_intr(int irq, void *dev_id);
+
+extern int psycho_iommu_init(struct pci_pbm_info *pbm, int tsbsize,
+ u32 dvma_offset, u32 dma_mask,
+ unsigned long write_complete_offset);
+
+extern void psycho_pbm_init_common(struct pci_pbm_info *pbm,
+ struct of_device *op,
+ const char *chip_name, int chip_type);
+
+#endif /* _PSYCHO_COMMON_H */
diff --git a/arch/sparc64/kernel/ptrace.c b/arch/sparc64/kernel/ptrace.c
index 10306e4..f43adbc 100644
--- a/arch/sparc64/kernel/ptrace.c
+++ b/arch/sparc64/kernel/ptrace.c
@@ -1050,31 +1050,17 @@
return ret;
}
-asmlinkage int syscall_trace(struct pt_regs *regs, int syscall_exit_p)
+asmlinkage int syscall_trace_enter(struct pt_regs *regs)
{
int ret = 0;
/* do the secure computing check first */
secure_computing(regs->u_regs[UREG_G1]);
- if (unlikely(current->audit_context) && syscall_exit_p) {
- unsigned long tstate = regs->tstate;
- int result = AUDITSC_SUCCESS;
+ if (test_thread_flag(TIF_SYSCALL_TRACE))
+ ret = tracehook_report_syscall_entry(regs);
- if (unlikely(tstate & (TSTATE_XCARRY | TSTATE_ICARRY)))
- result = AUDITSC_FAILURE;
-
- audit_syscall_exit(result, regs->u_regs[UREG_I0]);
- }
-
- if (test_thread_flag(TIF_SYSCALL_TRACE)) {
- if (syscall_exit_p)
- tracehook_report_syscall_exit(regs, 0);
- else
- ret = tracehook_report_syscall_entry(regs);
- }
-
- if (unlikely(current->audit_context) && !syscall_exit_p && !ret)
+ if (unlikely(current->audit_context) && !ret)
audit_syscall_entry((test_thread_flag(TIF_32BIT) ?
AUDIT_ARCH_SPARC :
AUDIT_ARCH_SPARC64),
@@ -1086,3 +1072,19 @@
return ret;
}
+
+asmlinkage void syscall_trace_leave(struct pt_regs *regs)
+{
+ if (unlikely(current->audit_context)) {
+ unsigned long tstate = regs->tstate;
+ int result = AUDITSC_SUCCESS;
+
+ if (unlikely(tstate & (TSTATE_XCARRY | TSTATE_ICARRY)))
+ result = AUDITSC_FAILURE;
+
+ audit_syscall_exit(result, regs->u_regs[UREG_I0]);
+ }
+
+ if (test_thread_flag(TIF_SYSCALL_TRACE))
+ tracehook_report_syscall_exit(regs, 0);
+}
diff --git a/arch/sparc64/kernel/reboot.c b/arch/sparc64/kernel/reboot.c
new file mode 100644
index 0000000..ef89d3d
--- /dev/null
+++ b/arch/sparc64/kernel/reboot.c
@@ -0,0 +1,53 @@
+/* reboot.c: reboot/shutdown/halt/poweroff handling
+ *
+ * Copyright (C) 2008 David S. Miller <davem@davemloft.net>
+ */
+#include <linux/kernel.h>
+#include <linux/reboot.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+
+#include <asm/system.h>
+#include <asm/oplib.h>
+#include <asm/prom.h>
+
+/* sysctl - toggle power-off restriction for serial console
+ * systems in machine_power_off()
+ */
+int scons_pwroff = 1;
+
+/* This isn't actually used, it exists merely to satisfy the
+ * reference in kernel/sys.c
+ */
+void (*pm_power_off)(void) = machine_power_off;
+EXPORT_SYMBOL(pm_power_off);
+
+void machine_power_off(void)
+{
+ if (strcmp(of_console_device->type, "serial") || scons_pwroff)
+ prom_halt_power_off();
+
+ prom_halt();
+}
+
+void machine_halt(void)
+{
+ prom_halt();
+ panic("Halt failed!");
+}
+
+void machine_restart(char *cmd)
+{
+ char *p;
+
+ p = strchr(reboot_command, '\n');
+ if (p)
+ *p = 0;
+ if (cmd)
+ prom_reboot(cmd);
+ if (*reboot_command)
+ prom_reboot(reboot_command);
+ prom_reboot("");
+ panic("Reboot failed!");
+}
+
diff --git a/arch/sparc64/kernel/sbus.c b/arch/sparc64/kernel/sbus.c
index e33a8a6..2ead310 100644
--- a/arch/sparc64/kernel/sbus.c
+++ b/arch/sparc64/kernel/sbus.c
@@ -11,15 +11,17 @@
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <asm/page.h>
-#include <asm/sbus.h>
#include <asm/io.h>
#include <asm/upa.h>
#include <asm/cache.h>
#include <asm/dma.h>
#include <asm/irq.h>
#include <asm/prom.h>
+#include <asm/oplib.h>
#include <asm/starfire.h>
#include "iommu_common.h"
@@ -52,13 +54,23 @@
#define STRBUF_TAG_VALID 0x02UL
/* Enable 64-bit DVMA mode for the given device. */
-void sbus_set_sbus64(struct sbus_dev *sdev, int bursts)
+void sbus_set_sbus64(struct device *dev, int bursts)
{
- struct iommu *iommu = sdev->ofdev.dev.archdata.iommu;
- int slot = sdev->slot;
+ struct iommu *iommu = dev->archdata.iommu;
+ struct of_device *op = to_of_device(dev);
+ const struct linux_prom_registers *regs;
unsigned long cfg_reg;
+ int slot;
u64 val;
+ regs = of_get_property(op->node, "reg", NULL);
+ if (!regs) {
+ printk(KERN_ERR "sbus_set_sbus64: Cannot find regs for %s\n",
+ op->node->full_name);
+ return;
+ }
+ slot = regs->which_io;
+
cfg_reg = iommu->write_complete_reg;
switch (slot) {
case 0:
@@ -191,10 +203,9 @@
return imap + diff;
}
-unsigned int sbus_build_irq(void *buscookie, unsigned int ino)
+static unsigned int sbus_build_irq(struct of_device *op, unsigned int ino)
{
- struct sbus_bus *sbus = (struct sbus_bus *)buscookie;
- struct iommu *iommu = sbus->ofdev.dev.archdata.iommu;
+ struct iommu *iommu = op->dev.archdata.iommu;
unsigned long reg_base = iommu->write_complete_reg - 0x2000UL;
unsigned long imap, iclr;
int sbus_level = 0;
@@ -255,12 +266,12 @@
#define SYSIO_UEAFSR_RESV2 0x0000001fffffffffUL /* Reserved */
static irqreturn_t sysio_ue_handler(int irq, void *dev_id)
{
- struct sbus_bus *sbus = dev_id;
- struct iommu *iommu = sbus->ofdev.dev.archdata.iommu;
+ struct of_device *op = dev_id;
+ struct iommu *iommu = op->dev.archdata.iommu;
unsigned long reg_base = iommu->write_complete_reg - 0x2000UL;
unsigned long afsr_reg, afar_reg;
unsigned long afsr, afar, error_bits;
- int reported;
+ int reported, portid;
afsr_reg = reg_base + SYSIO_UE_AFSR;
afar_reg = reg_base + SYSIO_UE_AFAR;
@@ -275,9 +286,11 @@
SYSIO_UEAFSR_SPIO | SYSIO_UEAFSR_SDRD | SYSIO_UEAFSR_SDWR);
upa_writeq(error_bits, afsr_reg);
+ portid = of_getintprop_default(op->node, "portid", -1);
+
/* Log the error. */
printk("SYSIO[%x]: Uncorrectable ECC Error, primary error type[%s]\n",
- sbus->portid,
+ portid,
(((error_bits & SYSIO_UEAFSR_PPIO) ?
"PIO" :
((error_bits & SYSIO_UEAFSR_PDRD) ?
@@ -285,12 +298,12 @@
((error_bits & SYSIO_UEAFSR_PDWR) ?
"DVMA Write" : "???")))));
printk("SYSIO[%x]: DOFF[%lx] SIZE[%lx] MID[%lx]\n",
- sbus->portid,
+ portid,
(afsr & SYSIO_UEAFSR_DOFF) >> 45UL,
(afsr & SYSIO_UEAFSR_SIZE) >> 42UL,
(afsr & SYSIO_UEAFSR_MID) >> 37UL);
- printk("SYSIO[%x]: AFAR[%016lx]\n", sbus->portid, afar);
- printk("SYSIO[%x]: Secondary UE errors [", sbus->portid);
+ printk("SYSIO[%x]: AFAR[%016lx]\n", portid, afar);
+ printk("SYSIO[%x]: Secondary UE errors [", portid);
reported = 0;
if (afsr & SYSIO_UEAFSR_SPIO) {
reported++;
@@ -327,12 +340,12 @@
#define SYSIO_CEAFSR_RESV2 0x0000001fffffffffUL /* Reserved */
static irqreturn_t sysio_ce_handler(int irq, void *dev_id)
{
- struct sbus_bus *sbus = dev_id;
- struct iommu *iommu = sbus->ofdev.dev.archdata.iommu;
+ struct of_device *op = dev_id;
+ struct iommu *iommu = op->dev.archdata.iommu;
unsigned long reg_base = iommu->write_complete_reg - 0x2000UL;
unsigned long afsr_reg, afar_reg;
unsigned long afsr, afar, error_bits;
- int reported;
+ int reported, portid;
afsr_reg = reg_base + SYSIO_CE_AFSR;
afar_reg = reg_base + SYSIO_CE_AFAR;
@@ -347,8 +360,10 @@
SYSIO_CEAFSR_SPIO | SYSIO_CEAFSR_SDRD | SYSIO_CEAFSR_SDWR);
upa_writeq(error_bits, afsr_reg);
+ portid = of_getintprop_default(op->node, "portid", -1);
+
printk("SYSIO[%x]: Correctable ECC Error, primary error type[%s]\n",
- sbus->portid,
+ portid,
(((error_bits & SYSIO_CEAFSR_PPIO) ?
"PIO" :
((error_bits & SYSIO_CEAFSR_PDRD) ?
@@ -360,14 +375,14 @@
* XXX UDB CE trap handler does... -DaveM
*/
printk("SYSIO[%x]: DOFF[%lx] ECC Syndrome[%lx] Size[%lx] MID[%lx]\n",
- sbus->portid,
+ portid,
(afsr & SYSIO_CEAFSR_DOFF) >> 45UL,
(afsr & SYSIO_CEAFSR_ESYND) >> 48UL,
(afsr & SYSIO_CEAFSR_SIZE) >> 42UL,
(afsr & SYSIO_CEAFSR_MID) >> 37UL);
- printk("SYSIO[%x]: AFAR[%016lx]\n", sbus->portid, afar);
+ printk("SYSIO[%x]: AFAR[%016lx]\n", portid, afar);
- printk("SYSIO[%x]: Secondary CE errors [", sbus->portid);
+ printk("SYSIO[%x]: Secondary CE errors [", portid);
reported = 0;
if (afsr & SYSIO_CEAFSR_SPIO) {
reported++;
@@ -404,11 +419,11 @@
#define SYSIO_SBAFSR_RESV3 0x0000001fffffffffUL /* Reserved */
static irqreturn_t sysio_sbus_error_handler(int irq, void *dev_id)
{
- struct sbus_bus *sbus = dev_id;
- struct iommu *iommu = sbus->ofdev.dev.archdata.iommu;
+ struct of_device *op = dev_id;
+ struct iommu *iommu = op->dev.archdata.iommu;
unsigned long afsr_reg, afar_reg, reg_base;
unsigned long afsr, afar, error_bits;
- int reported;
+ int reported, portid;
reg_base = iommu->write_complete_reg - 0x2000UL;
afsr_reg = reg_base + SYSIO_SBUS_AFSR;
@@ -423,9 +438,11 @@
SYSIO_SBAFSR_SLE | SYSIO_SBAFSR_STO | SYSIO_SBAFSR_SBERR);
upa_writeq(error_bits, afsr_reg);
+ portid = of_getintprop_default(op->node, "portid", -1);
+
/* Log the error. */
printk("SYSIO[%x]: SBUS Error, primary error type[%s] read(%d)\n",
- sbus->portid,
+ portid,
(((error_bits & SYSIO_SBAFSR_PLE) ?
"Late PIO Error" :
((error_bits & SYSIO_SBAFSR_PTO) ?
@@ -434,11 +451,11 @@
"Error Ack" : "???")))),
(afsr & SYSIO_SBAFSR_RD) ? 1 : 0);
printk("SYSIO[%x]: size[%lx] MID[%lx]\n",
- sbus->portid,
+ portid,
(afsr & SYSIO_SBAFSR_SIZE) >> 42UL,
(afsr & SYSIO_SBAFSR_MID) >> 37UL);
- printk("SYSIO[%x]: AFAR[%016lx]\n", sbus->portid, afar);
- printk("SYSIO[%x]: Secondary SBUS errors [", sbus->portid);
+ printk("SYSIO[%x]: AFAR[%016lx]\n", portid, afar);
+ printk("SYSIO[%x]: Secondary SBUS errors [", portid);
reported = 0;
if (afsr & SYSIO_SBAFSR_SLE) {
reported++;
@@ -470,34 +487,37 @@
#define SYSIO_CE_INO 0x35
#define SYSIO_SBUSERR_INO 0x36
-static void __init sysio_register_error_handlers(struct sbus_bus *sbus)
+static void __init sysio_register_error_handlers(struct of_device *op)
{
- struct iommu *iommu = sbus->ofdev.dev.archdata.iommu;
+ struct iommu *iommu = op->dev.archdata.iommu;
unsigned long reg_base = iommu->write_complete_reg - 0x2000UL;
unsigned int irq;
u64 control;
+ int portid;
- irq = sbus_build_irq(sbus, SYSIO_UE_INO);
+ portid = of_getintprop_default(op->node, "portid", -1);
+
+ irq = sbus_build_irq(op, SYSIO_UE_INO);
if (request_irq(irq, sysio_ue_handler, 0,
- "SYSIO_UE", sbus) < 0) {
+ "SYSIO_UE", op) < 0) {
prom_printf("SYSIO[%x]: Cannot register UE interrupt.\n",
- sbus->portid);
+ portid);
prom_halt();
}
- irq = sbus_build_irq(sbus, SYSIO_CE_INO);
+ irq = sbus_build_irq(op, SYSIO_CE_INO);
if (request_irq(irq, sysio_ce_handler, 0,
- "SYSIO_CE", sbus) < 0) {
+ "SYSIO_CE", op) < 0) {
prom_printf("SYSIO[%x]: Cannot register CE interrupt.\n",
- sbus->portid);
+ portid);
prom_halt();
}
- irq = sbus_build_irq(sbus, SYSIO_SBUSERR_INO);
+ irq = sbus_build_irq(op, SYSIO_SBUSERR_INO);
if (request_irq(irq, sysio_sbus_error_handler, 0,
- "SYSIO_SBERR", sbus) < 0) {
+ "SYSIO_SBERR", op) < 0) {
prom_printf("SYSIO[%x]: Cannot register SBUS Error interrupt.\n",
- sbus->portid);
+ portid);
prom_halt();
}
@@ -513,19 +533,15 @@
}
/* Boot time initialization. */
-static void __init sbus_iommu_init(int __node, struct sbus_bus *sbus)
+static void __init sbus_iommu_init(struct of_device *op)
{
const struct linux_prom64_registers *pr;
- struct device_node *dp;
+ struct device_node *dp = op->node;
struct iommu *iommu;
struct strbuf *strbuf;
unsigned long regs, reg_base;
+ int i, portid;
u64 control;
- int i;
-
- dp = of_find_node_by_phandle(__node);
-
- sbus->portid = of_getintprop_default(dp, "upa-portid", -1);
pr = of_get_property(dp, "reg", NULL);
if (!pr) {
@@ -542,9 +558,9 @@
if (!strbuf)
goto fatal_memory_error;
- sbus->ofdev.dev.archdata.iommu = iommu;
- sbus->ofdev.dev.archdata.stc = strbuf;
- sbus->ofdev.dev.archdata.numa_node = -1;
+ op->dev.archdata.iommu = iommu;
+ op->dev.archdata.stc = strbuf;
+ op->dev.archdata.numa_node = -1;
reg_base = regs + SYSIO_IOMMUREG_BASE;
iommu->iommu_control = reg_base + IOMMU_CONTROL;
@@ -572,8 +588,9 @@
*/
iommu->write_complete_reg = regs + 0x2000UL;
- printk("SYSIO: UPA portID %x, at %016lx\n",
- sbus->portid, regs);
+ portid = of_getintprop_default(op->node, "portid", -1);
+ printk(KERN_INFO "SYSIO: UPA portID %x, at %016lx\n",
+ portid, regs);
/* Setup for TSB_SIZE=7, TBW_SIZE=0, MMU_DE=1, MMU_EN=1 */
if (iommu_table_init(iommu, IO_TSB_SIZE, MAP_BASE, 0xffffffff, -1))
@@ -631,56 +648,27 @@
/* Now some Xfire specific grot... */
if (this_is_starfire)
- starfire_hookup(sbus->portid);
+ starfire_hookup(portid);
- sysio_register_error_handlers(sbus);
+ sysio_register_error_handlers(op);
return;
fatal_memory_error:
prom_printf("sbus_iommu_init: Fatal memory allocation error.\n");
}
-void sbus_fill_device_irq(struct sbus_dev *sdev)
+static int __init sbus_init(void)
{
- struct device_node *dp = of_find_node_by_phandle(sdev->prom_node);
- const struct linux_prom_irqs *irqs;
+ struct device_node *dp;
- irqs = of_get_property(dp, "interrupts", NULL);
- if (!irqs) {
- sdev->irqs[0] = 0;
- sdev->num_irqs = 0;
- } else {
- unsigned int pri = irqs[0].pri;
+ for_each_node_by_name(dp, "sbus") {
+ struct of_device *op = of_find_device_by_node(dp);
- sdev->num_irqs = 1;
- if (pri < 0x20)
- pri += sdev->slot * 8;
-
- sdev->irqs[0] = sbus_build_irq(sdev->bus, pri);
+ sbus_iommu_init(op);
+ of_propagate_archdata(op);
}
-}
-void __init sbus_arch_bus_ranges_init(struct device_node *pn, struct sbus_bus *sbus)
-{
-}
-
-void __init sbus_setup_iommu(struct sbus_bus *sbus, struct device_node *dp)
-{
- sbus_iommu_init(dp->node, sbus);
-}
-
-void __init sbus_setup_arch_props(struct sbus_bus *sbus, struct device_node *dp)
-{
-}
-
-int __init sbus_arch_preinit(void)
-{
return 0;
}
-void __init sbus_arch_postinit(void)
-{
- extern void firetruck_init(void);
-
- firetruck_init();
-}
+subsys_initcall(sbus_init);
diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c
index 2be166c..e562711 100644
--- a/arch/sparc64/kernel/smp.c
+++ b/arch/sparc64/kernel/smp.c
@@ -21,6 +21,7 @@
#include <linux/jiffies.h>
#include <linux/profile.h>
#include <linux/lmb.h>
+#include <linux/cpu.h>
#include <asm/head.h>
#include <asm/ptrace.h>
@@ -115,6 +116,9 @@
atomic_inc(&init_mm.mm_count);
current->active_mm = &init_mm;
+ /* inform the notifiers about the new cpu */
+ notify_cpu_starting(cpuid);
+
while (!cpu_isset(cpuid, smp_commenced_mask))
rmb();
diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c
index 0804f71..30bba8b 100644
--- a/arch/sparc64/kernel/sparc64_ksyms.c
+++ b/arch/sparc64/kernel/sparc64_ksyms.c
@@ -36,7 +36,6 @@
#include <asm/elf.h>
#include <asm/head.h>
#include <asm/smp.h>
-#include <asm/mostek.h>
#include <asm/ptrace.h>
#include <asm/uaccess.h>
#include <asm/checksum.h>
@@ -44,12 +43,8 @@
#include <asm/pgalloc.h>
#include <asm/cacheflush.h>
#ifdef CONFIG_SBUS
-#include <asm/sbus.h>
#include <asm/dma.h>
#endif
-#ifdef CONFIG_PCI
-#include <asm/ebus.h>
-#endif
#include <asm/ns87303.h>
#include <asm/timer.h>
#include <asm/cpudata.h>
@@ -68,7 +63,6 @@
extern void *__memscan_generic(void *, int, size_t);
extern int __memcmp(const void *, const void *, __kernel_size_t);
extern __kernel_size_t strlen(const char *);
-extern void syscall_trace(struct pt_regs *, int);
extern void sys_sigsuspend(void);
extern int compat_sys_ioctl(unsigned int fd, unsigned int cmd, u32 arg);
extern int (*handle_mathemu)(struct pt_regs *, struct fpustate *);
@@ -154,26 +148,12 @@
EXPORT_SYMBOL(__flush_dcache_range);
#endif
-EXPORT_SYMBOL(mostek_lock);
-EXPORT_SYMBOL(mstk48t02_regs);
#ifdef CONFIG_SUN_AUXIO
EXPORT_SYMBOL(auxio_set_led);
EXPORT_SYMBOL(auxio_set_lte);
#endif
#ifdef CONFIG_SBUS
-EXPORT_SYMBOL(sbus_root);
-EXPORT_SYMBOL(dma_chain);
EXPORT_SYMBOL(sbus_set_sbus64);
-EXPORT_SYMBOL(sbus_alloc_consistent);
-EXPORT_SYMBOL(sbus_free_consistent);
-EXPORT_SYMBOL(sbus_map_single);
-EXPORT_SYMBOL(sbus_unmap_single);
-EXPORT_SYMBOL(sbus_map_sg);
-EXPORT_SYMBOL(sbus_unmap_sg);
-EXPORT_SYMBOL(sbus_dma_sync_single_for_cpu);
-EXPORT_SYMBOL(sbus_dma_sync_single_for_device);
-EXPORT_SYMBOL(sbus_dma_sync_sg_for_cpu);
-EXPORT_SYMBOL(sbus_dma_sync_sg_for_device);
#endif
EXPORT_SYMBOL(outsb);
EXPORT_SYMBOL(outsw);
@@ -182,7 +162,6 @@
EXPORT_SYMBOL(insw);
EXPORT_SYMBOL(insl);
#ifdef CONFIG_PCI
-EXPORT_SYMBOL(ebus_chain);
EXPORT_SYMBOL(pci_alloc_consistent);
EXPORT_SYMBOL(pci_free_consistent);
EXPORT_SYMBOL(pci_map_single);
@@ -300,3 +279,5 @@
EXPORT_SYMBOL(xor_niagara_3);
EXPORT_SYMBOL(xor_niagara_4);
EXPORT_SYMBOL(xor_niagara_5);
+
+EXPORT_SYMBOL_GPL(real_hard_smp_processor_id);
diff --git a/arch/sparc64/kernel/sstate.c b/arch/sparc64/kernel/sstate.c
index 5b6e75b..8cdbe59 100644
--- a/arch/sparc64/kernel/sstate.c
+++ b/arch/sparc64/kernel/sstate.c
@@ -1,14 +1,15 @@
/* sstate.c: System soft state support.
*
- * Copyright (C) 2007 David S. Miller <davem@davemloft.net>
+ * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net>
*/
#include <linux/kernel.h>
#include <linux/notifier.h>
+#include <linux/reboot.h>
#include <linux/init.h>
#include <asm/hypervisor.h>
-#include <asm/sstate.h>
+#include <asm/spitfire.h>
#include <asm/oplib.h>
#include <asm/head.h>
#include <asm/io.h>
@@ -50,30 +51,33 @@
static const char panicing_msg[32] __attribute__((aligned(32))) =
"Linux panicing";
-void sstate_booting(void)
+static int sstate_reboot_call(struct notifier_block *np, unsigned long type, void *_unused)
{
- do_set_sstate(HV_SOFT_STATE_TRANSITION, booting_msg);
+ const char *msg;
+
+ switch (type) {
+ case SYS_DOWN:
+ default:
+ msg = rebooting_msg;
+ break;
+
+ case SYS_HALT:
+ msg = halting_msg;
+ break;
+
+ case SYS_POWER_OFF:
+ msg = poweroff_msg;
+ break;
+ }
+
+ do_set_sstate(HV_SOFT_STATE_TRANSITION, msg);
+
+ return NOTIFY_OK;
}
-void sstate_running(void)
-{
- do_set_sstate(HV_SOFT_STATE_NORMAL, running_msg);
-}
-
-void sstate_halt(void)
-{
- do_set_sstate(HV_SOFT_STATE_TRANSITION, halting_msg);
-}
-
-void sstate_poweroff(void)
-{
- do_set_sstate(HV_SOFT_STATE_TRANSITION, poweroff_msg);
-}
-
-void sstate_reboot(void)
-{
- do_set_sstate(HV_SOFT_STATE_TRANSITION, rebooting_msg);
-}
+static struct notifier_block sstate_reboot_notifier = {
+ .notifier_call = sstate_reboot_call,
+};
static int sstate_panic_event(struct notifier_block *n, unsigned long event, void *ptr)
{
@@ -87,18 +91,37 @@
.priority = INT_MAX,
};
-void __init sun4v_sstate_init(void)
+static int __init sstate_init(void)
{
unsigned long major, minor;
+ if (tlb_type != hypervisor)
+ return 0;
+
major = 1;
minor = 0;
if (sun4v_hvapi_register(HV_GRP_SOFT_STATE, major, &minor))
- return;
+ return 0;
hv_supports_soft_state = 1;
prom_sun4v_guest_soft_state();
+
+ do_set_sstate(HV_SOFT_STATE_TRANSITION, booting_msg);
+
atomic_notifier_chain_register(&panic_notifier_list,
&sstate_panic_block);
+ register_reboot_notifier(&sstate_reboot_notifier);
+
+ return 0;
}
+
+core_initcall(sstate_init);
+
+static int __init sstate_running(void)
+{
+ do_set_sstate(HV_SOFT_STATE_NORMAL, running_msg);
+ return 0;
+}
+
+late_initcall(sstate_running);
diff --git a/arch/sparc64/kernel/starfire.c b/arch/sparc64/kernel/starfire.c
index 7461581..060d0f3 100644
--- a/arch/sparc64/kernel/starfire.c
+++ b/arch/sparc64/kernel/starfire.c
@@ -28,11 +28,6 @@
this_is_starfire = 1;
}
-void starfire_cpu_setup(void)
-{
- /* Currently, nothing to do. */
-}
-
int starfire_hard_smp_processor_id(void)
{
return upa_readl(0x1fff40000d0UL);
diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c
index 3d11853..3320c9d 100644
--- a/arch/sparc64/kernel/sys_sparc32.c
+++ b/arch/sparc64/kernel/sys_sparc32.c
@@ -575,14 +575,6 @@
return do_sys_settimeofday(tv ? &kts : NULL, tz ? &ktz : NULL);
}
-/* These are here just in case some old sparc32 binary calls it. */
-asmlinkage long sys32_pause(void)
-{
- current->state = TASK_INTERRUPTIBLE;
- schedule();
- return -ERESTARTNOHAND;
-}
-
asmlinkage compat_ssize_t sys32_pread64(unsigned int fd,
char __user *ubuf,
compat_size_t count,
diff --git a/arch/sparc64/kernel/syscalls.S b/arch/sparc64/kernel/syscalls.S
index a2f2427..7a6786a 100644
--- a/arch/sparc64/kernel/syscalls.S
+++ b/arch/sparc64/kernel/syscalls.S
@@ -65,9 +65,8 @@
andcc %l5, (_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT), %g0
be,pt %icc, rtrap
nop
- add %sp, PTREGS_OFF, %o0
- call syscall_trace
- mov 1, %o1
+ call syscall_trace_leave
+ add %sp, PTREGS_OFF, %o0
ba,pt %xcc, rtrap
nop
@@ -159,9 +158,8 @@
or %l7, %lo(sys_ni_syscall), %l7
linux_syscall_trace32:
- add %sp, PTREGS_OFF, %o0
- call syscall_trace
- clr %o1
+ call syscall_trace_enter
+ add %sp, PTREGS_OFF, %o0
brnz,pn %o0, 3f
mov -ENOSYS, %o0
srl %i0, 0, %o0
@@ -172,9 +170,8 @@
srl %i3, 0, %o3
linux_syscall_trace:
- add %sp, PTREGS_OFF, %o0
- call syscall_trace
- clr %o1
+ call syscall_trace_enter
+ add %sp, PTREGS_OFF, %o0
brnz,pn %o0, 3f
mov -ENOSYS, %o0
mov %i0, %o0
@@ -275,9 +272,8 @@
b,pt %xcc, rtrap
stx %l2, [%sp + PTREGS_OFF + PT_V9_TNPC]
linux_syscall_trace2:
- add %sp, PTREGS_OFF, %o0
- call syscall_trace
- mov 1, %o1
+ call syscall_trace_leave
+ add %sp, PTREGS_OFF, %o0
stx %l1, [%sp + PTREGS_OFF + PT_V9_TPC]
ba,pt %xcc, rtrap
stx %l2, [%sp + PTREGS_OFF + PT_V9_TNPC]
diff --git a/arch/sparc64/kernel/systbls.S b/arch/sparc64/kernel/systbls.S
index 0fdbf3b..5daee4b 100644
--- a/arch/sparc64/kernel/systbls.S
+++ b/arch/sparc64/kernel/systbls.S
@@ -23,7 +23,7 @@
/*10*/ .word sys_unlink, sunos_execv, sys_chdir, sys_chown16, sys32_mknod
/*15*/ .word sys_chmod, sys_lchown16, sparc_brk, sys32_perfctr, sys32_lseek
/*20*/ .word sys_getpid, sys_capget, sys_capset, sys_setuid16, sys_getuid16
-/*25*/ .word sys32_vmsplice, compat_sys_ptrace, sys_alarm, sys32_sigaltstack, sys32_pause
+/*25*/ .word sys32_vmsplice, compat_sys_ptrace, sys_alarm, sys32_sigaltstack, sys_pause
/*30*/ .word compat_sys_utime, sys_lchown, sys_fchown, sys32_access, sys32_nice
.word sys_chown, sys_sync, sys32_kill, compat_sys_newstat, sys32_sendfile
/*40*/ .word compat_sys_newlstat, sys_dup, sys_pipe, compat_sys_times, sys_getuid
diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c
index cc16fdc..80d71a5 100644
--- a/arch/sparc64/kernel/time.c
+++ b/arch/sparc64/kernel/time.c
@@ -30,13 +30,14 @@
#include <linux/percpu.h>
#include <linux/miscdevice.h>
#include <linux/rtc.h>
+#include <linux/rtc/m48t59.h>
#include <linux/kernel_stat.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/of_device.h>
+#include <linux/platform_device.h>
#include <asm/oplib.h>
-#include <asm/mostek.h>
#include <asm/timer.h>
#include <asm/irq.h>
#include <asm/io.h>
@@ -50,18 +51,7 @@
#include "entry.h"
-DEFINE_SPINLOCK(mostek_lock);
DEFINE_SPINLOCK(rtc_lock);
-void __iomem *mstk48t02_regs = NULL;
-#ifdef CONFIG_PCI
-unsigned long ds1287_regs = 0UL;
-static void __iomem *bq4802_regs;
-#endif
-
-static void __iomem *mstk48t08_regs;
-static void __iomem *mstk48t59_regs;
-
-static int set_rtc_mmss(unsigned long);
#define TICK_PRIV_BIT (1UL << 63)
#define TICKCMP_IRQ_BIT (1UL << 63)
@@ -405,313 +395,167 @@
int update_persistent_clock(struct timespec now)
{
- return set_rtc_mmss(now.tv_sec);
-}
+ struct rtc_device *rtc = rtc_class_open("rtc0");
+ int err = -1;
-/* Kick start a stopped clock (procedure from the Sun NVRAM/hostid FAQ). */
-static void __init kick_start_clock(void)
-{
- void __iomem *regs = mstk48t02_regs;
- u8 sec, tmp;
- int i, count;
-
- prom_printf("CLOCK: Clock was stopped. Kick start ");
-
- spin_lock_irq(&mostek_lock);
-
- /* Turn on the kick start bit to start the oscillator. */
- tmp = mostek_read(regs + MOSTEK_CREG);
- tmp |= MSTK_CREG_WRITE;
- mostek_write(regs + MOSTEK_CREG, tmp);
- tmp = mostek_read(regs + MOSTEK_SEC);
- tmp &= ~MSTK_STOP;
- mostek_write(regs + MOSTEK_SEC, tmp);
- tmp = mostek_read(regs + MOSTEK_HOUR);
- tmp |= MSTK_KICK_START;
- mostek_write(regs + MOSTEK_HOUR, tmp);
- tmp = mostek_read(regs + MOSTEK_CREG);
- tmp &= ~MSTK_CREG_WRITE;
- mostek_write(regs + MOSTEK_CREG, tmp);
-
- spin_unlock_irq(&mostek_lock);
-
- /* Delay to allow the clock oscillator to start. */
- sec = MSTK_REG_SEC(regs);
- for (i = 0; i < 3; i++) {
- while (sec == MSTK_REG_SEC(regs))
- for (count = 0; count < 100000; count++)
- /* nothing */ ;
- prom_printf(".");
- sec = MSTK_REG_SEC(regs);
- }
- prom_printf("\n");
-
- spin_lock_irq(&mostek_lock);
-
- /* Turn off kick start and set a "valid" time and date. */
- tmp = mostek_read(regs + MOSTEK_CREG);
- tmp |= MSTK_CREG_WRITE;
- mostek_write(regs + MOSTEK_CREG, tmp);
- tmp = mostek_read(regs + MOSTEK_HOUR);
- tmp &= ~MSTK_KICK_START;
- mostek_write(regs + MOSTEK_HOUR, tmp);
- MSTK_SET_REG_SEC(regs,0);
- MSTK_SET_REG_MIN(regs,0);
- MSTK_SET_REG_HOUR(regs,0);
- MSTK_SET_REG_DOW(regs,5);
- MSTK_SET_REG_DOM(regs,1);
- MSTK_SET_REG_MONTH(regs,8);
- MSTK_SET_REG_YEAR(regs,1996 - MSTK_YEAR_ZERO);
- tmp = mostek_read(regs + MOSTEK_CREG);
- tmp &= ~MSTK_CREG_WRITE;
- mostek_write(regs + MOSTEK_CREG, tmp);
-
- spin_unlock_irq(&mostek_lock);
-
- /* Ensure the kick start bit is off. If it isn't, turn it off. */
- while (mostek_read(regs + MOSTEK_HOUR) & MSTK_KICK_START) {
- prom_printf("CLOCK: Kick start still on!\n");
-
- spin_lock_irq(&mostek_lock);
-
- tmp = mostek_read(regs + MOSTEK_CREG);
- tmp |= MSTK_CREG_WRITE;
- mostek_write(regs + MOSTEK_CREG, tmp);
-
- tmp = mostek_read(regs + MOSTEK_HOUR);
- tmp &= ~MSTK_KICK_START;
- mostek_write(regs + MOSTEK_HOUR, tmp);
-
- tmp = mostek_read(regs + MOSTEK_CREG);
- tmp &= ~MSTK_CREG_WRITE;
- mostek_write(regs + MOSTEK_CREG, tmp);
-
- spin_unlock_irq(&mostek_lock);
+ if (rtc) {
+ err = rtc_set_mmss(rtc, now.tv_sec);
+ rtc_class_close(rtc);
}
- prom_printf("CLOCK: Kick start procedure successful.\n");
+ return err;
}
-/* Return nonzero if the clock chip battery is low. */
-static int __init has_low_battery(void)
+unsigned long cmos_regs;
+EXPORT_SYMBOL(cmos_regs);
+
+static struct resource rtc_cmos_resource;
+
+static struct platform_device rtc_cmos_device = {
+ .name = "rtc_cmos",
+ .id = -1,
+ .resource = &rtc_cmos_resource,
+ .num_resources = 1,
+};
+
+static int __devinit rtc_probe(struct of_device *op, const struct of_device_id *match)
{
- void __iomem *regs = mstk48t02_regs;
- u8 data1, data2;
+ struct resource *r;
- spin_lock_irq(&mostek_lock);
+ printk(KERN_INFO "%s: RTC regs at 0x%lx\n",
+ op->node->full_name, op->resource[0].start);
- data1 = mostek_read(regs + MOSTEK_EEPROM); /* Read some data. */
- mostek_write(regs + MOSTEK_EEPROM, ~data1); /* Write back the complement. */
- data2 = mostek_read(regs + MOSTEK_EEPROM); /* Read back the complement. */
- mostek_write(regs + MOSTEK_EEPROM, data1); /* Restore original value. */
-
- spin_unlock_irq(&mostek_lock);
-
- return (data1 == data2); /* Was the write blocked? */
-}
-
-static void __init mostek_set_system_time(void __iomem *mregs)
-{
- unsigned int year, mon, day, hour, min, sec;
- u8 tmp;
-
- spin_lock_irq(&mostek_lock);
-
- /* Traditional Mostek chip. */
- tmp = mostek_read(mregs + MOSTEK_CREG);
- tmp |= MSTK_CREG_READ;
- mostek_write(mregs + MOSTEK_CREG, tmp);
-
- sec = MSTK_REG_SEC(mregs);
- min = MSTK_REG_MIN(mregs);
- hour = MSTK_REG_HOUR(mregs);
- day = MSTK_REG_DOM(mregs);
- mon = MSTK_REG_MONTH(mregs);
- year = MSTK_CVT_YEAR( MSTK_REG_YEAR(mregs) );
-
- xtime.tv_sec = mktime(year, mon, day, hour, min, sec);
- xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ);
- set_normalized_timespec(&wall_to_monotonic,
- -xtime.tv_sec, -xtime.tv_nsec);
-
- tmp = mostek_read(mregs + MOSTEK_CREG);
- tmp &= ~MSTK_CREG_READ;
- mostek_write(mregs + MOSTEK_CREG, tmp);
-
- spin_unlock_irq(&mostek_lock);
-}
-
-/* Probe for the real time clock chip. */
-static void __init set_system_time(void)
-{
- unsigned int year, mon, day, hour, min, sec;
- void __iomem *mregs = mstk48t02_regs;
-#ifdef CONFIG_PCI
- unsigned long dregs = ds1287_regs;
- void __iomem *bregs = bq4802_regs;
-#else
- unsigned long dregs = 0UL;
- void __iomem *bregs = 0UL;
-#endif
-
- if (!mregs && !dregs && !bregs) {
- prom_printf("Something wrong, clock regs not mapped yet.\n");
- prom_halt();
- }
-
- if (mregs) {
- mostek_set_system_time(mregs);
- return;
- }
-
- if (bregs) {
- unsigned char val = readb(bregs + 0x0e);
- unsigned int century;
-
- /* BQ4802 RTC chip. */
-
- writeb(val | 0x08, bregs + 0x0e);
-
- sec = readb(bregs + 0x00);
- min = readb(bregs + 0x02);
- hour = readb(bregs + 0x04);
- day = readb(bregs + 0x06);
- mon = readb(bregs + 0x09);
- year = readb(bregs + 0x0a);
- century = readb(bregs + 0x0f);
-
- writeb(val, bregs + 0x0e);
-
- BCD_TO_BIN(sec);
- BCD_TO_BIN(min);
- BCD_TO_BIN(hour);
- BCD_TO_BIN(day);
- BCD_TO_BIN(mon);
- BCD_TO_BIN(year);
- BCD_TO_BIN(century);
-
- year += (century * 100);
- } else {
- /* Dallas 12887 RTC chip. */
-
- do {
- sec = CMOS_READ(RTC_SECONDS);
- min = CMOS_READ(RTC_MINUTES);
- hour = CMOS_READ(RTC_HOURS);
- day = CMOS_READ(RTC_DAY_OF_MONTH);
- mon = CMOS_READ(RTC_MONTH);
- year = CMOS_READ(RTC_YEAR);
- } while (sec != CMOS_READ(RTC_SECONDS));
-
- if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
- BCD_TO_BIN(sec);
- BCD_TO_BIN(min);
- BCD_TO_BIN(hour);
- BCD_TO_BIN(day);
- BCD_TO_BIN(mon);
- BCD_TO_BIN(year);
- }
- if ((year += 1900) < 1970)
- year += 100;
- }
-
- xtime.tv_sec = mktime(year, mon, day, hour, min, sec);
- xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ);
- set_normalized_timespec(&wall_to_monotonic,
- -xtime.tv_sec, -xtime.tv_nsec);
-}
-
-/* davem suggests we keep this within the 4M locked kernel image */
-static u32 starfire_get_time(void)
-{
- static char obp_gettod[32];
- static u32 unix_tod;
-
- sprintf(obp_gettod, "h# %08x unix-gettod",
- (unsigned int) (long) &unix_tod);
- prom_feval(obp_gettod);
-
- return unix_tod;
-}
-
-static int starfire_set_time(u32 val)
-{
- /* Do nothing, time is set using the service processor
- * console on this platform.
+ /* The CMOS RTC driver only accepts IORESOURCE_IO, so cons
+ * up a fake resource so that the probe works for all cases.
+ * When the RTC is behind an ISA bus it will have IORESOURCE_IO
+ * already, whereas when it's behind EBUS is will be IORESOURCE_MEM.
*/
- return 0;
+
+ r = &rtc_cmos_resource;
+ r->flags = IORESOURCE_IO;
+ r->name = op->resource[0].name;
+ r->start = op->resource[0].start;
+ r->end = op->resource[0].end;
+
+ cmos_regs = op->resource[0].start;
+ return platform_device_register(&rtc_cmos_device);
}
-static u32 hypervisor_get_time(void)
-{
- unsigned long ret, time;
- int retries = 10000;
+static struct of_device_id __initdata rtc_match[] = {
+ {
+ .name = "rtc",
+ .compatible = "m5819",
+ },
+ {
+ .name = "rtc",
+ .compatible = "isa-m5819p",
+ },
+ {
+ .name = "rtc",
+ .compatible = "isa-m5823p",
+ },
+ {
+ .name = "rtc",
+ .compatible = "ds1287",
+ },
+ {},
+};
-retry:
- ret = sun4v_tod_get(&time);
- if (ret == HV_EOK)
- return time;
- if (ret == HV_EWOULDBLOCK) {
- if (--retries > 0) {
- udelay(100);
- goto retry;
- }
- printk(KERN_WARNING "SUN4V: tod_get() timed out.\n");
- return 0;
+static struct of_platform_driver rtc_driver = {
+ .match_table = rtc_match,
+ .probe = rtc_probe,
+ .driver = {
+ .name = "rtc",
+ },
+};
+
+static struct platform_device rtc_bq4802_device = {
+ .name = "rtc-bq4802",
+ .id = -1,
+ .num_resources = 1,
+};
+
+static int __devinit bq4802_probe(struct of_device *op, const struct of_device_id *match)
+{
+
+ printk(KERN_INFO "%s: BQ4802 regs at 0x%lx\n",
+ op->node->full_name, op->resource[0].start);
+
+ rtc_bq4802_device.resource = &op->resource[0];
+ return platform_device_register(&rtc_bq4802_device);
+}
+
+static struct of_device_id __initdata bq4802_match[] = {
+ {
+ .name = "rtc",
+ .compatible = "bq4802",
+ },
+};
+
+static struct of_platform_driver bq4802_driver = {
+ .match_table = bq4802_match,
+ .probe = bq4802_probe,
+ .driver = {
+ .name = "bq4802",
+ },
+};
+
+static unsigned char mostek_read_byte(struct device *dev, u32 ofs)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct m48t59_plat_data *pdata = pdev->dev.platform_data;
+ void __iomem *regs;
+ unsigned char val;
+
+ regs = (void __iomem *) pdev->resource[0].start;
+ val = readb(regs + ofs);
+
+ /* the year 0 is 1968 */
+ if (ofs == pdata->offset + M48T59_YEAR) {
+ val += 0x68;
+ if ((val & 0xf) > 9)
+ val += 6;
}
- printk(KERN_WARNING "SUN4V: tod_get() not supported.\n");
- return 0;
+ return val;
}
-static int hypervisor_set_time(u32 secs)
+static void mostek_write_byte(struct device *dev, u32 ofs, u8 val)
{
- unsigned long ret;
- int retries = 10000;
-
-retry:
- ret = sun4v_tod_set(secs);
- if (ret == HV_EOK)
- return 0;
- if (ret == HV_EWOULDBLOCK) {
- if (--retries > 0) {
- udelay(100);
- goto retry;
- }
- printk(KERN_WARNING "SUN4V: tod_set() timed out.\n");
- return -EAGAIN;
- }
- printk(KERN_WARNING "SUN4V: tod_set() not supported.\n");
- return -EOPNOTSUPP;
-}
-
-static int __init clock_model_matches(const char *model)
-{
- if (strcmp(model, "mk48t02") &&
- strcmp(model, "mk48t08") &&
- strcmp(model, "mk48t59") &&
- strcmp(model, "m5819") &&
- strcmp(model, "m5819p") &&
- strcmp(model, "m5823") &&
- strcmp(model, "ds1287") &&
- strcmp(model, "bq4802"))
- return 0;
-
- return 1;
-}
-
-static int __devinit clock_probe(struct of_device *op, const struct of_device_id *match)
-{
- struct device_node *dp = op->node;
- const char *model = of_get_property(dp, "model", NULL);
- const char *compat = of_get_property(dp, "compatible", NULL);
- unsigned long size, flags;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct m48t59_plat_data *pdata = pdev->dev.platform_data;
void __iomem *regs;
- if (!model)
- model = compat;
+ regs = (void __iomem *) pdev->resource[0].start;
+ if (ofs == pdata->offset + M48T59_YEAR) {
+ if (val < 0x68)
+ val += 0x32;
+ else
+ val -= 0x68;
+ if ((val & 0xf) > 9)
+ val += 6;
+ if ((val & 0xf0) > 0x9A)
+ val += 0x60;
+ }
+ writeb(val, regs + ofs);
+}
- if (!model || !clock_model_matches(model))
- return -ENODEV;
+static struct m48t59_plat_data m48t59_data = {
+ .read_byte = mostek_read_byte,
+ .write_byte = mostek_write_byte,
+};
+
+static struct platform_device m48t59_rtc = {
+ .name = "rtc-m48t59",
+ .id = 0,
+ .num_resources = 1,
+ .dev = {
+ .platform_data = &m48t59_data,
+ },
+};
+
+static int __devinit mostek_probe(struct of_device *op, const struct of_device_id *match)
+{
+ struct device_node *dp = op->node;
/* On an Enterprise system there can be multiple mostek clocks.
* We should only match the one that is on the central FHC bus.
@@ -720,88 +564,51 @@
strcmp(dp->parent->parent->name, "central") != 0)
return -ENODEV;
- size = (op->resource[0].end - op->resource[0].start) + 1;
- regs = of_ioremap(&op->resource[0], 0, size, "clock");
- if (!regs)
- return -ENOMEM;
+ printk(KERN_INFO "%s: Mostek regs at 0x%lx\n",
+ dp->full_name, op->resource[0].start);
-#ifdef CONFIG_PCI
- if (!strcmp(model, "ds1287") ||
- !strcmp(model, "m5819") ||
- !strcmp(model, "m5819p") ||
- !strcmp(model, "m5823")) {
- ds1287_regs = (unsigned long) regs;
- } else if (!strcmp(model, "bq4802")) {
- bq4802_regs = regs;
- } else
-#endif
- if (model[5] == '0' && model[6] == '2') {
- mstk48t02_regs = regs;
- } else if(model[5] == '0' && model[6] == '8') {
- mstk48t08_regs = regs;
- mstk48t02_regs = mstk48t08_regs + MOSTEK_48T08_48T02;
- } else {
- mstk48t59_regs = regs;
- mstk48t02_regs = mstk48t59_regs + MOSTEK_48T59_48T02;
- }
-
- printk(KERN_INFO "%s: Clock regs at %p\n", dp->full_name, regs);
-
- local_irq_save(flags);
-
- if (mstk48t02_regs != NULL) {
- /* Report a low battery voltage condition. */
- if (has_low_battery())
- prom_printf("NVRAM: Low battery voltage!\n");
-
- /* Kick start the clock if it is completely stopped. */
- if (mostek_read(mstk48t02_regs + MOSTEK_SEC) & MSTK_STOP)
- kick_start_clock();
- }
-
- set_system_time();
-
- local_irq_restore(flags);
-
- return 0;
+ m48t59_rtc.resource = &op->resource[0];
+ return platform_device_register(&m48t59_rtc);
}
-static struct of_device_id clock_match[] = {
+static struct of_device_id __initdata mostek_match[] = {
{
.name = "eeprom",
},
- {
- .name = "rtc",
- },
{},
};
-static struct of_platform_driver clock_driver = {
- .match_table = clock_match,
- .probe = clock_probe,
+static struct of_platform_driver mostek_driver = {
+ .match_table = mostek_match,
+ .probe = mostek_probe,
.driver = {
- .name = "clock",
+ .name = "mostek",
},
};
+static struct platform_device rtc_sun4v_device = {
+ .name = "rtc-sun4v",
+ .id = -1,
+};
+
+static struct platform_device rtc_starfire_device = {
+ .name = "rtc-starfire",
+ .id = -1,
+};
+
static int __init clock_init(void)
{
- if (this_is_starfire) {
- xtime.tv_sec = starfire_get_time();
- xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ);
- set_normalized_timespec(&wall_to_monotonic,
- -xtime.tv_sec, -xtime.tv_nsec);
- return 0;
- }
- if (tlb_type == hypervisor) {
- xtime.tv_sec = hypervisor_get_time();
- xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ);
- set_normalized_timespec(&wall_to_monotonic,
- -xtime.tv_sec, -xtime.tv_nsec);
- return 0;
- }
+ if (this_is_starfire)
+ return platform_device_register(&rtc_starfire_device);
- return of_register_driver(&clock_driver, &of_platform_bus_type);
+ if (tlb_type == hypervisor)
+ return platform_device_register(&rtc_sun4v_device);
+
+ (void) of_register_driver(&rtc_driver, &of_platform_bus_type);
+ (void) of_register_driver(&mostek_driver, &of_platform_bus_type);
+ (void) of_register_driver(&bq4802_driver, &of_platform_bus_type);
+
+ return 0;
}
/* Must be after subsys_initcall() so that busses are probed. Must
@@ -814,7 +621,7 @@
static unsigned long sparc64_init_timers(void)
{
struct device_node *dp;
- unsigned long clock;
+ unsigned long freq;
dp = of_find_node_by_path("/");
if (tlb_type == spitfire) {
@@ -827,17 +634,17 @@
if (manuf == 0x17 && impl == 0x13) {
/* Hummingbird, aka Ultra-IIe */
tick_ops = &hbtick_operations;
- clock = of_getintprop_default(dp, "stick-frequency", 0);
+ freq = of_getintprop_default(dp, "stick-frequency", 0);
} else {
tick_ops = &tick_operations;
- clock = local_cpu_data().clock_tick;
+ freq = local_cpu_data().clock_tick;
}
} else {
tick_ops = &stick_operations;
- clock = of_getintprop_default(dp, "stick-frequency", 0);
+ freq = of_getintprop_default(dp, "stick-frequency", 0);
}
- return clock;
+ return freq;
}
struct freq_table {
@@ -1029,16 +836,16 @@
void __init time_init(void)
{
- unsigned long clock = sparc64_init_timers();
+ unsigned long freq = sparc64_init_timers();
- tb_ticks_per_usec = clock / USEC_PER_SEC;
+ tb_ticks_per_usec = freq / USEC_PER_SEC;
timer_ticks_per_nsec_quotient =
- clocksource_hz2mult(clock, SPARC64_NSEC_PER_CYC_SHIFT);
+ clocksource_hz2mult(freq, SPARC64_NSEC_PER_CYC_SHIFT);
clocksource_tick.name = tick_ops->name;
clocksource_tick.mult =
- clocksource_hz2mult(clock,
+ clocksource_hz2mult(freq,
clocksource_tick.shift);
clocksource_tick.read = tick_ops->get_tick;
@@ -1049,7 +856,7 @@
sparc64_clockevent.name = tick_ops->name;
- setup_clockevent_multiplier(clock);
+ setup_clockevent_multiplier(freq);
sparc64_clockevent.max_delta_ns =
clockevent_delta2ns(0x7fffffffffffffffUL, &sparc64_clockevent);
@@ -1070,672 +877,8 @@
>> SPARC64_NSEC_PER_CYC_SHIFT;
}
-static int set_rtc_mmss(unsigned long nowtime)
-{
- int real_seconds, real_minutes, chip_minutes;
- void __iomem *mregs = mstk48t02_regs;
-#ifdef CONFIG_PCI
- unsigned long dregs = ds1287_regs;
- void __iomem *bregs = bq4802_regs;
-#else
- unsigned long dregs = 0UL;
- void __iomem *bregs = 0UL;
-#endif
- unsigned long flags;
- u8 tmp;
-
- /*
- * Not having a register set can lead to trouble.
- * Also starfire doesn't have a tod clock.
- */
- if (!mregs && !dregs && !bregs)
- return -1;
-
- if (mregs) {
- spin_lock_irqsave(&mostek_lock, flags);
-
- /* Read the current RTC minutes. */
- tmp = mostek_read(mregs + MOSTEK_CREG);
- tmp |= MSTK_CREG_READ;
- mostek_write(mregs + MOSTEK_CREG, tmp);
-
- chip_minutes = MSTK_REG_MIN(mregs);
-
- tmp = mostek_read(mregs + MOSTEK_CREG);
- tmp &= ~MSTK_CREG_READ;
- mostek_write(mregs + MOSTEK_CREG, tmp);
-
- /*
- * since we're only adjusting minutes and seconds,
- * don't interfere with hour overflow. This avoids
- * messing with unknown time zones but requires your
- * RTC not to be off by more than 15 minutes
- */
- real_seconds = nowtime % 60;
- real_minutes = nowtime / 60;
- if (((abs(real_minutes - chip_minutes) + 15)/30) & 1)
- real_minutes += 30; /* correct for half hour time zone */
- real_minutes %= 60;
-
- if (abs(real_minutes - chip_minutes) < 30) {
- tmp = mostek_read(mregs + MOSTEK_CREG);
- tmp |= MSTK_CREG_WRITE;
- mostek_write(mregs + MOSTEK_CREG, tmp);
-
- MSTK_SET_REG_SEC(mregs,real_seconds);
- MSTK_SET_REG_MIN(mregs,real_minutes);
-
- tmp = mostek_read(mregs + MOSTEK_CREG);
- tmp &= ~MSTK_CREG_WRITE;
- mostek_write(mregs + MOSTEK_CREG, tmp);
-
- spin_unlock_irqrestore(&mostek_lock, flags);
-
- return 0;
- } else {
- spin_unlock_irqrestore(&mostek_lock, flags);
-
- return -1;
- }
- } else if (bregs) {
- int retval = 0;
- unsigned char val = readb(bregs + 0x0e);
-
- /* BQ4802 RTC chip. */
-
- writeb(val | 0x08, bregs + 0x0e);
-
- chip_minutes = readb(bregs + 0x02);
- BCD_TO_BIN(chip_minutes);
- real_seconds = nowtime % 60;
- real_minutes = nowtime / 60;
- if (((abs(real_minutes - chip_minutes) + 15)/30) & 1)
- real_minutes += 30;
- real_minutes %= 60;
-
- if (abs(real_minutes - chip_minutes) < 30) {
- BIN_TO_BCD(real_seconds);
- BIN_TO_BCD(real_minutes);
- writeb(real_seconds, bregs + 0x00);
- writeb(real_minutes, bregs + 0x02);
- } else {
- printk(KERN_WARNING
- "set_rtc_mmss: can't update from %d to %d\n",
- chip_minutes, real_minutes);
- retval = -1;
- }
-
- writeb(val, bregs + 0x0e);
-
- return retval;
- } else {
- int retval = 0;
- unsigned char save_control, save_freq_select;
-
- /* Stolen from arch/i386/kernel/time.c, see there for
- * credits and descriptive comments.
- */
- spin_lock_irqsave(&rtc_lock, flags);
- save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */
- CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
-
- save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */
- CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
-
- chip_minutes = CMOS_READ(RTC_MINUTES);
- if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
- BCD_TO_BIN(chip_minutes);
- real_seconds = nowtime % 60;
- real_minutes = nowtime / 60;
- if (((abs(real_minutes - chip_minutes) + 15)/30) & 1)
- real_minutes += 30;
- real_minutes %= 60;
-
- if (abs(real_minutes - chip_minutes) < 30) {
- if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
- BIN_TO_BCD(real_seconds);
- BIN_TO_BCD(real_minutes);
- }
- CMOS_WRITE(real_seconds,RTC_SECONDS);
- CMOS_WRITE(real_minutes,RTC_MINUTES);
- } else {
- printk(KERN_WARNING
- "set_rtc_mmss: can't update from %d to %d\n",
- chip_minutes, real_minutes);
- retval = -1;
- }
-
- CMOS_WRITE(save_control, RTC_CONTROL);
- CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
- spin_unlock_irqrestore(&rtc_lock, flags);
-
- return retval;
- }
-}
-
-#define RTC_IS_OPEN 0x01 /* means /dev/rtc is in use */
-static unsigned char mini_rtc_status; /* bitmapped status byte. */
-
-#define FEBRUARY 2
-#define STARTOFTIME 1970
-#define SECDAY 86400L
-#define SECYR (SECDAY * 365)
-#define leapyear(year) ((year) % 4 == 0 && \
- ((year) % 100 != 0 || (year) % 400 == 0))
-#define days_in_year(a) (leapyear(a) ? 366 : 365)
-#define days_in_month(a) (month_days[(a) - 1])
-
-static int month_days[12] = {
- 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
-};
-
-/*
- * This only works for the Gregorian calendar - i.e. after 1752 (in the UK)
- */
-static void GregorianDay(struct rtc_time * tm)
-{
- int leapsToDate;
- int lastYear;
- int day;
- int MonthOffset[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
-
- lastYear = tm->tm_year - 1;
-
- /*
- * Number of leap corrections to apply up to end of last year
- */
- leapsToDate = lastYear / 4 - lastYear / 100 + lastYear / 400;
-
- /*
- * This year is a leap year if it is divisible by 4 except when it is
- * divisible by 100 unless it is divisible by 400
- *
- * e.g. 1904 was a leap year, 1900 was not, 1996 is, and 2000 was
- */
- day = tm->tm_mon > 2 && leapyear(tm->tm_year);
-
- day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] +
- tm->tm_mday;
-
- tm->tm_wday = day % 7;
-}
-
-static void to_tm(int tim, struct rtc_time *tm)
-{
- register int i;
- register long hms, day;
-
- day = tim / SECDAY;
- hms = tim % SECDAY;
-
- /* Hours, minutes, seconds are easy */
- tm->tm_hour = hms / 3600;
- tm->tm_min = (hms % 3600) / 60;
- tm->tm_sec = (hms % 3600) % 60;
-
- /* Number of years in days */
- for (i = STARTOFTIME; day >= days_in_year(i); i++)
- day -= days_in_year(i);
- tm->tm_year = i;
-
- /* Number of months in days left */
- if (leapyear(tm->tm_year))
- days_in_month(FEBRUARY) = 29;
- for (i = 1; day >= days_in_month(i); i++)
- day -= days_in_month(i);
- days_in_month(FEBRUARY) = 28;
- tm->tm_mon = i;
-
- /* Days are what is left over (+1) from all that. */
- tm->tm_mday = day + 1;
-
- /*
- * Determine the day of week
- */
- GregorianDay(tm);
-}
-
-/* Both Starfire and SUN4V give us seconds since Jan 1st, 1970,
- * aka Unix time. So we have to convert to/from rtc_time.
- */
-static void starfire_get_rtc_time(struct rtc_time *time)
-{
- u32 seconds = starfire_get_time();
-
- to_tm(seconds, time);
- time->tm_year -= 1900;
- time->tm_mon -= 1;
-}
-
-static int starfire_set_rtc_time(struct rtc_time *time)
-{
- u32 seconds = mktime(time->tm_year + 1900, time->tm_mon + 1,
- time->tm_mday, time->tm_hour,
- time->tm_min, time->tm_sec);
-
- return starfire_set_time(seconds);
-}
-
-static void hypervisor_get_rtc_time(struct rtc_time *time)
-{
- u32 seconds = hypervisor_get_time();
-
- to_tm(seconds, time);
- time->tm_year -= 1900;
- time->tm_mon -= 1;
-}
-
-static int hypervisor_set_rtc_time(struct rtc_time *time)
-{
- u32 seconds = mktime(time->tm_year + 1900, time->tm_mon + 1,
- time->tm_mday, time->tm_hour,
- time->tm_min, time->tm_sec);
-
- return hypervisor_set_time(seconds);
-}
-
-#ifdef CONFIG_PCI
-static void bq4802_get_rtc_time(struct rtc_time *time)
-{
- unsigned char val = readb(bq4802_regs + 0x0e);
- unsigned int century;
-
- writeb(val | 0x08, bq4802_regs + 0x0e);
-
- time->tm_sec = readb(bq4802_regs + 0x00);
- time->tm_min = readb(bq4802_regs + 0x02);
- time->tm_hour = readb(bq4802_regs + 0x04);
- time->tm_mday = readb(bq4802_regs + 0x06);
- time->tm_mon = readb(bq4802_regs + 0x09);
- time->tm_year = readb(bq4802_regs + 0x0a);
- time->tm_wday = readb(bq4802_regs + 0x08);
- century = readb(bq4802_regs + 0x0f);
-
- writeb(val, bq4802_regs + 0x0e);
-
- BCD_TO_BIN(time->tm_sec);
- BCD_TO_BIN(time->tm_min);
- BCD_TO_BIN(time->tm_hour);
- BCD_TO_BIN(time->tm_mday);
- BCD_TO_BIN(time->tm_mon);
- BCD_TO_BIN(time->tm_year);
- BCD_TO_BIN(time->tm_wday);
- BCD_TO_BIN(century);
-
- time->tm_year += (century * 100);
- time->tm_year -= 1900;
-
- time->tm_mon--;
-}
-
-static int bq4802_set_rtc_time(struct rtc_time *time)
-{
- unsigned char val = readb(bq4802_regs + 0x0e);
- unsigned char sec, min, hrs, day, mon, yrs, century;
- unsigned int year;
-
- year = time->tm_year + 1900;
- century = year / 100;
- yrs = year % 100;
-
- mon = time->tm_mon + 1; /* tm_mon starts at zero */
- day = time->tm_mday;
- hrs = time->tm_hour;
- min = time->tm_min;
- sec = time->tm_sec;
-
- BIN_TO_BCD(sec);
- BIN_TO_BCD(min);
- BIN_TO_BCD(hrs);
- BIN_TO_BCD(day);
- BIN_TO_BCD(mon);
- BIN_TO_BCD(yrs);
- BIN_TO_BCD(century);
-
- writeb(val | 0x08, bq4802_regs + 0x0e);
-
- writeb(sec, bq4802_regs + 0x00);
- writeb(min, bq4802_regs + 0x02);
- writeb(hrs, bq4802_regs + 0x04);
- writeb(day, bq4802_regs + 0x06);
- writeb(mon, bq4802_regs + 0x09);
- writeb(yrs, bq4802_regs + 0x0a);
- writeb(century, bq4802_regs + 0x0f);
-
- writeb(val, bq4802_regs + 0x0e);
-
- return 0;
-}
-
-static void cmos_get_rtc_time(struct rtc_time *rtc_tm)
-{
- unsigned char ctrl;
-
- rtc_tm->tm_sec = CMOS_READ(RTC_SECONDS);
- rtc_tm->tm_min = CMOS_READ(RTC_MINUTES);
- rtc_tm->tm_hour = CMOS_READ(RTC_HOURS);
- rtc_tm->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH);
- rtc_tm->tm_mon = CMOS_READ(RTC_MONTH);
- rtc_tm->tm_year = CMOS_READ(RTC_YEAR);
- rtc_tm->tm_wday = CMOS_READ(RTC_DAY_OF_WEEK);
-
- ctrl = CMOS_READ(RTC_CONTROL);
- if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
- BCD_TO_BIN(rtc_tm->tm_sec);
- BCD_TO_BIN(rtc_tm->tm_min);
- BCD_TO_BIN(rtc_tm->tm_hour);
- BCD_TO_BIN(rtc_tm->tm_mday);
- BCD_TO_BIN(rtc_tm->tm_mon);
- BCD_TO_BIN(rtc_tm->tm_year);
- BCD_TO_BIN(rtc_tm->tm_wday);
- }
-
- if (rtc_tm->tm_year <= 69)
- rtc_tm->tm_year += 100;
-
- rtc_tm->tm_mon--;
-}
-
-static int cmos_set_rtc_time(struct rtc_time *rtc_tm)
-{
- unsigned char mon, day, hrs, min, sec;
- unsigned char save_control, save_freq_select;
- unsigned int yrs;
-
- yrs = rtc_tm->tm_year;
- mon = rtc_tm->tm_mon + 1;
- day = rtc_tm->tm_mday;
- hrs = rtc_tm->tm_hour;
- min = rtc_tm->tm_min;
- sec = rtc_tm->tm_sec;
-
- if (yrs >= 100)
- yrs -= 100;
-
- if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
- BIN_TO_BCD(sec);
- BIN_TO_BCD(min);
- BIN_TO_BCD(hrs);
- BIN_TO_BCD(day);
- BIN_TO_BCD(mon);
- BIN_TO_BCD(yrs);
- }
-
- save_control = CMOS_READ(RTC_CONTROL);
- CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
- save_freq_select = CMOS_READ(RTC_FREQ_SELECT);
- CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
-
- CMOS_WRITE(yrs, RTC_YEAR);
- CMOS_WRITE(mon, RTC_MONTH);
- CMOS_WRITE(day, RTC_DAY_OF_MONTH);
- CMOS_WRITE(hrs, RTC_HOURS);
- CMOS_WRITE(min, RTC_MINUTES);
- CMOS_WRITE(sec, RTC_SECONDS);
-
- CMOS_WRITE(save_control, RTC_CONTROL);
- CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
-
- return 0;
-}
-#endif /* CONFIG_PCI */
-
-static void mostek_get_rtc_time(struct rtc_time *rtc_tm)
-{
- void __iomem *regs = mstk48t02_regs;
- u8 tmp;
-
- spin_lock_irq(&mostek_lock);
-
- tmp = mostek_read(regs + MOSTEK_CREG);
- tmp |= MSTK_CREG_READ;
- mostek_write(regs + MOSTEK_CREG, tmp);
-
- rtc_tm->tm_sec = MSTK_REG_SEC(regs);
- rtc_tm->tm_min = MSTK_REG_MIN(regs);
- rtc_tm->tm_hour = MSTK_REG_HOUR(regs);
- rtc_tm->tm_mday = MSTK_REG_DOM(regs);
- rtc_tm->tm_mon = MSTK_REG_MONTH(regs);
- rtc_tm->tm_year = MSTK_CVT_YEAR( MSTK_REG_YEAR(regs) );
- rtc_tm->tm_wday = MSTK_REG_DOW(regs);
-
- tmp = mostek_read(regs + MOSTEK_CREG);
- tmp &= ~MSTK_CREG_READ;
- mostek_write(regs + MOSTEK_CREG, tmp);
-
- spin_unlock_irq(&mostek_lock);
-
- rtc_tm->tm_mon--;
- rtc_tm->tm_wday--;
- rtc_tm->tm_year -= 1900;
-}
-
-static int mostek_set_rtc_time(struct rtc_time *rtc_tm)
-{
- unsigned char mon, day, hrs, min, sec, wday;
- void __iomem *regs = mstk48t02_regs;
- unsigned int yrs;
- u8 tmp;
-
- yrs = rtc_tm->tm_year + 1900;
- mon = rtc_tm->tm_mon + 1;
- day = rtc_tm->tm_mday;
- wday = rtc_tm->tm_wday + 1;
- hrs = rtc_tm->tm_hour;
- min = rtc_tm->tm_min;
- sec = rtc_tm->tm_sec;
-
- spin_lock_irq(&mostek_lock);
-
- tmp = mostek_read(regs + MOSTEK_CREG);
- tmp |= MSTK_CREG_WRITE;
- mostek_write(regs + MOSTEK_CREG, tmp);
-
- MSTK_SET_REG_SEC(regs, sec);
- MSTK_SET_REG_MIN(regs, min);
- MSTK_SET_REG_HOUR(regs, hrs);
- MSTK_SET_REG_DOW(regs, wday);
- MSTK_SET_REG_DOM(regs, day);
- MSTK_SET_REG_MONTH(regs, mon);
- MSTK_SET_REG_YEAR(regs, yrs - MSTK_YEAR_ZERO);
-
- tmp = mostek_read(regs + MOSTEK_CREG);
- tmp &= ~MSTK_CREG_WRITE;
- mostek_write(regs + MOSTEK_CREG, tmp);
-
- spin_unlock_irq(&mostek_lock);
-
- return 0;
-}
-
-struct mini_rtc_ops {
- void (*get_rtc_time)(struct rtc_time *);
- int (*set_rtc_time)(struct rtc_time *);
-};
-
-static struct mini_rtc_ops starfire_rtc_ops = {
- .get_rtc_time = starfire_get_rtc_time,
- .set_rtc_time = starfire_set_rtc_time,
-};
-
-static struct mini_rtc_ops hypervisor_rtc_ops = {
- .get_rtc_time = hypervisor_get_rtc_time,
- .set_rtc_time = hypervisor_set_rtc_time,
-};
-
-#ifdef CONFIG_PCI
-static struct mini_rtc_ops bq4802_rtc_ops = {
- .get_rtc_time = bq4802_get_rtc_time,
- .set_rtc_time = bq4802_set_rtc_time,
-};
-
-static struct mini_rtc_ops cmos_rtc_ops = {
- .get_rtc_time = cmos_get_rtc_time,
- .set_rtc_time = cmos_set_rtc_time,
-};
-#endif /* CONFIG_PCI */
-
-static struct mini_rtc_ops mostek_rtc_ops = {
- .get_rtc_time = mostek_get_rtc_time,
- .set_rtc_time = mostek_set_rtc_time,
-};
-
-static struct mini_rtc_ops *mini_rtc_ops;
-
-static inline void mini_get_rtc_time(struct rtc_time *time)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&rtc_lock, flags);
- mini_rtc_ops->get_rtc_time(time);
- spin_unlock_irqrestore(&rtc_lock, flags);
-}
-
-static inline int mini_set_rtc_time(struct rtc_time *time)
-{
- unsigned long flags;
- int err;
-
- spin_lock_irqsave(&rtc_lock, flags);
- err = mini_rtc_ops->set_rtc_time(time);
- spin_unlock_irqrestore(&rtc_lock, flags);
-
- return err;
-}
-
-static int mini_rtc_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- struct rtc_time wtime;
- void __user *argp = (void __user *)arg;
-
- switch (cmd) {
-
- case RTC_PLL_GET:
- return -EINVAL;
-
- case RTC_PLL_SET:
- return -EINVAL;
-
- case RTC_UIE_OFF: /* disable ints from RTC updates. */
- return 0;
-
- case RTC_UIE_ON: /* enable ints for RTC updates. */
- return -EINVAL;
-
- case RTC_RD_TIME: /* Read the time/date from RTC */
- /* this doesn't get week-day, who cares */
- memset(&wtime, 0, sizeof(wtime));
- mini_get_rtc_time(&wtime);
-
- return copy_to_user(argp, &wtime, sizeof(wtime)) ? -EFAULT : 0;
-
- case RTC_SET_TIME: /* Set the RTC */
- {
- int year, days;
-
- if (!capable(CAP_SYS_TIME))
- return -EACCES;
-
- if (copy_from_user(&wtime, argp, sizeof(wtime)))
- return -EFAULT;
-
- year = wtime.tm_year + 1900;
- days = month_days[wtime.tm_mon] +
- ((wtime.tm_mon == 1) && leapyear(year));
-
- if ((wtime.tm_mon < 0 || wtime.tm_mon > 11) ||
- (wtime.tm_mday < 1))
- return -EINVAL;
-
- if (wtime.tm_mday < 0 || wtime.tm_mday > days)
- return -EINVAL;
-
- if (wtime.tm_hour < 0 || wtime.tm_hour >= 24 ||
- wtime.tm_min < 0 || wtime.tm_min >= 60 ||
- wtime.tm_sec < 0 || wtime.tm_sec >= 60)
- return -EINVAL;
-
- return mini_set_rtc_time(&wtime);
- }
- }
-
- return -EINVAL;
-}
-
-static int mini_rtc_open(struct inode *inode, struct file *file)
-{
- lock_kernel();
- if (mini_rtc_status & RTC_IS_OPEN) {
- unlock_kernel();
- return -EBUSY;
- }
-
- mini_rtc_status |= RTC_IS_OPEN;
- unlock_kernel();
-
- return 0;
-}
-
-static int mini_rtc_release(struct inode *inode, struct file *file)
-{
- mini_rtc_status &= ~RTC_IS_OPEN;
- return 0;
-}
-
-
-static const struct file_operations mini_rtc_fops = {
- .owner = THIS_MODULE,
- .ioctl = mini_rtc_ioctl,
- .open = mini_rtc_open,
- .release = mini_rtc_release,
-};
-
-static struct miscdevice rtc_mini_dev =
-{
- .minor = RTC_MINOR,
- .name = "rtc",
- .fops = &mini_rtc_fops,
-};
-
-static int __init rtc_mini_init(void)
-{
- int retval;
-
- if (tlb_type == hypervisor)
- mini_rtc_ops = &hypervisor_rtc_ops;
- else if (this_is_starfire)
- mini_rtc_ops = &starfire_rtc_ops;
-#ifdef CONFIG_PCI
- else if (bq4802_regs)
- mini_rtc_ops = &bq4802_rtc_ops;
- else if (ds1287_regs)
- mini_rtc_ops = &cmos_rtc_ops;
-#endif /* CONFIG_PCI */
- else if (mstk48t02_regs)
- mini_rtc_ops = &mostek_rtc_ops;
- else
- return -ENODEV;
-
- printk(KERN_INFO "Mini RTC Driver\n");
-
- retval = misc_register(&rtc_mini_dev);
- if (retval < 0)
- return retval;
-
- return 0;
-}
-
-static void __exit rtc_mini_exit(void)
-{
- misc_deregister(&rtc_mini_dev);
-}
-
int __devinit read_current_timer(unsigned long *timer_val)
{
*timer_val = tick_ops->get_tick();
return 0;
}
-
-module_init(rtc_mini_init);
-module_exit(rtc_mini_exit);
diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c
index c824df1..81ccd22 100644
--- a/arch/sparc64/kernel/traps.c
+++ b/arch/sparc64/kernel/traps.c
@@ -38,6 +38,7 @@
#include <asm/timer.h>
#include <asm/head.h>
#include <asm/prom.h>
+#include <asm/memctrl.h>
#include "entry.h"
#include "kstack.h"
@@ -129,6 +130,56 @@
}
#endif
+static DEFINE_SPINLOCK(dimm_handler_lock);
+static dimm_printer_t dimm_handler;
+
+static int sprintf_dimm(int synd_code, unsigned long paddr, char *buf, int buflen)
+{
+ unsigned long flags;
+ int ret = -ENODEV;
+
+ spin_lock_irqsave(&dimm_handler_lock, flags);
+ if (dimm_handler) {
+ ret = dimm_handler(synd_code, paddr, buf, buflen);
+ } else if (tlb_type == spitfire) {
+ if (prom_getunumber(synd_code, paddr, buf, buflen) == -1)
+ ret = -EINVAL;
+ else
+ ret = 0;
+ } else
+ ret = -ENODEV;
+ spin_unlock_irqrestore(&dimm_handler_lock, flags);
+
+ return ret;
+}
+
+int register_dimm_printer(dimm_printer_t func)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&dimm_handler_lock, flags);
+ if (!dimm_handler)
+ dimm_handler = func;
+ else
+ ret = -EEXIST;
+ spin_unlock_irqrestore(&dimm_handler_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(register_dimm_printer);
+
+void unregister_dimm_printer(dimm_printer_t func)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dimm_handler_lock, flags);
+ if (dimm_handler == func)
+ dimm_handler = NULL;
+ spin_unlock_irqrestore(&dimm_handler_lock, flags);
+}
+EXPORT_SYMBOL_GPL(unregister_dimm_printer);
+
void spitfire_insn_access_exception(struct pt_regs *regs, unsigned long sfsr, unsigned long sfar)
{
siginfo_t info;
@@ -291,10 +342,7 @@
}
#ifdef CONFIG_PCI
-/* This is really pathetic... */
-extern volatile int pci_poke_in_progress;
-extern volatile int pci_poke_cpu;
-extern volatile int pci_poke_faulted;
+#include "pci_impl.h"
#endif
/* When access exceptions happen, we must do this. */
@@ -376,8 +424,7 @@
if (udbl & bit) {
scode = ecc_syndrome_table[udbl & 0xff];
- if (prom_getunumber(scode, afar,
- memmod_str, sizeof(memmod_str)) == -1)
+ if (sprintf_dimm(scode, afar, memmod_str, sizeof(memmod_str)) < 0)
p = syndrome_unknown;
else
p = memmod_str;
@@ -388,8 +435,7 @@
if (udbh & bit) {
scode = ecc_syndrome_table[udbh & 0xff];
- if (prom_getunumber(scode, afar,
- memmod_str, sizeof(memmod_str)) == -1)
+ if (sprintf_dimm(scode, afar, memmod_str, sizeof(memmod_str)) < 0)
p = syndrome_unknown;
else
p = memmod_str;
@@ -1062,8 +1108,6 @@
return "???";
}
-extern int chmc_getunumber(int, unsigned long, char *, int);
-
static void cheetah_log_errors(struct pt_regs *regs, struct cheetah_err_info *info,
unsigned long afsr, unsigned long afar, int recoverable)
{
@@ -1105,7 +1149,7 @@
syndrome = (afsr & CHAFSR_E_SYNDROME) >> CHAFSR_E_SYNDROME_SHIFT;
syndrome = cheetah_ecc_syntab[syndrome];
- ret = chmc_getunumber(syndrome, afar, unum, sizeof(unum));
+ ret = sprintf_dimm(syndrome, afar, unum, sizeof(unum));
if (ret != -1)
printk("%s" "ERROR(%d): AFAR E-syndrome [%s]\n",
(recoverable ? KERN_WARNING : KERN_CRIT),
@@ -1116,7 +1160,7 @@
syndrome = (afsr & CHAFSR_M_SYNDROME) >> CHAFSR_M_SYNDROME_SHIFT;
syndrome = cheetah_mtag_syntab[syndrome];
- ret = chmc_getunumber(syndrome, afar, unum, sizeof(unum));
+ ret = sprintf_dimm(syndrome, afar, unum, sizeof(unum));
if (ret != -1)
printk("%s" "ERROR(%d): AFAR M-syndrome [%s]\n",
(recoverable ? KERN_WARNING : KERN_CRIT),
@@ -2224,7 +2268,6 @@
extern int handle_popc(u32 insn, struct pt_regs *regs);
extern int handle_ldf_stq(u32 insn, struct pt_regs *regs);
-extern int vis_emul(struct pt_regs *, unsigned int);
void do_illegal_instruction(struct pt_regs *regs)
{
diff --git a/arch/sparc64/kernel/vio.c b/arch/sparc64/kernel/vio.c
index a490077..92b1f8e 100644
--- a/arch/sparc64/kernel/vio.c
+++ b/arch/sparc64/kernel/vio.c
@@ -152,7 +152,7 @@
static DEVICE_ATTR(obppath, S_IRUSR | S_IRGRP | S_IROTH,
show_pciobppath_attr, NULL);
-struct device_node *cdev_node;
+static struct device_node *cdev_node;
static struct vio_dev *root_vdev;
static u64 cdev_cfg_handle;
@@ -371,9 +371,9 @@
.node_name = "domain-services-port",
};
-const char *channel_devices_node = "channel-devices";
-const char *channel_devices_compat = "SUNW,sun4v-channel-devices";
-const char *cfg_handle_prop = "cfg-handle";
+static const char *channel_devices_node = "channel-devices";
+static const char *channel_devices_compat = "SUNW,sun4v-channel-devices";
+static const char *cfg_handle_prop = "cfg-handle";
static int __init vio_init(void)
{
diff --git a/arch/sparc64/kernel/visemul.c b/arch/sparc64/kernel/visemul.c
index c3fd647..9e05cb5 100644
--- a/arch/sparc64/kernel/visemul.c
+++ b/arch/sparc64/kernel/visemul.c
@@ -243,7 +243,7 @@
struct edge_tab {
u16 left, right;
};
-struct edge_tab edge8_tab[8] = {
+static struct edge_tab edge8_tab[8] = {
{ 0xff, 0x80 },
{ 0x7f, 0xc0 },
{ 0x3f, 0xe0 },
@@ -253,7 +253,7 @@
{ 0x03, 0xfe },
{ 0x01, 0xff },
};
-struct edge_tab edge8_tab_l[8] = {
+static struct edge_tab edge8_tab_l[8] = {
{ 0xff, 0x01 },
{ 0xfe, 0x03 },
{ 0xfc, 0x07 },
@@ -263,23 +263,23 @@
{ 0xc0, 0x7f },
{ 0x80, 0xff },
};
-struct edge_tab edge16_tab[4] = {
+static struct edge_tab edge16_tab[4] = {
{ 0xf, 0x8 },
{ 0x7, 0xc },
{ 0x3, 0xe },
{ 0x1, 0xf },
};
-struct edge_tab edge16_tab_l[4] = {
+static struct edge_tab edge16_tab_l[4] = {
{ 0xf, 0x1 },
{ 0xe, 0x3 },
{ 0xc, 0x7 },
{ 0x8, 0xf },
};
-struct edge_tab edge32_tab[2] = {
+static struct edge_tab edge32_tab[2] = {
{ 0x3, 0x2 },
{ 0x1, 0x3 },
};
-struct edge_tab edge32_tab_l[2] = {
+static struct edge_tab edge32_tab_l[2] = {
{ 0x3, 0x1 },
{ 0x2, 0x3 },
};
diff --git a/arch/sparc64/mm/fault.c b/arch/sparc64/mm/fault.c
index ea7d7ae..a9e474b 100644
--- a/arch/sparc64/mm/fault.c
+++ b/arch/sparc64/mm/fault.c
@@ -51,43 +51,6 @@
}
#endif
-/*
- * To debug kernel to catch accesses to certain virtual/physical addresses.
- * Mode = 0 selects physical watchpoints, mode = 1 selects virtual watchpoints.
- * flags = VM_READ watches memread accesses, flags = VM_WRITE watches memwrite accesses.
- * Caller passes in a 64bit aligned addr, with mask set to the bytes that need to be
- * watched. This is only useful on a single cpu machine for now. After the watchpoint
- * is detected, the process causing it will be killed, thus preventing an infinite loop.
- */
-void set_brkpt(unsigned long addr, unsigned char mask, int flags, int mode)
-{
- unsigned long lsubits;
-
- __asm__ __volatile__("ldxa [%%g0] %1, %0"
- : "=r" (lsubits)
- : "i" (ASI_LSU_CONTROL));
- lsubits &= ~(LSU_CONTROL_PM | LSU_CONTROL_VM |
- LSU_CONTROL_PR | LSU_CONTROL_VR |
- LSU_CONTROL_PW | LSU_CONTROL_VW);
-
- __asm__ __volatile__("stxa %0, [%1] %2\n\t"
- "membar #Sync"
- : /* no outputs */
- : "r" (addr), "r" (mode ? VIRT_WATCHPOINT : PHYS_WATCHPOINT),
- "i" (ASI_DMMU));
-
- lsubits |= ((unsigned long)mask << (mode ? 25 : 33));
- if (flags & VM_READ)
- lsubits |= (mode ? LSU_CONTROL_VR : LSU_CONTROL_PR);
- if (flags & VM_WRITE)
- lsubits |= (mode ? LSU_CONTROL_VW : LSU_CONTROL_PW);
- __asm__ __volatile__("stxa %0, [%%g0] %1\n\t"
- "membar #Sync"
- : /* no outputs */
- : "r" (lsubits), "i" (ASI_LSU_CONTROL)
- : "memory");
-}
-
static void __kprobes unhandled_fault(unsigned long address,
struct task_struct *tsk,
struct pt_regs *regs)
diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c
index a41df7b..3c10daf 100644
--- a/arch/sparc64/mm/init.c
+++ b/arch/sparc64/mm/init.c
@@ -46,15 +46,11 @@
#include <asm/tsb.h>
#include <asm/hypervisor.h>
#include <asm/prom.h>
-#include <asm/sstate.h>
#include <asm/mdesc.h>
#include <asm/cpudata.h>
#include <asm/irq.h>
-#define MAX_PHYS_ADDRESS (1UL << 42UL)
-#define KPTE_BITMAP_CHUNK_SZ (256UL * 1024UL * 1024UL)
-#define KPTE_BITMAP_BYTES \
- ((MAX_PHYS_ADDRESS / KPTE_BITMAP_CHUNK_SZ) / 8)
+#include "init.h"
unsigned long kern_linear_pte_xor[2] __read_mostly;
@@ -416,17 +412,9 @@
#endif /* CONFIG_DEBUG_DCFLUSH */
}
-struct linux_prom_translation {
- unsigned long virt;
- unsigned long size;
- unsigned long data;
-};
-
-/* Exported for kernel TLB miss handling in ktlb.S */
struct linux_prom_translation prom_trans[512] __read_mostly;
unsigned int prom_trans_ents __read_mostly;
-/* Exported for SMP bootup purposes. */
unsigned long kern_locked_tte_data;
/* The obp translations are saved based on 8k pagesize, since obp can
@@ -938,6 +926,10 @@
int count, nid;
u64 grp;
+ /* This is the right thing to do on currently supported
+ * SUN4U NUMA platforms as well, as the PCI controller does
+ * not sit behind any particular memory controller.
+ */
if (!mlgroups)
return -1;
@@ -1206,8 +1198,44 @@
return err;
}
+static int __init numa_parse_jbus(void)
+{
+ unsigned long cpu, index;
+
+ /* NUMA node id is encoded in bits 36 and higher, and there is
+ * a 1-to-1 mapping from CPU ID to NUMA node ID.
+ */
+ index = 0;
+ for_each_present_cpu(cpu) {
+ numa_cpu_lookup_table[cpu] = index;
+ numa_cpumask_lookup_table[index] = cpumask_of_cpu(cpu);
+ node_masks[index].mask = ~((1UL << 36UL) - 1UL);
+ node_masks[index].val = cpu << 36UL;
+
+ index++;
+ }
+ num_node_masks = index;
+
+ add_node_ranges();
+
+ for (index = 0; index < num_node_masks; index++) {
+ allocate_node_data(index);
+ node_set_online(index);
+ }
+
+ return 0;
+}
+
static int __init numa_parse_sun4u(void)
{
+ if (tlb_type == cheetah || tlb_type == cheetah_plus) {
+ unsigned long ver;
+
+ __asm__ ("rdpr %%ver, %0" : "=r" (ver));
+ if ((ver >> 32UL) == __JALAPENO_ID ||
+ (ver >> 32UL) == __SERRANO_ID)
+ return numa_parse_jbus();
+ }
return -1;
}
@@ -1633,8 +1661,6 @@
/* paging_init() sets up the page tables */
-extern void central_probe(void);
-
static unsigned long last_valid_pfn;
pgd_t swapper_pg_dir[2048];
@@ -1679,8 +1705,6 @@
kern_base = (prom_boot_mapping_phys_low >> 22UL) << 22UL;
kern_size = (unsigned long)&_end - (unsigned long)KERNBASE;
- sstate_booting();
-
/* Invalidate both kernel TSBs. */
memset(swapper_tsb, 0x40, sizeof(swapper_tsb));
#ifndef CONFIG_DEBUG_PAGEALLOC
@@ -1803,9 +1827,6 @@
}
printk("Booting Linux...\n");
-
- central_probe();
- cpu_probe();
}
int __init page_in_phys_avail(unsigned long paddr)
@@ -2032,7 +2053,6 @@
pgprot_t PAGE_SHARED __read_mostly;
EXPORT_SYMBOL(PAGE_SHARED);
-pgprot_t PAGE_EXEC __read_mostly;
unsigned long pg_iobits __read_mostly;
unsigned long _PAGE_IE __read_mostly;
@@ -2045,14 +2065,6 @@
EXPORT_SYMBOL(_PAGE_CACHE);
#ifdef CONFIG_SPARSEMEM_VMEMMAP
-
-#define VMEMMAP_CHUNK_SHIFT 22
-#define VMEMMAP_CHUNK (1UL << VMEMMAP_CHUNK_SHIFT)
-#define VMEMMAP_CHUNK_MASK ~(VMEMMAP_CHUNK - 1UL)
-#define VMEMMAP_ALIGN(x) (((x)+VMEMMAP_CHUNK-1UL)&VMEMMAP_CHUNK_MASK)
-
-#define VMEMMAP_SIZE ((((1UL << MAX_PHYSADDR_BITS) >> PAGE_SHIFT) * \
- sizeof(struct page *)) >> VMEMMAP_CHUNK_SHIFT)
unsigned long vmemmap_table[VMEMMAP_SIZE];
int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node)
@@ -2136,7 +2148,6 @@
_PAGE_CACHE_4U | _PAGE_P_4U |
__ACCESS_BITS_4U | __DIRTY_BITS_4U |
_PAGE_EXEC_4U | _PAGE_L_4U);
- PAGE_EXEC = __pgprot(_PAGE_EXEC_4U);
_PAGE_IE = _PAGE_IE_4U;
_PAGE_E = _PAGE_E_4U;
@@ -2147,10 +2158,10 @@
#ifdef CONFIG_DEBUG_PAGEALLOC
kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZBITS_4U) ^
- 0xfffff80000000000;
+ 0xfffff80000000000UL;
#else
kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZ4MB_4U) ^
- 0xfffff80000000000;
+ 0xfffff80000000000UL;
#endif
kern_linear_pte_xor[0] |= (_PAGE_CP_4U | _PAGE_CV_4U |
_PAGE_P_4U | _PAGE_W_4U);
@@ -2188,7 +2199,6 @@
__ACCESS_BITS_4V | __DIRTY_BITS_4V |
_PAGE_EXEC_4V);
PAGE_KERNEL_LOCKED = PAGE_KERNEL;
- PAGE_EXEC = __pgprot(_PAGE_EXEC_4V);
_PAGE_IE = _PAGE_IE_4V;
_PAGE_E = _PAGE_E_4V;
@@ -2196,20 +2206,20 @@
#ifdef CONFIG_DEBUG_PAGEALLOC
kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZBITS_4V) ^
- 0xfffff80000000000;
+ 0xfffff80000000000UL;
#else
kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZ4MB_4V) ^
- 0xfffff80000000000;
+ 0xfffff80000000000UL;
#endif
kern_linear_pte_xor[0] |= (_PAGE_CP_4V | _PAGE_CV_4V |
_PAGE_P_4V | _PAGE_W_4V);
#ifdef CONFIG_DEBUG_PAGEALLOC
kern_linear_pte_xor[1] = (_PAGE_VALID | _PAGE_SZBITS_4V) ^
- 0xfffff80000000000;
+ 0xfffff80000000000UL;
#else
kern_linear_pte_xor[1] = (_PAGE_VALID | _PAGE_SZ256MB_4V) ^
- 0xfffff80000000000;
+ 0xfffff80000000000UL;
#endif
kern_linear_pte_xor[1] |= (_PAGE_CP_4V | _PAGE_CV_4V |
_PAGE_P_4V | _PAGE_W_4V);
diff --git a/arch/sparc64/mm/init.h b/arch/sparc64/mm/init.h
new file mode 100644
index 0000000..1606387
--- /dev/null
+++ b/arch/sparc64/mm/init.h
@@ -0,0 +1,49 @@
+#ifndef _SPARC64_MM_INIT_H
+#define _SPARC64_MM_INIT_H
+
+/* Most of the symbols in this file are defined in init.c and
+ * marked non-static so that assembler code can get at them.
+ */
+
+#define MAX_PHYS_ADDRESS (1UL << 42UL)
+#define KPTE_BITMAP_CHUNK_SZ (256UL * 1024UL * 1024UL)
+#define KPTE_BITMAP_BYTES \
+ ((MAX_PHYS_ADDRESS / KPTE_BITMAP_CHUNK_SZ) / 8)
+
+extern unsigned long kern_linear_pte_xor[2];
+extern unsigned long kpte_linear_bitmap[KPTE_BITMAP_BYTES / sizeof(unsigned long)];
+extern unsigned int sparc64_highest_unlocked_tlb_ent;
+extern unsigned long sparc64_kern_pri_context;
+extern unsigned long sparc64_kern_pri_nuc_bits;
+extern unsigned long sparc64_kern_sec_context;
+extern void mmu_info(struct seq_file *m);
+
+struct linux_prom_translation {
+ unsigned long virt;
+ unsigned long size;
+ unsigned long data;
+};
+
+/* Exported for kernel TLB miss handling in ktlb.S */
+extern struct linux_prom_translation prom_trans[512];
+extern unsigned int prom_trans_ents;
+
+/* Exported for SMP bootup purposes. */
+extern unsigned long kern_locked_tte_data;
+
+extern void prom_world(int enter);
+
+extern void free_initmem(void);
+
+#ifdef CONFIG_SPARSEMEM_VMEMMAP
+#define VMEMMAP_CHUNK_SHIFT 22
+#define VMEMMAP_CHUNK (1UL << VMEMMAP_CHUNK_SHIFT)
+#define VMEMMAP_CHUNK_MASK ~(VMEMMAP_CHUNK - 1UL)
+#define VMEMMAP_ALIGN(x) (((x)+VMEMMAP_CHUNK-1UL)&VMEMMAP_CHUNK_MASK)
+
+#define VMEMMAP_SIZE ((((1UL << MAX_PHYSADDR_BITS) >> PAGE_SHIFT) * \
+ sizeof(struct page *)) >> VMEMMAP_CHUNK_SHIFT)
+extern unsigned long vmemmap_table[VMEMMAP_SIZE];
+#endif
+
+#endif /* _SPARC64_MM_INIT_H */
diff --git a/arch/sparc64/mm/tlb.c b/arch/sparc64/mm/tlb.c
index ae24919..d8f21e2 100644
--- a/arch/sparc64/mm/tlb.c
+++ b/arch/sparc64/mm/tlb.c
@@ -19,7 +19,7 @@
/* Heavily inspired by the ppc64 code. */
-DEFINE_PER_CPU(struct mmu_gather, mmu_gathers) = { 0, };
+DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
void flush_tlb_pending(void)
{
diff --git a/arch/um/Kconfig.i386 b/arch/um/Kconfig.i386
index e09edfa..1f57c11 100644
--- a/arch/um/Kconfig.i386
+++ b/arch/um/Kconfig.i386
@@ -9,8 +9,9 @@
default y
config X86_32
- bool
- default y
+ bool
+ default y
+ select HAVE_AOUT
config RWSEM_XCHGADD_ALGORITHM
def_bool y
@@ -42,6 +43,3 @@
config GENERIC_HWEIGHT
bool
default y
-
-config ARCH_SUPPORTS_AOUT
- def_bool y
diff --git a/arch/um/Kconfig.x86_64 b/arch/um/Kconfig.x86_64
index 5696e7b..40b3407 100644
--- a/arch/um/Kconfig.x86_64
+++ b/arch/um/Kconfig.x86_64
@@ -37,6 +37,3 @@
config GENERIC_HWEIGHT
bool
default y
-
-config ARCH_SUPPORTS_AOUT
- def_bool y
diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c
index d741f35..14a102e 100644
--- a/arch/um/drivers/line.c
+++ b/arch/um/drivers/line.c
@@ -275,6 +275,8 @@
case TIOCGLTC:
case TIOCSLTC:
#endif
+ /* Note: these are out of date as we now have TCGETS2 etc but this
+ whole lot should probably go away */
case TCGETS:
case TCSETSF:
case TCSETSW:
diff --git a/arch/um/sys-x86_64/syscall_table.c b/arch/um/sys-x86_64/syscall_table.c
index c128eb8..32f5fbe 100644
--- a/arch/um/sys-x86_64/syscall_table.c
+++ b/arch/um/sys-x86_64/syscall_table.c
@@ -41,12 +41,12 @@
#define stub_rt_sigreturn sys_rt_sigreturn
#define __SYSCALL(nr, sym) extern asmlinkage void sym(void) ;
-#undef _ASM_X86_64_UNISTD_H_
+#undef ASM_X86__UNISTD_64_H
#include <asm-x86/unistd_64.h>
#undef __SYSCALL
#define __SYSCALL(nr, sym) [ nr ] = sym,
-#undef _ASM_X86_64_UNISTD_H_
+#undef ASM_X86__UNISTD_64_H
typedef void (*sys_call_ptr_t)(void);
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 0d7cdbb..f65c274 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -18,6 +18,7 @@
### Arch settings
config X86
def_bool y
+ select HAVE_AOUT if X86_32
select HAVE_UNSTABLE_SCHED_CLOCK
select HAVE_IDE
select HAVE_OPROFILE
@@ -152,9 +153,6 @@
bool
default X86_64
-config ARCH_SUPPORTS_AOUT
- def_bool y
-
config ARCH_SUPPORTS_OPTIMIZED_INLINING
def_bool y
@@ -778,23 +776,45 @@
Say N otherwise.
config MICROCODE
- tristate "/dev/cpu/microcode - Intel IA32 CPU microcode support"
+ tristate "/dev/cpu/microcode - microcode support"
select FW_LOADER
---help---
If you say Y here, you will be able to update the microcode on
- Intel processors in the IA32 family, e.g. Pentium Pro, Pentium II,
- Pentium III, Pentium 4, Xeon etc. You will obviously need the
- actual microcode binary data itself which is not shipped with the
- Linux kernel.
+ certain Intel and AMD processors. The Intel support is for the
+ IA32 family, e.g. Pentium Pro, Pentium II, Pentium III,
+ Pentium 4, Xeon etc. The AMD support is for family 0x10 and
+ 0x11 processors, e.g. Opteron, Phenom and Turion 64 Ultra.
+ You will obviously need the actual microcode binary data itself
+ which is not shipped with the Linux kernel.
- For latest news and information on obtaining all the required
- ingredients for this driver, check:
- <http://www.urbanmyth.org/microcode/>.
+ This option selects the general module only, you need to select
+ at least one vendor specific module as well.
To compile this driver as a module, choose M here: the
module will be called microcode.
-config MICROCODE_OLD_INTERFACE
+config MICROCODE_INTEL
+ bool "Intel microcode patch loading support"
+ depends on MICROCODE
+ default MICROCODE
+ select FW_LOADER
+ --help---
+ This options enables microcode patch loading support for Intel
+ processors.
+
+ For latest news and information on obtaining all the required
+ Intel ingredients for this driver, check:
+ <http://www.urbanmyth.org/microcode/>.
+
+config MICROCODE_AMD
+ bool "AMD microcode patch loading support"
+ depends on MICROCODE
+ select FW_LOADER
+ --help---
+ If you select this option, microcode patch loading support for AMD
+ processors will be enabled.
+
+ config MICROCODE_OLD_INTERFACE
def_bool y
depends on MICROCODE
@@ -1061,6 +1081,56 @@
low memory. Setting this option will put user-space page table
entries in high memory.
+config X86_CHECK_BIOS_CORRUPTION
+ bool "Check for low memory corruption"
+ help
+ Periodically check for memory corruption in low memory, which
+ is suspected to be caused by BIOS. Even when enabled in the
+ configuration, it is disabled at runtime. Enable it by
+ setting "memory_corruption_check=1" on the kernel command
+ line. By default it scans the low 64k of memory every 60
+ seconds; see the memory_corruption_check_size and
+ memory_corruption_check_period parameters in
+ Documentation/kernel-parameters.txt to adjust this.
+
+ When enabled with the default parameters, this option has
+ almost no overhead, as it reserves a relatively small amount
+ of memory and scans it infrequently. It both detects corruption
+ and prevents it from affecting the running system.
+
+ It is, however, intended as a diagnostic tool; if repeatable
+ BIOS-originated corruption always affects the same memory,
+ you can use memmap= to prevent the kernel from using that
+ memory.
+
+config X86_BOOTPARAM_MEMORY_CORRUPTION_CHECK
+ bool "Set the default setting of memory_corruption_check"
+ depends on X86_CHECK_BIOS_CORRUPTION
+ default y
+ help
+ Set whether the default state of memory_corruption_check is
+ on or off.
+
+config X86_RESERVE_LOW_64K
+ bool "Reserve low 64K of RAM on AMI/Phoenix BIOSen"
+ default y
+ help
+ Reserve the first 64K of physical RAM on BIOSes that are known
+ to potentially corrupt that memory range. A numbers of BIOSes are
+ known to utilize this area during suspend/resume, so it must not
+ be used by the kernel.
+
+ Set this to N if you are absolutely sure that you trust the BIOS
+ to get all its memory reservations and usages right.
+
+ If you have doubts about the BIOS (e.g. suspend/resume does not
+ work or there's kernel crashes after certain hardware hotplug
+ events) and it's not AMI or Phoenix, then you might want to enable
+ X86_CHECK_BIOS_CORRUPTION=y to allow the kernel to check typical
+ corruption patterns.
+
+ Say Y if unsure.
+
config MATH_EMULATION
bool
prompt "Math emulation" if X86_32
@@ -1689,6 +1759,14 @@
workaround will setup a 1:1 mapping for the first
16M to make floppy (an ISA device) work.
+config INTR_REMAP
+ bool "Support for Interrupt Remapping (EXPERIMENTAL)"
+ depends on X86_64 && X86_IO_APIC && PCI_MSI && ACPI && EXPERIMENTAL
+ help
+ Supports Interrupt remapping for IO-APIC and MSI devices.
+ To use x2apic mode in the CPU's which support x2APIC enhancements or
+ to support platforms with CPU's having > 8 bit APIC ID, say Y.
+
source "drivers/pci/pcie/Kconfig"
source "drivers/pci/Kconfig"
@@ -1805,7 +1883,7 @@
config IA32_AOUT
tristate "IA32 a.out support"
- depends on IA32_EMULATION && ARCH_SUPPORTS_AOUT
+ depends on IA32_EMULATION
help
Support old a.out binaries in the 32bit emulation.
diff --git a/arch/x86/Kconfig.cpu b/arch/x86/Kconfig.cpu
index 60a8576..0b7c4a3 100644
--- a/arch/x86/Kconfig.cpu
+++ b/arch/x86/Kconfig.cpu
@@ -38,8 +38,7 @@
- "Crusoe" for the Transmeta Crusoe series.
- "Efficeon" for the Transmeta Efficeon series.
- "Winchip-C6" for original IDT Winchip.
- - "Winchip-2" for IDT Winchip 2.
- - "Winchip-2A" for IDT Winchips with 3dNow! capabilities.
+ - "Winchip-2" for IDT Winchips with 3dNow! capabilities.
- "GeodeGX1" for Geode GX1 (Cyrix MediaGX).
- "Geode GX/LX" For AMD Geode GX and LX processors.
- "CyrixIII/VIA C3" for VIA Cyrix III or VIA C3.
@@ -194,19 +193,11 @@
treat this chip as a 586TSC with some extended instructions
and alignment requirements.
-config MWINCHIP2
- bool "Winchip-2"
- depends on X86_32
- help
- Select this for an IDT Winchip-2. Linux and GCC
- treat this chip as a 586TSC with some extended instructions
- and alignment requirements.
-
config MWINCHIP3D
- bool "Winchip-2A/Winchip-3"
+ bool "Winchip-2/Winchip-2A/Winchip-3"
depends on X86_32
help
- Select this for an IDT Winchip-2A or 3. Linux and GCC
+ Select this for an IDT Winchip-2, 2A or 3. Linux and GCC
treat this chip as a 586TSC with some extended instructions
and alignment requirements. Also enable out of order memory
stores for this CPU, which can increase performance of some
@@ -318,7 +309,7 @@
int
default "7" if MPENTIUM4 || X86_GENERIC || GENERIC_CPU || MPSC
default "4" if X86_ELAN || M486 || M386 || MGEODEGX1
- default "5" if MWINCHIP3D || MWINCHIP2 || MWINCHIPC6 || MCRUSOE || MEFFICEON || MCYRIXIII || MK6 || MPENTIUMIII || MPENTIUMII || M686 || M586MMX || M586TSC || M586 || MVIAC3_2 || MGEODE_LX
+ default "5" if MWINCHIP3D || MWINCHIPC6 || MCRUSOE || MEFFICEON || MCYRIXIII || MK6 || MPENTIUMIII || MPENTIUMII || M686 || M586MMX || M586TSC || M586 || MVIAC3_2 || MGEODE_LX
default "6" if MK7 || MK8 || MPENTIUMM || MCORE2 || MVIAC7
config X86_XADD
@@ -360,7 +351,7 @@
config X86_ALIGNMENT_16
def_bool y
- depends on MWINCHIP3D || MWINCHIP2 || MWINCHIPC6 || MCYRIXIII || X86_ELAN || MK6 || M586MMX || M586TSC || M586 || M486 || MVIAC3_2 || MGEODEGX1
+ depends on MWINCHIP3D || MWINCHIPC6 || MCYRIXIII || X86_ELAN || MK6 || M586MMX || M586TSC || M586 || M486 || MVIAC3_2 || MGEODEGX1
config X86_INTEL_USERCOPY
def_bool y
@@ -368,7 +359,7 @@
config X86_USE_PPRO_CHECKSUM
def_bool y
- depends on MWINCHIP3D || MWINCHIP2 || MWINCHIPC6 || MCYRIXIII || MK7 || MK6 || MPENTIUM4 || MPENTIUMM || MPENTIUMIII || MPENTIUMII || M686 || MK8 || MVIAC3_2 || MEFFICEON || MGEODE_LX || MCORE2
+ depends on MWINCHIP3D || MWINCHIPC6 || MCYRIXIII || MK7 || MK6 || MPENTIUM4 || MPENTIUMM || MPENTIUMIII || MPENTIUMII || M686 || MK8 || MVIAC3_2 || MEFFICEON || MGEODE_LX || MCORE2
config X86_USE_3DNOW
def_bool y
@@ -376,7 +367,7 @@
config X86_OOSTORE
def_bool y
- depends on (MWINCHIP3D || MWINCHIP2 || MWINCHIPC6) && MTRR
+ depends on (MWINCHIP3D || MWINCHIPC6) && MTRR
#
# P6_NOPs are a relatively minor optimization that require a family >=
@@ -396,7 +387,7 @@
config X86_TSC
def_bool y
- depends on ((MWINCHIP3D || MWINCHIP2 || MCRUSOE || MEFFICEON || MCYRIXIII || MK7 || MK6 || MPENTIUM4 || MPENTIUMM || MPENTIUMIII || MPENTIUMII || M686 || M586MMX || M586TSC || MK8 || MVIAC3_2 || MVIAC7 || MGEODEGX1 || MGEODE_LX || MCORE2) && !X86_NUMAQ) || X86_64
+ depends on ((MWINCHIP3D || MCRUSOE || MEFFICEON || MCYRIXIII || MK7 || MK6 || MPENTIUM4 || MPENTIUMM || MPENTIUMIII || MPENTIUMII || M686 || M586MMX || M586TSC || MK8 || MVIAC3_2 || MVIAC7 || MGEODEGX1 || MGEODE_LX || MCORE2) && !X86_NUMAQ) || X86_64
config X86_CMPXCHG64
def_bool y
@@ -406,7 +397,7 @@
# generates cmov.
config X86_CMOV
def_bool y
- depends on (MK7 || MPENTIUM4 || MPENTIUMM || MPENTIUMIII || MPENTIUMII || M686 || MVIAC3_2 || MVIAC7 || X86_64)
+ depends on (MK8 || MK7 || MCORE2 || MPENTIUM4 || MPENTIUMM || MPENTIUMIII || MPENTIUMII || M686 || MVIAC3_2 || MVIAC7 || MCRUSOE || MEFFICEON || X86_64)
config X86_MINIMUM_CPU_FAMILY
int
@@ -417,7 +408,109 @@
config X86_DEBUGCTLMSR
def_bool y
- depends on !(MK6 || MWINCHIPC6 || MWINCHIP2 || MWINCHIP3D || MCYRIXIII || M586MMX || M586TSC || M586 || M486 || M386)
+ depends on !(MK6 || MWINCHIPC6 || MWINCHIP3D || MCYRIXIII || M586MMX || M586TSC || M586 || M486 || M386)
+
+menuconfig PROCESSOR_SELECT
+ bool "Supported processor vendors" if EMBEDDED
+ help
+ This lets you choose what x86 vendor support code your kernel
+ will include.
+
+config CPU_SUP_INTEL
+ default y
+ bool "Support Intel processors" if PROCESSOR_SELECT
+ help
+ This enables detection, tunings and quirks for Intel processors
+
+ You need this enabled if you want your kernel to run on an
+ Intel CPU. Disabling this option on other types of CPUs
+ makes the kernel a tiny bit smaller. Disabling it on an Intel
+ CPU might render the kernel unbootable.
+
+ If unsure, say N.
+
+config CPU_SUP_CYRIX_32
+ default y
+ bool "Support Cyrix processors" if PROCESSOR_SELECT
+ depends on !64BIT
+ help
+ This enables detection, tunings and quirks for Cyrix processors
+
+ You need this enabled if you want your kernel to run on a
+ Cyrix CPU. Disabling this option on other types of CPUs
+ makes the kernel a tiny bit smaller. Disabling it on a Cyrix
+ CPU might render the kernel unbootable.
+
+ If unsure, say N.
+
+config CPU_SUP_AMD
+ default y
+ bool "Support AMD processors" if PROCESSOR_SELECT
+ help
+ This enables detection, tunings and quirks for AMD processors
+
+ You need this enabled if you want your kernel to run on an
+ AMD CPU. Disabling this option on other types of CPUs
+ makes the kernel a tiny bit smaller. Disabling it on an AMD
+ CPU might render the kernel unbootable.
+
+ If unsure, say N.
+
+config CPU_SUP_CENTAUR_32
+ default y
+ bool "Support Centaur processors" if PROCESSOR_SELECT
+ depends on !64BIT
+ help
+ This enables detection, tunings and quirks for Centaur processors
+
+ You need this enabled if you want your kernel to run on a
+ Centaur CPU. Disabling this option on other types of CPUs
+ makes the kernel a tiny bit smaller. Disabling it on a Centaur
+ CPU might render the kernel unbootable.
+
+ If unsure, say N.
+
+config CPU_SUP_CENTAUR_64
+ default y
+ bool "Support Centaur processors" if PROCESSOR_SELECT
+ depends on 64BIT
+ help
+ This enables detection, tunings and quirks for Centaur processors
+
+ You need this enabled if you want your kernel to run on a
+ Centaur CPU. Disabling this option on other types of CPUs
+ makes the kernel a tiny bit smaller. Disabling it on a Centaur
+ CPU might render the kernel unbootable.
+
+ If unsure, say N.
+
+config CPU_SUP_TRANSMETA_32
+ default y
+ bool "Support Transmeta processors" if PROCESSOR_SELECT
+ depends on !64BIT
+ help
+ This enables detection, tunings and quirks for Transmeta processors
+
+ You need this enabled if you want your kernel to run on a
+ Transmeta CPU. Disabling this option on other types of CPUs
+ makes the kernel a tiny bit smaller. Disabling it on a Transmeta
+ CPU might render the kernel unbootable.
+
+ If unsure, say N.
+
+config CPU_SUP_UMC_32
+ default y
+ bool "Support UMC processors" if PROCESSOR_SELECT
+ depends on !64BIT
+ help
+ This enables detection, tunings and quirks for UMC processors
+
+ You need this enabled if you want your kernel to run on a
+ UMC CPU. Disabling this option on other types of CPUs
+ makes the kernel a tiny bit smaller. Disabling it on a UMC
+ CPU might render the kernel unbootable.
+
+ If unsure, say N.
config X86_DS
bool "Debug Store support"
diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug
index 092f019..2a3dfbd 100644
--- a/arch/x86/Kconfig.debug
+++ b/arch/x86/Kconfig.debug
@@ -43,6 +43,19 @@
with klogd/syslogd or the X server. You should normally N here,
unless you want to debug such a crash.
+config EARLY_PRINTK_DBGP
+ bool "Early printk via EHCI debug port"
+ default n
+ depends on EARLY_PRINTK && PCI
+ help
+ Write kernel log output directly into the EHCI debug port.
+
+ This is useful for kernel debugging when your machine crashes very
+ early before the console code is initialized. For normal operation
+ it is not recommended because it looks ugly and doesn't cooperate
+ with klogd/syslogd or the X server. You should normally N here,
+ unless you want to debug such a crash. You need usb debug device.
+
config DEBUG_STACKOVERFLOW
bool "Check for stack overflows"
depends on DEBUG_KERNEL
diff --git a/arch/x86/Makefile_32.cpu b/arch/x86/Makefile_32.cpu
index e372b58..80177ec 100644
--- a/arch/x86/Makefile_32.cpu
+++ b/arch/x86/Makefile_32.cpu
@@ -28,7 +28,6 @@
cflags-$(CONFIG_MCRUSOE) += -march=i686 $(align)-functions=0 $(align)-jumps=0 $(align)-loops=0
cflags-$(CONFIG_MEFFICEON) += -march=i686 $(call tune,pentium3) $(align)-functions=0 $(align)-jumps=0 $(align)-loops=0
cflags-$(CONFIG_MWINCHIPC6) += $(call cc-option,-march=winchip-c6,-march=i586)
-cflags-$(CONFIG_MWINCHIP2) += $(call cc-option,-march=winchip2,-march=i586)
cflags-$(CONFIG_MWINCHIP3D) += $(call cc-option,-march=winchip2,-march=i586)
cflags-$(CONFIG_MCYRIXIII) += $(call cc-option,-march=c3,-march=i486) $(align)-functions=0 $(align)-jumps=0 $(align)-loops=0
cflags-$(CONFIG_MVIAC3_2) += $(call cc-option,-march=c3-2,-march=i686)
@@ -45,3 +44,8 @@
# cpu entries
cflags-$(CONFIG_X86_GENERIC) += $(call tune,generic,$(call tune,i686))
+# Bug fix for binutils: this option is required in order to keep
+# binutils from generating NOPL instructions against our will.
+ifneq ($(CONFIG_X86_P6_NOP),y)
+cflags-y += $(call cc-option,-Wa$(comma)-mtune=generic32,)
+endif
diff --git a/arch/x86/boot/Makefile b/arch/x86/boot/Makefile
index 7ee102f..cd48c72 100644
--- a/arch/x86/boot/Makefile
+++ b/arch/x86/boot/Makefile
@@ -72,9 +72,7 @@
KBUILD_CFLAGS += $(call cc-option,-m32)
KBUILD_AFLAGS := $(KBUILD_CFLAGS) -D__ASSEMBLY__
-$(obj)/zImage: IMAGE_OFFSET := 0x1000
$(obj)/zImage: asflags-y := $(SVGA_MODE) $(RAMDISK)
-$(obj)/bzImage: IMAGE_OFFSET := 0x100000
$(obj)/bzImage: ccflags-y := -D__BIG_KERNEL__
$(obj)/bzImage: asflags-y := $(SVGA_MODE) $(RAMDISK) -D__BIG_KERNEL__
$(obj)/bzImage: BUILDFLAGS := -b
@@ -117,7 +115,7 @@
$(call if_changed,objcopy)
$(obj)/compressed/vmlinux: FORCE
- $(Q)$(MAKE) $(build)=$(obj)/compressed IMAGE_OFFSET=$(IMAGE_OFFSET) $@
+ $(Q)$(MAKE) $(build)=$(obj)/compressed $@
# Set this if you want to pass append arguments to the zdisk/fdimage/isoimage kernel
FDARGS =
@@ -181,6 +179,7 @@
mkisofs -J -r -o $(obj)/image.iso -b isolinux.bin -c boot.cat \
-no-emul-boot -boot-load-size 4 -boot-info-table \
$(obj)/isoimage
+ isohybrid $(obj)/image.iso 2>/dev/null || true
rm -rf $(obj)/isoimage
zlilo: $(BOOTIMAGE)
diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index 92fdd35..1771c80 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -27,9 +27,8 @@
$(call if_changed,objcopy)
-ifeq ($(CONFIG_X86_32),y)
-targets += vmlinux.bin.all vmlinux.relocs
-hostprogs-y := relocs
+targets += vmlinux.bin.all vmlinux.relocs relocs
+hostprogs-$(CONFIG_X86_32) += relocs
quiet_cmd_relocs = RELOCS $@
cmd_relocs = $(obj)/relocs $< > $@;$(obj)/relocs --abs-relocs $<
@@ -43,6 +42,8 @@
$(obj)/vmlinux.bin.all: $(vmlinux.bin.all-y) FORCE
$(call if_changed,relocbin)
+ifeq ($(CONFIG_X86_32),y)
+
ifdef CONFIG_RELOCATABLE
$(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin.all FORCE
$(call if_changed,gzip)
@@ -59,6 +60,5 @@
LDFLAGS_piggy.o := -r --format binary --oformat elf64-x86-64 -T
endif
-
$(obj)/piggy.o: $(obj)/vmlinux.scr $(obj)/vmlinux.bin.gz FORCE
$(call if_changed,ld)
diff --git a/arch/x86/boot/cpu.c b/arch/x86/boot/cpu.c
index 75298fe..6ec6bb6 100644
--- a/arch/x86/boot/cpu.c
+++ b/arch/x86/boot/cpu.c
@@ -59,17 +59,18 @@
u32 e = err_flags[i];
for (j = 0; j < 32; j++) {
- int n = (i << 5)+j;
- if (*msg_strs < n) {
+ if (msg_strs[0] < i ||
+ (msg_strs[0] == i && msg_strs[1] < j)) {
/* Skip to the next string */
- do {
- msg_strs++;
- } while (*msg_strs);
- msg_strs++;
+ msg_strs += 2;
+ while (*msg_strs++)
+ ;
}
if (e & 1) {
- if (*msg_strs == n && msg_strs[1])
- printf("%s ", msg_strs+1);
+ if (msg_strs[0] == i &&
+ msg_strs[1] == j &&
+ msg_strs[2])
+ printf("%s ", msg_strs+2);
else
printf("%d:%d ", i, j);
}
diff --git a/arch/x86/boot/edd.c b/arch/x86/boot/edd.c
index d93cbc6..1aae8f3 100644
--- a/arch/x86/boot/edd.c
+++ b/arch/x86/boot/edd.c
@@ -41,6 +41,7 @@
char *mbrbuf_ptr, *mbrbuf_end;
u32 buf_base, mbr_base;
extern char _end[];
+ u16 mbr_magic;
sector_size = ei->params.bytes_per_sector;
if (!sector_size)
@@ -58,11 +59,15 @@
if (mbrbuf_end > (char *)(size_t)boot_params.hdr.heap_end_ptr)
return -1;
+ memset(mbrbuf_ptr, 0, sector_size);
if (read_mbr(devno, mbrbuf_ptr))
return -1;
*mbrsig = *(u32 *)&mbrbuf_ptr[EDD_MBR_SIG_OFFSET];
- return 0;
+ mbr_magic = *(u16 *)&mbrbuf_ptr[510];
+
+ /* check for valid MBR magic */
+ return mbr_magic == 0xAA55 ? 0 : -1;
}
static int get_edd_info(u8 devno, struct edd_info *ei)
diff --git a/arch/x86/boot/mkcpustr.c b/arch/x86/boot/mkcpustr.c
index bbe7695..8ef60f2 100644
--- a/arch/x86/boot/mkcpustr.c
+++ b/arch/x86/boot/mkcpustr.c
@@ -15,33 +15,33 @@
#include <stdio.h>
-#include "../kernel/cpu/feature_names.c"
-
-#if NCAPFLAGS > 8
-# error "Need to adjust the boot code handling of CPUID strings"
-#endif
+#include "../kernel/cpu/capflags.c"
int main(void)
{
- int i;
+ int i, j;
const char *str;
printf("static const char x86_cap_strs[] = \n");
- for (i = 0; i < NCAPINTS*32; i++) {
- str = x86_cap_flags[i];
+ for (i = 0; i < NCAPINTS; i++) {
+ for (j = 0; j < 32; j++) {
+ str = x86_cap_flags[i*32+j];
- if (i == NCAPINTS*32-1) {
- /* The last entry must be unconditional; this
- also consumes the compiler-added null character */
- if (!str)
- str = "";
- printf("\t\"\\x%02x\"\"%s\"\n", i, str);
- } else if (str) {
- printf("#if REQUIRED_MASK%d & (1 << %d)\n"
- "\t\"\\x%02x\"\"%s\\0\"\n"
- "#endif\n",
- i >> 5, i & 31, i, str);
+ if (i == NCAPINTS-1 && j == 31) {
+ /* The last entry must be unconditional; this
+ also consumes the compiler-added null
+ character */
+ if (!str)
+ str = "";
+ printf("\t\"\\x%02x\\x%02x\"\"%s\"\n",
+ i, j, str);
+ } else if (str) {
+ printf("#if REQUIRED_MASK%d & (1 << %d)\n"
+ "\t\"\\x%02x\\x%02x\"\"%s\\0\"\n"
+ "#endif\n",
+ i, j, i, j, str);
+ }
}
}
printf("\t;\n");
diff --git a/arch/x86/boot/video-vesa.c b/arch/x86/boot/video-vesa.c
index 401ad99..1e6fe02 100644
--- a/arch/x86/boot/video-vesa.c
+++ b/arch/x86/boot/video-vesa.c
@@ -224,7 +224,7 @@
static void vesa_store_mode_params_graphics(void)
{
/* Tell the kernel we're in VESA graphics mode */
- boot_params.screen_info.orig_video_isVGA = 0x23;
+ boot_params.screen_info.orig_video_isVGA = VIDEO_TYPE_VLFB;
/* Mode parameters */
boot_params.screen_info.vesa_attributes = vminfo.mode_attr;
diff --git a/arch/x86/configs/i386_defconfig b/arch/x86/configs/i386_defconfig
index ef9a520..52d0359 100644
--- a/arch/x86/configs/i386_defconfig
+++ b/arch/x86/configs/i386_defconfig
@@ -213,7 +213,6 @@
# CONFIG_MCRUSOE is not set
# CONFIG_MEFFICEON is not set
# CONFIG_MWINCHIPC6 is not set
-# CONFIG_MWINCHIP2 is not set
# CONFIG_MWINCHIP3D is not set
# CONFIG_MGEODEGX1 is not set
# CONFIG_MGEODE_LX is not set
@@ -1535,7 +1534,6 @@
CONFIG_VGA_CONSOLE=y
CONFIG_VGACON_SOFT_SCROLLBACK=y
CONFIG_VGACON_SOFT_SCROLLBACK_SIZE=64
-CONFIG_VIDEO_SELECT=y
CONFIG_DUMMY_CONSOLE=y
# CONFIG_FRAMEBUFFER_CONSOLE is not set
CONFIG_LOGO=y
diff --git a/arch/x86/configs/x86_64_defconfig b/arch/x86/configs/x86_64_defconfig
index e620ea6..f0a03d7 100644
--- a/arch/x86/configs/x86_64_defconfig
+++ b/arch/x86/configs/x86_64_defconfig
@@ -210,7 +210,6 @@
# CONFIG_MCRUSOE is not set
# CONFIG_MEFFICEON is not set
# CONFIG_MWINCHIPC6 is not set
-# CONFIG_MWINCHIP2 is not set
# CONFIG_MWINCHIP3D is not set
# CONFIG_MGEODEGX1 is not set
# CONFIG_MGEODE_LX is not set
@@ -1505,7 +1504,6 @@
CONFIG_VGA_CONSOLE=y
CONFIG_VGACON_SOFT_SCROLLBACK=y
CONFIG_VGACON_SOFT_SCROLLBACK_SIZE=64
-CONFIG_VIDEO_SELECT=y
CONFIG_DUMMY_CONSOLE=y
# CONFIG_FRAMEBUFFER_CONSOLE is not set
CONFIG_LOGO=y
diff --git a/arch/x86/ia32/ia32_signal.c b/arch/x86/ia32/ia32_signal.c
index f1a2ac7..4bc02b2 100644
--- a/arch/x86/ia32/ia32_signal.c
+++ b/arch/x86/ia32/ia32_signal.c
@@ -179,9 +179,10 @@
u32 pretcode;
int sig;
struct sigcontext_ia32 sc;
- struct _fpstate_ia32 fpstate;
+ struct _fpstate_ia32 fpstate_unused; /* look at kernel/sigframe.h */
unsigned int extramask[_COMPAT_NSIG_WORDS-1];
char retcode[8];
+ /* fp state follows here */
};
struct rt_sigframe
@@ -192,8 +193,8 @@
u32 puc;
compat_siginfo_t info;
struct ucontext_ia32 uc;
- struct _fpstate_ia32 fpstate;
char retcode[8];
+ /* fp state follows here */
};
#define COPY(x) { \
@@ -215,7 +216,7 @@
unsigned int *peax)
{
unsigned int tmpflags, gs, oldgs, err = 0;
- struct _fpstate_ia32 __user *buf;
+ void __user *buf;
u32 tmp;
/* Always make any pending restarted system calls return -EINTR */
@@ -259,26 +260,12 @@
err |= __get_user(tmp, &sc->fpstate);
buf = compat_ptr(tmp);
- if (buf) {
- if (!access_ok(VERIFY_READ, buf, sizeof(*buf)))
- goto badframe;
- err |= restore_i387_ia32(buf);
- } else {
- struct task_struct *me = current;
-
- if (used_math()) {
- clear_fpu(me);
- clear_used_math();
- }
- }
+ err |= restore_i387_xstate_ia32(buf);
err |= __get_user(tmp, &sc->ax);
*peax = tmp;
return err;
-
-badframe:
- return 1;
}
asmlinkage long sys32_sigreturn(struct pt_regs *regs)
@@ -350,7 +337,7 @@
*/
static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc,
- struct _fpstate_ia32 __user *fpstate,
+ void __user *fpstate,
struct pt_regs *regs, unsigned int mask)
{
int tmp, err = 0;
@@ -364,31 +351,28 @@
savesegment(es, tmp);
err |= __put_user(tmp, (unsigned int __user *)&sc->es);
- err |= __put_user((u32)regs->di, &sc->di);
- err |= __put_user((u32)regs->si, &sc->si);
- err |= __put_user((u32)regs->bp, &sc->bp);
- err |= __put_user((u32)regs->sp, &sc->sp);
- err |= __put_user((u32)regs->bx, &sc->bx);
- err |= __put_user((u32)regs->dx, &sc->dx);
- err |= __put_user((u32)regs->cx, &sc->cx);
- err |= __put_user((u32)regs->ax, &sc->ax);
- err |= __put_user((u32)regs->cs, &sc->cs);
- err |= __put_user((u32)regs->ss, &sc->ss);
+ err |= __put_user(regs->di, &sc->di);
+ err |= __put_user(regs->si, &sc->si);
+ err |= __put_user(regs->bp, &sc->bp);
+ err |= __put_user(regs->sp, &sc->sp);
+ err |= __put_user(regs->bx, &sc->bx);
+ err |= __put_user(regs->dx, &sc->dx);
+ err |= __put_user(regs->cx, &sc->cx);
+ err |= __put_user(regs->ax, &sc->ax);
+ err |= __put_user(regs->cs, &sc->cs);
+ err |= __put_user(regs->ss, &sc->ss);
err |= __put_user(current->thread.trap_no, &sc->trapno);
err |= __put_user(current->thread.error_code, &sc->err);
- err |= __put_user((u32)regs->ip, &sc->ip);
- err |= __put_user((u32)regs->flags, &sc->flags);
- err |= __put_user((u32)regs->sp, &sc->sp_at_signal);
+ err |= __put_user(regs->ip, &sc->ip);
+ err |= __put_user(regs->flags, &sc->flags);
+ err |= __put_user(regs->sp, &sc->sp_at_signal);
- tmp = save_i387_ia32(fpstate);
+ tmp = save_i387_xstate_ia32(fpstate);
if (tmp < 0)
err = -EFAULT;
- else {
- clear_used_math();
- stts();
+ else
err |= __put_user(ptr_to_compat(tmp ? fpstate : NULL),
&sc->fpstate);
- }
/* non-iBCS2 extensions.. */
err |= __put_user(mask, &sc->oldmask);
@@ -401,7 +385,8 @@
* Determine which stack to use..
*/
static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
- size_t frame_size)
+ size_t frame_size,
+ void **fpstate)
{
unsigned long sp;
@@ -420,6 +405,11 @@
ka->sa.sa_restorer)
sp = (unsigned long) ka->sa.sa_restorer;
+ if (used_math()) {
+ sp = sp - sig_xstate_ia32_size;
+ *fpstate = (struct _fpstate_ia32 *) sp;
+ }
+
sp -= frame_size;
/* Align the stack pointer according to the i386 ABI,
* i.e. so that on function entry ((sp + 4) & 15) == 0. */
@@ -433,6 +423,7 @@
struct sigframe __user *frame;
void __user *restorer;
int err = 0;
+ void __user *fpstate = NULL;
/* copy_to_user optimizes that into a single 8 byte store */
static const struct {
@@ -447,25 +438,21 @@
0,
};
- frame = get_sigframe(ka, regs, sizeof(*frame));
+ frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
- goto give_sigsegv;
+ return -EFAULT;
- err |= __put_user(sig, &frame->sig);
- if (err)
- goto give_sigsegv;
+ if (__put_user(sig, &frame->sig))
+ return -EFAULT;
- err |= ia32_setup_sigcontext(&frame->sc, &frame->fpstate, regs,
- set->sig[0]);
- if (err)
- goto give_sigsegv;
+ if (ia32_setup_sigcontext(&frame->sc, fpstate, regs, set->sig[0]))
+ return -EFAULT;
if (_COMPAT_NSIG_WORDS > 1) {
- err |= __copy_to_user(frame->extramask, &set->sig[1],
- sizeof(frame->extramask));
- if (err)
- goto give_sigsegv;
+ if (__copy_to_user(frame->extramask, &set->sig[1],
+ sizeof(frame->extramask)))
+ return -EFAULT;
}
if (ka->sa.sa_flags & SA_RESTORER) {
@@ -486,7 +473,7 @@
*/
err |= __copy_to_user(frame->retcode, &code, 8);
if (err)
- goto give_sigsegv;
+ return -EFAULT;
/* Set up registers for signal handler */
regs->sp = (unsigned long) frame;
@@ -509,10 +496,6 @@
#endif
return 0;
-
-give_sigsegv:
- force_sigsegv(sig, current);
- return -EFAULT;
}
int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
@@ -521,6 +504,7 @@
struct rt_sigframe __user *frame;
void __user *restorer;
int err = 0;
+ void __user *fpstate = NULL;
/* __copy_to_user optimizes that into a single 8 byte store */
static const struct {
@@ -536,30 +520,33 @@
0,
};
- frame = get_sigframe(ka, regs, sizeof(*frame));
+ frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
- goto give_sigsegv;
+ return -EFAULT;
err |= __put_user(sig, &frame->sig);
err |= __put_user(ptr_to_compat(&frame->info), &frame->pinfo);
err |= __put_user(ptr_to_compat(&frame->uc), &frame->puc);
err |= copy_siginfo_to_user32(&frame->info, info);
if (err)
- goto give_sigsegv;
+ return -EFAULT;
/* Create the ucontext. */
- err |= __put_user(0, &frame->uc.uc_flags);
+ if (cpu_has_xsave)
+ err |= __put_user(UC_FP_XSTATE, &frame->uc.uc_flags);
+ else
+ err |= __put_user(0, &frame->uc.uc_flags);
err |= __put_user(0, &frame->uc.uc_link);
err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
err |= __put_user(sas_ss_flags(regs->sp),
&frame->uc.uc_stack.ss_flags);
err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
- err |= ia32_setup_sigcontext(&frame->uc.uc_mcontext, &frame->fpstate,
+ err |= ia32_setup_sigcontext(&frame->uc.uc_mcontext, fpstate,
regs, set->sig[0]);
err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
if (err)
- goto give_sigsegv;
+ return -EFAULT;
if (ka->sa.sa_flags & SA_RESTORER)
restorer = ka->sa.sa_restorer;
@@ -574,7 +561,7 @@
*/
err |= __copy_to_user(frame->retcode, &code, 8);
if (err)
- goto give_sigsegv;
+ return -EFAULT;
/* Set up registers for signal handler */
regs->sp = (unsigned long) frame;
@@ -602,8 +589,4 @@
#endif
return 0;
-
-give_sigsegv:
- force_sigsegv(sig, current);
- return -EFAULT;
}
diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S
index ffc1bb4..eb43147 100644
--- a/arch/x86/ia32/ia32entry.S
+++ b/arch/x86/ia32/ia32entry.S
@@ -39,11 +39,11 @@
.endm
/* clobbers %eax */
- .macro CLEAR_RREGS
+ .macro CLEAR_RREGS _r9=rax
xorl %eax,%eax
movq %rax,R11(%rsp)
movq %rax,R10(%rsp)
- movq %rax,R9(%rsp)
+ movq %\_r9,R9(%rsp)
movq %rax,R8(%rsp)
.endm
@@ -52,11 +52,10 @@
* We don't reload %eax because syscall_trace_enter() returned
* the value it wants us to use in the table lookup.
*/
- .macro LOAD_ARGS32 offset
- movl \offset(%rsp),%r11d
- movl \offset+8(%rsp),%r10d
+ .macro LOAD_ARGS32 offset, _r9=0
+ .if \_r9
movl \offset+16(%rsp),%r9d
- movl \offset+24(%rsp),%r8d
+ .endif
movl \offset+40(%rsp),%ecx
movl \offset+48(%rsp),%edx
movl \offset+56(%rsp),%esi
@@ -145,7 +144,7 @@
SAVE_ARGS 0,0,1
/* no need to do an access_ok check here because rbp has been
32bit zero extended */
-1: movl (%rbp),%r9d
+1: movl (%rbp),%ebp
.section __ex_table,"a"
.quad 1b,ia32_badarg
.previous
@@ -157,7 +156,7 @@
cmpl $(IA32_NR_syscalls-1),%eax
ja ia32_badsys
sysenter_do_call:
- IA32_ARG_FIXUP 1
+ IA32_ARG_FIXUP
sysenter_dispatch:
call *ia32_sys_call_table(,%rax,8)
movq %rax,RAX-ARGOFFSET(%rsp)
@@ -234,20 +233,17 @@
#endif
sysenter_tracesys:
- xchgl %r9d,%ebp
#ifdef CONFIG_AUDITSYSCALL
testl $(_TIF_WORK_SYSCALL_ENTRY & ~_TIF_SYSCALL_AUDIT),TI_flags(%r10)
jz sysenter_auditsys
#endif
SAVE_REST
CLEAR_RREGS
- movq %r9,R9(%rsp)
movq $-ENOSYS,RAX(%rsp)/* ptrace can change this for a bad syscall */
movq %rsp,%rdi /* &pt_regs -> arg1 */
call syscall_trace_enter
LOAD_ARGS32 ARGOFFSET /* reload args from stack in case ptrace changed it */
RESTORE_REST
- xchgl %ebp,%r9d
cmpl $(IA32_NR_syscalls-1),%eax
ja int_ret_from_sys_call /* sysenter_tracesys has set RAX(%rsp) */
jmp sysenter_do_call
@@ -314,9 +310,9 @@
testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%r10)
CFI_REMEMBER_STATE
jnz cstar_tracesys
-cstar_do_call:
cmpl $IA32_NR_syscalls-1,%eax
ja ia32_badsys
+cstar_do_call:
IA32_ARG_FIXUP 1
cstar_dispatch:
call *ia32_sys_call_table(,%rax,8)
@@ -357,15 +353,13 @@
#endif
xchgl %r9d,%ebp
SAVE_REST
- CLEAR_RREGS
- movq %r9,R9(%rsp)
+ CLEAR_RREGS r9
movq $-ENOSYS,RAX(%rsp) /* ptrace can change this for a bad syscall */
movq %rsp,%rdi /* &pt_regs -> arg1 */
call syscall_trace_enter
- LOAD_ARGS32 ARGOFFSET /* reload args from stack in case ptrace changed it */
+ LOAD_ARGS32 ARGOFFSET, 1 /* reload args from stack in case ptrace changed it */
RESTORE_REST
xchgl %ebp,%r9d
- movl RSP-ARGOFFSET(%rsp), %r8d
cmpl $(IA32_NR_syscalls-1),%eax
ja int_ret_from_sys_call /* cstar_tracesys has set RAX(%rsp) */
jmp cstar_do_call
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 3db651f..0d41f03 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -10,7 +10,7 @@
# Do not profile debug and lowlevel utilities
CFLAGS_REMOVE_tsc.o = -pg
CFLAGS_REMOVE_rtc.o = -pg
-CFLAGS_REMOVE_paravirt.o = -pg
+CFLAGS_REMOVE_paravirt-spinlocks.o = -pg
endif
#
@@ -23,7 +23,7 @@
CFLAGS_tsc.o := $(nostackp)
obj-y := process_$(BITS).o signal_$(BITS).o entry_$(BITS).o
-obj-y += traps_$(BITS).o irq_$(BITS).o
+obj-y += traps.o irq_$(BITS).o dumpstack_$(BITS).o
obj-y += time_$(BITS).o ioport.o ldt.o
obj-y += setup.o i8259.o irqinit_$(BITS).o setup_percpu.o
obj-$(CONFIG_X86_VISWS) += visws_quirks.o
@@ -38,7 +38,7 @@
obj-$(CONFIG_X86_TRAMPOLINE) += trampoline.o
obj-y += process.o
-obj-y += i387.o
+obj-y += i387.o xsave.o
obj-y += ptrace.o
obj-y += ds.o
obj-$(CONFIG_X86_32) += tls.o
@@ -51,7 +51,6 @@
obj-$(CONFIG_MCA) += mca_32.o
obj-$(CONFIG_X86_MSR) += msr.o
obj-$(CONFIG_X86_CPUID) += cpuid.o
-obj-$(CONFIG_MICROCODE) += microcode.o
obj-$(CONFIG_PCI) += early-quirks.o
apm-y := apm_32.o
obj-$(CONFIG_APM) += apm.o
@@ -69,6 +68,7 @@
obj-$(CONFIG_KEXEC) += relocate_kernel_$(BITS).o crash.o
obj-$(CONFIG_CRASH_DUMP) += crash_dump_$(BITS).o
obj-$(CONFIG_X86_NUMAQ) += numaq_32.o
+obj-$(CONFIG_X86_ES7000) += es7000_32.o
obj-$(CONFIG_X86_SUMMIT_NUMA) += summit_32.o
obj-y += vsmp_64.o
obj-$(CONFIG_KPROBES) += kprobes.o
@@ -89,7 +89,7 @@
obj-$(CONFIG_VMI) += vmi_32.o vmiclock_32.o
obj-$(CONFIG_KVM_GUEST) += kvm.o
obj-$(CONFIG_KVM_CLOCK) += kvmclock.o
-obj-$(CONFIG_PARAVIRT) += paravirt.o paravirt_patch_$(BITS).o
+obj-$(CONFIG_PARAVIRT) += paravirt.o paravirt_patch_$(BITS).o paravirt-spinlocks.o
obj-$(CONFIG_PARAVIRT_CLOCK) += pvclock.o
obj-$(CONFIG_PCSPKR_PLATFORM) += pcspeaker.o
@@ -99,11 +99,18 @@
obj-$(CONFIG_OLPC) += olpc.o
+microcode-y := microcode_core.o
+microcode-$(CONFIG_MICROCODE_INTEL) += microcode_intel.o
+microcode-$(CONFIG_MICROCODE_AMD) += microcode_amd.o
+obj-$(CONFIG_MICROCODE) += microcode.o
+
###
# 64 bit specific files
ifeq ($(CONFIG_X86_64),y)
obj-y += genapic_64.o genapic_flat_64.o genx2apic_uv_x.o tlb_uv.o
obj-y += bios_uv.o
+ obj-y += genx2apic_cluster.o
+ obj-y += genx2apic_phys.o
obj-$(CONFIG_X86_PM_TIMER) += pmtimer_64.o
obj-$(CONFIG_AUDIT) += audit_64.o
diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
index 7d40ef7..eb875cd 100644
--- a/arch/x86/kernel/acpi/boot.c
+++ b/arch/x86/kernel/acpi/boot.c
@@ -252,10 +252,8 @@
return;
}
-#ifdef CONFIG_X86_32
if (boot_cpu_physical_apicid != -1U)
ver = apic_version[boot_cpu_physical_apicid];
-#endif
generic_processor_info(id, ver);
}
@@ -774,11 +772,9 @@
set_fixmap_nocache(FIX_APIC_BASE, address);
if (boot_cpu_physical_apicid == -1U) {
- boot_cpu_physical_apicid = GET_APIC_ID(read_apic_id());
-#ifdef CONFIG_X86_32
+ boot_cpu_physical_apicid = read_apic_id();
apic_version[boot_cpu_physical_apicid] =
GET_APIC_VERSION(apic_read(APIC_LVR));
-#endif
}
}
@@ -1350,7 +1346,9 @@
acpi_ioapic = 1;
smp_found_config = 1;
+#ifdef CONFIG_X86_32
setup_apic_routing();
+#endif
}
}
if (error == -EINVAL) {
@@ -1420,8 +1418,16 @@
*/
static int __init dmi_ignore_irq0_timer_override(const struct dmi_system_id *d)
{
- pr_notice("%s detected: Ignoring BIOS IRQ0 pin2 override\n", d->ident);
- acpi_skip_timer_override = 1;
+ /*
+ * The ati_ixp4x0_rev() early PCI quirk should have set
+ * the acpi_skip_timer_override flag already:
+ */
+ if (!acpi_skip_timer_override) {
+ WARN(1, KERN_ERR "ati_ixp4x0 quirk not complete.\n");
+ pr_notice("%s detected: Ignoring BIOS IRQ0 pin2 override\n",
+ d->ident);
+ acpi_skip_timer_override = 1;
+ }
return 0;
}
diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c
index fb04e49..a84ac7b 100644
--- a/arch/x86/kernel/alternative.c
+++ b/arch/x86/kernel/alternative.c
@@ -444,7 +444,7 @@
_text, _etext);
/* Only switch to UP mode if we don't immediately boot others */
- if (num_possible_cpus() == 1 || setup_max_cpus <= 1)
+ if (num_present_cpus() == 1 || setup_max_cpus <= 1)
alternatives_smp_switch(0);
}
#endif
diff --git a/arch/x86/kernel/amd_iommu_init.c b/arch/x86/kernel/amd_iommu_init.c
index 148fcfe..4cd8083 100644
--- a/arch/x86/kernel/amd_iommu_init.c
+++ b/arch/x86/kernel/amd_iommu_init.c
@@ -723,9 +723,7 @@
init_iommu_from_acpi(iommu, h);
init_iommu_devices(iommu);
- pci_enable_device(iommu->dev);
-
- return 0;
+ return pci_enable_device(iommu->dev);
}
/*
diff --git a/arch/x86/kernel/apic_32.c b/arch/x86/kernel/apic_32.c
index f88bd0d..21c831d 100644
--- a/arch/x86/kernel/apic_32.c
+++ b/arch/x86/kernel/apic_32.c
@@ -60,10 +60,8 @@
static int force_enable_local_apic;
int disable_apic;
-/* Local APIC timer verification ok */
-static int local_apic_timer_verify_ok;
/* Disable local APIC timer from the kernel commandline or via dmi quirk */
-static int local_apic_timer_disabled;
+static int disable_apic_timer __cpuinitdata;
/* Local APIC timer works in C2 */
int local_apic_timer_c2_ok;
EXPORT_SYMBOL_GPL(local_apic_timer_c2_ok);
@@ -130,7 +128,11 @@
*/
static inline int lapic_is_integrated(void)
{
+#ifdef CONFIG_X86_64
+ return 1;
+#else
return APIC_INTEGRATED(lapic_get_version());
+#endif
}
/*
@@ -145,13 +147,18 @@
return lapic_get_version() >= 0x14;
}
-void apic_wait_icr_idle(void)
+/*
+ * Paravirt kernels also might be using these below ops. So we still
+ * use generic apic_read()/apic_write(), which might be pointing to different
+ * ops in PARAVIRT case.
+ */
+void xapic_wait_icr_idle(void)
{
while (apic_read(APIC_ICR) & APIC_ICR_BUSY)
cpu_relax();
}
-u32 safe_apic_wait_icr_idle(void)
+u32 safe_xapic_wait_icr_idle(void)
{
u32 send_status;
int timeout;
@@ -167,16 +174,48 @@
return send_status;
}
+void xapic_icr_write(u32 low, u32 id)
+{
+ apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(id));
+ apic_write(APIC_ICR, low);
+}
+
+u64 xapic_icr_read(void)
+{
+ u32 icr1, icr2;
+
+ icr2 = apic_read(APIC_ICR2);
+ icr1 = apic_read(APIC_ICR);
+
+ return icr1 | ((u64)icr2 << 32);
+}
+
+static struct apic_ops xapic_ops = {
+ .read = native_apic_mem_read,
+ .write = native_apic_mem_write,
+ .icr_read = xapic_icr_read,
+ .icr_write = xapic_icr_write,
+ .wait_icr_idle = xapic_wait_icr_idle,
+ .safe_wait_icr_idle = safe_xapic_wait_icr_idle,
+};
+
+struct apic_ops __read_mostly *apic_ops = &xapic_ops;
+EXPORT_SYMBOL_GPL(apic_ops);
+
/**
* enable_NMI_through_LVT0 - enable NMI through local vector table 0
*/
void __cpuinit enable_NMI_through_LVT0(void)
{
- unsigned int v = APIC_DM_NMI;
+ unsigned int v;
- /* Level triggered for 82489DX */
+ /* unmask and set to NMI */
+ v = APIC_DM_NMI;
+
+ /* Level triggered for 82489DX (32bit mode) */
if (!lapic_is_integrated())
v |= APIC_LVT_LEVEL_TRIGGER;
+
apic_write(APIC_LVT0, v);
}
@@ -193,9 +232,13 @@
*/
int lapic_get_maxlvt(void)
{
- unsigned int v = apic_read(APIC_LVR);
+ unsigned int v;
- /* 82489DXs do not report # of LVT entries. */
+ v = apic_read(APIC_LVR);
+ /*
+ * - we always have APIC integrated on 64bit mode
+ * - 82489DXs do not report # of LVT entries
+ */
return APIC_INTEGRATED(GET_APIC_VERSION(v)) ? GET_APIC_MAXLVT(v) : 2;
}
@@ -203,8 +246,12 @@
* Local APIC timer
*/
-/* Clock divisor is set to 16 */
+/* Clock divisor */
+#ifdef CONFG_X86_64
+#define APIC_DIVISOR 1
+#else
#define APIC_DIVISOR 16
+#endif
/*
* This function sets up the local APIC timer, with a timeout of
@@ -212,6 +259,9 @@
* this function twice on the boot CPU, once with a bogus timeout
* value, second time for real. The other (noncalibrating) CPUs
* call this function only once, with the real, calibrated value.
+ *
+ * We do reads before writes even if unnecessary, to get around the
+ * P5 APIC double write bug.
*/
static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen)
{
@@ -233,14 +283,48 @@
*/
tmp_value = apic_read(APIC_TDCR);
apic_write(APIC_TDCR,
- (tmp_value & ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE)) |
- APIC_TDR_DIV_16);
+ (tmp_value & ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE)) |
+ APIC_TDR_DIV_16);
if (!oneshot)
apic_write(APIC_TMICT, clocks / APIC_DIVISOR);
}
/*
+ * Setup extended LVT, AMD specific (K8, family 10h)
+ *
+ * Vector mappings are hard coded. On K8 only offset 0 (APIC500) and
+ * MCE interrupts are supported. Thus MCE offset must be set to 0.
+ *
+ * If mask=1, the LVT entry does not generate interrupts while mask=0
+ * enables the vector. See also the BKDGs.
+ */
+
+#define APIC_EILVT_LVTOFF_MCE 0
+#define APIC_EILVT_LVTOFF_IBS 1
+
+static void setup_APIC_eilvt(u8 lvt_off, u8 vector, u8 msg_type, u8 mask)
+{
+ unsigned long reg = (lvt_off << 4) + APIC_EILVT0;
+ unsigned int v = (mask << 16) | (msg_type << 8) | vector;
+
+ apic_write(reg, v);
+}
+
+u8 setup_APIC_eilvt_mce(u8 vector, u8 msg_type, u8 mask)
+{
+ setup_APIC_eilvt(APIC_EILVT_LVTOFF_MCE, vector, msg_type, mask);
+ return APIC_EILVT_LVTOFF_MCE;
+}
+
+u8 setup_APIC_eilvt_ibs(u8 vector, u8 msg_type, u8 mask)
+{
+ setup_APIC_eilvt(APIC_EILVT_LVTOFF_IBS, vector, msg_type, mask);
+ return APIC_EILVT_LVTOFF_IBS;
+}
+EXPORT_SYMBOL_GPL(setup_APIC_eilvt_ibs);
+
+/*
* Program the next event, relative to now
*/
static int lapic_next_event(unsigned long delta,
@@ -259,8 +343,8 @@
unsigned long flags;
unsigned int v;
- /* Lapic used for broadcast ? */
- if (!local_apic_timer_verify_ok)
+ /* Lapic used as dummy for broadcast ? */
+ if (evt->features & CLOCK_EVT_FEAT_DUMMY)
return;
local_irq_save(flags);
@@ -473,7 +557,7 @@
return -1;
}
- local_apic_timer_verify_ok = 1;
+ levt->features &= ~CLOCK_EVT_FEAT_DUMMY;
/* We trust the pm timer based calibration */
if (!pm_referenced) {
@@ -507,11 +591,11 @@
if (deltaj >= LAPIC_CAL_LOOPS-2 && deltaj <= LAPIC_CAL_LOOPS+2)
apic_printk(APIC_VERBOSE, "... jiffies result ok\n");
else
- local_apic_timer_verify_ok = 0;
+ levt->features |= CLOCK_EVT_FEAT_DUMMY;
} else
local_irq_enable();
- if (!local_apic_timer_verify_ok) {
+ if (levt->features & CLOCK_EVT_FEAT_DUMMY) {
printk(KERN_WARNING
"APIC timer disabled due to verification failure.\n");
return -1;
@@ -533,7 +617,8 @@
* timer as a dummy clock event source on SMP systems, so the
* broadcast mechanism is used. On UP systems simply ignore it.
*/
- if (local_apic_timer_disabled) {
+ if (disable_apic_timer) {
+ printk(KERN_INFO "Disabling APIC timer\n");
/* No broadcast on UP ! */
if (num_possible_cpus() > 1) {
lapic_clockevent.mult = 1;
@@ -602,7 +687,11 @@
/*
* the NMI deadlock-detector uses this.
*/
+#ifdef CONFIG_X86_64
+ add_pda(apic_timer_irqs, 1);
+#else
per_cpu(irq_stat, cpu).apic_timer_irqs++;
+#endif
evt->event_handler(evt);
}
@@ -642,35 +731,6 @@
}
/*
- * Setup extended LVT, AMD specific (K8, family 10h)
- *
- * Vector mappings are hard coded. On K8 only offset 0 (APIC500) and
- * MCE interrupts are supported. Thus MCE offset must be set to 0.
- */
-
-#define APIC_EILVT_LVTOFF_MCE 0
-#define APIC_EILVT_LVTOFF_IBS 1
-
-static void setup_APIC_eilvt(u8 lvt_off, u8 vector, u8 msg_type, u8 mask)
-{
- unsigned long reg = (lvt_off << 4) + APIC_EILVT0;
- unsigned int v = (mask << 16) | (msg_type << 8) | vector;
- apic_write(reg, v);
-}
-
-u8 setup_APIC_eilvt_mce(u8 vector, u8 msg_type, u8 mask)
-{
- setup_APIC_eilvt(APIC_EILVT_LVTOFF_MCE, vector, msg_type, mask);
- return APIC_EILVT_LVTOFF_MCE;
-}
-
-u8 setup_APIC_eilvt_ibs(u8 vector, u8 msg_type, u8 mask)
-{
- setup_APIC_eilvt(APIC_EILVT_LVTOFF_IBS, vector, msg_type, mask);
- return APIC_EILVT_LVTOFF_IBS;
-}
-
-/*
* Local APIC start and shutdown
*/
@@ -715,7 +775,7 @@
}
/* lets not touch this if we didn't frob it */
-#ifdef CONFIG_X86_MCE_P4THERMAL
+#if defined(CONFIG_X86_MCE_P4THERMAL) || defined(X86_MCE_INTEL)
if (maxlvt >= 5) {
v = apic_read(APIC_LVTTHMR);
apic_write(APIC_LVTTHMR, v | APIC_LVT_MASKED);
@@ -732,10 +792,6 @@
if (maxlvt >= 4)
apic_write(APIC_LVTPC, APIC_LVT_MASKED);
-#ifdef CONFIG_X86_MCE_P4THERMAL
- if (maxlvt >= 5)
- apic_write(APIC_LVTTHMR, APIC_LVT_MASKED);
-#endif
/* Integrated APIC (!82489DX) ? */
if (lapic_is_integrated()) {
if (maxlvt > 3)
@@ -750,7 +806,7 @@
*/
void disable_local_APIC(void)
{
- unsigned long value;
+ unsigned int value;
clear_local_APIC();
@@ -762,6 +818,7 @@
value &= ~APIC_SPIV_APIC_ENABLED;
apic_write(APIC_SPIV, value);
+#ifdef CONFIG_X86_32
/*
* When LAPIC was disabled by the BIOS and enabled by the kernel,
* restore the disabled state.
@@ -773,6 +830,7 @@
l &= ~MSR_IA32_APICBASE_ENABLE;
wrmsr(MSR_IA32_APICBASE, l, h);
}
+#endif
}
/*
@@ -789,11 +847,15 @@
return;
local_irq_save(flags);
- clear_local_APIC();
- if (enabled_via_apicbase)
+#ifdef CONFIG_X86_32
+ if (!enabled_via_apicbase)
+ clear_local_APIC();
+ else
+#endif
disable_local_APIC();
+
local_irq_restore(flags);
}
@@ -838,6 +900,12 @@
*/
reg0 = apic_read(APIC_ID);
apic_printk(APIC_DEBUG, "Getting ID: %x\n", reg0);
+ apic_write(APIC_ID, reg0 ^ APIC_ID_MASK);
+ reg1 = apic_read(APIC_ID);
+ apic_printk(APIC_DEBUG, "Getting ID: %x\n", reg1);
+ apic_write(APIC_ID, reg0);
+ if (reg1 != (reg0 ^ APIC_ID_MASK))
+ return 0;
/*
* The next two are just to see if we have sane values.
@@ -863,14 +931,15 @@
*/
if (modern_apic() || boot_cpu_data.x86_vendor == X86_VENDOR_AMD)
return;
+
/*
* Wait for idle.
*/
apic_wait_icr_idle();
apic_printk(APIC_DEBUG, "Synchronizing Arb IDs.\n");
- apic_write(APIC_ICR,
- APIC_DEST_ALLINC | APIC_INT_LEVELTRIG | APIC_DM_INIT);
+ apic_write(APIC_ICR, APIC_DEST_ALLINC |
+ APIC_INT_LEVELTRIG | APIC_DM_INIT);
}
/*
@@ -878,7 +947,7 @@
*/
void __init init_bsp_APIC(void)
{
- unsigned long value;
+ unsigned int value;
/*
* Don't do the setup now if we have a SMP BIOS as the
@@ -899,11 +968,13 @@
value &= ~APIC_VECTOR_MASK;
value |= APIC_SPIV_APIC_ENABLED;
+#ifdef CONFIG_X86_32
/* This bit is reserved on P4/Xeon and should be cleared */
if ((boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) &&
(boot_cpu_data.x86 == 15))
value &= ~APIC_SPIV_FOCUS_DISABLED;
else
+#endif
value |= APIC_SPIV_FOCUS_DISABLED;
value |= SPURIOUS_APIC_VECTOR;
apic_write(APIC_SPIV, value);
@@ -922,6 +993,16 @@
{
unsigned long oldvalue, value, maxlvt;
if (lapic_is_integrated() && !esr_disable) {
+ if (esr_disable) {
+ /*
+ * Something untraceable is creating bad interrupts on
+ * secondary quads ... for the moment, just leave the
+ * ESR disabled - we can't do anything useful with the
+ * errors anyway - mbligh
+ */
+ printk(KERN_INFO "Leaving ESR disabled.\n");
+ return;
+ }
/* !82489DX */
maxlvt = lapic_get_maxlvt();
if (maxlvt > 3) /* Due to the Pentium erratum 3AP. */
@@ -942,16 +1023,7 @@
"vector: 0x%08lx after: 0x%08lx\n",
oldvalue, value);
} else {
- if (esr_disable)
- /*
- * Something untraceable is creating bad interrupts on
- * secondary quads ... for the moment, just leave the
- * ESR disabled - we can't do anything useful with the
- * errors anyway - mbligh
- */
- printk(KERN_INFO "Leaving ESR disabled.\n");
- else
- printk(KERN_INFO "No ESR for 82489DX.\n");
+ printk(KERN_INFO "No ESR for 82489DX.\n");
}
}
@@ -1089,13 +1161,17 @@
void __cpuinit end_local_APIC_setup(void)
{
- unsigned long value;
-
lapic_setup_esr();
- /* Disable the local apic timer */
- value = apic_read(APIC_LVTT);
- value |= (APIC_LVT_MASKED | LOCAL_TIMER_VECTOR);
- apic_write(APIC_LVTT, value);
+
+#ifdef CONFIG_X86_32
+ {
+ unsigned int value;
+ /* Disable the local apic timer */
+ value = apic_read(APIC_LVTT);
+ value |= (APIC_LVT_MASKED | LOCAL_TIMER_VECTOR);
+ apic_write(APIC_LVTT, value);
+ }
+#endif
setup_apic_nmi_watchdog(NULL);
apic_pm_activate();
@@ -1205,7 +1281,7 @@
* default configuration (or the MP table is broken).
*/
if (boot_cpu_physical_apicid == -1U)
- boot_cpu_physical_apicid = GET_APIC_ID(read_apic_id());
+ boot_cpu_physical_apicid = read_apic_id();
}
@@ -1242,7 +1318,7 @@
* might be zero if read from MP tables. Get it from LAPIC.
*/
#ifdef CONFIG_CRASH_DUMP
- boot_cpu_physical_apicid = GET_APIC_ID(read_apic_id());
+ boot_cpu_physical_apicid = read_apic_id();
#endif
physid_set_mask_of_physid(boot_cpu_physical_apicid, &phys_cpu_present_map);
@@ -1321,59 +1397,12 @@
irq_exit();
}
-#ifdef CONFIG_SMP
-void __init smp_intr_init(void)
-{
- /*
- * IRQ0 must be given a fixed assignment and initialized,
- * because it's used before the IO-APIC is set up.
- */
- set_intr_gate(FIRST_DEVICE_VECTOR, interrupt[0]);
-
- /*
- * The reschedule interrupt is a CPU-to-CPU reschedule-helper
- * IPI, driven by wakeup.
- */
- alloc_intr_gate(RESCHEDULE_VECTOR, reschedule_interrupt);
-
- /* IPI for invalidation */
- alloc_intr_gate(INVALIDATE_TLB_VECTOR, invalidate_interrupt);
-
- /* IPI for generic function call */
- alloc_intr_gate(CALL_FUNCTION_VECTOR, call_function_interrupt);
-
- /* IPI for single call function */
- set_intr_gate(CALL_FUNCTION_SINGLE_VECTOR,
- call_function_single_interrupt);
-}
-#endif
-
-/*
- * Initialize APIC interrupts
- */
-void __init apic_intr_init(void)
-{
-#ifdef CONFIG_SMP
- smp_intr_init();
-#endif
- /* self generated IPI for local APIC timer */
- alloc_intr_gate(LOCAL_TIMER_VECTOR, apic_timer_interrupt);
-
- /* IPI vectors for APIC spurious and error interrupts */
- alloc_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt);
- alloc_intr_gate(ERROR_APIC_VECTOR, error_interrupt);
-
- /* thermal monitor LVT interrupt */
-#ifdef CONFIG_X86_MCE_P4THERMAL
- alloc_intr_gate(THERMAL_APIC_VECTOR, thermal_interrupt);
-#endif
-}
-
/**
* connect_bsp_APIC - attach the APIC to the interrupt system
*/
void __init connect_bsp_APIC(void)
{
+#ifdef CONFIG_X86_32
if (pic_mode) {
/*
* Do not trust the local APIC being empty at bootup.
@@ -1388,6 +1417,7 @@
outb(0x70, 0x22);
outb(0x01, 0x23);
}
+#endif
enable_apic_mode();
}
@@ -1400,6 +1430,9 @@
*/
void disconnect_bsp_APIC(int virt_wire_setup)
{
+ unsigned int value;
+
+#ifdef CONFIG_X86_32
if (pic_mode) {
/*
* Put the board back into PIC mode (has an effect only on
@@ -1411,54 +1444,53 @@
"entering PIC mode.\n");
outb(0x70, 0x22);
outb(0x00, 0x23);
- } else {
- /* Go back to Virtual Wire compatibility mode */
- unsigned long value;
+ return;
+ }
+#endif
- /* For the spurious interrupt use vector F, and enable it */
- value = apic_read(APIC_SPIV);
- value &= ~APIC_VECTOR_MASK;
- value |= APIC_SPIV_APIC_ENABLED;
- value |= 0xf;
- apic_write(APIC_SPIV, value);
+ /* Go back to Virtual Wire compatibility mode */
- if (!virt_wire_setup) {
- /*
- * For LVT0 make it edge triggered, active high,
- * external and enabled
- */
- value = apic_read(APIC_LVT0);
- value &= ~(APIC_MODE_MASK | APIC_SEND_PENDING |
- APIC_INPUT_POLARITY | APIC_LVT_REMOTE_IRR |
- APIC_LVT_LEVEL_TRIGGER | APIC_LVT_MASKED);
- value |= APIC_LVT_REMOTE_IRR | APIC_SEND_PENDING;
- value = SET_APIC_DELIVERY_MODE(value, APIC_MODE_EXTINT);
- apic_write(APIC_LVT0, value);
- } else {
- /* Disable LVT0 */
- apic_write(APIC_LVT0, APIC_LVT_MASKED);
- }
+ /* For the spurious interrupt use vector F, and enable it */
+ value = apic_read(APIC_SPIV);
+ value &= ~APIC_VECTOR_MASK;
+ value |= APIC_SPIV_APIC_ENABLED;
+ value |= 0xf;
+ apic_write(APIC_SPIV, value);
+ if (!virt_wire_setup) {
/*
- * For LVT1 make it edge triggered, active high, nmi and
- * enabled
+ * For LVT0 make it edge triggered, active high,
+ * external and enabled
*/
- value = apic_read(APIC_LVT1);
- value &= ~(
- APIC_MODE_MASK | APIC_SEND_PENDING |
+ value = apic_read(APIC_LVT0);
+ value &= ~(APIC_MODE_MASK | APIC_SEND_PENDING |
APIC_INPUT_POLARITY | APIC_LVT_REMOTE_IRR |
APIC_LVT_LEVEL_TRIGGER | APIC_LVT_MASKED);
value |= APIC_LVT_REMOTE_IRR | APIC_SEND_PENDING;
- value = SET_APIC_DELIVERY_MODE(value, APIC_MODE_NMI);
- apic_write(APIC_LVT1, value);
+ value = SET_APIC_DELIVERY_MODE(value, APIC_MODE_EXTINT);
+ apic_write(APIC_LVT0, value);
+ } else {
+ /* Disable LVT0 */
+ apic_write(APIC_LVT0, APIC_LVT_MASKED);
}
+
+ /*
+ * For LVT1 make it edge triggered, active high,
+ * nmi and enabled
+ */
+ value = apic_read(APIC_LVT1);
+ value &= ~(APIC_MODE_MASK | APIC_SEND_PENDING |
+ APIC_INPUT_POLARITY | APIC_LVT_REMOTE_IRR |
+ APIC_LVT_LEVEL_TRIGGER | APIC_LVT_MASKED);
+ value |= APIC_LVT_REMOTE_IRR | APIC_SEND_PENDING;
+ value = SET_APIC_DELIVERY_MODE(value, APIC_MODE_NMI);
+ apic_write(APIC_LVT1, value);
}
void __cpuinit generic_processor_info(int apicid, int version)
{
int cpu;
cpumask_t tmp_map;
- physid_mask_t phys_cpu;
/*
* Validate version
@@ -1471,9 +1503,6 @@
}
apic_version[apicid] = version;
- phys_cpu = apicid_to_cpu_present(apicid);
- physids_or(phys_cpu_present_map, phys_cpu_present_map, phys_cpu);
-
if (num_processors >= NR_CPUS) {
printk(KERN_WARNING "WARNING: NR_CPUS limit of %i reached."
" Processor ignored.\n", NR_CPUS);
@@ -1484,17 +1513,19 @@
cpus_complement(tmp_map, cpu_present_map);
cpu = first_cpu(tmp_map);
- if (apicid == boot_cpu_physical_apicid)
+ physid_set(apicid, phys_cpu_present_map);
+ if (apicid == boot_cpu_physical_apicid) {
/*
* x86_bios_cpu_apicid is required to have processors listed
* in same order as logical cpu numbers. Hence the first
* entry is BSP, and so on.
*/
cpu = 0;
-
+ }
if (apicid > max_physical_apicid)
max_physical_apicid = apicid;
+#ifdef CONFIG_X86_32
/*
* Would be preferable to switch to bigsmp when CONFIG_HOTPLUG_CPU=y
* but we need to work other dependencies like SMP_SUSPEND etc
@@ -1514,7 +1545,9 @@
def_to_bigsmp = 1;
}
}
-#ifdef CONFIG_SMP
+#endif
+
+#if defined(CONFIG_X86_SMP) || defined(CONFIG_X86_64)
/* are we being called early in kernel startup? */
if (early_per_cpu_ptr(x86_cpu_to_apicid)) {
u16 *cpu_to_apicid = early_per_cpu_ptr(x86_cpu_to_apicid);
@@ -1527,6 +1560,7 @@
per_cpu(x86_bios_cpu_apicid, cpu) = apicid;
}
#endif
+
cpu_set(cpu, cpu_possible_map);
cpu_set(cpu, cpu_present_map);
}
@@ -1537,6 +1571,11 @@
#ifdef CONFIG_PM
static struct {
+ /*
+ * 'active' is true if the local APIC was enabled by us and
+ * not the BIOS; this signifies that we are also responsible
+ * for disabling it before entering apm/acpi suspend
+ */
int active;
/* r/w apic fields */
unsigned int apic_id;
@@ -1577,7 +1616,7 @@
apic_pm_state.apic_lvterr = apic_read(APIC_LVTERR);
apic_pm_state.apic_tmict = apic_read(APIC_TMICT);
apic_pm_state.apic_tdcr = apic_read(APIC_TDCR);
-#ifdef CONFIG_X86_MCE_P4THERMAL
+#if defined(CONFIG_X86_MCE_P4THERMAL) || defined(CONFIG_X86_MCE_INTEL)
if (maxlvt >= 5)
apic_pm_state.apic_thmr = apic_read(APIC_LVTTHMR);
#endif
@@ -1601,16 +1640,23 @@
local_irq_save(flags);
- /*
- * Make sure the APICBASE points to the right address
- *
- * FIXME! This will be wrong if we ever support suspend on
- * SMP! We'll need to do this as part of the CPU restore!
- */
- rdmsr(MSR_IA32_APICBASE, l, h);
- l &= ~MSR_IA32_APICBASE_BASE;
- l |= MSR_IA32_APICBASE_ENABLE | mp_lapic_addr;
- wrmsr(MSR_IA32_APICBASE, l, h);
+#ifdef CONFIG_X86_64
+ if (x2apic)
+ enable_x2apic();
+ else
+#endif
+ {
+ /*
+ * Make sure the APICBASE points to the right address
+ *
+ * FIXME! This will be wrong if we ever support suspend on
+ * SMP! We'll need to do this as part of the CPU restore!
+ */
+ rdmsr(MSR_IA32_APICBASE, l, h);
+ l &= ~MSR_IA32_APICBASE_BASE;
+ l |= MSR_IA32_APICBASE_ENABLE | mp_lapic_addr;
+ wrmsr(MSR_IA32_APICBASE, l, h);
+ }
apic_write(APIC_LVTERR, ERROR_APIC_VECTOR | APIC_LVT_MASKED);
apic_write(APIC_ID, apic_pm_state.apic_id);
@@ -1620,7 +1666,7 @@
apic_write(APIC_SPIV, apic_pm_state.apic_spiv);
apic_write(APIC_LVT0, apic_pm_state.apic_lvt0);
apic_write(APIC_LVT1, apic_pm_state.apic_lvt1);
-#ifdef CONFIG_X86_MCE_P4THERMAL
+#if defined(CONFIG_X86_MCE_P4THERMAL) || defined(CONFIG_X86_MCE_INTEL)
if (maxlvt >= 5)
apic_write(APIC_LVTTHMR, apic_pm_state.apic_thmr);
#endif
@@ -1634,7 +1680,9 @@
apic_write(APIC_LVTERR, apic_pm_state.apic_lvterr);
apic_write(APIC_ESR, 0);
apic_read(APIC_ESR);
+
local_irq_restore(flags);
+
return 0;
}
@@ -1690,20 +1738,20 @@
}
early_param("lapic", parse_lapic);
-static int __init parse_nolapic(char *arg)
+static int __init setup_disableapic(char *arg)
{
disable_apic = 1;
setup_clear_cpu_cap(X86_FEATURE_APIC);
return 0;
}
-early_param("nolapic", parse_nolapic);
+early_param("disableapic", setup_disableapic);
-static int __init parse_disable_lapic_timer(char *arg)
+/* same as disableapic, for compatibility */
+static int __init setup_nolapic(char *arg)
{
- local_apic_timer_disabled = 1;
- return 0;
+ return setup_disableapic(arg);
}
-early_param("nolapic_timer", parse_disable_lapic_timer);
+early_param("nolapic", setup_nolapic);
static int __init parse_lapic_timer_c2_ok(char *arg)
{
@@ -1712,15 +1760,40 @@
}
early_param("lapic_timer_c2_ok", parse_lapic_timer_c2_ok);
+static int __init parse_disable_apic_timer(char *arg)
+{
+ disable_apic_timer = 1;
+ return 0;
+}
+early_param("noapictimer", parse_disable_apic_timer);
+
+static int __init parse_nolapic_timer(char *arg)
+{
+ disable_apic_timer = 1;
+ return 0;
+}
+early_param("nolapic_timer", parse_nolapic_timer);
+
static int __init apic_set_verbosity(char *arg)
{
- if (!arg)
+ if (!arg) {
+#ifdef CONFIG_X86_64
+ skip_ioapic_setup = 0;
+ ioapic_force = 1;
+ return 0;
+#endif
return -EINVAL;
+ }
- if (strcmp(arg, "debug") == 0)
+ if (strcmp("debug", arg) == 0)
apic_verbosity = APIC_DEBUG;
- else if (strcmp(arg, "verbose") == 0)
+ else if (strcmp("verbose", arg) == 0)
apic_verbosity = APIC_VERBOSE;
+ else {
+ printk(KERN_WARNING "APIC Verbosity level %s not recognised"
+ " use apic=verbose or apic=debug\n", arg);
+ return -EINVAL;
+ }
return 0;
}
diff --git a/arch/x86/kernel/apic_64.c b/arch/x86/kernel/apic_64.c
index 446c062..94ddb69 100644
--- a/arch/x86/kernel/apic_64.c
+++ b/arch/x86/kernel/apic_64.c
@@ -27,6 +27,7 @@
#include <linux/clockchips.h>
#include <linux/acpi_pmtmr.h>
#include <linux/module.h>
+#include <linux/dmar.h>
#include <asm/atomic.h>
#include <asm/smp.h>
@@ -39,13 +40,20 @@
#include <asm/proto.h>
#include <asm/timex.h>
#include <asm/apic.h>
+#include <asm/i8259.h>
#include <mach_ipi.h>
#include <mach_apic.h>
+/* Disable local APIC timer from the kernel commandline or via dmi quirk */
static int disable_apic_timer __cpuinitdata;
static int apic_calibrate_pmtmr __initdata;
int disable_apic;
+int disable_x2apic;
+int x2apic;
+
+/* x2apic enabled before OS handover */
+int x2apic_preenabled;
/* Local APIC timer works in C2 */
int local_apic_timer_c2_ok;
@@ -73,6 +81,9 @@
static void lapic_timer_broadcast(cpumask_t mask);
static void apic_pm_activate(void);
+/*
+ * The local apic timer can be used for any function which is CPU local.
+ */
static struct clock_event_device lapic_clockevent = {
.name = "lapic",
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT
@@ -99,11 +110,15 @@
}
/*
- * Check, if the APIC is integrated or a seperate chip
+ * Check, if the APIC is integrated or a separate chip
*/
static inline int lapic_is_integrated(void)
{
+#ifdef CONFIG_X86_64
return 1;
+#else
+ return APIC_INTEGRATED(lapic_get_version());
+#endif
}
/*
@@ -118,13 +133,18 @@
return lapic_get_version() >= 0x14;
}
-void apic_wait_icr_idle(void)
+/*
+ * Paravirt kernels also might be using these below ops. So we still
+ * use generic apic_read()/apic_write(), which might be pointing to different
+ * ops in PARAVIRT case.
+ */
+void xapic_wait_icr_idle(void)
{
while (apic_read(APIC_ICR) & APIC_ICR_BUSY)
cpu_relax();
}
-u32 safe_apic_wait_icr_idle(void)
+u32 safe_xapic_wait_icr_idle(void)
{
u32 send_status;
int timeout;
@@ -140,6 +160,68 @@
return send_status;
}
+void xapic_icr_write(u32 low, u32 id)
+{
+ apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(id));
+ apic_write(APIC_ICR, low);
+}
+
+u64 xapic_icr_read(void)
+{
+ u32 icr1, icr2;
+
+ icr2 = apic_read(APIC_ICR2);
+ icr1 = apic_read(APIC_ICR);
+
+ return icr1 | ((u64)icr2 << 32);
+}
+
+static struct apic_ops xapic_ops = {
+ .read = native_apic_mem_read,
+ .write = native_apic_mem_write,
+ .icr_read = xapic_icr_read,
+ .icr_write = xapic_icr_write,
+ .wait_icr_idle = xapic_wait_icr_idle,
+ .safe_wait_icr_idle = safe_xapic_wait_icr_idle,
+};
+
+struct apic_ops __read_mostly *apic_ops = &xapic_ops;
+EXPORT_SYMBOL_GPL(apic_ops);
+
+static void x2apic_wait_icr_idle(void)
+{
+ /* no need to wait for icr idle in x2apic */
+ return;
+}
+
+static u32 safe_x2apic_wait_icr_idle(void)
+{
+ /* no need to wait for icr idle in x2apic */
+ return 0;
+}
+
+void x2apic_icr_write(u32 low, u32 id)
+{
+ wrmsrl(APIC_BASE_MSR + (APIC_ICR >> 4), ((__u64) id) << 32 | low);
+}
+
+u64 x2apic_icr_read(void)
+{
+ unsigned long val;
+
+ rdmsrl(APIC_BASE_MSR + (APIC_ICR >> 4), val);
+ return val;
+}
+
+static struct apic_ops x2apic_ops = {
+ .read = native_apic_msr_read,
+ .write = native_apic_msr_write,
+ .icr_read = x2apic_icr_read,
+ .icr_write = x2apic_icr_write,
+ .wait_icr_idle = x2apic_wait_icr_idle,
+ .safe_wait_icr_idle = safe_x2apic_wait_icr_idle,
+};
+
/**
* enable_NMI_through_LVT0 - enable NMI through local vector table 0
*/
@@ -149,6 +231,11 @@
/* unmask and set to NMI */
v = APIC_DM_NMI;
+
+ /* Level triggered for 82489DX (32bit mode) */
+ if (!lapic_is_integrated())
+ v |= APIC_LVT_LEVEL_TRIGGER;
+
apic_write(APIC_LVT0, v);
}
@@ -157,14 +244,28 @@
*/
int lapic_get_maxlvt(void)
{
- unsigned int v, maxlvt;
+ unsigned int v;
v = apic_read(APIC_LVR);
- maxlvt = GET_APIC_MAXLVT(v);
- return maxlvt;
+ /*
+ * - we always have APIC integrated on 64bit mode
+ * - 82489DXs do not report # of LVT entries
+ */
+ return APIC_INTEGRATED(GET_APIC_VERSION(v)) ? GET_APIC_MAXLVT(v) : 2;
}
/*
+ * Local APIC timer
+ */
+
+/* Clock divisor */
+#ifdef CONFG_X86_64
+#define APIC_DIVISOR 1
+#else
+#define APIC_DIVISOR 16
+#endif
+
+/*
* This function sets up the local APIC timer, with a timeout of
* 'clocks' APIC bus clock. During calibration we actually call
* this function twice on the boot CPU, once with a bogus timeout
@@ -174,7 +275,6 @@
* We do reads before writes even if unnecessary, to get around the
* P5 APIC double write bug.
*/
-
static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen)
{
unsigned int lvtt_value, tmp_value;
@@ -182,6 +282,9 @@
lvtt_value = LOCAL_TIMER_VECTOR;
if (!oneshot)
lvtt_value |= APIC_LVT_TIMER_PERIODIC;
+ if (!lapic_is_integrated())
+ lvtt_value |= SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV);
+
if (!irqen)
lvtt_value |= APIC_LVT_MASKED;
@@ -191,12 +294,12 @@
* Divide PICLK by 16
*/
tmp_value = apic_read(APIC_TDCR);
- apic_write(APIC_TDCR, (tmp_value
- & ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE))
- | APIC_TDR_DIV_16);
+ apic_write(APIC_TDCR,
+ (tmp_value & ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE)) |
+ APIC_TDR_DIV_16);
if (!oneshot)
- apic_write(APIC_TMICT, clocks);
+ apic_write(APIC_TMICT, clocks / APIC_DIVISOR);
}
/*
@@ -204,6 +307,9 @@
*
* Vector mappings are hard coded. On K8 only offset 0 (APIC500) and
* MCE interrupts are supported. Thus MCE offset must be set to 0.
+ *
+ * If mask=1, the LVT entry does not generate interrupts while mask=0
+ * enables the vector. See also the BKDGs.
*/
#define APIC_EILVT_LVTOFF_MCE 0
@@ -228,6 +334,7 @@
setup_APIC_eilvt(APIC_EILVT_LVTOFF_IBS, vector, msg_type, mask);
return APIC_EILVT_LVTOFF_IBS;
}
+EXPORT_SYMBOL_GPL(setup_APIC_eilvt_ibs);
/*
* Program the next event, relative to now
@@ -366,7 +473,7 @@
lapic_clockevent.min_delta_ns =
clockevent_delta2ns(0xF, &lapic_clockevent);
- calibration_result = result / HZ;
+ calibration_result = (result * APIC_DIVISOR) / HZ;
/*
* Do a sanity check on the APIC calibration result
@@ -388,10 +495,10 @@
void __init setup_boot_APIC_clock(void)
{
/*
- * The local apic timer can be disabled via the kernel commandline.
- * Register the lapic timer as a dummy clock event source on SMP
- * systems, so the broadcast mechanism is used. On UP systems simply
- * ignore it.
+ * The local apic timer can be disabled via the kernel
+ * commandline or from the CPU detection code. Register the lapic
+ * timer as a dummy clock event source on SMP systems, so the
+ * broadcast mechanism is used. On UP systems simply ignore it.
*/
if (disable_apic_timer) {
printk(KERN_INFO "Disabling APIC timer\n");
@@ -403,7 +510,9 @@
return;
}
- printk(KERN_INFO "Using local APIC timer interrupts.\n");
+ 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)
@@ -422,6 +531,7 @@
printk(KERN_WARNING "APIC timer registered as dummy,"
" due to nmi_watchdog=%d!\n", nmi_watchdog);
+ /* Setup the lapic or request the broadcast */
setup_APIC_timer();
}
@@ -460,7 +570,11 @@
/*
* the NMI deadlock-detector uses this.
*/
+#ifdef CONFIG_X86_64
add_pda(apic_timer_irqs, 1);
+#else
+ per_cpu(irq_stat, cpu).apic_timer_irqs++;
+#endif
evt->event_handler(evt);
}
@@ -491,6 +605,7 @@
irq_enter();
local_apic_timer_interrupt();
irq_exit();
+
set_irq_regs(old_regs);
}
@@ -544,6 +659,13 @@
apic_write(APIC_LVTPC, v | APIC_LVT_MASKED);
}
+ /* lets not touch this if we didn't frob it */
+#if defined(CONFIG_X86_MCE_P4THERMAL) || defined(X86_MCE_INTEL)
+ if (maxlvt >= 5) {
+ v = apic_read(APIC_LVTTHMR);
+ apic_write(APIC_LVTTHMR, v | APIC_LVT_MASKED);
+ }
+#endif
/*
* Clean APIC state for other OSs:
*/
@@ -554,8 +676,14 @@
apic_write(APIC_LVTERR, APIC_LVT_MASKED);
if (maxlvt >= 4)
apic_write(APIC_LVTPC, APIC_LVT_MASKED);
- apic_write(APIC_ESR, 0);
- apic_read(APIC_ESR);
+
+ /* Integrated APIC (!82489DX) ? */
+ if (lapic_is_integrated()) {
+ if (maxlvt > 3)
+ /* Clear ESR due to Pentium errata 3AP and 11AP */
+ apic_write(APIC_ESR, 0);
+ apic_read(APIC_ESR);
+ }
}
/**
@@ -574,8 +702,28 @@
value = apic_read(APIC_SPIV);
value &= ~APIC_SPIV_APIC_ENABLED;
apic_write(APIC_SPIV, value);
+
+#ifdef CONFIG_X86_32
+ /*
+ * When LAPIC was disabled by the BIOS and enabled by the kernel,
+ * restore the disabled state.
+ */
+ if (enabled_via_apicbase) {
+ unsigned int l, h;
+
+ rdmsr(MSR_IA32_APICBASE, l, h);
+ l &= ~MSR_IA32_APICBASE_ENABLE;
+ wrmsr(MSR_IA32_APICBASE, l, h);
+ }
+#endif
}
+/*
+ * If Linux enabled the LAPIC against the BIOS default disable it down before
+ * re-entering the BIOS on shutdown. Otherwise the BIOS may get confused and
+ * not power-off. Additionally clear all LVT entries before disable_local_APIC
+ * for the case where Linux didn't enable the LAPIC.
+ */
void lapic_shutdown(void)
{
unsigned long flags;
@@ -585,7 +733,13 @@
local_irq_save(flags);
- disable_local_APIC();
+#ifdef CONFIG_X86_32
+ if (!enabled_via_apicbase)
+ clear_local_APIC();
+ else
+#endif
+ disable_local_APIC();
+
local_irq_restore(flags);
}
@@ -629,10 +783,10 @@
/*
* The ID register is read/write in a real APIC.
*/
- reg0 = read_apic_id();
+ reg0 = apic_read(APIC_ID);
apic_printk(APIC_DEBUG, "Getting ID: %x\n", reg0);
apic_write(APIC_ID, reg0 ^ APIC_ID_MASK);
- reg1 = read_apic_id();
+ reg1 = apic_read(APIC_ID);
apic_printk(APIC_DEBUG, "Getting ID: %x\n", reg1);
apic_write(APIC_ID, reg0);
if (reg1 != (reg0 ^ APIC_ID_MASK))
@@ -656,8 +810,11 @@
*/
void __init sync_Arb_IDs(void)
{
- /* Unsupported on P4 - see Intel Dev. Manual Vol. 3, Ch. 8.6.1 */
- if (modern_apic())
+ /*
+ * Unsupported on P4 - see Intel Dev. Manual Vol. 3, Ch. 8.6.1 And not
+ * needed on AMD.
+ */
+ if (modern_apic() || boot_cpu_data.x86_vendor == X86_VENDOR_AMD)
return;
/*
@@ -666,8 +823,8 @@
apic_wait_icr_idle();
apic_printk(APIC_DEBUG, "Synchronizing Arb IDs.\n");
- apic_write(APIC_ICR, APIC_DEST_ALLINC | APIC_INT_LEVELTRIG
- | APIC_DM_INIT);
+ apic_write(APIC_ICR, APIC_DEST_ALLINC |
+ APIC_INT_LEVELTRIG | APIC_DM_INIT);
}
/*
@@ -684,8 +841,6 @@
if (smp_found_config || !cpu_has_apic)
return;
- value = apic_read(APIC_LVR);
-
/*
* Do not trust the local APIC being empty at bootup.
*/
@@ -697,7 +852,15 @@
value = apic_read(APIC_SPIV);
value &= ~APIC_VECTOR_MASK;
value |= APIC_SPIV_APIC_ENABLED;
- value |= APIC_SPIV_FOCUS_DISABLED;
+
+#ifdef CONFIG_X86_32
+ /* This bit is reserved on P4/Xeon and should be cleared */
+ if ((boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) &&
+ (boot_cpu_data.x86 == 15))
+ value &= ~APIC_SPIV_FOCUS_DISABLED;
+ else
+#endif
+ value |= APIC_SPIV_FOCUS_DISABLED;
value |= SPURIOUS_APIC_VECTOR;
apic_write(APIC_SPIV, value);
@@ -706,9 +869,50 @@
*/
apic_write(APIC_LVT0, APIC_DM_EXTINT);
value = APIC_DM_NMI;
+ if (!lapic_is_integrated()) /* 82489DX */
+ value |= APIC_LVT_LEVEL_TRIGGER;
apic_write(APIC_LVT1, value);
}
+static void __cpuinit lapic_setup_esr(void)
+{
+ unsigned long oldvalue, value, maxlvt;
+ if (lapic_is_integrated() && !esr_disable) {
+ if (esr_disable) {
+ /*
+ * Something untraceable is creating bad interrupts on
+ * secondary quads ... for the moment, just leave the
+ * ESR disabled - we can't do anything useful with the
+ * errors anyway - mbligh
+ */
+ printk(KERN_INFO "Leaving ESR disabled.\n");
+ return;
+ }
+ /* !82489DX */
+ maxlvt = lapic_get_maxlvt();
+ if (maxlvt > 3) /* Due to the Pentium erratum 3AP. */
+ apic_write(APIC_ESR, 0);
+ oldvalue = apic_read(APIC_ESR);
+
+ /* enables sending errors */
+ value = ERROR_APIC_VECTOR;
+ apic_write(APIC_LVTERR, value);
+ /*
+ * spec says clear errors after enabling vector.
+ */
+ if (maxlvt > 3)
+ apic_write(APIC_ESR, 0);
+ value = apic_read(APIC_ESR);
+ if (value != oldvalue)
+ apic_printk(APIC_VERBOSE, "ESR value before enabling "
+ "vector: 0x%08lx after: 0x%08lx\n",
+ oldvalue, value);
+ } else {
+ printk(KERN_INFO "No ESR for 82489DX.\n");
+ }
+}
+
+
/**
* setup_local_APIC - setup the local APIC
*/
@@ -814,25 +1018,143 @@
preempt_enable();
}
-static void __cpuinit lapic_setup_esr(void)
-{
- unsigned maxlvt = lapic_get_maxlvt();
-
- apic_write(APIC_LVTERR, ERROR_APIC_VECTOR);
- /*
- * spec says clear errors after enabling vector.
- */
- if (maxlvt > 3)
- apic_write(APIC_ESR, 0);
-}
-
void __cpuinit end_local_APIC_setup(void)
{
lapic_setup_esr();
+
+#ifdef CONFIG_X86_32
+ {
+ unsigned int value;
+ /* Disable the local apic timer */
+ value = apic_read(APIC_LVTT);
+ value |= (APIC_LVT_MASKED | LOCAL_TIMER_VECTOR);
+ apic_write(APIC_LVTT, value);
+ }
+#endif
+
setup_apic_nmi_watchdog(NULL);
apic_pm_activate();
}
+void check_x2apic(void)
+{
+ int msr, msr2;
+
+ rdmsr(MSR_IA32_APICBASE, msr, msr2);
+
+ if (msr & X2APIC_ENABLE) {
+ printk("x2apic enabled by BIOS, switching to x2apic ops\n");
+ x2apic_preenabled = x2apic = 1;
+ apic_ops = &x2apic_ops;
+ }
+}
+
+void enable_x2apic(void)
+{
+ int msr, msr2;
+
+ rdmsr(MSR_IA32_APICBASE, msr, msr2);
+ if (!(msr & X2APIC_ENABLE)) {
+ printk("Enabling x2apic\n");
+ wrmsr(MSR_IA32_APICBASE, msr | X2APIC_ENABLE, 0);
+ }
+}
+
+void enable_IR_x2apic(void)
+{
+#ifdef CONFIG_INTR_REMAP
+ int ret;
+ unsigned long flags;
+
+ if (!cpu_has_x2apic)
+ return;
+
+ if (!x2apic_preenabled && disable_x2apic) {
+ printk(KERN_INFO
+ "Skipped enabling x2apic and Interrupt-remapping "
+ "because of nox2apic\n");
+ return;
+ }
+
+ if (x2apic_preenabled && disable_x2apic)
+ panic("Bios already enabled x2apic, can't enforce nox2apic");
+
+ if (!x2apic_preenabled && skip_ioapic_setup) {
+ printk(KERN_INFO
+ "Skipped enabling x2apic and Interrupt-remapping "
+ "because of skipping io-apic setup\n");
+ return;
+ }
+
+ ret = dmar_table_init();
+ if (ret) {
+ printk(KERN_INFO
+ "dmar_table_init() failed with %d:\n", ret);
+
+ if (x2apic_preenabled)
+ panic("x2apic enabled by bios. But IR enabling failed");
+ else
+ printk(KERN_INFO
+ "Not enabling x2apic,Intr-remapping\n");
+ return;
+ }
+
+ local_irq_save(flags);
+ mask_8259A();
+ save_mask_IO_APIC_setup();
+
+ ret = enable_intr_remapping(1);
+
+ if (ret && x2apic_preenabled) {
+ local_irq_restore(flags);
+ panic("x2apic enabled by bios. But IR enabling failed");
+ }
+
+ if (ret)
+ goto end;
+
+ if (!x2apic) {
+ x2apic = 1;
+ apic_ops = &x2apic_ops;
+ enable_x2apic();
+ }
+end:
+ if (ret)
+ /*
+ * IR enabling failed
+ */
+ restore_IO_APIC_setup();
+ else
+ reinit_intr_remapped_IO_APIC(x2apic_preenabled);
+
+ unmask_8259A();
+ local_irq_restore(flags);
+
+ if (!ret) {
+ if (!x2apic_preenabled)
+ printk(KERN_INFO
+ "Enabled x2apic and interrupt-remapping\n");
+ else
+ printk(KERN_INFO
+ "Enabled Interrupt-remapping\n");
+ } else
+ printk(KERN_ERR
+ "Failed to enable Interrupt-remapping and x2apic\n");
+#else
+ if (!cpu_has_x2apic)
+ return;
+
+ if (x2apic_preenabled)
+ panic("x2apic enabled prior OS handover,"
+ " enable CONFIG_INTR_REMAP");
+
+ printk(KERN_INFO "Enable CONFIG_INTR_REMAP for enabling intr-remapping "
+ " and x2apic\n");
+#endif
+
+ return;
+}
+
/*
* Detect and enable local APICs on non-SMP boards.
* Original code written by Keir Fraser.
@@ -872,7 +1194,7 @@
* Fetch the APIC ID of the BSP in case we have a
* default configuration (or the MP table is broken).
*/
- boot_cpu_physical_apicid = GET_APIC_ID(read_apic_id());
+ boot_cpu_physical_apicid = read_apic_id();
}
/**
@@ -880,6 +1202,11 @@
*/
void __init init_apic_mappings(void)
{
+ if (x2apic) {
+ boot_cpu_physical_apicid = read_apic_id();
+ return;
+ }
+
/*
* If no local APIC can be found then set up a fake all
* zeroes page to simulate the local APIC and another
@@ -899,13 +1226,15 @@
* Fetch the APIC ID of the BSP in case we have a
* default configuration (or the MP table is broken).
*/
- boot_cpu_physical_apicid = GET_APIC_ID(read_apic_id());
+ boot_cpu_physical_apicid = read_apic_id();
}
/*
* This initializes the IO-APIC and APIC hardware if this is
* a UP kernel.
*/
+int apic_version[MAX_APICS];
+
int __init APIC_init_uniprocessor(void)
{
if (disable_apic) {
@@ -918,6 +1247,9 @@
return -1;
}
+ enable_IR_x2apic();
+ setup_apic_routing();
+
verify_local_APIC();
connect_bsp_APIC();
@@ -1004,17 +1336,57 @@
}
/**
- * * connect_bsp_APIC - attach the APIC to the interrupt system
- * */
+ * connect_bsp_APIC - attach the APIC to the interrupt system
+ */
void __init connect_bsp_APIC(void)
{
+#ifdef CONFIG_X86_32
+ if (pic_mode) {
+ /*
+ * Do not trust the local APIC being empty at bootup.
+ */
+ clear_local_APIC();
+ /*
+ * PIC mode, enable APIC mode in the IMCR, i.e. connect BSP's
+ * local APIC to INT and NMI lines.
+ */
+ apic_printk(APIC_VERBOSE, "leaving PIC mode, "
+ "enabling APIC mode.\n");
+ outb(0x70, 0x22);
+ outb(0x01, 0x23);
+ }
+#endif
enable_apic_mode();
}
+/**
+ * disconnect_bsp_APIC - detach the APIC from the interrupt system
+ * @virt_wire_setup: indicates, whether virtual wire mode is selected
+ *
+ * Virtual wire mode is necessary to deliver legacy interrupts even when the
+ * APIC is disabled.
+ */
void disconnect_bsp_APIC(int virt_wire_setup)
{
+ unsigned int value;
+
+#ifdef CONFIG_X86_32
+ if (pic_mode) {
+ /*
+ * Put the board back into PIC mode (has an effect only on
+ * certain older boards). Note that APIC interrupts, including
+ * IPIs, won't work beyond this point! The only exception are
+ * INIT IPIs.
+ */
+ apic_printk(APIC_VERBOSE, "disabling APIC mode, "
+ "entering PIC mode.\n");
+ outb(0x70, 0x22);
+ outb(0x00, 0x23);
+ return;
+ }
+#endif
+
/* Go back to Virtual Wire compatibility mode */
- unsigned long value;
/* For the spurious interrupt use vector F, and enable it */
value = apic_read(APIC_SPIV);
@@ -1040,7 +1412,10 @@
apic_write(APIC_LVT0, APIC_LVT_MASKED);
}
- /* For LVT1 make it edge triggered, active high, nmi and enabled */
+ /*
+ * For LVT1 make it edge triggered, active high,
+ * nmi and enabled
+ */
value = apic_read(APIC_LVT1);
value &= ~(APIC_MODE_MASK | APIC_SEND_PENDING |
APIC_INPUT_POLARITY | APIC_LVT_REMOTE_IRR |
@@ -1055,9 +1430,20 @@
int cpu;
cpumask_t tmp_map;
+ /*
+ * Validate version
+ */
+ if (version == 0x0) {
+ printk(KERN_WARNING "BIOS bug, APIC version is 0 for CPU#%d! "
+ "fixing up to 0x10. (tell your hw vendor)\n",
+ version);
+ version = 0x10;
+ }
+ apic_version[apicid] = version;
+
if (num_processors >= NR_CPUS) {
printk(KERN_WARNING "WARNING: NR_CPUS limit of %i reached."
- " Processor ignored.\n", NR_CPUS);
+ " Processor ignored.\n", NR_CPUS);
return;
}
@@ -1077,6 +1463,29 @@
if (apicid > max_physical_apicid)
max_physical_apicid = apicid;
+#ifdef CONFIG_X86_32
+ /*
+ * Would be preferable to switch to bigsmp when CONFIG_HOTPLUG_CPU=y
+ * but we need to work other dependencies like SMP_SUSPEND etc
+ * before this can be done without some confusion.
+ * if (CPU_HOTPLUG_ENABLED || num_processors > 8)
+ * - Ashok Raj <ashok.raj@intel.com>
+ */
+ if (max_physical_apicid >= 8) {
+ switch (boot_cpu_data.x86_vendor) {
+ case X86_VENDOR_INTEL:
+ if (!APIC_XAPIC(version)) {
+ def_to_bigsmp = 0;
+ break;
+ }
+ /* If P4 and above fall through */
+ case X86_VENDOR_AMD:
+ def_to_bigsmp = 1;
+ }
+ }
+#endif
+
+#if defined(CONFIG_X86_SMP) || defined(CONFIG_X86_64)
/* are we being called early in kernel startup? */
if (early_per_cpu_ptr(x86_cpu_to_apicid)) {
u16 *cpu_to_apicid = early_per_cpu_ptr(x86_cpu_to_apicid);
@@ -1088,20 +1497,28 @@
per_cpu(x86_cpu_to_apicid, cpu) = apicid;
per_cpu(x86_bios_cpu_apicid, cpu) = apicid;
}
+#endif
cpu_set(cpu, cpu_possible_map);
cpu_set(cpu, cpu_present_map);
}
+int hard_smp_processor_id(void)
+{
+ return read_apic_id();
+}
+
/*
* Power management
*/
#ifdef CONFIG_PM
static struct {
- /* 'active' is true if the local APIC was enabled by us and
- not the BIOS; this signifies that we are also responsible
- for disabling it before entering apm/acpi suspend */
+ /*
+ * 'active' is true if the local APIC was enabled by us and
+ * not the BIOS; this signifies that we are also responsible
+ * for disabling it before entering apm/acpi suspend
+ */
int active;
/* r/w apic fields */
unsigned int apic_id;
@@ -1129,7 +1546,7 @@
maxlvt = lapic_get_maxlvt();
- apic_pm_state.apic_id = read_apic_id();
+ apic_pm_state.apic_id = apic_read(APIC_ID);
apic_pm_state.apic_taskpri = apic_read(APIC_TASKPRI);
apic_pm_state.apic_ldr = apic_read(APIC_LDR);
apic_pm_state.apic_dfr = apic_read(APIC_DFR);
@@ -1142,10 +1559,11 @@
apic_pm_state.apic_lvterr = apic_read(APIC_LVTERR);
apic_pm_state.apic_tmict = apic_read(APIC_TMICT);
apic_pm_state.apic_tdcr = apic_read(APIC_TDCR);
-#ifdef CONFIG_X86_MCE_INTEL
+#if defined(CONFIG_X86_MCE_P4THERMAL) || defined(CONFIG_X86_MCE_INTEL)
if (maxlvt >= 5)
apic_pm_state.apic_thmr = apic_read(APIC_LVTTHMR);
#endif
+
local_irq_save(flags);
disable_local_APIC();
local_irq_restore(flags);
@@ -1164,10 +1582,25 @@
maxlvt = lapic_get_maxlvt();
local_irq_save(flags);
- rdmsr(MSR_IA32_APICBASE, l, h);
- l &= ~MSR_IA32_APICBASE_BASE;
- l |= MSR_IA32_APICBASE_ENABLE | mp_lapic_addr;
- wrmsr(MSR_IA32_APICBASE, l, h);
+
+#ifdef CONFIG_X86_64
+ if (x2apic)
+ enable_x2apic();
+ else
+#endif
+ {
+ /*
+ * Make sure the APICBASE points to the right address
+ *
+ * FIXME! This will be wrong if we ever support suspend on
+ * SMP! We'll need to do this as part of the CPU restore!
+ */
+ rdmsr(MSR_IA32_APICBASE, l, h);
+ l &= ~MSR_IA32_APICBASE_BASE;
+ l |= MSR_IA32_APICBASE_ENABLE | mp_lapic_addr;
+ wrmsr(MSR_IA32_APICBASE, l, h);
+ }
+
apic_write(APIC_LVTERR, ERROR_APIC_VECTOR | APIC_LVT_MASKED);
apic_write(APIC_ID, apic_pm_state.apic_id);
apic_write(APIC_DFR, apic_pm_state.apic_dfr);
@@ -1176,7 +1609,7 @@
apic_write(APIC_SPIV, apic_pm_state.apic_spiv);
apic_write(APIC_LVT0, apic_pm_state.apic_lvt0);
apic_write(APIC_LVT1, apic_pm_state.apic_lvt1);
-#ifdef CONFIG_X86_MCE_INTEL
+#if defined(CONFIG_X86_MCE_P4THERMAL) || defined(CONFIG_X86_MCE_INTEL)
if (maxlvt >= 5)
apic_write(APIC_LVTTHMR, apic_pm_state.apic_thmr);
#endif
@@ -1190,10 +1623,17 @@
apic_write(APIC_LVTERR, apic_pm_state.apic_lvterr);
apic_write(APIC_ESR, 0);
apic_read(APIC_ESR);
+
local_irq_restore(flags);
+
return 0;
}
+/*
+ * This device has no shutdown method - fully functioning local APICs
+ * are needed on every CPU up until machine_halt/restart/poweroff.
+ */
+
static struct sysdev_class lapic_sysclass = {
.name = "lapic",
.resume = lapic_resume,
@@ -1307,31 +1747,19 @@
return (clusters > 2);
}
+static __init int setup_nox2apic(char *str)
+{
+ disable_x2apic = 1;
+ clear_cpu_cap(&boot_cpu_data, X86_FEATURE_X2APIC);
+ return 0;
+}
+early_param("nox2apic", setup_nox2apic);
+
+
/*
* APIC command line parameters
*/
-static int __init apic_set_verbosity(char *str)
-{
- if (str == NULL) {
- skip_ioapic_setup = 0;
- ioapic_force = 1;
- return 0;
- }
- if (strcmp("debug", str) == 0)
- apic_verbosity = APIC_DEBUG;
- else if (strcmp("verbose", str) == 0)
- apic_verbosity = APIC_VERBOSE;
- else {
- printk(KERN_WARNING "APIC Verbosity level %s not recognised"
- " use apic=verbose or apic=debug\n", str);
- return -EINVAL;
- }
-
- return 0;
-}
-early_param("apic", apic_set_verbosity);
-
-static __init int setup_disableapic(char *str)
+static int __init setup_disableapic(char *arg)
{
disable_apic = 1;
setup_clear_cpu_cap(X86_FEATURE_APIC);
@@ -1340,9 +1768,9 @@
early_param("disableapic", setup_disableapic);
/* same as disableapic, for compatibility */
-static __init int setup_nolapic(char *str)
+static int __init setup_nolapic(char *arg)
{
- return setup_disableapic(str);
+ return setup_disableapic(arg);
}
early_param("nolapic", setup_nolapic);
@@ -1353,14 +1781,19 @@
}
early_param("lapic_timer_c2_ok", parse_lapic_timer_c2_ok);
-static __init int setup_noapictimer(char *str)
+static int __init parse_disable_apic_timer(char *arg)
{
- if (str[0] != ' ' && str[0] != 0)
- return 0;
disable_apic_timer = 1;
- return 1;
+ return 0;
}
-__setup("noapictimer", setup_noapictimer);
+early_param("noapictimer", parse_disable_apic_timer);
+
+static int __init parse_nolapic_timer(char *arg)
+{
+ disable_apic_timer = 1;
+ return 0;
+}
+early_param("nolapic_timer", parse_nolapic_timer);
static __init int setup_apicpmtimer(char *s)
{
@@ -1370,6 +1803,31 @@
}
__setup("apicpmtimer", setup_apicpmtimer);
+static int __init apic_set_verbosity(char *arg)
+{
+ if (!arg) {
+#ifdef CONFIG_X86_64
+ skip_ioapic_setup = 0;
+ ioapic_force = 1;
+ return 0;
+#endif
+ return -EINVAL;
+ }
+
+ if (strcmp("debug", arg) == 0)
+ apic_verbosity = APIC_DEBUG;
+ else if (strcmp("verbose", arg) == 0)
+ apic_verbosity = APIC_VERBOSE;
+ else {
+ printk(KERN_WARNING "APIC Verbosity level %s not recognised"
+ " use apic=verbose or apic=debug\n", arg);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+early_param("apic", apic_set_verbosity);
+
static int __init lapic_insert_resource(void)
{
if (!apic_phys)
diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile
index ee76eaa..7f0b45a 100644
--- a/arch/x86/kernel/cpu/Makefile
+++ b/arch/x86/kernel/cpu/Makefile
@@ -3,22 +3,30 @@
#
obj-y := intel_cacheinfo.o addon_cpuid_features.o
-obj-y += proc.o feature_names.o
+obj-y += proc.o capflags.o powerflags.o common.o
-obj-$(CONFIG_X86_32) += common.o bugs.o
-obj-$(CONFIG_X86_64) += common_64.o bugs_64.o
-obj-$(CONFIG_X86_32) += amd.o
-obj-$(CONFIG_X86_64) += amd_64.o
-obj-$(CONFIG_X86_32) += cyrix.o
-obj-$(CONFIG_X86_32) += centaur.o
-obj-$(CONFIG_X86_64) += centaur_64.o
-obj-$(CONFIG_X86_32) += transmeta.o
-obj-$(CONFIG_X86_32) += intel.o
-obj-$(CONFIG_X86_64) += intel_64.o
-obj-$(CONFIG_X86_32) += umc.o
+obj-$(CONFIG_X86_32) += bugs.o cmpxchg.o
+obj-$(CONFIG_X86_64) += bugs_64.o
+
+obj-$(CONFIG_CPU_SUP_INTEL) += intel.o
+obj-$(CONFIG_CPU_SUP_AMD) += amd.o
+obj-$(CONFIG_CPU_SUP_CYRIX_32) += cyrix.o
+obj-$(CONFIG_CPU_SUP_CENTAUR_32) += centaur.o
+obj-$(CONFIG_CPU_SUP_CENTAUR_64) += centaur_64.o
+obj-$(CONFIG_CPU_SUP_TRANSMETA_32) += transmeta.o
+obj-$(CONFIG_CPU_SUP_UMC_32) += umc.o
obj-$(CONFIG_X86_MCE) += mcheck/
obj-$(CONFIG_MTRR) += mtrr/
obj-$(CONFIG_CPU_FREQ) += cpufreq/
obj-$(CONFIG_X86_LOCAL_APIC) += perfctr-watchdog.o
+
+quiet_cmd_mkcapflags = MKCAP $@
+ cmd_mkcapflags = $(PERL) $(srctree)/$(src)/mkcapflags.pl $< $@
+
+cpufeature = $(src)/../../../../include/asm-x86/cpufeature.h
+
+targets += capflags.c
+$(obj)/capflags.c: $(cpufeature) $(src)/mkcapflags.pl FORCE
+ $(call if_changed,mkcapflags)
diff --git a/arch/x86/kernel/cpu/addon_cpuid_features.c b/arch/x86/kernel/cpu/addon_cpuid_features.c
index a6ef672..0d9c993 100644
--- a/arch/x86/kernel/cpu/addon_cpuid_features.c
+++ b/arch/x86/kernel/cpu/addon_cpuid_features.c
@@ -7,6 +7,8 @@
#include <asm/pat.h>
#include <asm/processor.h>
+#include <mach_apic.h>
+
struct cpuid_bit {
u16 feature;
u8 reg;
@@ -48,6 +50,92 @@
}
}
+/* leaf 0xb SMT level */
+#define SMT_LEVEL 0
+
+/* leaf 0xb sub-leaf types */
+#define INVALID_TYPE 0
+#define SMT_TYPE 1
+#define CORE_TYPE 2
+
+#define LEAFB_SUBTYPE(ecx) (((ecx) >> 8) & 0xff)
+#define BITS_SHIFT_NEXT_LEVEL(eax) ((eax) & 0x1f)
+#define LEVEL_MAX_SIBLINGS(ebx) ((ebx) & 0xffff)
+
+/*
+ * Check for extended topology enumeration cpuid leaf 0xb and if it
+ * exists, use it for populating initial_apicid and cpu topology
+ * detection.
+ */
+void __cpuinit detect_extended_topology(struct cpuinfo_x86 *c)
+{
+#ifdef CONFIG_SMP
+ unsigned int eax, ebx, ecx, edx, sub_index;
+ unsigned int ht_mask_width, core_plus_mask_width;
+ unsigned int core_select_mask, core_level_siblings;
+
+ if (c->cpuid_level < 0xb)
+ return;
+
+ cpuid_count(0xb, SMT_LEVEL, &eax, &ebx, &ecx, &edx);
+
+ /*
+ * check if the cpuid leaf 0xb is actually implemented.
+ */
+ if (ebx == 0 || (LEAFB_SUBTYPE(ecx) != SMT_TYPE))
+ return;
+
+ set_cpu_cap(c, X86_FEATURE_XTOPOLOGY);
+
+ /*
+ * initial apic id, which also represents 32-bit extended x2apic id.
+ */
+ c->initial_apicid = edx;
+
+ /*
+ * Populate HT related information from sub-leaf level 0.
+ */
+ core_level_siblings = smp_num_siblings = LEVEL_MAX_SIBLINGS(ebx);
+ core_plus_mask_width = ht_mask_width = BITS_SHIFT_NEXT_LEVEL(eax);
+
+ sub_index = 1;
+ do {
+ cpuid_count(0xb, sub_index, &eax, &ebx, &ecx, &edx);
+
+ /*
+ * Check for the Core type in the implemented sub leaves.
+ */
+ if (LEAFB_SUBTYPE(ecx) == CORE_TYPE) {
+ core_level_siblings = LEVEL_MAX_SIBLINGS(ebx);
+ core_plus_mask_width = BITS_SHIFT_NEXT_LEVEL(eax);
+ break;
+ }
+
+ sub_index++;
+ } while (LEAFB_SUBTYPE(ecx) != INVALID_TYPE);
+
+ core_select_mask = (~(-1 << core_plus_mask_width)) >> ht_mask_width;
+
+#ifdef CONFIG_X86_32
+ c->cpu_core_id = phys_pkg_id(c->initial_apicid, ht_mask_width)
+ & core_select_mask;
+ c->phys_proc_id = phys_pkg_id(c->initial_apicid, core_plus_mask_width);
+#else
+ c->cpu_core_id = phys_pkg_id(ht_mask_width) & core_select_mask;
+ c->phys_proc_id = phys_pkg_id(core_plus_mask_width);
+#endif
+ c->x86_max_cores = (core_level_siblings / smp_num_siblings);
+
+
+ printk(KERN_INFO "CPU: Physical Processor ID: %d\n",
+ c->phys_proc_id);
+ if (c->x86_max_cores > 1)
+ printk(KERN_INFO "CPU: Processor Core ID: %d\n",
+ c->cpu_core_id);
+ return;
+#endif
+}
+
#ifdef CONFIG_X86_PAT
void __cpuinit validate_pat_support(struct cpuinfo_x86 *c)
{
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c
index 18514ed..32e7352 100644
--- a/arch/x86/kernel/cpu/amd.c
+++ b/arch/x86/kernel/cpu/amd.c
@@ -1,13 +1,22 @@
#include <linux/init.h>
#include <linux/bitops.h>
#include <linux/mm.h>
+
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/apic.h>
+#ifdef CONFIG_X86_64
+# include <asm/numa_64.h>
+# include <asm/mmconfig.h>
+# include <asm/cacheflush.h>
+#endif
+
#include <mach_apic.h>
+
#include "cpu.h"
+#ifdef CONFIG_X86_32
/*
* B step AMD K6 before B 9730xxxx have hardware bugs that can cause
* misexecution of code under Linux. Owners of such processors should
@@ -24,26 +33,273 @@
extern void vide(void);
__asm__(".align 4\nvide: ret");
-static void __cpuinit early_init_amd(struct cpuinfo_x86 *c)
+static void __cpuinit init_amd_k5(struct cpuinfo_x86 *c)
{
- if (cpuid_eax(0x80000000) >= 0x80000007) {
- c->x86_power = cpuid_edx(0x80000007);
- if (c->x86_power & (1<<8))
- set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC);
+/*
+ * General Systems BIOSen alias the cpu frequency registers
+ * of the Elan at 0x000df000. Unfortuantly, one of the Linux
+ * drivers subsequently pokes it, and changes the CPU speed.
+ * Workaround : Remove the unneeded alias.
+ */
+#define CBAR (0xfffc) /* Configuration Base Address (32-bit) */
+#define CBAR_ENB (0x80000000)
+#define CBAR_KEY (0X000000CB)
+ if (c->x86_model == 9 || c->x86_model == 10) {
+ if (inl (CBAR) & CBAR_ENB)
+ outl (0 | CBAR_KEY, CBAR);
+ }
+}
+
+
+static void __cpuinit init_amd_k6(struct cpuinfo_x86 *c)
+{
+ u32 l, h;
+ int mbytes = num_physpages >> (20-PAGE_SHIFT);
+
+ if (c->x86_model < 6) {
+ /* Based on AMD doc 20734R - June 2000 */
+ if (c->x86_model == 0) {
+ clear_cpu_cap(c, X86_FEATURE_APIC);
+ set_cpu_cap(c, X86_FEATURE_PGE);
+ }
+ return;
}
+ if (c->x86_model == 6 && c->x86_mask == 1) {
+ const int K6_BUG_LOOP = 1000000;
+ int n;
+ void (*f_vide)(void);
+ unsigned long d, d2;
+
+ printk(KERN_INFO "AMD K6 stepping B detected - ");
+
+ /*
+ * It looks like AMD fixed the 2.6.2 bug and improved indirect
+ * calls at the same time.
+ */
+
+ n = K6_BUG_LOOP;
+ f_vide = vide;
+ rdtscl(d);
+ while (n--)
+ f_vide();
+ rdtscl(d2);
+ d = d2-d;
+
+ if (d > 20*K6_BUG_LOOP)
+ printk("system stability may be impaired when more than 32 MB are used.\n");
+ else
+ printk("probably OK (after B9730xxxx).\n");
+ printk(KERN_INFO "Please see http://membres.lycos.fr/poulot/k6bug.html\n");
+ }
+
+ /* K6 with old style WHCR */
+ if (c->x86_model < 8 ||
+ (c->x86_model == 8 && c->x86_mask < 8)) {
+ /* We can only write allocate on the low 508Mb */
+ if (mbytes > 508)
+ mbytes = 508;
+
+ rdmsr(MSR_K6_WHCR, l, h);
+ if ((l&0x0000FFFF) == 0) {
+ unsigned long flags;
+ l = (1<<0)|((mbytes/4)<<1);
+ local_irq_save(flags);
+ wbinvd();
+ wrmsr(MSR_K6_WHCR, l, h);
+ local_irq_restore(flags);
+ printk(KERN_INFO "Enabling old style K6 write allocation for %d Mb\n",
+ mbytes);
+ }
+ return;
+ }
+
+ if ((c->x86_model == 8 && c->x86_mask > 7) ||
+ c->x86_model == 9 || c->x86_model == 13) {
+ /* The more serious chips .. */
+
+ if (mbytes > 4092)
+ mbytes = 4092;
+
+ rdmsr(MSR_K6_WHCR, l, h);
+ if ((l&0xFFFF0000) == 0) {
+ unsigned long flags;
+ l = ((mbytes>>2)<<22)|(1<<16);
+ local_irq_save(flags);
+ wbinvd();
+ wrmsr(MSR_K6_WHCR, l, h);
+ local_irq_restore(flags);
+ printk(KERN_INFO "Enabling new style K6 write allocation for %d Mb\n",
+ mbytes);
+ }
+
+ return;
+ }
+
+ if (c->x86_model == 10) {
+ /* AMD Geode LX is model 10 */
+ /* placeholder for any needed mods */
+ return;
+ }
+}
+
+static void __cpuinit init_amd_k7(struct cpuinfo_x86 *c)
+{
+ u32 l, h;
+
+ /*
+ * Bit 15 of Athlon specific MSR 15, needs to be 0
+ * to enable SSE on Palomino/Morgan/Barton CPU's.
+ * If the BIOS didn't enable it already, enable it here.
+ */
+ if (c->x86_model >= 6 && c->x86_model <= 10) {
+ if (!cpu_has(c, X86_FEATURE_XMM)) {
+ printk(KERN_INFO "Enabling disabled K7/SSE Support.\n");
+ rdmsr(MSR_K7_HWCR, l, h);
+ l &= ~0x00008000;
+ wrmsr(MSR_K7_HWCR, l, h);
+ set_cpu_cap(c, X86_FEATURE_XMM);
+ }
+ }
+
+ /*
+ * It's been determined by AMD that Athlons since model 8 stepping 1
+ * are more robust with CLK_CTL set to 200xxxxx instead of 600xxxxx
+ * As per AMD technical note 27212 0.2
+ */
+ if ((c->x86_model == 8 && c->x86_mask >= 1) || (c->x86_model > 8)) {
+ rdmsr(MSR_K7_CLK_CTL, l, h);
+ if ((l & 0xfff00000) != 0x20000000) {
+ printk ("CPU: CLK_CTL MSR was %x. Reprogramming to %x\n", l,
+ ((l & 0x000fffff)|0x20000000));
+ wrmsr(MSR_K7_CLK_CTL, (l & 0x000fffff)|0x20000000, h);
+ }
+ }
+
+ set_cpu_cap(c, X86_FEATURE_K7);
+}
+#endif
+
+#if defined(CONFIG_NUMA) && defined(CONFIG_X86_64)
+static int __cpuinit nearby_node(int apicid)
+{
+ int i, node;
+
+ for (i = apicid - 1; i >= 0; i--) {
+ node = apicid_to_node[i];
+ if (node != NUMA_NO_NODE && node_online(node))
+ return node;
+ }
+ for (i = apicid + 1; i < MAX_LOCAL_APIC; i++) {
+ node = apicid_to_node[i];
+ if (node != NUMA_NO_NODE && node_online(node))
+ return node;
+ }
+ return first_node(node_online_map); /* Shouldn't happen */
+}
+#endif
+
+/*
+ * On a AMD dual core setup the lower bits of the APIC id distingush the cores.
+ * Assumes number of cores is a power of two.
+ */
+static void __cpuinit amd_detect_cmp(struct cpuinfo_x86 *c)
+{
+#ifdef CONFIG_X86_HT
+ unsigned bits;
+
+ bits = c->x86_coreid_bits;
+
+ /* Low order bits define the core id (index of core in socket) */
+ c->cpu_core_id = c->initial_apicid & ((1 << bits)-1);
+ /* Convert the initial APIC ID into the socket ID */
+ c->phys_proc_id = c->initial_apicid >> bits;
+#endif
+}
+
+static void __cpuinit srat_detect_node(struct cpuinfo_x86 *c)
+{
+#if defined(CONFIG_NUMA) && defined(CONFIG_X86_64)
+ int cpu = smp_processor_id();
+ int node;
+ unsigned apicid = hard_smp_processor_id();
+
+ node = c->phys_proc_id;
+ if (apicid_to_node[apicid] != NUMA_NO_NODE)
+ node = apicid_to_node[apicid];
+ if (!node_online(node)) {
+ /* Two possibilities here:
+ - The CPU is missing memory and no node was created.
+ In that case try picking one from a nearby CPU
+ - The APIC IDs differ from the HyperTransport node IDs
+ which the K8 northbridge parsing fills in.
+ Assume they are all increased by a constant offset,
+ but in the same order as the HT nodeids.
+ If that doesn't result in a usable node fall back to the
+ path for the previous case. */
+
+ int ht_nodeid = c->initial_apicid;
+
+ if (ht_nodeid >= 0 &&
+ apicid_to_node[ht_nodeid] != NUMA_NO_NODE)
+ node = apicid_to_node[ht_nodeid];
+ /* Pick a nearby node */
+ if (!node_online(node))
+ node = nearby_node(apicid);
+ }
+ numa_set_node(cpu, node);
+
+ printk(KERN_INFO "CPU %d/%x -> Node %d\n", cpu, apicid, node);
+#endif
+}
+
+static void __cpuinit early_init_amd_mc(struct cpuinfo_x86 *c)
+{
+#ifdef CONFIG_X86_HT
+ unsigned bits, ecx;
+
+ /* Multi core CPU? */
+ if (c->extended_cpuid_level < 0x80000008)
+ return;
+
+ ecx = cpuid_ecx(0x80000008);
+
+ c->x86_max_cores = (ecx & 0xff) + 1;
+
+ /* CPU telling us the core id bits shift? */
+ bits = (ecx >> 12) & 0xF;
+
+ /* Otherwise recompute */
+ if (bits == 0) {
+ while ((1 << bits) < c->x86_max_cores)
+ bits++;
+ }
+
+ c->x86_coreid_bits = bits;
+#endif
+}
+
+static void __cpuinit early_init_amd(struct cpuinfo_x86 *c)
+{
+ early_init_amd_mc(c);
+
+ /* c->x86_power is 8000_0007 edx. Bit 8 is constant TSC */
+ if (c->x86_power & (1<<8))
+ set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC);
+
+#ifdef CONFIG_X86_64
+ set_cpu_cap(c, X86_FEATURE_SYSCALL32);
+#else
/* Set MTRR capability flag if appropriate */
- if (c->x86_model == 13 || c->x86_model == 9 ||
- (c->x86_model == 8 && c->x86_mask >= 8))
- set_cpu_cap(c, X86_FEATURE_K6_MTRR);
+ if (c->x86 == 5)
+ if (c->x86_model == 13 || c->x86_model == 9 ||
+ (c->x86_model == 8 && c->x86_mask >= 8))
+ set_cpu_cap(c, X86_FEATURE_K6_MTRR);
+#endif
}
static void __cpuinit init_amd(struct cpuinfo_x86 *c)
{
- u32 l, h;
- int mbytes = num_physpages >> (20-PAGE_SHIFT);
- int r;
-
#ifdef CONFIG_SMP
unsigned long long value;
@@ -54,7 +310,7 @@
* Errata 63 for SH-B3 steppings
* Errata 122 for all steppings (F+ have it disabled by default)
*/
- if (c->x86 == 15) {
+ if (c->x86 == 0xf) {
rdmsrl(MSR_K7_HWCR, value);
value |= 1 << 6;
wrmsrl(MSR_K7_HWCR, value);
@@ -64,209 +320,119 @@
early_init_amd(c);
/*
- * FIXME: We should handle the K5 here. Set up the write
- * range and also turn on MSR 83 bits 4 and 31 (write alloc,
- * no bus pipeline)
- */
-
- /*
* Bit 31 in normal CPUID used for nonstandard 3DNow ID;
* 3DNow is IDd by bit 31 in extended CPUID (1*32+31) anyway
*/
clear_cpu_cap(c, 0*32+31);
- r = get_model_name(c);
+#ifdef CONFIG_X86_64
+ /* On C+ stepping K8 rep microcode works well for copy/memset */
+ if (c->x86 == 0xf) {
+ u32 level;
+
+ level = cpuid_eax(1);
+ if((level >= 0x0f48 && level < 0x0f50) || level >= 0x0f58)
+ set_cpu_cap(c, X86_FEATURE_REP_GOOD);
+ }
+ if (c->x86 == 0x10 || c->x86 == 0x11)
+ set_cpu_cap(c, X86_FEATURE_REP_GOOD);
+#else
+
+ /*
+ * FIXME: We should handle the K5 here. Set up the write
+ * range and also turn on MSR 83 bits 4 and 31 (write alloc,
+ * no bus pipeline)
+ */
switch (c->x86) {
case 4:
- /*
- * General Systems BIOSen alias the cpu frequency registers
- * of the Elan at 0x000df000. Unfortuantly, one of the Linux
- * drivers subsequently pokes it, and changes the CPU speed.
- * Workaround : Remove the unneeded alias.
- */
-#define CBAR (0xfffc) /* Configuration Base Address (32-bit) */
-#define CBAR_ENB (0x80000000)
-#define CBAR_KEY (0X000000CB)
- if (c->x86_model == 9 || c->x86_model == 10) {
- if (inl (CBAR) & CBAR_ENB)
- outl (0 | CBAR_KEY, CBAR);
- }
- break;
+ init_amd_k5(c);
+ break;
case 5:
- if (c->x86_model < 6) {
- /* Based on AMD doc 20734R - June 2000 */
- if (c->x86_model == 0) {
- clear_cpu_cap(c, X86_FEATURE_APIC);
- set_cpu_cap(c, X86_FEATURE_PGE);
- }
- break;
- }
-
- if (c->x86_model == 6 && c->x86_mask == 1) {
- const int K6_BUG_LOOP = 1000000;
- int n;
- void (*f_vide)(void);
- unsigned long d, d2;
-
- printk(KERN_INFO "AMD K6 stepping B detected - ");
-
- /*
- * It looks like AMD fixed the 2.6.2 bug and improved indirect
- * calls at the same time.
- */
-
- n = K6_BUG_LOOP;
- f_vide = vide;
- rdtscl(d);
- while (n--)
- f_vide();
- rdtscl(d2);
- d = d2-d;
-
- if (d > 20*K6_BUG_LOOP)
- printk("system stability may be impaired when more than 32 MB are used.\n");
- else
- printk("probably OK (after B9730xxxx).\n");
- printk(KERN_INFO "Please see http://membres.lycos.fr/poulot/k6bug.html\n");
- }
-
- /* K6 with old style WHCR */
- if (c->x86_model < 8 ||
- (c->x86_model == 8 && c->x86_mask < 8)) {
- /* We can only write allocate on the low 508Mb */
- if (mbytes > 508)
- mbytes = 508;
-
- rdmsr(MSR_K6_WHCR, l, h);
- if ((l&0x0000FFFF) == 0) {
- unsigned long flags;
- l = (1<<0)|((mbytes/4)<<1);
- local_irq_save(flags);
- wbinvd();
- wrmsr(MSR_K6_WHCR, l, h);
- local_irq_restore(flags);
- printk(KERN_INFO "Enabling old style K6 write allocation for %d Mb\n",
- mbytes);
- }
- break;
- }
-
- if ((c->x86_model == 8 && c->x86_mask > 7) ||
- c->x86_model == 9 || c->x86_model == 13) {
- /* The more serious chips .. */
-
- if (mbytes > 4092)
- mbytes = 4092;
-
- rdmsr(MSR_K6_WHCR, l, h);
- if ((l&0xFFFF0000) == 0) {
- unsigned long flags;
- l = ((mbytes>>2)<<22)|(1<<16);
- local_irq_save(flags);
- wbinvd();
- wrmsr(MSR_K6_WHCR, l, h);
- local_irq_restore(flags);
- printk(KERN_INFO "Enabling new style K6 write allocation for %d Mb\n",
- mbytes);
- }
-
- break;
- }
-
- if (c->x86_model == 10) {
- /* AMD Geode LX is model 10 */
- /* placeholder for any needed mods */
- break;
- }
- break;
+ init_amd_k6(c);
+ break;
case 6: /* An Athlon/Duron */
-
- /*
- * Bit 15 of Athlon specific MSR 15, needs to be 0
- * to enable SSE on Palomino/Morgan/Barton CPU's.
- * If the BIOS didn't enable it already, enable it here.
- */
- if (c->x86_model >= 6 && c->x86_model <= 10) {
- if (!cpu_has(c, X86_FEATURE_XMM)) {
- printk(KERN_INFO "Enabling disabled K7/SSE Support.\n");
- rdmsr(MSR_K7_HWCR, l, h);
- l &= ~0x00008000;
- wrmsr(MSR_K7_HWCR, l, h);
- set_cpu_cap(c, X86_FEATURE_XMM);
- }
- }
-
- /*
- * It's been determined by AMD that Athlons since model 8 stepping 1
- * are more robust with CLK_CTL set to 200xxxxx instead of 600xxxxx
- * As per AMD technical note 27212 0.2
- */
- if ((c->x86_model == 8 && c->x86_mask >= 1) || (c->x86_model > 8)) {
- rdmsr(MSR_K7_CLK_CTL, l, h);
- if ((l & 0xfff00000) != 0x20000000) {
- printk ("CPU: CLK_CTL MSR was %x. Reprogramming to %x\n", l,
- ((l & 0x000fffff)|0x20000000));
- wrmsr(MSR_K7_CLK_CTL, (l & 0x000fffff)|0x20000000, h);
- }
- }
- break;
- }
-
- switch (c->x86) {
- case 15:
- /* Use K8 tuning for Fam10h and Fam11h */
- case 0x10:
- case 0x11:
- set_cpu_cap(c, X86_FEATURE_K8);
+ init_amd_k7(c);
break;
- case 6:
- set_cpu_cap(c, X86_FEATURE_K7);
- break;
- }
- if (c->x86 >= 6)
- set_cpu_cap(c, X86_FEATURE_FXSAVE_LEAK);
-
- display_cacheinfo(c);
-
- if (cpuid_eax(0x80000000) >= 0x80000008)
- c->x86_max_cores = (cpuid_ecx(0x80000008) & 0xff) + 1;
-
-#ifdef CONFIG_X86_HT
- /*
- * On a AMD multi core setup the lower bits of the APIC id
- * distinguish the cores.
- */
- if (c->x86_max_cores > 1) {
- int cpu = smp_processor_id();
- unsigned bits = (cpuid_ecx(0x80000008) >> 12) & 0xf;
-
- if (bits == 0) {
- while ((1 << bits) < c->x86_max_cores)
- bits++;
- }
- c->cpu_core_id = c->phys_proc_id & ((1<<bits)-1);
- c->phys_proc_id >>= bits;
- printk(KERN_INFO "CPU %d(%d) -> Core %d\n",
- cpu, c->x86_max_cores, c->cpu_core_id);
- }
-#endif
-
- if (cpuid_eax(0x80000000) >= 0x80000006) {
- if ((c->x86 == 0x10) && (cpuid_edx(0x80000006) & 0xf000))
- num_cache_leaves = 4;
- else
- num_cache_leaves = 3;
}
/* K6s reports MCEs but don't actually have all the MSRs */
if (c->x86 < 6)
clear_cpu_cap(c, X86_FEATURE_MCE);
+#endif
- if (cpu_has_xmm2)
+ /* Enable workaround for FXSAVE leak */
+ if (c->x86 >= 6)
+ set_cpu_cap(c, X86_FEATURE_FXSAVE_LEAK);
+
+ if (!c->x86_model_id[0]) {
+ switch (c->x86) {
+ case 0xf:
+ /* Should distinguish Models here, but this is only
+ a fallback anyways. */
+ strcpy(c->x86_model_id, "Hammer");
+ break;
+ }
+ }
+
+ display_cacheinfo(c);
+
+ /* Multi core CPU? */
+ if (c->extended_cpuid_level >= 0x80000008) {
+ amd_detect_cmp(c);
+ srat_detect_node(c);
+ }
+
+#ifdef CONFIG_X86_32
+ detect_ht(c);
+#endif
+
+ if (c->extended_cpuid_level >= 0x80000006) {
+ if ((c->x86 >= 0x0f) && (cpuid_edx(0x80000006) & 0xf000))
+ num_cache_leaves = 4;
+ else
+ num_cache_leaves = 3;
+ }
+
+ if (c->x86 >= 0xf && c->x86 <= 0x11)
+ set_cpu_cap(c, X86_FEATURE_K8);
+
+ if (cpu_has_xmm2) {
+ /* MFENCE stops RDTSC speculation */
set_cpu_cap(c, X86_FEATURE_MFENCE_RDTSC);
+ }
+
+#ifdef CONFIG_X86_64
+ if (c->x86 == 0x10) {
+ /* do this for boot cpu */
+ if (c == &boot_cpu_data)
+ check_enable_amd_mmconf_dmi();
+
+ fam10h_check_enable_mmcfg();
+ }
+
+ if (c == &boot_cpu_data && c->x86 >= 0xf && c->x86 <= 0x11) {
+ unsigned long long tseg;
+
+ /*
+ * Split up direct mapping around the TSEG SMM area.
+ * Don't do it for gbpages because there seems very little
+ * benefit in doing so.
+ */
+ if (!rdmsrl_safe(MSR_K8_TSEG_ADDR, &tseg)) {
+ printk(KERN_DEBUG "tseg: %010llx\n", tseg);
+ if ((tseg>>PMD_SHIFT) <
+ (max_low_pfn_mapped>>(PMD_SHIFT-PAGE_SHIFT)) ||
+ ((tseg>>PMD_SHIFT) <
+ (max_pfn_mapped>>(PMD_SHIFT-PAGE_SHIFT)) &&
+ (tseg>>PMD_SHIFT) >= (1ULL<<(32 - PMD_SHIFT))))
+ set_memory_4k((unsigned long)__va(tseg), 1);
+ }
+ }
+#endif
}
+#ifdef CONFIG_X86_32
static unsigned int __cpuinit amd_size_cache(struct cpuinfo_x86 *c, unsigned int size)
{
/* AMD errata T13 (order #21922) */
@@ -279,10 +445,12 @@
}
return size;
}
+#endif
static struct cpu_dev amd_cpu_dev __cpuinitdata = {
.c_vendor = "AMD",
.c_ident = { "AuthenticAMD" },
+#ifdef CONFIG_X86_32
.c_models = {
{ .vendor = X86_VENDOR_AMD, .family = 4, .model_names =
{
@@ -295,9 +463,11 @@
}
},
},
+ .c_size_cache = amd_size_cache,
+#endif
.c_early_init = early_init_amd,
.c_init = init_amd,
- .c_size_cache = amd_size_cache,
+ .c_x86_vendor = X86_VENDOR_AMD,
};
-cpu_vendor_dev_register(X86_VENDOR_AMD, &amd_cpu_dev);
+cpu_dev_register(amd_cpu_dev);
diff --git a/arch/x86/kernel/cpu/amd_64.c b/arch/x86/kernel/cpu/amd_64.c
deleted file mode 100644
index d1692b2..0000000
--- a/arch/x86/kernel/cpu/amd_64.c
+++ /dev/null
@@ -1,224 +0,0 @@
-#include <linux/init.h>
-#include <linux/mm.h>
-
-#include <asm/numa_64.h>
-#include <asm/mmconfig.h>
-#include <asm/cacheflush.h>
-
-#include <mach_apic.h>
-
-#include "cpu.h"
-
-int force_mwait __cpuinitdata;
-
-#ifdef CONFIG_NUMA
-static int __cpuinit nearby_node(int apicid)
-{
- int i, node;
-
- for (i = apicid - 1; i >= 0; i--) {
- node = apicid_to_node[i];
- if (node != NUMA_NO_NODE && node_online(node))
- return node;
- }
- for (i = apicid + 1; i < MAX_LOCAL_APIC; i++) {
- node = apicid_to_node[i];
- if (node != NUMA_NO_NODE && node_online(node))
- return node;
- }
- return first_node(node_online_map); /* Shouldn't happen */
-}
-#endif
-
-/*
- * On a AMD dual core setup the lower bits of the APIC id distingush the cores.
- * Assumes number of cores is a power of two.
- */
-static void __cpuinit amd_detect_cmp(struct cpuinfo_x86 *c)
-{
-#ifdef CONFIG_SMP
- unsigned bits;
-#ifdef CONFIG_NUMA
- int cpu = smp_processor_id();
- int node = 0;
- unsigned apicid = hard_smp_processor_id();
-#endif
- bits = c->x86_coreid_bits;
-
- /* Low order bits define the core id (index of core in socket) */
- c->cpu_core_id = c->initial_apicid & ((1 << bits)-1);
- /* Convert the initial APIC ID into the socket ID */
- c->phys_proc_id = c->initial_apicid >> bits;
-
-#ifdef CONFIG_NUMA
- node = c->phys_proc_id;
- if (apicid_to_node[apicid] != NUMA_NO_NODE)
- node = apicid_to_node[apicid];
- if (!node_online(node)) {
- /* Two possibilities here:
- - The CPU is missing memory and no node was created.
- In that case try picking one from a nearby CPU
- - The APIC IDs differ from the HyperTransport node IDs
- which the K8 northbridge parsing fills in.
- Assume they are all increased by a constant offset,
- but in the same order as the HT nodeids.
- If that doesn't result in a usable node fall back to the
- path for the previous case. */
-
- int ht_nodeid = c->initial_apicid;
-
- if (ht_nodeid >= 0 &&
- apicid_to_node[ht_nodeid] != NUMA_NO_NODE)
- node = apicid_to_node[ht_nodeid];
- /* Pick a nearby node */
- if (!node_online(node))
- node = nearby_node(apicid);
- }
- numa_set_node(cpu, node);
-
- printk(KERN_INFO "CPU %d/%x -> Node %d\n", cpu, apicid, node);
-#endif
-#endif
-}
-
-static void __cpuinit early_init_amd_mc(struct cpuinfo_x86 *c)
-{
-#ifdef CONFIG_SMP
- unsigned bits, ecx;
-
- /* Multi core CPU? */
- if (c->extended_cpuid_level < 0x80000008)
- return;
-
- ecx = cpuid_ecx(0x80000008);
-
- c->x86_max_cores = (ecx & 0xff) + 1;
-
- /* CPU telling us the core id bits shift? */
- bits = (ecx >> 12) & 0xF;
-
- /* Otherwise recompute */
- if (bits == 0) {
- while ((1 << bits) < c->x86_max_cores)
- bits++;
- }
-
- c->x86_coreid_bits = bits;
-
-#endif
-}
-
-static void __cpuinit early_init_amd(struct cpuinfo_x86 *c)
-{
- early_init_amd_mc(c);
-
- /* c->x86_power is 8000_0007 edx. Bit 8 is constant TSC */
- if (c->x86_power & (1<<8))
- set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC);
-
- set_cpu_cap(c, X86_FEATURE_SYSCALL32);
-}
-
-static void __cpuinit init_amd(struct cpuinfo_x86 *c)
-{
- unsigned level;
-
-#ifdef CONFIG_SMP
- unsigned long value;
-
- /*
- * Disable TLB flush filter by setting HWCR.FFDIS on K8
- * bit 6 of msr C001_0015
- *
- * Errata 63 for SH-B3 steppings
- * Errata 122 for all steppings (F+ have it disabled by default)
- */
- if (c->x86 == 0xf) {
- rdmsrl(MSR_K8_HWCR, value);
- value |= 1 << 6;
- wrmsrl(MSR_K8_HWCR, value);
- }
-#endif
-
- /* Bit 31 in normal CPUID used for nonstandard 3DNow ID;
- 3DNow is IDd by bit 31 in extended CPUID (1*32+31) anyway */
- clear_cpu_cap(c, 0*32+31);
-
- /* On C+ stepping K8 rep microcode works well for copy/memset */
- if (c->x86 == 0xf) {
- level = cpuid_eax(1);
- if((level >= 0x0f48 && level < 0x0f50) || level >= 0x0f58)
- set_cpu_cap(c, X86_FEATURE_REP_GOOD);
- }
- if (c->x86 == 0x10 || c->x86 == 0x11)
- set_cpu_cap(c, X86_FEATURE_REP_GOOD);
-
- /* Enable workaround for FXSAVE leak */
- if (c->x86 >= 6)
- set_cpu_cap(c, X86_FEATURE_FXSAVE_LEAK);
-
- level = get_model_name(c);
- if (!level) {
- switch (c->x86) {
- case 0xf:
- /* Should distinguish Models here, but this is only
- a fallback anyways. */
- strcpy(c->x86_model_id, "Hammer");
- break;
- }
- }
- display_cacheinfo(c);
-
- /* Multi core CPU? */
- if (c->extended_cpuid_level >= 0x80000008)
- amd_detect_cmp(c);
-
- if (c->extended_cpuid_level >= 0x80000006 &&
- (cpuid_edx(0x80000006) & 0xf000))
- num_cache_leaves = 4;
- else
- num_cache_leaves = 3;
-
- if (c->x86 >= 0xf && c->x86 <= 0x11)
- set_cpu_cap(c, X86_FEATURE_K8);
-
- /* MFENCE stops RDTSC speculation */
- set_cpu_cap(c, X86_FEATURE_MFENCE_RDTSC);
-
- if (c->x86 == 0x10) {
- /* do this for boot cpu */
- if (c == &boot_cpu_data)
- check_enable_amd_mmconf_dmi();
-
- fam10h_check_enable_mmcfg();
- }
-
- if (c == &boot_cpu_data && c->x86 >= 0xf && c->x86 <= 0x11) {
- unsigned long long tseg;
-
- /*
- * Split up direct mapping around the TSEG SMM area.
- * Don't do it for gbpages because there seems very little
- * benefit in doing so.
- */
- if (!rdmsrl_safe(MSR_K8_TSEG_ADDR, &tseg)) {
- printk(KERN_DEBUG "tseg: %010llx\n", tseg);
- if ((tseg>>PMD_SHIFT) <
- (max_low_pfn_mapped>>(PMD_SHIFT-PAGE_SHIFT)) ||
- ((tseg>>PMD_SHIFT) <
- (max_pfn_mapped>>(PMD_SHIFT-PAGE_SHIFT)) &&
- (tseg>>PMD_SHIFT) >= (1ULL<<(32 - PMD_SHIFT))))
- set_memory_4k((unsigned long)__va(tseg), 1);
- }
- }
-}
-
-static struct cpu_dev amd_cpu_dev __cpuinitdata = {
- .c_vendor = "AMD",
- .c_ident = { "AuthenticAMD" },
- .c_early_init = early_init_amd,
- .c_init = init_amd,
-};
-
-cpu_vendor_dev_register(X86_VENDOR_AMD, &amd_cpu_dev);
-
diff --git a/arch/x86/kernel/cpu/centaur.c b/arch/x86/kernel/cpu/centaur.c
index a0534c0..89bfdd9 100644
--- a/arch/x86/kernel/cpu/centaur.c
+++ b/arch/x86/kernel/cpu/centaur.c
@@ -289,7 +289,6 @@
if (c->x86_model >= 6 && c->x86_model < 9)
set_cpu_cap(c, X86_FEATURE_3DNOW);
- get_model_name(c);
display_cacheinfo(c);
}
@@ -475,6 +474,7 @@
.c_early_init = early_init_centaur,
.c_init = init_centaur,
.c_size_cache = centaur_size_cache,
+ .c_x86_vendor = X86_VENDOR_CENTAUR,
};
-cpu_vendor_dev_register(X86_VENDOR_CENTAUR, ¢aur_cpu_dev);
+cpu_dev_register(centaur_cpu_dev);
diff --git a/arch/x86/kernel/cpu/centaur_64.c b/arch/x86/kernel/cpu/centaur_64.c
index 1d181c4..a1625f5 100644
--- a/arch/x86/kernel/cpu/centaur_64.c
+++ b/arch/x86/kernel/cpu/centaur_64.c
@@ -16,9 +16,10 @@
static void __cpuinit init_centaur(struct cpuinfo_x86 *c)
{
+ early_init_centaur(c);
+
if (c->x86 == 0x6 && c->x86_model >= 0xf) {
c->x86_cache_alignment = c->x86_clflush_size * 2;
- set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC);
set_cpu_cap(c, X86_FEATURE_REP_GOOD);
}
set_cpu_cap(c, X86_FEATURE_LFENCE_RDTSC);
@@ -29,7 +30,8 @@
.c_ident = { "CentaurHauls" },
.c_early_init = early_init_centaur,
.c_init = init_centaur,
+ .c_x86_vendor = X86_VENDOR_CENTAUR,
};
-cpu_vendor_dev_register(X86_VENDOR_CENTAUR, ¢aur_cpu_dev);
+cpu_dev_register(centaur_cpu_dev);
diff --git a/arch/x86/kernel/cpu/cmpxchg.c b/arch/x86/kernel/cpu/cmpxchg.c
new file mode 100644
index 0000000..2056ccf
--- /dev/null
+++ b/arch/x86/kernel/cpu/cmpxchg.c
@@ -0,0 +1,72 @@
+/*
+ * cmpxchg*() fallbacks for CPU not supporting these instructions
+ */
+
+#include <linux/kernel.h>
+#include <linux/smp.h>
+#include <linux/module.h>
+
+#ifndef CONFIG_X86_CMPXCHG
+unsigned long cmpxchg_386_u8(volatile void *ptr, u8 old, u8 new)
+{
+ u8 prev;
+ unsigned long flags;
+
+ /* Poor man's cmpxchg for 386. Unsuitable for SMP */
+ local_irq_save(flags);
+ prev = *(u8 *)ptr;
+ if (prev == old)
+ *(u8 *)ptr = new;
+ local_irq_restore(flags);
+ return prev;
+}
+EXPORT_SYMBOL(cmpxchg_386_u8);
+
+unsigned long cmpxchg_386_u16(volatile void *ptr, u16 old, u16 new)
+{
+ u16 prev;
+ unsigned long flags;
+
+ /* Poor man's cmpxchg for 386. Unsuitable for SMP */
+ local_irq_save(flags);
+ prev = *(u16 *)ptr;
+ if (prev == old)
+ *(u16 *)ptr = new;
+ local_irq_restore(flags);
+ return prev;
+}
+EXPORT_SYMBOL(cmpxchg_386_u16);
+
+unsigned long cmpxchg_386_u32(volatile void *ptr, u32 old, u32 new)
+{
+ u32 prev;
+ unsigned long flags;
+
+ /* Poor man's cmpxchg for 386. Unsuitable for SMP */
+ local_irq_save(flags);
+ prev = *(u32 *)ptr;
+ if (prev == old)
+ *(u32 *)ptr = new;
+ local_irq_restore(flags);
+ return prev;
+}
+EXPORT_SYMBOL(cmpxchg_386_u32);
+#endif
+
+#ifndef CONFIG_X86_CMPXCHG64
+unsigned long long cmpxchg_486_u64(volatile void *ptr, u64 old, u64 new)
+{
+ u64 prev;
+ unsigned long flags;
+
+ /* Poor man's cmpxchg8b for 386 and 486. Unsuitable for SMP */
+ local_irq_save(flags);
+ prev = *(u64 *)ptr;
+ if (prev == old)
+ *(u64 *)ptr = new;
+ local_irq_restore(flags);
+ return prev;
+}
+EXPORT_SYMBOL(cmpxchg_486_u64);
+#endif
+
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index 4e456bd..25581dc 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -1,28 +1,62 @@
#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
#include <linux/string.h>
+#include <linux/bootmem.h>
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <linux/kgdb.h>
+#include <linux/topology.h>
#include <linux/delay.h>
#include <linux/smp.h>
-#include <linux/module.h>
#include <linux/percpu.h>
-#include <linux/bootmem.h>
-#include <asm/processor.h>
#include <asm/i387.h>
#include <asm/msr.h>
#include <asm/io.h>
+#include <asm/linkage.h>
#include <asm/mmu_context.h>
#include <asm/mtrr.h>
#include <asm/mce.h>
#include <asm/pat.h>
#include <asm/asm.h>
+#include <asm/numa.h>
#ifdef CONFIG_X86_LOCAL_APIC
#include <asm/mpspec.h>
#include <asm/apic.h>
#include <mach_apic.h>
+#include <asm/genapic.h>
#endif
+#include <asm/pda.h>
+#include <asm/pgtable.h>
+#include <asm/processor.h>
+#include <asm/desc.h>
+#include <asm/atomic.h>
+#include <asm/proto.h>
+#include <asm/sections.h>
+#include <asm/setup.h>
+
#include "cpu.h"
+static struct cpu_dev *this_cpu __cpuinitdata;
+
+#ifdef CONFIG_X86_64
+/* We need valid kernel segments for data and code in long mode too
+ * IRET will check the segment types kkeil 2000/10/28
+ * Also sysret mandates a special GDT layout
+ */
+/* The TLS descriptors are currently at a different place compared to i386.
+ Hopefully nobody expects them at a fixed place (Wine?) */
DEFINE_PER_CPU(struct gdt_page, gdt_page) = { .gdt = {
+ [GDT_ENTRY_KERNEL32_CS] = { { { 0x0000ffff, 0x00cf9b00 } } },
+ [GDT_ENTRY_KERNEL_CS] = { { { 0x0000ffff, 0x00af9b00 } } },
+ [GDT_ENTRY_KERNEL_DS] = { { { 0x0000ffff, 0x00cf9300 } } },
+ [GDT_ENTRY_DEFAULT_USER32_CS] = { { { 0x0000ffff, 0x00cffb00 } } },
+ [GDT_ENTRY_DEFAULT_USER_DS] = { { { 0x0000ffff, 0x00cff300 } } },
+ [GDT_ENTRY_DEFAULT_USER_CS] = { { { 0x0000ffff, 0x00affb00 } } },
+} };
+#else
+DEFINE_PER_CPU_PAGE_ALIGNED(struct gdt_page, gdt_page) = { .gdt = {
[GDT_ENTRY_KERNEL_CS] = { { { 0x0000ffff, 0x00cf9a00 } } },
[GDT_ENTRY_KERNEL_DS] = { { { 0x0000ffff, 0x00cf9200 } } },
[GDT_ENTRY_DEFAULT_USER_CS] = { { { 0x0000ffff, 0x00cffa00 } } },
@@ -56,34 +90,13 @@
[GDT_ENTRY_ESPFIX_SS] = { { { 0x00000000, 0x00c09200 } } },
[GDT_ENTRY_PERCPU] = { { { 0x00000000, 0x00000000 } } },
} };
+#endif
EXPORT_PER_CPU_SYMBOL_GPL(gdt_page);
-__u32 cleared_cpu_caps[NCAPINTS] __cpuinitdata;
-
+#ifdef CONFIG_X86_32
static int cachesize_override __cpuinitdata = -1;
static int disable_x86_serial_nr __cpuinitdata = 1;
-struct cpu_dev *cpu_devs[X86_VENDOR_NUM] = {};
-
-static void __cpuinit default_init(struct cpuinfo_x86 *c)
-{
- /* Not much we can do here... */
- /* Check if at least it has cpuid */
- if (c->cpuid_level == -1) {
- /* No cpuid. It must be an ancient CPU */
- if (c->x86 == 4)
- strcpy(c->x86_model_id, "486");
- else if (c->x86 == 3)
- strcpy(c->x86_model_id, "386");
- }
-}
-
-static struct cpu_dev __cpuinitdata default_cpu = {
- .c_init = default_init,
- .c_vendor = "Unknown",
-};
-static struct cpu_dev *this_cpu __cpuinitdata = &default_cpu;
-
static int __init cachesize_setup(char *str)
{
get_option(&str, &cachesize_override);
@@ -91,71 +104,91 @@
}
__setup("cachesize=", cachesize_setup);
-int __cpuinit get_model_name(struct cpuinfo_x86 *c)
+static int __init x86_fxsr_setup(char *s)
{
- unsigned int *v;
- char *p, *q;
-
- if (cpuid_eax(0x80000000) < 0x80000004)
- return 0;
-
- v = (unsigned int *) c->x86_model_id;
- cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]);
- cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]);
- cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]);
- c->x86_model_id[48] = 0;
-
- /* Intel chips right-justify this string for some dumb reason;
- undo that brain damage */
- p = q = &c->x86_model_id[0];
- while (*p == ' ')
- p++;
- if (p != q) {
- while (*p)
- *q++ = *p++;
- while (q <= &c->x86_model_id[48])
- *q++ = '\0'; /* Zero-pad the rest */
- }
-
+ setup_clear_cpu_cap(X86_FEATURE_FXSR);
+ setup_clear_cpu_cap(X86_FEATURE_XMM);
return 1;
}
+__setup("nofxsr", x86_fxsr_setup);
-
-void __cpuinit display_cacheinfo(struct cpuinfo_x86 *c)
+static int __init x86_sep_setup(char *s)
{
- unsigned int n, dummy, ecx, edx, l2size;
-
- n = cpuid_eax(0x80000000);
-
- if (n >= 0x80000005) {
- cpuid(0x80000005, &dummy, &dummy, &ecx, &edx);
- printk(KERN_INFO "CPU: L1 I Cache: %dK (%d bytes/line), D cache %dK (%d bytes/line)\n",
- edx>>24, edx&0xFF, ecx>>24, ecx&0xFF);
- c->x86_cache_size = (ecx>>24)+(edx>>24);
- }
-
- if (n < 0x80000006) /* Some chips just has a large L1. */
- return;
-
- ecx = cpuid_ecx(0x80000006);
- l2size = ecx >> 16;
-
- /* do processor-specific cache resizing */
- if (this_cpu->c_size_cache)
- l2size = this_cpu->c_size_cache(c, l2size);
-
- /* Allow user to override all this if necessary. */
- if (cachesize_override != -1)
- l2size = cachesize_override;
-
- if (l2size == 0)
- return; /* Again, no L2 cache is possible */
-
- c->x86_cache_size = l2size;
-
- printk(KERN_INFO "CPU: L2 Cache: %dK (%d bytes/line)\n",
- l2size, ecx & 0xFF);
+ setup_clear_cpu_cap(X86_FEATURE_SEP);
+ return 1;
}
+__setup("nosep", x86_sep_setup);
+
+/* Standard macro to see if a specific flag is changeable */
+static inline int flag_is_changeable_p(u32 flag)
+{
+ u32 f1, f2;
+
+ /*
+ * Cyrix and IDT cpus allow disabling of CPUID
+ * so the code below may return different results
+ * when it is executed before and after enabling
+ * the CPUID. Add "volatile" to not allow gcc to
+ * optimize the subsequent calls to this function.
+ */
+ asm volatile ("pushfl\n\t"
+ "pushfl\n\t"
+ "popl %0\n\t"
+ "movl %0,%1\n\t"
+ "xorl %2,%0\n\t"
+ "pushl %0\n\t"
+ "popfl\n\t"
+ "pushfl\n\t"
+ "popl %0\n\t"
+ "popfl\n\t"
+ : "=&r" (f1), "=&r" (f2)
+ : "ir" (flag));
+
+ return ((f1^f2) & flag) != 0;
+}
+
+/* Probe for the CPUID instruction */
+static int __cpuinit have_cpuid_p(void)
+{
+ return flag_is_changeable_p(X86_EFLAGS_ID);
+}
+
+static void __cpuinit squash_the_stupid_serial_number(struct cpuinfo_x86 *c)
+{
+ if (cpu_has(c, X86_FEATURE_PN) && disable_x86_serial_nr) {
+ /* Disable processor serial number */
+ unsigned long lo, hi;
+ rdmsr(MSR_IA32_BBL_CR_CTL, lo, hi);
+ lo |= 0x200000;
+ wrmsr(MSR_IA32_BBL_CR_CTL, lo, hi);
+ printk(KERN_NOTICE "CPU serial number disabled.\n");
+ clear_cpu_cap(c, X86_FEATURE_PN);
+
+ /* Disabling the serial number may affect the cpuid level */
+ c->cpuid_level = cpuid_eax(0);
+ }
+}
+
+static int __init x86_serial_nr_setup(char *s)
+{
+ disable_x86_serial_nr = 0;
+ return 1;
+}
+__setup("serialnumber", x86_serial_nr_setup);
+#else
+static inline int flag_is_changeable_p(u32 flag)
+{
+ return 1;
+}
+/* Probe for the CPUID instruction */
+static inline int have_cpuid_p(void)
+{
+ return 1;
+}
+static inline void squash_the_stupid_serial_number(struct cpuinfo_x86 *c)
+{
+}
+#endif
/*
* Naming convention should be: <Name> [(<Codename>)]
@@ -185,81 +218,210 @@
return NULL; /* Not found */
}
+__u32 cleared_cpu_caps[NCAPINTS] __cpuinitdata;
-static void __cpuinit get_cpu_vendor(struct cpuinfo_x86 *c, int early)
+/* Current gdt points %fs at the "master" per-cpu area: after this,
+ * it's on the real one. */
+void switch_to_new_gdt(void)
+{
+ struct desc_ptr gdt_descr;
+
+ gdt_descr.address = (long)get_cpu_gdt_table(smp_processor_id());
+ gdt_descr.size = GDT_SIZE - 1;
+ load_gdt(&gdt_descr);
+#ifdef CONFIG_X86_32
+ asm("mov %0, %%fs" : : "r" (__KERNEL_PERCPU) : "memory");
+#endif
+}
+
+static struct cpu_dev *cpu_devs[X86_VENDOR_NUM] = {};
+
+static void __cpuinit default_init(struct cpuinfo_x86 *c)
+{
+#ifdef CONFIG_X86_64
+ display_cacheinfo(c);
+#else
+ /* Not much we can do here... */
+ /* Check if at least it has cpuid */
+ if (c->cpuid_level == -1) {
+ /* No cpuid. It must be an ancient CPU */
+ if (c->x86 == 4)
+ strcpy(c->x86_model_id, "486");
+ else if (c->x86 == 3)
+ strcpy(c->x86_model_id, "386");
+ }
+#endif
+}
+
+static struct cpu_dev __cpuinitdata default_cpu = {
+ .c_init = default_init,
+ .c_vendor = "Unknown",
+ .c_x86_vendor = X86_VENDOR_UNKNOWN,
+};
+
+static void __cpuinit get_model_name(struct cpuinfo_x86 *c)
+{
+ unsigned int *v;
+ char *p, *q;
+
+ if (c->extended_cpuid_level < 0x80000004)
+ return;
+
+ v = (unsigned int *) c->x86_model_id;
+ cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]);
+ cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]);
+ cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]);
+ c->x86_model_id[48] = 0;
+
+ /* Intel chips right-justify this string for some dumb reason;
+ undo that brain damage */
+ p = q = &c->x86_model_id[0];
+ while (*p == ' ')
+ p++;
+ if (p != q) {
+ while (*p)
+ *q++ = *p++;
+ while (q <= &c->x86_model_id[48])
+ *q++ = '\0'; /* Zero-pad the rest */
+ }
+}
+
+void __cpuinit display_cacheinfo(struct cpuinfo_x86 *c)
+{
+ unsigned int n, dummy, ebx, ecx, edx, l2size;
+
+ n = c->extended_cpuid_level;
+
+ if (n >= 0x80000005) {
+ cpuid(0x80000005, &dummy, &ebx, &ecx, &edx);
+ printk(KERN_INFO "CPU: L1 I Cache: %dK (%d bytes/line), D cache %dK (%d bytes/line)\n",
+ edx>>24, edx&0xFF, ecx>>24, ecx&0xFF);
+ c->x86_cache_size = (ecx>>24) + (edx>>24);
+#ifdef CONFIG_X86_64
+ /* On K8 L1 TLB is inclusive, so don't count it */
+ c->x86_tlbsize = 0;
+#endif
+ }
+
+ if (n < 0x80000006) /* Some chips just has a large L1. */
+ return;
+
+ cpuid(0x80000006, &dummy, &ebx, &ecx, &edx);
+ l2size = ecx >> 16;
+
+#ifdef CONFIG_X86_64
+ c->x86_tlbsize += ((ebx >> 16) & 0xfff) + (ebx & 0xfff);
+#else
+ /* do processor-specific cache resizing */
+ if (this_cpu->c_size_cache)
+ l2size = this_cpu->c_size_cache(c, l2size);
+
+ /* Allow user to override all this if necessary. */
+ if (cachesize_override != -1)
+ l2size = cachesize_override;
+
+ if (l2size == 0)
+ return; /* Again, no L2 cache is possible */
+#endif
+
+ c->x86_cache_size = l2size;
+
+ printk(KERN_INFO "CPU: L2 Cache: %dK (%d bytes/line)\n",
+ l2size, ecx & 0xFF);
+}
+
+void __cpuinit detect_ht(struct cpuinfo_x86 *c)
+{
+#ifdef CONFIG_X86_HT
+ u32 eax, ebx, ecx, edx;
+ int index_msb, core_bits;
+
+ if (!cpu_has(c, X86_FEATURE_HT))
+ return;
+
+ if (cpu_has(c, X86_FEATURE_CMP_LEGACY))
+ goto out;
+
+ if (cpu_has(c, X86_FEATURE_XTOPOLOGY))
+ return;
+
+ cpuid(1, &eax, &ebx, &ecx, &edx);
+
+ smp_num_siblings = (ebx & 0xff0000) >> 16;
+
+ if (smp_num_siblings == 1) {
+ printk(KERN_INFO "CPU: Hyper-Threading is disabled\n");
+ } else if (smp_num_siblings > 1) {
+
+ if (smp_num_siblings > NR_CPUS) {
+ printk(KERN_WARNING "CPU: Unsupported number of siblings %d",
+ smp_num_siblings);
+ smp_num_siblings = 1;
+ return;
+ }
+
+ index_msb = get_count_order(smp_num_siblings);
+#ifdef CONFIG_X86_64
+ c->phys_proc_id = phys_pkg_id(index_msb);
+#else
+ c->phys_proc_id = phys_pkg_id(c->initial_apicid, index_msb);
+#endif
+
+ smp_num_siblings = smp_num_siblings / c->x86_max_cores;
+
+ index_msb = get_count_order(smp_num_siblings);
+
+ core_bits = get_count_order(c->x86_max_cores);
+
+#ifdef CONFIG_X86_64
+ c->cpu_core_id = phys_pkg_id(index_msb) &
+ ((1 << core_bits) - 1);
+#else
+ c->cpu_core_id = phys_pkg_id(c->initial_apicid, index_msb) &
+ ((1 << core_bits) - 1);
+#endif
+ }
+
+out:
+ if ((c->x86_max_cores * smp_num_siblings) > 1) {
+ printk(KERN_INFO "CPU: Physical Processor ID: %d\n",
+ c->phys_proc_id);
+ printk(KERN_INFO "CPU: Processor Core ID: %d\n",
+ c->cpu_core_id);
+ }
+#endif
+}
+
+static void __cpuinit get_cpu_vendor(struct cpuinfo_x86 *c)
{
char *v = c->x86_vendor_id;
int i;
static int printed;
for (i = 0; i < X86_VENDOR_NUM; i++) {
- if (cpu_devs[i]) {
- if (!strcmp(v, cpu_devs[i]->c_ident[0]) ||
- (cpu_devs[i]->c_ident[1] &&
- !strcmp(v, cpu_devs[i]->c_ident[1]))) {
- c->x86_vendor = i;
- if (!early)
- this_cpu = cpu_devs[i];
- return;
- }
+ if (!cpu_devs[i])
+ break;
+
+ if (!strcmp(v, cpu_devs[i]->c_ident[0]) ||
+ (cpu_devs[i]->c_ident[1] &&
+ !strcmp(v, cpu_devs[i]->c_ident[1]))) {
+ this_cpu = cpu_devs[i];
+ c->x86_vendor = this_cpu->c_x86_vendor;
+ return;
}
}
+
if (!printed) {
printed++;
- printk(KERN_ERR "CPU: Vendor unknown, using generic init.\n");
+ printk(KERN_ERR "CPU: vendor_id '%s' unknown, using generic init.\n", v);
printk(KERN_ERR "CPU: Your system may be unstable.\n");
}
+
c->x86_vendor = X86_VENDOR_UNKNOWN;
this_cpu = &default_cpu;
}
-
-static int __init x86_fxsr_setup(char *s)
-{
- setup_clear_cpu_cap(X86_FEATURE_FXSR);
- setup_clear_cpu_cap(X86_FEATURE_XMM);
- return 1;
-}
-__setup("nofxsr", x86_fxsr_setup);
-
-
-static int __init x86_sep_setup(char *s)
-{
- setup_clear_cpu_cap(X86_FEATURE_SEP);
- return 1;
-}
-__setup("nosep", x86_sep_setup);
-
-
-/* Standard macro to see if a specific flag is changeable */
-static inline int flag_is_changeable_p(u32 flag)
-{
- u32 f1, f2;
-
- asm("pushfl\n\t"
- "pushfl\n\t"
- "popl %0\n\t"
- "movl %0,%1\n\t"
- "xorl %2,%0\n\t"
- "pushl %0\n\t"
- "popfl\n\t"
- "pushfl\n\t"
- "popl %0\n\t"
- "popfl\n\t"
- : "=&r" (f1), "=&r" (f2)
- : "ir" (flag));
-
- return ((f1^f2) & flag) != 0;
-}
-
-
-/* Probe for the CPUID instruction */
-static int __cpuinit have_cpuid_p(void)
-{
- return flag_is_changeable_p(X86_EFLAGS_ID);
-}
-
-void __init cpu_detect(struct cpuinfo_x86 *c)
+void __cpuinit cpu_detect(struct cpuinfo_x86 *c)
{
/* Get vendor name */
cpuid(0x00000000, (unsigned int *)&c->cpuid_level,
@@ -268,48 +430,85 @@
(unsigned int *)&c->x86_vendor_id[4]);
c->x86 = 4;
+ /* Intel-defined flags: level 0x00000001 */
if (c->cpuid_level >= 0x00000001) {
u32 junk, tfms, cap0, misc;
cpuid(0x00000001, &tfms, &misc, &junk, &cap0);
- c->x86 = (tfms >> 8) & 15;
- c->x86_model = (tfms >> 4) & 15;
+ c->x86 = (tfms >> 8) & 0xf;
+ c->x86_model = (tfms >> 4) & 0xf;
+ c->x86_mask = tfms & 0xf;
if (c->x86 == 0xf)
c->x86 += (tfms >> 20) & 0xff;
if (c->x86 >= 0x6)
- c->x86_model += ((tfms >> 16) & 0xF) << 4;
- c->x86_mask = tfms & 15;
+ c->x86_model += ((tfms >> 16) & 0xf) << 4;
if (cap0 & (1<<19)) {
- c->x86_cache_alignment = ((misc >> 8) & 0xff) * 8;
c->x86_clflush_size = ((misc >> 8) & 0xff) * 8;
+ c->x86_cache_alignment = c->x86_clflush_size;
}
}
}
-static void __cpuinit early_get_cap(struct cpuinfo_x86 *c)
+
+static void __cpuinit get_cpu_cap(struct cpuinfo_x86 *c)
{
u32 tfms, xlvl;
- unsigned int ebx;
+ u32 ebx;
- memset(&c->x86_capability, 0, sizeof c->x86_capability);
- if (have_cpuid_p()) {
- /* Intel-defined flags: level 0x00000001 */
- if (c->cpuid_level >= 0x00000001) {
- u32 capability, excap;
- cpuid(0x00000001, &tfms, &ebx, &excap, &capability);
- c->x86_capability[0] = capability;
- c->x86_capability[4] = excap;
- }
-
- /* AMD-defined flags: level 0x80000001 */
- xlvl = cpuid_eax(0x80000000);
- if ((xlvl & 0xffff0000) == 0x80000000) {
- if (xlvl >= 0x80000001) {
- c->x86_capability[1] = cpuid_edx(0x80000001);
- c->x86_capability[6] = cpuid_ecx(0x80000001);
- }
- }
-
+ /* Intel-defined flags: level 0x00000001 */
+ if (c->cpuid_level >= 0x00000001) {
+ u32 capability, excap;
+ cpuid(0x00000001, &tfms, &ebx, &excap, &capability);
+ c->x86_capability[0] = capability;
+ c->x86_capability[4] = excap;
}
+ /* AMD-defined flags: level 0x80000001 */
+ xlvl = cpuid_eax(0x80000000);
+ c->extended_cpuid_level = xlvl;
+ if ((xlvl & 0xffff0000) == 0x80000000) {
+ if (xlvl >= 0x80000001) {
+ c->x86_capability[1] = cpuid_edx(0x80000001);
+ c->x86_capability[6] = cpuid_ecx(0x80000001);
+ }
+ }
+
+#ifdef CONFIG_X86_64
+ if (c->extended_cpuid_level >= 0x80000008) {
+ u32 eax = cpuid_eax(0x80000008);
+
+ c->x86_virt_bits = (eax >> 8) & 0xff;
+ c->x86_phys_bits = eax & 0xff;
+ }
+#endif
+
+ if (c->extended_cpuid_level >= 0x80000007)
+ c->x86_power = cpuid_edx(0x80000007);
+
+}
+
+static void __cpuinit identify_cpu_without_cpuid(struct cpuinfo_x86 *c)
+{
+#ifdef CONFIG_X86_32
+ int i;
+
+ /*
+ * First of all, decide if this is a 486 or higher
+ * It's a 486 if we can modify the AC flag
+ */
+ if (flag_is_changeable_p(X86_EFLAGS_AC))
+ c->x86 = 4;
+ else
+ c->x86 = 3;
+
+ for (i = 0; i < X86_VENDOR_NUM; i++)
+ if (cpu_devs[i] && cpu_devs[i]->c_identify) {
+ c->x86_vendor_id[0] = 0;
+ cpu_devs[i]->c_identify(c);
+ if (c->x86_vendor_id[0]) {
+ get_cpu_vendor(c);
+ break;
+ }
+ }
+#endif
}
/*
@@ -321,25 +520,61 @@
* WARNING: this function is only called on the BP. Don't add code here
* that is supposed to run on all CPUs.
*/
-static void __init early_cpu_detect(void)
+static void __init early_identify_cpu(struct cpuinfo_x86 *c)
{
- struct cpuinfo_x86 *c = &boot_cpu_data;
-
- c->x86_cache_alignment = 32;
+#ifdef CONFIG_X86_64
+ c->x86_clflush_size = 64;
+#else
c->x86_clflush_size = 32;
+#endif
+ c->x86_cache_alignment = c->x86_clflush_size;
+ memset(&c->x86_capability, 0, sizeof c->x86_capability);
+ c->extended_cpuid_level = 0;
+
+ if (!have_cpuid_p())
+ identify_cpu_without_cpuid(c);
+
+ /* cyrix could have cpuid enabled via c_identify()*/
if (!have_cpuid_p())
return;
cpu_detect(c);
- get_cpu_vendor(c, 1);
+ get_cpu_vendor(c);
- early_get_cap(c);
+ get_cpu_cap(c);
- if (c->x86_vendor != X86_VENDOR_UNKNOWN &&
- cpu_devs[c->x86_vendor]->c_early_init)
- cpu_devs[c->x86_vendor]->c_early_init(c);
+ if (this_cpu->c_early_init)
+ this_cpu->c_early_init(c);
+
+ validate_pat_support(c);
+}
+
+void __init early_cpu_init(void)
+{
+ struct cpu_dev **cdev;
+ int count = 0;
+
+ printk("KERNEL supported cpus:\n");
+ for (cdev = __x86_cpu_dev_start; cdev < __x86_cpu_dev_end; cdev++) {
+ struct cpu_dev *cpudev = *cdev;
+ unsigned int j;
+
+ if (count >= X86_VENDOR_NUM)
+ break;
+ cpu_devs[count] = cpudev;
+ count++;
+
+ for (j = 0; j < 2; j++) {
+ if (!cpudev->c_ident[j])
+ continue;
+ printk(" %s %s\n", cpudev->c_vendor,
+ cpudev->c_ident[j]);
+ }
+ }
+
+ early_identify_cpu(&boot_cpu_data);
}
/*
@@ -357,87 +592,42 @@
static void __cpuinit generic_identify(struct cpuinfo_x86 *c)
{
- u32 tfms, xlvl;
- unsigned int ebx;
+ c->extended_cpuid_level = 0;
- if (have_cpuid_p()) {
- /* Get vendor name */
- cpuid(0x00000000, (unsigned int *)&c->cpuid_level,
- (unsigned int *)&c->x86_vendor_id[0],
- (unsigned int *)&c->x86_vendor_id[8],
- (unsigned int *)&c->x86_vendor_id[4]);
+ if (!have_cpuid_p())
+ identify_cpu_without_cpuid(c);
- get_cpu_vendor(c, 0);
- /* Initialize the standard set of capabilities */
- /* Note that the vendor-specific code below might override */
- /* Intel-defined flags: level 0x00000001 */
- if (c->cpuid_level >= 0x00000001) {
- u32 capability, excap;
- cpuid(0x00000001, &tfms, &ebx, &excap, &capability);
- c->x86_capability[0] = capability;
- c->x86_capability[4] = excap;
- c->x86 = (tfms >> 8) & 15;
- c->x86_model = (tfms >> 4) & 15;
- if (c->x86 == 0xf)
- c->x86 += (tfms >> 20) & 0xff;
- if (c->x86 >= 0x6)
- c->x86_model += ((tfms >> 16) & 0xF) << 4;
- c->x86_mask = tfms & 15;
- c->initial_apicid = (ebx >> 24) & 0xFF;
-#ifdef CONFIG_X86_HT
- c->apicid = phys_pkg_id(c->initial_apicid, 0);
- c->phys_proc_id = c->initial_apicid;
-#else
- c->apicid = c->initial_apicid;
+ /* cyrix could have cpuid enabled via c_identify()*/
+ if (!have_cpuid_p())
+ return;
+
+ cpu_detect(c);
+
+ get_cpu_vendor(c);
+
+ get_cpu_cap(c);
+
+ if (c->cpuid_level >= 0x00000001) {
+ c->initial_apicid = (cpuid_ebx(1) >> 24) & 0xFF;
+#ifdef CONFIG_X86_32
+# ifdef CONFIG_X86_HT
+ c->apicid = phys_pkg_id(c->initial_apicid, 0);
+# else
+ c->apicid = c->initial_apicid;
+# endif
#endif
- if (test_cpu_cap(c, X86_FEATURE_CLFLSH))
- c->x86_clflush_size = ((ebx >> 8) & 0xff) * 8;
- } else {
- /* Have CPUID level 0 only - unheard of */
- c->x86 = 4;
- }
- /* AMD-defined flags: level 0x80000001 */
- xlvl = cpuid_eax(0x80000000);
- if ((xlvl & 0xffff0000) == 0x80000000) {
- if (xlvl >= 0x80000001) {
- c->x86_capability[1] = cpuid_edx(0x80000001);
- c->x86_capability[6] = cpuid_ecx(0x80000001);
- }
- if (xlvl >= 0x80000004)
- get_model_name(c); /* Default name */
- }
-
- init_scattered_cpuid_features(c);
- detect_nopl(c);
+#ifdef CONFIG_X86_HT
+ c->phys_proc_id = c->initial_apicid;
+#endif
}
+
+ get_model_name(c); /* Default name */
+
+ init_scattered_cpuid_features(c);
+ detect_nopl(c);
}
-static void __cpuinit squash_the_stupid_serial_number(struct cpuinfo_x86 *c)
-{
- if (cpu_has(c, X86_FEATURE_PN) && disable_x86_serial_nr) {
- /* Disable processor serial number */
- unsigned long lo, hi;
- rdmsr(MSR_IA32_BBL_CR_CTL, lo, hi);
- lo |= 0x200000;
- wrmsr(MSR_IA32_BBL_CR_CTL, lo, hi);
- printk(KERN_NOTICE "CPU serial number disabled.\n");
- clear_cpu_cap(c, X86_FEATURE_PN);
-
- /* Disabling the serial number may affect the cpuid level */
- c->cpuid_level = cpuid_eax(0);
- }
-}
-
-static int __init x86_serial_nr_setup(char *s)
-{
- disable_x86_serial_nr = 0;
- return 1;
-}
-__setup("serialnumber", x86_serial_nr_setup);
-
-
-
/*
* This does the hard work of actually picking apart the CPU stuff...
*/
@@ -448,30 +638,29 @@
c->loops_per_jiffy = loops_per_jiffy;
c->x86_cache_size = -1;
c->x86_vendor = X86_VENDOR_UNKNOWN;
- c->cpuid_level = -1; /* CPUID not detected */
c->x86_model = c->x86_mask = 0; /* So far unknown... */
c->x86_vendor_id[0] = '\0'; /* Unset */
c->x86_model_id[0] = '\0'; /* Unset */
c->x86_max_cores = 1;
+ c->x86_coreid_bits = 0;
+#ifdef CONFIG_X86_64
+ c->x86_clflush_size = 64;
+#else
+ c->cpuid_level = -1; /* CPUID not detected */
c->x86_clflush_size = 32;
+#endif
+ c->x86_cache_alignment = c->x86_clflush_size;
memset(&c->x86_capability, 0, sizeof c->x86_capability);
- if (!have_cpuid_p()) {
- /*
- * First of all, decide if this is a 486 or higher
- * It's a 486 if we can modify the AC flag
- */
- if (flag_is_changeable_p(X86_EFLAGS_AC))
- c->x86 = 4;
- else
- c->x86 = 3;
- }
-
generic_identify(c);
if (this_cpu->c_identify)
this_cpu->c_identify(c);
+#ifdef CONFIG_X86_64
+ c->apicid = phys_pkg_id(0);
+#endif
+
/*
* Vendor-specific initialization. In this section we
* canonicalize the feature flags, meaning if there are
@@ -505,6 +694,10 @@
c->x86, c->x86_model);
}
+#ifdef CONFIG_X86_64
+ detect_ht(c);
+#endif
+
/*
* On SMP, boot_cpu_data holds the common feature set between
* all CPUs; so make sure that we indicate which features are
@@ -513,7 +706,7 @@
*/
if (c != &boot_cpu_data) {
/* AND the already accumulated flags with these */
- for (i = 0 ; i < NCAPINTS ; i++)
+ for (i = 0; i < NCAPINTS; i++)
boot_cpu_data.x86_capability[i] &= c->x86_capability[i];
}
@@ -521,72 +714,91 @@
for (i = 0; i < NCAPINTS; i++)
c->x86_capability[i] &= ~cleared_cpu_caps[i];
+#ifdef CONFIG_X86_MCE
/* Init Machine Check Exception if available. */
mcheck_init(c);
+#endif
select_idle_routine(c);
+
+#if defined(CONFIG_NUMA) && defined(CONFIG_X86_64)
+ numa_add_cpu(smp_processor_id());
+#endif
}
+#ifdef CONFIG_X86_64
+static void vgetcpu_set_mode(void)
+{
+ if (cpu_has(&boot_cpu_data, X86_FEATURE_RDTSCP))
+ vgetcpu_mode = VGETCPU_RDTSCP;
+ else
+ vgetcpu_mode = VGETCPU_LSL;
+}
+#endif
+
void __init identify_boot_cpu(void)
{
identify_cpu(&boot_cpu_data);
+#ifdef CONFIG_X86_32
sysenter_setup();
enable_sep_cpu();
+#else
+ vgetcpu_set_mode();
+#endif
}
void __cpuinit identify_secondary_cpu(struct cpuinfo_x86 *c)
{
BUG_ON(c == &boot_cpu_data);
identify_cpu(c);
+#ifdef CONFIG_X86_32
enable_sep_cpu();
+#endif
mtrr_ap_init();
}
-#ifdef CONFIG_X86_HT
-void __cpuinit detect_ht(struct cpuinfo_x86 *c)
+struct msr_range {
+ unsigned min;
+ unsigned max;
+};
+
+static struct msr_range msr_range_array[] __cpuinitdata = {
+ { 0x00000000, 0x00000418},
+ { 0xc0000000, 0xc000040b},
+ { 0xc0010000, 0xc0010142},
+ { 0xc0011000, 0xc001103b},
+};
+
+static void __cpuinit print_cpu_msr(void)
{
- u32 eax, ebx, ecx, edx;
- int index_msb, core_bits;
+ unsigned index;
+ u64 val;
+ int i;
+ unsigned index_min, index_max;
- cpuid(1, &eax, &ebx, &ecx, &edx);
-
- if (!cpu_has(c, X86_FEATURE_HT) || cpu_has(c, X86_FEATURE_CMP_LEGACY))
- return;
-
- smp_num_siblings = (ebx & 0xff0000) >> 16;
-
- if (smp_num_siblings == 1) {
- printk(KERN_INFO "CPU: Hyper-Threading is disabled\n");
- } else if (smp_num_siblings > 1) {
-
- if (smp_num_siblings > NR_CPUS) {
- printk(KERN_WARNING "CPU: Unsupported number of the "
- "siblings %d", smp_num_siblings);
- smp_num_siblings = 1;
- return;
+ for (i = 0; i < ARRAY_SIZE(msr_range_array); i++) {
+ index_min = msr_range_array[i].min;
+ index_max = msr_range_array[i].max;
+ for (index = index_min; index < index_max; index++) {
+ if (rdmsrl_amd_safe(index, &val))
+ continue;
+ printk(KERN_INFO " MSR%08x: %016llx\n", index, val);
}
-
- index_msb = get_count_order(smp_num_siblings);
- c->phys_proc_id = phys_pkg_id(c->initial_apicid, index_msb);
-
- printk(KERN_INFO "CPU: Physical Processor ID: %d\n",
- c->phys_proc_id);
-
- smp_num_siblings = smp_num_siblings / c->x86_max_cores;
-
- index_msb = get_count_order(smp_num_siblings) ;
-
- core_bits = get_count_order(c->x86_max_cores);
-
- c->cpu_core_id = phys_pkg_id(c->initial_apicid, index_msb) &
- ((1 << core_bits) - 1);
-
- if (c->x86_max_cores > 1)
- printk(KERN_INFO "CPU: Processor Core ID: %d\n",
- c->cpu_core_id);
}
}
-#endif
+
+static int show_msr __cpuinitdata;
+static __init int setup_show_msr(char *arg)
+{
+ int num;
+
+ get_option(&arg, &num);
+
+ if (num > 0)
+ show_msr = num;
+ return 1;
+}
+__setup("show_msr=", setup_show_msr);
static __init int setup_noclflush(char *arg)
{
@@ -604,18 +816,26 @@
else if (c->cpuid_level >= 0)
vendor = c->x86_vendor_id;
- if (vendor && strncmp(c->x86_model_id, vendor, strlen(vendor)))
- printk("%s ", vendor);
+ if (vendor && !strstr(c->x86_model_id, vendor))
+ printk(KERN_CONT "%s ", vendor);
- if (!c->x86_model_id[0])
- printk("%d86", c->x86);
+ if (c->x86_model_id[0])
+ printk(KERN_CONT "%s", c->x86_model_id);
else
- printk("%s", c->x86_model_id);
+ printk(KERN_CONT "%d86", c->x86);
if (c->x86_mask || c->cpuid_level >= 0)
- printk(" stepping %02x\n", c->x86_mask);
+ printk(KERN_CONT " stepping %02x\n", c->x86_mask);
else
- printk("\n");
+ printk(KERN_CONT "\n");
+
+#ifdef CONFIG_SMP
+ if (c->cpu_index < show_msr)
+ print_cpu_msr();
+#else
+ if (show_msr)
+ print_cpu_msr();
+#endif
}
static __init int setup_disablecpuid(char *arg)
@@ -631,19 +851,89 @@
cpumask_t cpu_initialized __cpuinitdata = CPU_MASK_NONE;
-void __init early_cpu_init(void)
+#ifdef CONFIG_X86_64
+struct x8664_pda **_cpu_pda __read_mostly;
+EXPORT_SYMBOL(_cpu_pda);
+
+struct desc_ptr idt_descr = { 256 * 16 - 1, (unsigned long) idt_table };
+
+char boot_cpu_stack[IRQSTACKSIZE] __page_aligned_bss;
+
+void __cpuinit pda_init(int cpu)
{
- struct cpu_vendor_dev *cvdev;
+ struct x8664_pda *pda = cpu_pda(cpu);
- for (cvdev = __x86cpuvendor_start ;
- cvdev < __x86cpuvendor_end ;
- cvdev++)
- cpu_devs[cvdev->vendor] = cvdev->cpu_dev;
+ /* Setup up data that may be needed in __get_free_pages early */
+ loadsegment(fs, 0);
+ loadsegment(gs, 0);
+ /* Memory clobbers used to order PDA accessed */
+ mb();
+ wrmsrl(MSR_GS_BASE, pda);
+ mb();
- early_cpu_detect();
- validate_pat_support(&boot_cpu_data);
+ pda->cpunumber = cpu;
+ pda->irqcount = -1;
+ pda->kernelstack = (unsigned long)stack_thread_info() -
+ PDA_STACKOFFSET + THREAD_SIZE;
+ pda->active_mm = &init_mm;
+ pda->mmu_state = 0;
+
+ if (cpu == 0) {
+ /* others are initialized in smpboot.c */
+ pda->pcurrent = &init_task;
+ pda->irqstackptr = boot_cpu_stack;
+ pda->irqstackptr += IRQSTACKSIZE - 64;
+ } else {
+ if (!pda->irqstackptr) {
+ pda->irqstackptr = (char *)
+ __get_free_pages(GFP_ATOMIC, IRQSTACK_ORDER);
+ if (!pda->irqstackptr)
+ panic("cannot allocate irqstack for cpu %d",
+ cpu);
+ pda->irqstackptr += IRQSTACKSIZE - 64;
+ }
+
+ if (pda->nodenumber == 0 && cpu_to_node(cpu) != NUMA_NO_NODE)
+ pda->nodenumber = cpu_to_node(cpu);
+ }
}
+char boot_exception_stacks[(N_EXCEPTION_STACKS - 1) * EXCEPTION_STKSZ +
+ DEBUG_STKSZ] __page_aligned_bss;
+
+extern asmlinkage void ignore_sysret(void);
+
+/* May not be marked __init: used by software suspend */
+void syscall_init(void)
+{
+ /*
+ * LSTAR and STAR live in a bit strange symbiosis.
+ * They both write to the same internal register. STAR allows to
+ * set CS/DS but only a 32bit target. LSTAR sets the 64bit rip.
+ */
+ wrmsrl(MSR_STAR, ((u64)__USER32_CS)<<48 | ((u64)__KERNEL_CS)<<32);
+ wrmsrl(MSR_LSTAR, system_call);
+ wrmsrl(MSR_CSTAR, ignore_sysret);
+
+#ifdef CONFIG_IA32_EMULATION
+ syscall32_cpu_init();
+#endif
+
+ /* Flags to clear on syscall */
+ wrmsrl(MSR_SYSCALL_MASK,
+ X86_EFLAGS_TF|X86_EFLAGS_DF|X86_EFLAGS_IF|X86_EFLAGS_IOPL);
+}
+
+unsigned long kernel_eflags;
+
+/*
+ * Copies of the original ist values from the tss are only accessed during
+ * debugging, no special alignment required.
+ */
+DEFINE_PER_CPU(struct orig_ist, orig_ist);
+
+#else
+
/* Make sure %fs is initialized properly in idle threads */
struct pt_regs * __cpuinit idle_regs(struct pt_regs *regs)
{
@@ -651,25 +941,136 @@
regs->fs = __KERNEL_PERCPU;
return regs;
}
-
-/* Current gdt points %fs at the "master" per-cpu area: after this,
- * it's on the real one. */
-void switch_to_new_gdt(void)
-{
- struct desc_ptr gdt_descr;
-
- gdt_descr.address = (long)get_cpu_gdt_table(smp_processor_id());
- gdt_descr.size = GDT_SIZE - 1;
- load_gdt(&gdt_descr);
- asm("mov %0, %%fs" : : "r" (__KERNEL_PERCPU) : "memory");
-}
+#endif
/*
* cpu_init() initializes state that is per-CPU. Some data is already
* initialized (naturally) in the bootstrap process, such as the GDT
* and IDT. We reload them nevertheless, this function acts as a
* 'CPU state barrier', nothing should get across.
+ * A lot of state is already set up in PDA init for 64 bit
*/
+#ifdef CONFIG_X86_64
+void __cpuinit cpu_init(void)
+{
+ int cpu = stack_smp_processor_id();
+ struct tss_struct *t = &per_cpu(init_tss, cpu);
+ struct orig_ist *orig_ist = &per_cpu(orig_ist, cpu);
+ unsigned long v;
+ char *estacks = NULL;
+ struct task_struct *me;
+ int i;
+
+ /* CPU 0 is initialised in head64.c */
+ if (cpu != 0)
+ pda_init(cpu);
+ else
+ estacks = boot_exception_stacks;
+
+ me = current;
+
+ if (cpu_test_and_set(cpu, cpu_initialized))
+ panic("CPU#%d already initialized!\n", cpu);
+
+ printk(KERN_INFO "Initializing CPU#%d\n", cpu);
+
+ clear_in_cr4(X86_CR4_VME|X86_CR4_PVI|X86_CR4_TSD|X86_CR4_DE);
+
+ /*
+ * Initialize the per-CPU GDT with the boot GDT,
+ * and set up the GDT descriptor:
+ */
+
+ switch_to_new_gdt();
+ load_idt((const struct desc_ptr *)&idt_descr);
+
+ memset(me->thread.tls_array, 0, GDT_ENTRY_TLS_ENTRIES * 8);
+ syscall_init();
+
+ wrmsrl(MSR_FS_BASE, 0);
+ wrmsrl(MSR_KERNEL_GS_BASE, 0);
+ barrier();
+
+ check_efer();
+ if (cpu != 0 && x2apic)
+ enable_x2apic();
+
+ /*
+ * set up and load the per-CPU TSS
+ */
+ if (!orig_ist->ist[0]) {
+ static const unsigned int order[N_EXCEPTION_STACKS] = {
+ [0 ... N_EXCEPTION_STACKS - 1] = EXCEPTION_STACK_ORDER,
+ [DEBUG_STACK - 1] = DEBUG_STACK_ORDER
+ };
+ for (v = 0; v < N_EXCEPTION_STACKS; v++) {
+ if (cpu) {
+ estacks = (char *)__get_free_pages(GFP_ATOMIC, order[v]);
+ if (!estacks)
+ panic("Cannot allocate exception "
+ "stack %ld %d\n", v, cpu);
+ }
+ estacks += PAGE_SIZE << order[v];
+ orig_ist->ist[v] = t->x86_tss.ist[v] =
+ (unsigned long)estacks;
+ }
+ }
+
+ t->x86_tss.io_bitmap_base = offsetof(struct tss_struct, io_bitmap);
+ /*
+ * <= is required because the CPU will access up to
+ * 8 bits beyond the end of the IO permission bitmap.
+ */
+ for (i = 0; i <= IO_BITMAP_LONGS; i++)
+ t->io_bitmap[i] = ~0UL;
+
+ atomic_inc(&init_mm.mm_count);
+ me->active_mm = &init_mm;
+ if (me->mm)
+ BUG();
+ enter_lazy_tlb(&init_mm, me);
+
+ load_sp0(t, ¤t->thread);
+ set_tss_desc(cpu, t);
+ load_TR_desc();
+ load_LDT(&init_mm.context);
+
+#ifdef CONFIG_KGDB
+ /*
+ * If the kgdb is connected no debug regs should be altered. This
+ * is only applicable when KGDB and a KGDB I/O module are built
+ * into the kernel and you are using early debugging with
+ * kgdbwait. KGDB will control the kernel HW breakpoint registers.
+ */
+ if (kgdb_connected && arch_kgdb_ops.correct_hw_break)
+ arch_kgdb_ops.correct_hw_break();
+ else {
+#endif
+ /*
+ * Clear all 6 debug registers:
+ */
+
+ set_debugreg(0UL, 0);
+ set_debugreg(0UL, 1);
+ set_debugreg(0UL, 2);
+ set_debugreg(0UL, 3);
+ set_debugreg(0UL, 6);
+ set_debugreg(0UL, 7);
+#ifdef CONFIG_KGDB
+ /* If the kgdb is connected no debug regs should be altered. */
+ }
+#endif
+
+ fpu_init();
+
+ raw_local_save_flags(kernel_eflags);
+
+ if (is_uv_system())
+ uv_cpu_init();
+}
+
+#else
+
void __cpuinit cpu_init(void)
{
int cpu = smp_processor_id();
@@ -723,19 +1124,21 @@
/*
* Force FPU initialization:
*/
- current_thread_info()->status = 0;
+ if (cpu_has_xsave)
+ current_thread_info()->status = TS_XSAVE;
+ else
+ current_thread_info()->status = 0;
clear_used_math();
mxcsr_feature_mask_init();
+
+ /*
+ * Boot processor to setup the FP and extended state context info.
+ */
+ if (!smp_processor_id())
+ init_thread_xstate();
+
+ xsave_init();
}
-#ifdef CONFIG_HOTPLUG_CPU
-void __cpuinit cpu_uninit(void)
-{
- int cpu = raw_smp_processor_id();
- cpu_clear(cpu, cpu_initialized);
- /* lazy TLB state */
- per_cpu(cpu_tlbstate, cpu).state = 0;
- per_cpu(cpu_tlbstate, cpu).active_mm = &init_mm;
-}
#endif
diff --git a/arch/x86/kernel/cpu/common_64.c b/arch/x86/kernel/cpu/common_64.c
deleted file mode 100644
index 305b465..0000000
--- a/arch/x86/kernel/cpu/common_64.c
+++ /dev/null
@@ -1,763 +0,0 @@
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/bootmem.h>
-#include <linux/bitops.h>
-#include <linux/module.h>
-#include <linux/kgdb.h>
-#include <linux/topology.h>
-#include <linux/delay.h>
-#include <linux/smp.h>
-#include <linux/percpu.h>
-#include <asm/i387.h>
-#include <asm/msr.h>
-#include <asm/io.h>
-#include <asm/linkage.h>
-#include <asm/mmu_context.h>
-#include <asm/mtrr.h>
-#include <asm/mce.h>
-#include <asm/pat.h>
-#include <asm/asm.h>
-#include <asm/numa.h>
-#ifdef CONFIG_X86_LOCAL_APIC
-#include <asm/mpspec.h>
-#include <asm/apic.h>
-#include <mach_apic.h>
-#endif
-#include <asm/pda.h>
-#include <asm/pgtable.h>
-#include <asm/processor.h>
-#include <asm/desc.h>
-#include <asm/atomic.h>
-#include <asm/proto.h>
-#include <asm/sections.h>
-#include <asm/setup.h>
-#include <asm/genapic.h>
-
-#include "cpu.h"
-
-/* We need valid kernel segments for data and code in long mode too
- * IRET will check the segment types kkeil 2000/10/28
- * Also sysret mandates a special GDT layout
- */
-/* The TLS descriptors are currently at a different place compared to i386.
- Hopefully nobody expects them at a fixed place (Wine?) */
-DEFINE_PER_CPU(struct gdt_page, gdt_page) = { .gdt = {
- [GDT_ENTRY_KERNEL32_CS] = { { { 0x0000ffff, 0x00cf9b00 } } },
- [GDT_ENTRY_KERNEL_CS] = { { { 0x0000ffff, 0x00af9b00 } } },
- [GDT_ENTRY_KERNEL_DS] = { { { 0x0000ffff, 0x00cf9300 } } },
- [GDT_ENTRY_DEFAULT_USER32_CS] = { { { 0x0000ffff, 0x00cffb00 } } },
- [GDT_ENTRY_DEFAULT_USER_DS] = { { { 0x0000ffff, 0x00cff300 } } },
- [GDT_ENTRY_DEFAULT_USER_CS] = { { { 0x0000ffff, 0x00affb00 } } },
-} };
-EXPORT_PER_CPU_SYMBOL_GPL(gdt_page);
-
-__u32 cleared_cpu_caps[NCAPINTS] __cpuinitdata;
-
-/* Current gdt points %fs at the "master" per-cpu area: after this,
- * it's on the real one. */
-void switch_to_new_gdt(void)
-{
- struct desc_ptr gdt_descr;
-
- gdt_descr.address = (long)get_cpu_gdt_table(smp_processor_id());
- gdt_descr.size = GDT_SIZE - 1;
- load_gdt(&gdt_descr);
-}
-
-struct cpu_dev *cpu_devs[X86_VENDOR_NUM] = {};
-
-static void __cpuinit default_init(struct cpuinfo_x86 *c)
-{
- display_cacheinfo(c);
-}
-
-static struct cpu_dev __cpuinitdata default_cpu = {
- .c_init = default_init,
- .c_vendor = "Unknown",
-};
-static struct cpu_dev *this_cpu __cpuinitdata = &default_cpu;
-
-int __cpuinit get_model_name(struct cpuinfo_x86 *c)
-{
- unsigned int *v;
-
- if (c->extended_cpuid_level < 0x80000004)
- return 0;
-
- v = (unsigned int *) c->x86_model_id;
- cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]);
- cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]);
- cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]);
- c->x86_model_id[48] = 0;
- return 1;
-}
-
-
-void __cpuinit display_cacheinfo(struct cpuinfo_x86 *c)
-{
- unsigned int n, dummy, ebx, ecx, edx;
-
- n = c->extended_cpuid_level;
-
- if (n >= 0x80000005) {
- cpuid(0x80000005, &dummy, &ebx, &ecx, &edx);
- printk(KERN_INFO "CPU: L1 I Cache: %dK (%d bytes/line), "
- "D cache %dK (%d bytes/line)\n",
- edx>>24, edx&0xFF, ecx>>24, ecx&0xFF);
- c->x86_cache_size = (ecx>>24) + (edx>>24);
- /* On K8 L1 TLB is inclusive, so don't count it */
- c->x86_tlbsize = 0;
- }
-
- if (n >= 0x80000006) {
- cpuid(0x80000006, &dummy, &ebx, &ecx, &edx);
- ecx = cpuid_ecx(0x80000006);
- c->x86_cache_size = ecx >> 16;
- c->x86_tlbsize += ((ebx >> 16) & 0xfff) + (ebx & 0xfff);
-
- printk(KERN_INFO "CPU: L2 Cache: %dK (%d bytes/line)\n",
- c->x86_cache_size, ecx & 0xFF);
- }
-}
-
-void __cpuinit detect_ht(struct cpuinfo_x86 *c)
-{
-#ifdef CONFIG_SMP
- u32 eax, ebx, ecx, edx;
- int index_msb, core_bits;
-
- cpuid(1, &eax, &ebx, &ecx, &edx);
-
-
- if (!cpu_has(c, X86_FEATURE_HT))
- return;
- if (cpu_has(c, X86_FEATURE_CMP_LEGACY))
- goto out;
-
- smp_num_siblings = (ebx & 0xff0000) >> 16;
-
- if (smp_num_siblings == 1) {
- printk(KERN_INFO "CPU: Hyper-Threading is disabled\n");
- } else if (smp_num_siblings > 1) {
-
- if (smp_num_siblings > NR_CPUS) {
- printk(KERN_WARNING "CPU: Unsupported number of "
- "siblings %d", smp_num_siblings);
- smp_num_siblings = 1;
- return;
- }
-
- index_msb = get_count_order(smp_num_siblings);
- c->phys_proc_id = phys_pkg_id(index_msb);
-
- smp_num_siblings = smp_num_siblings / c->x86_max_cores;
-
- index_msb = get_count_order(smp_num_siblings);
-
- core_bits = get_count_order(c->x86_max_cores);
-
- c->cpu_core_id = phys_pkg_id(index_msb) &
- ((1 << core_bits) - 1);
- }
-out:
- if ((c->x86_max_cores * smp_num_siblings) > 1) {
- printk(KERN_INFO "CPU: Physical Processor ID: %d\n",
- c->phys_proc_id);
- printk(KERN_INFO "CPU: Processor Core ID: %d\n",
- c->cpu_core_id);
- }
-
-#endif
-}
-
-static void __cpuinit get_cpu_vendor(struct cpuinfo_x86 *c)
-{
- char *v = c->x86_vendor_id;
- int i;
- static int printed;
-
- for (i = 0; i < X86_VENDOR_NUM; i++) {
- if (cpu_devs[i]) {
- if (!strcmp(v, cpu_devs[i]->c_ident[0]) ||
- (cpu_devs[i]->c_ident[1] &&
- !strcmp(v, cpu_devs[i]->c_ident[1]))) {
- c->x86_vendor = i;
- this_cpu = cpu_devs[i];
- return;
- }
- }
- }
- if (!printed) {
- printed++;
- printk(KERN_ERR "CPU: Vendor unknown, using generic init.\n");
- printk(KERN_ERR "CPU: Your system may be unstable.\n");
- }
- c->x86_vendor = X86_VENDOR_UNKNOWN;
-}
-
-static void __init early_cpu_support_print(void)
-{
- int i,j;
- struct cpu_dev *cpu_devx;
-
- printk("KERNEL supported cpus:\n");
- for (i = 0; i < X86_VENDOR_NUM; i++) {
- cpu_devx = cpu_devs[i];
- if (!cpu_devx)
- continue;
- for (j = 0; j < 2; j++) {
- if (!cpu_devx->c_ident[j])
- continue;
- printk(" %s %s\n", cpu_devx->c_vendor,
- cpu_devx->c_ident[j]);
- }
- }
-}
-
-/*
- * The NOPL instruction is supposed to exist on all CPUs with
- * family >= 6, unfortunately, that's not true in practice because
- * of early VIA chips and (more importantly) broken virtualizers that
- * are not easy to detect. Hence, probe for it based on first
- * principles.
- *
- * Note: no 64-bit chip is known to lack these, but put the code here
- * for consistency with 32 bits, and to make it utterly trivial to
- * diagnose the problem should it ever surface.
- */
-static void __cpuinit detect_nopl(struct cpuinfo_x86 *c)
-{
- const u32 nopl_signature = 0x888c53b1; /* Random number */
- u32 has_nopl = nopl_signature;
-
- clear_cpu_cap(c, X86_FEATURE_NOPL);
- if (c->x86 >= 6) {
- asm volatile("\n"
- "1: .byte 0x0f,0x1f,0xc0\n" /* nopl %eax */
- "2:\n"
- " .section .fixup,\"ax\"\n"
- "3: xor %0,%0\n"
- " jmp 2b\n"
- " .previous\n"
- _ASM_EXTABLE(1b,3b)
- : "+a" (has_nopl));
-
- if (has_nopl == nopl_signature)
- set_cpu_cap(c, X86_FEATURE_NOPL);
- }
-}
-
-static void __cpuinit early_identify_cpu(struct cpuinfo_x86 *c);
-
-void __init early_cpu_init(void)
-{
- struct cpu_vendor_dev *cvdev;
-
- for (cvdev = __x86cpuvendor_start ;
- cvdev < __x86cpuvendor_end ;
- cvdev++)
- cpu_devs[cvdev->vendor] = cvdev->cpu_dev;
- early_cpu_support_print();
- early_identify_cpu(&boot_cpu_data);
-}
-
-/* Do some early cpuid on the boot CPU to get some parameter that are
- needed before check_bugs. Everything advanced is in identify_cpu
- below. */
-static void __cpuinit early_identify_cpu(struct cpuinfo_x86 *c)
-{
- u32 tfms, xlvl;
-
- c->loops_per_jiffy = loops_per_jiffy;
- c->x86_cache_size = -1;
- c->x86_vendor = X86_VENDOR_UNKNOWN;
- c->x86_model = c->x86_mask = 0; /* So far unknown... */
- c->x86_vendor_id[0] = '\0'; /* Unset */
- c->x86_model_id[0] = '\0'; /* Unset */
- c->x86_clflush_size = 64;
- c->x86_cache_alignment = c->x86_clflush_size;
- c->x86_max_cores = 1;
- c->x86_coreid_bits = 0;
- c->extended_cpuid_level = 0;
- memset(&c->x86_capability, 0, sizeof c->x86_capability);
-
- /* Get vendor name */
- cpuid(0x00000000, (unsigned int *)&c->cpuid_level,
- (unsigned int *)&c->x86_vendor_id[0],
- (unsigned int *)&c->x86_vendor_id[8],
- (unsigned int *)&c->x86_vendor_id[4]);
-
- get_cpu_vendor(c);
-
- /* Initialize the standard set of capabilities */
- /* Note that the vendor-specific code below might override */
-
- /* Intel-defined flags: level 0x00000001 */
- if (c->cpuid_level >= 0x00000001) {
- __u32 misc;
- cpuid(0x00000001, &tfms, &misc, &c->x86_capability[4],
- &c->x86_capability[0]);
- c->x86 = (tfms >> 8) & 0xf;
- c->x86_model = (tfms >> 4) & 0xf;
- c->x86_mask = tfms & 0xf;
- if (c->x86 == 0xf)
- c->x86 += (tfms >> 20) & 0xff;
- if (c->x86 >= 0x6)
- c->x86_model += ((tfms >> 16) & 0xF) << 4;
- if (test_cpu_cap(c, X86_FEATURE_CLFLSH))
- c->x86_clflush_size = ((misc >> 8) & 0xff) * 8;
- } else {
- /* Have CPUID level 0 only - unheard of */
- c->x86 = 4;
- }
-
- c->initial_apicid = (cpuid_ebx(1) >> 24) & 0xff;
-#ifdef CONFIG_SMP
- c->phys_proc_id = c->initial_apicid;
-#endif
- /* AMD-defined flags: level 0x80000001 */
- xlvl = cpuid_eax(0x80000000);
- c->extended_cpuid_level = xlvl;
- if ((xlvl & 0xffff0000) == 0x80000000) {
- if (xlvl >= 0x80000001) {
- c->x86_capability[1] = cpuid_edx(0x80000001);
- c->x86_capability[6] = cpuid_ecx(0x80000001);
- }
- if (xlvl >= 0x80000004)
- get_model_name(c); /* Default name */
- }
-
- /* Transmeta-defined flags: level 0x80860001 */
- xlvl = cpuid_eax(0x80860000);
- if ((xlvl & 0xffff0000) == 0x80860000) {
- /* Don't set x86_cpuid_level here for now to not confuse. */
- if (xlvl >= 0x80860001)
- c->x86_capability[2] = cpuid_edx(0x80860001);
- }
-
- if (c->extended_cpuid_level >= 0x80000007)
- c->x86_power = cpuid_edx(0x80000007);
-
- if (c->extended_cpuid_level >= 0x80000008) {
- u32 eax = cpuid_eax(0x80000008);
-
- c->x86_virt_bits = (eax >> 8) & 0xff;
- c->x86_phys_bits = eax & 0xff;
- }
-
- detect_nopl(c);
-
- if (c->x86_vendor != X86_VENDOR_UNKNOWN &&
- cpu_devs[c->x86_vendor]->c_early_init)
- cpu_devs[c->x86_vendor]->c_early_init(c);
-
- validate_pat_support(c);
-}
-
-/*
- * This does the hard work of actually picking apart the CPU stuff...
- */
-static void __cpuinit identify_cpu(struct cpuinfo_x86 *c)
-{
- int i;
-
- early_identify_cpu(c);
-
- init_scattered_cpuid_features(c);
-
- c->apicid = phys_pkg_id(0);
-
- /*
- * Vendor-specific initialization. In this section we
- * canonicalize the feature flags, meaning if there are
- * features a certain CPU supports which CPUID doesn't
- * tell us, CPUID claiming incorrect flags, or other bugs,
- * we handle them here.
- *
- * At the end of this section, c->x86_capability better
- * indicate the features this CPU genuinely supports!
- */
- if (this_cpu->c_init)
- this_cpu->c_init(c);
-
- detect_ht(c);
-
- /*
- * On SMP, boot_cpu_data holds the common feature set between
- * all CPUs; so make sure that we indicate which features are
- * common between the CPUs. The first time this routine gets
- * executed, c == &boot_cpu_data.
- */
- if (c != &boot_cpu_data) {
- /* AND the already accumulated flags with these */
- for (i = 0; i < NCAPINTS; i++)
- boot_cpu_data.x86_capability[i] &= c->x86_capability[i];
- }
-
- /* Clear all flags overriden by options */
- for (i = 0; i < NCAPINTS; i++)
- c->x86_capability[i] &= ~cleared_cpu_caps[i];
-
-#ifdef CONFIG_X86_MCE
- mcheck_init(c);
-#endif
- select_idle_routine(c);
-
-#ifdef CONFIG_NUMA
- numa_add_cpu(smp_processor_id());
-#endif
-
-}
-
-void __cpuinit identify_boot_cpu(void)
-{
- identify_cpu(&boot_cpu_data);
-}
-
-void __cpuinit identify_secondary_cpu(struct cpuinfo_x86 *c)
-{
- BUG_ON(c == &boot_cpu_data);
- identify_cpu(c);
- mtrr_ap_init();
-}
-
-static __init int setup_noclflush(char *arg)
-{
- setup_clear_cpu_cap(X86_FEATURE_CLFLSH);
- return 1;
-}
-__setup("noclflush", setup_noclflush);
-
-struct msr_range {
- unsigned min;
- unsigned max;
-};
-
-static struct msr_range msr_range_array[] __cpuinitdata = {
- { 0x00000000, 0x00000418},
- { 0xc0000000, 0xc000040b},
- { 0xc0010000, 0xc0010142},
- { 0xc0011000, 0xc001103b},
-};
-
-static void __cpuinit print_cpu_msr(void)
-{
- unsigned index;
- u64 val;
- int i;
- unsigned index_min, index_max;
-
- for (i = 0; i < ARRAY_SIZE(msr_range_array); i++) {
- index_min = msr_range_array[i].min;
- index_max = msr_range_array[i].max;
- for (index = index_min; index < index_max; index++) {
- if (rdmsrl_amd_safe(index, &val))
- continue;
- printk(KERN_INFO " MSR%08x: %016llx\n", index, val);
- }
- }
-}
-
-static int show_msr __cpuinitdata;
-static __init int setup_show_msr(char *arg)
-{
- int num;
-
- get_option(&arg, &num);
-
- if (num > 0)
- show_msr = num;
- return 1;
-}
-__setup("show_msr=", setup_show_msr);
-
-void __cpuinit print_cpu_info(struct cpuinfo_x86 *c)
-{
- if (c->x86_model_id[0])
- printk(KERN_CONT "%s", c->x86_model_id);
-
- if (c->x86_mask || c->cpuid_level >= 0)
- printk(KERN_CONT " stepping %02x\n", c->x86_mask);
- else
- printk(KERN_CONT "\n");
-
-#ifdef CONFIG_SMP
- if (c->cpu_index < show_msr)
- print_cpu_msr();
-#else
- if (show_msr)
- print_cpu_msr();
-#endif
-}
-
-static __init int setup_disablecpuid(char *arg)
-{
- int bit;
- if (get_option(&arg, &bit) && bit < NCAPINTS*32)
- setup_clear_cpu_cap(bit);
- else
- return 0;
- return 1;
-}
-__setup("clearcpuid=", setup_disablecpuid);
-
-cpumask_t cpu_initialized __cpuinitdata = CPU_MASK_NONE;
-
-struct x8664_pda **_cpu_pda __read_mostly;
-EXPORT_SYMBOL(_cpu_pda);
-
-struct desc_ptr idt_descr = { 256 * 16 - 1, (unsigned long) idt_table };
-
-char boot_cpu_stack[IRQSTACKSIZE] __page_aligned_bss;
-
-unsigned long __supported_pte_mask __read_mostly = ~0UL;
-EXPORT_SYMBOL_GPL(__supported_pte_mask);
-
-static int do_not_nx __cpuinitdata;
-
-/* noexec=on|off
-Control non executable mappings for 64bit processes.
-
-on Enable(default)
-off Disable
-*/
-static int __init nonx_setup(char *str)
-{
- if (!str)
- return -EINVAL;
- if (!strncmp(str, "on", 2)) {
- __supported_pte_mask |= _PAGE_NX;
- do_not_nx = 0;
- } else if (!strncmp(str, "off", 3)) {
- do_not_nx = 1;
- __supported_pte_mask &= ~_PAGE_NX;
- }
- return 0;
-}
-early_param("noexec", nonx_setup);
-
-int force_personality32;
-
-/* noexec32=on|off
-Control non executable heap for 32bit processes.
-To control the stack too use noexec=off
-
-on PROT_READ does not imply PROT_EXEC for 32bit processes (default)
-off PROT_READ implies PROT_EXEC
-*/
-static int __init nonx32_setup(char *str)
-{
- if (!strcmp(str, "on"))
- force_personality32 &= ~READ_IMPLIES_EXEC;
- else if (!strcmp(str, "off"))
- force_personality32 |= READ_IMPLIES_EXEC;
- return 1;
-}
-__setup("noexec32=", nonx32_setup);
-
-void pda_init(int cpu)
-{
- struct x8664_pda *pda = cpu_pda(cpu);
-
- /* Setup up data that may be needed in __get_free_pages early */
- loadsegment(fs, 0);
- loadsegment(gs, 0);
- /* Memory clobbers used to order PDA accessed */
- mb();
- wrmsrl(MSR_GS_BASE, pda);
- mb();
-
- pda->cpunumber = cpu;
- pda->irqcount = -1;
- pda->kernelstack = (unsigned long)stack_thread_info() -
- PDA_STACKOFFSET + THREAD_SIZE;
- pda->active_mm = &init_mm;
- pda->mmu_state = 0;
-
- if (cpu == 0) {
- /* others are initialized in smpboot.c */
- pda->pcurrent = &init_task;
- pda->irqstackptr = boot_cpu_stack;
- pda->irqstackptr += IRQSTACKSIZE - 64;
- } else {
- if (!pda->irqstackptr) {
- pda->irqstackptr = (char *)
- __get_free_pages(GFP_ATOMIC, IRQSTACK_ORDER);
- if (!pda->irqstackptr)
- panic("cannot allocate irqstack for cpu %d",
- cpu);
- pda->irqstackptr += IRQSTACKSIZE - 64;
- }
-
- if (pda->nodenumber == 0 && cpu_to_node(cpu) != NUMA_NO_NODE)
- pda->nodenumber = cpu_to_node(cpu);
- }
-}
-
-char boot_exception_stacks[(N_EXCEPTION_STACKS - 1) * EXCEPTION_STKSZ +
- DEBUG_STKSZ] __page_aligned_bss;
-
-extern asmlinkage void ignore_sysret(void);
-
-/* May not be marked __init: used by software suspend */
-void syscall_init(void)
-{
- /*
- * LSTAR and STAR live in a bit strange symbiosis.
- * They both write to the same internal register. STAR allows to
- * set CS/DS but only a 32bit target. LSTAR sets the 64bit rip.
- */
- wrmsrl(MSR_STAR, ((u64)__USER32_CS)<<48 | ((u64)__KERNEL_CS)<<32);
- wrmsrl(MSR_LSTAR, system_call);
- wrmsrl(MSR_CSTAR, ignore_sysret);
-
-#ifdef CONFIG_IA32_EMULATION
- syscall32_cpu_init();
-#endif
-
- /* Flags to clear on syscall */
- wrmsrl(MSR_SYSCALL_MASK,
- X86_EFLAGS_TF|X86_EFLAGS_DF|X86_EFLAGS_IF|X86_EFLAGS_IOPL);
-}
-
-void __cpuinit check_efer(void)
-{
- unsigned long efer;
-
- rdmsrl(MSR_EFER, efer);
- if (!(efer & EFER_NX) || do_not_nx)
- __supported_pte_mask &= ~_PAGE_NX;
-}
-
-unsigned long kernel_eflags;
-
-/*
- * Copies of the original ist values from the tss are only accessed during
- * debugging, no special alignment required.
- */
-DEFINE_PER_CPU(struct orig_ist, orig_ist);
-
-/*
- * cpu_init() initializes state that is per-CPU. Some data is already
- * initialized (naturally) in the bootstrap process, such as the GDT
- * and IDT. We reload them nevertheless, this function acts as a
- * 'CPU state barrier', nothing should get across.
- * A lot of state is already set up in PDA init.
- */
-void __cpuinit cpu_init(void)
-{
- int cpu = stack_smp_processor_id();
- struct tss_struct *t = &per_cpu(init_tss, cpu);
- struct orig_ist *orig_ist = &per_cpu(orig_ist, cpu);
- unsigned long v;
- char *estacks = NULL;
- struct task_struct *me;
- int i;
-
- /* CPU 0 is initialised in head64.c */
- if (cpu != 0)
- pda_init(cpu);
- else
- estacks = boot_exception_stacks;
-
- me = current;
-
- if (cpu_test_and_set(cpu, cpu_initialized))
- panic("CPU#%d already initialized!\n", cpu);
-
- printk(KERN_INFO "Initializing CPU#%d\n", cpu);
-
- clear_in_cr4(X86_CR4_VME|X86_CR4_PVI|X86_CR4_TSD|X86_CR4_DE);
-
- /*
- * Initialize the per-CPU GDT with the boot GDT,
- * and set up the GDT descriptor:
- */
-
- switch_to_new_gdt();
- load_idt((const struct desc_ptr *)&idt_descr);
-
- memset(me->thread.tls_array, 0, GDT_ENTRY_TLS_ENTRIES * 8);
- syscall_init();
-
- wrmsrl(MSR_FS_BASE, 0);
- wrmsrl(MSR_KERNEL_GS_BASE, 0);
- barrier();
-
- check_efer();
-
- /*
- * set up and load the per-CPU TSS
- */
- if (!orig_ist->ist[0]) {
- static const unsigned int order[N_EXCEPTION_STACKS] = {
- [0 ... N_EXCEPTION_STACKS - 1] = EXCEPTION_STACK_ORDER,
- [DEBUG_STACK - 1] = DEBUG_STACK_ORDER
- };
- for (v = 0; v < N_EXCEPTION_STACKS; v++) {
- if (cpu) {
- estacks = (char *)__get_free_pages(GFP_ATOMIC, order[v]);
- if (!estacks)
- panic("Cannot allocate exception "
- "stack %ld %d\n", v, cpu);
- }
- estacks += PAGE_SIZE << order[v];
- orig_ist->ist[v] = t->x86_tss.ist[v] =
- (unsigned long)estacks;
- }
- }
-
- t->x86_tss.io_bitmap_base = offsetof(struct tss_struct, io_bitmap);
- /*
- * <= is required because the CPU will access up to
- * 8 bits beyond the end of the IO permission bitmap.
- */
- for (i = 0; i <= IO_BITMAP_LONGS; i++)
- t->io_bitmap[i] = ~0UL;
-
- atomic_inc(&init_mm.mm_count);
- me->active_mm = &init_mm;
- if (me->mm)
- BUG();
- enter_lazy_tlb(&init_mm, me);
-
- load_sp0(t, ¤t->thread);
- set_tss_desc(cpu, t);
- load_TR_desc();
- load_LDT(&init_mm.context);
-
-#ifdef CONFIG_KGDB
- /*
- * If the kgdb is connected no debug regs should be altered. This
- * is only applicable when KGDB and a KGDB I/O module are built
- * into the kernel and you are using early debugging with
- * kgdbwait. KGDB will control the kernel HW breakpoint registers.
- */
- if (kgdb_connected && arch_kgdb_ops.correct_hw_break)
- arch_kgdb_ops.correct_hw_break();
- else {
-#endif
- /*
- * Clear all 6 debug registers:
- */
-
- set_debugreg(0UL, 0);
- set_debugreg(0UL, 1);
- set_debugreg(0UL, 2);
- set_debugreg(0UL, 3);
- set_debugreg(0UL, 6);
- set_debugreg(0UL, 7);
-#ifdef CONFIG_KGDB
- /* If the kgdb is connected no debug regs should be altered. */
- }
-#endif
-
- fpu_init();
-
- raw_local_save_flags(kernel_eflags);
-
- if (is_uv_system())
- uv_cpu_init();
-}
diff --git a/arch/x86/kernel/cpu/cpu.h b/arch/x86/kernel/cpu/cpu.h
index 4d894e8..de4094a 100644
--- a/arch/x86/kernel/cpu/cpu.h
+++ b/arch/x86/kernel/cpu/cpu.h
@@ -21,23 +21,16 @@
void (*c_init)(struct cpuinfo_x86 * c);
void (*c_identify)(struct cpuinfo_x86 * c);
unsigned int (*c_size_cache)(struct cpuinfo_x86 * c, unsigned int size);
+ int c_x86_vendor;
};
-extern struct cpu_dev * cpu_devs [X86_VENDOR_NUM];
+#define cpu_dev_register(cpu_devX) \
+ static struct cpu_dev *__cpu_dev_##cpu_devX __used \
+ __attribute__((__section__(".x86_cpu_dev.init"))) = \
+ &cpu_devX;
-struct cpu_vendor_dev {
- int vendor;
- struct cpu_dev *cpu_dev;
-};
+extern struct cpu_dev *__x86_cpu_dev_start[], *__x86_cpu_dev_end[];
-#define cpu_vendor_dev_register(cpu_vendor_id, cpu_dev) \
- static struct cpu_vendor_dev __cpu_vendor_dev_##cpu_vendor_id __used \
- __attribute__((__section__(".x86cpuvendor.init"))) = \
- { cpu_vendor_id, cpu_dev }
-
-extern struct cpu_vendor_dev __x86cpuvendor_start[], __x86cpuvendor_end[];
-
-extern int get_model_name(struct cpuinfo_x86 *c);
extern void display_cacheinfo(struct cpuinfo_x86 *c);
#endif
diff --git a/arch/x86/kernel/cpu/cyrix.c b/arch/x86/kernel/cpu/cyrix.c
index 898a5a2..ffd0f5e 100644
--- a/arch/x86/kernel/cpu/cyrix.c
+++ b/arch/x86/kernel/cpu/cyrix.c
@@ -121,7 +121,7 @@
setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */
/* Load/Store Serialize to mem access disable (=reorder it) */
- setCx86(CX86_PCR0, getCx86(CX86_PCR0) & ~0x80);
+ setCx86_old(CX86_PCR0, getCx86_old(CX86_PCR0) & ~0x80);
/* set load/store serialize from 1GB to 4GB */
ccr3 |= 0xe0;
setCx86(CX86_CCR3, ccr3);
@@ -132,11 +132,11 @@
printk(KERN_INFO "Enable Memory-Write-back mode on Cyrix/NSC processor.\n");
/* CCR2 bit 2: unlock NW bit */
- setCx86(CX86_CCR2, getCx86(CX86_CCR2) & ~0x04);
+ setCx86_old(CX86_CCR2, getCx86_old(CX86_CCR2) & ~0x04);
/* set 'Not Write-through' */
write_cr0(read_cr0() | X86_CR0_NW);
/* CCR2 bit 2: lock NW bit and set WT1 */
- setCx86(CX86_CCR2, getCx86(CX86_CCR2) | 0x14);
+ setCx86_old(CX86_CCR2, getCx86_old(CX86_CCR2) | 0x14);
}
/*
@@ -150,14 +150,14 @@
local_irq_save(flags);
/* Suspend on halt power saving and enable #SUSP pin */
- setCx86(CX86_CCR2, getCx86(CX86_CCR2) | 0x88);
+ setCx86_old(CX86_CCR2, getCx86_old(CX86_CCR2) | 0x88);
ccr3 = getCx86(CX86_CCR3);
setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */
/* FPU fast, DTE cache, Mem bypass */
- setCx86(CX86_CCR4, getCx86(CX86_CCR4) | 0x38);
+ setCx86_old(CX86_CCR4, getCx86_old(CX86_CCR4) | 0x38);
setCx86(CX86_CCR3, ccr3); /* disable MAPEN */
set_cx86_memwb();
@@ -291,7 +291,7 @@
/* GXm supports extended cpuid levels 'ala' AMD */
if (c->cpuid_level == 2) {
/* Enable cxMMX extensions (GX1 Datasheet 54) */
- setCx86(CX86_CCR7, getCx86(CX86_CCR7) | 1);
+ setCx86_old(CX86_CCR7, getCx86_old(CX86_CCR7) | 1);
/*
* GXm : 0x30 ... 0x5f GXm datasheet 51
@@ -301,7 +301,6 @@
*/
if ((0x30 <= dir1 && dir1 <= 0x6f) || (0x80 <= dir1 && dir1 <= 0x8f))
geode_configure();
- get_model_name(c); /* get CPU marketing name */
return;
} else { /* MediaGX */
Cx86_cb[2] = (dir0_lsn & 1) ? '3' : '4';
@@ -314,7 +313,7 @@
if (dir1 > 7) {
dir0_msn++; /* M II */
/* Enable MMX extensions (App note 108) */
- setCx86(CX86_CCR7, getCx86(CX86_CCR7)|1);
+ setCx86_old(CX86_CCR7, getCx86_old(CX86_CCR7)|1);
} else {
c->coma_bug = 1; /* 6x86MX, it has the bug. */
}
@@ -429,7 +428,7 @@
local_irq_save(flags);
ccr3 = getCx86(CX86_CCR3);
setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */
- setCx86(CX86_CCR4, getCx86(CX86_CCR4) | 0x80); /* enable cpuid */
+ setCx86_old(CX86_CCR4, getCx86_old(CX86_CCR4) | 0x80); /* enable cpuid */
setCx86(CX86_CCR3, ccr3); /* disable MAPEN */
local_irq_restore(flags);
}
@@ -442,14 +441,16 @@
.c_early_init = early_init_cyrix,
.c_init = init_cyrix,
.c_identify = cyrix_identify,
+ .c_x86_vendor = X86_VENDOR_CYRIX,
};
-cpu_vendor_dev_register(X86_VENDOR_CYRIX, &cyrix_cpu_dev);
+cpu_dev_register(cyrix_cpu_dev);
static struct cpu_dev nsc_cpu_dev __cpuinitdata = {
.c_vendor = "NSC",
.c_ident = { "Geode by NSC" },
.c_init = init_nsc,
+ .c_x86_vendor = X86_VENDOR_NSC,
};
-cpu_vendor_dev_register(X86_VENDOR_NSC, &nsc_cpu_dev);
+cpu_dev_register(nsc_cpu_dev);
diff --git a/arch/x86/kernel/cpu/feature_names.c b/arch/x86/kernel/cpu/feature_names.c
deleted file mode 100644
index c901779..0000000
--- a/arch/x86/kernel/cpu/feature_names.c
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Strings for the various x86 capability flags.
- *
- * This file must not contain any executable code.
- */
-
-#include <asm/cpufeature.h>
-
-/*
- * These flag bits must match the definitions in <asm/cpufeature.h>.
- * NULL means this bit is undefined or reserved; either way it doesn't
- * have meaning as far as Linux is concerned. Note that it's important
- * to realize there is a difference between this table and CPUID -- if
- * applications want to get the raw CPUID data, they should access
- * /dev/cpu/<cpu_nr>/cpuid instead.
- */
-const char * const x86_cap_flags[NCAPINTS*32] = {
- /* Intel-defined */
- "fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce",
- "cx8", "apic", NULL, "sep", "mtrr", "pge", "mca", "cmov",
- "pat", "pse36", "pn", "clflush", NULL, "dts", "acpi", "mmx",
- "fxsr", "sse", "sse2", "ss", "ht", "tm", "ia64", "pbe",
-
- /* AMD-defined */
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, "syscall", NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, "mp", "nx", NULL, "mmxext", NULL,
- NULL, "fxsr_opt", "pdpe1gb", "rdtscp", NULL, "lm",
- "3dnowext", "3dnow",
-
- /* Transmeta-defined */
- "recovery", "longrun", NULL, "lrti", NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-
- /* Other (Linux-defined) */
- "cxmmx", "k6_mtrr", "cyrix_arr", "centaur_mcr",
- NULL, NULL, NULL, NULL,
- "constant_tsc", "up", NULL, "arch_perfmon",
- "pebs", "bts", NULL, NULL,
- "rep_good", NULL, NULL, NULL,
- "nopl", NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-
- /* Intel-defined (#2) */
- "pni", NULL, NULL, "monitor", "ds_cpl", "vmx", "smx", "est",
- "tm2", "ssse3", "cid", NULL, NULL, "cx16", "xtpr", NULL,
- NULL, NULL, "dca", "sse4_1", "sse4_2", NULL, NULL, "popcnt",
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-
- /* VIA/Cyrix/Centaur-defined */
- NULL, NULL, "rng", "rng_en", NULL, NULL, "ace", "ace_en",
- "ace2", "ace2_en", "phe", "phe_en", "pmm", "pmm_en", NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-
- /* AMD-defined (#2) */
- "lahf_lm", "cmp_legacy", "svm", "extapic",
- "cr8_legacy", "abm", "sse4a", "misalignsse",
- "3dnowprefetch", "osvw", "ibs", "sse5",
- "skinit", "wdt", NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-
- /* Auxiliary (Linux-defined) */
- "ida", NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-};
-
-const char *const x86_power_flags[32] = {
- "ts", /* temperature sensor */
- "fid", /* frequency id control */
- "vid", /* voltage id control */
- "ttp", /* thermal trip */
- "tm",
- "stc",
- "100mhzsteps",
- "hwpstate",
- "", /* tsc invariant mapped to constant_tsc */
- /* nothing */
-};
diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c
index f113ef4..99468db 100644
--- a/arch/x86/kernel/cpu/intel.c
+++ b/arch/x86/kernel/cpu/intel.c
@@ -15,6 +15,11 @@
#include <asm/ds.h>
#include <asm/bugs.h>
+#ifdef CONFIG_X86_64
+#include <asm/topology.h>
+#include <asm/numa_64.h>
+#endif
+
#include "cpu.h"
#ifdef CONFIG_X86_LOCAL_APIC
@@ -23,23 +28,22 @@
#include <mach_apic.h>
#endif
-#ifdef CONFIG_X86_INTEL_USERCOPY
-/*
- * Alignment at which movsl is preferred for bulk memory copies.
- */
-struct movsl_mask movsl_mask __read_mostly;
-#endif
-
static void __cpuinit early_init_intel(struct cpuinfo_x86 *c)
{
- /* Netburst reports 64 bytes clflush size, but does IO in 128 bytes */
- if (c->x86 == 15 && c->x86_cache_alignment == 64)
- c->x86_cache_alignment = 128;
if ((c->x86 == 0xf && c->x86_model >= 0x03) ||
(c->x86 == 0x6 && c->x86_model >= 0x0e))
set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC);
+
+#ifdef CONFIG_X86_64
+ set_cpu_cap(c, X86_FEATURE_SYSENTER32);
+#else
+ /* Netburst reports 64 bytes clflush size, but does IO in 128 bytes */
+ if (c->x86 == 15 && c->x86_cache_alignment == 64)
+ c->x86_cache_alignment = 128;
+#endif
}
+#ifdef CONFIG_X86_32
/*
* Early probe support logic for ppro memory erratum #50
*
@@ -59,45 +63,6 @@
return 0;
}
-
-/*
- * P4 Xeon errata 037 workaround.
- * Hardware prefetcher may cause stale data to be loaded into the cache.
- */
-static void __cpuinit Intel_errata_workarounds(struct cpuinfo_x86 *c)
-{
- unsigned long lo, hi;
-
- if ((c->x86 == 15) && (c->x86_model == 1) && (c->x86_mask == 1)) {
- rdmsr(MSR_IA32_MISC_ENABLE, lo, hi);
- if ((lo & (1<<9)) == 0) {
- printk (KERN_INFO "CPU: C0 stepping P4 Xeon detected.\n");
- printk (KERN_INFO "CPU: Disabling hardware prefetching (Errata 037)\n");
- lo |= (1<<9); /* Disable hw prefetching */
- wrmsr (MSR_IA32_MISC_ENABLE, lo, hi);
- }
- }
-}
-
-
-/*
- * find out the number of processor cores on the die
- */
-static int __cpuinit num_cpu_cores(struct cpuinfo_x86 *c)
-{
- unsigned int eax, ebx, ecx, edx;
-
- if (c->cpuid_level < 4)
- return 1;
-
- /* Intel has a non-standard dependency on %ecx for this CPUID level. */
- cpuid_count(4, 0, &eax, &ebx, &ecx, &edx);
- if (eax & 0x1f)
- return ((eax >> 26) + 1);
- else
- return 1;
-}
-
#ifdef CONFIG_X86_F00F_BUG
static void __cpuinit trap_init_f00f_bug(void)
{
@@ -112,12 +77,9 @@
}
#endif
-static void __cpuinit init_intel(struct cpuinfo_x86 *c)
+static void __cpuinit intel_workarounds(struct cpuinfo_x86 *c)
{
- unsigned int l2 = 0;
- char *p = NULL;
-
- early_init_intel(c);
+ unsigned long lo, hi;
#ifdef CONFIG_X86_F00F_BUG
/*
@@ -138,6 +100,148 @@
}
#endif
+ /*
+ * SEP CPUID bug: Pentium Pro reports SEP but doesn't have it until
+ * model 3 mask 3
+ */
+ if ((c->x86<<8 | c->x86_model<<4 | c->x86_mask) < 0x633)
+ clear_cpu_cap(c, X86_FEATURE_SEP);
+
+ /*
+ * P4 Xeon errata 037 workaround.
+ * Hardware prefetcher may cause stale data to be loaded into the cache.
+ */
+ if ((c->x86 == 15) && (c->x86_model == 1) && (c->x86_mask == 1)) {
+ rdmsr(MSR_IA32_MISC_ENABLE, lo, hi);
+ if ((lo & (1<<9)) == 0) {
+ printk (KERN_INFO "CPU: C0 stepping P4 Xeon detected.\n");
+ printk (KERN_INFO "CPU: Disabling hardware prefetching (Errata 037)\n");
+ lo |= (1<<9); /* Disable hw prefetching */
+ wrmsr (MSR_IA32_MISC_ENABLE, lo, hi);
+ }
+ }
+
+ /*
+ * See if we have a good local APIC by checking for buggy Pentia,
+ * i.e. all B steppings and the C2 stepping of P54C when using their
+ * integrated APIC (see 11AP erratum in "Pentium Processor
+ * Specification Update").
+ */
+ if (cpu_has_apic && (c->x86<<8 | c->x86_model<<4) == 0x520 &&
+ (c->x86_mask < 0x6 || c->x86_mask == 0xb))
+ set_cpu_cap(c, X86_FEATURE_11AP);
+
+
+#ifdef CONFIG_X86_INTEL_USERCOPY
+ /*
+ * Set up the preferred alignment for movsl bulk memory moves
+ */
+ switch (c->x86) {
+ case 4: /* 486: untested */
+ break;
+ case 5: /* Old Pentia: untested */
+ break;
+ case 6: /* PII/PIII only like movsl with 8-byte alignment */
+ movsl_mask.mask = 7;
+ break;
+ case 15: /* P4 is OK down to 8-byte alignment */
+ movsl_mask.mask = 7;
+ break;
+ }
+#endif
+
+#ifdef CONFIG_X86_NUMAQ
+ numaq_tsc_disable();
+#endif
+}
+#else
+static void __cpuinit intel_workarounds(struct cpuinfo_x86 *c)
+{
+}
+#endif
+
+static void __cpuinit srat_detect_node(void)
+{
+#if defined(CONFIG_NUMA) && defined(CONFIG_X86_64)
+ unsigned node;
+ int cpu = smp_processor_id();
+ int apicid = hard_smp_processor_id();
+
+ /* Don't do the funky fallback heuristics the AMD version employs
+ for now. */
+ node = apicid_to_node[apicid];
+ if (node == NUMA_NO_NODE || !node_online(node))
+ node = first_node(node_online_map);
+ numa_set_node(cpu, node);
+
+ printk(KERN_INFO "CPU %d/%x -> Node %d\n", cpu, apicid, node);
+#endif
+}
+
+/*
+ * find out the number of processor cores on the die
+ */
+static int __cpuinit intel_num_cpu_cores(struct cpuinfo_x86 *c)
+{
+ unsigned int eax, ebx, ecx, edx;
+
+ if (c->cpuid_level < 4)
+ return 1;
+
+ /* Intel has a non-standard dependency on %ecx for this CPUID level. */
+ cpuid_count(4, 0, &eax, &ebx, &ecx, &edx);
+ if (eax & 0x1f)
+ return ((eax >> 26) + 1);
+ else
+ return 1;
+}
+
+static void __cpuinit detect_vmx_virtcap(struct cpuinfo_x86 *c)
+{
+ /* Intel VMX MSR indicated features */
+#define X86_VMX_FEATURE_PROC_CTLS_TPR_SHADOW 0x00200000
+#define X86_VMX_FEATURE_PROC_CTLS_VNMI 0x00400000
+#define X86_VMX_FEATURE_PROC_CTLS_2ND_CTLS 0x80000000
+#define X86_VMX_FEATURE_PROC_CTLS2_VIRT_APIC 0x00000001
+#define X86_VMX_FEATURE_PROC_CTLS2_EPT 0x00000002
+#define X86_VMX_FEATURE_PROC_CTLS2_VPID 0x00000020
+
+ u32 vmx_msr_low, vmx_msr_high, msr_ctl, msr_ctl2;
+
+ clear_cpu_cap(c, X86_FEATURE_TPR_SHADOW);
+ clear_cpu_cap(c, X86_FEATURE_VNMI);
+ clear_cpu_cap(c, X86_FEATURE_FLEXPRIORITY);
+ clear_cpu_cap(c, X86_FEATURE_EPT);
+ clear_cpu_cap(c, X86_FEATURE_VPID);
+
+ rdmsr(MSR_IA32_VMX_PROCBASED_CTLS, vmx_msr_low, vmx_msr_high);
+ msr_ctl = vmx_msr_high | vmx_msr_low;
+ if (msr_ctl & X86_VMX_FEATURE_PROC_CTLS_TPR_SHADOW)
+ set_cpu_cap(c, X86_FEATURE_TPR_SHADOW);
+ if (msr_ctl & X86_VMX_FEATURE_PROC_CTLS_VNMI)
+ set_cpu_cap(c, X86_FEATURE_VNMI);
+ if (msr_ctl & X86_VMX_FEATURE_PROC_CTLS_2ND_CTLS) {
+ rdmsr(MSR_IA32_VMX_PROCBASED_CTLS2,
+ vmx_msr_low, vmx_msr_high);
+ msr_ctl2 = vmx_msr_high | vmx_msr_low;
+ if ((msr_ctl2 & X86_VMX_FEATURE_PROC_CTLS2_VIRT_APIC) &&
+ (msr_ctl & X86_VMX_FEATURE_PROC_CTLS_TPR_SHADOW))
+ set_cpu_cap(c, X86_FEATURE_FLEXPRIORITY);
+ if (msr_ctl2 & X86_VMX_FEATURE_PROC_CTLS2_EPT)
+ set_cpu_cap(c, X86_FEATURE_EPT);
+ if (msr_ctl2 & X86_VMX_FEATURE_PROC_CTLS2_VPID)
+ set_cpu_cap(c, X86_FEATURE_VPID);
+ }
+}
+
+static void __cpuinit init_intel(struct cpuinfo_x86 *c)
+{
+ unsigned int l2 = 0;
+
+ early_init_intel(c);
+
+ intel_workarounds(c);
+
l2 = init_intel_cacheinfo(c);
if (c->cpuid_level > 9) {
unsigned eax = cpuid_eax(10);
@@ -146,16 +250,32 @@
set_cpu_cap(c, X86_FEATURE_ARCH_PERFMON);
}
- /* SEP CPUID bug: Pentium Pro reports SEP but doesn't have it until model 3 mask 3 */
- if ((c->x86<<8 | c->x86_model<<4 | c->x86_mask) < 0x633)
- clear_cpu_cap(c, X86_FEATURE_SEP);
+ if (cpu_has_xmm2)
+ set_cpu_cap(c, X86_FEATURE_LFENCE_RDTSC);
+ if (cpu_has_ds) {
+ unsigned int l1;
+ rdmsr(MSR_IA32_MISC_ENABLE, l1, l2);
+ if (!(l1 & (1<<11)))
+ set_cpu_cap(c, X86_FEATURE_BTS);
+ if (!(l1 & (1<<12)))
+ set_cpu_cap(c, X86_FEATURE_PEBS);
+ ds_init_intel(c);
+ }
+#ifdef CONFIG_X86_64
+ if (c->x86 == 15)
+ c->x86_cache_alignment = c->x86_clflush_size * 2;
+ if (c->x86 == 6)
+ set_cpu_cap(c, X86_FEATURE_REP_GOOD);
+#else
/*
* Names for the Pentium II/Celeron processors
* detectable only by also checking the cache size.
* Dixon is NOT a Celeron.
*/
if (c->x86 == 6) {
+ char *p = NULL;
+
switch (c->x86_model) {
case 5:
if (c->x86_mask == 0) {
@@ -178,71 +298,41 @@
p = "Celeron (Coppermine)";
break;
}
+
+ if (p)
+ strcpy(c->x86_model_id, p);
}
- if (p)
- strcpy(c->x86_model_id, p);
-
- c->x86_max_cores = num_cpu_cores(c);
-
- detect_ht(c);
-
- /* Work around errata */
- Intel_errata_workarounds(c);
-
-#ifdef CONFIG_X86_INTEL_USERCOPY
- /*
- * Set up the preferred alignment for movsl bulk memory moves
- */
- switch (c->x86) {
- case 4: /* 486: untested */
- break;
- case 5: /* Old Pentia: untested */
- break;
- case 6: /* PII/PIII only like movsl with 8-byte alignment */
- movsl_mask.mask = 7;
- break;
- case 15: /* P4 is OK down to 8-byte alignment */
- movsl_mask.mask = 7;
- break;
- }
-#endif
-
- if (cpu_has_xmm2)
- set_cpu_cap(c, X86_FEATURE_LFENCE_RDTSC);
- if (c->x86 == 15) {
+ if (c->x86 == 15)
set_cpu_cap(c, X86_FEATURE_P4);
- }
if (c->x86 == 6)
set_cpu_cap(c, X86_FEATURE_P3);
- if (cpu_has_ds) {
- unsigned int l1;
- rdmsr(MSR_IA32_MISC_ENABLE, l1, l2);
- if (!(l1 & (1<<11)))
- set_cpu_cap(c, X86_FEATURE_BTS);
- if (!(l1 & (1<<12)))
- set_cpu_cap(c, X86_FEATURE_PEBS);
- ds_init_intel(c);
- }
if (cpu_has_bts)
ptrace_bts_init_intel(c);
- /*
- * See if we have a good local APIC by checking for buggy Pentia,
- * i.e. all B steppings and the C2 stepping of P54C when using their
- * integrated APIC (see 11AP erratum in "Pentium Processor
- * Specification Update").
- */
- if (cpu_has_apic && (c->x86<<8 | c->x86_model<<4) == 0x520 &&
- (c->x86_mask < 0x6 || c->x86_mask == 0xb))
- set_cpu_cap(c, X86_FEATURE_11AP);
-
-#ifdef CONFIG_X86_NUMAQ
- numaq_tsc_disable();
#endif
+
+ detect_extended_topology(c);
+ if (!cpu_has(c, X86_FEATURE_XTOPOLOGY)) {
+ /*
+ * let's use the legacy cpuid vector 0x1 and 0x4 for topology
+ * detection.
+ */
+ c->x86_max_cores = intel_num_cpu_cores(c);
+#ifdef CONFIG_X86_32
+ detect_ht(c);
+#endif
+ }
+
+ /* Work around errata */
+ srat_detect_node();
+
+ if (cpu_has(c, X86_FEATURE_VMX))
+ detect_vmx_virtcap(c);
}
+#ifdef CONFIG_X86_32
static unsigned int __cpuinit intel_size_cache(struct cpuinfo_x86 *c, unsigned int size)
{
/*
@@ -255,10 +345,12 @@
size = 256;
return size;
}
+#endif
static struct cpu_dev intel_cpu_dev __cpuinitdata = {
.c_vendor = "Intel",
.c_ident = { "GenuineIntel" },
+#ifdef CONFIG_X86_32
.c_models = {
{ .vendor = X86_VENDOR_INTEL, .family = 4, .model_names =
{
@@ -308,76 +400,12 @@
}
},
},
+ .c_size_cache = intel_size_cache,
+#endif
.c_early_init = early_init_intel,
.c_init = init_intel,
- .c_size_cache = intel_size_cache,
+ .c_x86_vendor = X86_VENDOR_INTEL,
};
-cpu_vendor_dev_register(X86_VENDOR_INTEL, &intel_cpu_dev);
-
-#ifndef CONFIG_X86_CMPXCHG
-unsigned long cmpxchg_386_u8(volatile void *ptr, u8 old, u8 new)
-{
- u8 prev;
- unsigned long flags;
-
- /* Poor man's cmpxchg for 386. Unsuitable for SMP */
- local_irq_save(flags);
- prev = *(u8 *)ptr;
- if (prev == old)
- *(u8 *)ptr = new;
- local_irq_restore(flags);
- return prev;
-}
-EXPORT_SYMBOL(cmpxchg_386_u8);
-
-unsigned long cmpxchg_386_u16(volatile void *ptr, u16 old, u16 new)
-{
- u16 prev;
- unsigned long flags;
-
- /* Poor man's cmpxchg for 386. Unsuitable for SMP */
- local_irq_save(flags);
- prev = *(u16 *)ptr;
- if (prev == old)
- *(u16 *)ptr = new;
- local_irq_restore(flags);
- return prev;
-}
-EXPORT_SYMBOL(cmpxchg_386_u16);
-
-unsigned long cmpxchg_386_u32(volatile void *ptr, u32 old, u32 new)
-{
- u32 prev;
- unsigned long flags;
-
- /* Poor man's cmpxchg for 386. Unsuitable for SMP */
- local_irq_save(flags);
- prev = *(u32 *)ptr;
- if (prev == old)
- *(u32 *)ptr = new;
- local_irq_restore(flags);
- return prev;
-}
-EXPORT_SYMBOL(cmpxchg_386_u32);
-#endif
-
-#ifndef CONFIG_X86_CMPXCHG64
-unsigned long long cmpxchg_486_u64(volatile void *ptr, u64 old, u64 new)
-{
- u64 prev;
- unsigned long flags;
-
- /* Poor man's cmpxchg8b for 386 and 486. Unsuitable for SMP */
- local_irq_save(flags);
- prev = *(u64 *)ptr;
- if (prev == old)
- *(u64 *)ptr = new;
- local_irq_restore(flags);
- return prev;
-}
-EXPORT_SYMBOL(cmpxchg_486_u64);
-#endif
-
-/* arch_initcall(intel_cpu_init); */
+cpu_dev_register(intel_cpu_dev);
diff --git a/arch/x86/kernel/cpu/intel_64.c b/arch/x86/kernel/cpu/intel_64.c
deleted file mode 100644
index 1019c58..0000000
--- a/arch/x86/kernel/cpu/intel_64.c
+++ /dev/null
@@ -1,95 +0,0 @@
-#include <linux/init.h>
-#include <linux/smp.h>
-#include <asm/processor.h>
-#include <asm/ptrace.h>
-#include <asm/topology.h>
-#include <asm/numa_64.h>
-
-#include "cpu.h"
-
-static void __cpuinit early_init_intel(struct cpuinfo_x86 *c)
-{
- if ((c->x86 == 0xf && c->x86_model >= 0x03) ||
- (c->x86 == 0x6 && c->x86_model >= 0x0e))
- set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC);
-
- set_cpu_cap(c, X86_FEATURE_SYSENTER32);
-}
-
-/*
- * find out the number of processor cores on the die
- */
-static int __cpuinit intel_num_cpu_cores(struct cpuinfo_x86 *c)
-{
- unsigned int eax, t;
-
- if (c->cpuid_level < 4)
- return 1;
-
- cpuid_count(4, 0, &eax, &t, &t, &t);
-
- if (eax & 0x1f)
- return ((eax >> 26) + 1);
- else
- return 1;
-}
-
-static void __cpuinit srat_detect_node(void)
-{
-#ifdef CONFIG_NUMA
- unsigned node;
- int cpu = smp_processor_id();
- int apicid = hard_smp_processor_id();
-
- /* Don't do the funky fallback heuristics the AMD version employs
- for now. */
- node = apicid_to_node[apicid];
- if (node == NUMA_NO_NODE || !node_online(node))
- node = first_node(node_online_map);
- numa_set_node(cpu, node);
-
- printk(KERN_INFO "CPU %d/%x -> Node %d\n", cpu, apicid, node);
-#endif
-}
-
-static void __cpuinit init_intel(struct cpuinfo_x86 *c)
-{
- init_intel_cacheinfo(c);
- if (c->cpuid_level > 9) {
- unsigned eax = cpuid_eax(10);
- /* Check for version and the number of counters */
- if ((eax & 0xff) && (((eax>>8) & 0xff) > 1))
- set_cpu_cap(c, X86_FEATURE_ARCH_PERFMON);
- }
-
- if (cpu_has_ds) {
- unsigned int l1, l2;
- rdmsr(MSR_IA32_MISC_ENABLE, l1, l2);
- if (!(l1 & (1<<11)))
- set_cpu_cap(c, X86_FEATURE_BTS);
- if (!(l1 & (1<<12)))
- set_cpu_cap(c, X86_FEATURE_PEBS);
- }
-
-
- if (cpu_has_bts)
- ds_init_intel(c);
-
- if (c->x86 == 15)
- c->x86_cache_alignment = c->x86_clflush_size * 2;
- if (c->x86 == 6)
- set_cpu_cap(c, X86_FEATURE_REP_GOOD);
- set_cpu_cap(c, X86_FEATURE_LFENCE_RDTSC);
- c->x86_max_cores = intel_num_cpu_cores(c);
-
- srat_detect_node();
-}
-
-static struct cpu_dev intel_cpu_dev __cpuinitdata = {
- .c_vendor = "Intel",
- .c_ident = { "GenuineIntel" },
- .c_early_init = early_init_intel,
- .c_init = init_intel,
-};
-cpu_vendor_dev_register(X86_VENDOR_INTEL, &intel_cpu_dev);
-
diff --git a/arch/x86/kernel/cpu/intel_cacheinfo.c b/arch/x86/kernel/cpu/intel_cacheinfo.c
index 6b0a10b..3f46afb 100644
--- a/arch/x86/kernel/cpu/intel_cacheinfo.c
+++ b/arch/x86/kernel/cpu/intel_cacheinfo.c
@@ -1,8 +1,8 @@
/*
- * Routines to indentify caches on Intel CPU.
+ * Routines to indentify caches on Intel CPU.
*
- * Changes:
- * Venkatesh Pallipadi : Adding cache identification through cpuid(4)
+ * Changes:
+ * Venkatesh Pallipadi : Adding cache identification through cpuid(4)
* Ashok Raj <ashok.raj@intel.com>: Work with CPU hotplug infrastructure.
* Andi Kleen / Andreas Herrmann : CPUID4 emulation on AMD.
*/
@@ -13,6 +13,7 @@
#include <linux/compiler.h>
#include <linux/cpu.h>
#include <linux/sched.h>
+#include <linux/pci.h>
#include <asm/processor.h>
#include <asm/smp.h>
@@ -130,9 +131,18 @@
union _cpuid4_leaf_ebx ebx;
union _cpuid4_leaf_ecx ecx;
unsigned long size;
+ unsigned long can_disable;
cpumask_t shared_cpu_map; /* future?: only cpus/node is needed */
};
+#ifdef CONFIG_PCI
+static struct pci_device_id k8_nb_id[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x1103) },
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x1203) },
+ {}
+};
+#endif
+
unsigned short num_cache_leaves;
/* AMD doesn't have CPUID4. Emulate it here to report the same
@@ -182,9 +192,10 @@
static unsigned char levels[] __cpuinitdata = { 1, 1, 2, 3 };
static unsigned char types[] __cpuinitdata = { 1, 2, 3, 3 };
-static void __cpuinit amd_cpuid4(int leaf, union _cpuid4_leaf_eax *eax,
- union _cpuid4_leaf_ebx *ebx,
- union _cpuid4_leaf_ecx *ecx)
+static void __cpuinit
+amd_cpuid4(int leaf, union _cpuid4_leaf_eax *eax,
+ union _cpuid4_leaf_ebx *ebx,
+ union _cpuid4_leaf_ecx *ecx)
{
unsigned dummy;
unsigned line_size, lines_per_tag, assoc, size_in_kb;
@@ -251,27 +262,40 @@
(ebx->split.ways_of_associativity + 1) - 1;
}
-static int __cpuinit cpuid4_cache_lookup(int index, struct _cpuid4_info *this_leaf)
+static void __cpuinit
+amd_check_l3_disable(int index, struct _cpuid4_info *this_leaf)
+{
+ if (index < 3)
+ return;
+ this_leaf->can_disable = 1;
+}
+
+static int
+__cpuinit cpuid4_cache_lookup(int index, struct _cpuid4_info *this_leaf)
{
union _cpuid4_leaf_eax eax;
union _cpuid4_leaf_ebx ebx;
union _cpuid4_leaf_ecx ecx;
unsigned edx;
- if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD)
+ if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) {
amd_cpuid4(index, &eax, &ebx, &ecx);
- else
- cpuid_count(4, index, &eax.full, &ebx.full, &ecx.full, &edx);
+ if (boot_cpu_data.x86 >= 0x10)
+ amd_check_l3_disable(index, this_leaf);
+ } else {
+ cpuid_count(4, index, &eax.full, &ebx.full, &ecx.full, &edx);
+ }
+
if (eax.split.type == CACHE_TYPE_NULL)
return -EIO; /* better error ? */
this_leaf->eax = eax;
this_leaf->ebx = ebx;
this_leaf->ecx = ecx;
- this_leaf->size = (ecx.split.number_of_sets + 1) *
- (ebx.split.coherency_line_size + 1) *
- (ebx.split.physical_line_partition + 1) *
- (ebx.split.ways_of_associativity + 1);
+ this_leaf->size = (ecx.split.number_of_sets + 1) *
+ (ebx.split.coherency_line_size + 1) *
+ (ebx.split.physical_line_partition + 1) *
+ (ebx.split.ways_of_associativity + 1);
return 0;
}
@@ -453,7 +477,7 @@
/* pointer to _cpuid4_info array (for each cache leaf) */
static DEFINE_PER_CPU(struct _cpuid4_info *, cpuid4_info);
-#define CPUID4_INFO_IDX(x, y) (&((per_cpu(cpuid4_info, x))[y]))
+#define CPUID4_INFO_IDX(x, y) (&((per_cpu(cpuid4_info, x))[y]))
#ifdef CONFIG_SMP
static void __cpuinit cache_shared_cpu_map_setup(unsigned int cpu, int index)
@@ -490,7 +514,7 @@
this_leaf = CPUID4_INFO_IDX(cpu, index);
for_each_cpu_mask_nr(sibling, this_leaf->shared_cpu_map) {
- sibling_leaf = CPUID4_INFO_IDX(sibling, index);
+ sibling_leaf = CPUID4_INFO_IDX(sibling, index);
cpu_clear(cpu, sibling_leaf->shared_cpu_map);
}
}
@@ -572,7 +596,7 @@
/* pointer to array of kobjects for cpuX/cache/indexY */
static DEFINE_PER_CPU(struct _index_kobject *, index_kobject);
-#define INDEX_KOBJECT_PTR(x, y) (&((per_cpu(index_kobject, x))[y]))
+#define INDEX_KOBJECT_PTR(x, y) (&((per_cpu(index_kobject, x))[y]))
#define show_one_plus(file_name, object, val) \
static ssize_t show_##file_name \
@@ -637,6 +661,99 @@
}
}
+#define to_object(k) container_of(k, struct _index_kobject, kobj)
+#define to_attr(a) container_of(a, struct _cache_attr, attr)
+
+#ifdef CONFIG_PCI
+static struct pci_dev *get_k8_northbridge(int node)
+{
+ struct pci_dev *dev = NULL;
+ int i;
+
+ for (i = 0; i <= node; i++) {
+ do {
+ dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev);
+ if (!dev)
+ break;
+ } while (!pci_match_id(&k8_nb_id[0], dev));
+ if (!dev)
+ break;
+ }
+ return dev;
+}
+#else
+static struct pci_dev *get_k8_northbridge(int node)
+{
+ return NULL;
+}
+#endif
+
+static ssize_t show_cache_disable(struct _cpuid4_info *this_leaf, char *buf)
+{
+ int node = cpu_to_node(first_cpu(this_leaf->shared_cpu_map));
+ struct pci_dev *dev = NULL;
+ ssize_t ret = 0;
+ int i;
+
+ if (!this_leaf->can_disable)
+ return sprintf(buf, "Feature not enabled\n");
+
+ dev = get_k8_northbridge(node);
+ if (!dev) {
+ printk(KERN_ERR "Attempting AMD northbridge operation on a system with no northbridge\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 2; i++) {
+ unsigned int reg;
+
+ pci_read_config_dword(dev, 0x1BC + i * 4, ®);
+
+ ret += sprintf(buf, "%sEntry: %d\n", buf, i);
+ ret += sprintf(buf, "%sReads: %s\tNew Entries: %s\n",
+ buf,
+ reg & 0x80000000 ? "Disabled" : "Allowed",
+ reg & 0x40000000 ? "Disabled" : "Allowed");
+ ret += sprintf(buf, "%sSubCache: %x\tIndex: %x\n",
+ buf, (reg & 0x30000) >> 16, reg & 0xfff);
+ }
+ return ret;
+}
+
+static ssize_t
+store_cache_disable(struct _cpuid4_info *this_leaf, const char *buf,
+ size_t count)
+{
+ int node = cpu_to_node(first_cpu(this_leaf->shared_cpu_map));
+ struct pci_dev *dev = NULL;
+ unsigned int ret, index, val;
+
+ if (!this_leaf->can_disable)
+ return 0;
+
+ if (strlen(buf) > 15)
+ return -EINVAL;
+
+ ret = sscanf(buf, "%x %x", &index, &val);
+ if (ret != 2)
+ return -EINVAL;
+ if (index > 1)
+ return -EINVAL;
+
+ val |= 0xc0000000;
+ dev = get_k8_northbridge(node);
+ if (!dev) {
+ printk(KERN_ERR "Attempting AMD northbridge operation on a system with no northbridge\n");
+ return -EINVAL;
+ }
+
+ pci_write_config_dword(dev, 0x1BC + index * 4, val & ~0x40000000);
+ wbinvd();
+ pci_write_config_dword(dev, 0x1BC + index * 4, val);
+
+ return 1;
+}
+
struct _cache_attr {
struct attribute attr;
ssize_t (*show)(struct _cpuid4_info *, char *);
@@ -657,6 +774,8 @@
define_one_ro(shared_cpu_map);
define_one_ro(shared_cpu_list);
+static struct _cache_attr cache_disable = __ATTR(cache_disable, 0644, show_cache_disable, store_cache_disable);
+
static struct attribute * default_attrs[] = {
&type.attr,
&level.attr,
@@ -667,12 +786,10 @@
&size.attr,
&shared_cpu_map.attr,
&shared_cpu_list.attr,
+ &cache_disable.attr,
NULL
};
-#define to_object(k) container_of(k, struct _index_kobject, kobj)
-#define to_attr(a) container_of(a, struct _cache_attr, attr)
-
static ssize_t show(struct kobject * kobj, struct attribute * attr, char * buf)
{
struct _cache_attr *fattr = to_attr(attr);
@@ -682,14 +799,22 @@
ret = fattr->show ?
fattr->show(CPUID4_INFO_IDX(this_leaf->cpu, this_leaf->index),
buf) :
- 0;
+ 0;
return ret;
}
static ssize_t store(struct kobject * kobj, struct attribute * attr,
const char * buf, size_t count)
{
- return 0;
+ struct _cache_attr *fattr = to_attr(attr);
+ struct _index_kobject *this_leaf = to_object(kobj);
+ ssize_t ret;
+
+ ret = fattr->store ?
+ fattr->store(CPUID4_INFO_IDX(this_leaf->cpu, this_leaf->index),
+ buf, count) :
+ 0;
+ return ret;
}
static struct sysfs_ops sysfs_ops = {
diff --git a/arch/x86/kernel/cpu/mcheck/mce_64.c b/arch/x86/kernel/cpu/mcheck/mce_64.c
index 726a5fc..4b031a4 100644
--- a/arch/x86/kernel/cpu/mcheck/mce_64.c
+++ b/arch/x86/kernel/cpu/mcheck/mce_64.c
@@ -860,7 +860,7 @@
return err;
}
-static void mce_remove_device(unsigned int cpu)
+static __cpuinit void mce_remove_device(unsigned int cpu)
{
int i;
diff --git a/arch/x86/kernel/cpu/mkcapflags.pl b/arch/x86/kernel/cpu/mkcapflags.pl
new file mode 100644
index 0000000..dfea390
--- /dev/null
+++ b/arch/x86/kernel/cpu/mkcapflags.pl
@@ -0,0 +1,32 @@
+#!/usr/bin/perl
+#
+# Generate the x86_cap_flags[] array from include/asm-x86/cpufeature.h
+#
+
+($in, $out) = @ARGV;
+
+open(IN, "< $in\0") or die "$0: cannot open: $in: $!\n";
+open(OUT, "> $out\0") or die "$0: cannot create: $out: $!\n";
+
+print OUT "#include <asm/cpufeature.h>\n\n";
+print OUT "const char * const x86_cap_flags[NCAPINTS*32] = {\n";
+
+while (defined($line = <IN>)) {
+ if ($line =~ /^\s*\#\s*define\s+(X86_FEATURE_(\S+))\s+(.*)$/) {
+ $macro = $1;
+ $feature = $2;
+ $tail = $3;
+ if ($tail =~ /\/\*\s*\"([^"]*)\".*\*\//) {
+ $feature = $1;
+ }
+
+ if ($feature ne '') {
+ printf OUT "\t%-32s = \"%s\",\n",
+ "[$macro]", "\L$feature";
+ }
+ }
+}
+print OUT "};\n";
+
+close(IN);
+close(OUT);
diff --git a/arch/x86/kernel/cpu/powerflags.c b/arch/x86/kernel/cpu/powerflags.c
new file mode 100644
index 0000000..5abbea2
--- /dev/null
+++ b/arch/x86/kernel/cpu/powerflags.c
@@ -0,0 +1,20 @@
+/*
+ * Strings for the various x86 power flags
+ *
+ * This file must not contain any executable code.
+ */
+
+#include <asm/cpufeature.h>
+
+const char *const x86_power_flags[32] = {
+ "ts", /* temperature sensor */
+ "fid", /* frequency id control */
+ "vid", /* voltage id control */
+ "ttp", /* thermal trip */
+ "tm",
+ "stc",
+ "100mhzsteps",
+ "hwpstate",
+ "", /* tsc invariant mapped to constant_tsc */
+ /* nothing */
+};
diff --git a/arch/x86/kernel/cpu/transmeta.c b/arch/x86/kernel/cpu/transmeta.c
index b911a2c..52b3fef 100644
--- a/arch/x86/kernel/cpu/transmeta.c
+++ b/arch/x86/kernel/cpu/transmeta.c
@@ -5,6 +5,18 @@
#include <asm/msr.h>
#include "cpu.h"
+static void __cpuinit early_init_transmeta(struct cpuinfo_x86 *c)
+{
+ u32 xlvl;
+
+ /* Transmeta-defined flags: level 0x80860001 */
+ xlvl = cpuid_eax(0x80860000);
+ if ((xlvl & 0xffff0000) == 0x80860000) {
+ if (xlvl >= 0x80860001)
+ c->x86_capability[2] = cpuid_edx(0x80860001);
+ }
+}
+
static void __cpuinit init_transmeta(struct cpuinfo_x86 *c)
{
unsigned int cap_mask, uk, max, dummy;
@@ -12,7 +24,8 @@
unsigned int cpu_rev, cpu_freq = 0, cpu_flags, new_cpu_rev;
char cpu_info[65];
- get_model_name(c); /* Same as AMD/Cyrix */
+ early_init_transmeta(c);
+
display_cacheinfo(c);
/* Print CMS and CPU revision */
@@ -85,23 +98,12 @@
#endif
}
-static void __cpuinit transmeta_identify(struct cpuinfo_x86 *c)
-{
- u32 xlvl;
-
- /* Transmeta-defined flags: level 0x80860001 */
- xlvl = cpuid_eax(0x80860000);
- if ((xlvl & 0xffff0000) == 0x80860000) {
- if (xlvl >= 0x80860001)
- c->x86_capability[2] = cpuid_edx(0x80860001);
- }
-}
-
static struct cpu_dev transmeta_cpu_dev __cpuinitdata = {
.c_vendor = "Transmeta",
.c_ident = { "GenuineTMx86", "TransmetaCPU" },
+ .c_early_init = early_init_transmeta,
.c_init = init_transmeta,
- .c_identify = transmeta_identify,
+ .c_x86_vendor = X86_VENDOR_TRANSMETA,
};
-cpu_vendor_dev_register(X86_VENDOR_TRANSMETA, &transmeta_cpu_dev);
+cpu_dev_register(transmeta_cpu_dev);
diff --git a/arch/x86/kernel/cpu/umc.c b/arch/x86/kernel/cpu/umc.c
index b1fc909..e777f79 100644
--- a/arch/x86/kernel/cpu/umc.c
+++ b/arch/x86/kernel/cpu/umc.c
@@ -19,7 +19,8 @@
}
},
},
+ .c_x86_vendor = X86_VENDOR_UMC,
};
-cpu_vendor_dev_register(X86_VENDOR_UMC, &umc_cpu_dev);
+cpu_dev_register(umc_cpu_dev);
diff --git a/arch/x86/kernel/doublefault_32.c b/arch/x86/kernel/doublefault_32.c
index a47798b..b4f14c6 100644
--- a/arch/x86/kernel/doublefault_32.c
+++ b/arch/x86/kernel/doublefault_32.c
@@ -66,6 +66,6 @@
.ds = __USER_DS,
.fs = __KERNEL_PERCPU,
- .__cr3 = __pa(swapper_pg_dir)
+ .__cr3 = __pa_nodebug(swapper_pg_dir),
}
};
diff --git a/arch/x86/kernel/dumpstack_32.c b/arch/x86/kernel/dumpstack_32.c
new file mode 100644
index 0000000..201ee35
--- /dev/null
+++ b/arch/x86/kernel/dumpstack_32.c
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs
+ */
+#include <linux/kallsyms.h>
+#include <linux/kprobes.h>
+#include <linux/uaccess.h>
+#include <linux/utsname.h>
+#include <linux/hardirq.h>
+#include <linux/kdebug.h>
+#include <linux/module.h>
+#include <linux/ptrace.h>
+#include <linux/kexec.h>
+#include <linux/bug.h>
+#include <linux/nmi.h>
+
+#include <asm/stacktrace.h>
+
+#define STACKSLOTS_PER_LINE 8
+#define get_bp(bp) asm("movl %%ebp, %0" : "=r" (bp) :)
+
+int panic_on_unrecovered_nmi;
+int kstack_depth_to_print = 3 * STACKSLOTS_PER_LINE;
+static unsigned int code_bytes = 64;
+static int die_counter;
+
+void printk_address(unsigned long address, int reliable)
+{
+ printk(" [<%p>] %s%pS\n", (void *) address,
+ reliable ? "" : "? ", (void *) address);
+}
+
+static inline int valid_stack_ptr(struct thread_info *tinfo,
+ void *p, unsigned int size, void *end)
+{
+ void *t = tinfo;
+ if (end) {
+ if (p < end && p >= (end-THREAD_SIZE))
+ return 1;
+ else
+ return 0;
+ }
+ return p > t && p < t + THREAD_SIZE - size;
+}
+
+/* The form of the top of the frame on the stack */
+struct stack_frame {
+ struct stack_frame *next_frame;
+ unsigned long return_address;
+};
+
+static inline unsigned long
+print_context_stack(struct thread_info *tinfo,
+ unsigned long *stack, unsigned long bp,
+ const struct stacktrace_ops *ops, void *data,
+ unsigned long *end)
+{
+ struct stack_frame *frame = (struct stack_frame *)bp;
+
+ while (valid_stack_ptr(tinfo, stack, sizeof(*stack), end)) {
+ unsigned long addr;
+
+ addr = *stack;
+ if (__kernel_text_address(addr)) {
+ if ((unsigned long) stack == bp + sizeof(long)) {
+ ops->address(data, addr, 1);
+ frame = frame->next_frame;
+ bp = (unsigned long) frame;
+ } else {
+ ops->address(data, addr, bp == 0);
+ }
+ }
+ stack++;
+ }
+ return bp;
+}
+
+void dump_trace(struct task_struct *task, struct pt_regs *regs,
+ unsigned long *stack, unsigned long bp,
+ const struct stacktrace_ops *ops, void *data)
+{
+ if (!task)
+ task = current;
+
+ if (!stack) {
+ unsigned long dummy;
+ stack = &dummy;
+ if (task && task != current)
+ stack = (unsigned long *)task->thread.sp;
+ }
+
+#ifdef CONFIG_FRAME_POINTER
+ if (!bp) {
+ if (task == current) {
+ /* Grab bp right from our regs */
+ get_bp(bp);
+ } else {
+ /* bp is the last reg pushed by switch_to */
+ bp = *(unsigned long *) task->thread.sp;
+ }
+ }
+#endif
+
+ for (;;) {
+ struct thread_info *context;
+
+ context = (struct thread_info *)
+ ((unsigned long)stack & (~(THREAD_SIZE - 1)));
+ bp = print_context_stack(context, stack, bp, ops, data, NULL);
+
+ stack = (unsigned long *)context->previous_esp;
+ if (!stack)
+ break;
+ if (ops->stack(data, "IRQ") < 0)
+ break;
+ touch_nmi_watchdog();
+ }
+}
+EXPORT_SYMBOL(dump_trace);
+
+static void
+print_trace_warning_symbol(void *data, char *msg, unsigned long symbol)
+{
+ printk(data);
+ print_symbol(msg, symbol);
+ printk("\n");
+}
+
+static void print_trace_warning(void *data, char *msg)
+{
+ printk("%s%s\n", (char *)data, msg);
+}
+
+static int print_trace_stack(void *data, char *name)
+{
+ printk("%s <%s> ", (char *)data, name);
+ return 0;
+}
+
+/*
+ * Print one address/symbol entries per line.
+ */
+static void print_trace_address(void *data, unsigned long addr, int reliable)
+{
+ touch_nmi_watchdog();
+ printk(data);
+ printk_address(addr, reliable);
+}
+
+static const struct stacktrace_ops print_trace_ops = {
+ .warning = print_trace_warning,
+ .warning_symbol = print_trace_warning_symbol,
+ .stack = print_trace_stack,
+ .address = print_trace_address,
+};
+
+static void
+show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs,
+ unsigned long *stack, unsigned long bp, char *log_lvl)
+{
+ printk("%sCall Trace:\n", log_lvl);
+ dump_trace(task, regs, stack, bp, &print_trace_ops, log_lvl);
+}
+
+void show_trace(struct task_struct *task, struct pt_regs *regs,
+ unsigned long *stack, unsigned long bp)
+{
+ show_trace_log_lvl(task, regs, stack, bp, "");
+}
+
+static void
+show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs,
+ unsigned long *sp, unsigned long bp, char *log_lvl)
+{
+ unsigned long *stack;
+ int i;
+
+ if (sp == NULL) {
+ if (task)
+ sp = (unsigned long *)task->thread.sp;
+ else
+ sp = (unsigned long *)&sp;
+ }
+
+ stack = sp;
+ for (i = 0; i < kstack_depth_to_print; i++) {
+ if (kstack_end(stack))
+ break;
+ if (i && ((i % STACKSLOTS_PER_LINE) == 0))
+ printk("\n%s", log_lvl);
+ printk(" %08lx", *stack++);
+ touch_nmi_watchdog();
+ }
+ printk("\n");
+ show_trace_log_lvl(task, regs, sp, bp, log_lvl);
+}
+
+void show_stack(struct task_struct *task, unsigned long *sp)
+{
+ show_stack_log_lvl(task, NULL, sp, 0, "");
+}
+
+/*
+ * The architecture-independent dump_stack generator
+ */
+void dump_stack(void)
+{
+ unsigned long bp = 0;
+ unsigned long stack;
+
+#ifdef CONFIG_FRAME_POINTER
+ if (!bp)
+ get_bp(bp);
+#endif
+
+ printk("Pid: %d, comm: %.20s %s %s %.*s\n",
+ current->pid, current->comm, print_tainted(),
+ init_utsname()->release,
+ (int)strcspn(init_utsname()->version, " "),
+ init_utsname()->version);
+ show_trace(NULL, NULL, &stack, bp);
+}
+
+EXPORT_SYMBOL(dump_stack);
+
+void show_registers(struct pt_regs *regs)
+{
+ int i;
+
+ print_modules();
+ __show_regs(regs, 0);
+
+ printk(KERN_EMERG "Process %.*s (pid: %d, ti=%p task=%p task.ti=%p)\n",
+ TASK_COMM_LEN, current->comm, task_pid_nr(current),
+ current_thread_info(), current, task_thread_info(current));
+ /*
+ * When in-kernel, we also print out the stack and code at the
+ * time of the fault..
+ */
+ if (!user_mode_vm(regs)) {
+ unsigned int code_prologue = code_bytes * 43 / 64;
+ unsigned int code_len = code_bytes;
+ unsigned char c;
+ u8 *ip;
+
+ printk(KERN_EMERG "Stack:\n");
+ show_stack_log_lvl(NULL, regs, ®s->sp,
+ 0, KERN_EMERG);
+
+ printk(KERN_EMERG "Code: ");
+
+ ip = (u8 *)regs->ip - code_prologue;
+ if (ip < (u8 *)PAGE_OFFSET || probe_kernel_address(ip, c)) {
+ /* try starting at IP */
+ ip = (u8 *)regs->ip;
+ code_len = code_len - code_prologue + 1;
+ }
+ for (i = 0; i < code_len; i++, ip++) {
+ if (ip < (u8 *)PAGE_OFFSET ||
+ probe_kernel_address(ip, c)) {
+ printk(" Bad EIP value.");
+ break;
+ }
+ if (ip == (u8 *)regs->ip)
+ printk("<%02x> ", c);
+ else
+ printk("%02x ", c);
+ }
+ }
+ printk("\n");
+}
+
+int is_valid_bugaddr(unsigned long ip)
+{
+ unsigned short ud2;
+
+ if (ip < PAGE_OFFSET)
+ return 0;
+ if (probe_kernel_address((unsigned short *)ip, ud2))
+ return 0;
+
+ return ud2 == 0x0b0f;
+}
+
+static raw_spinlock_t die_lock = __RAW_SPIN_LOCK_UNLOCKED;
+static int die_owner = -1;
+static unsigned int die_nest_count;
+
+unsigned __kprobes long oops_begin(void)
+{
+ unsigned long flags;
+
+ oops_enter();
+
+ if (die_owner != raw_smp_processor_id()) {
+ console_verbose();
+ raw_local_irq_save(flags);
+ __raw_spin_lock(&die_lock);
+ die_owner = smp_processor_id();
+ die_nest_count = 0;
+ bust_spinlocks(1);
+ } else {
+ raw_local_irq_save(flags);
+ }
+ die_nest_count++;
+ return flags;
+}
+
+void __kprobes oops_end(unsigned long flags, struct pt_regs *regs, int signr)
+{
+ bust_spinlocks(0);
+ die_owner = -1;
+ add_taint(TAINT_DIE);
+ __raw_spin_unlock(&die_lock);
+ raw_local_irq_restore(flags);
+
+ if (!regs)
+ return;
+
+ if (kexec_should_crash(current))
+ crash_kexec(regs);
+ if (in_interrupt())
+ panic("Fatal exception in interrupt");
+ if (panic_on_oops)
+ panic("Fatal exception");
+ oops_exit();
+ do_exit(signr);
+}
+
+int __kprobes __die(const char *str, struct pt_regs *regs, long err)
+{
+ unsigned short ss;
+ unsigned long sp;
+
+ printk(KERN_EMERG "%s: %04lx [#%d] ", str, err & 0xffff, ++die_counter);
+#ifdef CONFIG_PREEMPT
+ printk("PREEMPT ");
+#endif
+#ifdef CONFIG_SMP
+ printk("SMP ");
+#endif
+#ifdef CONFIG_DEBUG_PAGEALLOC
+ printk("DEBUG_PAGEALLOC");
+#endif
+ printk("\n");
+ if (notify_die(DIE_OOPS, str, regs, err,
+ current->thread.trap_no, SIGSEGV) == NOTIFY_STOP)
+ return 1;
+
+ show_registers(regs);
+ /* Executive summary in case the oops scrolled away */
+ sp = (unsigned long) (®s->sp);
+ savesegment(ss, ss);
+ if (user_mode(regs)) {
+ sp = regs->sp;
+ ss = regs->ss & 0xffff;
+ }
+ printk(KERN_EMERG "EIP: [<%08lx>] ", regs->ip);
+ print_symbol("%s", regs->ip);
+ printk(" SS:ESP %04x:%08lx\n", ss, sp);
+ return 0;
+}
+
+/*
+ * This is gone through when something in the kernel has done something bad
+ * and is about to be terminated:
+ */
+void die(const char *str, struct pt_regs *regs, long err)
+{
+ unsigned long flags = oops_begin();
+
+ if (die_nest_count < 3) {
+ report_bug(regs->ip, regs);
+
+ if (__die(str, regs, err))
+ regs = NULL;
+ } else {
+ printk(KERN_EMERG "Recursive die() failure, output suppressed\n");
+ }
+
+ oops_end(flags, regs, SIGSEGV);
+}
+
+static DEFINE_SPINLOCK(nmi_print_lock);
+
+void notrace __kprobes
+die_nmi(char *str, struct pt_regs *regs, int do_panic)
+{
+ if (notify_die(DIE_NMIWATCHDOG, str, regs, 0, 2, SIGINT) == NOTIFY_STOP)
+ return;
+
+ spin_lock(&nmi_print_lock);
+ /*
+ * We are in trouble anyway, lets at least try
+ * to get a message out:
+ */
+ bust_spinlocks(1);
+ printk(KERN_EMERG "%s", str);
+ printk(" on CPU%d, ip %08lx, registers:\n",
+ smp_processor_id(), regs->ip);
+ show_registers(regs);
+ if (do_panic)
+ panic("Non maskable interrupt");
+ console_silent();
+ spin_unlock(&nmi_print_lock);
+ bust_spinlocks(0);
+
+ /*
+ * If we are in kernel we are probably nested up pretty bad
+ * and might aswell get out now while we still can:
+ */
+ if (!user_mode_vm(regs)) {
+ current->thread.trap_no = 2;
+ crash_kexec(regs);
+ }
+
+ do_exit(SIGSEGV);
+}
+
+static int __init oops_setup(char *s)
+{
+ if (!s)
+ return -EINVAL;
+ if (!strcmp(s, "panic"))
+ panic_on_oops = 1;
+ return 0;
+}
+early_param("oops", oops_setup);
+
+static int __init kstack_setup(char *s)
+{
+ if (!s)
+ return -EINVAL;
+ kstack_depth_to_print = simple_strtoul(s, NULL, 0);
+ return 0;
+}
+early_param("kstack", kstack_setup);
+
+static int __init code_bytes_setup(char *s)
+{
+ code_bytes = simple_strtoul(s, NULL, 0);
+ if (code_bytes > 8192)
+ code_bytes = 8192;
+
+ return 1;
+}
+__setup("code_bytes=", code_bytes_setup);
diff --git a/arch/x86/kernel/dumpstack_64.c b/arch/x86/kernel/dumpstack_64.c
new file mode 100644
index 0000000..086cc81
--- /dev/null
+++ b/arch/x86/kernel/dumpstack_64.c
@@ -0,0 +1,573 @@
+/*
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs
+ */
+#include <linux/kallsyms.h>
+#include <linux/kprobes.h>
+#include <linux/uaccess.h>
+#include <linux/utsname.h>
+#include <linux/hardirq.h>
+#include <linux/kdebug.h>
+#include <linux/module.h>
+#include <linux/ptrace.h>
+#include <linux/kexec.h>
+#include <linux/bug.h>
+#include <linux/nmi.h>
+
+#include <asm/stacktrace.h>
+
+#define STACKSLOTS_PER_LINE 4
+#define get_bp(bp) asm("movq %%rbp, %0" : "=r" (bp) :)
+
+int panic_on_unrecovered_nmi;
+int kstack_depth_to_print = 3 * STACKSLOTS_PER_LINE;
+static unsigned int code_bytes = 64;
+static int die_counter;
+
+void printk_address(unsigned long address, int reliable)
+{
+ printk(" [<%p>] %s%pS\n", (void *) address,
+ reliable ? "" : "? ", (void *) address);
+}
+
+static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack,
+ unsigned *usedp, char **idp)
+{
+ static char ids[][8] = {
+ [DEBUG_STACK - 1] = "#DB",
+ [NMI_STACK - 1] = "NMI",
+ [DOUBLEFAULT_STACK - 1] = "#DF",
+ [STACKFAULT_STACK - 1] = "#SS",
+ [MCE_STACK - 1] = "#MC",
+#if DEBUG_STKSZ > EXCEPTION_STKSZ
+ [N_EXCEPTION_STACKS ...
+ N_EXCEPTION_STACKS + DEBUG_STKSZ / EXCEPTION_STKSZ - 2] = "#DB[?]"
+#endif
+ };
+ unsigned k;
+
+ /*
+ * Iterate over all exception stacks, and figure out whether
+ * 'stack' is in one of them:
+ */
+ for (k = 0; k < N_EXCEPTION_STACKS; k++) {
+ unsigned long end = per_cpu(orig_ist, cpu).ist[k];
+ /*
+ * Is 'stack' above this exception frame's end?
+ * If yes then skip to the next frame.
+ */
+ if (stack >= end)
+ continue;
+ /*
+ * Is 'stack' above this exception frame's start address?
+ * If yes then we found the right frame.
+ */
+ if (stack >= end - EXCEPTION_STKSZ) {
+ /*
+ * Make sure we only iterate through an exception
+ * stack once. If it comes up for the second time
+ * then there's something wrong going on - just
+ * break out and return NULL:
+ */
+ if (*usedp & (1U << k))
+ break;
+ *usedp |= 1U << k;
+ *idp = ids[k];
+ return (unsigned long *)end;
+ }
+ /*
+ * If this is a debug stack, and if it has a larger size than
+ * the usual exception stacks, then 'stack' might still
+ * be within the lower portion of the debug stack:
+ */
+#if DEBUG_STKSZ > EXCEPTION_STKSZ
+ if (k == DEBUG_STACK - 1 && stack >= end - DEBUG_STKSZ) {
+ unsigned j = N_EXCEPTION_STACKS - 1;
+
+ /*
+ * Black magic. A large debug stack is composed of
+ * multiple exception stack entries, which we
+ * iterate through now. Dont look:
+ */
+ do {
+ ++j;
+ end -= EXCEPTION_STKSZ;
+ ids[j][4] = '1' + (j - N_EXCEPTION_STACKS);
+ } while (stack < end - EXCEPTION_STKSZ);
+ if (*usedp & (1U << j))
+ break;
+ *usedp |= 1U << j;
+ *idp = ids[j];
+ return (unsigned long *)end;
+ }
+#endif
+ }
+ return NULL;
+}
+
+/*
+ * x86-64 can have up to three kernel stacks:
+ * process stack
+ * interrupt stack
+ * severe exception (double fault, nmi, stack fault, debug, mce) hardware stack
+ */
+
+static inline int valid_stack_ptr(struct thread_info *tinfo,
+ void *p, unsigned int size, void *end)
+{
+ void *t = tinfo;
+ if (end) {
+ if (p < end && p >= (end-THREAD_SIZE))
+ return 1;
+ else
+ return 0;
+ }
+ return p > t && p < t + THREAD_SIZE - size;
+}
+
+/* The form of the top of the frame on the stack */
+struct stack_frame {
+ struct stack_frame *next_frame;
+ unsigned long return_address;
+};
+
+static inline unsigned long
+print_context_stack(struct thread_info *tinfo,
+ unsigned long *stack, unsigned long bp,
+ const struct stacktrace_ops *ops, void *data,
+ unsigned long *end)
+{
+ struct stack_frame *frame = (struct stack_frame *)bp;
+
+ while (valid_stack_ptr(tinfo, stack, sizeof(*stack), end)) {
+ unsigned long addr;
+
+ addr = *stack;
+ if (__kernel_text_address(addr)) {
+ if ((unsigned long) stack == bp + sizeof(long)) {
+ ops->address(data, addr, 1);
+ frame = frame->next_frame;
+ bp = (unsigned long) frame;
+ } else {
+ ops->address(data, addr, bp == 0);
+ }
+ }
+ stack++;
+ }
+ return bp;
+}
+
+void dump_trace(struct task_struct *task, struct pt_regs *regs,
+ unsigned long *stack, unsigned long bp,
+ const struct stacktrace_ops *ops, void *data)
+{
+ const unsigned cpu = get_cpu();
+ unsigned long *irqstack_end = (unsigned long *)cpu_pda(cpu)->irqstackptr;
+ unsigned used = 0;
+ struct thread_info *tinfo;
+
+ if (!task)
+ task = current;
+
+ if (!stack) {
+ unsigned long dummy;
+ stack = &dummy;
+ if (task && task != current)
+ stack = (unsigned long *)task->thread.sp;
+ }
+
+#ifdef CONFIG_FRAME_POINTER
+ if (!bp) {
+ if (task == current) {
+ /* Grab bp right from our regs */
+ get_bp(bp);
+ } else {
+ /* bp is the last reg pushed by switch_to */
+ bp = *(unsigned long *) task->thread.sp;
+ }
+ }
+#endif
+
+ /*
+ * Print function call entries in all stacks, starting at the
+ * current stack address. If the stacks consist of nested
+ * exceptions
+ */
+ tinfo = task_thread_info(task);
+ for (;;) {
+ char *id;
+ unsigned long *estack_end;
+ estack_end = in_exception_stack(cpu, (unsigned long)stack,
+ &used, &id);
+
+ if (estack_end) {
+ if (ops->stack(data, id) < 0)
+ break;
+
+ bp = print_context_stack(tinfo, stack, bp, ops,
+ data, estack_end);
+ ops->stack(data, "<EOE>");
+ /*
+ * We link to the next stack via the
+ * second-to-last pointer (index -2 to end) in the
+ * exception stack:
+ */
+ stack = (unsigned long *) estack_end[-2];
+ continue;
+ }
+ if (irqstack_end) {
+ unsigned long *irqstack;
+ irqstack = irqstack_end -
+ (IRQSTACKSIZE - 64) / sizeof(*irqstack);
+
+ if (stack >= irqstack && stack < irqstack_end) {
+ if (ops->stack(data, "IRQ") < 0)
+ break;
+ bp = print_context_stack(tinfo, stack, bp,
+ ops, data, irqstack_end);
+ /*
+ * We link to the next stack (which would be
+ * the process stack normally) the last
+ * pointer (index -1 to end) in the IRQ stack:
+ */
+ stack = (unsigned long *) (irqstack_end[-1]);
+ irqstack_end = NULL;
+ ops->stack(data, "EOI");
+ continue;
+ }
+ }
+ break;
+ }
+
+ /*
+ * This handles the process stack:
+ */
+ bp = print_context_stack(tinfo, stack, bp, ops, data, NULL);
+ put_cpu();
+}
+EXPORT_SYMBOL(dump_trace);
+
+static void
+print_trace_warning_symbol(void *data, char *msg, unsigned long symbol)
+{
+ printk(data);
+ print_symbol(msg, symbol);
+ printk("\n");
+}
+
+static void print_trace_warning(void *data, char *msg)
+{
+ printk("%s%s\n", (char *)data, msg);
+}
+
+static int print_trace_stack(void *data, char *name)
+{
+ printk("%s <%s> ", (char *)data, name);
+ return 0;
+}
+
+/*
+ * Print one address/symbol entries per line.
+ */
+static void print_trace_address(void *data, unsigned long addr, int reliable)
+{
+ touch_nmi_watchdog();
+ printk(data);
+ printk_address(addr, reliable);
+}
+
+static const struct stacktrace_ops print_trace_ops = {
+ .warning = print_trace_warning,
+ .warning_symbol = print_trace_warning_symbol,
+ .stack = print_trace_stack,
+ .address = print_trace_address,
+};
+
+static void
+show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs,
+ unsigned long *stack, unsigned long bp, char *log_lvl)
+{
+ printk("%sCall Trace:\n", log_lvl);
+ dump_trace(task, regs, stack, bp, &print_trace_ops, log_lvl);
+}
+
+void show_trace(struct task_struct *task, struct pt_regs *regs,
+ unsigned long *stack, unsigned long bp)
+{
+ show_trace_log_lvl(task, regs, stack, bp, "");
+}
+
+static void
+show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs,
+ unsigned long *sp, unsigned long bp, char *log_lvl)
+{
+ unsigned long *stack;
+ int i;
+ const int cpu = smp_processor_id();
+ unsigned long *irqstack_end =
+ (unsigned long *) (cpu_pda(cpu)->irqstackptr);
+ unsigned long *irqstack =
+ (unsigned long *) (cpu_pda(cpu)->irqstackptr - IRQSTACKSIZE);
+
+ /*
+ * debugging aid: "show_stack(NULL, NULL);" prints the
+ * back trace for this cpu.
+ */
+
+ if (sp == NULL) {
+ if (task)
+ sp = (unsigned long *)task->thread.sp;
+ else
+ sp = (unsigned long *)&sp;
+ }
+
+ stack = sp;
+ for (i = 0; i < kstack_depth_to_print; i++) {
+ if (stack >= irqstack && stack <= irqstack_end) {
+ if (stack == irqstack_end) {
+ stack = (unsigned long *) (irqstack_end[-1]);
+ printk(" <EOI> ");
+ }
+ } else {
+ if (((long) stack & (THREAD_SIZE-1)) == 0)
+ break;
+ }
+ if (i && ((i % STACKSLOTS_PER_LINE) == 0))
+ printk("\n%s", log_lvl);
+ printk(" %016lx", *stack++);
+ touch_nmi_watchdog();
+ }
+ printk("\n");
+ show_trace_log_lvl(task, regs, sp, bp, log_lvl);
+}
+
+void show_stack(struct task_struct *task, unsigned long *sp)
+{
+ show_stack_log_lvl(task, NULL, sp, 0, "");
+}
+
+/*
+ * The architecture-independent dump_stack generator
+ */
+void dump_stack(void)
+{
+ unsigned long bp = 0;
+ unsigned long stack;
+
+#ifdef CONFIG_FRAME_POINTER
+ if (!bp)
+ get_bp(bp);
+#endif
+
+ printk("Pid: %d, comm: %.20s %s %s %.*s\n",
+ current->pid, current->comm, print_tainted(),
+ init_utsname()->release,
+ (int)strcspn(init_utsname()->version, " "),
+ init_utsname()->version);
+ show_trace(NULL, NULL, &stack, bp);
+}
+EXPORT_SYMBOL(dump_stack);
+
+void show_registers(struct pt_regs *regs)
+{
+ int i;
+ unsigned long sp;
+ const int cpu = smp_processor_id();
+ struct task_struct *cur = cpu_pda(cpu)->pcurrent;
+
+ sp = regs->sp;
+ printk("CPU %d ", cpu);
+ __show_regs(regs, 1);
+ printk("Process %s (pid: %d, threadinfo %p, task %p)\n",
+ cur->comm, cur->pid, task_thread_info(cur), cur);
+
+ /*
+ * When in-kernel, we also print out the stack and code at the
+ * time of the fault..
+ */
+ if (!user_mode(regs)) {
+ unsigned int code_prologue = code_bytes * 43 / 64;
+ unsigned int code_len = code_bytes;
+ unsigned char c;
+ u8 *ip;
+
+ printk(KERN_EMERG "Stack:\n");
+ show_stack_log_lvl(NULL, regs, (unsigned long *)sp,
+ regs->bp, KERN_EMERG);
+
+ printk(KERN_EMERG "Code: ");
+
+ ip = (u8 *)regs->ip - code_prologue;
+ if (ip < (u8 *)PAGE_OFFSET || probe_kernel_address(ip, c)) {
+ /* try starting at IP */
+ ip = (u8 *)regs->ip;
+ code_len = code_len - code_prologue + 1;
+ }
+ for (i = 0; i < code_len; i++, ip++) {
+ if (ip < (u8 *)PAGE_OFFSET ||
+ probe_kernel_address(ip, c)) {
+ printk(" Bad RIP value.");
+ break;
+ }
+ if (ip == (u8 *)regs->ip)
+ printk("<%02x> ", c);
+ else
+ printk("%02x ", c);
+ }
+ }
+ printk("\n");
+}
+
+int is_valid_bugaddr(unsigned long ip)
+{
+ unsigned short ud2;
+
+ if (__copy_from_user(&ud2, (const void __user *) ip, sizeof(ud2)))
+ return 0;
+
+ return ud2 == 0x0b0f;
+}
+
+static raw_spinlock_t die_lock = __RAW_SPIN_LOCK_UNLOCKED;
+static int die_owner = -1;
+static unsigned int die_nest_count;
+
+unsigned __kprobes long oops_begin(void)
+{
+ int cpu;
+ unsigned long flags;
+
+ oops_enter();
+
+ /* racy, but better than risking deadlock. */
+ raw_local_irq_save(flags);
+ cpu = smp_processor_id();
+ if (!__raw_spin_trylock(&die_lock)) {
+ if (cpu == die_owner)
+ /* nested oops. should stop eventually */;
+ else
+ __raw_spin_lock(&die_lock);
+ }
+ die_nest_count++;
+ die_owner = cpu;
+ console_verbose();
+ bust_spinlocks(1);
+ return flags;
+}
+
+void __kprobes oops_end(unsigned long flags, struct pt_regs *regs, int signr)
+{
+ die_owner = -1;
+ bust_spinlocks(0);
+ die_nest_count--;
+ if (!die_nest_count)
+ /* Nest count reaches zero, release the lock. */
+ __raw_spin_unlock(&die_lock);
+ raw_local_irq_restore(flags);
+ if (!regs) {
+ oops_exit();
+ return;
+ }
+ if (in_interrupt())
+ panic("Fatal exception in interrupt");
+ if (panic_on_oops)
+ panic("Fatal exception");
+ oops_exit();
+ do_exit(signr);
+}
+
+int __kprobes __die(const char *str, struct pt_regs *regs, long err)
+{
+ printk(KERN_EMERG "%s: %04lx [#%d] ", str, err & 0xffff, ++die_counter);
+#ifdef CONFIG_PREEMPT
+ printk("PREEMPT ");
+#endif
+#ifdef CONFIG_SMP
+ printk("SMP ");
+#endif
+#ifdef CONFIG_DEBUG_PAGEALLOC
+ printk("DEBUG_PAGEALLOC");
+#endif
+ printk("\n");
+ if (notify_die(DIE_OOPS, str, regs, err,
+ current->thread.trap_no, SIGSEGV) == NOTIFY_STOP)
+ return 1;
+
+ show_registers(regs);
+ add_taint(TAINT_DIE);
+ /* Executive summary in case the oops scrolled away */
+ printk(KERN_ALERT "RIP ");
+ printk_address(regs->ip, 1);
+ printk(" RSP <%016lx>\n", regs->sp);
+ if (kexec_should_crash(current))
+ crash_kexec(regs);
+ return 0;
+}
+
+void die(const char *str, struct pt_regs *regs, long err)
+{
+ unsigned long flags = oops_begin();
+
+ if (!user_mode(regs))
+ report_bug(regs->ip, regs);
+
+ if (__die(str, regs, err))
+ regs = NULL;
+ oops_end(flags, regs, SIGSEGV);
+}
+
+notrace __kprobes void
+die_nmi(char *str, struct pt_regs *regs, int do_panic)
+{
+ unsigned long flags;
+
+ if (notify_die(DIE_NMIWATCHDOG, str, regs, 0, 2, SIGINT) == NOTIFY_STOP)
+ return;
+
+ flags = oops_begin();
+ /*
+ * We are in trouble anyway, lets at least try
+ * to get a message out.
+ */
+ printk(KERN_EMERG "%s", str);
+ printk(" on CPU%d, ip %08lx, registers:\n",
+ smp_processor_id(), regs->ip);
+ show_registers(regs);
+ if (kexec_should_crash(current))
+ crash_kexec(regs);
+ if (do_panic || panic_on_oops)
+ panic("Non maskable interrupt");
+ oops_end(flags, NULL, SIGBUS);
+ nmi_exit();
+ local_irq_enable();
+ do_exit(SIGBUS);
+}
+
+static int __init oops_setup(char *s)
+{
+ if (!s)
+ return -EINVAL;
+ if (!strcmp(s, "panic"))
+ panic_on_oops = 1;
+ return 0;
+}
+early_param("oops", oops_setup);
+
+static int __init kstack_setup(char *s)
+{
+ if (!s)
+ return -EINVAL;
+ kstack_depth_to_print = simple_strtoul(s, NULL, 0);
+ return 0;
+}
+early_param("kstack", kstack_setup);
+
+static int __init code_bytes_setup(char *s)
+{
+ code_bytes = simple_strtoul(s, NULL, 0);
+ if (code_bytes > 8192)
+ code_bytes = 8192;
+
+ return 1;
+}
+__setup("code_bytes=", code_bytes_setup);
diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c
index 66e48aa..78e642f 100644
--- a/arch/x86/kernel/e820.c
+++ b/arch/x86/kernel/e820.c
@@ -148,6 +148,9 @@
case E820_NVS:
printk(KERN_CONT "(ACPI NVS)\n");
break;
+ case E820_UNUSABLE:
+ printk("(unusable)\n");
+ break;
default:
printk(KERN_CONT "type %u\n", e820.map[i].type);
break;
@@ -1260,6 +1263,7 @@
case E820_RAM: return "System RAM";
case E820_ACPI: return "ACPI Tables";
case E820_NVS: return "ACPI Non-volatile Storage";
+ case E820_UNUSABLE: return "Unusable memory";
default: return "reserved";
}
}
@@ -1267,6 +1271,7 @@
/*
* Mark e820 reserved areas as busy for the resource manager.
*/
+static struct resource __initdata *e820_res;
void __init e820_reserve_resources(void)
{
int i;
@@ -1274,6 +1279,7 @@
u64 end;
res = alloc_bootmem_low(sizeof(struct resource) * e820.nr_map);
+ e820_res = res;
for (i = 0; i < e820.nr_map; i++) {
end = e820.map[i].addr + e820.map[i].size - 1;
#ifndef CONFIG_RESOURCES_64BIT
@@ -1287,7 +1293,14 @@
res->end = end;
res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
- insert_resource(&iomem_resource, res);
+
+ /*
+ * don't register the region that could be conflicted with
+ * pci device BAR resource and insert them later in
+ * pcibios_resource_survey()
+ */
+ if (e820.map[i].type != E820_RESERVED || res->start < (1ULL<<20))
+ insert_resource(&iomem_resource, res);
res++;
}
@@ -1299,6 +1312,19 @@
}
}
+void __init e820_reserve_resources_late(void)
+{
+ int i;
+ struct resource *res;
+
+ res = e820_res;
+ for (i = 0; i < e820.nr_map; i++) {
+ if (!res->parent && res->end)
+ reserve_region_with_split(&iomem_resource, res->start, res->end, res->name);
+ res++;
+ }
+}
+
char *__init default_machine_specific_memory_setup(void)
{
char *who = "BIOS-e820";
diff --git a/arch/x86/kernel/early-quirks.c b/arch/x86/kernel/early-quirks.c
index 24bb5fa..733c4f8 100644
--- a/arch/x86/kernel/early-quirks.c
+++ b/arch/x86/kernel/early-quirks.c
@@ -95,6 +95,52 @@
}
+static u32 ati_ixp4x0_rev(int num, int slot, int func)
+{
+ u32 d;
+ u8 b;
+
+ b = read_pci_config_byte(num, slot, func, 0xac);
+ b &= ~(1<<5);
+ write_pci_config_byte(num, slot, func, 0xac, b);
+
+ d = read_pci_config(num, slot, func, 0x70);
+ d |= 1<<8;
+ write_pci_config(num, slot, func, 0x70, d);
+
+ d = read_pci_config(num, slot, func, 0x8);
+ d &= 0xff;
+ return d;
+}
+
+static void __init ati_bugs(int num, int slot, int func)
+{
+#if defined(CONFIG_ACPI) && defined (CONFIG_X86_IO_APIC)
+ u32 d;
+ u8 b;
+
+ if (acpi_use_timer_override)
+ return;
+
+ d = ati_ixp4x0_rev(num, slot, func);
+ if (d < 0x82)
+ acpi_skip_timer_override = 1;
+ else {
+ /* check for IRQ0 interrupt swap */
+ outb(0x72, 0xcd6); b = inb(0xcd7);
+ if (!(b & 0x2))
+ acpi_skip_timer_override = 1;
+ }
+
+ if (acpi_skip_timer_override) {
+ printk(KERN_INFO "SB4X0 revision 0x%x\n", d);
+ printk(KERN_INFO "Ignoring ACPI timer override.\n");
+ printk(KERN_INFO "If you got timer trouble "
+ "try acpi_use_timer_override\n");
+ }
+#endif
+}
+
#ifdef CONFIG_DMAR
static void __init intel_g33_dmar(int num, int slot, int func)
{
@@ -128,6 +174,8 @@
PCI_CLASS_BRIDGE_PCI, PCI_ANY_ID, QFLAG_APPLY_ONCE, via_bugs },
{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_NB,
PCI_CLASS_BRIDGE_HOST, PCI_ANY_ID, 0, fix_hypertransport_config },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS,
+ PCI_CLASS_SERIAL_SMBUS, PCI_ANY_ID, 0, ati_bugs },
#ifdef CONFIG_DMAR
{ PCI_VENDOR_ID_INTEL, 0x29c0,
PCI_CLASS_BRIDGE_HOST, PCI_ANY_ID, 0, intel_g33_dmar },
diff --git a/arch/x86/kernel/early_printk.c b/arch/x86/kernel/early_printk.c
index ff9e735..34ad997 100644
--- a/arch/x86/kernel/early_printk.c
+++ b/arch/x86/kernel/early_printk.c
@@ -3,11 +3,19 @@
#include <linux/init.h>
#include <linux/string.h>
#include <linux/screen_info.h>
+#include <linux/usb/ch9.h>
+#include <linux/pci_regs.h>
+#include <linux/pci_ids.h>
+#include <linux/errno.h>
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/fcntl.h>
#include <asm/setup.h>
#include <xen/hvc-console.h>
+#include <asm/pci-direct.h>
+#include <asm/pgtable.h>
+#include <asm/fixmap.h>
+#include <linux/usb/ehci_def.h>
/* Simple VGA output */
#define VGABASE (__ISA_IO_base + 0xb8000)
@@ -78,6 +86,7 @@
static int early_serial_putc(unsigned char ch)
{
unsigned timeout = 0xffff;
+
while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout)
cpu_relax();
outb(ch, early_serial_base + TXR);
@@ -111,7 +120,7 @@
if (!strncmp(s, "0x", 2)) {
early_serial_base = simple_strtoul(s, &e, 16);
} else {
- static int bases[] = { 0x3f8, 0x2f8 };
+ static const int __initconst bases[] = { 0x3f8, 0x2f8 };
if (!strncmp(s, "ttyS", 4))
s += 4;
@@ -151,6 +160,721 @@
.index = -1,
};
+#ifdef CONFIG_EARLY_PRINTK_DBGP
+
+static struct ehci_caps __iomem *ehci_caps;
+static struct ehci_regs __iomem *ehci_regs;
+static struct ehci_dbg_port __iomem *ehci_debug;
+static unsigned int dbgp_endpoint_out;
+
+struct ehci_dev {
+ u32 bus;
+ u32 slot;
+ u32 func;
+};
+
+static struct ehci_dev ehci_dev;
+
+#define USB_DEBUG_DEVNUM 127
+
+#define DBGP_DATA_TOGGLE 0x8800
+
+static inline u32 dbgp_pid_update(u32 x, u32 tok)
+{
+ return ((x ^ DBGP_DATA_TOGGLE) & 0xffff00) | (tok & 0xff);
+}
+
+static inline u32 dbgp_len_update(u32 x, u32 len)
+{
+ return (x & ~0x0f) | (len & 0x0f);
+}
+
+/*
+ * USB Packet IDs (PIDs)
+ */
+
+/* token */
+#define USB_PID_OUT 0xe1
+#define USB_PID_IN 0x69
+#define USB_PID_SOF 0xa5
+#define USB_PID_SETUP 0x2d
+/* handshake */
+#define USB_PID_ACK 0xd2
+#define USB_PID_NAK 0x5a
+#define USB_PID_STALL 0x1e
+#define USB_PID_NYET 0x96
+/* data */
+#define USB_PID_DATA0 0xc3
+#define USB_PID_DATA1 0x4b
+#define USB_PID_DATA2 0x87
+#define USB_PID_MDATA 0x0f
+/* Special */
+#define USB_PID_PREAMBLE 0x3c
+#define USB_PID_ERR 0x3c
+#define USB_PID_SPLIT 0x78
+#define USB_PID_PING 0xb4
+#define USB_PID_UNDEF_0 0xf0
+
+#define USB_PID_DATA_TOGGLE 0x88
+#define DBGP_CLAIM (DBGP_OWNER | DBGP_ENABLED | DBGP_INUSE)
+
+#define PCI_CAP_ID_EHCI_DEBUG 0xa
+
+#define HUB_ROOT_RESET_TIME 50 /* times are in msec */
+#define HUB_SHORT_RESET_TIME 10
+#define HUB_LONG_RESET_TIME 200
+#define HUB_RESET_TIMEOUT 500
+
+#define DBGP_MAX_PACKET 8
+
+static int dbgp_wait_until_complete(void)
+{
+ u32 ctrl;
+ int loop = 0x100000;
+
+ do {
+ ctrl = readl(&ehci_debug->control);
+ /* Stop when the transaction is finished */
+ if (ctrl & DBGP_DONE)
+ break;
+ } while (--loop > 0);
+
+ if (!loop)
+ return -1;
+
+ /*
+ * Now that we have observed the completed transaction,
+ * clear the done bit.
+ */
+ writel(ctrl | DBGP_DONE, &ehci_debug->control);
+ return (ctrl & DBGP_ERROR) ? -DBGP_ERRCODE(ctrl) : DBGP_LEN(ctrl);
+}
+
+static void dbgp_mdelay(int ms)
+{
+ int i;
+
+ while (ms--) {
+ for (i = 0; i < 1000; i++)
+ outb(0x1, 0x80);
+ }
+}
+
+static void dbgp_breath(void)
+{
+ /* Sleep to give the debug port a chance to breathe */
+}
+
+static int dbgp_wait_until_done(unsigned ctrl)
+{
+ u32 pids, lpid;
+ int ret;
+ int loop = 3;
+
+retry:
+ writel(ctrl | DBGP_GO, &ehci_debug->control);
+ ret = dbgp_wait_until_complete();
+ pids = readl(&ehci_debug->pids);
+ lpid = DBGP_PID_GET(pids);
+
+ if (ret < 0)
+ return ret;
+
+ /*
+ * If the port is getting full or it has dropped data
+ * start pacing ourselves, not necessary but it's friendly.
+ */
+ if ((lpid == USB_PID_NAK) || (lpid == USB_PID_NYET))
+ dbgp_breath();
+
+ /* If I get a NACK reissue the transmission */
+ if (lpid == USB_PID_NAK) {
+ if (--loop > 0)
+ goto retry;
+ }
+
+ return ret;
+}
+
+static void dbgp_set_data(const void *buf, int size)
+{
+ const unsigned char *bytes = buf;
+ u32 lo, hi;
+ int i;
+
+ lo = hi = 0;
+ for (i = 0; i < 4 && i < size; i++)
+ lo |= bytes[i] << (8*i);
+ for (; i < 8 && i < size; i++)
+ hi |= bytes[i] << (8*(i - 4));
+ writel(lo, &ehci_debug->data03);
+ writel(hi, &ehci_debug->data47);
+}
+
+static void dbgp_get_data(void *buf, int size)
+{
+ unsigned char *bytes = buf;
+ u32 lo, hi;
+ int i;
+
+ lo = readl(&ehci_debug->data03);
+ hi = readl(&ehci_debug->data47);
+ for (i = 0; i < 4 && i < size; i++)
+ bytes[i] = (lo >> (8*i)) & 0xff;
+ for (; i < 8 && i < size; i++)
+ bytes[i] = (hi >> (8*(i - 4))) & 0xff;
+}
+
+static int dbgp_bulk_write(unsigned devnum, unsigned endpoint,
+ const char *bytes, int size)
+{
+ u32 pids, addr, ctrl;
+ int ret;
+
+ if (size > DBGP_MAX_PACKET)
+ return -1;
+
+ addr = DBGP_EPADDR(devnum, endpoint);
+
+ pids = readl(&ehci_debug->pids);
+ pids = dbgp_pid_update(pids, USB_PID_OUT);
+
+ ctrl = readl(&ehci_debug->control);
+ ctrl = dbgp_len_update(ctrl, size);
+ ctrl |= DBGP_OUT;
+ ctrl |= DBGP_GO;
+
+ dbgp_set_data(bytes, size);
+ writel(addr, &ehci_debug->address);
+ writel(pids, &ehci_debug->pids);
+
+ ret = dbgp_wait_until_done(ctrl);
+ if (ret < 0)
+ return ret;
+
+ return ret;
+}
+
+static int dbgp_bulk_read(unsigned devnum, unsigned endpoint, void *data,
+ int size)
+{
+ u32 pids, addr, ctrl;
+ int ret;
+
+ if (size > DBGP_MAX_PACKET)
+ return -1;
+
+ addr = DBGP_EPADDR(devnum, endpoint);
+
+ pids = readl(&ehci_debug->pids);
+ pids = dbgp_pid_update(pids, USB_PID_IN);
+
+ ctrl = readl(&ehci_debug->control);
+ ctrl = dbgp_len_update(ctrl, size);
+ ctrl &= ~DBGP_OUT;
+ ctrl |= DBGP_GO;
+
+ writel(addr, &ehci_debug->address);
+ writel(pids, &ehci_debug->pids);
+ ret = dbgp_wait_until_done(ctrl);
+ if (ret < 0)
+ return ret;
+
+ if (size > ret)
+ size = ret;
+ dbgp_get_data(data, size);
+ return ret;
+}
+
+static int dbgp_control_msg(unsigned devnum, int requesttype, int request,
+ int value, int index, void *data, int size)
+{
+ u32 pids, addr, ctrl;
+ struct usb_ctrlrequest req;
+ int read;
+ int ret;
+
+ read = (requesttype & USB_DIR_IN) != 0;
+ if (size > (read ? DBGP_MAX_PACKET:0))
+ return -1;
+
+ /* Compute the control message */
+ req.bRequestType = requesttype;
+ req.bRequest = request;
+ req.wValue = cpu_to_le16(value);
+ req.wIndex = cpu_to_le16(index);
+ req.wLength = cpu_to_le16(size);
+
+ pids = DBGP_PID_SET(USB_PID_DATA0, USB_PID_SETUP);
+ addr = DBGP_EPADDR(devnum, 0);
+
+ ctrl = readl(&ehci_debug->control);
+ ctrl = dbgp_len_update(ctrl, sizeof(req));
+ ctrl |= DBGP_OUT;
+ ctrl |= DBGP_GO;
+
+ /* Send the setup message */
+ dbgp_set_data(&req, sizeof(req));
+ writel(addr, &ehci_debug->address);
+ writel(pids, &ehci_debug->pids);
+ ret = dbgp_wait_until_done(ctrl);
+ if (ret < 0)
+ return ret;
+
+ /* Read the result */
+ return dbgp_bulk_read(devnum, 0, data, size);
+}
+
+
+/* Find a PCI capability */
+static u32 __init find_cap(u32 num, u32 slot, u32 func, int cap)
+{
+ u8 pos;
+ int bytes;
+
+ if (!(read_pci_config_16(num, slot, func, PCI_STATUS) &
+ PCI_STATUS_CAP_LIST))
+ return 0;
+
+ pos = read_pci_config_byte(num, slot, func, PCI_CAPABILITY_LIST);
+ for (bytes = 0; bytes < 48 && pos >= 0x40; bytes++) {
+ u8 id;
+
+ pos &= ~3;
+ id = read_pci_config_byte(num, slot, func, pos+PCI_CAP_LIST_ID);
+ if (id == 0xff)
+ break;
+ if (id == cap)
+ return pos;
+
+ pos = read_pci_config_byte(num, slot, func,
+ pos+PCI_CAP_LIST_NEXT);
+ }
+ return 0;
+}
+
+static u32 __init __find_dbgp(u32 bus, u32 slot, u32 func)
+{
+ u32 class;
+
+ class = read_pci_config(bus, slot, func, PCI_CLASS_REVISION);
+ if ((class >> 8) != PCI_CLASS_SERIAL_USB_EHCI)
+ return 0;
+
+ return find_cap(bus, slot, func, PCI_CAP_ID_EHCI_DEBUG);
+}
+
+static u32 __init find_dbgp(int ehci_num, u32 *rbus, u32 *rslot, u32 *rfunc)
+{
+ u32 bus, slot, func;
+
+ for (bus = 0; bus < 256; bus++) {
+ for (slot = 0; slot < 32; slot++) {
+ for (func = 0; func < 8; func++) {
+ unsigned cap;
+
+ cap = __find_dbgp(bus, slot, func);
+
+ if (!cap)
+ continue;
+ if (ehci_num-- != 0)
+ continue;
+ *rbus = bus;
+ *rslot = slot;
+ *rfunc = func;
+ return cap;
+ }
+ }
+ }
+ return 0;
+}
+
+static int ehci_reset_port(int port)
+{
+ u32 portsc;
+ u32 delay_time, delay;
+ int loop;
+
+ /* Reset the usb debug port */
+ portsc = readl(&ehci_regs->port_status[port - 1]);
+ portsc &= ~PORT_PE;
+ portsc |= PORT_RESET;
+ writel(portsc, &ehci_regs->port_status[port - 1]);
+
+ delay = HUB_ROOT_RESET_TIME;
+ for (delay_time = 0; delay_time < HUB_RESET_TIMEOUT;
+ delay_time += delay) {
+ dbgp_mdelay(delay);
+
+ portsc = readl(&ehci_regs->port_status[port - 1]);
+ if (portsc & PORT_RESET) {
+ /* force reset to complete */
+ loop = 2;
+ writel(portsc & ~(PORT_RWC_BITS | PORT_RESET),
+ &ehci_regs->port_status[port - 1]);
+ do {
+ portsc = readl(&ehci_regs->port_status[port-1]);
+ } while ((portsc & PORT_RESET) && (--loop > 0));
+ }
+
+ /* Device went away? */
+ if (!(portsc & PORT_CONNECT))
+ return -ENOTCONN;
+
+ /* bomb out completely if something weird happend */
+ if ((portsc & PORT_CSC))
+ return -EINVAL;
+
+ /* If we've finished resetting, then break out of the loop */
+ if (!(portsc & PORT_RESET) && (portsc & PORT_PE))
+ return 0;
+ }
+ return -EBUSY;
+}
+
+static int ehci_wait_for_port(int port)
+{
+ u32 status;
+ int ret, reps;
+
+ for (reps = 0; reps < 3; reps++) {
+ dbgp_mdelay(100);
+ status = readl(&ehci_regs->status);
+ if (status & STS_PCD) {
+ ret = ehci_reset_port(port);
+ if (ret == 0)
+ return 0;
+ }
+ }
+ return -ENOTCONN;
+}
+
+#ifdef DBGP_DEBUG
+# define dbgp_printk early_printk
+#else
+static inline void dbgp_printk(const char *fmt, ...) { }
+#endif
+
+typedef void (*set_debug_port_t)(int port);
+
+static void default_set_debug_port(int port)
+{
+}
+
+static set_debug_port_t set_debug_port = default_set_debug_port;
+
+static void nvidia_set_debug_port(int port)
+{
+ u32 dword;
+ dword = read_pci_config(ehci_dev.bus, ehci_dev.slot, ehci_dev.func,
+ 0x74);
+ dword &= ~(0x0f<<12);
+ dword |= ((port & 0x0f)<<12);
+ write_pci_config(ehci_dev.bus, ehci_dev.slot, ehci_dev.func, 0x74,
+ dword);
+ dbgp_printk("set debug port to %d\n", port);
+}
+
+static void __init detect_set_debug_port(void)
+{
+ u32 vendorid;
+
+ vendorid = read_pci_config(ehci_dev.bus, ehci_dev.slot, ehci_dev.func,
+ 0x00);
+
+ if ((vendorid & 0xffff) == 0x10de) {
+ dbgp_printk("using nvidia set_debug_port\n");
+ set_debug_port = nvidia_set_debug_port;
+ }
+}
+
+static int __init ehci_setup(void)
+{
+ struct usb_debug_descriptor dbgp_desc;
+ u32 cmd, ctrl, status, portsc, hcs_params;
+ u32 debug_port, new_debug_port = 0, n_ports;
+ u32 devnum;
+ int ret, i;
+ int loop;
+ int port_map_tried;
+ int playtimes = 3;
+
+try_next_time:
+ port_map_tried = 0;
+
+try_next_port:
+
+ hcs_params = readl(&ehci_caps->hcs_params);
+ debug_port = HCS_DEBUG_PORT(hcs_params);
+ n_ports = HCS_N_PORTS(hcs_params);
+
+ dbgp_printk("debug_port: %d\n", debug_port);
+ dbgp_printk("n_ports: %d\n", n_ports);
+
+ for (i = 1; i <= n_ports; i++) {
+ portsc = readl(&ehci_regs->port_status[i-1]);
+ dbgp_printk("portstatus%d: %08x\n", i, portsc);
+ }
+
+ if (port_map_tried && (new_debug_port != debug_port)) {
+ if (--playtimes) {
+ set_debug_port(new_debug_port);
+ goto try_next_time;
+ }
+ return -1;
+ }
+
+ loop = 10;
+ /* Reset the EHCI controller */
+ cmd = readl(&ehci_regs->command);
+ cmd |= CMD_RESET;
+ writel(cmd, &ehci_regs->command);
+ do {
+ cmd = readl(&ehci_regs->command);
+ } while ((cmd & CMD_RESET) && (--loop > 0));
+
+ if (!loop) {
+ dbgp_printk("can not reset ehci\n");
+ return -1;
+ }
+ dbgp_printk("ehci reset done\n");
+
+ /* Claim ownership, but do not enable yet */
+ ctrl = readl(&ehci_debug->control);
+ ctrl |= DBGP_OWNER;
+ ctrl &= ~(DBGP_ENABLED | DBGP_INUSE);
+ writel(ctrl, &ehci_debug->control);
+
+ /* Start the ehci running */
+ cmd = readl(&ehci_regs->command);
+ cmd &= ~(CMD_LRESET | CMD_IAAD | CMD_PSE | CMD_ASE | CMD_RESET);
+ cmd |= CMD_RUN;
+ writel(cmd, &ehci_regs->command);
+
+ /* Ensure everything is routed to the EHCI */
+ writel(FLAG_CF, &ehci_regs->configured_flag);
+
+ /* Wait until the controller is no longer halted */
+ loop = 10;
+ do {
+ status = readl(&ehci_regs->status);
+ } while ((status & STS_HALT) && (--loop > 0));
+
+ if (!loop) {
+ dbgp_printk("ehci can be started\n");
+ return -1;
+ }
+ dbgp_printk("ehci started\n");
+
+ /* Wait for a device to show up in the debug port */
+ ret = ehci_wait_for_port(debug_port);
+ if (ret < 0) {
+ dbgp_printk("No device found in debug port\n");
+ goto next_debug_port;
+ }
+ dbgp_printk("ehci wait for port done\n");
+
+ /* Enable the debug port */
+ ctrl = readl(&ehci_debug->control);
+ ctrl |= DBGP_CLAIM;
+ writel(ctrl, &ehci_debug->control);
+ ctrl = readl(&ehci_debug->control);
+ if ((ctrl & DBGP_CLAIM) != DBGP_CLAIM) {
+ dbgp_printk("No device in debug port\n");
+ writel(ctrl & ~DBGP_CLAIM, &ehci_debug->control);
+ goto err;
+ }
+ dbgp_printk("debug ported enabled\n");
+
+ /* Completely transfer the debug device to the debug controller */
+ portsc = readl(&ehci_regs->port_status[debug_port - 1]);
+ portsc &= ~PORT_PE;
+ writel(portsc, &ehci_regs->port_status[debug_port - 1]);
+
+ dbgp_mdelay(100);
+
+ /* Find the debug device and make it device number 127 */
+ for (devnum = 0; devnum <= 127; devnum++) {
+ ret = dbgp_control_msg(devnum,
+ USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+ USB_REQ_GET_DESCRIPTOR, (USB_DT_DEBUG << 8), 0,
+ &dbgp_desc, sizeof(dbgp_desc));
+ if (ret > 0)
+ break;
+ }
+ if (devnum > 127) {
+ dbgp_printk("Could not find attached debug device\n");
+ goto err;
+ }
+ if (ret < 0) {
+ dbgp_printk("Attached device is not a debug device\n");
+ goto err;
+ }
+ dbgp_endpoint_out = dbgp_desc.bDebugOutEndpoint;
+
+ /* Move the device to 127 if it isn't already there */
+ if (devnum != USB_DEBUG_DEVNUM) {
+ ret = dbgp_control_msg(devnum,
+ USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+ USB_REQ_SET_ADDRESS, USB_DEBUG_DEVNUM, 0, NULL, 0);
+ if (ret < 0) {
+ dbgp_printk("Could not move attached device to %d\n",
+ USB_DEBUG_DEVNUM);
+ goto err;
+ }
+ devnum = USB_DEBUG_DEVNUM;
+ dbgp_printk("debug device renamed to 127\n");
+ }
+
+ /* Enable the debug interface */
+ ret = dbgp_control_msg(USB_DEBUG_DEVNUM,
+ USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+ USB_REQ_SET_FEATURE, USB_DEVICE_DEBUG_MODE, 0, NULL, 0);
+ if (ret < 0) {
+ dbgp_printk(" Could not enable the debug device\n");
+ goto err;
+ }
+ dbgp_printk("debug interface enabled\n");
+
+ /* Perform a small write to get the even/odd data state in sync
+ */
+ ret = dbgp_bulk_write(USB_DEBUG_DEVNUM, dbgp_endpoint_out, " ", 1);
+ if (ret < 0) {
+ dbgp_printk("dbgp_bulk_write failed: %d\n", ret);
+ goto err;
+ }
+ dbgp_printk("small write doned\n");
+
+ return 0;
+err:
+ /* Things didn't work so remove my claim */
+ ctrl = readl(&ehci_debug->control);
+ ctrl &= ~(DBGP_CLAIM | DBGP_OUT);
+ writel(ctrl, &ehci_debug->control);
+ return -1;
+
+next_debug_port:
+ port_map_tried |= (1<<(debug_port - 1));
+ new_debug_port = ((debug_port-1+1)%n_ports) + 1;
+ if (port_map_tried != ((1<<n_ports) - 1)) {
+ set_debug_port(new_debug_port);
+ goto try_next_port;
+ }
+ if (--playtimes) {
+ set_debug_port(new_debug_port);
+ goto try_next_time;
+ }
+
+ return -1;
+}
+
+static int __init early_dbgp_init(char *s)
+{
+ u32 debug_port, bar, offset;
+ u32 bus, slot, func, cap;
+ void __iomem *ehci_bar;
+ u32 dbgp_num;
+ u32 bar_val;
+ char *e;
+ int ret;
+ u8 byte;
+
+ if (!early_pci_allowed())
+ return -1;
+
+ dbgp_num = 0;
+ if (*s)
+ dbgp_num = simple_strtoul(s, &e, 10);
+ dbgp_printk("dbgp_num: %d\n", dbgp_num);
+
+ cap = find_dbgp(dbgp_num, &bus, &slot, &func);
+ if (!cap)
+ return -1;
+
+ dbgp_printk("Found EHCI debug port on %02x:%02x.%1x\n", bus, slot,
+ func);
+
+ debug_port = read_pci_config(bus, slot, func, cap);
+ bar = (debug_port >> 29) & 0x7;
+ bar = (bar * 4) + 0xc;
+ offset = (debug_port >> 16) & 0xfff;
+ dbgp_printk("bar: %02x offset: %03x\n", bar, offset);
+ if (bar != PCI_BASE_ADDRESS_0) {
+ dbgp_printk("only debug ports on bar 1 handled.\n");
+
+ return -1;
+ }
+
+ bar_val = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_0);
+ dbgp_printk("bar_val: %02x offset: %03x\n", bar_val, offset);
+ if (bar_val & ~PCI_BASE_ADDRESS_MEM_MASK) {
+ dbgp_printk("only simple 32bit mmio bars supported\n");
+
+ return -1;
+ }
+
+ /* double check if the mem space is enabled */
+ byte = read_pci_config_byte(bus, slot, func, 0x04);
+ if (!(byte & 0x2)) {
+ byte |= 0x02;
+ write_pci_config_byte(bus, slot, func, 0x04, byte);
+ dbgp_printk("mmio for ehci enabled\n");
+ }
+
+ /*
+ * FIXME I don't have the bar size so just guess PAGE_SIZE is more
+ * than enough. 1K is the biggest I have seen.
+ */
+ set_fixmap_nocache(FIX_DBGP_BASE, bar_val & PAGE_MASK);
+ ehci_bar = (void __iomem *)__fix_to_virt(FIX_DBGP_BASE);
+ ehci_bar += bar_val & ~PAGE_MASK;
+ dbgp_printk("ehci_bar: %p\n", ehci_bar);
+
+ ehci_caps = ehci_bar;
+ ehci_regs = ehci_bar + HC_LENGTH(readl(&ehci_caps->hc_capbase));
+ ehci_debug = ehci_bar + offset;
+ ehci_dev.bus = bus;
+ ehci_dev.slot = slot;
+ ehci_dev.func = func;
+
+ detect_set_debug_port();
+
+ ret = ehci_setup();
+ if (ret < 0) {
+ dbgp_printk("ehci_setup failed\n");
+ ehci_debug = NULL;
+
+ return -1;
+ }
+
+ return 0;
+}
+
+static void early_dbgp_write(struct console *con, const char *str, u32 n)
+{
+ int chunk, ret;
+
+ if (!ehci_debug)
+ return;
+ while (n > 0) {
+ chunk = n;
+ if (chunk > DBGP_MAX_PACKET)
+ chunk = DBGP_MAX_PACKET;
+ ret = dbgp_bulk_write(USB_DEBUG_DEVNUM,
+ dbgp_endpoint_out, str, chunk);
+ str += chunk;
+ n -= chunk;
+ }
+}
+
+static struct console early_dbgp_console = {
+ .name = "earlydbg",
+ .write = early_dbgp_write,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+};
+#endif
+
/* Console interface to a host file on AMD's SimNow! */
static int simnow_fd;
@@ -165,6 +889,7 @@
static noinline long simnow(long cmd, long a, long b, long c)
{
long ret;
+
asm volatile("cpuid" :
"=a" (ret) :
"b" (a), "c" (b), "d" (c), "0" (MAGIC1), "D" (cmd + MAGIC2));
@@ -174,6 +899,7 @@
static void __init simnow_init(char *str)
{
char *fn = "klog";
+
if (*str == '=')
fn = ++str;
/* error ignored */
@@ -194,7 +920,7 @@
/* Direct interface for emergencies */
static struct console *early_console = &early_vga_console;
-static int early_console_initialized;
+static int __initdata early_console_initialized;
asmlinkage void early_printk(const char *fmt, ...)
{
@@ -208,10 +934,11 @@
va_end(ap);
}
-static int __initdata keep_early;
static int __init setup_early_printk(char *buf)
{
+ int keep_early;
+
if (!buf)
return 0;
@@ -219,8 +946,7 @@
return 0;
early_console_initialized = 1;
- if (strstr(buf, "keep"))
- keep_early = 1;
+ keep_early = (strstr(buf, "keep") != NULL);
if (!strncmp(buf, "serial", 6)) {
early_serial_init(buf + 6);
@@ -238,6 +964,17 @@
simnow_init(buf + 6);
early_console = &simnow_console;
keep_early = 1;
+#ifdef CONFIG_EARLY_PRINTK_DBGP
+ } else if (!strncmp(buf, "dbgp", 4)) {
+ if (early_dbgp_init(buf+4) < 0)
+ return 0;
+ early_console = &early_dbgp_console;
+ /*
+ * usb subsys will reset ehci controller, so don't keep
+ * that early console
+ */
+ keep_early = 0;
+#endif
#ifdef CONFIG_HVC_XEN
} else if (!strncmp(buf, "xen", 3)) {
early_console = &xenboot_console;
@@ -251,4 +988,5 @@
register_console(early_console);
return 0;
}
+
early_param("earlyprintk", setup_early_printk);
diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S
index 109792b..b21fbfa 100644
--- a/arch/x86/kernel/entry_32.S
+++ b/arch/x86/kernel/entry_32.S
@@ -730,6 +730,7 @@
movl $(__USER_DS), %ecx
movl %ecx, %ds
movl %ecx, %es
+ TRACE_IRQS_OFF
movl %esp,%eax # pt_regs pointer
call *%edi
jmp ret_from_exception
@@ -760,20 +761,9 @@
RING0_INT_FRAME
pushl $-1 # mark this as an int
CFI_ADJUST_CFA_OFFSET 4
- SAVE_ALL
- GET_CR0_INTO_EAX
- testl $0x4, %eax # EM (math emulation bit)
- jne device_not_available_emulate
- preempt_stop(CLBR_ANY)
- call math_state_restore
- jmp ret_from_exception
-device_not_available_emulate:
- pushl $0 # temporary storage for ORIG_EIP
+ pushl $do_device_not_available
CFI_ADJUST_CFA_OFFSET 4
- call math_emulate
- addl $4, %esp
- CFI_ADJUST_CFA_OFFSET -4
- jmp ret_from_exception
+ jmp error_code
CFI_ENDPROC
END(device_not_available)
@@ -814,6 +804,7 @@
pushl $-1 # mark this as an int
CFI_ADJUST_CFA_OFFSET 4
SAVE_ALL
+ TRACE_IRQS_OFF
xorl %edx,%edx # error code 0
movl %esp,%eax # pt_regs pointer
call do_debug
@@ -858,6 +849,7 @@
pushl %eax
CFI_ADJUST_CFA_OFFSET 4
SAVE_ALL
+ TRACE_IRQS_OFF
xorl %edx,%edx # zero error code
movl %esp,%eax # pt_regs pointer
call do_nmi
@@ -898,6 +890,7 @@
pushl %eax
CFI_ADJUST_CFA_OFFSET 4
SAVE_ALL
+ TRACE_IRQS_OFF
FIXUP_ESPFIX_STACK # %eax == %esp
xorl %edx,%edx # zero error code
call do_nmi
@@ -928,6 +921,7 @@
pushl $-1 # mark this as an int
CFI_ADJUST_CFA_OFFSET 4
SAVE_ALL
+ TRACE_IRQS_OFF
xorl %edx,%edx # zero error code
movl %esp,%eax # pt_regs pointer
call do_int3
@@ -1030,7 +1024,7 @@
RING0_INT_FRAME
pushl $0
CFI_ADJUST_CFA_OFFSET 4
- pushl machine_check_vector
+ pushl $do_machine_check
CFI_ADJUST_CFA_OFFSET 4
jmp error_code
CFI_ENDPROC
diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S
index cf3a0b2..1db6ce4 100644
--- a/arch/x86/kernel/entry_64.S
+++ b/arch/x86/kernel/entry_64.S
@@ -667,6 +667,13 @@
SAVE_ARGS
leaq -ARGOFFSET(%rsp),%rdi # arg1 for handler
pushq %rbp
+ /*
+ * Save rbp twice: One is for marking the stack frame, as usual, and the
+ * other, to fill pt_regs properly. This is because bx comes right
+ * before the last saved register in that structure, and not bp. If the
+ * base pointer were in the place bx is today, this would not be needed.
+ */
+ movq %rbp, -8(%rsp)
CFI_ADJUST_CFA_OFFSET 8
CFI_REL_OFFSET rbp, 0
movq %rsp,%rbp
@@ -932,6 +939,9 @@
.if \ist
movq %gs:pda_data_offset, %rbp
.endif
+ .if \irqtrace
+ TRACE_IRQS_OFF
+ .endif
movq %rsp,%rdi
movq ORIG_RAX(%rsp),%rsi
movq $-1,ORIG_RAX(%rsp)
@@ -1058,7 +1068,8 @@
je error_kernelspace
error_swapgs:
SWAPGS
-error_sti:
+error_sti:
+ TRACE_IRQS_OFF
movq %rdi,RDI(%rsp)
CFI_REL_OFFSET rdi,RDI
movq %rsp,%rdi
@@ -1232,7 +1243,7 @@
END(simd_coprocessor_error)
ENTRY(device_not_available)
- zeroentry math_state_restore
+ zeroentry do_device_not_available
END(device_not_available)
/* runs on exception stack */
diff --git a/arch/x86/mach-es7000/es7000plat.c b/arch/x86/kernel/es7000_32.c
similarity index 70%
rename from arch/x86/mach-es7000/es7000plat.c
rename to arch/x86/kernel/es7000_32.c
index 50189af..f454c78 100644
--- a/arch/x86/mach-es7000/es7000plat.c
+++ b/arch/x86/kernel/es7000_32.c
@@ -39,10 +39,94 @@
#include <asm/nmi.h>
#include <asm/smp.h>
#include <asm/apicdef.h>
-#include "es7000.h"
#include <mach_mpparse.h>
/*
+ * ES7000 chipsets
+ */
+
+#define NON_UNISYS 0
+#define ES7000_CLASSIC 1
+#define ES7000_ZORRO 2
+
+
+#define MIP_REG 1
+#define MIP_PSAI_REG 4
+
+#define MIP_BUSY 1
+#define MIP_SPIN 0xf0000
+#define MIP_VALID 0x0100000000000000ULL
+#define MIP_PORT(VALUE) ((VALUE >> 32) & 0xffff)
+
+#define MIP_RD_LO(VALUE) (VALUE & 0xffffffff)
+
+struct mip_reg_info {
+ unsigned long long mip_info;
+ unsigned long long delivery_info;
+ unsigned long long host_reg;
+ unsigned long long mip_reg;
+};
+
+struct part_info {
+ unsigned char type;
+ unsigned char length;
+ unsigned char part_id;
+ unsigned char apic_mode;
+ unsigned long snum;
+ char ptype[16];
+ char sname[64];
+ char pname[64];
+};
+
+struct psai {
+ unsigned long long entry_type;
+ unsigned long long addr;
+ unsigned long long bep_addr;
+};
+
+struct es7000_mem_info {
+ unsigned char type;
+ unsigned char length;
+ unsigned char resv[6];
+ unsigned long long start;
+ unsigned long long size;
+};
+
+struct es7000_oem_table {
+ unsigned long long hdr;
+ struct mip_reg_info mip;
+ struct part_info pif;
+ struct es7000_mem_info shm;
+ struct psai psai;
+};
+
+#ifdef CONFIG_ACPI
+
+struct oem_table {
+ struct acpi_table_header Header;
+ u32 OEMTableAddr;
+ u32 OEMTableSize;
+};
+
+extern int find_unisys_acpi_oem_table(unsigned long *oem_addr);
+extern void unmap_unisys_acpi_oem_table(unsigned long oem_addr);
+#endif
+
+struct mip_reg {
+ unsigned long long off_0;
+ unsigned long long off_8;
+ unsigned long long off_10;
+ unsigned long long off_18;
+ unsigned long long off_20;
+ unsigned long long off_28;
+ unsigned long long off_30;
+ unsigned long long off_38;
+};
+
+#define MIP_SW_APIC 0x1020b
+#define MIP_FUNC(VALUE) (VALUE & 0xff)
+
+/*
* ES7000 Globals
*/
@@ -72,7 +156,7 @@
base += nr_ioapic_registers[i];
}
- if (!ioapic && (gsi < 16))
+ if (!ioapic && (gsi < 16))
gsi += base;
return gsi;
}
@@ -160,21 +244,38 @@
}
#ifdef CONFIG_ACPI
-int __init
-find_unisys_acpi_oem_table(unsigned long *oem_addr)
+static unsigned long oem_addrX;
+static unsigned long oem_size;
+int __init find_unisys_acpi_oem_table(unsigned long *oem_addr)
{
struct acpi_table_header *header = NULL;
int i = 0;
- while (ACPI_SUCCESS(acpi_get_table("OEM1", i++, &header))) {
+ acpi_size tbl_size;
+
+ while (ACPI_SUCCESS(acpi_get_table_with_size("OEM1", i++, &header, &tbl_size))) {
if (!memcmp((char *) &header->oem_id, "UNISYS", 6)) {
struct oem_table *t = (struct oem_table *)header;
- *oem_addr = (unsigned long)__acpi_map_table(t->OEMTableAddr,
- t->OEMTableSize);
+
+ oem_addrX = t->OEMTableAddr;
+ oem_size = t->OEMTableSize;
+ early_acpi_os_unmap_memory(header, tbl_size);
+
+ *oem_addr = (unsigned long)__acpi_map_table(oem_addrX,
+ oem_size);
return 0;
}
+ early_acpi_os_unmap_memory(header, tbl_size);
}
return -1;
}
+
+void __init unmap_unisys_acpi_oem_table(unsigned long oem_addr)
+{
+ if (!oem_addr)
+ return;
+
+ __acpi_unmap_table((char *)oem_addr, oem_size);
+}
#endif
static void
diff --git a/arch/x86/kernel/genapic_64.c b/arch/x86/kernel/genapic_64.c
index eaff0bb..6c9bfc9 100644
--- a/arch/x86/kernel/genapic_64.c
+++ b/arch/x86/kernel/genapic_64.c
@@ -16,87 +16,63 @@
#include <linux/ctype.h>
#include <linux/init.h>
#include <linux/hardirq.h>
+#include <linux/dmar.h>
#include <asm/smp.h>
#include <asm/ipi.h>
#include <asm/genapic.h>
-#ifdef CONFIG_ACPI
-#include <acpi/acpi_bus.h>
-#endif
-
-DEFINE_PER_CPU(int, x2apic_extra_bits);
+extern struct genapic apic_flat;
+extern struct genapic apic_physflat;
+extern struct genapic apic_x2xpic_uv_x;
+extern struct genapic apic_x2apic_phys;
+extern struct genapic apic_x2apic_cluster;
struct genapic __read_mostly *genapic = &apic_flat;
-static enum uv_system_type uv_system_type;
+static struct genapic *apic_probe[] __initdata = {
+ &apic_x2apic_uv_x,
+ &apic_x2apic_phys,
+ &apic_x2apic_cluster,
+ &apic_physflat,
+ NULL,
+};
/*
* Check the APIC IDs in bios_cpu_apicid and choose the APIC mode.
*/
void __init setup_apic_routing(void)
{
- if (uv_system_type == UV_NON_UNIQUE_APIC)
- genapic = &apic_x2apic_uv_x;
- else
-#ifdef CONFIG_ACPI
- /*
- * Quirk: some x86_64 machines can only use physical APIC mode
- * regardless of how many processors are present (x86_64 ES7000
- * is an example).
- */
- if (acpi_gbl_FADT.header.revision > FADT2_REVISION_ID &&
- (acpi_gbl_FADT.flags & ACPI_FADT_APIC_PHYSICAL))
- genapic = &apic_physflat;
- else
-#endif
+ if (genapic == &apic_x2apic_phys || genapic == &apic_x2apic_cluster) {
+ if (!intr_remapping_enabled)
+ genapic = &apic_flat;
+ }
- if (max_physical_apicid < 8)
- genapic = &apic_flat;
- else
- genapic = &apic_physflat;
-
- printk(KERN_INFO "Setting APIC routing to %s\n", genapic->name);
+ if (genapic == &apic_flat) {
+ if (max_physical_apicid >= 8)
+ genapic = &apic_physflat;
+ printk(KERN_INFO "Setting APIC routing to %s\n", genapic->name);
+ }
}
/* Same for both flat and physical. */
-void send_IPI_self(int vector)
+void apic_send_IPI_self(int vector)
{
__send_IPI_shortcut(APIC_DEST_SELF, vector, APIC_DEST_PHYSICAL);
}
int __init acpi_madt_oem_check(char *oem_id, char *oem_table_id)
{
- if (!strcmp(oem_id, "SGI")) {
- if (!strcmp(oem_table_id, "UVL"))
- uv_system_type = UV_LEGACY_APIC;
- else if (!strcmp(oem_table_id, "UVX"))
- uv_system_type = UV_X2APIC;
- else if (!strcmp(oem_table_id, "UVH"))
- uv_system_type = UV_NON_UNIQUE_APIC;
+ int i;
+
+ for (i = 0; apic_probe[i]; ++i) {
+ if (apic_probe[i]->acpi_madt_oem_check(oem_id, oem_table_id)) {
+ genapic = apic_probe[i];
+ printk(KERN_INFO "Setting APIC routing to %s.\n",
+ genapic->name);
+ return 1;
+ }
}
return 0;
}
-
-unsigned int read_apic_id(void)
-{
- unsigned int id;
-
- WARN_ON(preemptible() && num_online_cpus() > 1);
- id = apic_read(APIC_ID);
- if (uv_system_type >= UV_X2APIC)
- id |= __get_cpu_var(x2apic_extra_bits);
- return id;
-}
-
-enum uv_system_type get_uv_system_type(void)
-{
- return uv_system_type;
-}
-
-int is_uv_system(void)
-{
- return uv_system_type != UV_NONE;
-}
-EXPORT_SYMBOL_GPL(is_uv_system);
diff --git a/arch/x86/kernel/genapic_flat_64.c b/arch/x86/kernel/genapic_flat_64.c
index 786548a..9eca5ba 100644
--- a/arch/x86/kernel/genapic_flat_64.c
+++ b/arch/x86/kernel/genapic_flat_64.c
@@ -15,9 +15,20 @@
#include <linux/kernel.h>
#include <linux/ctype.h>
#include <linux/init.h>
+#include <linux/hardirq.h>
#include <asm/smp.h>
#include <asm/ipi.h>
#include <asm/genapic.h>
+#include <mach_apicdef.h>
+
+#ifdef CONFIG_ACPI
+#include <acpi/acpi_bus.h>
+#endif
+
+static int __init flat_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
+{
+ return 1;
+}
static cpumask_t flat_target_cpus(void)
{
@@ -95,9 +106,33 @@
__send_IPI_shortcut(APIC_DEST_ALLINC, vector, APIC_DEST_LOGICAL);
}
+static unsigned int get_apic_id(unsigned long x)
+{
+ unsigned int id;
+
+ id = (((x)>>24) & 0xFFu);
+ return id;
+}
+
+static unsigned long set_apic_id(unsigned int id)
+{
+ unsigned long x;
+
+ x = ((id & 0xFFu)<<24);
+ return x;
+}
+
+static unsigned int read_xapic_id(void)
+{
+ unsigned int id;
+
+ id = get_apic_id(apic_read(APIC_ID));
+ return id;
+}
+
static int flat_apic_id_registered(void)
{
- return physid_isset(GET_APIC_ID(read_apic_id()), phys_cpu_present_map);
+ return physid_isset(read_xapic_id(), phys_cpu_present_map);
}
static unsigned int flat_cpu_mask_to_apicid(cpumask_t cpumask)
@@ -112,6 +147,7 @@
struct genapic apic_flat = {
.name = "flat",
+ .acpi_madt_oem_check = flat_acpi_madt_oem_check,
.int_delivery_mode = dest_LowestPrio,
.int_dest_mode = (APIC_DEST_LOGICAL != 0),
.target_cpus = flat_target_cpus,
@@ -121,8 +157,12 @@
.send_IPI_all = flat_send_IPI_all,
.send_IPI_allbutself = flat_send_IPI_allbutself,
.send_IPI_mask = flat_send_IPI_mask,
+ .send_IPI_self = apic_send_IPI_self,
.cpu_mask_to_apicid = flat_cpu_mask_to_apicid,
.phys_pkg_id = phys_pkg_id,
+ .get_apic_id = get_apic_id,
+ .set_apic_id = set_apic_id,
+ .apic_id_mask = (0xFFu<<24),
};
/*
@@ -130,6 +170,21 @@
* We cannot use logical delivery in this case because the mask
* overflows, so use physical mode.
*/
+static int __init physflat_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
+{
+#ifdef CONFIG_ACPI
+ /*
+ * Quirk: some x86_64 machines can only use physical APIC mode
+ * regardless of how many processors are present (x86_64 ES7000
+ * is an example).
+ */
+ if (acpi_gbl_FADT.header.revision > FADT2_REVISION_ID &&
+ (acpi_gbl_FADT.flags & ACPI_FADT_APIC_PHYSICAL))
+ return 1;
+#endif
+
+ return 0;
+}
static cpumask_t physflat_target_cpus(void)
{
@@ -176,6 +231,7 @@
struct genapic apic_physflat = {
.name = "physical flat",
+ .acpi_madt_oem_check = physflat_acpi_madt_oem_check,
.int_delivery_mode = dest_Fixed,
.int_dest_mode = (APIC_DEST_PHYSICAL != 0),
.target_cpus = physflat_target_cpus,
@@ -185,6 +241,10 @@
.send_IPI_all = physflat_send_IPI_all,
.send_IPI_allbutself = physflat_send_IPI_allbutself,
.send_IPI_mask = physflat_send_IPI_mask,
+ .send_IPI_self = apic_send_IPI_self,
.cpu_mask_to_apicid = physflat_cpu_mask_to_apicid,
.phys_pkg_id = phys_pkg_id,
+ .get_apic_id = get_apic_id,
+ .set_apic_id = set_apic_id,
+ .apic_id_mask = (0xFFu<<24),
};
diff --git a/arch/x86/kernel/genx2apic_cluster.c b/arch/x86/kernel/genx2apic_cluster.c
new file mode 100644
index 0000000..e4bf2cc
--- /dev/null
+++ b/arch/x86/kernel/genx2apic_cluster.c
@@ -0,0 +1,159 @@
+#include <linux/threads.h>
+#include <linux/cpumask.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/ctype.h>
+#include <linux/init.h>
+#include <linux/dmar.h>
+
+#include <asm/smp.h>
+#include <asm/ipi.h>
+#include <asm/genapic.h>
+
+DEFINE_PER_CPU(u32, x86_cpu_to_logical_apicid);
+
+static int __init x2apic_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
+{
+ if (cpu_has_x2apic)
+ return 1;
+
+ return 0;
+}
+
+/* Start with all IRQs pointing to boot CPU. IRQ balancing will shift them. */
+
+static cpumask_t x2apic_target_cpus(void)
+{
+ return cpumask_of_cpu(0);
+}
+
+/*
+ * for now each logical cpu is in its own vector allocation domain.
+ */
+static cpumask_t x2apic_vector_allocation_domain(int cpu)
+{
+ cpumask_t domain = CPU_MASK_NONE;
+ cpu_set(cpu, domain);
+ return domain;
+}
+
+static void __x2apic_send_IPI_dest(unsigned int apicid, int vector,
+ unsigned int dest)
+{
+ unsigned long cfg;
+
+ cfg = __prepare_ICR(0, vector, dest);
+
+ /*
+ * send the IPI.
+ */
+ x2apic_icr_write(cfg, apicid);
+}
+
+/*
+ * for now, we send the IPI's one by one in the cpumask.
+ * TBD: Based on the cpu mask, we can send the IPI's to the cluster group
+ * at once. We have 16 cpu's in a cluster. This will minimize IPI register
+ * writes.
+ */
+static void x2apic_send_IPI_mask(cpumask_t mask, int vector)
+{
+ unsigned long flags;
+ unsigned long query_cpu;
+
+ local_irq_save(flags);
+ for_each_cpu_mask(query_cpu, mask) {
+ __x2apic_send_IPI_dest(per_cpu(x86_cpu_to_logical_apicid, query_cpu),
+ vector, APIC_DEST_LOGICAL);
+ }
+ local_irq_restore(flags);
+}
+
+static void x2apic_send_IPI_allbutself(int vector)
+{
+ cpumask_t mask = cpu_online_map;
+
+ cpu_clear(smp_processor_id(), mask);
+
+ if (!cpus_empty(mask))
+ x2apic_send_IPI_mask(mask, vector);
+}
+
+static void x2apic_send_IPI_all(int vector)
+{
+ x2apic_send_IPI_mask(cpu_online_map, vector);
+}
+
+static int x2apic_apic_id_registered(void)
+{
+ return 1;
+}
+
+static unsigned int x2apic_cpu_mask_to_apicid(cpumask_t cpumask)
+{
+ int cpu;
+
+ /*
+ * We're using fixed IRQ delivery, can only return one phys APIC ID.
+ * May as well be the first.
+ */
+ cpu = first_cpu(cpumask);
+ if ((unsigned)cpu < NR_CPUS)
+ return per_cpu(x86_cpu_to_logical_apicid, cpu);
+ else
+ return BAD_APICID;
+}
+
+static unsigned int get_apic_id(unsigned long x)
+{
+ unsigned int id;
+
+ id = x;
+ return id;
+}
+
+static unsigned long set_apic_id(unsigned int id)
+{
+ unsigned long x;
+
+ x = id;
+ return x;
+}
+
+static unsigned int phys_pkg_id(int index_msb)
+{
+ return current_cpu_data.initial_apicid >> index_msb;
+}
+
+static void x2apic_send_IPI_self(int vector)
+{
+ apic_write(APIC_SELF_IPI, vector);
+}
+
+static void init_x2apic_ldr(void)
+{
+ int cpu = smp_processor_id();
+
+ per_cpu(x86_cpu_to_logical_apicid, cpu) = apic_read(APIC_LDR);
+ return;
+}
+
+struct genapic apic_x2apic_cluster = {
+ .name = "cluster x2apic",
+ .acpi_madt_oem_check = x2apic_acpi_madt_oem_check,
+ .int_delivery_mode = dest_LowestPrio,
+ .int_dest_mode = (APIC_DEST_LOGICAL != 0),
+ .target_cpus = x2apic_target_cpus,
+ .vector_allocation_domain = x2apic_vector_allocation_domain,
+ .apic_id_registered = x2apic_apic_id_registered,
+ .init_apic_ldr = init_x2apic_ldr,
+ .send_IPI_all = x2apic_send_IPI_all,
+ .send_IPI_allbutself = x2apic_send_IPI_allbutself,
+ .send_IPI_mask = x2apic_send_IPI_mask,
+ .send_IPI_self = x2apic_send_IPI_self,
+ .cpu_mask_to_apicid = x2apic_cpu_mask_to_apicid,
+ .phys_pkg_id = phys_pkg_id,
+ .get_apic_id = get_apic_id,
+ .set_apic_id = set_apic_id,
+ .apic_id_mask = (0xFFFFFFFFu),
+};
diff --git a/arch/x86/kernel/genx2apic_phys.c b/arch/x86/kernel/genx2apic_phys.c
new file mode 100644
index 0000000..8f1343d
--- /dev/null
+++ b/arch/x86/kernel/genx2apic_phys.c
@@ -0,0 +1,154 @@
+#include <linux/threads.h>
+#include <linux/cpumask.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/ctype.h>
+#include <linux/init.h>
+#include <linux/dmar.h>
+
+#include <asm/smp.h>
+#include <asm/ipi.h>
+#include <asm/genapic.h>
+
+static int x2apic_phys;
+
+static int set_x2apic_phys_mode(char *arg)
+{
+ x2apic_phys = 1;
+ return 0;
+}
+early_param("x2apic_phys", set_x2apic_phys_mode);
+
+static int __init x2apic_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
+{
+ if (cpu_has_x2apic && x2apic_phys)
+ return 1;
+
+ return 0;
+}
+
+/* Start with all IRQs pointing to boot CPU. IRQ balancing will shift them. */
+
+static cpumask_t x2apic_target_cpus(void)
+{
+ return cpumask_of_cpu(0);
+}
+
+static cpumask_t x2apic_vector_allocation_domain(int cpu)
+{
+ cpumask_t domain = CPU_MASK_NONE;
+ cpu_set(cpu, domain);
+ return domain;
+}
+
+static void __x2apic_send_IPI_dest(unsigned int apicid, int vector,
+ unsigned int dest)
+{
+ unsigned long cfg;
+
+ cfg = __prepare_ICR(0, vector, dest);
+
+ /*
+ * send the IPI.
+ */
+ x2apic_icr_write(cfg, apicid);
+}
+
+static void x2apic_send_IPI_mask(cpumask_t mask, int vector)
+{
+ unsigned long flags;
+ unsigned long query_cpu;
+
+ local_irq_save(flags);
+ for_each_cpu_mask(query_cpu, mask) {
+ __x2apic_send_IPI_dest(per_cpu(x86_cpu_to_apicid, query_cpu),
+ vector, APIC_DEST_PHYSICAL);
+ }
+ local_irq_restore(flags);
+}
+
+static void x2apic_send_IPI_allbutself(int vector)
+{
+ cpumask_t mask = cpu_online_map;
+
+ cpu_clear(smp_processor_id(), mask);
+
+ if (!cpus_empty(mask))
+ x2apic_send_IPI_mask(mask, vector);
+}
+
+static void x2apic_send_IPI_all(int vector)
+{
+ x2apic_send_IPI_mask(cpu_online_map, vector);
+}
+
+static int x2apic_apic_id_registered(void)
+{
+ return 1;
+}
+
+static unsigned int x2apic_cpu_mask_to_apicid(cpumask_t cpumask)
+{
+ int cpu;
+
+ /*
+ * We're using fixed IRQ delivery, can only return one phys APIC ID.
+ * May as well be the first.
+ */
+ cpu = first_cpu(cpumask);
+ if ((unsigned)cpu < NR_CPUS)
+ return per_cpu(x86_cpu_to_apicid, cpu);
+ else
+ return BAD_APICID;
+}
+
+static unsigned int get_apic_id(unsigned long x)
+{
+ unsigned int id;
+
+ id = x;
+ return id;
+}
+
+static unsigned long set_apic_id(unsigned int id)
+{
+ unsigned long x;
+
+ x = id;
+ return x;
+}
+
+static unsigned int phys_pkg_id(int index_msb)
+{
+ return current_cpu_data.initial_apicid >> index_msb;
+}
+
+void x2apic_send_IPI_self(int vector)
+{
+ apic_write(APIC_SELF_IPI, vector);
+}
+
+void init_x2apic_ldr(void)
+{
+ return;
+}
+
+struct genapic apic_x2apic_phys = {
+ .name = "physical x2apic",
+ .acpi_madt_oem_check = x2apic_acpi_madt_oem_check,
+ .int_delivery_mode = dest_Fixed,
+ .int_dest_mode = (APIC_DEST_PHYSICAL != 0),
+ .target_cpus = x2apic_target_cpus,
+ .vector_allocation_domain = x2apic_vector_allocation_domain,
+ .apic_id_registered = x2apic_apic_id_registered,
+ .init_apic_ldr = init_x2apic_ldr,
+ .send_IPI_all = x2apic_send_IPI_all,
+ .send_IPI_allbutself = x2apic_send_IPI_allbutself,
+ .send_IPI_mask = x2apic_send_IPI_mask,
+ .send_IPI_self = x2apic_send_IPI_self,
+ .cpu_mask_to_apicid = x2apic_cpu_mask_to_apicid,
+ .phys_pkg_id = phys_pkg_id,
+ .get_apic_id = get_apic_id,
+ .set_apic_id = set_apic_id,
+ .apic_id_mask = (0xFFFFFFFFu),
+};
diff --git a/arch/x86/kernel/genx2apic_uv_x.c b/arch/x86/kernel/genx2apic_uv_x.c
index bfa837c..33581d9 100644
--- a/arch/x86/kernel/genx2apic_uv_x.c
+++ b/arch/x86/kernel/genx2apic_uv_x.c
@@ -12,12 +12,12 @@
#include <linux/threads.h>
#include <linux/cpumask.h>
#include <linux/string.h>
-#include <linux/kernel.h>
#include <linux/ctype.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/bootmem.h>
#include <linux/module.h>
+#include <linux/hardirq.h>
#include <asm/smp.h>
#include <asm/ipi.h>
#include <asm/genapic.h>
@@ -26,6 +26,36 @@
#include <asm/uv/uv_hub.h>
#include <asm/uv/bios.h>
+DEFINE_PER_CPU(int, x2apic_extra_bits);
+
+static enum uv_system_type uv_system_type;
+
+static int __init uv_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
+{
+ if (!strcmp(oem_id, "SGI")) {
+ if (!strcmp(oem_table_id, "UVL"))
+ uv_system_type = UV_LEGACY_APIC;
+ else if (!strcmp(oem_table_id, "UVX"))
+ uv_system_type = UV_X2APIC;
+ else if (!strcmp(oem_table_id, "UVH")) {
+ uv_system_type = UV_NON_UNIQUE_APIC;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+enum uv_system_type get_uv_system_type(void)
+{
+ return uv_system_type;
+}
+
+int is_uv_system(void)
+{
+ return uv_system_type != UV_NONE;
+}
+EXPORT_SYMBOL_GPL(is_uv_system);
+
DEFINE_PER_CPU(struct uv_hub_info_s, __uv_hub_info);
EXPORT_PER_CPU_SYMBOL_GPL(__uv_hub_info);
@@ -84,7 +114,7 @@
unsigned long val, apicid, lapicid;
int pnode;
- apicid = per_cpu(x86_cpu_to_apicid, cpu); /* ZZZ - cache node-local ? */
+ apicid = per_cpu(x86_cpu_to_apicid, cpu);
lapicid = apicid & 0x3f; /* ZZZ macro needed */
pnode = uv_apicid_to_pnode(apicid);
val =
@@ -123,6 +153,10 @@
return 1;
}
+static void uv_init_apic_ldr(void)
+{
+}
+
static unsigned int uv_cpu_mask_to_apicid(cpumask_t cpumask)
{
int cpu;
@@ -138,31 +172,59 @@
return BAD_APICID;
}
-static unsigned int phys_pkg_id(int index_msb)
+static unsigned int get_apic_id(unsigned long x)
{
- return GET_APIC_ID(read_apic_id()) >> index_msb;
+ unsigned int id;
+
+ WARN_ON(preemptible() && num_online_cpus() > 1);
+ id = x | __get_cpu_var(x2apic_extra_bits);
+
+ return id;
}
-#ifdef ZZZ /* Needs x2apic patch */
+static unsigned long set_apic_id(unsigned int id)
+{
+ unsigned long x;
+
+ /* maskout x2apic_extra_bits ? */
+ x = id;
+ return x;
+}
+
+static unsigned int uv_read_apic_id(void)
+{
+
+ return get_apic_id(apic_read(APIC_ID));
+}
+
+static unsigned int phys_pkg_id(int index_msb)
+{
+ return uv_read_apic_id() >> index_msb;
+}
+
static void uv_send_IPI_self(int vector)
{
apic_write(APIC_SELF_IPI, vector);
}
-#endif
struct genapic apic_x2apic_uv_x = {
.name = "UV large system",
+ .acpi_madt_oem_check = uv_acpi_madt_oem_check,
.int_delivery_mode = dest_Fixed,
.int_dest_mode = (APIC_DEST_PHYSICAL != 0),
.target_cpus = uv_target_cpus,
- .vector_allocation_domain = uv_vector_allocation_domain,/* Fixme ZZZ */
+ .vector_allocation_domain = uv_vector_allocation_domain,
.apic_id_registered = uv_apic_id_registered,
+ .init_apic_ldr = uv_init_apic_ldr,
.send_IPI_all = uv_send_IPI_all,
.send_IPI_allbutself = uv_send_IPI_allbutself,
.send_IPI_mask = uv_send_IPI_mask,
- /* ZZZ.send_IPI_self = uv_send_IPI_self, */
+ .send_IPI_self = uv_send_IPI_self,
.cpu_mask_to_apicid = uv_cpu_mask_to_apicid,
- .phys_pkg_id = phys_pkg_id, /* Fixme ZZZ */
+ .phys_pkg_id = phys_pkg_id,
+ .get_apic_id = get_apic_id,
+ .set_apic_id = set_apic_id,
+ .apic_id_mask = (0xFFFFFFFFu),
};
static __cpuinit void set_x2apic_extra_bits(int pnode)
@@ -222,12 +284,13 @@
enum map_type {map_wb, map_uc};
-static __init void map_high(char *id, unsigned long base, int shift, enum map_type map_type)
+static __init void map_high(char *id, unsigned long base, int shift,
+ int max_pnode, enum map_type map_type)
{
unsigned long bytes, paddr;
paddr = base << shift;
- bytes = (1UL << shift);
+ bytes = (1UL << shift) * (max_pnode + 1);
printk(KERN_INFO "UV: Map %s_HI 0x%lx - 0x%lx\n", id, paddr,
paddr + bytes);
if (map_type == map_uc)
@@ -243,7 +306,7 @@
gru.v = uv_read_local_mmr(UVH_RH_GAM_GRU_OVERLAY_CONFIG_MMR);
if (gru.s.enable)
- map_high("GRU", gru.s.base, shift, map_wb);
+ map_high("GRU", gru.s.base, shift, max_pnode, map_wb);
}
static __init void map_config_high(int max_pnode)
@@ -253,7 +316,7 @@
cfg.v = uv_read_local_mmr(UVH_RH_GAM_CFG_OVERLAY_CONFIG_MMR);
if (cfg.s.enable)
- map_high("CONFIG", cfg.s.base, shift, map_uc);
+ map_high("CONFIG", cfg.s.base, shift, max_pnode, map_uc);
}
static __init void map_mmr_high(int max_pnode)
@@ -263,7 +326,7 @@
mmr.v = uv_read_local_mmr(UVH_RH_GAM_MMR_OVERLAY_CONFIG_MMR);
if (mmr.s.enable)
- map_high("MMR", mmr.s.base, shift, map_uc);
+ map_high("MMR", mmr.s.base, shift, max_pnode, map_uc);
}
static __init void map_mmioh_high(int max_pnode)
@@ -273,7 +336,7 @@
mmioh.v = uv_read_local_mmr(UVH_RH_GAM_MMIOH_OVERLAY_CONFIG_MMR);
if (mmioh.s.enable)
- map_high("MMIOH", mmioh.s.base, shift, map_uc);
+ map_high("MMIOH", mmioh.s.base, shift, max_pnode, map_uc);
}
static __init void uv_rtc_init(void)
@@ -401,3 +464,5 @@
if (get_uv_system_type() == UV_NON_UNIQUE_APIC)
set_x2apic_extra_bits(uv_hub_info->pnode);
}
+
+
diff --git a/arch/x86/kernel/head.c b/arch/x86/kernel/head.c
index 3e66bd3..1dcb0f1 100644
--- a/arch/x86/kernel/head.c
+++ b/arch/x86/kernel/head.c
@@ -35,6 +35,7 @@
/* start of EBDA area */
ebda_addr = get_bios_ebda();
+ printk(KERN_INFO "BIOS EBDA/lowmem at: %08x/%08x\n", ebda_addr, lowmem);
/* Fixup: bios puts an EBDA in the top 64K segment */
/* of conventional memory, but does not adjust lowmem. */
diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c
index 73deaff..acf62fc 100644
--- a/arch/x86/kernel/hpet.c
+++ b/arch/x86/kernel/hpet.c
@@ -115,13 +115,17 @@
hd.hd_phys_address = hpet_address;
hd.hd_address = hpet;
hd.hd_nirqs = nrtimers;
- hd.hd_flags = HPET_DATA_PLATFORM;
hpet_reserve_timer(&hd, 0);
#ifdef CONFIG_HPET_EMULATE_RTC
hpet_reserve_timer(&hd, 1);
#endif
+ /*
+ * NOTE that hd_irq[] reflects IOAPIC input pins (LEGACY_8254
+ * is wrong for i8259!) not the output IRQ. Many BIOS writers
+ * don't bother configuring *any* comparator interrupts.
+ */
hd.hd_irq[0] = HPET_LEGACY_8254;
hd.hd_irq[1] = HPET_LEGACY_RTC;
diff --git a/arch/x86/kernel/i387.c b/arch/x86/kernel/i387.c
index eb9ddd8..1f20608 100644
--- a/arch/x86/kernel/i387.c
+++ b/arch/x86/kernel/i387.c
@@ -21,9 +21,12 @@
# include <asm/sigcontext32.h>
# include <asm/user32.h>
#else
-# define save_i387_ia32 save_i387
-# define restore_i387_ia32 restore_i387
+# define save_i387_xstate_ia32 save_i387_xstate
+# define restore_i387_xstate_ia32 restore_i387_xstate
# define _fpstate_ia32 _fpstate
+# define _xstate_ia32 _xstate
+# define sig_xstate_ia32_size sig_xstate_size
+# define fx_sw_reserved_ia32 fx_sw_reserved
# define user_i387_ia32_struct user_i387_struct
# define user32_fxsr_struct user_fxsr_struct
#endif
@@ -36,6 +39,7 @@
static unsigned int mxcsr_feature_mask __read_mostly = 0xffffffffu;
unsigned int xstate_size;
+unsigned int sig_xstate_ia32_size = sizeof(struct _fpstate_ia32);
static struct i387_fxsave_struct fx_scratch __cpuinitdata;
void __cpuinit mxcsr_feature_mask_init(void)
@@ -61,6 +65,11 @@
return;
}
+ if (cpu_has_xsave) {
+ xsave_cntxt_init();
+ return;
+ }
+
if (cpu_has_fxsr)
xstate_size = sizeof(struct i387_fxsave_struct);
#ifdef CONFIG_X86_32
@@ -83,9 +92,19 @@
write_cr0(oldcr0 & ~(X86_CR0_TS|X86_CR0_EM)); /* clear TS and EM */
+ /*
+ * Boot processor to setup the FP and extended state context info.
+ */
+ if (!smp_processor_id())
+ init_thread_xstate();
+ xsave_init();
+
mxcsr_feature_mask_init();
/* clean state in init */
- current_thread_info()->status = 0;
+ if (cpu_has_xsave)
+ current_thread_info()->status = TS_XSAVE;
+ else
+ current_thread_info()->status = 0;
clear_used_math();
}
#endif /* CONFIG_X86_64 */
@@ -195,6 +214,13 @@
*/
target->thread.xstate->fxsave.mxcsr &= mxcsr_feature_mask;
+ /*
+ * update the header bits in the xsave header, indicating the
+ * presence of FP and SSE state.
+ */
+ if (cpu_has_xsave)
+ target->thread.xstate->xsave.xsave_hdr.xstate_bv |= XSTATE_FPSSE;
+
return ret;
}
@@ -395,6 +421,12 @@
if (!ret)
convert_to_fxsr(target, &env);
+ /*
+ * update the header bit in the xsave header, indicating the
+ * presence of FP.
+ */
+ if (cpu_has_xsave)
+ target->thread.xstate->xsave.xsave_hdr.xstate_bv |= XSTATE_FP;
return ret;
}
@@ -407,7 +439,6 @@
struct task_struct *tsk = current;
struct i387_fsave_struct *fp = &tsk->thread.xstate->fsave;
- unlazy_fpu(tsk);
fp->status = fp->swd;
if (__copy_to_user(buf, fp, sizeof(struct i387_fsave_struct)))
return -1;
@@ -421,8 +452,6 @@
struct user_i387_ia32_struct env;
int err = 0;
- unlazy_fpu(tsk);
-
convert_from_fxsr(&env, tsk);
if (__copy_to_user(buf, &env, sizeof(env)))
return -1;
@@ -432,16 +461,54 @@
if (err)
return -1;
- if (__copy_to_user(&buf->_fxsr_env[0], fx,
- sizeof(struct i387_fxsave_struct)))
+ if (__copy_to_user(&buf->_fxsr_env[0], fx, xstate_size))
return -1;
return 1;
}
-int save_i387_ia32(struct _fpstate_ia32 __user *buf)
+static int save_i387_xsave(void __user *buf)
{
+ struct task_struct *tsk = current;
+ struct _fpstate_ia32 __user *fx = buf;
+ int err = 0;
+
+ /*
+ * For legacy compatible, we always set FP/SSE bits in the bit
+ * vector while saving the state to the user context.
+ * This will enable us capturing any changes(during sigreturn) to
+ * the FP/SSE bits by the legacy applications which don't touch
+ * xstate_bv in the xsave header.
+ *
+ * xsave aware applications can change the xstate_bv in the xsave
+ * header as well as change any contents in the memory layout.
+ * xrestore as part of sigreturn will capture all the changes.
+ */
+ tsk->thread.xstate->xsave.xsave_hdr.xstate_bv |= XSTATE_FPSSE;
+
+ if (save_i387_fxsave(fx) < 0)
+ return -1;
+
+ err = __copy_to_user(&fx->sw_reserved, &fx_sw_reserved_ia32,
+ sizeof(struct _fpx_sw_bytes));
+ err |= __put_user(FP_XSTATE_MAGIC2,
+ (__u32 __user *) (buf + sig_xstate_ia32_size
+ - FP_XSTATE_MAGIC2_SIZE));
+ if (err)
+ return -1;
+
+ return 1;
+}
+
+int save_i387_xstate_ia32(void __user *buf)
+{
+ struct _fpstate_ia32 __user *fp = (struct _fpstate_ia32 __user *) buf;
+ struct task_struct *tsk = current;
+
if (!used_math())
return 0;
+
+ if (!access_ok(VERIFY_WRITE, buf, sig_xstate_ia32_size))
+ return -EACCES;
/*
* This will cause a "finit" to be triggered by the next
* attempted FPU operation by the 'current' process.
@@ -451,13 +518,17 @@
if (!HAVE_HWFP) {
return fpregs_soft_get(current, NULL,
0, sizeof(struct user_i387_ia32_struct),
- NULL, buf) ? -1 : 1;
+ NULL, fp) ? -1 : 1;
}
+ unlazy_fpu(tsk);
+
+ if (cpu_has_xsave)
+ return save_i387_xsave(fp);
if (cpu_has_fxsr)
- return save_i387_fxsave(buf);
+ return save_i387_fxsave(fp);
else
- return save_i387_fsave(buf);
+ return save_i387_fsave(fp);
}
static inline int restore_i387_fsave(struct _fpstate_ia32 __user *buf)
@@ -468,14 +539,15 @@
sizeof(struct i387_fsave_struct));
}
-static int restore_i387_fxsave(struct _fpstate_ia32 __user *buf)
+static int restore_i387_fxsave(struct _fpstate_ia32 __user *buf,
+ unsigned int size)
{
struct task_struct *tsk = current;
struct user_i387_ia32_struct env;
int err;
err = __copy_from_user(&tsk->thread.xstate->fxsave, &buf->_fxsr_env[0],
- sizeof(struct i387_fxsave_struct));
+ size);
/* mxcsr reserved bits must be masked to zero for security reasons */
tsk->thread.xstate->fxsave.mxcsr &= mxcsr_feature_mask;
if (err || __copy_from_user(&env, buf, sizeof(env)))
@@ -485,14 +557,69 @@
return 0;
}
-int restore_i387_ia32(struct _fpstate_ia32 __user *buf)
+static int restore_i387_xsave(void __user *buf)
+{
+ struct _fpx_sw_bytes fx_sw_user;
+ struct _fpstate_ia32 __user *fx_user =
+ ((struct _fpstate_ia32 __user *) buf);
+ struct i387_fxsave_struct __user *fx =
+ (struct i387_fxsave_struct __user *) &fx_user->_fxsr_env[0];
+ struct xsave_hdr_struct *xsave_hdr =
+ ¤t->thread.xstate->xsave.xsave_hdr;
+ u64 mask;
+ int err;
+
+ if (check_for_xstate(fx, buf, &fx_sw_user))
+ goto fx_only;
+
+ mask = fx_sw_user.xstate_bv;
+
+ err = restore_i387_fxsave(buf, fx_sw_user.xstate_size);
+
+ xsave_hdr->xstate_bv &= pcntxt_mask;
+ /*
+ * These bits must be zero.
+ */
+ xsave_hdr->reserved1[0] = xsave_hdr->reserved1[1] = 0;
+
+ /*
+ * Init the state that is not present in the memory layout
+ * and enabled by the OS.
+ */
+ mask = ~(pcntxt_mask & ~mask);
+ xsave_hdr->xstate_bv &= mask;
+
+ return err;
+fx_only:
+ /*
+ * Couldn't find the extended state information in the memory
+ * layout. Restore the FP/SSE and init the other extended state
+ * enabled by the OS.
+ */
+ xsave_hdr->xstate_bv = XSTATE_FPSSE;
+ return restore_i387_fxsave(buf, sizeof(struct i387_fxsave_struct));
+}
+
+int restore_i387_xstate_ia32(void __user *buf)
{
int err;
struct task_struct *tsk = current;
+ struct _fpstate_ia32 __user *fp = (struct _fpstate_ia32 __user *) buf;
if (HAVE_HWFP)
clear_fpu(tsk);
+ if (!buf) {
+ if (used_math()) {
+ clear_fpu(tsk);
+ clear_used_math();
+ }
+
+ return 0;
+ } else
+ if (!access_ok(VERIFY_READ, buf, sig_xstate_ia32_size))
+ return -EACCES;
+
if (!used_math()) {
err = init_fpu(tsk);
if (err)
@@ -500,14 +627,17 @@
}
if (HAVE_HWFP) {
- if (cpu_has_fxsr)
- err = restore_i387_fxsave(buf);
+ if (cpu_has_xsave)
+ err = restore_i387_xsave(buf);
+ else if (cpu_has_fxsr)
+ err = restore_i387_fxsave(fp, sizeof(struct
+ i387_fxsave_struct));
else
- err = restore_i387_fsave(buf);
+ err = restore_i387_fsave(fp);
} else {
err = fpregs_soft_set(current, NULL,
0, sizeof(struct user_i387_ia32_struct),
- NULL, buf) != 0;
+ NULL, fp) != 0;
}
set_used_math();
diff --git a/arch/x86/kernel/i8259.c b/arch/x86/kernel/i8259.c
index dc92b49..4b8a53d 100644
--- a/arch/x86/kernel/i8259.c
+++ b/arch/x86/kernel/i8259.c
@@ -282,6 +282,30 @@
device_initcall(i8259A_init_sysfs);
+void mask_8259A(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&i8259A_lock, flags);
+
+ outb(0xff, PIC_MASTER_IMR); /* mask all of 8259A-1 */
+ outb(0xff, PIC_SLAVE_IMR); /* mask all of 8259A-2 */
+
+ spin_unlock_irqrestore(&i8259A_lock, flags);
+}
+
+void unmask_8259A(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&i8259A_lock, flags);
+
+ outb(cached_master_mask, PIC_MASTER_IMR); /* restore master IRQ mask */
+ outb(cached_slave_mask, PIC_SLAVE_IMR); /* restore slave IRQ mask */
+
+ spin_unlock_irqrestore(&i8259A_lock, flags);
+}
+
void init_8259A(int auto_eoi)
{
unsigned long flags;
diff --git a/arch/x86/kernel/io_apic_32.c b/arch/x86/kernel/io_apic_32.c
index 09cddb5..e710289 100644
--- a/arch/x86/kernel/io_apic_32.c
+++ b/arch/x86/kernel/io_apic_32.c
@@ -46,10 +46,13 @@
#include <asm/nmi.h>
#include <asm/msidef.h>
#include <asm/hypertransport.h>
+#include <asm/setup.h>
#include <mach_apic.h>
#include <mach_apicdef.h>
+#define __apicdebuginit(type) static type __init
+
int (*ioapic_renumber_irq)(int ioapic, int irq);
atomic_t irq_mis_count;
@@ -1341,7 +1344,8 @@
ioapic_write_entry(apic, pin, entry);
}
-void __init print_IO_APIC(void)
+
+__apicdebuginit(void) print_IO_APIC(void)
{
int apic, i;
union IO_APIC_reg_00 reg_00;
@@ -1456,9 +1460,7 @@
return;
}
-#if 0
-
-static void print_APIC_bitfield(int base)
+__apicdebuginit(void) print_APIC_bitfield(int base)
{
unsigned int v;
int i, j;
@@ -1479,9 +1481,10 @@
}
}
-void /*__init*/ print_local_APIC(void *dummy)
+__apicdebuginit(void) print_local_APIC(void *dummy)
{
unsigned int v, ver, maxlvt;
+ u64 icr;
if (apic_verbosity == APIC_QUIET)
return;
@@ -1490,7 +1493,7 @@
smp_processor_id(), hard_smp_processor_id());
v = apic_read(APIC_ID);
printk(KERN_INFO "... APIC ID: %08x (%01x)\n", v,
- GET_APIC_ID(read_apic_id()));
+ GET_APIC_ID(v));
v = apic_read(APIC_LVR);
printk(KERN_INFO "... APIC VERSION: %08x\n", v);
ver = GET_APIC_VERSION(v);
@@ -1532,10 +1535,9 @@
printk(KERN_DEBUG "... APIC ESR: %08x\n", v);
}
- v = apic_read(APIC_ICR);
- printk(KERN_DEBUG "... APIC ICR: %08x\n", v);
- v = apic_read(APIC_ICR2);
- printk(KERN_DEBUG "... APIC ICR2: %08x\n", v);
+ icr = apic_icr_read();
+ printk(KERN_DEBUG "... APIC ICR: %08x\n", icr);
+ printk(KERN_DEBUG "... APIC ICR2: %08x\n", icr >> 32);
v = apic_read(APIC_LVTT);
printk(KERN_DEBUG "... APIC LVTT: %08x\n", v);
@@ -1563,12 +1565,12 @@
printk("\n");
}
-void print_all_local_APICs(void)
+__apicdebuginit(void) print_all_local_APICs(void)
{
on_each_cpu(print_local_APIC, NULL, 1);
}
-void /*__init*/ print_PIC(void)
+__apicdebuginit(void) print_PIC(void)
{
unsigned int v;
unsigned long flags;
@@ -1600,7 +1602,17 @@
printk(KERN_DEBUG "... PIC ELCR: %04x\n", v);
}
-#endif /* 0 */
+__apicdebuginit(int) print_all_ICs(void)
+{
+ print_PIC();
+ print_all_local_APICs();
+ print_IO_APIC();
+
+ return 0;
+}
+
+fs_initcall(print_all_ICs);
+
static void __init enable_IO_APIC(void)
{
@@ -1698,8 +1710,7 @@
entry.dest_mode = 0; /* Physical */
entry.delivery_mode = dest_ExtINT; /* ExtInt */
entry.vector = 0;
- entry.dest.physical.physical_dest =
- GET_APIC_ID(read_apic_id());
+ entry.dest.physical.physical_dest = read_apic_id();
/*
* Add it to the IO-APIC irq-routing table:
@@ -1725,10 +1736,8 @@
unsigned char old_id;
unsigned long flags;
-#ifdef CONFIG_X86_NUMAQ
- if (found_numaq)
+ if (x86_quirks->setup_ioapic_ids && x86_quirks->setup_ioapic_ids())
return;
-#endif
/*
* Don't check I/O APIC IDs for xAPIC systems. They have
@@ -2329,8 +2338,6 @@
setup_IO_APIC_irqs();
init_IO_APIC_traps();
check_timer();
- if (!acpi_ioapic)
- print_IO_APIC();
}
/*
diff --git a/arch/x86/kernel/io_apic_64.c b/arch/x86/kernel/io_apic_64.c
index 61a83b7..02063ae 100644
--- a/arch/x86/kernel/io_apic_64.c
+++ b/arch/x86/kernel/io_apic_64.c
@@ -37,6 +37,7 @@
#include <acpi/acpi_bus.h>
#endif
#include <linux/bootmem.h>
+#include <linux/dmar.h>
#include <asm/idle.h>
#include <asm/io.h>
@@ -49,10 +50,13 @@
#include <asm/nmi.h>
#include <asm/msidef.h>
#include <asm/hypertransport.h>
+#include <asm/irq_remapping.h>
#include <mach_ipi.h>
#include <mach_apic.h>
+#define __apicdebuginit(type) static type __init
+
struct irq_cfg {
cpumask_t domain;
cpumask_t old_domain;
@@ -87,8 +91,6 @@
char system_vectors[NR_VECTORS] = { [0 ... NR_VECTORS-1] = SYS_VECTOR_FREE};
-#define __apicdebuginit __init
-
int sis_apic_bug; /* not actually supported, dummy for compile */
static int no_timer_check;
@@ -108,6 +110,9 @@
*/
int nr_ioapic_registers[MAX_IO_APICS];
+/* I/O APIC RTE contents at the OS boot up */
+struct IO_APIC_route_entry *early_ioapic_entries[MAX_IO_APICS];
+
/* I/O APIC entries */
struct mp_config_ioapic mp_ioapics[MAX_IO_APICS];
int nr_ioapics;
@@ -303,7 +308,12 @@
pin = entry->pin;
if (pin == -1)
break;
- io_apic_write(apic, 0x11 + pin*2, dest);
+ /*
+ * With interrupt-remapping, destination information comes
+ * from interrupt-remapping table entry.
+ */
+ if (!irq_remapped(irq))
+ io_apic_write(apic, 0x11 + pin*2, dest);
reg = io_apic_read(apic, 0x10 + pin*2);
reg &= ~IO_APIC_REDIR_VECTOR_MASK;
reg |= vector;
@@ -440,6 +450,69 @@
clear_IO_APIC_pin(apic, pin);
}
+/*
+ * Saves and masks all the unmasked IO-APIC RTE's
+ */
+int save_mask_IO_APIC_setup(void)
+{
+ union IO_APIC_reg_01 reg_01;
+ unsigned long flags;
+ int apic, pin;
+
+ /*
+ * The number of IO-APIC IRQ registers (== #pins):
+ */
+ for (apic = 0; apic < nr_ioapics; apic++) {
+ spin_lock_irqsave(&ioapic_lock, flags);
+ reg_01.raw = io_apic_read(apic, 1);
+ spin_unlock_irqrestore(&ioapic_lock, flags);
+ nr_ioapic_registers[apic] = reg_01.bits.entries+1;
+ }
+
+ for (apic = 0; apic < nr_ioapics; apic++) {
+ early_ioapic_entries[apic] =
+ kzalloc(sizeof(struct IO_APIC_route_entry) *
+ nr_ioapic_registers[apic], GFP_KERNEL);
+ if (!early_ioapic_entries[apic])
+ return -ENOMEM;
+ }
+
+ for (apic = 0; apic < nr_ioapics; apic++)
+ for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) {
+ struct IO_APIC_route_entry entry;
+
+ entry = early_ioapic_entries[apic][pin] =
+ ioapic_read_entry(apic, pin);
+ if (!entry.mask) {
+ entry.mask = 1;
+ ioapic_write_entry(apic, pin, entry);
+ }
+ }
+ return 0;
+}
+
+void restore_IO_APIC_setup(void)
+{
+ int apic, pin;
+
+ for (apic = 0; apic < nr_ioapics; apic++)
+ for (pin = 0; pin < nr_ioapic_registers[apic]; pin++)
+ ioapic_write_entry(apic, pin,
+ early_ioapic_entries[apic][pin]);
+}
+
+void reinit_intr_remapped_IO_APIC(int intr_remapping)
+{
+ /*
+ * for now plain restore of previous settings.
+ * TBD: In the case of OS enabling interrupt-remapping,
+ * IO-APIC RTE's need to be setup to point to interrupt-remapping
+ * table entries. for now, do a plain restore, and wait for
+ * the setup_IO_APIC_irqs() to do proper initialization.
+ */
+ restore_IO_APIC_setup();
+}
+
int skip_ioapic_setup;
int ioapic_force;
@@ -839,18 +912,98 @@
}
static struct irq_chip ioapic_chip;
+#ifdef CONFIG_INTR_REMAP
+static struct irq_chip ir_ioapic_chip;
+#endif
static void ioapic_register_intr(int irq, unsigned long trigger)
{
- if (trigger) {
+ if (trigger)
irq_desc[irq].status |= IRQ_LEVEL;
- set_irq_chip_and_handler_name(irq, &ioapic_chip,
- handle_fasteoi_irq, "fasteoi");
- } else {
+ else
irq_desc[irq].status &= ~IRQ_LEVEL;
+
+#ifdef CONFIG_INTR_REMAP
+ if (irq_remapped(irq)) {
+ irq_desc[irq].status |= IRQ_MOVE_PCNTXT;
+ if (trigger)
+ set_irq_chip_and_handler_name(irq, &ir_ioapic_chip,
+ handle_fasteoi_irq,
+ "fasteoi");
+ else
+ set_irq_chip_and_handler_name(irq, &ir_ioapic_chip,
+ handle_edge_irq, "edge");
+ return;
+ }
+#endif
+ if (trigger)
+ set_irq_chip_and_handler_name(irq, &ioapic_chip,
+ handle_fasteoi_irq,
+ "fasteoi");
+ else
set_irq_chip_and_handler_name(irq, &ioapic_chip,
handle_edge_irq, "edge");
+}
+
+static int setup_ioapic_entry(int apic, int irq,
+ struct IO_APIC_route_entry *entry,
+ unsigned int destination, int trigger,
+ int polarity, int vector)
+{
+ /*
+ * add it to the IO-APIC irq-routing table:
+ */
+ memset(entry,0,sizeof(*entry));
+
+#ifdef CONFIG_INTR_REMAP
+ if (intr_remapping_enabled) {
+ struct intel_iommu *iommu = map_ioapic_to_ir(apic);
+ struct irte irte;
+ struct IR_IO_APIC_route_entry *ir_entry =
+ (struct IR_IO_APIC_route_entry *) entry;
+ int index;
+
+ if (!iommu)
+ panic("No mapping iommu for ioapic %d\n", apic);
+
+ index = alloc_irte(iommu, irq, 1);
+ if (index < 0)
+ panic("Failed to allocate IRTE for ioapic %d\n", apic);
+
+ memset(&irte, 0, sizeof(irte));
+
+ irte.present = 1;
+ irte.dst_mode = INT_DEST_MODE;
+ irte.trigger_mode = trigger;
+ irte.dlvry_mode = INT_DELIVERY_MODE;
+ irte.vector = vector;
+ irte.dest_id = IRTE_DEST(destination);
+
+ modify_irte(irq, &irte);
+
+ ir_entry->index2 = (index >> 15) & 0x1;
+ ir_entry->zero = 0;
+ ir_entry->format = 1;
+ ir_entry->index = (index & 0x7fff);
+ } else
+#endif
+ {
+ entry->delivery_mode = INT_DELIVERY_MODE;
+ entry->dest_mode = INT_DEST_MODE;
+ entry->dest = destination;
}
+
+ entry->mask = 0; /* enable IRQ */
+ entry->trigger = trigger;
+ entry->polarity = polarity;
+ entry->vector = vector;
+
+ /* Mask level triggered irqs.
+ * Use IRQ_DELAYED_DISABLE for edge triggered irqs.
+ */
+ if (trigger)
+ entry->mask = 1;
+ return 0;
}
static void setup_IO_APIC_irq(int apic, int pin, unsigned int irq,
@@ -875,24 +1028,15 @@
apic, mp_ioapics[apic].mp_apicid, pin, cfg->vector,
irq, trigger, polarity);
- /*
- * add it to the IO-APIC irq-routing table:
- */
- memset(&entry,0,sizeof(entry));
- entry.delivery_mode = INT_DELIVERY_MODE;
- entry.dest_mode = INT_DEST_MODE;
- entry.dest = cpu_mask_to_apicid(mask);
- entry.mask = 0; /* enable IRQ */
- entry.trigger = trigger;
- entry.polarity = polarity;
- entry.vector = cfg->vector;
-
- /* Mask level triggered irqs.
- * Use IRQ_DELAYED_DISABLE for edge triggered irqs.
- */
- if (trigger)
- entry.mask = 1;
+ if (setup_ioapic_entry(mp_ioapics[apic].mp_apicid, irq, &entry,
+ cpu_mask_to_apicid(mask), trigger, polarity,
+ cfg->vector)) {
+ printk("Failed to setup ioapic entry for ioapic %d, pin %d\n",
+ mp_ioapics[apic].mp_apicid, pin);
+ __clear_irq_vector(irq);
+ return;
+ }
ioapic_register_intr(irq, trigger);
if (irq < 16)
@@ -944,6 +1088,9 @@
{
struct IO_APIC_route_entry entry;
+ if (intr_remapping_enabled)
+ return;
+
memset(&entry, 0, sizeof(entry));
/*
@@ -970,7 +1117,8 @@
ioapic_write_entry(apic, pin, entry);
}
-void __apicdebuginit print_IO_APIC(void)
+
+__apicdebuginit(void) print_IO_APIC(void)
{
int apic, i;
union IO_APIC_reg_00 reg_00;
@@ -1064,9 +1212,7 @@
return;
}
-#if 0
-
-static __apicdebuginit void print_APIC_bitfield (int base)
+__apicdebuginit(void) print_APIC_bitfield(int base)
{
unsigned int v;
int i, j;
@@ -1087,9 +1233,10 @@
}
}
-void __apicdebuginit print_local_APIC(void * dummy)
+__apicdebuginit(void) print_local_APIC(void *dummy)
{
unsigned int v, ver, maxlvt;
+ unsigned long icr;
if (apic_verbosity == APIC_QUIET)
return;
@@ -1097,7 +1244,7 @@
printk("\n" KERN_DEBUG "printing local APIC contents on CPU#%d/%d:\n",
smp_processor_id(), hard_smp_processor_id());
v = apic_read(APIC_ID);
- printk(KERN_INFO "... APIC ID: %08x (%01x)\n", v, GET_APIC_ID(read_apic_id()));
+ printk(KERN_INFO "... APIC ID: %08x (%01x)\n", v, read_apic_id());
v = apic_read(APIC_LVR);
printk(KERN_INFO "... APIC VERSION: %08x\n", v);
ver = GET_APIC_VERSION(v);
@@ -1133,10 +1280,9 @@
v = apic_read(APIC_ESR);
printk(KERN_DEBUG "... APIC ESR: %08x\n", v);
- v = apic_read(APIC_ICR);
- printk(KERN_DEBUG "... APIC ICR: %08x\n", v);
- v = apic_read(APIC_ICR2);
- printk(KERN_DEBUG "... APIC ICR2: %08x\n", v);
+ icr = apic_icr_read();
+ printk(KERN_DEBUG "... APIC ICR: %08x\n", (u32)icr);
+ printk(KERN_DEBUG "... APIC ICR2: %08x\n", (u32)(icr >> 32));
v = apic_read(APIC_LVTT);
printk(KERN_DEBUG "... APIC LVTT: %08x\n", v);
@@ -1164,12 +1310,12 @@
printk("\n");
}
-void print_all_local_APICs (void)
+__apicdebuginit(void) print_all_local_APICs(void)
{
on_each_cpu(print_local_APIC, NULL, 1);
}
-void __apicdebuginit print_PIC(void)
+__apicdebuginit(void) print_PIC(void)
{
unsigned int v;
unsigned long flags;
@@ -1201,7 +1347,17 @@
printk(KERN_DEBUG "... PIC ELCR: %04x\n", v);
}
-#endif /* 0 */
+__apicdebuginit(int) print_all_ICs(void)
+{
+ print_PIC();
+ print_all_local_APICs();
+ print_IO_APIC();
+
+ return 0;
+}
+
+fs_initcall(print_all_ICs);
+
void __init enable_IO_APIC(void)
{
@@ -1291,7 +1447,7 @@
entry.dest_mode = 0; /* Physical */
entry.delivery_mode = dest_ExtINT; /* ExtInt */
entry.vector = 0;
- entry.dest = GET_APIC_ID(read_apic_id());
+ entry.dest = read_apic_id();
/*
* Add it to the IO-APIC irq-routing table:
@@ -1397,6 +1553,147 @@
*/
#ifdef CONFIG_SMP
+
+#ifdef CONFIG_INTR_REMAP
+static void ir_irq_migration(struct work_struct *work);
+
+static DECLARE_DELAYED_WORK(ir_migration_work, ir_irq_migration);
+
+/*
+ * Migrate the IO-APIC irq in the presence of intr-remapping.
+ *
+ * For edge triggered, irq migration is a simple atomic update(of vector
+ * and cpu destination) of IRTE and flush the hardware cache.
+ *
+ * For level triggered, we need to modify the io-apic RTE aswell with the update
+ * vector information, along with modifying IRTE with vector and destination.
+ * So irq migration for level triggered is little bit more complex compared to
+ * edge triggered migration. But the good news is, we use the same algorithm
+ * for level triggered migration as we have today, only difference being,
+ * we now initiate the irq migration from process context instead of the
+ * interrupt context.
+ *
+ * In future, when we do a directed EOI (combined with cpu EOI broadcast
+ * suppression) to the IO-APIC, level triggered irq migration will also be
+ * as simple as edge triggered migration and we can do the irq migration
+ * with a simple atomic update to IO-APIC RTE.
+ */
+static void migrate_ioapic_irq(int irq, cpumask_t mask)
+{
+ struct irq_cfg *cfg = irq_cfg + irq;
+ struct irq_desc *desc = irq_desc + irq;
+ cpumask_t tmp, cleanup_mask;
+ struct irte irte;
+ int modify_ioapic_rte = desc->status & IRQ_LEVEL;
+ unsigned int dest;
+ unsigned long flags;
+
+ cpus_and(tmp, mask, cpu_online_map);
+ if (cpus_empty(tmp))
+ return;
+
+ if (get_irte(irq, &irte))
+ return;
+
+ if (assign_irq_vector(irq, mask))
+ return;
+
+ cpus_and(tmp, cfg->domain, mask);
+ dest = cpu_mask_to_apicid(tmp);
+
+ if (modify_ioapic_rte) {
+ spin_lock_irqsave(&ioapic_lock, flags);
+ __target_IO_APIC_irq(irq, dest, cfg->vector);
+ spin_unlock_irqrestore(&ioapic_lock, flags);
+ }
+
+ irte.vector = cfg->vector;
+ irte.dest_id = IRTE_DEST(dest);
+
+ /*
+ * Modified the IRTE and flushes the Interrupt entry cache.
+ */
+ modify_irte(irq, &irte);
+
+ if (cfg->move_in_progress) {
+ cpus_and(cleanup_mask, cfg->old_domain, cpu_online_map);
+ cfg->move_cleanup_count = cpus_weight(cleanup_mask);
+ send_IPI_mask(cleanup_mask, IRQ_MOVE_CLEANUP_VECTOR);
+ cfg->move_in_progress = 0;
+ }
+
+ irq_desc[irq].affinity = mask;
+}
+
+static int migrate_irq_remapped_level(int irq)
+{
+ int ret = -1;
+
+ mask_IO_APIC_irq(irq);
+
+ if (io_apic_level_ack_pending(irq)) {
+ /*
+ * Interrupt in progress. Migrating irq now will change the
+ * vector information in the IO-APIC RTE and that will confuse
+ * the EOI broadcast performed by cpu.
+ * So, delay the irq migration to the next instance.
+ */
+ schedule_delayed_work(&ir_migration_work, 1);
+ goto unmask;
+ }
+
+ /* everthing is clear. we have right of way */
+ migrate_ioapic_irq(irq, irq_desc[irq].pending_mask);
+
+ ret = 0;
+ irq_desc[irq].status &= ~IRQ_MOVE_PENDING;
+ cpus_clear(irq_desc[irq].pending_mask);
+
+unmask:
+ unmask_IO_APIC_irq(irq);
+ return ret;
+}
+
+static void ir_irq_migration(struct work_struct *work)
+{
+ int irq;
+
+ for (irq = 0; irq < NR_IRQS; irq++) {
+ struct irq_desc *desc = irq_desc + irq;
+ if (desc->status & IRQ_MOVE_PENDING) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&desc->lock, flags);
+ if (!desc->chip->set_affinity ||
+ !(desc->status & IRQ_MOVE_PENDING)) {
+ desc->status &= ~IRQ_MOVE_PENDING;
+ spin_unlock_irqrestore(&desc->lock, flags);
+ continue;
+ }
+
+ desc->chip->set_affinity(irq,
+ irq_desc[irq].pending_mask);
+ spin_unlock_irqrestore(&desc->lock, flags);
+ }
+ }
+}
+
+/*
+ * Migrates the IRQ destination in the process context.
+ */
+static void set_ir_ioapic_affinity_irq(unsigned int irq, cpumask_t mask)
+{
+ if (irq_desc[irq].status & IRQ_LEVEL) {
+ irq_desc[irq].status |= IRQ_MOVE_PENDING;
+ irq_desc[irq].pending_mask = mask;
+ migrate_irq_remapped_level(irq);
+ return;
+ }
+
+ migrate_ioapic_irq(irq, mask);
+}
+#endif
+
asmlinkage void smp_irq_move_cleanup_interrupt(void)
{
unsigned vector, me;
@@ -1453,6 +1750,17 @@
#else
static inline void irq_complete_move(unsigned int irq) {}
#endif
+#ifdef CONFIG_INTR_REMAP
+static void ack_x2apic_level(unsigned int irq)
+{
+ ack_x2APIC_irq();
+}
+
+static void ack_x2apic_edge(unsigned int irq)
+{
+ ack_x2APIC_irq();
+}
+#endif
static void ack_apic_edge(unsigned int irq)
{
@@ -1527,6 +1835,21 @@
.retrigger = ioapic_retrigger_irq,
};
+#ifdef CONFIG_INTR_REMAP
+static struct irq_chip ir_ioapic_chip __read_mostly = {
+ .name = "IR-IO-APIC",
+ .startup = startup_ioapic_irq,
+ .mask = mask_IO_APIC_irq,
+ .unmask = unmask_IO_APIC_irq,
+ .ack = ack_x2apic_edge,
+ .eoi = ack_x2apic_level,
+#ifdef CONFIG_SMP
+ .set_affinity = set_ir_ioapic_affinity_irq,
+#endif
+ .retrigger = ioapic_retrigger_irq,
+};
+#endif
+
static inline void init_IO_APIC_traps(void)
{
int irq;
@@ -1712,6 +2035,8 @@
* 8259A.
*/
if (pin1 == -1) {
+ if (intr_remapping_enabled)
+ panic("BIOS bug: timer not connected to IO-APIC");
pin1 = pin2;
apic1 = apic2;
no_pin1 = 1;
@@ -1738,6 +2063,8 @@
clear_IO_APIC_pin(0, pin1);
goto out;
}
+ if (intr_remapping_enabled)
+ panic("timer doesn't work through Interrupt-remapped IO-APIC");
clear_IO_APIC_pin(apic1, pin1);
if (!no_pin1)
apic_printk(APIC_QUIET, KERN_ERR "..MP-BIOS bug: "
@@ -1854,8 +2181,6 @@
setup_IO_APIC_irqs();
init_IO_APIC_traps();
check_timer();
- if (!acpi_ioapic)
- print_IO_APIC();
}
struct sysfs_ioapic_data {
@@ -1977,6 +2302,9 @@
dynamic_irq_cleanup(irq);
+#ifdef CONFIG_INTR_REMAP
+ free_irte(irq);
+#endif
spin_lock_irqsave(&vector_lock, flags);
__clear_irq_vector(irq);
spin_unlock_irqrestore(&vector_lock, flags);
@@ -1995,10 +2323,41 @@
tmp = TARGET_CPUS;
err = assign_irq_vector(irq, tmp);
- if (!err) {
- cpus_and(tmp, cfg->domain, tmp);
- dest = cpu_mask_to_apicid(tmp);
+ if (err)
+ return err;
+ cpus_and(tmp, cfg->domain, tmp);
+ dest = cpu_mask_to_apicid(tmp);
+
+#ifdef CONFIG_INTR_REMAP
+ if (irq_remapped(irq)) {
+ struct irte irte;
+ int ir_index;
+ u16 sub_handle;
+
+ ir_index = map_irq_to_irte_handle(irq, &sub_handle);
+ BUG_ON(ir_index == -1);
+
+ memset (&irte, 0, sizeof(irte));
+
+ irte.present = 1;
+ irte.dst_mode = INT_DEST_MODE;
+ irte.trigger_mode = 0; /* edge */
+ irte.dlvry_mode = INT_DELIVERY_MODE;
+ irte.vector = cfg->vector;
+ irte.dest_id = IRTE_DEST(dest);
+
+ modify_irte(irq, &irte);
+
+ msg->address_hi = MSI_ADDR_BASE_HI;
+ msg->data = sub_handle;
+ msg->address_lo = MSI_ADDR_BASE_LO | MSI_ADDR_IR_EXT_INT |
+ MSI_ADDR_IR_SHV |
+ MSI_ADDR_IR_INDEX1(ir_index) |
+ MSI_ADDR_IR_INDEX2(ir_index);
+ } else
+#endif
+ {
msg->address_hi = MSI_ADDR_BASE_HI;
msg->address_lo =
MSI_ADDR_BASE_LO |
@@ -2049,6 +2408,55 @@
write_msi_msg(irq, &msg);
irq_desc[irq].affinity = mask;
}
+
+#ifdef CONFIG_INTR_REMAP
+/*
+ * Migrate the MSI irq to another cpumask. This migration is
+ * done in the process context using interrupt-remapping hardware.
+ */
+static void ir_set_msi_irq_affinity(unsigned int irq, cpumask_t mask)
+{
+ struct irq_cfg *cfg = irq_cfg + irq;
+ unsigned int dest;
+ cpumask_t tmp, cleanup_mask;
+ struct irte irte;
+
+ cpus_and(tmp, mask, cpu_online_map);
+ if (cpus_empty(tmp))
+ return;
+
+ if (get_irte(irq, &irte))
+ return;
+
+ if (assign_irq_vector(irq, mask))
+ return;
+
+ cpus_and(tmp, cfg->domain, mask);
+ dest = cpu_mask_to_apicid(tmp);
+
+ irte.vector = cfg->vector;
+ irte.dest_id = IRTE_DEST(dest);
+
+ /*
+ * atomically update the IRTE with the new destination and vector.
+ */
+ modify_irte(irq, &irte);
+
+ /*
+ * After this point, all the interrupts will start arriving
+ * at the new destination. So, time to cleanup the previous
+ * vector allocation.
+ */
+ if (cfg->move_in_progress) {
+ cpus_and(cleanup_mask, cfg->old_domain, cpu_online_map);
+ cfg->move_cleanup_count = cpus_weight(cleanup_mask);
+ send_IPI_mask(cleanup_mask, IRQ_MOVE_CLEANUP_VECTOR);
+ cfg->move_in_progress = 0;
+ }
+
+ irq_desc[irq].affinity = mask;
+}
+#endif
#endif /* CONFIG_SMP */
/*
@@ -2066,28 +2474,159 @@
.retrigger = ioapic_retrigger_irq,
};
-int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc)
+#ifdef CONFIG_INTR_REMAP
+static struct irq_chip msi_ir_chip = {
+ .name = "IR-PCI-MSI",
+ .unmask = unmask_msi_irq,
+ .mask = mask_msi_irq,
+ .ack = ack_x2apic_edge,
+#ifdef CONFIG_SMP
+ .set_affinity = ir_set_msi_irq_affinity,
+#endif
+ .retrigger = ioapic_retrigger_irq,
+};
+
+/*
+ * Map the PCI dev to the corresponding remapping hardware unit
+ * and allocate 'nvec' consecutive interrupt-remapping table entries
+ * in it.
+ */
+static int msi_alloc_irte(struct pci_dev *dev, int irq, int nvec)
{
+ struct intel_iommu *iommu;
+ int index;
+
+ iommu = map_dev_to_ir(dev);
+ if (!iommu) {
+ printk(KERN_ERR
+ "Unable to map PCI %s to iommu\n", pci_name(dev));
+ return -ENOENT;
+ }
+
+ index = alloc_irte(iommu, irq, nvec);
+ if (index < 0) {
+ printk(KERN_ERR
+ "Unable to allocate %d IRTE for PCI %s\n", nvec,
+ pci_name(dev));
+ return -ENOSPC;
+ }
+ return index;
+}
+#endif
+
+static int setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc, int irq)
+{
+ int ret;
struct msi_msg msg;
- int irq, ret;
- irq = create_irq();
- if (irq < 0)
- return irq;
ret = msi_compose_msg(dev, irq, &msg);
- if (ret < 0) {
- destroy_irq(irq);
+ if (ret < 0)
return ret;
- }
set_irq_msi(irq, desc);
write_msi_msg(irq, &msg);
- set_irq_chip_and_handler_name(irq, &msi_chip, handle_edge_irq, "edge");
+#ifdef CONFIG_INTR_REMAP
+ if (irq_remapped(irq)) {
+ struct irq_desc *desc = irq_desc + irq;
+ /*
+ * irq migration in process context
+ */
+ desc->status |= IRQ_MOVE_PCNTXT;
+ set_irq_chip_and_handler_name(irq, &msi_ir_chip, handle_edge_irq, "edge");
+ } else
+#endif
+ set_irq_chip_and_handler_name(irq, &msi_chip, handle_edge_irq, "edge");
return 0;
}
+int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc)
+{
+ int irq, ret;
+
+ irq = create_irq();
+ if (irq < 0)
+ return irq;
+
+#ifdef CONFIG_INTR_REMAP
+ if (!intr_remapping_enabled)
+ goto no_ir;
+
+ ret = msi_alloc_irte(dev, irq, 1);
+ if (ret < 0)
+ goto error;
+no_ir:
+#endif
+ ret = setup_msi_irq(dev, desc, irq);
+ if (ret < 0) {
+ destroy_irq(irq);
+ return ret;
+ }
+ return 0;
+
+#ifdef CONFIG_INTR_REMAP
+error:
+ destroy_irq(irq);
+ return ret;
+#endif
+}
+
+int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
+{
+ int irq, ret, sub_handle;
+ struct msi_desc *desc;
+#ifdef CONFIG_INTR_REMAP
+ struct intel_iommu *iommu = 0;
+ int index = 0;
+#endif
+
+ sub_handle = 0;
+ list_for_each_entry(desc, &dev->msi_list, list) {
+ irq = create_irq();
+ if (irq < 0)
+ return irq;
+#ifdef CONFIG_INTR_REMAP
+ if (!intr_remapping_enabled)
+ goto no_ir;
+
+ if (!sub_handle) {
+ /*
+ * allocate the consecutive block of IRTE's
+ * for 'nvec'
+ */
+ index = msi_alloc_irte(dev, irq, nvec);
+ if (index < 0) {
+ ret = index;
+ goto error;
+ }
+ } else {
+ iommu = map_dev_to_ir(dev);
+ if (!iommu) {
+ ret = -ENOENT;
+ goto error;
+ }
+ /*
+ * setup the mapping between the irq and the IRTE
+ * base index, the sub_handle pointing to the
+ * appropriate interrupt remap table entry.
+ */
+ set_irte_irq(irq, iommu, index, sub_handle);
+ }
+no_ir:
+#endif
+ ret = setup_msi_irq(dev, desc, irq);
+ if (ret < 0)
+ goto error;
+ sub_handle++;
+ }
+ return 0;
+
+error:
+ destroy_irq(irq);
+ return ret;
+}
+
void arch_teardown_msi_irq(unsigned int irq)
{
destroy_irq(irq);
@@ -2333,6 +2872,10 @@
setup_IO_APIC_irq(ioapic, pin, irq,
irq_trigger(irq_entry),
irq_polarity(irq_entry));
+#ifdef CONFIG_INTR_REMAP
+ else if (intr_remapping_enabled)
+ set_ir_ioapic_affinity_irq(irq, TARGET_CPUS);
+#endif
else
set_ioapic_affinity_irq(irq, TARGET_CPUS);
}
diff --git a/arch/x86/kernel/irqinit_32.c b/arch/x86/kernel/irqinit_32.c
index d669142..9200a1e 100644
--- a/arch/x86/kernel/irqinit_32.c
+++ b/arch/x86/kernel/irqinit_32.c
@@ -74,6 +74,15 @@
}
}
+/*
+ * IRQ2 is cascade interrupt to second interrupt controller
+ */
+static struct irqaction irq2 = {
+ .handler = no_action,
+ .mask = CPU_MASK_NONE,
+ .name = "cascade",
+};
+
/* Overridden in paravirt.c */
void init_IRQ(void) __attribute__((weak, alias("native_init_IRQ")));
@@ -98,6 +107,46 @@
set_intr_gate(vector, interrupt[i]);
}
+#if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_SMP)
+ /*
+ * IRQ0 must be given a fixed assignment and initialized,
+ * because it's used before the IO-APIC is set up.
+ */
+ set_intr_gate(FIRST_DEVICE_VECTOR, interrupt[0]);
+
+ /*
+ * The reschedule interrupt is a CPU-to-CPU reschedule-helper
+ * IPI, driven by wakeup.
+ */
+ alloc_intr_gate(RESCHEDULE_VECTOR, reschedule_interrupt);
+
+ /* IPI for invalidation */
+ alloc_intr_gate(INVALIDATE_TLB_VECTOR, invalidate_interrupt);
+
+ /* IPI for generic function call */
+ alloc_intr_gate(CALL_FUNCTION_VECTOR, call_function_interrupt);
+
+ /* IPI for single call function */
+ set_intr_gate(CALL_FUNCTION_SINGLE_VECTOR, call_function_single_interrupt);
+#endif
+
+#ifdef CONFIG_X86_LOCAL_APIC
+ /* self generated IPI for local APIC timer */
+ alloc_intr_gate(LOCAL_TIMER_VECTOR, apic_timer_interrupt);
+
+ /* IPI vectors for APIC spurious and error interrupts */
+ alloc_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt);
+ alloc_intr_gate(ERROR_APIC_VECTOR, error_interrupt);
+#endif
+
+#if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_X86_MCE_P4THERMAL)
+ /* thermal monitor LVT interrupt */
+ alloc_intr_gate(THERMAL_APIC_VECTOR, thermal_interrupt);
+#endif
+
+ if (!acpi_ioapic)
+ setup_irq(2, &irq2);
+
/* setup after call gates are initialised (usually add in
* the architecture specific gates)
*/
diff --git a/arch/x86/kernel/irqinit_64.c b/arch/x86/kernel/irqinit_64.c
index 1f26fd9..5b5be9d 100644
--- a/arch/x86/kernel/irqinit_64.c
+++ b/arch/x86/kernel/irqinit_64.c
@@ -135,7 +135,7 @@
[IRQ15_VECTOR + 1 ... NR_VECTORS - 1] = -1
};
-static void __init init_ISA_irqs (void)
+void __init init_ISA_irqs(void)
{
int i;
@@ -164,22 +164,8 @@
void init_IRQ(void) __attribute__((weak, alias("native_init_IRQ")));
-void __init native_init_IRQ(void)
+static void __init smp_intr_init(void)
{
- int i;
-
- init_ISA_irqs();
- /*
- * Cover the whole vector space, no vector can escape
- * us. (some of these will be overridden and become
- * 'special' SMP interrupts)
- */
- for (i = 0; i < (NR_VECTORS - FIRST_EXTERNAL_VECTOR); i++) {
- int vector = FIRST_EXTERNAL_VECTOR + i;
- if (vector != IA32_SYSCALL_VECTOR)
- set_intr_gate(vector, interrupt[i]);
- }
-
#ifdef CONFIG_SMP
/*
* The reschedule interrupt is a CPU-to-CPU reschedule-helper
@@ -207,6 +193,12 @@
/* Low priority IPI to cleanup after moving an irq */
set_intr_gate(IRQ_MOVE_CLEANUP_VECTOR, irq_move_cleanup_interrupt);
#endif
+}
+
+static void __init apic_intr_init(void)
+{
+ smp_intr_init();
+
alloc_intr_gate(THERMAL_APIC_VECTOR, thermal_interrupt);
alloc_intr_gate(THRESHOLD_APIC_VECTOR, threshold_interrupt);
@@ -216,6 +208,25 @@
/* IPI vectors for APIC spurious and error interrupts */
alloc_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt);
alloc_intr_gate(ERROR_APIC_VECTOR, error_interrupt);
+}
+
+void __init native_init_IRQ(void)
+{
+ int i;
+
+ init_ISA_irqs();
+ /*
+ * Cover the whole vector space, no vector can escape
+ * us. (some of these will be overridden and become
+ * 'special' SMP interrupts)
+ */
+ for (i = 0; i < (NR_VECTORS - FIRST_EXTERNAL_VECTOR); i++) {
+ int vector = FIRST_EXTERNAL_VECTOR + i;
+ if (vector != IA32_SYSCALL_VECTOR)
+ set_intr_gate(vector, interrupt[i]);
+ }
+
+ apic_intr_init();
if (!acpi_ioapic)
setup_irq(2, &irq2);
diff --git a/arch/x86/kernel/ldt.c b/arch/x86/kernel/ldt.c
index 0ed5f93..eee32b4 100644
--- a/arch/x86/kernel/ldt.c
+++ b/arch/x86/kernel/ldt.c
@@ -52,6 +52,8 @@
memset(newldt + oldsize * LDT_ENTRY_SIZE, 0,
(mincount - oldsize) * LDT_ENTRY_SIZE);
+ paravirt_alloc_ldt(newldt, mincount);
+
#ifdef CONFIG_X86_64
/* CHECKME: Do we really need this ? */
wmb();
@@ -74,6 +76,7 @@
#endif
}
if (oldsize) {
+ paravirt_free_ldt(oldldt, oldsize);
if (oldsize * LDT_ENTRY_SIZE > PAGE_SIZE)
vfree(oldldt);
else
@@ -85,10 +88,13 @@
static inline int copy_ldt(mm_context_t *new, mm_context_t *old)
{
int err = alloc_ldt(new, old->size, 0);
+ int i;
if (err < 0)
return err;
- memcpy(new->ldt, old->ldt, old->size * LDT_ENTRY_SIZE);
+
+ for(i = 0; i < old->size; i++)
+ write_ldt_entry(new->ldt, i, old->ldt + i * LDT_ENTRY_SIZE);
return 0;
}
@@ -125,6 +131,7 @@
if (mm == current->active_mm)
clear_LDT();
#endif
+ paravirt_free_ldt(mm->context.ldt, mm->context.size);
if (mm->context.size * LDT_ENTRY_SIZE > PAGE_SIZE)
vfree(mm->context.ldt);
else
diff --git a/arch/x86/kernel/microcode.c b/arch/x86/kernel/microcode.c
deleted file mode 100644
index 652fa5c..0000000
--- a/arch/x86/kernel/microcode.c
+++ /dev/null
@@ -1,853 +0,0 @@
-/*
- * Intel CPU Microcode Update Driver for Linux
- *
- * Copyright (C) 2000-2006 Tigran Aivazian <tigran@aivazian.fsnet.co.uk>
- * 2006 Shaohua Li <shaohua.li@intel.com>
- *
- * This driver allows to upgrade microcode on Intel processors
- * belonging to IA-32 family - PentiumPro, Pentium II,
- * Pentium III, Xeon, Pentium 4, etc.
- *
- * Reference: Section 8.11 of Volume 3a, IA-32 Intel? Architecture
- * Software Developer's Manual
- * Order Number 253668 or free download from:
- *
- * http://developer.intel.com/design/pentium4/manuals/253668.htm
- *
- * For more information, go to http://www.urbanmyth.org/microcode
- *
- * 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.
- *
- * 1.0 16 Feb 2000, Tigran Aivazian <tigran@sco.com>
- * Initial release.
- * 1.01 18 Feb 2000, Tigran Aivazian <tigran@sco.com>
- * Added read() support + cleanups.
- * 1.02 21 Feb 2000, Tigran Aivazian <tigran@sco.com>
- * Added 'device trimming' support. open(O_WRONLY) zeroes
- * and frees the saved copy of applied microcode.
- * 1.03 29 Feb 2000, Tigran Aivazian <tigran@sco.com>
- * Made to use devfs (/dev/cpu/microcode) + cleanups.
- * 1.04 06 Jun 2000, Simon Trimmer <simon@veritas.com>
- * Added misc device support (now uses both devfs and misc).
- * Added MICROCODE_IOCFREE ioctl to clear memory.
- * 1.05 09 Jun 2000, Simon Trimmer <simon@veritas.com>
- * Messages for error cases (non Intel & no suitable microcode).
- * 1.06 03 Aug 2000, Tigran Aivazian <tigran@veritas.com>
- * Removed ->release(). Removed exclusive open and status bitmap.
- * Added microcode_rwsem to serialize read()/write()/ioctl().
- * Removed global kernel lock usage.
- * 1.07 07 Sep 2000, Tigran Aivazian <tigran@veritas.com>
- * Write 0 to 0x8B msr and then cpuid before reading revision,
- * so that it works even if there were no update done by the
- * BIOS. Otherwise, reading from 0x8B gives junk (which happened
- * to be 0 on my machine which is why it worked even when I
- * disabled update by the BIOS)
- * Thanks to Eric W. Biederman <ebiederman@lnxi.com> for the fix.
- * 1.08 11 Dec 2000, Richard Schaal <richard.schaal@intel.com> and
- * Tigran Aivazian <tigran@veritas.com>
- * Intel Pentium 4 processor support and bugfixes.
- * 1.09 30 Oct 2001, Tigran Aivazian <tigran@veritas.com>
- * Bugfix for HT (Hyper-Threading) enabled processors
- * whereby processor resources are shared by all logical processors
- * in a single CPU package.
- * 1.10 28 Feb 2002 Asit K Mallick <asit.k.mallick@intel.com> and
- * Tigran Aivazian <tigran@veritas.com>,
- * Serialize updates as required on HT processors due to speculative
- * nature of implementation.
- * 1.11 22 Mar 2002 Tigran Aivazian <tigran@veritas.com>
- * Fix the panic when writing zero-length microcode chunk.
- * 1.12 29 Sep 2003 Nitin Kamble <nitin.a.kamble@intel.com>,
- * Jun Nakajima <jun.nakajima@intel.com>
- * Support for the microcode updates in the new format.
- * 1.13 10 Oct 2003 Tigran Aivazian <tigran@veritas.com>
- * Removed ->read() method and obsoleted MICROCODE_IOCFREE ioctl
- * because we no longer hold a copy of applied microcode
- * in kernel memory.
- * 1.14 25 Jun 2004 Tigran Aivazian <tigran@veritas.com>
- * Fix sigmatch() macro to handle old CPUs with pf == 0.
- * Thanks to Stuart Swales for pointing out this bug.
- */
-
-//#define DEBUG /* pr_debug */
-#include <linux/capability.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/smp_lock.h>
-#include <linux/cpumask.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-#include <linux/miscdevice.h>
-#include <linux/spinlock.h>
-#include <linux/mm.h>
-#include <linux/fs.h>
-#include <linux/mutex.h>
-#include <linux/cpu.h>
-#include <linux/firmware.h>
-#include <linux/platform_device.h>
-
-#include <asm/msr.h>
-#include <asm/uaccess.h>
-#include <asm/processor.h>
-
-MODULE_DESCRIPTION("Intel CPU (IA-32) Microcode Update Driver");
-MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>");
-MODULE_LICENSE("GPL");
-
-#define MICROCODE_VERSION "1.14a"
-
-#define DEFAULT_UCODE_DATASIZE (2000) /* 2000 bytes */
-#define MC_HEADER_SIZE (sizeof (microcode_header_t)) /* 48 bytes */
-#define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE) /* 2048 bytes */
-#define EXT_HEADER_SIZE (sizeof (struct extended_sigtable)) /* 20 bytes */
-#define EXT_SIGNATURE_SIZE (sizeof (struct extended_signature)) /* 12 bytes */
-#define DWSIZE (sizeof (u32))
-#define get_totalsize(mc) \
- (((microcode_t *)mc)->hdr.totalsize ? \
- ((microcode_t *)mc)->hdr.totalsize : DEFAULT_UCODE_TOTALSIZE)
-#define get_datasize(mc) \
- (((microcode_t *)mc)->hdr.datasize ? \
- ((microcode_t *)mc)->hdr.datasize : DEFAULT_UCODE_DATASIZE)
-
-#define sigmatch(s1, s2, p1, p2) \
- (((s1) == (s2)) && (((p1) & (p2)) || (((p1) == 0) && ((p2) == 0))))
-
-#define exttable_size(et) ((et)->count * EXT_SIGNATURE_SIZE + EXT_HEADER_SIZE)
-
-/* serialize access to the physical write to MSR 0x79 */
-static DEFINE_SPINLOCK(microcode_update_lock);
-
-/* no concurrent ->write()s are allowed on /dev/cpu/microcode */
-static DEFINE_MUTEX(microcode_mutex);
-
-static struct ucode_cpu_info {
- int valid;
- unsigned int sig;
- unsigned int pf;
- unsigned int rev;
- microcode_t *mc;
-} ucode_cpu_info[NR_CPUS];
-
-static void collect_cpu_info(int cpu_num)
-{
- struct cpuinfo_x86 *c = &cpu_data(cpu_num);
- struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num;
- unsigned int val[2];
-
- /* We should bind the task to the CPU */
- BUG_ON(raw_smp_processor_id() != cpu_num);
- uci->pf = uci->rev = 0;
- uci->mc = NULL;
- uci->valid = 1;
-
- if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6 ||
- cpu_has(c, X86_FEATURE_IA64)) {
- printk(KERN_ERR "microcode: CPU%d not a capable Intel "
- "processor\n", cpu_num);
- uci->valid = 0;
- return;
- }
-
- uci->sig = cpuid_eax(0x00000001);
-
- if ((c->x86_model >= 5) || (c->x86 > 6)) {
- /* get processor flags from MSR 0x17 */
- rdmsr(MSR_IA32_PLATFORM_ID, val[0], val[1]);
- uci->pf = 1 << ((val[1] >> 18) & 7);
- }
-
- wrmsr(MSR_IA32_UCODE_REV, 0, 0);
- /* see notes above for revision 1.07. Apparent chip bug */
- sync_core();
- /* get the current revision from MSR 0x8B */
- rdmsr(MSR_IA32_UCODE_REV, val[0], uci->rev);
- pr_debug("microcode: collect_cpu_info : sig=0x%x, pf=0x%x, rev=0x%x\n",
- uci->sig, uci->pf, uci->rev);
-}
-
-static inline int microcode_update_match(int cpu_num,
- microcode_header_t *mc_header, int sig, int pf)
-{
- struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num;
-
- if (!sigmatch(sig, uci->sig, pf, uci->pf)
- || mc_header->rev <= uci->rev)
- return 0;
- return 1;
-}
-
-static int microcode_sanity_check(void *mc)
-{
- microcode_header_t *mc_header = mc;
- struct extended_sigtable *ext_header = NULL;
- struct extended_signature *ext_sig;
- unsigned long total_size, data_size, ext_table_size;
- int sum, orig_sum, ext_sigcount = 0, i;
-
- total_size = get_totalsize(mc_header);
- data_size = get_datasize(mc_header);
- if (data_size + MC_HEADER_SIZE > total_size) {
- printk(KERN_ERR "microcode: error! "
- "Bad data size in microcode data file\n");
- return -EINVAL;
- }
-
- if (mc_header->ldrver != 1 || mc_header->hdrver != 1) {
- printk(KERN_ERR "microcode: error! "
- "Unknown microcode update format\n");
- return -EINVAL;
- }
- ext_table_size = total_size - (MC_HEADER_SIZE + data_size);
- if (ext_table_size) {
- if ((ext_table_size < EXT_HEADER_SIZE)
- || ((ext_table_size - EXT_HEADER_SIZE) % EXT_SIGNATURE_SIZE)) {
- printk(KERN_ERR "microcode: error! "
- "Small exttable size in microcode data file\n");
- return -EINVAL;
- }
- ext_header = mc + MC_HEADER_SIZE + data_size;
- if (ext_table_size != exttable_size(ext_header)) {
- printk(KERN_ERR "microcode: error! "
- "Bad exttable size in microcode data file\n");
- return -EFAULT;
- }
- ext_sigcount = ext_header->count;
- }
-
- /* check extended table checksum */
- if (ext_table_size) {
- int ext_table_sum = 0;
- int *ext_tablep = (int *)ext_header;
-
- i = ext_table_size / DWSIZE;
- while (i--)
- ext_table_sum += ext_tablep[i];
- if (ext_table_sum) {
- printk(KERN_WARNING "microcode: aborting, "
- "bad extended signature table checksum\n");
- return -EINVAL;
- }
- }
-
- /* calculate the checksum */
- orig_sum = 0;
- i = (MC_HEADER_SIZE + data_size) / DWSIZE;
- while (i--)
- orig_sum += ((int *)mc)[i];
- if (orig_sum) {
- printk(KERN_ERR "microcode: aborting, bad checksum\n");
- return -EINVAL;
- }
- if (!ext_table_size)
- return 0;
- /* check extended signature checksum */
- for (i = 0; i < ext_sigcount; i++) {
- ext_sig = (void *)ext_header + EXT_HEADER_SIZE +
- EXT_SIGNATURE_SIZE * i;
- sum = orig_sum
- - (mc_header->sig + mc_header->pf + mc_header->cksum)
- + (ext_sig->sig + ext_sig->pf + ext_sig->cksum);
- if (sum) {
- printk(KERN_ERR "microcode: aborting, bad checksum\n");
- return -EINVAL;
- }
- }
- return 0;
-}
-
-/*
- * return 0 - no update found
- * return 1 - found update
- * return < 0 - error
- */
-static int get_maching_microcode(void *mc, int cpu)
-{
- struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
- microcode_header_t *mc_header = mc;
- struct extended_sigtable *ext_header;
- unsigned long total_size = get_totalsize(mc_header);
- int ext_sigcount, i;
- struct extended_signature *ext_sig;
- void *new_mc;
-
- if (microcode_update_match(cpu, mc_header,
- mc_header->sig, mc_header->pf))
- goto find;
-
- if (total_size <= get_datasize(mc_header) + MC_HEADER_SIZE)
- return 0;
-
- ext_header = mc + get_datasize(mc_header) + MC_HEADER_SIZE;
- ext_sigcount = ext_header->count;
- ext_sig = (void *)ext_header + EXT_HEADER_SIZE;
- for (i = 0; i < ext_sigcount; i++) {
- if (microcode_update_match(cpu, mc_header,
- ext_sig->sig, ext_sig->pf))
- goto find;
- ext_sig++;
- }
- return 0;
-find:
- pr_debug("microcode: CPU%d found a matching microcode update with"
- " version 0x%x (current=0x%x)\n", cpu, mc_header->rev,uci->rev);
- new_mc = vmalloc(total_size);
- if (!new_mc) {
- printk(KERN_ERR "microcode: error! Can not allocate memory\n");
- return -ENOMEM;
- }
-
- /* free previous update file */
- vfree(uci->mc);
-
- memcpy(new_mc, mc, total_size);
- uci->mc = new_mc;
- return 1;
-}
-
-static void apply_microcode(int cpu)
-{
- unsigned long flags;
- unsigned int val[2];
- int cpu_num = raw_smp_processor_id();
- struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num;
-
- /* We should bind the task to the CPU */
- BUG_ON(cpu_num != cpu);
-
- if (uci->mc == NULL)
- return;
-
- /* serialize access to the physical write to MSR 0x79 */
- spin_lock_irqsave(µcode_update_lock, flags);
-
- /* write microcode via MSR 0x79 */
- wrmsr(MSR_IA32_UCODE_WRITE,
- (unsigned long) uci->mc->bits,
- (unsigned long) uci->mc->bits >> 16 >> 16);
- wrmsr(MSR_IA32_UCODE_REV, 0, 0);
-
- /* see notes above for revision 1.07. Apparent chip bug */
- sync_core();
-
- /* get the current revision from MSR 0x8B */
- rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]);
-
- spin_unlock_irqrestore(µcode_update_lock, flags);
- if (val[1] != uci->mc->hdr.rev) {
- printk(KERN_ERR "microcode: CPU%d update from revision "
- "0x%x to 0x%x failed\n", cpu_num, uci->rev, val[1]);
- return;
- }
- printk(KERN_INFO "microcode: CPU%d updated from revision "
- "0x%x to 0x%x, date = %08x \n",
- cpu_num, uci->rev, val[1], uci->mc->hdr.date);
- uci->rev = val[1];
-}
-
-#ifdef CONFIG_MICROCODE_OLD_INTERFACE
-static void __user *user_buffer; /* user area microcode data buffer */
-static unsigned int user_buffer_size; /* it's size */
-
-static long get_next_ucode(void **mc, long offset)
-{
- microcode_header_t mc_header;
- unsigned long total_size;
-
- /* No more data */
- if (offset >= user_buffer_size)
- return 0;
- if (copy_from_user(&mc_header, user_buffer + offset, MC_HEADER_SIZE)) {
- printk(KERN_ERR "microcode: error! Can not read user data\n");
- return -EFAULT;
- }
- total_size = get_totalsize(&mc_header);
- if (offset + total_size > user_buffer_size) {
- printk(KERN_ERR "microcode: error! Bad total size in microcode "
- "data file\n");
- return -EINVAL;
- }
- *mc = vmalloc(total_size);
- if (!*mc)
- return -ENOMEM;
- if (copy_from_user(*mc, user_buffer + offset, total_size)) {
- printk(KERN_ERR "microcode: error! Can not read user data\n");
- vfree(*mc);
- return -EFAULT;
- }
- return offset + total_size;
-}
-
-static int do_microcode_update (void)
-{
- long cursor = 0;
- int error = 0;
- void *new_mc = NULL;
- int cpu;
- cpumask_t old;
-
- old = current->cpus_allowed;
-
- while ((cursor = get_next_ucode(&new_mc, cursor)) > 0) {
- error = microcode_sanity_check(new_mc);
- if (error)
- goto out;
- /*
- * It's possible the data file has multiple matching ucode,
- * lets keep searching till the latest version
- */
- for_each_online_cpu(cpu) {
- struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
-
- if (!uci->valid)
- continue;
- set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));
- error = get_maching_microcode(new_mc, cpu);
- if (error < 0)
- goto out;
- if (error == 1)
- apply_microcode(cpu);
- }
- vfree(new_mc);
- }
-out:
- if (cursor > 0)
- vfree(new_mc);
- if (cursor < 0)
- error = cursor;
- set_cpus_allowed_ptr(current, &old);
- return error;
-}
-
-static int microcode_open (struct inode *unused1, struct file *unused2)
-{
- cycle_kernel_lock();
- return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
-}
-
-static ssize_t microcode_write (struct file *file, const char __user *buf, size_t len, loff_t *ppos)
-{
- ssize_t ret;
-
- if ((len >> PAGE_SHIFT) > num_physpages) {
- printk(KERN_ERR "microcode: too much data (max %ld pages)\n", num_physpages);
- return -EINVAL;
- }
-
- get_online_cpus();
- mutex_lock(µcode_mutex);
-
- user_buffer = (void __user *) buf;
- user_buffer_size = (int) len;
-
- ret = do_microcode_update();
- if (!ret)
- ret = (ssize_t)len;
-
- mutex_unlock(µcode_mutex);
- put_online_cpus();
-
- return ret;
-}
-
-static const struct file_operations microcode_fops = {
- .owner = THIS_MODULE,
- .write = microcode_write,
- .open = microcode_open,
-};
-
-static struct miscdevice microcode_dev = {
- .minor = MICROCODE_MINOR,
- .name = "microcode",
- .fops = µcode_fops,
-};
-
-static int __init microcode_dev_init (void)
-{
- int error;
-
- error = misc_register(µcode_dev);
- if (error) {
- printk(KERN_ERR
- "microcode: can't misc_register on minor=%d\n",
- MICROCODE_MINOR);
- return error;
- }
-
- return 0;
-}
-
-static void microcode_dev_exit (void)
-{
- misc_deregister(µcode_dev);
-}
-
-MODULE_ALIAS_MISCDEV(MICROCODE_MINOR);
-#else
-#define microcode_dev_init() 0
-#define microcode_dev_exit() do { } while(0)
-#endif
-
-static long get_next_ucode_from_buffer(void **mc, const u8 *buf,
- unsigned long size, long offset)
-{
- microcode_header_t *mc_header;
- unsigned long total_size;
-
- /* No more data */
- if (offset >= size)
- return 0;
- mc_header = (microcode_header_t *)(buf + offset);
- total_size = get_totalsize(mc_header);
-
- if (offset + total_size > size) {
- printk(KERN_ERR "microcode: error! Bad data in microcode data file\n");
- return -EINVAL;
- }
-
- *mc = vmalloc(total_size);
- if (!*mc) {
- printk(KERN_ERR "microcode: error! Can not allocate memory\n");
- return -ENOMEM;
- }
- memcpy(*mc, buf + offset, total_size);
- return offset + total_size;
-}
-
-/* fake device for request_firmware */
-static struct platform_device *microcode_pdev;
-
-static int cpu_request_microcode(int cpu)
-{
- char name[30];
- struct cpuinfo_x86 *c = &cpu_data(cpu);
- const struct firmware *firmware;
- const u8 *buf;
- unsigned long size;
- long offset = 0;
- int error;
- void *mc;
-
- /* We should bind the task to the CPU */
- BUG_ON(cpu != raw_smp_processor_id());
- sprintf(name,"intel-ucode/%02x-%02x-%02x",
- c->x86, c->x86_model, c->x86_mask);
- error = request_firmware(&firmware, name, µcode_pdev->dev);
- if (error) {
- pr_debug("microcode: data file %s load failed\n", name);
- return error;
- }
- buf = firmware->data;
- size = firmware->size;
- while ((offset = get_next_ucode_from_buffer(&mc, buf, size, offset))
- > 0) {
- error = microcode_sanity_check(mc);
- if (error)
- break;
- error = get_maching_microcode(mc, cpu);
- if (error < 0)
- break;
- /*
- * It's possible the data file has multiple matching ucode,
- * lets keep searching till the latest version
- */
- if (error == 1) {
- apply_microcode(cpu);
- error = 0;
- }
- vfree(mc);
- }
- if (offset > 0)
- vfree(mc);
- if (offset < 0)
- error = offset;
- release_firmware(firmware);
-
- return error;
-}
-
-static int apply_microcode_check_cpu(int cpu)
-{
- struct cpuinfo_x86 *c = &cpu_data(cpu);
- struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
- cpumask_t old;
- unsigned int val[2];
- int err = 0;
-
- /* Check if the microcode is available */
- if (!uci->mc)
- return 0;
-
- old = current->cpus_allowed;
- set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));
-
- /* Check if the microcode we have in memory matches the CPU */
- if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6 ||
- cpu_has(c, X86_FEATURE_IA64) || uci->sig != cpuid_eax(0x00000001))
- err = -EINVAL;
-
- if (!err && ((c->x86_model >= 5) || (c->x86 > 6))) {
- /* get processor flags from MSR 0x17 */
- rdmsr(MSR_IA32_PLATFORM_ID, val[0], val[1]);
- if (uci->pf != (1 << ((val[1] >> 18) & 7)))
- err = -EINVAL;
- }
-
- if (!err) {
- wrmsr(MSR_IA32_UCODE_REV, 0, 0);
- /* see notes above for revision 1.07. Apparent chip bug */
- sync_core();
- /* get the current revision from MSR 0x8B */
- rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]);
- if (uci->rev != val[1])
- err = -EINVAL;
- }
-
- if (!err)
- apply_microcode(cpu);
- else
- printk(KERN_ERR "microcode: Could not apply microcode to CPU%d:"
- " sig=0x%x, pf=0x%x, rev=0x%x\n",
- cpu, uci->sig, uci->pf, uci->rev);
-
- set_cpus_allowed_ptr(current, &old);
- return err;
-}
-
-static void microcode_init_cpu(int cpu, int resume)
-{
- cpumask_t old;
- struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
-
- old = current->cpus_allowed;
-
- set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));
- mutex_lock(µcode_mutex);
- collect_cpu_info(cpu);
- if (uci->valid && system_state == SYSTEM_RUNNING && !resume)
- cpu_request_microcode(cpu);
- mutex_unlock(µcode_mutex);
- set_cpus_allowed_ptr(current, &old);
-}
-
-static void microcode_fini_cpu(int cpu)
-{
- struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
-
- mutex_lock(µcode_mutex);
- uci->valid = 0;
- vfree(uci->mc);
- uci->mc = NULL;
- mutex_unlock(µcode_mutex);
-}
-
-static ssize_t reload_store(struct sys_device *dev,
- struct sysdev_attribute *attr,
- const char *buf, size_t sz)
-{
- struct ucode_cpu_info *uci = ucode_cpu_info + dev->id;
- char *end;
- unsigned long val = simple_strtoul(buf, &end, 0);
- int err = 0;
- int cpu = dev->id;
-
- if (end == buf)
- return -EINVAL;
- if (val == 1) {
- cpumask_t old = current->cpus_allowed;
-
- get_online_cpus();
- set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));
-
- mutex_lock(µcode_mutex);
- if (uci->valid)
- err = cpu_request_microcode(cpu);
- mutex_unlock(µcode_mutex);
- put_online_cpus();
- set_cpus_allowed_ptr(current, &old);
- }
- if (err)
- return err;
- return sz;
-}
-
-static ssize_t version_show(struct sys_device *dev,
- struct sysdev_attribute *attr, char *buf)
-{
- struct ucode_cpu_info *uci = ucode_cpu_info + dev->id;
-
- return sprintf(buf, "0x%x\n", uci->rev);
-}
-
-static ssize_t pf_show(struct sys_device *dev,
- struct sysdev_attribute *attr, char *buf)
-{
- struct ucode_cpu_info *uci = ucode_cpu_info + dev->id;
-
- return sprintf(buf, "0x%x\n", uci->pf);
-}
-
-static SYSDEV_ATTR(reload, 0200, NULL, reload_store);
-static SYSDEV_ATTR(version, 0400, version_show, NULL);
-static SYSDEV_ATTR(processor_flags, 0400, pf_show, NULL);
-
-static struct attribute *mc_default_attrs[] = {
- &attr_reload.attr,
- &attr_version.attr,
- &attr_processor_flags.attr,
- NULL
-};
-
-static struct attribute_group mc_attr_group = {
- .attrs = mc_default_attrs,
- .name = "microcode",
-};
-
-static int __mc_sysdev_add(struct sys_device *sys_dev, int resume)
-{
- int err, cpu = sys_dev->id;
- struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
-
- if (!cpu_online(cpu))
- return 0;
-
- pr_debug("microcode: CPU%d added\n", cpu);
- memset(uci, 0, sizeof(*uci));
-
- err = sysfs_create_group(&sys_dev->kobj, &mc_attr_group);
- if (err)
- return err;
-
- microcode_init_cpu(cpu, resume);
-
- return 0;
-}
-
-static int mc_sysdev_add(struct sys_device *sys_dev)
-{
- return __mc_sysdev_add(sys_dev, 0);
-}
-
-static int mc_sysdev_remove(struct sys_device *sys_dev)
-{
- int cpu = sys_dev->id;
-
- if (!cpu_online(cpu))
- return 0;
-
- pr_debug("microcode: CPU%d removed\n", cpu);
- microcode_fini_cpu(cpu);
- sysfs_remove_group(&sys_dev->kobj, &mc_attr_group);
- return 0;
-}
-
-static int mc_sysdev_resume(struct sys_device *dev)
-{
- int cpu = dev->id;
-
- if (!cpu_online(cpu))
- return 0;
- pr_debug("microcode: CPU%d resumed\n", cpu);
- /* only CPU 0 will apply ucode here */
- apply_microcode(0);
- return 0;
-}
-
-static struct sysdev_driver mc_sysdev_driver = {
- .add = mc_sysdev_add,
- .remove = mc_sysdev_remove,
- .resume = mc_sysdev_resume,
-};
-
-static __cpuinit int
-mc_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu)
-{
- unsigned int cpu = (unsigned long)hcpu;
- struct sys_device *sys_dev;
-
- sys_dev = get_cpu_sysdev(cpu);
- switch (action) {
- case CPU_UP_CANCELED_FROZEN:
- /* The CPU refused to come up during a system resume */
- microcode_fini_cpu(cpu);
- break;
- case CPU_ONLINE:
- case CPU_DOWN_FAILED:
- mc_sysdev_add(sys_dev);
- break;
- case CPU_ONLINE_FROZEN:
- /* System-wide resume is in progress, try to apply microcode */
- if (apply_microcode_check_cpu(cpu)) {
- /* The application of microcode failed */
- microcode_fini_cpu(cpu);
- __mc_sysdev_add(sys_dev, 1);
- break;
- }
- case CPU_DOWN_FAILED_FROZEN:
- if (sysfs_create_group(&sys_dev->kobj, &mc_attr_group))
- printk(KERN_ERR "microcode: Failed to create the sysfs "
- "group for CPU%d\n", cpu);
- break;
- case CPU_DOWN_PREPARE:
- mc_sysdev_remove(sys_dev);
- break;
- case CPU_DOWN_PREPARE_FROZEN:
- /* Suspend is in progress, only remove the interface */
- sysfs_remove_group(&sys_dev->kobj, &mc_attr_group);
- break;
- }
- return NOTIFY_OK;
-}
-
-static struct notifier_block __refdata mc_cpu_notifier = {
- .notifier_call = mc_cpu_callback,
-};
-
-static int __init microcode_init (void)
-{
- int error;
-
- printk(KERN_INFO
- "IA-32 Microcode Update Driver: v" MICROCODE_VERSION " <tigran@aivazian.fsnet.co.uk>\n");
-
- error = microcode_dev_init();
- if (error)
- return error;
- microcode_pdev = platform_device_register_simple("microcode", -1,
- NULL, 0);
- if (IS_ERR(microcode_pdev)) {
- microcode_dev_exit();
- return PTR_ERR(microcode_pdev);
- }
-
- get_online_cpus();
- error = sysdev_driver_register(&cpu_sysdev_class, &mc_sysdev_driver);
- put_online_cpus();
- if (error) {
- microcode_dev_exit();
- platform_device_unregister(microcode_pdev);
- return error;
- }
-
- register_hotcpu_notifier(&mc_cpu_notifier);
- return 0;
-}
-
-static void __exit microcode_exit (void)
-{
- microcode_dev_exit();
-
- unregister_hotcpu_notifier(&mc_cpu_notifier);
-
- get_online_cpus();
- sysdev_driver_unregister(&cpu_sysdev_class, &mc_sysdev_driver);
- put_online_cpus();
-
- platform_device_unregister(microcode_pdev);
-}
-
-module_init(microcode_init)
-module_exit(microcode_exit)
diff --git a/arch/x86/kernel/microcode_amd.c b/arch/x86/kernel/microcode_amd.c
new file mode 100644
index 0000000..7a1f8ee
--- /dev/null
+++ b/arch/x86/kernel/microcode_amd.c
@@ -0,0 +1,435 @@
+/*
+ * AMD CPU Microcode Update Driver for Linux
+ * Copyright (C) 2008 Advanced Micro Devices Inc.
+ *
+ * Author: Peter Oruba <peter.oruba@amd.com>
+ *
+ * Based on work by:
+ * Tigran Aivazian <tigran@aivazian.fsnet.co.uk>
+ *
+ * This driver allows to upgrade microcode on AMD
+ * family 0x10 and 0x11 processors.
+ *
+ * Licensed unter the terms of the GNU General Public
+ * License version 2. See file COPYING for details.
+*/
+
+#include <linux/capability.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/cpumask.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/mutex.h>
+#include <linux/cpu.h>
+#include <linux/firmware.h>
+#include <linux/platform_device.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+
+#include <asm/msr.h>
+#include <asm/uaccess.h>
+#include <asm/processor.h>
+#include <asm/microcode.h>
+
+MODULE_DESCRIPTION("AMD Microcode Update Driver");
+MODULE_AUTHOR("Peter Oruba <peter.oruba@amd.com>");
+MODULE_LICENSE("GPL v2");
+
+#define UCODE_MAGIC 0x00414d44
+#define UCODE_EQUIV_CPU_TABLE_TYPE 0x00000000
+#define UCODE_UCODE_TYPE 0x00000001
+
+struct equiv_cpu_entry {
+ unsigned int installed_cpu;
+ unsigned int fixed_errata_mask;
+ unsigned int fixed_errata_compare;
+ unsigned int equiv_cpu;
+};
+
+struct microcode_header_amd {
+ unsigned int data_code;
+ unsigned int patch_id;
+ unsigned char mc_patch_data_id[2];
+ unsigned char mc_patch_data_len;
+ unsigned char init_flag;
+ unsigned int mc_patch_data_checksum;
+ unsigned int nb_dev_id;
+ unsigned int sb_dev_id;
+ unsigned char processor_rev_id[2];
+ unsigned char nb_rev_id;
+ unsigned char sb_rev_id;
+ unsigned char bios_api_rev;
+ unsigned char reserved1[3];
+ unsigned int match_reg[8];
+};
+
+struct microcode_amd {
+ struct microcode_header_amd hdr;
+ unsigned int mpb[0];
+};
+
+#define UCODE_MAX_SIZE (2048)
+#define DEFAULT_UCODE_DATASIZE (896)
+#define MC_HEADER_SIZE (sizeof(struct microcode_header_amd))
+#define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE)
+#define DWSIZE (sizeof(u32))
+/* For now we support a fixed ucode total size only */
+#define get_totalsize(mc) \
+ ((((struct microcode_amd *)mc)->hdr.mc_patch_data_len * 28) \
+ + MC_HEADER_SIZE)
+
+/* serialize access to the physical write */
+static DEFINE_SPINLOCK(microcode_update_lock);
+
+static struct equiv_cpu_entry *equiv_cpu_table;
+
+static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig)
+{
+ struct cpuinfo_x86 *c = &cpu_data(cpu);
+
+ memset(csig, 0, sizeof(*csig));
+
+ if (c->x86_vendor != X86_VENDOR_AMD || c->x86 < 0x10) {
+ printk(KERN_ERR "microcode: CPU%d not a capable AMD processor\n",
+ cpu);
+ return -1;
+ }
+
+ asm volatile("movl %1, %%ecx; rdmsr"
+ : "=a" (csig->rev)
+ : "i" (0x0000008B) : "ecx");
+
+ printk(KERN_INFO "microcode: collect_cpu_info_amd : patch_id=0x%x\n",
+ csig->rev);
+
+ return 0;
+}
+
+static int get_matching_microcode(int cpu, void *mc, int rev)
+{
+ struct microcode_header_amd *mc_header = mc;
+ struct pci_dev *nb_pci_dev, *sb_pci_dev;
+ unsigned int current_cpu_id;
+ unsigned int equiv_cpu_id = 0x00;
+ unsigned int i = 0;
+
+ BUG_ON(equiv_cpu_table == NULL);
+ current_cpu_id = cpuid_eax(0x00000001);
+
+ while (equiv_cpu_table[i].installed_cpu != 0) {
+ if (current_cpu_id == equiv_cpu_table[i].installed_cpu) {
+ equiv_cpu_id = equiv_cpu_table[i].equiv_cpu;
+ break;
+ }
+ i++;
+ }
+
+ if (!equiv_cpu_id) {
+ printk(KERN_ERR "microcode: CPU%d cpu_id "
+ "not found in equivalent cpu table \n", cpu);
+ return 0;
+ }
+
+ if ((mc_header->processor_rev_id[0]) != (equiv_cpu_id & 0xff)) {
+ printk(KERN_ERR
+ "microcode: CPU%d patch does not match "
+ "(patch is %x, cpu extended is %x) \n",
+ cpu, mc_header->processor_rev_id[0],
+ (equiv_cpu_id & 0xff));
+ return 0;
+ }
+
+ if ((mc_header->processor_rev_id[1]) != ((equiv_cpu_id >> 16) & 0xff)) {
+ printk(KERN_ERR "microcode: CPU%d patch does not match "
+ "(patch is %x, cpu base id is %x) \n",
+ cpu, mc_header->processor_rev_id[1],
+ ((equiv_cpu_id >> 16) & 0xff));
+
+ return 0;
+ }
+
+ /* ucode may be northbridge specific */
+ if (mc_header->nb_dev_id) {
+ nb_pci_dev = pci_get_device(PCI_VENDOR_ID_AMD,
+ (mc_header->nb_dev_id & 0xff),
+ NULL);
+ if ((!nb_pci_dev) ||
+ (mc_header->nb_rev_id != nb_pci_dev->revision)) {
+ printk(KERN_ERR "microcode: CPU%d NB mismatch \n", cpu);
+ pci_dev_put(nb_pci_dev);
+ return 0;
+ }
+ pci_dev_put(nb_pci_dev);
+ }
+
+ /* ucode may be southbridge specific */
+ if (mc_header->sb_dev_id) {
+ sb_pci_dev = pci_get_device(PCI_VENDOR_ID_AMD,
+ (mc_header->sb_dev_id & 0xff),
+ NULL);
+ if ((!sb_pci_dev) ||
+ (mc_header->sb_rev_id != sb_pci_dev->revision)) {
+ printk(KERN_ERR "microcode: CPU%d SB mismatch \n", cpu);
+ pci_dev_put(sb_pci_dev);
+ return 0;
+ }
+ pci_dev_put(sb_pci_dev);
+ }
+
+ if (mc_header->patch_id <= rev)
+ return 0;
+
+ return 1;
+}
+
+static void apply_microcode_amd(int cpu)
+{
+ unsigned long flags;
+ unsigned int eax, edx;
+ unsigned int rev;
+ int cpu_num = raw_smp_processor_id();
+ struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num;
+ struct microcode_amd *mc_amd = uci->mc;
+ unsigned long addr;
+
+ /* We should bind the task to the CPU */
+ BUG_ON(cpu_num != cpu);
+
+ if (mc_amd == NULL)
+ return;
+
+ spin_lock_irqsave(µcode_update_lock, flags);
+
+ addr = (unsigned long)&mc_amd->hdr.data_code;
+ edx = (unsigned int)(((unsigned long)upper_32_bits(addr)));
+ eax = (unsigned int)(((unsigned long)lower_32_bits(addr)));
+
+ asm volatile("movl %0, %%ecx; wrmsr" :
+ : "i" (0xc0010020), "a" (eax), "d" (edx) : "ecx");
+
+ /* get patch id after patching */
+ asm volatile("movl %1, %%ecx; rdmsr"
+ : "=a" (rev)
+ : "i" (0x0000008B) : "ecx");
+
+ spin_unlock_irqrestore(µcode_update_lock, flags);
+
+ /* check current patch id and patch's id for match */
+ if (rev != mc_amd->hdr.patch_id) {
+ printk(KERN_ERR "microcode: CPU%d update from revision "
+ "0x%x to 0x%x failed\n", cpu_num,
+ mc_amd->hdr.patch_id, rev);
+ return;
+ }
+
+ printk(KERN_INFO "microcode: CPU%d updated from revision "
+ "0x%x to 0x%x \n",
+ cpu_num, uci->cpu_sig.rev, mc_amd->hdr.patch_id);
+
+ uci->cpu_sig.rev = rev;
+}
+
+static void * get_next_ucode(u8 *buf, unsigned int size,
+ int (*get_ucode_data)(void *, const void *, size_t),
+ unsigned int *mc_size)
+{
+ unsigned int total_size;
+#define UCODE_CONTAINER_SECTION_HDR 8
+ u8 section_hdr[UCODE_CONTAINER_SECTION_HDR];
+ void *mc;
+
+ if (get_ucode_data(section_hdr, buf, UCODE_CONTAINER_SECTION_HDR))
+ return NULL;
+
+ if (section_hdr[0] != UCODE_UCODE_TYPE) {
+ printk(KERN_ERR "microcode: error! "
+ "Wrong microcode payload type field\n");
+ return NULL;
+ }
+
+ total_size = (unsigned long) (section_hdr[4] + (section_hdr[5] << 8));
+
+ printk(KERN_INFO "microcode: size %u, total_size %u\n",
+ size, total_size);
+
+ if (total_size > size || total_size > UCODE_MAX_SIZE) {
+ printk(KERN_ERR "microcode: error! Bad data in microcode data file\n");
+ return NULL;
+ }
+
+ mc = vmalloc(UCODE_MAX_SIZE);
+ if (mc) {
+ memset(mc, 0, UCODE_MAX_SIZE);
+ if (get_ucode_data(mc, buf + UCODE_CONTAINER_SECTION_HDR, total_size)) {
+ vfree(mc);
+ mc = NULL;
+ } else
+ *mc_size = total_size + UCODE_CONTAINER_SECTION_HDR;
+ }
+#undef UCODE_CONTAINER_SECTION_HDR
+ return mc;
+}
+
+
+static int install_equiv_cpu_table(u8 *buf,
+ int (*get_ucode_data)(void *, const void *, size_t))
+{
+#define UCODE_CONTAINER_HEADER_SIZE 12
+ u8 *container_hdr[UCODE_CONTAINER_HEADER_SIZE];
+ unsigned int *buf_pos = (unsigned int *)container_hdr;
+ unsigned long size;
+
+ if (get_ucode_data(&container_hdr, buf, UCODE_CONTAINER_HEADER_SIZE))
+ return 0;
+
+ size = buf_pos[2];
+
+ if (buf_pos[1] != UCODE_EQUIV_CPU_TABLE_TYPE || !size) {
+ printk(KERN_ERR "microcode: error! "
+ "Wrong microcode equivalnet cpu table\n");
+ return 0;
+ }
+
+ equiv_cpu_table = (struct equiv_cpu_entry *) vmalloc(size);
+ if (!equiv_cpu_table) {
+ printk(KERN_ERR "microcode: error, can't allocate memory for equiv CPU table\n");
+ return 0;
+ }
+
+ buf += UCODE_CONTAINER_HEADER_SIZE;
+ if (get_ucode_data(equiv_cpu_table, buf, size)) {
+ vfree(equiv_cpu_table);
+ return 0;
+ }
+
+ return size + UCODE_CONTAINER_HEADER_SIZE; /* add header length */
+#undef UCODE_CONTAINER_HEADER_SIZE
+}
+
+static void free_equiv_cpu_table(void)
+{
+ if (equiv_cpu_table) {
+ vfree(equiv_cpu_table);
+ equiv_cpu_table = NULL;
+ }
+}
+
+static int generic_load_microcode(int cpu, void *data, size_t size,
+ int (*get_ucode_data)(void *, const void *, size_t))
+{
+ struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
+ u8 *ucode_ptr = data, *new_mc = NULL, *mc;
+ int new_rev = uci->cpu_sig.rev;
+ unsigned int leftover;
+ unsigned long offset;
+
+ offset = install_equiv_cpu_table(ucode_ptr, get_ucode_data);
+ if (!offset) {
+ printk(KERN_ERR "microcode: installing equivalent cpu table failed\n");
+ return -EINVAL;
+ }
+
+ ucode_ptr += offset;
+ leftover = size - offset;
+
+ while (leftover) {
+ unsigned int uninitialized_var(mc_size);
+ struct microcode_header_amd *mc_header;
+
+ mc = get_next_ucode(ucode_ptr, leftover, get_ucode_data, &mc_size);
+ if (!mc)
+ break;
+
+ mc_header = (struct microcode_header_amd *)mc;
+ if (get_matching_microcode(cpu, mc, new_rev)) {
+ if (new_mc)
+ vfree(new_mc);
+ new_rev = mc_header->patch_id;
+ new_mc = mc;
+ } else
+ vfree(mc);
+
+ ucode_ptr += mc_size;
+ leftover -= mc_size;
+ }
+
+ if (new_mc) {
+ if (!leftover) {
+ if (uci->mc)
+ vfree(uci->mc);
+ uci->mc = new_mc;
+ pr_debug("microcode: CPU%d found a matching microcode update with"
+ " version 0x%x (current=0x%x)\n",
+ cpu, new_rev, uci->cpu_sig.rev);
+ } else
+ vfree(new_mc);
+ }
+
+ free_equiv_cpu_table();
+
+ return (int)leftover;
+}
+
+static int get_ucode_fw(void *to, const void *from, size_t n)
+{
+ memcpy(to, from, n);
+ return 0;
+}
+
+static int request_microcode_fw(int cpu, struct device *device)
+{
+ const char *fw_name = "amd-ucode/microcode_amd.bin";
+ const struct firmware *firmware;
+ int ret;
+
+ /* We should bind the task to the CPU */
+ BUG_ON(cpu != raw_smp_processor_id());
+
+ ret = request_firmware(&firmware, fw_name, device);
+ if (ret) {
+ printk(KERN_ERR "microcode: ucode data file %s load failed\n", fw_name);
+ return ret;
+ }
+
+ ret = generic_load_microcode(cpu, (void*)firmware->data, firmware->size,
+ &get_ucode_fw);
+
+ release_firmware(firmware);
+
+ return ret;
+}
+
+static int request_microcode_user(int cpu, const void __user *buf, size_t size)
+{
+ printk(KERN_WARNING "microcode: AMD microcode update via /dev/cpu/microcode"
+ "is not supported\n");
+ return -1;
+}
+
+static void microcode_fini_cpu_amd(int cpu)
+{
+ struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
+
+ vfree(uci->mc);
+ uci->mc = NULL;
+}
+
+static struct microcode_ops microcode_amd_ops = {
+ .request_microcode_user = request_microcode_user,
+ .request_microcode_fw = request_microcode_fw,
+ .collect_cpu_info = collect_cpu_info_amd,
+ .apply_microcode = apply_microcode_amd,
+ .microcode_fini_cpu = microcode_fini_cpu_amd,
+};
+
+struct microcode_ops * __init init_amd_microcode(void)
+{
+ return µcode_amd_ops;
+}
diff --git a/arch/x86/kernel/microcode_core.c b/arch/x86/kernel/microcode_core.c
new file mode 100644
index 0000000..936d8d5
--- /dev/null
+++ b/arch/x86/kernel/microcode_core.c
@@ -0,0 +1,508 @@
+/*
+ * Intel CPU Microcode Update Driver for Linux
+ *
+ * Copyright (C) 2000-2006 Tigran Aivazian <tigran@aivazian.fsnet.co.uk>
+ * 2006 Shaohua Li <shaohua.li@intel.com>
+ *
+ * This driver allows to upgrade microcode on Intel processors
+ * belonging to IA-32 family - PentiumPro, Pentium II,
+ * Pentium III, Xeon, Pentium 4, etc.
+ *
+ * Reference: Section 8.11 of Volume 3a, IA-32 Intel? Architecture
+ * Software Developer's Manual
+ * Order Number 253668 or free download from:
+ *
+ * http://developer.intel.com/design/pentium4/manuals/253668.htm
+ *
+ * For more information, go to http://www.urbanmyth.org/microcode
+ *
+ * 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.
+ *
+ * 1.0 16 Feb 2000, Tigran Aivazian <tigran@sco.com>
+ * Initial release.
+ * 1.01 18 Feb 2000, Tigran Aivazian <tigran@sco.com>
+ * Added read() support + cleanups.
+ * 1.02 21 Feb 2000, Tigran Aivazian <tigran@sco.com>
+ * Added 'device trimming' support. open(O_WRONLY) zeroes
+ * and frees the saved copy of applied microcode.
+ * 1.03 29 Feb 2000, Tigran Aivazian <tigran@sco.com>
+ * Made to use devfs (/dev/cpu/microcode) + cleanups.
+ * 1.04 06 Jun 2000, Simon Trimmer <simon@veritas.com>
+ * Added misc device support (now uses both devfs and misc).
+ * Added MICROCODE_IOCFREE ioctl to clear memory.
+ * 1.05 09 Jun 2000, Simon Trimmer <simon@veritas.com>
+ * Messages for error cases (non Intel & no suitable microcode).
+ * 1.06 03 Aug 2000, Tigran Aivazian <tigran@veritas.com>
+ * Removed ->release(). Removed exclusive open and status bitmap.
+ * Added microcode_rwsem to serialize read()/write()/ioctl().
+ * Removed global kernel lock usage.
+ * 1.07 07 Sep 2000, Tigran Aivazian <tigran@veritas.com>
+ * Write 0 to 0x8B msr and then cpuid before reading revision,
+ * so that it works even if there were no update done by the
+ * BIOS. Otherwise, reading from 0x8B gives junk (which happened
+ * to be 0 on my machine which is why it worked even when I
+ * disabled update by the BIOS)
+ * Thanks to Eric W. Biederman <ebiederman@lnxi.com> for the fix.
+ * 1.08 11 Dec 2000, Richard Schaal <richard.schaal@intel.com> and
+ * Tigran Aivazian <tigran@veritas.com>
+ * Intel Pentium 4 processor support and bugfixes.
+ * 1.09 30 Oct 2001, Tigran Aivazian <tigran@veritas.com>
+ * Bugfix for HT (Hyper-Threading) enabled processors
+ * whereby processor resources are shared by all logical processors
+ * in a single CPU package.
+ * 1.10 28 Feb 2002 Asit K Mallick <asit.k.mallick@intel.com> and
+ * Tigran Aivazian <tigran@veritas.com>,
+ * Serialize updates as required on HT processors due to
+ * speculative nature of implementation.
+ * 1.11 22 Mar 2002 Tigran Aivazian <tigran@veritas.com>
+ * Fix the panic when writing zero-length microcode chunk.
+ * 1.12 29 Sep 2003 Nitin Kamble <nitin.a.kamble@intel.com>,
+ * Jun Nakajima <jun.nakajima@intel.com>
+ * Support for the microcode updates in the new format.
+ * 1.13 10 Oct 2003 Tigran Aivazian <tigran@veritas.com>
+ * Removed ->read() method and obsoleted MICROCODE_IOCFREE ioctl
+ * because we no longer hold a copy of applied microcode
+ * in kernel memory.
+ * 1.14 25 Jun 2004 Tigran Aivazian <tigran@veritas.com>
+ * Fix sigmatch() macro to handle old CPUs with pf == 0.
+ * Thanks to Stuart Swales for pointing out this bug.
+ */
+#include <linux/capability.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/cpumask.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/mutex.h>
+#include <linux/cpu.h>
+#include <linux/firmware.h>
+#include <linux/platform_device.h>
+
+#include <asm/msr.h>
+#include <asm/uaccess.h>
+#include <asm/processor.h>
+#include <asm/microcode.h>
+
+MODULE_DESCRIPTION("Microcode Update Driver");
+MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>");
+MODULE_LICENSE("GPL");
+
+#define MICROCODE_VERSION "2.00"
+
+struct microcode_ops *microcode_ops;
+
+/* no concurrent ->write()s are allowed on /dev/cpu/microcode */
+static DEFINE_MUTEX(microcode_mutex);
+
+struct ucode_cpu_info ucode_cpu_info[NR_CPUS];
+EXPORT_SYMBOL_GPL(ucode_cpu_info);
+
+#ifdef CONFIG_MICROCODE_OLD_INTERFACE
+static int do_microcode_update(const void __user *buf, size_t size)
+{
+ cpumask_t old;
+ int error = 0;
+ int cpu;
+
+ old = current->cpus_allowed;
+
+ for_each_online_cpu(cpu) {
+ struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
+
+ if (!uci->valid)
+ continue;
+
+ set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));
+ error = microcode_ops->request_microcode_user(cpu, buf, size);
+ if (error < 0)
+ goto out;
+ if (!error)
+ microcode_ops->apply_microcode(cpu);
+ }
+out:
+ set_cpus_allowed_ptr(current, &old);
+ return error;
+}
+
+static int microcode_open(struct inode *unused1, struct file *unused2)
+{
+ cycle_kernel_lock();
+ return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
+}
+
+static ssize_t microcode_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ ssize_t ret;
+
+ if ((len >> PAGE_SHIFT) > num_physpages) {
+ printk(KERN_ERR "microcode: too much data (max %ld pages)\n",
+ num_physpages);
+ return -EINVAL;
+ }
+
+ get_online_cpus();
+ mutex_lock(µcode_mutex);
+
+ ret = do_microcode_update(buf, len);
+ if (!ret)
+ ret = (ssize_t)len;
+
+ mutex_unlock(µcode_mutex);
+ put_online_cpus();
+
+ return ret;
+}
+
+static const struct file_operations microcode_fops = {
+ .owner = THIS_MODULE,
+ .write = microcode_write,
+ .open = microcode_open,
+};
+
+static struct miscdevice microcode_dev = {
+ .minor = MICROCODE_MINOR,
+ .name = "microcode",
+ .fops = µcode_fops,
+};
+
+static int __init microcode_dev_init(void)
+{
+ int error;
+
+ error = misc_register(µcode_dev);
+ if (error) {
+ printk(KERN_ERR
+ "microcode: can't misc_register on minor=%d\n",
+ MICROCODE_MINOR);
+ return error;
+ }
+
+ return 0;
+}
+
+static void microcode_dev_exit(void)
+{
+ misc_deregister(µcode_dev);
+}
+
+MODULE_ALIAS_MISCDEV(MICROCODE_MINOR);
+#else
+#define microcode_dev_init() 0
+#define microcode_dev_exit() do { } while (0)
+#endif
+
+/* fake device for request_firmware */
+struct platform_device *microcode_pdev;
+
+static ssize_t reload_store(struct sys_device *dev,
+ struct sysdev_attribute *attr,
+ const char *buf, size_t sz)
+{
+ struct ucode_cpu_info *uci = ucode_cpu_info + dev->id;
+ char *end;
+ unsigned long val = simple_strtoul(buf, &end, 0);
+ int err = 0;
+ int cpu = dev->id;
+
+ if (end == buf)
+ return -EINVAL;
+ if (val == 1) {
+ cpumask_t old = current->cpus_allowed;
+
+ get_online_cpus();
+ if (cpu_online(cpu)) {
+ set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));
+ mutex_lock(µcode_mutex);
+ if (uci->valid) {
+ err = microcode_ops->request_microcode_fw(cpu,
+ µcode_pdev->dev);
+ if (!err)
+ microcode_ops->apply_microcode(cpu);
+ }
+ mutex_unlock(µcode_mutex);
+ set_cpus_allowed_ptr(current, &old);
+ }
+ put_online_cpus();
+ }
+ if (err)
+ return err;
+ return sz;
+}
+
+static ssize_t version_show(struct sys_device *dev,
+ struct sysdev_attribute *attr, char *buf)
+{
+ struct ucode_cpu_info *uci = ucode_cpu_info + dev->id;
+
+ return sprintf(buf, "0x%x\n", uci->cpu_sig.rev);
+}
+
+static ssize_t pf_show(struct sys_device *dev,
+ struct sysdev_attribute *attr, char *buf)
+{
+ struct ucode_cpu_info *uci = ucode_cpu_info + dev->id;
+
+ return sprintf(buf, "0x%x\n", uci->cpu_sig.pf);
+}
+
+static SYSDEV_ATTR(reload, 0200, NULL, reload_store);
+static SYSDEV_ATTR(version, 0400, version_show, NULL);
+static SYSDEV_ATTR(processor_flags, 0400, pf_show, NULL);
+
+static struct attribute *mc_default_attrs[] = {
+ &attr_reload.attr,
+ &attr_version.attr,
+ &attr_processor_flags.attr,
+ NULL
+};
+
+static struct attribute_group mc_attr_group = {
+ .attrs = mc_default_attrs,
+ .name = "microcode",
+};
+
+static void microcode_fini_cpu(int cpu)
+{
+ struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
+
+ mutex_lock(µcode_mutex);
+ microcode_ops->microcode_fini_cpu(cpu);
+ uci->valid = 0;
+ mutex_unlock(µcode_mutex);
+}
+
+static void collect_cpu_info(int cpu)
+{
+ struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
+
+ memset(uci, 0, sizeof(*uci));
+ if (!microcode_ops->collect_cpu_info(cpu, &uci->cpu_sig))
+ uci->valid = 1;
+}
+
+static int microcode_resume_cpu(int cpu)
+{
+ struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
+ struct cpu_signature nsig;
+
+ pr_debug("microcode: CPU%d resumed\n", cpu);
+
+ if (!uci->mc)
+ return 1;
+
+ /*
+ * Let's verify that the 'cached' ucode does belong
+ * to this cpu (a bit of paranoia):
+ */
+ if (microcode_ops->collect_cpu_info(cpu, &nsig)) {
+ microcode_fini_cpu(cpu);
+ return -1;
+ }
+
+ if (memcmp(&nsig, &uci->cpu_sig, sizeof(nsig))) {
+ microcode_fini_cpu(cpu);
+ /* Should we look for a new ucode here? */
+ return 1;
+ }
+
+ return 0;
+}
+
+void microcode_update_cpu(int cpu)
+{
+ struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
+ int err = 0;
+
+ /*
+ * Check if the system resume is in progress (uci->valid != NULL),
+ * otherwise just request a firmware:
+ */
+ if (uci->valid) {
+ err = microcode_resume_cpu(cpu);
+ } else {
+ collect_cpu_info(cpu);
+ if (uci->valid && system_state == SYSTEM_RUNNING)
+ err = microcode_ops->request_microcode_fw(cpu,
+ µcode_pdev->dev);
+ }
+ if (!err)
+ microcode_ops->apply_microcode(cpu);
+}
+
+static void microcode_init_cpu(int cpu)
+{
+ cpumask_t old = current->cpus_allowed;
+
+ set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));
+ /* We should bind the task to the CPU */
+ BUG_ON(raw_smp_processor_id() != cpu);
+
+ mutex_lock(µcode_mutex);
+ microcode_update_cpu(cpu);
+ mutex_unlock(µcode_mutex);
+
+ set_cpus_allowed_ptr(current, &old);
+}
+
+static int mc_sysdev_add(struct sys_device *sys_dev)
+{
+ int err, cpu = sys_dev->id;
+ struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
+
+ if (!cpu_online(cpu))
+ return 0;
+
+ pr_debug("microcode: CPU%d added\n", cpu);
+ memset(uci, 0, sizeof(*uci));
+
+ err = sysfs_create_group(&sys_dev->kobj, &mc_attr_group);
+ if (err)
+ return err;
+
+ microcode_init_cpu(cpu);
+ return 0;
+}
+
+static int mc_sysdev_remove(struct sys_device *sys_dev)
+{
+ int cpu = sys_dev->id;
+
+ if (!cpu_online(cpu))
+ return 0;
+
+ pr_debug("microcode: CPU%d removed\n", cpu);
+ microcode_fini_cpu(cpu);
+ sysfs_remove_group(&sys_dev->kobj, &mc_attr_group);
+ return 0;
+}
+
+static int mc_sysdev_resume(struct sys_device *dev)
+{
+ int cpu = dev->id;
+
+ if (!cpu_online(cpu))
+ return 0;
+
+ /* only CPU 0 will apply ucode here */
+ microcode_update_cpu(0);
+ return 0;
+}
+
+static struct sysdev_driver mc_sysdev_driver = {
+ .add = mc_sysdev_add,
+ .remove = mc_sysdev_remove,
+ .resume = mc_sysdev_resume,
+};
+
+static __cpuinit int
+mc_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu)
+{
+ unsigned int cpu = (unsigned long)hcpu;
+ struct sys_device *sys_dev;
+
+ sys_dev = get_cpu_sysdev(cpu);
+ switch (action) {
+ case CPU_ONLINE:
+ case CPU_ONLINE_FROZEN:
+ microcode_init_cpu(cpu);
+ case CPU_DOWN_FAILED:
+ case CPU_DOWN_FAILED_FROZEN:
+ pr_debug("microcode: CPU%d added\n", cpu);
+ if (sysfs_create_group(&sys_dev->kobj, &mc_attr_group))
+ printk(KERN_ERR "microcode: Failed to create the sysfs "
+ "group for CPU%d\n", cpu);
+ break;
+ case CPU_DOWN_PREPARE:
+ case CPU_DOWN_PREPARE_FROZEN:
+ /* Suspend is in progress, only remove the interface */
+ sysfs_remove_group(&sys_dev->kobj, &mc_attr_group);
+ pr_debug("microcode: CPU%d removed\n", cpu);
+ break;
+ case CPU_DEAD:
+ case CPU_UP_CANCELED_FROZEN:
+ /* The CPU refused to come up during a system resume */
+ microcode_fini_cpu(cpu);
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block __refdata mc_cpu_notifier = {
+ .notifier_call = mc_cpu_callback,
+};
+
+static int __init microcode_init(void)
+{
+ struct cpuinfo_x86 *c = &cpu_data(0);
+ int error;
+
+ if (c->x86_vendor == X86_VENDOR_INTEL)
+ microcode_ops = init_intel_microcode();
+ else if (c->x86_vendor == X86_VENDOR_AMD)
+ microcode_ops = init_amd_microcode();
+
+ if (!microcode_ops) {
+ printk(KERN_ERR "microcode: no support for this CPU vendor\n");
+ return -ENODEV;
+ }
+
+ error = microcode_dev_init();
+ if (error)
+ return error;
+ microcode_pdev = platform_device_register_simple("microcode", -1,
+ NULL, 0);
+ if (IS_ERR(microcode_pdev)) {
+ microcode_dev_exit();
+ return PTR_ERR(microcode_pdev);
+ }
+
+ get_online_cpus();
+ error = sysdev_driver_register(&cpu_sysdev_class, &mc_sysdev_driver);
+ put_online_cpus();
+ if (error) {
+ microcode_dev_exit();
+ platform_device_unregister(microcode_pdev);
+ return error;
+ }
+
+ register_hotcpu_notifier(&mc_cpu_notifier);
+
+ printk(KERN_INFO
+ "Microcode Update Driver: v" MICROCODE_VERSION
+ " <tigran@aivazian.fsnet.co.uk>"
+ " <peter.oruba@amd.com>\n");
+
+ return 0;
+}
+
+static void __exit microcode_exit(void)
+{
+ microcode_dev_exit();
+
+ unregister_hotcpu_notifier(&mc_cpu_notifier);
+
+ get_online_cpus();
+ sysdev_driver_unregister(&cpu_sysdev_class, &mc_sysdev_driver);
+ put_online_cpus();
+
+ platform_device_unregister(microcode_pdev);
+
+ microcode_ops = NULL;
+
+ printk(KERN_INFO
+ "Microcode Update Driver: v" MICROCODE_VERSION " removed.\n");
+}
+
+module_init(microcode_init);
+module_exit(microcode_exit);
diff --git a/arch/x86/kernel/microcode_intel.c b/arch/x86/kernel/microcode_intel.c
new file mode 100644
index 0000000..622dc4a
--- /dev/null
+++ b/arch/x86/kernel/microcode_intel.c
@@ -0,0 +1,480 @@
+/*
+ * Intel CPU Microcode Update Driver for Linux
+ *
+ * Copyright (C) 2000-2006 Tigran Aivazian <tigran@aivazian.fsnet.co.uk>
+ * 2006 Shaohua Li <shaohua.li@intel.com>
+ *
+ * This driver allows to upgrade microcode on Intel processors
+ * belonging to IA-32 family - PentiumPro, Pentium II,
+ * Pentium III, Xeon, Pentium 4, etc.
+ *
+ * Reference: Section 8.11 of Volume 3a, IA-32 Intel? Architecture
+ * Software Developer's Manual
+ * Order Number 253668 or free download from:
+ *
+ * http://developer.intel.com/design/pentium4/manuals/253668.htm
+ *
+ * For more information, go to http://www.urbanmyth.org/microcode
+ *
+ * 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.
+ *
+ * 1.0 16 Feb 2000, Tigran Aivazian <tigran@sco.com>
+ * Initial release.
+ * 1.01 18 Feb 2000, Tigran Aivazian <tigran@sco.com>
+ * Added read() support + cleanups.
+ * 1.02 21 Feb 2000, Tigran Aivazian <tigran@sco.com>
+ * Added 'device trimming' support. open(O_WRONLY) zeroes
+ * and frees the saved copy of applied microcode.
+ * 1.03 29 Feb 2000, Tigran Aivazian <tigran@sco.com>
+ * Made to use devfs (/dev/cpu/microcode) + cleanups.
+ * 1.04 06 Jun 2000, Simon Trimmer <simon@veritas.com>
+ * Added misc device support (now uses both devfs and misc).
+ * Added MICROCODE_IOCFREE ioctl to clear memory.
+ * 1.05 09 Jun 2000, Simon Trimmer <simon@veritas.com>
+ * Messages for error cases (non Intel & no suitable microcode).
+ * 1.06 03 Aug 2000, Tigran Aivazian <tigran@veritas.com>
+ * Removed ->release(). Removed exclusive open and status bitmap.
+ * Added microcode_rwsem to serialize read()/write()/ioctl().
+ * Removed global kernel lock usage.
+ * 1.07 07 Sep 2000, Tigran Aivazian <tigran@veritas.com>
+ * Write 0 to 0x8B msr and then cpuid before reading revision,
+ * so that it works even if there were no update done by the
+ * BIOS. Otherwise, reading from 0x8B gives junk (which happened
+ * to be 0 on my machine which is why it worked even when I
+ * disabled update by the BIOS)
+ * Thanks to Eric W. Biederman <ebiederman@lnxi.com> for the fix.
+ * 1.08 11 Dec 2000, Richard Schaal <richard.schaal@intel.com> and
+ * Tigran Aivazian <tigran@veritas.com>
+ * Intel Pentium 4 processor support and bugfixes.
+ * 1.09 30 Oct 2001, Tigran Aivazian <tigran@veritas.com>
+ * Bugfix for HT (Hyper-Threading) enabled processors
+ * whereby processor resources are shared by all logical processors
+ * in a single CPU package.
+ * 1.10 28 Feb 2002 Asit K Mallick <asit.k.mallick@intel.com> and
+ * Tigran Aivazian <tigran@veritas.com>,
+ * Serialize updates as required on HT processors due to
+ * speculative nature of implementation.
+ * 1.11 22 Mar 2002 Tigran Aivazian <tigran@veritas.com>
+ * Fix the panic when writing zero-length microcode chunk.
+ * 1.12 29 Sep 2003 Nitin Kamble <nitin.a.kamble@intel.com>,
+ * Jun Nakajima <jun.nakajima@intel.com>
+ * Support for the microcode updates in the new format.
+ * 1.13 10 Oct 2003 Tigran Aivazian <tigran@veritas.com>
+ * Removed ->read() method and obsoleted MICROCODE_IOCFREE ioctl
+ * because we no longer hold a copy of applied microcode
+ * in kernel memory.
+ * 1.14 25 Jun 2004 Tigran Aivazian <tigran@veritas.com>
+ * Fix sigmatch() macro to handle old CPUs with pf == 0.
+ * Thanks to Stuart Swales for pointing out this bug.
+ */
+#include <linux/capability.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/cpumask.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/mutex.h>
+#include <linux/cpu.h>
+#include <linux/firmware.h>
+#include <linux/platform_device.h>
+
+#include <asm/msr.h>
+#include <asm/uaccess.h>
+#include <asm/processor.h>
+#include <asm/microcode.h>
+
+MODULE_DESCRIPTION("Microcode Update Driver");
+MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>");
+MODULE_LICENSE("GPL");
+
+struct microcode_header_intel {
+ unsigned int hdrver;
+ unsigned int rev;
+ unsigned int date;
+ unsigned int sig;
+ unsigned int cksum;
+ unsigned int ldrver;
+ unsigned int pf;
+ unsigned int datasize;
+ unsigned int totalsize;
+ unsigned int reserved[3];
+};
+
+struct microcode_intel {
+ struct microcode_header_intel hdr;
+ unsigned int bits[0];
+};
+
+/* microcode format is extended from prescott processors */
+struct extended_signature {
+ unsigned int sig;
+ unsigned int pf;
+ unsigned int cksum;
+};
+
+struct extended_sigtable {
+ unsigned int count;
+ unsigned int cksum;
+ unsigned int reserved[3];
+ struct extended_signature sigs[0];
+};
+
+#define DEFAULT_UCODE_DATASIZE (2000)
+#define MC_HEADER_SIZE (sizeof(struct microcode_header_intel))
+#define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE)
+#define EXT_HEADER_SIZE (sizeof(struct extended_sigtable))
+#define EXT_SIGNATURE_SIZE (sizeof(struct extended_signature))
+#define DWSIZE (sizeof(u32))
+#define get_totalsize(mc) \
+ (((struct microcode_intel *)mc)->hdr.totalsize ? \
+ ((struct microcode_intel *)mc)->hdr.totalsize : \
+ DEFAULT_UCODE_TOTALSIZE)
+
+#define get_datasize(mc) \
+ (((struct microcode_intel *)mc)->hdr.datasize ? \
+ ((struct microcode_intel *)mc)->hdr.datasize : DEFAULT_UCODE_DATASIZE)
+
+#define sigmatch(s1, s2, p1, p2) \
+ (((s1) == (s2)) && (((p1) & (p2)) || (((p1) == 0) && ((p2) == 0))))
+
+#define exttable_size(et) ((et)->count * EXT_SIGNATURE_SIZE + EXT_HEADER_SIZE)
+
+/* serialize access to the physical write to MSR 0x79 */
+static DEFINE_SPINLOCK(microcode_update_lock);
+
+static int collect_cpu_info(int cpu_num, struct cpu_signature *csig)
+{
+ struct cpuinfo_x86 *c = &cpu_data(cpu_num);
+ unsigned int val[2];
+
+ memset(csig, 0, sizeof(*csig));
+
+ if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6 ||
+ cpu_has(c, X86_FEATURE_IA64)) {
+ printk(KERN_ERR "microcode: CPU%d not a capable Intel "
+ "processor\n", cpu_num);
+ return -1;
+ }
+
+ csig->sig = cpuid_eax(0x00000001);
+
+ if ((c->x86_model >= 5) || (c->x86 > 6)) {
+ /* get processor flags from MSR 0x17 */
+ rdmsr(MSR_IA32_PLATFORM_ID, val[0], val[1]);
+ csig->pf = 1 << ((val[1] >> 18) & 7);
+ }
+
+ wrmsr(MSR_IA32_UCODE_REV, 0, 0);
+ /* see notes above for revision 1.07. Apparent chip bug */
+ sync_core();
+ /* get the current revision from MSR 0x8B */
+ rdmsr(MSR_IA32_UCODE_REV, val[0], csig->rev);
+ pr_debug("microcode: collect_cpu_info : sig=0x%x, pf=0x%x, rev=0x%x\n",
+ csig->sig, csig->pf, csig->rev);
+
+ return 0;
+}
+
+static inline int update_match_cpu(struct cpu_signature *csig, int sig, int pf)
+{
+ return (!sigmatch(sig, csig->sig, pf, csig->pf)) ? 0 : 1;
+}
+
+static inline int
+update_match_revision(struct microcode_header_intel *mc_header, int rev)
+{
+ return (mc_header->rev <= rev) ? 0 : 1;
+}
+
+static int microcode_sanity_check(void *mc)
+{
+ struct microcode_header_intel *mc_header = mc;
+ struct extended_sigtable *ext_header = NULL;
+ struct extended_signature *ext_sig;
+ unsigned long total_size, data_size, ext_table_size;
+ int sum, orig_sum, ext_sigcount = 0, i;
+
+ total_size = get_totalsize(mc_header);
+ data_size = get_datasize(mc_header);
+ if (data_size + MC_HEADER_SIZE > total_size) {
+ printk(KERN_ERR "microcode: error! "
+ "Bad data size in microcode data file\n");
+ return -EINVAL;
+ }
+
+ if (mc_header->ldrver != 1 || mc_header->hdrver != 1) {
+ printk(KERN_ERR "microcode: error! "
+ "Unknown microcode update format\n");
+ return -EINVAL;
+ }
+ ext_table_size = total_size - (MC_HEADER_SIZE + data_size);
+ if (ext_table_size) {
+ if ((ext_table_size < EXT_HEADER_SIZE)
+ || ((ext_table_size - EXT_HEADER_SIZE) % EXT_SIGNATURE_SIZE)) {
+ printk(KERN_ERR "microcode: error! "
+ "Small exttable size in microcode data file\n");
+ return -EINVAL;
+ }
+ ext_header = mc + MC_HEADER_SIZE + data_size;
+ if (ext_table_size != exttable_size(ext_header)) {
+ printk(KERN_ERR "microcode: error! "
+ "Bad exttable size in microcode data file\n");
+ return -EFAULT;
+ }
+ ext_sigcount = ext_header->count;
+ }
+
+ /* check extended table checksum */
+ if (ext_table_size) {
+ int ext_table_sum = 0;
+ int *ext_tablep = (int *)ext_header;
+
+ i = ext_table_size / DWSIZE;
+ while (i--)
+ ext_table_sum += ext_tablep[i];
+ if (ext_table_sum) {
+ printk(KERN_WARNING "microcode: aborting, "
+ "bad extended signature table checksum\n");
+ return -EINVAL;
+ }
+ }
+
+ /* calculate the checksum */
+ orig_sum = 0;
+ i = (MC_HEADER_SIZE + data_size) / DWSIZE;
+ while (i--)
+ orig_sum += ((int *)mc)[i];
+ if (orig_sum) {
+ printk(KERN_ERR "microcode: aborting, bad checksum\n");
+ return -EINVAL;
+ }
+ if (!ext_table_size)
+ return 0;
+ /* check extended signature checksum */
+ for (i = 0; i < ext_sigcount; i++) {
+ ext_sig = (void *)ext_header + EXT_HEADER_SIZE +
+ EXT_SIGNATURE_SIZE * i;
+ sum = orig_sum
+ - (mc_header->sig + mc_header->pf + mc_header->cksum)
+ + (ext_sig->sig + ext_sig->pf + ext_sig->cksum);
+ if (sum) {
+ printk(KERN_ERR "microcode: aborting, bad checksum\n");
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+/*
+ * return 0 - no update found
+ * return 1 - found update
+ */
+static int
+get_matching_microcode(struct cpu_signature *cpu_sig, void *mc, int rev)
+{
+ struct microcode_header_intel *mc_header = mc;
+ struct extended_sigtable *ext_header;
+ unsigned long total_size = get_totalsize(mc_header);
+ int ext_sigcount, i;
+ struct extended_signature *ext_sig;
+
+ if (!update_match_revision(mc_header, rev))
+ return 0;
+
+ if (update_match_cpu(cpu_sig, mc_header->sig, mc_header->pf))
+ return 1;
+
+ /* Look for ext. headers: */
+ if (total_size <= get_datasize(mc_header) + MC_HEADER_SIZE)
+ return 0;
+
+ ext_header = mc + get_datasize(mc_header) + MC_HEADER_SIZE;
+ ext_sigcount = ext_header->count;
+ ext_sig = (void *)ext_header + EXT_HEADER_SIZE;
+
+ for (i = 0; i < ext_sigcount; i++) {
+ if (update_match_cpu(cpu_sig, ext_sig->sig, ext_sig->pf))
+ return 1;
+ ext_sig++;
+ }
+ return 0;
+}
+
+static void apply_microcode(int cpu)
+{
+ unsigned long flags;
+ unsigned int val[2];
+ int cpu_num = raw_smp_processor_id();
+ struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
+ struct microcode_intel *mc_intel = uci->mc;
+
+ /* We should bind the task to the CPU */
+ BUG_ON(cpu_num != cpu);
+
+ if (mc_intel == NULL)
+ return;
+
+ /* serialize access to the physical write to MSR 0x79 */
+ spin_lock_irqsave(µcode_update_lock, flags);
+
+ /* write microcode via MSR 0x79 */
+ wrmsr(MSR_IA32_UCODE_WRITE,
+ (unsigned long) mc_intel->bits,
+ (unsigned long) mc_intel->bits >> 16 >> 16);
+ wrmsr(MSR_IA32_UCODE_REV, 0, 0);
+
+ /* see notes above for revision 1.07. Apparent chip bug */
+ sync_core();
+
+ /* get the current revision from MSR 0x8B */
+ rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]);
+
+ spin_unlock_irqrestore(µcode_update_lock, flags);
+ if (val[1] != mc_intel->hdr.rev) {
+ printk(KERN_ERR "microcode: CPU%d update from revision "
+ "0x%x to 0x%x failed\n", cpu_num, uci->cpu_sig.rev, val[1]);
+ return;
+ }
+ printk(KERN_INFO "microcode: CPU%d updated from revision "
+ "0x%x to 0x%x, date = %04x-%02x-%02x \n",
+ cpu_num, uci->cpu_sig.rev, val[1],
+ mc_intel->hdr.date & 0xffff,
+ mc_intel->hdr.date >> 24,
+ (mc_intel->hdr.date >> 16) & 0xff);
+ uci->cpu_sig.rev = val[1];
+}
+
+static int generic_load_microcode(int cpu, void *data, size_t size,
+ int (*get_ucode_data)(void *, const void *, size_t))
+{
+ struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
+ u8 *ucode_ptr = data, *new_mc = NULL, *mc;
+ int new_rev = uci->cpu_sig.rev;
+ unsigned int leftover = size;
+
+ while (leftover) {
+ struct microcode_header_intel mc_header;
+ unsigned int mc_size;
+
+ if (get_ucode_data(&mc_header, ucode_ptr, sizeof(mc_header)))
+ break;
+
+ mc_size = get_totalsize(&mc_header);
+ if (!mc_size || mc_size > leftover) {
+ printk(KERN_ERR "microcode: error!"
+ "Bad data in microcode data file\n");
+ break;
+ }
+
+ mc = vmalloc(mc_size);
+ if (!mc)
+ break;
+
+ if (get_ucode_data(mc, ucode_ptr, mc_size) ||
+ microcode_sanity_check(mc) < 0) {
+ vfree(mc);
+ break;
+ }
+
+ if (get_matching_microcode(&uci->cpu_sig, mc, new_rev)) {
+ if (new_mc)
+ vfree(new_mc);
+ new_rev = mc_header.rev;
+ new_mc = mc;
+ } else
+ vfree(mc);
+
+ ucode_ptr += mc_size;
+ leftover -= mc_size;
+ }
+
+ if (new_mc) {
+ if (!leftover) {
+ if (uci->mc)
+ vfree(uci->mc);
+ uci->mc = (struct microcode_intel *)new_mc;
+ pr_debug("microcode: CPU%d found a matching microcode update with"
+ " version 0x%x (current=0x%x)\n",
+ cpu, new_rev, uci->cpu_sig.rev);
+ } else
+ vfree(new_mc);
+ }
+
+ return (int)leftover;
+}
+
+static int get_ucode_fw(void *to, const void *from, size_t n)
+{
+ memcpy(to, from, n);
+ return 0;
+}
+
+static int request_microcode_fw(int cpu, struct device *device)
+{
+ char name[30];
+ struct cpuinfo_x86 *c = &cpu_data(cpu);
+ const struct firmware *firmware;
+ int ret;
+
+ /* We should bind the task to the CPU */
+ BUG_ON(cpu != raw_smp_processor_id());
+ sprintf(name, "intel-ucode/%02x-%02x-%02x",
+ c->x86, c->x86_model, c->x86_mask);
+ ret = request_firmware(&firmware, name, device);
+ if (ret) {
+ pr_debug("microcode: data file %s load failed\n", name);
+ return ret;
+ }
+
+ ret = generic_load_microcode(cpu, (void*)firmware->data, firmware->size,
+ &get_ucode_fw);
+
+ release_firmware(firmware);
+
+ return ret;
+}
+
+static int get_ucode_user(void *to, const void *from, size_t n)
+{
+ return copy_from_user(to, from, n);
+}
+
+static int request_microcode_user(int cpu, const void __user *buf, size_t size)
+{
+ /* We should bind the task to the CPU */
+ BUG_ON(cpu != raw_smp_processor_id());
+
+ return generic_load_microcode(cpu, (void*)buf, size, &get_ucode_user);
+}
+
+static void microcode_fini_cpu(int cpu)
+{
+ struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
+
+ vfree(uci->mc);
+ uci->mc = NULL;
+}
+
+struct microcode_ops microcode_intel_ops = {
+ .request_microcode_user = request_microcode_user,
+ .request_microcode_fw = request_microcode_fw,
+ .collect_cpu_info = collect_cpu_info,
+ .apply_microcode = apply_microcode,
+ .microcode_fini_cpu = microcode_fini_cpu,
+};
+
+struct microcode_ops * __init init_intel_microcode(void)
+{
+ return µcode_intel_ops;
+}
+
diff --git a/arch/x86/kernel/mpparse.c b/arch/x86/kernel/mpparse.c
index b3fb430..f98f4e1 100644
--- a/arch/x86/kernel/mpparse.c
+++ b/arch/x86/kernel/mpparse.c
@@ -397,7 +397,9 @@
generic_bigsmp_probe();
#endif
+#ifdef CONFIG_X86_32
setup_apic_routing();
+#endif
if (!num_processors)
printk(KERN_ERR "MPTABLE: no processors registered!\n");
return num_processors;
diff --git a/arch/x86/kernel/numaq_32.c b/arch/x86/kernel/numaq_32.c
index eecc8c1..4caff39 100644
--- a/arch/x86/kernel/numaq_32.c
+++ b/arch/x86/kernel/numaq_32.c
@@ -229,6 +229,12 @@
}
}
+static int __init numaq_setup_ioapic_ids(void)
+{
+ /* so can skip it */
+ return 1;
+}
+
static struct x86_quirks numaq_x86_quirks __initdata = {
.arch_pre_time_init = numaq_pre_time_init,
.arch_time_init = NULL,
@@ -243,6 +249,7 @@
.mpc_oem_bus_info = mpc_oem_bus_info,
.mpc_oem_pci_bus = mpc_oem_pci_bus,
.smp_read_mpc_oem = smp_read_mpc_oem,
+ .setup_ioapic_ids = numaq_setup_ioapic_ids,
};
void numaq_mps_oem_check(struct mp_config_table *mpc, char *oem,
diff --git a/arch/x86/kernel/paravirt-spinlocks.c b/arch/x86/kernel/paravirt-spinlocks.c
new file mode 100644
index 0000000..0e9f198
--- /dev/null
+++ b/arch/x86/kernel/paravirt-spinlocks.c
@@ -0,0 +1,37 @@
+/*
+ * Split spinlock implementation out into its own file, so it can be
+ * compiled in a FTRACE-compatible way.
+ */
+#include <linux/spinlock.h>
+#include <linux/module.h>
+
+#include <asm/paravirt.h>
+
+static void default_spin_lock_flags(struct raw_spinlock *lock, unsigned long flags)
+{
+ __raw_spin_lock(lock);
+}
+
+struct pv_lock_ops pv_lock_ops = {
+#ifdef CONFIG_SMP
+ .spin_is_locked = __ticket_spin_is_locked,
+ .spin_is_contended = __ticket_spin_is_contended,
+
+ .spin_lock = __ticket_spin_lock,
+ .spin_lock_flags = default_spin_lock_flags,
+ .spin_trylock = __ticket_spin_trylock,
+ .spin_unlock = __ticket_spin_unlock,
+#endif
+};
+EXPORT_SYMBOL(pv_lock_ops);
+
+void __init paravirt_use_bytelocks(void)
+{
+#ifdef CONFIG_SMP
+ pv_lock_ops.spin_is_locked = __byte_spin_is_locked;
+ pv_lock_ops.spin_is_contended = __byte_spin_is_contended;
+ pv_lock_ops.spin_lock = __byte_spin_lock;
+ pv_lock_ops.spin_trylock = __byte_spin_trylock;
+ pv_lock_ops.spin_unlock = __byte_spin_unlock;
+#endif
+}
diff --git a/arch/x86/kernel/paravirt.c b/arch/x86/kernel/paravirt.c
index e2f4376..e4c8fb6 100644
--- a/arch/x86/kernel/paravirt.c
+++ b/arch/x86/kernel/paravirt.c
@@ -268,17 +268,6 @@
return __get_cpu_var(paravirt_lazy_mode);
}
-void __init paravirt_use_bytelocks(void)
-{
-#ifdef CONFIG_SMP
- pv_lock_ops.spin_is_locked = __byte_spin_is_locked;
- pv_lock_ops.spin_is_contended = __byte_spin_is_contended;
- pv_lock_ops.spin_lock = __byte_spin_lock;
- pv_lock_ops.spin_trylock = __byte_spin_trylock;
- pv_lock_ops.spin_unlock = __byte_spin_unlock;
-#endif
-}
-
struct pv_info pv_info = {
.name = "bare hardware",
.paravirt_enabled = 0,
@@ -349,6 +338,10 @@
.write_ldt_entry = native_write_ldt_entry,
.write_gdt_entry = native_write_gdt_entry,
.write_idt_entry = native_write_idt_entry,
+
+ .alloc_ldt = paravirt_nop,
+ .free_ldt = paravirt_nop,
+
.load_sp0 = native_load_sp0,
#if defined(CONFIG_X86_32) || defined(CONFIG_IA32_EMULATION)
@@ -374,8 +367,6 @@
struct pv_apic_ops pv_apic_ops = {
#ifdef CONFIG_X86_LOCAL_APIC
- .apic_write = native_apic_write,
- .apic_read = native_apic_read,
.setup_boot_clock = setup_boot_APIC_clock,
.setup_secondary_clock = setup_secondary_APIC_clock,
.startup_ipi_hook = paravirt_nop,
@@ -462,18 +453,6 @@
.set_fixmap = native_set_fixmap,
};
-struct pv_lock_ops pv_lock_ops = {
-#ifdef CONFIG_SMP
- .spin_is_locked = __ticket_spin_is_locked,
- .spin_is_contended = __ticket_spin_is_contended,
-
- .spin_lock = __ticket_spin_lock,
- .spin_trylock = __ticket_spin_trylock,
- .spin_unlock = __ticket_spin_unlock,
-#endif
-};
-EXPORT_SYMBOL(pv_lock_ops);
-
EXPORT_SYMBOL_GPL(pv_time_ops);
EXPORT_SYMBOL (pv_cpu_ops);
EXPORT_SYMBOL (pv_mmu_ops);
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
index ec7a2ba..c622772 100644
--- a/arch/x86/kernel/process.c
+++ b/arch/x86/kernel/process.c
@@ -15,7 +15,6 @@
EXPORT_SYMBOL(idle_nomwait);
struct kmem_cache *task_xstate_cachep;
-static int force_mwait __cpuinitdata;
int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
{
diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c
index 205188d..0a1302f 100644
--- a/arch/x86/kernel/process_32.c
+++ b/arch/x86/kernel/process_32.c
@@ -76,47 +76,12 @@
return ((unsigned long *)tsk->thread.sp)[3];
}
-#ifdef CONFIG_HOTPLUG_CPU
-#include <asm/nmi.h>
-
-static void cpu_exit_clear(void)
-{
- int cpu = raw_smp_processor_id();
-
- idle_task_exit();
-
- cpu_uninit();
- irq_ctx_exit(cpu);
-
- cpu_clear(cpu, cpu_callout_map);
- cpu_clear(cpu, cpu_callin_map);
-
- numa_remove_cpu(cpu);
- c1e_remove_cpu(cpu);
-}
-
-/* We don't actually take CPU down, just spin without interrupts. */
-static inline void play_dead(void)
-{
- /* This must be done before dead CPU ack */
- cpu_exit_clear();
- mb();
- /* Ack it */
- __get_cpu_var(cpu_state) = CPU_DEAD;
-
- /*
- * With physical CPU hotplug, we should halt the cpu
- */
- local_irq_disable();
- /* mask all interrupts, flush any and all caches, and halt */
- wbinvd_halt();
-}
-#else
+#ifndef CONFIG_SMP
static inline void play_dead(void)
{
BUG();
}
-#endif /* CONFIG_HOTPLUG_CPU */
+#endif
/*
* The idle thread. There's no useful work to be
@@ -158,7 +123,7 @@
}
}
-void __show_registers(struct pt_regs *regs, int all)
+void __show_regs(struct pt_regs *regs, int all)
{
unsigned long cr0 = 0L, cr2 = 0L, cr3 = 0L, cr4 = 0L;
unsigned long d0, d1, d2, d3, d6, d7;
@@ -224,7 +189,7 @@
void show_regs(struct pt_regs *regs)
{
- __show_registers(regs, 1);
+ __show_regs(regs, 1);
show_trace(NULL, regs, ®s->sp, regs->bp);
}
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
index 2a8ccb9..cd8c0ed 100644
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -86,30 +86,12 @@
__exit_idle();
}
-#ifdef CONFIG_HOTPLUG_CPU
-DECLARE_PER_CPU(int, cpu_state);
-
-#include <linux/nmi.h>
-/* We halt the CPU with physical CPU hotplug */
-static inline void play_dead(void)
-{
- idle_task_exit();
- c1e_remove_cpu(raw_smp_processor_id());
-
- mb();
- /* Ack it */
- __get_cpu_var(cpu_state) = CPU_DEAD;
-
- local_irq_disable();
- /* mask all interrupts, flush any and all caches, and halt */
- wbinvd_halt();
-}
-#else
+#ifndef CONFIG_SMP
static inline void play_dead(void)
{
BUG();
}
-#endif /* CONFIG_HOTPLUG_CPU */
+#endif
/*
* The idle thread. There's no useful work to be
@@ -154,7 +136,7 @@
}
/* Prints also some state that isn't saved in the pt_regs */
-void __show_regs(struct pt_regs *regs)
+void __show_regs(struct pt_regs *regs, int all)
{
unsigned long cr0 = 0L, cr2 = 0L, cr3 = 0L, cr4 = 0L, fs, gs, shadowgs;
unsigned long d0, d1, d2, d3, d6, d7;
@@ -193,6 +175,9 @@
rdmsrl(MSR_GS_BASE, gs);
rdmsrl(MSR_KERNEL_GS_BASE, shadowgs);
+ if (!all)
+ return;
+
cr0 = read_cr0();
cr2 = read_cr2();
cr3 = read_cr3();
@@ -218,7 +203,7 @@
void show_regs(struct pt_regs *regs)
{
printk(KERN_INFO "CPU %d:", smp_processor_id());
- __show_regs(regs);
+ __show_regs(regs, 1);
show_trace(NULL, regs, (void *)(regs + 1), regs->bp);
}
@@ -754,12 +739,12 @@
if (!p || p == current || p->state == TASK_RUNNING)
return 0;
stack = (unsigned long)task_stack_page(p);
- if (p->thread.sp < stack || p->thread.sp > stack+THREAD_SIZE)
+ if (p->thread.sp < stack || p->thread.sp >= stack+THREAD_SIZE)
return 0;
fp = *(u64 *)(p->thread.sp);
do {
if (fp < (unsigned long)stack ||
- fp > (unsigned long)stack+THREAD_SIZE)
+ fp >= (unsigned long)stack+THREAD_SIZE)
return 0;
ip = *(u64 *)(fp+8);
if (!in_sched_functions(ip))
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index e375b65..0a6d8c1 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -40,7 +40,9 @@
REGSET_GENERAL,
REGSET_FP,
REGSET_XFP,
+ REGSET_IOPERM64 = REGSET_XFP,
REGSET_TLS,
+ REGSET_IOPERM32,
};
/*
@@ -555,6 +557,29 @@
return 0;
}
+/*
+ * These access the current or another (stopped) task's io permission
+ * bitmap for debugging or core dump.
+ */
+static int ioperm_active(struct task_struct *target,
+ const struct user_regset *regset)
+{
+ return target->thread.io_bitmap_max / regset->size;
+}
+
+static int ioperm_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ if (!target->thread.io_bitmap_ptr)
+ return -ENXIO;
+
+ return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ target->thread.io_bitmap_ptr,
+ 0, IO_BITMAP_BYTES);
+}
+
#ifdef CONFIG_X86_PTRACE_BTS
/*
* The configuration for a particular BTS hardware implementation.
@@ -1385,6 +1410,12 @@
.size = sizeof(long), .align = sizeof(long),
.active = xfpregs_active, .get = xfpregs_get, .set = xfpregs_set
},
+ [REGSET_IOPERM64] = {
+ .core_note_type = NT_386_IOPERM,
+ .n = IO_BITMAP_LONGS,
+ .size = sizeof(long), .align = sizeof(long),
+ .active = ioperm_active, .get = ioperm_get
+ },
};
static const struct user_regset_view user_x86_64_view = {
@@ -1431,6 +1462,12 @@
.active = regset_tls_active,
.get = regset_tls_get, .set = regset_tls_set
},
+ [REGSET_IOPERM32] = {
+ .core_note_type = NT_386_IOPERM,
+ .n = IO_BITMAP_BYTES / sizeof(u32),
+ .size = sizeof(u32), .align = sizeof(u32),
+ .active = ioperm_active, .get = ioperm_get
+ },
};
static const struct user_regset_view user_x86_32_view = {
@@ -1452,7 +1489,8 @@
#endif
}
-void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, int error_code)
+void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs,
+ int error_code, int si_code)
{
struct siginfo info;
@@ -1461,7 +1499,7 @@
memset(&info, 0, sizeof(info));
info.si_signo = SIGTRAP;
- info.si_code = TRAP_BRKPT;
+ info.si_code = si_code;
/* User-mode ip? */
info.si_addr = user_mode_vm(regs) ? (void __user *) regs->ip : NULL;
@@ -1548,5 +1586,5 @@
*/
if (test_thread_flag(TIF_SINGLESTEP) &&
tracehook_consider_fatal_signal(current, SIGTRAP, SIG_DFL))
- send_sigtrap(current, regs, 0);
+ send_sigtrap(current, regs, 0, TRAP_BRKPT);
}
diff --git a/arch/x86/kernel/quirks.c b/arch/x86/kernel/quirks.c
index d138588..f6a11b9 100644
--- a/arch/x86/kernel/quirks.c
+++ b/arch/x86/kernel/quirks.c
@@ -354,9 +354,27 @@
printk(KERN_DEBUG "Force enabled HPET at resume\n");
}
+static u32 ati_ixp4x0_rev(struct pci_dev *dev)
+{
+ u32 d;
+ u8 b;
+
+ pci_read_config_byte(dev, 0xac, &b);
+ b &= ~(1<<5);
+ pci_write_config_byte(dev, 0xac, b);
+ pci_read_config_dword(dev, 0x70, &d);
+ d |= 1<<8;
+ pci_write_config_dword(dev, 0x70, d);
+ pci_read_config_dword(dev, 0x8, &d);
+ d &= 0xff;
+ dev_printk(KERN_DEBUG, &dev->dev, "SB4X0 revision 0x%x\n", d);
+ return d;
+}
+
static void ati_force_enable_hpet(struct pci_dev *dev)
{
- u32 uninitialized_var(val);
+ u32 d, val;
+ u8 b;
if (hpet_address || force_hpet_address)
return;
@@ -366,14 +384,33 @@
return;
}
+ d = ati_ixp4x0_rev(dev);
+ if (d < 0x82)
+ return;
+
+ /* base address */
pci_write_config_dword(dev, 0x14, 0xfed00000);
pci_read_config_dword(dev, 0x14, &val);
+
+ /* enable interrupt */
+ outb(0x72, 0xcd6); b = inb(0xcd7);
+ b |= 0x1;
+ outb(0x72, 0xcd6); outb(b, 0xcd7);
+ outb(0x72, 0xcd6); b = inb(0xcd7);
+ if (!(b & 0x1))
+ return;
+ pci_read_config_dword(dev, 0x64, &d);
+ d |= (1<<10);
+ pci_write_config_dword(dev, 0x64, d);
+ pci_read_config_dword(dev, 0x64, &d);
+ if (!(d & (1<<10)))
+ return;
+
force_hpet_address = val;
force_hpet_resume_type = ATI_FORCE_HPET_RESUME;
dev_printk(KERN_DEBUG, &dev->dev, "Force enabled HPET at 0x%lx\n",
force_hpet_address);
cached_dev = dev;
- return;
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS,
ati_force_enable_hpet);
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index 141efab..2255782 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -302,7 +302,7 @@
if (clen > MAX_MAP_CHUNK-slop)
clen = MAX_MAP_CHUNK-slop;
mapaddr = ramdisk_image & PAGE_MASK;
- p = early_ioremap(mapaddr, clen+slop);
+ p = early_memremap(mapaddr, clen+slop);
memcpy(q, p+slop, clen);
early_iounmap(p, clen+slop);
q += clen;
@@ -379,7 +379,7 @@
return;
pa_data = boot_params.hdr.setup_data;
while (pa_data) {
- data = early_ioremap(pa_data, PAGE_SIZE);
+ data = early_memremap(pa_data, PAGE_SIZE);
switch (data->type) {
case SETUP_E820_EXT:
parse_e820_ext(data, pa_data);
@@ -402,7 +402,7 @@
return;
pa_data = boot_params.hdr.setup_data;
while (pa_data) {
- data = early_ioremap(pa_data, sizeof(*data));
+ data = early_memremap(pa_data, sizeof(*data));
e820_update_range(pa_data, sizeof(*data)+data->len,
E820_RAM, E820_RESERVED_KERN);
found = 1;
@@ -428,7 +428,7 @@
return;
pa_data = boot_params.hdr.setup_data;
while (pa_data) {
- data = early_ioremap(pa_data, sizeof(*data));
+ data = early_memremap(pa_data, sizeof(*data));
sprintf(buf, "setup data %x", data->type);
reserve_early(pa_data, pa_data+sizeof(*data)+data->len, buf);
pa_data = data->next;
@@ -582,6 +582,190 @@
struct x86_quirks *x86_quirks __initdata = &default_x86_quirks;
/*
+ * Some BIOSes seem to corrupt the low 64k of memory during events
+ * like suspend/resume and unplugging an HDMI cable. Reserve all
+ * remaining free memory in that area and fill it with a distinct
+ * pattern.
+ */
+#ifdef CONFIG_X86_CHECK_BIOS_CORRUPTION
+#define MAX_SCAN_AREAS 8
+
+static int __read_mostly memory_corruption_check = -1;
+
+static unsigned __read_mostly corruption_check_size = 64*1024;
+static unsigned __read_mostly corruption_check_period = 60; /* seconds */
+
+static struct e820entry scan_areas[MAX_SCAN_AREAS];
+static int num_scan_areas;
+
+
+static int set_corruption_check(char *arg)
+{
+ char *end;
+
+ memory_corruption_check = simple_strtol(arg, &end, 10);
+
+ return (*end == 0) ? 0 : -EINVAL;
+}
+early_param("memory_corruption_check", set_corruption_check);
+
+static int set_corruption_check_period(char *arg)
+{
+ char *end;
+
+ corruption_check_period = simple_strtoul(arg, &end, 10);
+
+ return (*end == 0) ? 0 : -EINVAL;
+}
+early_param("memory_corruption_check_period", set_corruption_check_period);
+
+static int set_corruption_check_size(char *arg)
+{
+ char *end;
+ unsigned size;
+
+ size = memparse(arg, &end);
+
+ if (*end == '\0')
+ corruption_check_size = size;
+
+ return (size == corruption_check_size) ? 0 : -EINVAL;
+}
+early_param("memory_corruption_check_size", set_corruption_check_size);
+
+
+static void __init setup_bios_corruption_check(void)
+{
+ u64 addr = PAGE_SIZE; /* assume first page is reserved anyway */
+
+ if (memory_corruption_check == -1) {
+ memory_corruption_check =
+#ifdef CONFIG_X86_BOOTPARAM_MEMORY_CORRUPTION_CHECK
+ 1
+#else
+ 0
+#endif
+ ;
+ }
+
+ if (corruption_check_size == 0)
+ memory_corruption_check = 0;
+
+ if (!memory_corruption_check)
+ return;
+
+ corruption_check_size = round_up(corruption_check_size, PAGE_SIZE);
+
+ while(addr < corruption_check_size && num_scan_areas < MAX_SCAN_AREAS) {
+ u64 size;
+ addr = find_e820_area_size(addr, &size, PAGE_SIZE);
+
+ if (addr == 0)
+ break;
+
+ if ((addr + size) > corruption_check_size)
+ size = corruption_check_size - addr;
+
+ if (size == 0)
+ break;
+
+ e820_update_range(addr, size, E820_RAM, E820_RESERVED);
+ scan_areas[num_scan_areas].addr = addr;
+ scan_areas[num_scan_areas].size = size;
+ num_scan_areas++;
+
+ /* Assume we've already mapped this early memory */
+ memset(__va(addr), 0, size);
+
+ addr += size;
+ }
+
+ printk(KERN_INFO "Scanning %d areas for low memory corruption\n",
+ num_scan_areas);
+ update_e820();
+}
+
+static struct timer_list periodic_check_timer;
+
+void check_for_bios_corruption(void)
+{
+ int i;
+ int corruption = 0;
+
+ if (!memory_corruption_check)
+ return;
+
+ for(i = 0; i < num_scan_areas; i++) {
+ unsigned long *addr = __va(scan_areas[i].addr);
+ unsigned long size = scan_areas[i].size;
+
+ for(; size; addr++, size -= sizeof(unsigned long)) {
+ if (!*addr)
+ continue;
+ printk(KERN_ERR "Corrupted low memory at %p (%lx phys) = %08lx\n",
+ addr, __pa(addr), *addr);
+ corruption = 1;
+ *addr = 0;
+ }
+ }
+
+ WARN(corruption, KERN_ERR "Memory corruption detected in low memory\n");
+}
+
+static void periodic_check_for_corruption(unsigned long data)
+{
+ check_for_bios_corruption();
+ mod_timer(&periodic_check_timer, round_jiffies(jiffies + corruption_check_period*HZ));
+}
+
+void start_periodic_check_for_corruption(void)
+{
+ if (!memory_corruption_check || corruption_check_period == 0)
+ return;
+
+ printk(KERN_INFO "Scanning for low memory corruption every %d seconds\n",
+ corruption_check_period);
+
+ init_timer(&periodic_check_timer);
+ periodic_check_timer.function = &periodic_check_for_corruption;
+ periodic_check_for_corruption(0);
+}
+#endif
+
+static int __init dmi_low_memory_corruption(const struct dmi_system_id *d)
+{
+ printk(KERN_NOTICE
+ "%s detected: BIOS may corrupt low RAM, working it around.\n",
+ d->ident);
+
+ e820_update_range(0, 0x10000, E820_RAM, E820_RESERVED);
+ sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &e820.nr_map);
+
+ return 0;
+}
+
+/* List of systems that have known low memory corruption BIOS problems */
+static struct dmi_system_id __initdata bad_bios_dmi_table[] = {
+#ifdef CONFIG_X86_RESERVE_LOW_64K
+ {
+ .callback = dmi_low_memory_corruption,
+ .ident = "AMI BIOS",
+ .matches = {
+ DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."),
+ },
+ },
+ {
+ .callback = dmi_low_memory_corruption,
+ .ident = "Phoenix BIOS",
+ .matches = {
+ DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies, LTD"),
+ },
+ },
+#endif
+ {}
+};
+
+/*
* Determine if we were loaded by an EFI loader. If so, then we have also been
* passed the efi memmap, systab, etc., so we should use these data structures
* for initialization. Note, the efi init code path is determined by the
@@ -715,6 +899,10 @@
finish_e820_parsing();
+ dmi_scan_machine();
+
+ dmi_check_system(bad_bios_dmi_table);
+
#ifdef CONFIG_X86_32
probe_roms();
#endif
@@ -758,6 +946,8 @@
#else
num_physpages = max_pfn;
+ if (cpu_has_x2apic)
+ check_x2apic();
/* How many end-of-memory variables you have, grandma! */
/* need this before calling reserve_initrd */
@@ -769,6 +959,10 @@
high_memory = (void *)__va(max_pfn * PAGE_SIZE - 1) + 1;
#endif
+#ifdef CONFIG_X86_CHECK_BIOS_CORRUPTION
+ setup_bios_corruption_check();
+#endif
+
/* max_pfn_mapped is updated here */
max_low_pfn_mapped = init_memory_mapping(0, max_low_pfn<<PAGE_SHIFT);
max_pfn_mapped = max_low_pfn_mapped;
@@ -797,8 +991,6 @@
vsmp_init();
#endif
- dmi_scan_machine();
-
io_delay_init();
/*
@@ -806,6 +998,8 @@
*/
acpi_boot_table_init();
+ early_acpi_boot_init();
+
#ifdef CONFIG_ACPI_NUMA
/*
* Parse SRAT to discover nodes.
@@ -901,3 +1095,5 @@
#endif
#endif
}
+
+
diff --git a/arch/x86/kernel/sigframe.h b/arch/x86/kernel/sigframe.h
index 8b4956e..cc673aa 100644
--- a/arch/x86/kernel/sigframe.h
+++ b/arch/x86/kernel/sigframe.h
@@ -3,9 +3,18 @@
char __user *pretcode;
int sig;
struct sigcontext sc;
- struct _fpstate fpstate;
+ /*
+ * fpstate is unused. fpstate is moved/allocated after
+ * retcode[] below. This movement allows to have the FP state and the
+ * future state extensions (xsave) stay together.
+ * And at the same time retaining the unused fpstate, prevents changing
+ * the offset of extramask[] in the sigframe and thus prevent any
+ * legacy application accessing/modifying it.
+ */
+ struct _fpstate fpstate_unused;
unsigned long extramask[_NSIG_WORDS-1];
char retcode[8];
+ /* fp state follows here */
};
struct rt_sigframe {
@@ -15,14 +24,15 @@
void __user *puc;
struct siginfo info;
struct ucontext uc;
- struct _fpstate fpstate;
char retcode[8];
+ /* fp state follows here */
};
#else
struct rt_sigframe {
char __user *pretcode;
struct ucontext uc;
struct siginfo info;
+ /* fp state follows here */
};
int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
diff --git a/arch/x86/kernel/signal_32.c b/arch/x86/kernel/signal_32.c
index 2a2435d..d6dd057 100644
--- a/arch/x86/kernel/signal_32.c
+++ b/arch/x86/kernel/signal_32.c
@@ -27,6 +27,7 @@
#include <asm/uaccess.h>
#include <asm/i387.h>
#include <asm/vdso.h>
+#include <asm/syscall.h>
#include <asm/syscalls.h>
#include "sigframe.h"
@@ -112,6 +113,27 @@
return do_sigaltstack(uss, uoss, regs->sp);
}
+#define COPY(x) { \
+ err |= __get_user(regs->x, &sc->x); \
+}
+
+#define COPY_SEG(seg) { \
+ unsigned short tmp; \
+ err |= __get_user(tmp, &sc->seg); \
+ regs->seg = tmp; \
+}
+
+#define COPY_SEG_STRICT(seg) { \
+ unsigned short tmp; \
+ err |= __get_user(tmp, &sc->seg); \
+ regs->seg = tmp | 3; \
+}
+
+#define GET_SEG(seg) { \
+ unsigned short tmp; \
+ err |= __get_user(tmp, &sc->seg); \
+ loadsegment(seg, tmp); \
+}
/*
* Do a signal return; undo the signal stack.
@@ -120,28 +142,13 @@
restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc,
unsigned long *pax)
{
+ void __user *buf;
+ unsigned int tmpflags;
unsigned int err = 0;
/* Always make any pending restarted system calls return -EINTR */
current_thread_info()->restart_block.fn = do_no_restart_syscall;
-#define COPY(x) err |= __get_user(regs->x, &sc->x)
-
-#define COPY_SEG(seg) \
- { unsigned short tmp; \
- err |= __get_user(tmp, &sc->seg); \
- regs->seg = tmp; }
-
-#define COPY_SEG_STRICT(seg) \
- { unsigned short tmp; \
- err |= __get_user(tmp, &sc->seg); \
- regs->seg = tmp|3; }
-
-#define GET_SEG(seg) \
- { unsigned short tmp; \
- err |= __get_user(tmp, &sc->seg); \
- loadsegment(seg, tmp); }
-
GET_SEG(gs);
COPY_SEG(fs);
COPY_SEG(es);
@@ -151,38 +158,15 @@
COPY_SEG_STRICT(cs);
COPY_SEG_STRICT(ss);
- {
- unsigned int tmpflags;
+ err |= __get_user(tmpflags, &sc->flags);
+ regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS);
+ regs->orig_ax = -1; /* disable syscall checks */
- err |= __get_user(tmpflags, &sc->flags);
- regs->flags = (regs->flags & ~FIX_EFLAGS) |
- (tmpflags & FIX_EFLAGS);
- regs->orig_ax = -1; /* disable syscall checks */
- }
-
- {
- struct _fpstate __user *buf;
-
- err |= __get_user(buf, &sc->fpstate);
- if (buf) {
- if (!access_ok(VERIFY_READ, buf, sizeof(*buf)))
- goto badframe;
- err |= restore_i387(buf);
- } else {
- struct task_struct *me = current;
-
- if (used_math()) {
- clear_fpu(me);
- clear_used_math();
- }
- }
- }
+ err |= __get_user(buf, &sc->fpstate);
+ err |= restore_i387_xstate(buf);
err |= __get_user(*pax, &sc->ax);
return err;
-
-badframe:
- return 1;
}
asmlinkage unsigned long sys_sigreturn(unsigned long __unused)
@@ -228,9 +212,8 @@
return 0;
}
-asmlinkage int sys_rt_sigreturn(unsigned long __unused)
+static long do_rt_sigreturn(struct pt_regs *regs)
{
- struct pt_regs *regs = (struct pt_regs *)&__unused;
struct rt_sigframe __user *frame;
unsigned long ax;
sigset_t set;
@@ -256,15 +239,22 @@
return ax;
badframe:
- force_sig(SIGSEGV, current);
+ signal_fault(regs, frame, "rt_sigreturn");
return 0;
}
+asmlinkage int sys_rt_sigreturn(unsigned long __unused)
+{
+ struct pt_regs *regs = (struct pt_regs *)&__unused;
+
+ return do_rt_sigreturn(regs);
+}
+
/*
* Set up a signal frame.
*/
static int
-setup_sigcontext(struct sigcontext __user *sc, struct _fpstate __user *fpstate,
+setup_sigcontext(struct sigcontext __user *sc, void __user *fpstate,
struct pt_regs *regs, unsigned long mask)
{
int tmp, err = 0;
@@ -291,7 +281,7 @@
err |= __put_user(regs->sp, &sc->sp_at_signal);
err |= __put_user(regs->ss, (unsigned int __user *)&sc->ss);
- tmp = save_i387(fpstate);
+ tmp = save_i387_xstate(fpstate);
if (tmp < 0)
err = 1;
else
@@ -308,7 +298,8 @@
* Determine which stack to use..
*/
static inline void __user *
-get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size)
+get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size,
+ void **fpstate)
{
unsigned long sp;
@@ -334,6 +325,11 @@
sp = (unsigned long) ka->sa.sa_restorer;
}
+ if (used_math()) {
+ sp = sp - sig_xstate_size;
+ *fpstate = (struct _fpstate *) sp;
+ }
+
sp -= frame_size;
/*
* Align the stack pointer according to the i386 ABI,
@@ -345,38 +341,29 @@
}
static int
-setup_frame(int sig, struct k_sigaction *ka, sigset_t *set,
- struct pt_regs *regs)
+__setup_frame(int sig, struct k_sigaction *ka, sigset_t *set,
+ struct pt_regs *regs)
{
struct sigframe __user *frame;
void __user *restorer;
int err = 0;
- int usig;
+ void __user *fpstate = NULL;
- frame = get_sigframe(ka, regs, sizeof(*frame));
+ frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
- goto give_sigsegv;
+ return -EFAULT;
- usig = current_thread_info()->exec_domain
- && current_thread_info()->exec_domain->signal_invmap
- && sig < 32
- ? current_thread_info()->exec_domain->signal_invmap[sig]
- : sig;
+ if (__put_user(sig, &frame->sig))
+ return -EFAULT;
- err = __put_user(usig, &frame->sig);
- if (err)
- goto give_sigsegv;
-
- err = setup_sigcontext(&frame->sc, &frame->fpstate, regs, set->sig[0]);
- if (err)
- goto give_sigsegv;
+ if (setup_sigcontext(&frame->sc, fpstate, regs, set->sig[0]))
+ return -EFAULT;
if (_NSIG_WORDS > 1) {
- err = __copy_to_user(&frame->extramask, &set->sig[1],
- sizeof(frame->extramask));
- if (err)
- goto give_sigsegv;
+ if (__copy_to_user(&frame->extramask, &set->sig[1],
+ sizeof(frame->extramask)))
+ return -EFAULT;
}
if (current->mm->context.vdso)
@@ -401,7 +388,7 @@
err |= __put_user(0x80cd, (short __user *)(frame->retcode+6));
if (err)
- goto give_sigsegv;
+ return -EFAULT;
/* Set up registers for signal handler */
regs->sp = (unsigned long)frame;
@@ -416,50 +403,43 @@
regs->cs = __USER_CS;
return 0;
-
-give_sigsegv:
- force_sigsegv(sig, current);
- return -EFAULT;
}
-static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
- sigset_t *set, struct pt_regs *regs)
+static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
+ sigset_t *set, struct pt_regs *regs)
{
struct rt_sigframe __user *frame;
void __user *restorer;
int err = 0;
- int usig;
+ void __user *fpstate = NULL;
- frame = get_sigframe(ka, regs, sizeof(*frame));
+ frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
- goto give_sigsegv;
+ return -EFAULT;
- usig = current_thread_info()->exec_domain
- && current_thread_info()->exec_domain->signal_invmap
- && sig < 32
- ? current_thread_info()->exec_domain->signal_invmap[sig]
- : sig;
-
- err |= __put_user(usig, &frame->sig);
+ err |= __put_user(sig, &frame->sig);
err |= __put_user(&frame->info, &frame->pinfo);
err |= __put_user(&frame->uc, &frame->puc);
err |= copy_siginfo_to_user(&frame->info, info);
if (err)
- goto give_sigsegv;
+ return -EFAULT;
/* Create the ucontext. */
- err |= __put_user(0, &frame->uc.uc_flags);
+ if (cpu_has_xsave)
+ err |= __put_user(UC_FP_XSTATE, &frame->uc.uc_flags);
+ else
+ err |= __put_user(0, &frame->uc.uc_flags);
err |= __put_user(0, &frame->uc.uc_link);
err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
err |= __put_user(sas_ss_flags(regs->sp),
&frame->uc.uc_stack.ss_flags);
err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
- err |= setup_sigcontext(&frame->uc.uc_mcontext, &frame->fpstate,
+ err |= setup_sigcontext(&frame->uc.uc_mcontext, fpstate,
regs, set->sig[0]);
err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
if (err)
- goto give_sigsegv;
+ return -EFAULT;
/* Set up to return from userspace. */
restorer = VDSO32_SYMBOL(current->mm->context.vdso, rt_sigreturn);
@@ -479,12 +459,12 @@
err |= __put_user(0x80cd, (short __user *)(frame->retcode+5));
if (err)
- goto give_sigsegv;
+ return -EFAULT;
/* Set up registers for signal handler */
regs->sp = (unsigned long)frame;
regs->ip = (unsigned long)ka->sa.sa_handler;
- regs->ax = (unsigned long)usig;
+ regs->ax = (unsigned long)sig;
regs->dx = (unsigned long)&frame->info;
regs->cx = (unsigned long)&frame->uc;
@@ -494,15 +474,48 @@
regs->cs = __USER_CS;
return 0;
-
-give_sigsegv:
- force_sigsegv(sig, current);
- return -EFAULT;
}
/*
* OK, we're invoking a handler:
*/
+static int signr_convert(int sig)
+{
+ struct thread_info *info = current_thread_info();
+
+ if (info->exec_domain && info->exec_domain->signal_invmap && sig < 32)
+ return info->exec_domain->signal_invmap[sig];
+ return sig;
+}
+
+#define is_ia32 1
+#define ia32_setup_frame __setup_frame
+#define ia32_setup_rt_frame __setup_rt_frame
+
+static int
+setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
+ sigset_t *set, struct pt_regs *regs)
+{
+ int usig = signr_convert(sig);
+ int ret;
+
+ /* Set up the stack frame */
+ if (is_ia32) {
+ if (ka->sa.sa_flags & SA_SIGINFO)
+ ret = ia32_setup_rt_frame(usig, ka, info, set, regs);
+ else
+ ret = ia32_setup_frame(usig, ka, set, regs);
+ } else
+ ret = __setup_rt_frame(sig, ka, info, set, regs);
+
+ if (ret) {
+ force_sigsegv(sig, current);
+ return -EFAULT;
+ }
+
+ return ret;
+}
+
static int
handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
sigset_t *oldset, struct pt_regs *regs)
@@ -510,9 +523,9 @@
int ret;
/* Are we from a system call? */
- if ((long)regs->orig_ax >= 0) {
+ if (syscall_get_nr(current, regs) >= 0) {
/* If so, check system call restarting.. */
- switch (regs->ax) {
+ switch (syscall_get_error(current, regs)) {
case -ERESTART_RESTARTBLOCK:
case -ERESTARTNOHAND:
regs->ax = -EINTR;
@@ -539,15 +552,20 @@
likely(test_and_clear_thread_flag(TIF_FORCED_TF)))
regs->flags &= ~X86_EFLAGS_TF;
- /* Set up the stack frame */
- if (ka->sa.sa_flags & SA_SIGINFO)
- ret = setup_rt_frame(sig, ka, info, oldset, regs);
- else
- ret = setup_frame(sig, ka, oldset, regs);
+ ret = setup_rt_frame(sig, ka, info, oldset, regs);
if (ret)
return ret;
+#ifdef CONFIG_X86_64
+ /*
+ * This has nothing to do with segment registers,
+ * despite the name. This magic affects uaccess.h
+ * macros' behavior. Reset it to the normal setting.
+ */
+ set_fs(USER_DS);
+#endif
+
/*
* Clear the direction flag as per the ABI for function entry.
*/
@@ -574,6 +592,7 @@
return 0;
}
+#define NR_restart_syscall __NR_restart_syscall
/*
* Note that 'init' is a special process: it doesn't get signals it doesn't
* want to handle. Thus you cannot kill init even with a SIGKILL even by
@@ -626,9 +645,9 @@
}
/* Did we come from a system call? */
- if ((long)regs->orig_ax >= 0) {
+ if (syscall_get_nr(current, regs) >= 0) {
/* Restart the system call - no handlers present */
- switch (regs->ax) {
+ switch (syscall_get_error(current, regs)) {
case -ERESTARTNOHAND:
case -ERESTARTSYS:
case -ERESTARTNOINTR:
@@ -637,7 +656,7 @@
break;
case -ERESTART_RESTARTBLOCK:
- regs->ax = __NR_restart_syscall;
+ regs->ax = NR_restart_syscall;
regs->ip -= 2;
break;
}
@@ -660,6 +679,12 @@
void
do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags)
{
+#if defined(CONFIG_X86_64) && defined(CONFIG_X86_MCE)
+ /* notify userspace of pending MCEs */
+ if (thread_info_flags & _TIF_MCE_NOTIFY)
+ mce_notify_user();
+#endif /* CONFIG_X86_64 && CONFIG_X86_MCE */
+
/* deal with pending signal delivery */
if (thread_info_flags & _TIF_SIGPENDING)
do_signal(regs);
@@ -669,5 +694,23 @@
tracehook_notify_resume(regs);
}
+#ifdef CONFIG_X86_32
clear_thread_flag(TIF_IRET);
+#endif /* CONFIG_X86_32 */
+}
+
+void signal_fault(struct pt_regs *regs, void __user *frame, char *where)
+{
+ struct task_struct *me = current;
+
+ if (show_unhandled_signals && printk_ratelimit()) {
+ printk(KERN_INFO
+ "%s[%d] bad frame in %s frame:%p ip:%lx sp:%lx orax:%lx",
+ me->comm, me->pid, where, frame,
+ regs->ip, regs->sp, regs->orig_ax);
+ print_vma_addr(" in ", regs->ip);
+ printk(KERN_CONT "\n");
+ }
+
+ force_sig(SIGSEGV, me);
}
diff --git a/arch/x86/kernel/signal_64.c b/arch/x86/kernel/signal_64.c
index 694aa88..a5c9627 100644
--- a/arch/x86/kernel/signal_64.c
+++ b/arch/x86/kernel/signal_64.c
@@ -52,67 +52,14 @@
return do_sigaltstack(uss, uoss, regs->sp);
}
-/*
- * Signal frame handlers.
- */
-
-static inline int save_i387(struct _fpstate __user *buf)
-{
- struct task_struct *tsk = current;
- int err = 0;
-
- BUILD_BUG_ON(sizeof(struct user_i387_struct) !=
- sizeof(tsk->thread.xstate->fxsave));
-
- if ((unsigned long)buf % 16)
- printk("save_i387: bad fpstate %p\n", buf);
-
- if (!used_math())
- return 0;
- clear_used_math(); /* trigger finit */
- if (task_thread_info(tsk)->status & TS_USEDFPU) {
- err = save_i387_checking((struct i387_fxsave_struct __user *)
- buf);
- if (err)
- return err;
- task_thread_info(tsk)->status &= ~TS_USEDFPU;
- stts();
- } else {
- if (__copy_to_user(buf, &tsk->thread.xstate->fxsave,
- sizeof(struct i387_fxsave_struct)))
- return -1;
- }
- return 1;
+#define COPY(x) { \
+ err |= __get_user(regs->x, &sc->x); \
}
-/*
- * This restores directly out of user space. Exceptions are handled.
- */
-static inline int restore_i387(struct _fpstate __user *buf)
-{
- struct task_struct *tsk = current;
- int err;
-
- if (!used_math()) {
- err = init_fpu(tsk);
- if (err)
- return err;
- }
-
- if (!(task_thread_info(current)->status & TS_USEDFPU)) {
- clts();
- task_thread_info(current)->status |= TS_USEDFPU;
- }
- err = restore_fpu_checking((__force struct i387_fxsave_struct *)buf);
- if (unlikely(err)) {
- /*
- * Encountered an error while doing the restore from the
- * user buffer, clear the fpu state.
- */
- clear_fpu(tsk);
- clear_used_math();
- }
- return err;
+#define COPY_SEG_STRICT(seg) { \
+ unsigned short tmp; \
+ err |= __get_user(tmp, &sc->seg); \
+ regs->seg = tmp | 3; \
}
/*
@@ -122,13 +69,13 @@
restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc,
unsigned long *pax)
{
+ void __user *buf;
+ unsigned int tmpflags;
unsigned int err = 0;
/* Always make any pending restarted system calls return -EINTR */
current_thread_info()->restart_block.fn = do_no_restart_syscall;
-#define COPY(x) (err |= __get_user(regs->x, &sc->x))
-
COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx);
COPY(dx); COPY(cx); COPY(ip);
COPY(r8);
@@ -143,48 +90,24 @@
/* Kernel saves and restores only the CS segment register on signals,
* which is the bare minimum needed to allow mixed 32/64-bit code.
* App's signal handler can save/restore other segments if needed. */
- {
- unsigned cs;
- err |= __get_user(cs, &sc->cs);
- regs->cs = cs | 3; /* Force into user mode */
- }
+ COPY_SEG_STRICT(cs);
- {
- unsigned int tmpflags;
- err |= __get_user(tmpflags, &sc->flags);
- regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS);
- regs->orig_ax = -1; /* disable syscall checks */
- }
+ err |= __get_user(tmpflags, &sc->flags);
+ regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS);
+ regs->orig_ax = -1; /* disable syscall checks */
- {
- struct _fpstate __user *buf;
- err |= __get_user(buf, &sc->fpstate);
-
- if (buf) {
- if (!access_ok(VERIFY_READ, buf, sizeof(*buf)))
- goto badframe;
- err |= restore_i387(buf);
- } else {
- struct task_struct *me = current;
- if (used_math()) {
- clear_fpu(me);
- clear_used_math();
- }
- }
- }
+ err |= __get_user(buf, &sc->fpstate);
+ err |= restore_i387_xstate(buf);
err |= __get_user(*pax, &sc->ax);
return err;
-
-badframe:
- return 1;
}
-asmlinkage long sys_rt_sigreturn(struct pt_regs *regs)
+static long do_rt_sigreturn(struct pt_regs *regs)
{
struct rt_sigframe __user *frame;
- sigset_t set;
unsigned long ax;
+ sigset_t set;
frame = (struct rt_sigframe __user *)(regs->sp - sizeof(long));
if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
@@ -207,10 +130,15 @@
return ax;
badframe:
- signal_fault(regs, frame, "sigreturn");
+ signal_fault(regs, frame, "rt_sigreturn");
return 0;
}
+asmlinkage long sys_rt_sigreturn(struct pt_regs *regs)
+{
+ return do_rt_sigreturn(regs);
+}
+
/*
* Set up a signal frame.
*/
@@ -269,41 +197,40 @@
sp = current->sas_ss_sp + current->sas_ss_size;
}
- return (void __user *)round_down(sp - size, 16);
+ return (void __user *)round_down(sp - size, 64);
}
-static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
- sigset_t *set, struct pt_regs *regs)
+static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
+ sigset_t *set, struct pt_regs *regs)
{
struct rt_sigframe __user *frame;
- struct _fpstate __user *fp = NULL;
+ void __user *fp = NULL;
int err = 0;
struct task_struct *me = current;
if (used_math()) {
- fp = get_stack(ka, regs, sizeof(struct _fpstate));
+ fp = get_stack(ka, regs, sig_xstate_size);
frame = (void __user *)round_down(
(unsigned long)fp - sizeof(struct rt_sigframe), 16) - 8;
- if (!access_ok(VERIFY_WRITE, fp, sizeof(struct _fpstate)))
- goto give_sigsegv;
-
- if (save_i387(fp) < 0)
- err |= -1;
+ if (save_i387_xstate(fp) < 0)
+ return -EFAULT;
} else
frame = get_stack(ka, regs, sizeof(struct rt_sigframe)) - 8;
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
- goto give_sigsegv;
+ return -EFAULT;
if (ka->sa.sa_flags & SA_SIGINFO) {
- err |= copy_siginfo_to_user(&frame->info, info);
- if (err)
- goto give_sigsegv;
+ if (copy_siginfo_to_user(&frame->info, info))
+ return -EFAULT;
}
/* Create the ucontext. */
- err |= __put_user(0, &frame->uc.uc_flags);
+ if (cpu_has_xsave)
+ err |= __put_user(UC_FP_XSTATE, &frame->uc.uc_flags);
+ else
+ err |= __put_user(0, &frame->uc.uc_flags);
err |= __put_user(0, &frame->uc.uc_link);
err |= __put_user(me->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
err |= __put_user(sas_ss_flags(regs->sp),
@@ -324,11 +251,11 @@
err |= __put_user(ka->sa.sa_restorer, &frame->pretcode);
} else {
/* could use a vstub here */
- goto give_sigsegv;
+ return -EFAULT;
}
if (err)
- goto give_sigsegv;
+ return -EFAULT;
/* Set up registers for signal handler */
regs->di = sig;
@@ -348,15 +275,45 @@
regs->cs = __USER_CS;
return 0;
-
-give_sigsegv:
- force_sigsegv(sig, current);
- return -EFAULT;
}
/*
* OK, we're invoking a handler
*/
+static int signr_convert(int sig)
+{
+ return sig;
+}
+
+#ifdef CONFIG_IA32_EMULATION
+#define is_ia32 test_thread_flag(TIF_IA32)
+#else
+#define is_ia32 0
+#endif
+
+static int
+setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
+ sigset_t *set, struct pt_regs *regs)
+{
+ int usig = signr_convert(sig);
+ int ret;
+
+ /* Set up the stack frame */
+ if (is_ia32) {
+ if (ka->sa.sa_flags & SA_SIGINFO)
+ ret = ia32_setup_rt_frame(usig, ka, info, set, regs);
+ else
+ ret = ia32_setup_frame(usig, ka, set, regs);
+ } else
+ ret = __setup_rt_frame(sig, ka, info, set, regs);
+
+ if (ret) {
+ force_sigsegv(sig, current);
+ return -EFAULT;
+ }
+
+ return ret;
+}
static int
handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
@@ -394,51 +351,48 @@
likely(test_and_clear_thread_flag(TIF_FORCED_TF)))
regs->flags &= ~X86_EFLAGS_TF;
-#ifdef CONFIG_IA32_EMULATION
- if (test_thread_flag(TIF_IA32)) {
- if (ka->sa.sa_flags & SA_SIGINFO)
- ret = ia32_setup_rt_frame(sig, ka, info, oldset, regs);
- else
- ret = ia32_setup_frame(sig, ka, oldset, regs);
- } else
-#endif
ret = setup_rt_frame(sig, ka, info, oldset, regs);
- if (ret == 0) {
- /*
- * This has nothing to do with segment registers,
- * despite the name. This magic affects uaccess.h
- * macros' behavior. Reset it to the normal setting.
- */
- set_fs(USER_DS);
+ if (ret)
+ return ret;
- /*
- * Clear the direction flag as per the ABI for function entry.
- */
- regs->flags &= ~X86_EFLAGS_DF;
+#ifdef CONFIG_X86_64
+ /*
+ * This has nothing to do with segment registers,
+ * despite the name. This magic affects uaccess.h
+ * macros' behavior. Reset it to the normal setting.
+ */
+ set_fs(USER_DS);
+#endif
- /*
- * Clear TF when entering the signal handler, but
- * notify any tracer that was single-stepping it.
- * The tracer may want to single-step inside the
- * handler too.
- */
- regs->flags &= ~X86_EFLAGS_TF;
+ /*
+ * Clear the direction flag as per the ABI for function entry.
+ */
+ regs->flags &= ~X86_EFLAGS_DF;
- spin_lock_irq(¤t->sighand->siglock);
- sigorsets(¤t->blocked, ¤t->blocked, &ka->sa.sa_mask);
- if (!(ka->sa.sa_flags & SA_NODEFER))
- sigaddset(¤t->blocked, sig);
- recalc_sigpending();
- spin_unlock_irq(¤t->sighand->siglock);
+ /*
+ * Clear TF when entering the signal handler, but
+ * notify any tracer that was single-stepping it.
+ * The tracer may want to single-step inside the
+ * handler too.
+ */
+ regs->flags &= ~X86_EFLAGS_TF;
- tracehook_signal_handler(sig, info, ka, regs,
- test_thread_flag(TIF_SINGLESTEP));
- }
+ spin_lock_irq(¤t->sighand->siglock);
+ sigorsets(¤t->blocked, ¤t->blocked, &ka->sa.sa_mask);
+ if (!(ka->sa.sa_flags & SA_NODEFER))
+ sigaddset(¤t->blocked, sig);
+ recalc_sigpending();
+ spin_unlock_irq(¤t->sighand->siglock);
- return ret;
+ tracehook_signal_handler(sig, info, ka, regs,
+ test_thread_flag(TIF_SINGLESTEP));
+
+ return 0;
}
+#define NR_restart_syscall \
+ test_thread_flag(TIF_IA32) ? __NR_ia32_restart_syscall : __NR_restart_syscall
/*
* Note that 'init' is a special process: it doesn't get signals it doesn't
* want to handle. Thus you cannot kill init even with a SIGKILL even by
@@ -468,7 +422,8 @@
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
if (signr > 0) {
- /* Re-enable any watchpoints before delivering the
+ /*
+ * Re-enable any watchpoints before delivering the
* signal to user space. The processor register will
* have been cleared if the watchpoint triggered
* inside the kernel.
@@ -476,7 +431,7 @@
if (current->thread.debugreg7)
set_debugreg(current->thread.debugreg7, 7);
- /* Whee! Actually deliver the signal. */
+ /* Whee! Actually deliver the signal. */
if (handle_signal(signr, &info, &ka, oldset, regs) == 0) {
/*
* A signal was successfully delivered; the saved
@@ -499,10 +454,9 @@
regs->ax = regs->orig_ax;
regs->ip -= 2;
break;
+
case -ERESTART_RESTARTBLOCK:
- regs->ax = test_thread_flag(TIF_IA32) ?
- __NR_ia32_restart_syscall :
- __NR_restart_syscall;
+ regs->ax = NR_restart_syscall;
regs->ip -= 2;
break;
}
@@ -518,14 +472,18 @@
}
}
-void do_notify_resume(struct pt_regs *regs, void *unused,
- __u32 thread_info_flags)
+/*
+ * notification of userspace execution resumption
+ * - triggered by the TIF_WORK_MASK flags
+ */
+void
+do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags)
{
-#ifdef CONFIG_X86_MCE
+#if defined(CONFIG_X86_64) && defined(CONFIG_X86_MCE)
/* notify userspace of pending MCEs */
if (thread_info_flags & _TIF_MCE_NOTIFY)
mce_notify_user();
-#endif /* CONFIG_X86_MCE */
+#endif /* CONFIG_X86_64 && CONFIG_X86_MCE */
/* deal with pending signal delivery */
if (thread_info_flags & _TIF_SIGPENDING)
@@ -535,17 +493,23 @@
clear_thread_flag(TIF_NOTIFY_RESUME);
tracehook_notify_resume(regs);
}
+
+#ifdef CONFIG_X86_32
+ clear_thread_flag(TIF_IRET);
+#endif /* CONFIG_X86_32 */
}
void signal_fault(struct pt_regs *regs, void __user *frame, char *where)
{
struct task_struct *me = current;
+
if (show_unhandled_signals && printk_ratelimit()) {
- printk("%s[%d] bad frame in %s frame:%p ip:%lx sp:%lx orax:%lx",
- me->comm, me->pid, where, frame, regs->ip,
- regs->sp, regs->orig_ax);
+ printk(KERN_INFO
+ "%s[%d] bad frame in %s frame:%p ip:%lx sp:%lx orax:%lx",
+ me->comm, me->pid, where, frame,
+ regs->ip, regs->sp, regs->orig_ax);
print_vma_addr(" in ", regs->ip);
- printk("\n");
+ printk(KERN_CONT "\n");
}
force_sig(SIGSEGV, me);
diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c
index 361b7a4..18f9b19 100644
--- a/arch/x86/kernel/smp.c
+++ b/arch/x86/kernel/smp.c
@@ -214,12 +214,16 @@
struct smp_ops smp_ops = {
.smp_prepare_boot_cpu = native_smp_prepare_boot_cpu,
.smp_prepare_cpus = native_smp_prepare_cpus,
- .cpu_up = native_cpu_up,
.smp_cpus_done = native_smp_cpus_done,
.smp_send_stop = native_smp_send_stop,
.smp_send_reschedule = native_smp_send_reschedule,
+ .cpu_up = native_cpu_up,
+ .cpu_die = native_cpu_die,
+ .cpu_disable = native_cpu_disable,
+ .play_dead = native_play_dead,
+
.send_call_func_ipi = native_send_call_func_ipi,
.send_call_func_single_ipi = native_send_call_func_single_ipi,
};
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index 4e7ccb0..8c3aca7 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -52,6 +52,7 @@
#include <asm/desc.h>
#include <asm/nmi.h>
#include <asm/irq.h>
+#include <asm/idle.h>
#include <asm/smp.h>
#include <asm/trampoline.h>
#include <asm/cpu.h>
@@ -123,7 +124,6 @@
static atomic_t init_deasserted;
-static int boot_cpu_logical_apicid;
/* representing cpus for which sibling maps can be computed */
static cpumask_t cpu_sibling_setup_map;
@@ -165,6 +165,8 @@
#endif
#ifdef CONFIG_X86_32
+static int boot_cpu_logical_apicid;
+
u8 cpu_2_logical_apicid[NR_CPUS] __read_mostly =
{ [0 ... NR_CPUS-1] = BAD_APICID };
@@ -210,7 +212,7 @@
/*
* (This works even if the APIC is not enabled.)
*/
- phys_id = GET_APIC_ID(read_apic_id());
+ phys_id = read_apic_id();
cpuid = smp_processor_id();
if (cpu_isset(cpuid, cpu_callin_map)) {
panic("%s: phys CPU#%d, CPU#%d already present??\n", __func__,
@@ -332,14 +334,17 @@
* does not change while we are assigning vectors to cpus. Holding
* this lock ensures we don't half assign or remove an irq from a cpu.
*/
- ipi_call_lock_irq();
+ ipi_call_lock();
lock_vector_lock();
__setup_vector_irq(smp_processor_id());
cpu_set(smp_processor_id(), cpu_online_map);
unlock_vector_lock();
- ipi_call_unlock_irq();
+ ipi_call_unlock();
per_cpu(cpu_state, smp_processor_id()) = CPU_ONLINE;
+ /* enable local interrupts */
+ local_irq_enable();
+
setup_secondary_clock();
wmb();
@@ -551,8 +556,7 @@
printk(KERN_CONT
"a previous APIC delivery may have failed\n");
- apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(apicid));
- apic_write(APIC_ICR, APIC_DM_REMRD | regs[i]);
+ apic_icr_write(APIC_DM_REMRD | regs[i], apicid);
timeout = 0;
do {
@@ -584,11 +588,9 @@
int maxlvt;
/* Target chip */
- apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(logical_apicid));
-
/* Boot on the stack */
/* Kick the second */
- apic_write(APIC_ICR, APIC_DM_NMI | APIC_DEST_LOGICAL);
+ apic_icr_write(APIC_DM_NMI | APIC_DEST_LOGICAL, logical_apicid);
pr_debug("Waiting for send to finish...\n");
send_status = safe_apic_wait_icr_idle();
@@ -597,10 +599,12 @@
* Give the other CPU some time to accept the IPI.
*/
udelay(200);
- maxlvt = lapic_get_maxlvt();
- if (maxlvt > 3) /* Due to the Pentium erratum 3AP. */
- apic_write(APIC_ESR, 0);
- accept_status = (apic_read(APIC_ESR) & 0xEF);
+ if (APIC_INTEGRATED(apic_version[phys_apicid])) {
+ maxlvt = lapic_get_maxlvt();
+ if (maxlvt > 3) /* Due to the Pentium erratum 3AP. */
+ apic_write(APIC_ESR, 0);
+ accept_status = (apic_read(APIC_ESR) & 0xEF);
+ }
pr_debug("NMI sent.\n");
if (send_status)
@@ -641,13 +645,11 @@
/*
* Turn INIT on target chip
*/
- apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(phys_apicid));
-
/*
* Send IPI
*/
- apic_write(APIC_ICR,
- APIC_INT_LEVELTRIG | APIC_INT_ASSERT | APIC_DM_INIT);
+ apic_icr_write(APIC_INT_LEVELTRIG | APIC_INT_ASSERT | APIC_DM_INIT,
+ phys_apicid);
pr_debug("Waiting for send to finish...\n");
send_status = safe_apic_wait_icr_idle();
@@ -657,10 +659,8 @@
pr_debug("Deasserting INIT.\n");
/* Target chip */
- apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(phys_apicid));
-
/* Send IPI */
- apic_write(APIC_ICR, APIC_INT_LEVELTRIG | APIC_DM_INIT);
+ apic_icr_write(APIC_INT_LEVELTRIG | APIC_DM_INIT, phys_apicid);
pr_debug("Waiting for send to finish...\n");
send_status = safe_apic_wait_icr_idle();
@@ -703,11 +703,10 @@
*/
/* Target chip */
- apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(phys_apicid));
-
/* Boot on the stack */
/* Kick the second */
- apic_write(APIC_ICR, APIC_DM_STARTUP | (start_eip >> 12));
+ apic_icr_write(APIC_DM_STARTUP | (start_eip >> 12),
+ phys_apicid);
/*
* Give the other CPU some time to accept the IPI.
@@ -1176,10 +1175,17 @@
* Setup boot CPU information
*/
smp_store_cpu_info(0); /* Final full version of the data */
+#ifdef CONFIG_X86_32
boot_cpu_logical_apicid = logical_smp_processor_id();
+#endif
current_thread_info()->cpu = 0; /* needed? */
set_cpu_sibling_map(0);
+#ifdef CONFIG_X86_64
+ enable_IR_x2apic();
+ setup_apic_routing();
+#endif
+
if (smp_sanity_check(max_cpus) < 0) {
printk(KERN_INFO "SMP disabled\n");
disable_smp();
@@ -1187,9 +1193,9 @@
}
preempt_disable();
- if (GET_APIC_ID(read_apic_id()) != boot_cpu_physical_apicid) {
+ if (read_apic_id() != boot_cpu_physical_apicid) {
panic("Boot APIC ID in local APIC unexpected (%d vs %d)",
- GET_APIC_ID(read_apic_id()), boot_cpu_physical_apicid);
+ read_apic_id(), boot_cpu_physical_apicid);
/* Or can we switch back to PIC here? */
}
preempt_enable();
@@ -1255,6 +1261,44 @@
check_nmi_watchdog();
}
+/*
+ * cpu_possible_map should be static, it cannot change as cpu's
+ * are onlined, or offlined. The reason is per-cpu data-structures
+ * are allocated by some modules at init time, and dont expect to
+ * do this dynamically on cpu arrival/departure.
+ * cpu_present_map on the other hand can change dynamically.
+ * In case when cpu_hotplug is not compiled, then we resort to current
+ * behaviour, which is cpu_possible == cpu_present.
+ * - Ashok Raj
+ *
+ * Three ways to find out the number of additional hotplug CPUs:
+ * - If the BIOS specified disabled CPUs in ACPI/mptables use that.
+ * - The user can overwrite it with additional_cpus=NUM
+ * - Otherwise don't reserve additional CPUs.
+ * We do this because additional CPUs waste a lot of memory.
+ * -AK
+ */
+__init void prefill_possible_map(void)
+{
+ int i, possible;
+
+ /* no processor from mptable or madt */
+ if (!num_processors)
+ num_processors = 1;
+
+ possible = num_processors + disabled_cpus;
+ if (possible > NR_CPUS)
+ possible = NR_CPUS;
+
+ printk(KERN_INFO "SMP: Allowing %d CPUs, %d hotplug CPUs\n",
+ possible, max_t(int, possible - num_processors, 0));
+
+ for (i = 0; i < possible; i++)
+ cpu_set(i, cpu_possible_map);
+
+ nr_cpu_ids = possible;
+}
+
#ifdef CONFIG_HOTPLUG_CPU
static void remove_siblinginfo(int cpu)
@@ -1280,60 +1324,6 @@
cpu_clear(cpu, cpu_sibling_setup_map);
}
-static int additional_cpus __initdata = -1;
-
-static __init int setup_additional_cpus(char *s)
-{
- return s && get_option(&s, &additional_cpus) ? 0 : -EINVAL;
-}
-early_param("additional_cpus", setup_additional_cpus);
-
-/*
- * cpu_possible_map should be static, it cannot change as cpu's
- * are onlined, or offlined. The reason is per-cpu data-structures
- * are allocated by some modules at init time, and dont expect to
- * do this dynamically on cpu arrival/departure.
- * cpu_present_map on the other hand can change dynamically.
- * In case when cpu_hotplug is not compiled, then we resort to current
- * behaviour, which is cpu_possible == cpu_present.
- * - Ashok Raj
- *
- * Three ways to find out the number of additional hotplug CPUs:
- * - If the BIOS specified disabled CPUs in ACPI/mptables use that.
- * - The user can overwrite it with additional_cpus=NUM
- * - Otherwise don't reserve additional CPUs.
- * We do this because additional CPUs waste a lot of memory.
- * -AK
- */
-__init void prefill_possible_map(void)
-{
- int i;
- int possible;
-
- /* no processor from mptable or madt */
- if (!num_processors)
- num_processors = 1;
-
- if (additional_cpus == -1) {
- if (disabled_cpus > 0)
- additional_cpus = disabled_cpus;
- else
- additional_cpus = 0;
- }
-
- possible = num_processors + additional_cpus;
- if (possible > NR_CPUS)
- possible = NR_CPUS;
-
- printk(KERN_INFO "SMP: Allowing %d CPUs, %d hotplug CPUs\n",
- possible, max_t(int, possible - num_processors, 0));
-
- for (i = 0; i < possible; i++)
- cpu_set(i, cpu_possible_map);
-
- nr_cpu_ids = possible;
-}
-
static void __ref remove_cpu_from_maps(int cpu)
{
cpu_clear(cpu, cpu_online_map);
@@ -1344,7 +1334,29 @@
numa_remove_cpu(cpu);
}
-int __cpu_disable(void)
+void cpu_disable_common(void)
+{
+ int cpu = smp_processor_id();
+ /*
+ * HACK:
+ * Allow any queued timer interrupts to get serviced
+ * This is only a temporary solution until we cleanup
+ * fixup_irqs as we do for IA64.
+ */
+ local_irq_enable();
+ mdelay(1);
+
+ local_irq_disable();
+ remove_siblinginfo(cpu);
+
+ /* It's now safe to remove this processor from the online map */
+ lock_vector_lock();
+ remove_cpu_from_maps(cpu);
+ unlock_vector_lock();
+ fixup_irqs(cpu_online_map);
+}
+
+int native_cpu_disable(void)
{
int cpu = smp_processor_id();
@@ -1363,27 +1375,11 @@
stop_apic_nmi_watchdog(NULL);
clear_local_APIC();
- /*
- * HACK:
- * Allow any queued timer interrupts to get serviced
- * This is only a temporary solution until we cleanup
- * fixup_irqs as we do for IA64.
- */
- local_irq_enable();
- mdelay(1);
-
- local_irq_disable();
- remove_siblinginfo(cpu);
-
- /* It's now safe to remove this processor from the online map */
- lock_vector_lock();
- remove_cpu_from_maps(cpu);
- unlock_vector_lock();
- fixup_irqs(cpu_online_map);
+ cpu_disable_common();
return 0;
}
-void __cpu_die(unsigned int cpu)
+void native_cpu_die(unsigned int cpu)
{
/* We don't do anything here: idle task is faking death itself. */
unsigned int i;
@@ -1400,15 +1396,45 @@
}
printk(KERN_ERR "CPU %u didn't die...\n", cpu);
}
+
+void play_dead_common(void)
+{
+ idle_task_exit();
+ reset_lazy_tlbstate();
+ irq_ctx_exit(raw_smp_processor_id());
+ c1e_remove_cpu(raw_smp_processor_id());
+
+ mb();
+ /* Ack it */
+ __get_cpu_var(cpu_state) = CPU_DEAD;
+
+ /*
+ * With physical CPU hotplug, we should halt the cpu
+ */
+ local_irq_disable();
+}
+
+void native_play_dead(void)
+{
+ play_dead_common();
+ wbinvd_halt();
+}
+
#else /* ... !CONFIG_HOTPLUG_CPU */
-int __cpu_disable(void)
+int native_cpu_disable(void)
{
return -ENOSYS;
}
-void __cpu_die(unsigned int cpu)
+void native_cpu_die(unsigned int cpu)
{
/* We said "no" in __cpu_disable */
BUG();
}
+
+void native_play_dead(void)
+{
+ BUG();
+}
+
#endif
diff --git a/arch/x86/kernel/summit_32.c b/arch/x86/kernel/summit_32.c
index d67ce5f..7b98785 100644
--- a/arch/x86/kernel/summit_32.c
+++ b/arch/x86/kernel/summit_32.c
@@ -30,7 +30,7 @@
#include <linux/init.h>
#include <asm/io.h>
#include <asm/bios_ebda.h>
-#include <asm/mach-summit/mach_mpparse.h>
+#include <asm/summit/mpparse.h>
static struct rio_table_hdr *rio_table_hdr __initdata;
static struct scal_detail *scal_devs[MAX_NUMNODES] __initdata;
diff --git a/arch/x86/kernel/time_32.c b/arch/x86/kernel/time_32.c
index bbecf8b..77b400f 100644
--- a/arch/x86/kernel/time_32.c
+++ b/arch/x86/kernel/time_32.c
@@ -47,10 +47,9 @@
unsigned long pc = instruction_pointer(regs);
#ifdef CONFIG_SMP
- if (!v8086_mode(regs) && SEGMENT_IS_KERNEL_CODE(regs->cs) &&
- in_lock_functions(pc)) {
+ if (!user_mode_vm(regs) && in_lock_functions(pc)) {
#ifdef CONFIG_FRAME_POINTER
- return *(unsigned long *)(regs->bp + 4);
+ return *(unsigned long *)(regs->bp + sizeof(long));
#else
unsigned long *sp = (unsigned long *)®s->sp;
@@ -95,6 +94,7 @@
do_timer_interrupt_hook();
+#ifdef CONFIG_MCA
if (MCA_bus) {
/* The PS/2 uses level-triggered interrupts. You can't
turn them off, nor would you want to (any attempt to
@@ -108,6 +108,7 @@
u8 irq_v = inb_p( 0x61 ); /* read the current state */
outb_p( irq_v|0x80, 0x61 ); /* reset the IRQ */
}
+#endif
return IRQ_HANDLED;
}
diff --git a/arch/x86/kernel/time_64.c b/arch/x86/kernel/time_64.c
index e3d49c5..cb19d65 100644
--- a/arch/x86/kernel/time_64.c
+++ b/arch/x86/kernel/time_64.c
@@ -16,6 +16,7 @@
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/time.h>
+#include <linux/mca.h>
#include <asm/i8253.h>
#include <asm/hpet.h>
@@ -33,23 +34,34 @@
/* Assume the lock function has either no stack frame or a copy
of flags from PUSHF
Eflags always has bits 22 and up cleared unlike kernel addresses. */
- if (!user_mode(regs) && in_lock_functions(pc)) {
+ if (!user_mode_vm(regs) && in_lock_functions(pc)) {
+#ifdef CONFIG_FRAME_POINTER
+ return *(unsigned long *)(regs->bp + sizeof(long));
+#else
unsigned long *sp = (unsigned long *)regs->sp;
if (sp[0] >> 22)
return sp[0];
if (sp[1] >> 22)
return sp[1];
+#endif
}
return pc;
}
EXPORT_SYMBOL(profile_pc);
-static irqreturn_t timer_event_interrupt(int irq, void *dev_id)
+irqreturn_t timer_interrupt(int irq, void *dev_id)
{
add_pda(irq0_irqs, 1);
global_clock_event->event_handler(global_clock_event);
+#ifdef CONFIG_MCA
+ if (MCA_bus) {
+ u8 irq_v = inb_p(0x61); /* read the current state */
+ outb_p(irq_v|0x80, 0x61); /* reset the IRQ */
+ }
+#endif
+
return IRQ_HANDLED;
}
@@ -100,7 +112,7 @@
}
static struct irqaction irq0 = {
- .handler = timer_event_interrupt,
+ .handler = timer_interrupt,
.flags = IRQF_DISABLED | IRQF_IRQPOLL | IRQF_NOBALANCING,
.mask = CPU_MASK_NONE,
.name = "timer"
@@ -111,16 +123,13 @@
if (!hpet_enable())
setup_pit_timer();
+ irq0.mask = cpumask_of_cpu(0);
setup_irq(0, &irq0);
}
void __init time_init(void)
{
tsc_init();
- if (cpu_has(&boot_cpu_data, X86_FEATURE_RDTSCP))
- vgetcpu_mode = VGETCPU_RDTSCP;
- else
- vgetcpu_mode = VGETCPU_LSL;
late_time_init = choose_time_init();
}
diff --git a/arch/x86/kernel/tlb_32.c b/arch/x86/kernel/tlb_32.c
index fec1ece..e00534b 100644
--- a/arch/x86/kernel/tlb_32.c
+++ b/arch/x86/kernel/tlb_32.c
@@ -241,3 +241,11 @@
on_each_cpu(do_flush_tlb_all, NULL, 1);
}
+void reset_lazy_tlbstate(void)
+{
+ int cpu = raw_smp_processor_id();
+
+ per_cpu(cpu_tlbstate, cpu).state = 0;
+ per_cpu(cpu_tlbstate, cpu).active_mm = &init_mm;
+}
+
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
new file mode 100644
index 0000000..e062974
--- /dev/null
+++ b/arch/x86/kernel/traps.c
@@ -0,0 +1,1034 @@
+/*
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs
+ *
+ * Pentium III FXSR, SSE support
+ * Gareth Hughes <gareth@valinux.com>, May 2000
+ */
+
+/*
+ * Handle hardware traps and faults.
+ */
+#include <linux/interrupt.h>
+#include <linux/kallsyms.h>
+#include <linux/spinlock.h>
+#include <linux/kprobes.h>
+#include <linux/uaccess.h>
+#include <linux/utsname.h>
+#include <linux/kdebug.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/ptrace.h>
+#include <linux/string.h>
+#include <linux/unwind.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/kexec.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/init.h>
+#include <linux/bug.h>
+#include <linux/nmi.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/io.h>
+
+#ifdef CONFIG_EISA
+#include <linux/ioport.h>
+#include <linux/eisa.h>
+#endif
+
+#ifdef CONFIG_MCA
+#include <linux/mca.h>
+#endif
+
+#if defined(CONFIG_EDAC)
+#include <linux/edac.h>
+#endif
+
+#include <asm/stacktrace.h>
+#include <asm/processor.h>
+#include <asm/debugreg.h>
+#include <asm/atomic.h>
+#include <asm/system.h>
+#include <asm/unwind.h>
+#include <asm/traps.h>
+#include <asm/desc.h>
+#include <asm/i387.h>
+
+#include <mach_traps.h>
+
+#ifdef CONFIG_X86_64
+#include <asm/pgalloc.h>
+#include <asm/proto.h>
+#include <asm/pda.h>
+#else
+#include <asm/processor-flags.h>
+#include <asm/arch_hooks.h>
+#include <asm/nmi.h>
+#include <asm/smp.h>
+#include <asm/io.h>
+#include <asm/traps.h>
+
+#include "cpu/mcheck/mce.h"
+
+DECLARE_BITMAP(used_vectors, NR_VECTORS);
+EXPORT_SYMBOL_GPL(used_vectors);
+
+asmlinkage int system_call(void);
+
+/* Do we ignore FPU interrupts ? */
+char ignore_fpu_irq;
+
+/*
+ * The IDT has to be page-aligned to simplify the Pentium
+ * F0 0F bug workaround.. We have a special link segment
+ * for this.
+ */
+gate_desc idt_table[256]
+ __attribute__((__section__(".data.idt"))) = { { { { 0, 0 } } }, };
+#endif
+
+static int ignore_nmis;
+
+static inline void conditional_sti(struct pt_regs *regs)
+{
+ if (regs->flags & X86_EFLAGS_IF)
+ local_irq_enable();
+}
+
+static inline void preempt_conditional_sti(struct pt_regs *regs)
+{
+ inc_preempt_count();
+ if (regs->flags & X86_EFLAGS_IF)
+ local_irq_enable();
+}
+
+static inline void preempt_conditional_cli(struct pt_regs *regs)
+{
+ if (regs->flags & X86_EFLAGS_IF)
+ local_irq_disable();
+ dec_preempt_count();
+}
+
+#ifdef CONFIG_X86_32
+static inline void
+die_if_kernel(const char *str, struct pt_regs *regs, long err)
+{
+ if (!user_mode_vm(regs))
+ die(str, regs, err);
+}
+
+/*
+ * Perform the lazy TSS's I/O bitmap copy. If the TSS has an
+ * invalid offset set (the LAZY one) and the faulting thread has
+ * a valid I/O bitmap pointer, we copy the I/O bitmap in the TSS,
+ * we set the offset field correctly and return 1.
+ */
+static int lazy_iobitmap_copy(void)
+{
+ struct thread_struct *thread;
+ struct tss_struct *tss;
+ int cpu;
+
+ cpu = get_cpu();
+ tss = &per_cpu(init_tss, cpu);
+ thread = ¤t->thread;
+
+ if (tss->x86_tss.io_bitmap_base == INVALID_IO_BITMAP_OFFSET_LAZY &&
+ thread->io_bitmap_ptr) {
+ memcpy(tss->io_bitmap, thread->io_bitmap_ptr,
+ thread->io_bitmap_max);
+ /*
+ * If the previously set map was extending to higher ports
+ * than the current one, pad extra space with 0xff (no access).
+ */
+ if (thread->io_bitmap_max < tss->io_bitmap_max) {
+ memset((char *) tss->io_bitmap +
+ thread->io_bitmap_max, 0xff,
+ tss->io_bitmap_max - thread->io_bitmap_max);
+ }
+ tss->io_bitmap_max = thread->io_bitmap_max;
+ tss->x86_tss.io_bitmap_base = IO_BITMAP_OFFSET;
+ tss->io_bitmap_owner = thread;
+ put_cpu();
+
+ return 1;
+ }
+ put_cpu();
+
+ return 0;
+}
+#endif
+
+static void __kprobes
+do_trap(int trapnr, int signr, char *str, struct pt_regs *regs,
+ long error_code, siginfo_t *info)
+{
+ struct task_struct *tsk = current;
+
+#ifdef CONFIG_X86_32
+ if (regs->flags & X86_VM_MASK) {
+ /*
+ * traps 0, 1, 3, 4, and 5 should be forwarded to vm86.
+ * On nmi (interrupt 2), do_trap should not be called.
+ */
+ if (trapnr < 6)
+ goto vm86_trap;
+ goto trap_signal;
+ }
+#endif
+
+ if (!user_mode(regs))
+ goto kernel_trap;
+
+#ifdef CONFIG_X86_32
+trap_signal:
+#endif
+ /*
+ * We want error_code and trap_no set for userspace faults and
+ * kernelspace faults which result in die(), but not
+ * kernelspace faults which are fixed up. die() gives the
+ * process no chance to handle the signal and notice the
+ * kernel fault information, so that won't result in polluting
+ * the information about previously queued, but not yet
+ * delivered, faults. See also do_general_protection below.
+ */
+ tsk->thread.error_code = error_code;
+ tsk->thread.trap_no = trapnr;
+
+#ifdef CONFIG_X86_64
+ if (show_unhandled_signals && unhandled_signal(tsk, signr) &&
+ printk_ratelimit()) {
+ printk(KERN_INFO
+ "%s[%d] trap %s ip:%lx sp:%lx error:%lx",
+ tsk->comm, tsk->pid, str,
+ regs->ip, regs->sp, error_code);
+ print_vma_addr(" in ", regs->ip);
+ printk("\n");
+ }
+#endif
+
+ if (info)
+ force_sig_info(signr, info, tsk);
+ else
+ force_sig(signr, tsk);
+ return;
+
+kernel_trap:
+ if (!fixup_exception(regs)) {
+ tsk->thread.error_code = error_code;
+ tsk->thread.trap_no = trapnr;
+ die(str, regs, error_code);
+ }
+ return;
+
+#ifdef CONFIG_X86_32
+vm86_trap:
+ if (handle_vm86_trap((struct kernel_vm86_regs *) regs,
+ error_code, trapnr))
+ goto trap_signal;
+ return;
+#endif
+}
+
+#define DO_ERROR(trapnr, signr, str, name) \
+dotraplinkage void do_##name(struct pt_regs *regs, long error_code) \
+{ \
+ if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \
+ == NOTIFY_STOP) \
+ return; \
+ conditional_sti(regs); \
+ do_trap(trapnr, signr, str, regs, error_code, NULL); \
+}
+
+#define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \
+dotraplinkage void do_##name(struct pt_regs *regs, long error_code) \
+{ \
+ siginfo_t info; \
+ info.si_signo = signr; \
+ info.si_errno = 0; \
+ info.si_code = sicode; \
+ info.si_addr = (void __user *)siaddr; \
+ if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \
+ == NOTIFY_STOP) \
+ return; \
+ conditional_sti(regs); \
+ do_trap(trapnr, signr, str, regs, error_code, &info); \
+}
+
+DO_ERROR_INFO(0, SIGFPE, "divide error", divide_error, FPE_INTDIV, regs->ip)
+DO_ERROR(4, SIGSEGV, "overflow", overflow)
+DO_ERROR(5, SIGSEGV, "bounds", bounds)
+DO_ERROR_INFO(6, SIGILL, "invalid opcode", invalid_op, ILL_ILLOPN, regs->ip)
+DO_ERROR(9, SIGFPE, "coprocessor segment overrun", coprocessor_segment_overrun)
+DO_ERROR(10, SIGSEGV, "invalid TSS", invalid_TSS)
+DO_ERROR(11, SIGBUS, "segment not present", segment_not_present)
+#ifdef CONFIG_X86_32
+DO_ERROR(12, SIGBUS, "stack segment", stack_segment)
+#endif
+DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, 0)
+
+#ifdef CONFIG_X86_64
+/* Runs on IST stack */
+dotraplinkage void do_stack_segment(struct pt_regs *regs, long error_code)
+{
+ if (notify_die(DIE_TRAP, "stack segment", regs, error_code,
+ 12, SIGBUS) == NOTIFY_STOP)
+ return;
+ preempt_conditional_sti(regs);
+ do_trap(12, SIGBUS, "stack segment", regs, error_code, NULL);
+ preempt_conditional_cli(regs);
+}
+
+dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code)
+{
+ static const char str[] = "double fault";
+ struct task_struct *tsk = current;
+
+ /* Return not checked because double check cannot be ignored */
+ notify_die(DIE_TRAP, str, regs, error_code, 8, SIGSEGV);
+
+ tsk->thread.error_code = error_code;
+ tsk->thread.trap_no = 8;
+
+ /* This is always a kernel trap and never fixable (and thus must
+ never return). */
+ for (;;)
+ die(str, regs, error_code);
+}
+#endif
+
+dotraplinkage void __kprobes
+do_general_protection(struct pt_regs *regs, long error_code)
+{
+ struct task_struct *tsk;
+
+ conditional_sti(regs);
+
+#ifdef CONFIG_X86_32
+ if (lazy_iobitmap_copy()) {
+ /* restart the faulting instruction */
+ return;
+ }
+
+ if (regs->flags & X86_VM_MASK)
+ goto gp_in_vm86;
+#endif
+
+ tsk = current;
+ if (!user_mode(regs))
+ goto gp_in_kernel;
+
+ tsk->thread.error_code = error_code;
+ tsk->thread.trap_no = 13;
+
+ if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV) &&
+ printk_ratelimit()) {
+ printk(KERN_INFO
+ "%s[%d] general protection ip:%lx sp:%lx error:%lx",
+ tsk->comm, task_pid_nr(tsk),
+ regs->ip, regs->sp, error_code);
+ print_vma_addr(" in ", regs->ip);
+ printk("\n");
+ }
+
+ force_sig(SIGSEGV, tsk);
+ return;
+
+#ifdef CONFIG_X86_32
+gp_in_vm86:
+ local_irq_enable();
+ handle_vm86_fault((struct kernel_vm86_regs *) regs, error_code);
+ return;
+#endif
+
+gp_in_kernel:
+ if (fixup_exception(regs))
+ return;
+
+ tsk->thread.error_code = error_code;
+ tsk->thread.trap_no = 13;
+ if (notify_die(DIE_GPF, "general protection fault", regs,
+ error_code, 13, SIGSEGV) == NOTIFY_STOP)
+ return;
+ die("general protection fault", regs, error_code);
+}
+
+static notrace __kprobes void
+mem_parity_error(unsigned char reason, struct pt_regs *regs)
+{
+ printk(KERN_EMERG
+ "Uhhuh. NMI received for unknown reason %02x on CPU %d.\n",
+ reason, smp_processor_id());
+
+ printk(KERN_EMERG
+ "You have some hardware problem, likely on the PCI bus.\n");
+
+#if defined(CONFIG_EDAC)
+ if (edac_handler_set()) {
+ edac_atomic_assert_error();
+ return;
+ }
+#endif
+
+ if (panic_on_unrecovered_nmi)
+ panic("NMI: Not continuing");
+
+ printk(KERN_EMERG "Dazed and confused, but trying to continue\n");
+
+ /* Clear and disable the memory parity error line. */
+ reason = (reason & 0xf) | 4;
+ outb(reason, 0x61);
+}
+
+static notrace __kprobes void
+io_check_error(unsigned char reason, struct pt_regs *regs)
+{
+ unsigned long i;
+
+ printk(KERN_EMERG "NMI: IOCK error (debug interrupt?)\n");
+ show_registers(regs);
+
+ /* Re-enable the IOCK line, wait for a few seconds */
+ reason = (reason & 0xf) | 8;
+ outb(reason, 0x61);
+
+ i = 2000;
+ while (--i)
+ udelay(1000);
+
+ reason &= ~8;
+ outb(reason, 0x61);
+}
+
+static notrace __kprobes void
+unknown_nmi_error(unsigned char reason, struct pt_regs *regs)
+{
+ if (notify_die(DIE_NMIUNKNOWN, "nmi", regs, reason, 2, SIGINT) ==
+ NOTIFY_STOP)
+ return;
+#ifdef CONFIG_MCA
+ /*
+ * Might actually be able to figure out what the guilty party
+ * is:
+ */
+ if (MCA_bus) {
+ mca_handle_nmi();
+ return;
+ }
+#endif
+ printk(KERN_EMERG
+ "Uhhuh. NMI received for unknown reason %02x on CPU %d.\n",
+ reason, smp_processor_id());
+
+ printk(KERN_EMERG "Do you have a strange power saving mode enabled?\n");
+ if (panic_on_unrecovered_nmi)
+ panic("NMI: Not continuing");
+
+ printk(KERN_EMERG "Dazed and confused, but trying to continue\n");
+}
+
+static notrace __kprobes void default_do_nmi(struct pt_regs *regs)
+{
+ unsigned char reason = 0;
+ int cpu;
+
+ cpu = smp_processor_id();
+
+ /* Only the BSP gets external NMIs from the system. */
+ if (!cpu)
+ reason = get_nmi_reason();
+
+ if (!(reason & 0xc0)) {
+ if (notify_die(DIE_NMI_IPI, "nmi_ipi", regs, reason, 2, SIGINT)
+ == NOTIFY_STOP)
+ return;
+#ifdef CONFIG_X86_LOCAL_APIC
+ /*
+ * Ok, so this is none of the documented NMI sources,
+ * so it must be the NMI watchdog.
+ */
+ if (nmi_watchdog_tick(regs, reason))
+ return;
+ if (!do_nmi_callback(regs, cpu))
+ unknown_nmi_error(reason, regs);
+#else
+ unknown_nmi_error(reason, regs);
+#endif
+
+ return;
+ }
+ if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP)
+ return;
+
+ /* AK: following checks seem to be broken on modern chipsets. FIXME */
+ if (reason & 0x80)
+ mem_parity_error(reason, regs);
+ if (reason & 0x40)
+ io_check_error(reason, regs);
+#ifdef CONFIG_X86_32
+ /*
+ * Reassert NMI in case it became active meanwhile
+ * as it's edge-triggered:
+ */
+ reassert_nmi();
+#endif
+}
+
+dotraplinkage notrace __kprobes void
+do_nmi(struct pt_regs *regs, long error_code)
+{
+ nmi_enter();
+
+#ifdef CONFIG_X86_32
+ { int cpu; cpu = smp_processor_id(); ++nmi_count(cpu); }
+#else
+ add_pda(__nmi_count, 1);
+#endif
+
+ if (!ignore_nmis)
+ default_do_nmi(regs);
+
+ nmi_exit();
+}
+
+void stop_nmi(void)
+{
+ acpi_nmi_disable();
+ ignore_nmis++;
+}
+
+void restart_nmi(void)
+{
+ ignore_nmis--;
+ acpi_nmi_enable();
+}
+
+/* May run on IST stack. */
+dotraplinkage void __kprobes do_int3(struct pt_regs *regs, long error_code)
+{
+#ifdef CONFIG_KPROBES
+ if (notify_die(DIE_INT3, "int3", regs, error_code, 3, SIGTRAP)
+ == NOTIFY_STOP)
+ return;
+#else
+ if (notify_die(DIE_TRAP, "int3", regs, error_code, 3, SIGTRAP)
+ == NOTIFY_STOP)
+ return;
+#endif
+
+ preempt_conditional_sti(regs);
+ do_trap(3, SIGTRAP, "int3", regs, error_code, NULL);
+ preempt_conditional_cli(regs);
+}
+
+#ifdef CONFIG_X86_64
+/* Help handler running on IST stack to switch back to user stack
+ for scheduling or signal handling. The actual stack switch is done in
+ entry.S */
+asmlinkage __kprobes struct pt_regs *sync_regs(struct pt_regs *eregs)
+{
+ struct pt_regs *regs = eregs;
+ /* Did already sync */
+ if (eregs == (struct pt_regs *)eregs->sp)
+ ;
+ /* Exception from user space */
+ else if (user_mode(eregs))
+ regs = task_pt_regs(current);
+ /* Exception from kernel and interrupts are enabled. Move to
+ kernel process stack. */
+ else if (eregs->flags & X86_EFLAGS_IF)
+ regs = (struct pt_regs *)(eregs->sp -= sizeof(struct pt_regs));
+ if (eregs != regs)
+ *regs = *eregs;
+ return regs;
+}
+#endif
+
+/*
+ * Our handling of the processor debug registers is non-trivial.
+ * We do not clear them on entry and exit from the kernel. Therefore
+ * it is possible to get a watchpoint trap here from inside the kernel.
+ * However, the code in ./ptrace.c has ensured that the user can
+ * only set watchpoints on userspace addresses. Therefore the in-kernel
+ * watchpoint trap can only occur in code which is reading/writing
+ * from user space. Such code must not hold kernel locks (since it
+ * can equally take a page fault), therefore it is safe to call
+ * force_sig_info even though that claims and releases locks.
+ *
+ * Code in ./signal.c ensures that the debug control register
+ * is restored before we deliver any signal, and therefore that
+ * user code runs with the correct debug control register even though
+ * we clear it here.
+ *
+ * Being careful here means that we don't have to be as careful in a
+ * lot of more complicated places (task switching can be a bit lazy
+ * about restoring all the debug state, and ptrace doesn't have to
+ * find every occurrence of the TF bit that could be saved away even
+ * by user code)
+ *
+ * May run on IST stack.
+ */
+dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)
+{
+ struct task_struct *tsk = current;
+ unsigned long condition;
+ int si_code;
+
+ get_debugreg(condition, 6);
+
+ /*
+ * The processor cleared BTF, so don't mark that we need it set.
+ */
+ clear_tsk_thread_flag(tsk, TIF_DEBUGCTLMSR);
+ tsk->thread.debugctlmsr = 0;
+
+ if (notify_die(DIE_DEBUG, "debug", regs, condition, error_code,
+ SIGTRAP) == NOTIFY_STOP)
+ return;
+
+ /* It's safe to allow irq's after DR6 has been saved */
+ preempt_conditional_sti(regs);
+
+ /* Mask out spurious debug traps due to lazy DR7 setting */
+ if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) {
+ if (!tsk->thread.debugreg7)
+ goto clear_dr7;
+ }
+
+#ifdef CONFIG_X86_32
+ if (regs->flags & X86_VM_MASK)
+ goto debug_vm86;
+#endif
+
+ /* Save debug status register where ptrace can see it */
+ tsk->thread.debugreg6 = condition;
+
+ /*
+ * Single-stepping through TF: make sure we ignore any events in
+ * kernel space (but re-enable TF when returning to user mode).
+ */
+ if (condition & DR_STEP) {
+ if (!user_mode(regs))
+ goto clear_TF_reenable;
+ }
+
+ si_code = get_si_code(condition);
+ /* Ok, finally something we can handle */
+ send_sigtrap(tsk, regs, error_code, si_code);
+
+ /*
+ * Disable additional traps. They'll be re-enabled when
+ * the signal is delivered.
+ */
+clear_dr7:
+ set_debugreg(0, 7);
+ preempt_conditional_cli(regs);
+ return;
+
+#ifdef CONFIG_X86_32
+debug_vm86:
+ handle_vm86_trap((struct kernel_vm86_regs *) regs, error_code, 1);
+ preempt_conditional_cli(regs);
+ return;
+#endif
+
+clear_TF_reenable:
+ set_tsk_thread_flag(tsk, TIF_SINGLESTEP);
+ regs->flags &= ~X86_EFLAGS_TF;
+ preempt_conditional_cli(regs);
+ return;
+}
+
+#ifdef CONFIG_X86_64
+static int kernel_math_error(struct pt_regs *regs, const char *str, int trapnr)
+{
+ if (fixup_exception(regs))
+ return 1;
+
+ notify_die(DIE_GPF, str, regs, 0, trapnr, SIGFPE);
+ /* Illegal floating point operation in the kernel */
+ current->thread.trap_no = trapnr;
+ die(str, regs, 0);
+ return 0;
+}
+#endif
+
+/*
+ * Note that we play around with the 'TS' bit in an attempt to get
+ * the correct behaviour even in the presence of the asynchronous
+ * IRQ13 behaviour
+ */
+void math_error(void __user *ip)
+{
+ struct task_struct *task;
+ siginfo_t info;
+ unsigned short cwd, swd;
+
+ /*
+ * Save the info for the exception handler and clear the error.
+ */
+ task = current;
+ save_init_fpu(task);
+ task->thread.trap_no = 16;
+ task->thread.error_code = 0;
+ info.si_signo = SIGFPE;
+ info.si_errno = 0;
+ info.si_code = __SI_FAULT;
+ info.si_addr = ip;
+ /*
+ * (~cwd & swd) will mask out exceptions that are not set to unmasked
+ * status. 0x3f is the exception bits in these regs, 0x200 is the
+ * C1 reg you need in case of a stack fault, 0x040 is the stack
+ * fault bit. We should only be taking one exception at a time,
+ * so if this combination doesn't produce any single exception,
+ * then we have a bad program that isn't synchronizing its FPU usage
+ * and it will suffer the consequences since we won't be able to
+ * fully reproduce the context of the exception
+ */
+ cwd = get_fpu_cwd(task);
+ swd = get_fpu_swd(task);
+ switch (swd & ~cwd & 0x3f) {
+ case 0x000: /* No unmasked exception */
+#ifdef CONFIG_X86_32
+ return;
+#endif
+ default: /* Multiple exceptions */
+ break;
+ case 0x001: /* Invalid Op */
+ /*
+ * swd & 0x240 == 0x040: Stack Underflow
+ * swd & 0x240 == 0x240: Stack Overflow
+ * User must clear the SF bit (0x40) if set
+ */
+ info.si_code = FPE_FLTINV;
+ break;
+ case 0x002: /* Denormalize */
+ case 0x010: /* Underflow */
+ info.si_code = FPE_FLTUND;
+ break;
+ case 0x004: /* Zero Divide */
+ info.si_code = FPE_FLTDIV;
+ break;
+ case 0x008: /* Overflow */
+ info.si_code = FPE_FLTOVF;
+ break;
+ case 0x020: /* Precision */
+ info.si_code = FPE_FLTRES;
+ break;
+ }
+ force_sig_info(SIGFPE, &info, task);
+}
+
+dotraplinkage void do_coprocessor_error(struct pt_regs *regs, long error_code)
+{
+ conditional_sti(regs);
+
+#ifdef CONFIG_X86_32
+ ignore_fpu_irq = 1;
+#else
+ if (!user_mode(regs) &&
+ kernel_math_error(regs, "kernel x87 math error", 16))
+ return;
+#endif
+
+ math_error((void __user *)regs->ip);
+}
+
+static void simd_math_error(void __user *ip)
+{
+ struct task_struct *task;
+ siginfo_t info;
+ unsigned short mxcsr;
+
+ /*
+ * Save the info for the exception handler and clear the error.
+ */
+ task = current;
+ save_init_fpu(task);
+ task->thread.trap_no = 19;
+ task->thread.error_code = 0;
+ info.si_signo = SIGFPE;
+ info.si_errno = 0;
+ info.si_code = __SI_FAULT;
+ info.si_addr = ip;
+ /*
+ * The SIMD FPU exceptions are handled a little differently, as there
+ * is only a single status/control register. Thus, to determine which
+ * unmasked exception was caught we must mask the exception mask bits
+ * at 0x1f80, and then use these to mask the exception bits at 0x3f.
+ */
+ mxcsr = get_fpu_mxcsr(task);
+ switch (~((mxcsr & 0x1f80) >> 7) & (mxcsr & 0x3f)) {
+ case 0x000:
+ default:
+ break;
+ case 0x001: /* Invalid Op */
+ info.si_code = FPE_FLTINV;
+ break;
+ case 0x002: /* Denormalize */
+ case 0x010: /* Underflow */
+ info.si_code = FPE_FLTUND;
+ break;
+ case 0x004: /* Zero Divide */
+ info.si_code = FPE_FLTDIV;
+ break;
+ case 0x008: /* Overflow */
+ info.si_code = FPE_FLTOVF;
+ break;
+ case 0x020: /* Precision */
+ info.si_code = FPE_FLTRES;
+ break;
+ }
+ force_sig_info(SIGFPE, &info, task);
+}
+
+dotraplinkage void
+do_simd_coprocessor_error(struct pt_regs *regs, long error_code)
+{
+ conditional_sti(regs);
+
+#ifdef CONFIG_X86_32
+ if (cpu_has_xmm) {
+ /* Handle SIMD FPU exceptions on PIII+ processors. */
+ ignore_fpu_irq = 1;
+ simd_math_error((void __user *)regs->ip);
+ return;
+ }
+ /*
+ * Handle strange cache flush from user space exception
+ * in all other cases. This is undocumented behaviour.
+ */
+ if (regs->flags & X86_VM_MASK) {
+ handle_vm86_fault((struct kernel_vm86_regs *)regs, error_code);
+ return;
+ }
+ current->thread.trap_no = 19;
+ current->thread.error_code = error_code;
+ die_if_kernel("cache flush denied", regs, error_code);
+ force_sig(SIGSEGV, current);
+#else
+ if (!user_mode(regs) &&
+ kernel_math_error(regs, "kernel simd math error", 19))
+ return;
+ simd_math_error((void __user *)regs->ip);
+#endif
+}
+
+dotraplinkage void
+do_spurious_interrupt_bug(struct pt_regs *regs, long error_code)
+{
+ conditional_sti(regs);
+#if 0
+ /* No need to warn about this any longer. */
+ printk(KERN_INFO "Ignoring P6 Local APIC Spurious Interrupt Bug...\n");
+#endif
+}
+
+#ifdef CONFIG_X86_32
+unsigned long patch_espfix_desc(unsigned long uesp, unsigned long kesp)
+{
+ struct desc_struct *gdt = get_cpu_gdt_table(smp_processor_id());
+ unsigned long base = (kesp - uesp) & -THREAD_SIZE;
+ unsigned long new_kesp = kesp - base;
+ unsigned long lim_pages = (new_kesp | (THREAD_SIZE - 1)) >> PAGE_SHIFT;
+ __u64 desc = *(__u64 *)&gdt[GDT_ENTRY_ESPFIX_SS];
+
+ /* Set up base for espfix segment */
+ desc &= 0x00f0ff0000000000ULL;
+ desc |= ((((__u64)base) << 16) & 0x000000ffffff0000ULL) |
+ ((((__u64)base) << 32) & 0xff00000000000000ULL) |
+ ((((__u64)lim_pages) << 32) & 0x000f000000000000ULL) |
+ (lim_pages & 0xffff);
+ *(__u64 *)&gdt[GDT_ENTRY_ESPFIX_SS] = desc;
+
+ return new_kesp;
+}
+#else
+asmlinkage void __attribute__((weak)) smp_thermal_interrupt(void)
+{
+}
+
+asmlinkage void __attribute__((weak)) mce_threshold_interrupt(void)
+{
+}
+#endif
+
+/*
+ * 'math_state_restore()' saves the current math information in the
+ * old math state array, and gets the new ones from the current task
+ *
+ * Careful.. There are problems with IBM-designed IRQ13 behaviour.
+ * Don't touch unless you *really* know how it works.
+ *
+ * Must be called with kernel preemption disabled (in this case,
+ * local interrupts are disabled at the call-site in entry.S).
+ */
+asmlinkage void math_state_restore(void)
+{
+ struct thread_info *thread = current_thread_info();
+ struct task_struct *tsk = thread->task;
+
+ if (!tsk_used_math(tsk)) {
+ local_irq_enable();
+ /*
+ * does a slab alloc which can sleep
+ */
+ if (init_fpu(tsk)) {
+ /*
+ * ran out of memory!
+ */
+ do_group_exit(SIGKILL);
+ return;
+ }
+ local_irq_disable();
+ }
+
+ clts(); /* Allow maths ops (or we recurse) */
+#ifdef CONFIG_X86_32
+ restore_fpu(tsk);
+#else
+ /*
+ * Paranoid restore. send a SIGSEGV if we fail to restore the state.
+ */
+ if (unlikely(restore_fpu_checking(tsk))) {
+ stts();
+ force_sig(SIGSEGV, tsk);
+ return;
+ }
+#endif
+ thread->status |= TS_USEDFPU; /* So we fnsave on switch_to() */
+ tsk->fpu_counter++;
+}
+EXPORT_SYMBOL_GPL(math_state_restore);
+
+#ifndef CONFIG_MATH_EMULATION
+asmlinkage void math_emulate(long arg)
+{
+ printk(KERN_EMERG
+ "math-emulation not enabled and no coprocessor found.\n");
+ printk(KERN_EMERG "killing %s.\n", current->comm);
+ force_sig(SIGFPE, current);
+ schedule();
+}
+#endif /* CONFIG_MATH_EMULATION */
+
+dotraplinkage void __kprobes
+do_device_not_available(struct pt_regs *regs, long error)
+{
+#ifdef CONFIG_X86_32
+ if (read_cr0() & X86_CR0_EM) {
+ conditional_sti(regs);
+ math_emulate(0);
+ } else {
+ math_state_restore(); /* interrupts still off */
+ conditional_sti(regs);
+ }
+#else
+ math_state_restore();
+#endif
+}
+
+#ifdef CONFIG_X86_32
+#ifdef CONFIG_X86_MCE
+dotraplinkage void __kprobes do_machine_check(struct pt_regs *regs, long error)
+{
+ conditional_sti(regs);
+ machine_check_vector(regs, error);
+}
+#endif
+
+dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code)
+{
+ siginfo_t info;
+ local_irq_enable();
+
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_code = ILL_BADSTK;
+ info.si_addr = 0;
+ if (notify_die(DIE_TRAP, "iret exception",
+ regs, error_code, 32, SIGILL) == NOTIFY_STOP)
+ return;
+ do_trap(32, SIGILL, "iret exception", regs, error_code, &info);
+}
+#endif
+
+void __init trap_init(void)
+{
+#ifdef CONFIG_X86_32
+ int i;
+#endif
+
+#ifdef CONFIG_EISA
+ void __iomem *p = early_ioremap(0x0FFFD9, 4);
+
+ if (readl(p) == 'E' + ('I'<<8) + ('S'<<16) + ('A'<<24))
+ EISA_bus = 1;
+ early_iounmap(p, 4);
+#endif
+
+ set_intr_gate(0, ÷_error);
+ set_intr_gate_ist(1, &debug, DEBUG_STACK);
+ set_intr_gate_ist(2, &nmi, NMI_STACK);
+ /* int3 can be called from all */
+ set_system_intr_gate_ist(3, &int3, DEBUG_STACK);
+ /* int4 can be called from all */
+ set_system_intr_gate(4, &overflow);
+ set_intr_gate(5, &bounds);
+ set_intr_gate(6, &invalid_op);
+ set_intr_gate(7, &device_not_available);
+#ifdef CONFIG_X86_32
+ set_task_gate(8, GDT_ENTRY_DOUBLEFAULT_TSS);
+#else
+ set_intr_gate_ist(8, &double_fault, DOUBLEFAULT_STACK);
+#endif
+ set_intr_gate(9, &coprocessor_segment_overrun);
+ set_intr_gate(10, &invalid_TSS);
+ set_intr_gate(11, &segment_not_present);
+ set_intr_gate_ist(12, &stack_segment, STACKFAULT_STACK);
+ set_intr_gate(13, &general_protection);
+ set_intr_gate(14, &page_fault);
+ set_intr_gate(15, &spurious_interrupt_bug);
+ set_intr_gate(16, &coprocessor_error);
+ set_intr_gate(17, &alignment_check);
+#ifdef CONFIG_X86_MCE
+ set_intr_gate_ist(18, &machine_check, MCE_STACK);
+#endif
+ set_intr_gate(19, &simd_coprocessor_error);
+
+#ifdef CONFIG_IA32_EMULATION
+ set_system_intr_gate(IA32_SYSCALL_VECTOR, ia32_syscall);
+#endif
+
+#ifdef CONFIG_X86_32
+ if (cpu_has_fxsr) {
+ printk(KERN_INFO "Enabling fast FPU save and restore... ");
+ set_in_cr4(X86_CR4_OSFXSR);
+ printk("done.\n");
+ }
+ if (cpu_has_xmm) {
+ printk(KERN_INFO
+ "Enabling unmasked SIMD FPU exception support... ");
+ set_in_cr4(X86_CR4_OSXMMEXCPT);
+ printk("done.\n");
+ }
+
+ set_system_trap_gate(SYSCALL_VECTOR, &system_call);
+
+ /* Reserve all the builtin and the syscall vector: */
+ for (i = 0; i < FIRST_EXTERNAL_VECTOR; i++)
+ set_bit(i, used_vectors);
+
+ set_bit(SYSCALL_VECTOR, used_vectors);
+#endif
+ /*
+ * Should be a barrier for any external CPU state:
+ */
+ cpu_init();
+
+#ifdef CONFIG_X86_32
+ trap_init_hook();
+#endif
+}
diff --git a/arch/x86/kernel/traps_32.c b/arch/x86/kernel/traps_32.c
deleted file mode 100644
index 03df8e4..0000000
--- a/arch/x86/kernel/traps_32.c
+++ /dev/null
@@ -1,1256 +0,0 @@
-/*
- * Copyright (C) 1991, 1992 Linus Torvalds
- * Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs
- *
- * Pentium III FXSR, SSE support
- * Gareth Hughes <gareth@valinux.com>, May 2000
- */
-
-/*
- * 'Traps.c' handles hardware traps and faults after we have saved some
- * state in 'asm.s'.
- */
-#include <linux/interrupt.h>
-#include <linux/kallsyms.h>
-#include <linux/spinlock.h>
-#include <linux/highmem.h>
-#include <linux/kprobes.h>
-#include <linux/uaccess.h>
-#include <linux/utsname.h>
-#include <linux/kdebug.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/ptrace.h>
-#include <linux/string.h>
-#include <linux/unwind.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/kexec.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/init.h>
-#include <linux/bug.h>
-#include <linux/nmi.h>
-#include <linux/mm.h>
-
-#ifdef CONFIG_EISA
-#include <linux/ioport.h>
-#include <linux/eisa.h>
-#endif
-
-#ifdef CONFIG_MCA
-#include <linux/mca.h>
-#endif
-
-#if defined(CONFIG_EDAC)
-#include <linux/edac.h>
-#endif
-
-#include <asm/arch_hooks.h>
-#include <asm/stacktrace.h>
-#include <asm/processor.h>
-#include <asm/debugreg.h>
-#include <asm/atomic.h>
-#include <asm/system.h>
-#include <asm/unwind.h>
-#include <asm/desc.h>
-#include <asm/i387.h>
-#include <asm/nmi.h>
-#include <asm/smp.h>
-#include <asm/io.h>
-#include <asm/traps.h>
-
-#include "mach_traps.h"
-
-DECLARE_BITMAP(used_vectors, NR_VECTORS);
-EXPORT_SYMBOL_GPL(used_vectors);
-
-asmlinkage int system_call(void);
-
-/* Do we ignore FPU interrupts ? */
-char ignore_fpu_irq;
-
-/*
- * The IDT has to be page-aligned to simplify the Pentium
- * F0 0F bug workaround.. We have a special link segment
- * for this.
- */
-gate_desc idt_table[256]
- __attribute__((__section__(".data.idt"))) = { { { { 0, 0 } } }, };
-
-int panic_on_unrecovered_nmi;
-int kstack_depth_to_print = 24;
-static unsigned int code_bytes = 64;
-static int ignore_nmis;
-static int die_counter;
-
-void printk_address(unsigned long address, int reliable)
-{
-#ifdef CONFIG_KALLSYMS
- unsigned long offset = 0;
- unsigned long symsize;
- const char *symname;
- char *modname;
- char *delim = ":";
- char namebuf[KSYM_NAME_LEN];
- char reliab[4] = "";
-
- symname = kallsyms_lookup(address, &symsize, &offset,
- &modname, namebuf);
- if (!symname) {
- printk(" [<%08lx>]\n", address);
- return;
- }
- if (!reliable)
- strcpy(reliab, "? ");
-
- if (!modname)
- modname = delim = "";
- printk(" [<%08lx>] %s%s%s%s%s+0x%lx/0x%lx\n",
- address, reliab, delim, modname, delim, symname, offset, symsize);
-#else
- printk(" [<%08lx>]\n", address);
-#endif
-}
-
-static inline int valid_stack_ptr(struct thread_info *tinfo,
- void *p, unsigned int size)
-{
- void *t = tinfo;
- return p > t && p <= t + THREAD_SIZE - size;
-}
-
-/* The form of the top of the frame on the stack */
-struct stack_frame {
- struct stack_frame *next_frame;
- unsigned long return_address;
-};
-
-static inline unsigned long
-print_context_stack(struct thread_info *tinfo,
- unsigned long *stack, unsigned long bp,
- const struct stacktrace_ops *ops, void *data)
-{
- struct stack_frame *frame = (struct stack_frame *)bp;
-
- while (valid_stack_ptr(tinfo, stack, sizeof(*stack))) {
- unsigned long addr;
-
- addr = *stack;
- if (__kernel_text_address(addr)) {
- if ((unsigned long) stack == bp + 4) {
- ops->address(data, addr, 1);
- frame = frame->next_frame;
- bp = (unsigned long) frame;
- } else {
- ops->address(data, addr, bp == 0);
- }
- }
- stack++;
- }
- return bp;
-}
-
-void dump_trace(struct task_struct *task, struct pt_regs *regs,
- unsigned long *stack, unsigned long bp,
- const struct stacktrace_ops *ops, void *data)
-{
- if (!task)
- task = current;
-
- if (!stack) {
- unsigned long dummy;
- stack = &dummy;
- if (task != current)
- stack = (unsigned long *)task->thread.sp;
- }
-
-#ifdef CONFIG_FRAME_POINTER
- if (!bp) {
- if (task == current) {
- /* Grab bp right from our regs */
- asm("movl %%ebp, %0" : "=r" (bp) :);
- } else {
- /* bp is the last reg pushed by switch_to */
- bp = *(unsigned long *) task->thread.sp;
- }
- }
-#endif
-
- for (;;) {
- struct thread_info *context;
-
- context = (struct thread_info *)
- ((unsigned long)stack & (~(THREAD_SIZE - 1)));
- bp = print_context_stack(context, stack, bp, ops, data);
- /*
- * Should be after the line below, but somewhere
- * in early boot context comes out corrupted and we
- * can't reference it:
- */
- if (ops->stack(data, "IRQ") < 0)
- break;
- stack = (unsigned long *)context->previous_esp;
- if (!stack)
- break;
- touch_nmi_watchdog();
- }
-}
-EXPORT_SYMBOL(dump_trace);
-
-static void
-print_trace_warning_symbol(void *data, char *msg, unsigned long symbol)
-{
- printk(data);
- print_symbol(msg, symbol);
- printk("\n");
-}
-
-static void print_trace_warning(void *data, char *msg)
-{
- printk("%s%s\n", (char *)data, msg);
-}
-
-static int print_trace_stack(void *data, char *name)
-{
- return 0;
-}
-
-/*
- * Print one address/symbol entries per line.
- */
-static void print_trace_address(void *data, unsigned long addr, int reliable)
-{
- printk("%s [<%08lx>] ", (char *)data, addr);
- if (!reliable)
- printk("? ");
- print_symbol("%s\n", addr);
- touch_nmi_watchdog();
-}
-
-static const struct stacktrace_ops print_trace_ops = {
- .warning = print_trace_warning,
- .warning_symbol = print_trace_warning_symbol,
- .stack = print_trace_stack,
- .address = print_trace_address,
-};
-
-static void
-show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs,
- unsigned long *stack, unsigned long bp, char *log_lvl)
-{
- dump_trace(task, regs, stack, bp, &print_trace_ops, log_lvl);
- printk("%s =======================\n", log_lvl);
-}
-
-void show_trace(struct task_struct *task, struct pt_regs *regs,
- unsigned long *stack, unsigned long bp)
-{
- show_trace_log_lvl(task, regs, stack, bp, "");
-}
-
-static void
-show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs,
- unsigned long *sp, unsigned long bp, char *log_lvl)
-{
- unsigned long *stack;
- int i;
-
- if (sp == NULL) {
- if (task)
- sp = (unsigned long *)task->thread.sp;
- else
- sp = (unsigned long *)&sp;
- }
-
- stack = sp;
- for (i = 0; i < kstack_depth_to_print; i++) {
- if (kstack_end(stack))
- break;
- if (i && ((i % 8) == 0))
- printk("\n%s ", log_lvl);
- printk("%08lx ", *stack++);
- }
- printk("\n%sCall Trace:\n", log_lvl);
-
- show_trace_log_lvl(task, regs, sp, bp, log_lvl);
-}
-
-void show_stack(struct task_struct *task, unsigned long *sp)
-{
- printk(" ");
- show_stack_log_lvl(task, NULL, sp, 0, "");
-}
-
-/*
- * The architecture-independent dump_stack generator
- */
-void dump_stack(void)
-{
- unsigned long bp = 0;
- unsigned long stack;
-
-#ifdef CONFIG_FRAME_POINTER
- if (!bp)
- asm("movl %%ebp, %0" : "=r" (bp):);
-#endif
-
- printk("Pid: %d, comm: %.20s %s %s %.*s\n",
- current->pid, current->comm, print_tainted(),
- init_utsname()->release,
- (int)strcspn(init_utsname()->version, " "),
- init_utsname()->version);
-
- show_trace(current, NULL, &stack, bp);
-}
-
-EXPORT_SYMBOL(dump_stack);
-
-void show_registers(struct pt_regs *regs)
-{
- int i;
-
- print_modules();
- __show_registers(regs, 0);
-
- printk(KERN_EMERG "Process %.*s (pid: %d, ti=%p task=%p task.ti=%p)",
- TASK_COMM_LEN, current->comm, task_pid_nr(current),
- current_thread_info(), current, task_thread_info(current));
- /*
- * When in-kernel, we also print out the stack and code at the
- * time of the fault..
- */
- if (!user_mode_vm(regs)) {
- unsigned int code_prologue = code_bytes * 43 / 64;
- unsigned int code_len = code_bytes;
- unsigned char c;
- u8 *ip;
-
- printk("\n" KERN_EMERG "Stack: ");
- show_stack_log_lvl(NULL, regs, ®s->sp, 0, KERN_EMERG);
-
- printk(KERN_EMERG "Code: ");
-
- ip = (u8 *)regs->ip - code_prologue;
- if (ip < (u8 *)PAGE_OFFSET || probe_kernel_address(ip, c)) {
- /* try starting at EIP */
- ip = (u8 *)regs->ip;
- code_len = code_len - code_prologue + 1;
- }
- for (i = 0; i < code_len; i++, ip++) {
- if (ip < (u8 *)PAGE_OFFSET ||
- probe_kernel_address(ip, c)) {
- printk(" Bad EIP value.");
- break;
- }
- if (ip == (u8 *)regs->ip)
- printk("<%02x> ", c);
- else
- printk("%02x ", c);
- }
- }
- printk("\n");
-}
-
-int is_valid_bugaddr(unsigned long ip)
-{
- unsigned short ud2;
-
- if (ip < PAGE_OFFSET)
- return 0;
- if (probe_kernel_address((unsigned short *)ip, ud2))
- return 0;
-
- return ud2 == 0x0b0f;
-}
-
-static raw_spinlock_t die_lock = __RAW_SPIN_LOCK_UNLOCKED;
-static int die_owner = -1;
-static unsigned int die_nest_count;
-
-unsigned __kprobes long oops_begin(void)
-{
- unsigned long flags;
-
- oops_enter();
-
- if (die_owner != raw_smp_processor_id()) {
- console_verbose();
- raw_local_irq_save(flags);
- __raw_spin_lock(&die_lock);
- die_owner = smp_processor_id();
- die_nest_count = 0;
- bust_spinlocks(1);
- } else {
- raw_local_irq_save(flags);
- }
- die_nest_count++;
- return flags;
-}
-
-void __kprobes oops_end(unsigned long flags, struct pt_regs *regs, int signr)
-{
- bust_spinlocks(0);
- die_owner = -1;
- add_taint(TAINT_DIE);
- __raw_spin_unlock(&die_lock);
- raw_local_irq_restore(flags);
-
- if (!regs)
- return;
-
- if (kexec_should_crash(current))
- crash_kexec(regs);
-
- if (in_interrupt())
- panic("Fatal exception in interrupt");
-
- if (panic_on_oops)
- panic("Fatal exception");
-
- oops_exit();
- do_exit(signr);
-}
-
-int __kprobes __die(const char *str, struct pt_regs *regs, long err)
-{
- unsigned short ss;
- unsigned long sp;
-
- printk(KERN_EMERG "%s: %04lx [#%d] ", str, err & 0xffff, ++die_counter);
-#ifdef CONFIG_PREEMPT
- printk("PREEMPT ");
-#endif
-#ifdef CONFIG_SMP
- printk("SMP ");
-#endif
-#ifdef CONFIG_DEBUG_PAGEALLOC
- printk("DEBUG_PAGEALLOC");
-#endif
- printk("\n");
- if (notify_die(DIE_OOPS, str, regs, err,
- current->thread.trap_no, SIGSEGV) == NOTIFY_STOP)
- return 1;
-
- show_registers(regs);
- /* Executive summary in case the oops scrolled away */
- sp = (unsigned long) (®s->sp);
- savesegment(ss, ss);
- if (user_mode(regs)) {
- sp = regs->sp;
- ss = regs->ss & 0xffff;
- }
- printk(KERN_EMERG "EIP: [<%08lx>] ", regs->ip);
- print_symbol("%s", regs->ip);
- printk(" SS:ESP %04x:%08lx\n", ss, sp);
- return 0;
-}
-
-/*
- * This is gone through when something in the kernel has done something bad
- * and is about to be terminated:
- */
-void die(const char *str, struct pt_regs *regs, long err)
-{
- unsigned long flags = oops_begin();
-
- if (die_nest_count < 3) {
- report_bug(regs->ip, regs);
-
- if (__die(str, regs, err))
- regs = NULL;
- } else {
- printk(KERN_EMERG "Recursive die() failure, output suppressed\n");
- }
-
- oops_end(flags, regs, SIGSEGV);
-}
-
-static inline void
-die_if_kernel(const char *str, struct pt_regs *regs, long err)
-{
- if (!user_mode_vm(regs))
- die(str, regs, err);
-}
-
-static void __kprobes
-do_trap(int trapnr, int signr, char *str, int vm86, struct pt_regs *regs,
- long error_code, siginfo_t *info)
-{
- struct task_struct *tsk = current;
-
- if (regs->flags & X86_VM_MASK) {
- if (vm86)
- goto vm86_trap;
- goto trap_signal;
- }
-
- if (!user_mode(regs))
- goto kernel_trap;
-
-trap_signal:
- /*
- * We want error_code and trap_no set for userspace faults and
- * kernelspace faults which result in die(), but not
- * kernelspace faults which are fixed up. die() gives the
- * process no chance to handle the signal and notice the
- * kernel fault information, so that won't result in polluting
- * the information about previously queued, but not yet
- * delivered, faults. See also do_general_protection below.
- */
- tsk->thread.error_code = error_code;
- tsk->thread.trap_no = trapnr;
-
- if (info)
- force_sig_info(signr, info, tsk);
- else
- force_sig(signr, tsk);
- return;
-
-kernel_trap:
- if (!fixup_exception(regs)) {
- tsk->thread.error_code = error_code;
- tsk->thread.trap_no = trapnr;
- die(str, regs, error_code);
- }
- return;
-
-vm86_trap:
- if (handle_vm86_trap((struct kernel_vm86_regs *) regs,
- error_code, trapnr))
- goto trap_signal;
- return;
-}
-
-#define DO_ERROR(trapnr, signr, str, name) \
-void do_##name(struct pt_regs *regs, long error_code) \
-{ \
- trace_hardirqs_fixup(); \
- if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \
- == NOTIFY_STOP) \
- return; \
- do_trap(trapnr, signr, str, 0, regs, error_code, NULL); \
-}
-
-#define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr, irq) \
-void do_##name(struct pt_regs *regs, long error_code) \
-{ \
- siginfo_t info; \
- if (irq) \
- local_irq_enable(); \
- info.si_signo = signr; \
- info.si_errno = 0; \
- info.si_code = sicode; \
- info.si_addr = (void __user *)siaddr; \
- if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \
- == NOTIFY_STOP) \
- return; \
- do_trap(trapnr, signr, str, 0, regs, error_code, &info); \
-}
-
-#define DO_VM86_ERROR(trapnr, signr, str, name) \
-void do_##name(struct pt_regs *regs, long error_code) \
-{ \
- if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \
- == NOTIFY_STOP) \
- return; \
- do_trap(trapnr, signr, str, 1, regs, error_code, NULL); \
-}
-
-#define DO_VM86_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \
-void do_##name(struct pt_regs *regs, long error_code) \
-{ \
- siginfo_t info; \
- info.si_signo = signr; \
- info.si_errno = 0; \
- info.si_code = sicode; \
- info.si_addr = (void __user *)siaddr; \
- trace_hardirqs_fixup(); \
- if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \
- == NOTIFY_STOP) \
- return; \
- do_trap(trapnr, signr, str, 1, regs, error_code, &info); \
-}
-
-DO_VM86_ERROR_INFO(0, SIGFPE, "divide error", divide_error, FPE_INTDIV, regs->ip)
-#ifndef CONFIG_KPROBES
-DO_VM86_ERROR(3, SIGTRAP, "int3", int3)
-#endif
-DO_VM86_ERROR(4, SIGSEGV, "overflow", overflow)
-DO_VM86_ERROR(5, SIGSEGV, "bounds", bounds)
-DO_ERROR_INFO(6, SIGILL, "invalid opcode", invalid_op, ILL_ILLOPN, regs->ip, 0)
-DO_ERROR(9, SIGFPE, "coprocessor segment overrun", coprocessor_segment_overrun)
-DO_ERROR(10, SIGSEGV, "invalid TSS", invalid_TSS)
-DO_ERROR(11, SIGBUS, "segment not present", segment_not_present)
-DO_ERROR(12, SIGBUS, "stack segment", stack_segment)
-DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, 0, 0)
-DO_ERROR_INFO(32, SIGILL, "iret exception", iret_error, ILL_BADSTK, 0, 1)
-
-void __kprobes
-do_general_protection(struct pt_regs *regs, long error_code)
-{
- struct task_struct *tsk;
- struct thread_struct *thread;
- struct tss_struct *tss;
- int cpu;
-
- cpu = get_cpu();
- tss = &per_cpu(init_tss, cpu);
- thread = ¤t->thread;
-
- /*
- * Perform the lazy TSS's I/O bitmap copy. If the TSS has an
- * invalid offset set (the LAZY one) and the faulting thread has
- * a valid I/O bitmap pointer, we copy the I/O bitmap in the TSS
- * and we set the offset field correctly. Then we let the CPU to
- * restart the faulting instruction.
- */
- if (tss->x86_tss.io_bitmap_base == INVALID_IO_BITMAP_OFFSET_LAZY &&
- thread->io_bitmap_ptr) {
- memcpy(tss->io_bitmap, thread->io_bitmap_ptr,
- thread->io_bitmap_max);
- /*
- * If the previously set map was extending to higher ports
- * than the current one, pad extra space with 0xff (no access).
- */
- if (thread->io_bitmap_max < tss->io_bitmap_max) {
- memset((char *) tss->io_bitmap +
- thread->io_bitmap_max, 0xff,
- tss->io_bitmap_max - thread->io_bitmap_max);
- }
- tss->io_bitmap_max = thread->io_bitmap_max;
- tss->x86_tss.io_bitmap_base = IO_BITMAP_OFFSET;
- tss->io_bitmap_owner = thread;
- put_cpu();
-
- return;
- }
- put_cpu();
-
- if (regs->flags & X86_VM_MASK)
- goto gp_in_vm86;
-
- tsk = current;
- if (!user_mode(regs))
- goto gp_in_kernel;
-
- tsk->thread.error_code = error_code;
- tsk->thread.trap_no = 13;
-
- if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV) &&
- printk_ratelimit()) {
- printk(KERN_INFO
- "%s[%d] general protection ip:%lx sp:%lx error:%lx",
- tsk->comm, task_pid_nr(tsk),
- regs->ip, regs->sp, error_code);
- print_vma_addr(" in ", regs->ip);
- printk("\n");
- }
-
- force_sig(SIGSEGV, tsk);
- return;
-
-gp_in_vm86:
- local_irq_enable();
- handle_vm86_fault((struct kernel_vm86_regs *) regs, error_code);
- return;
-
-gp_in_kernel:
- if (fixup_exception(regs))
- return;
-
- tsk->thread.error_code = error_code;
- tsk->thread.trap_no = 13;
- if (notify_die(DIE_GPF, "general protection fault", regs,
- error_code, 13, SIGSEGV) == NOTIFY_STOP)
- return;
- die("general protection fault", regs, error_code);
-}
-
-static notrace __kprobes void
-mem_parity_error(unsigned char reason, struct pt_regs *regs)
-{
- printk(KERN_EMERG
- "Uhhuh. NMI received for unknown reason %02x on CPU %d.\n",
- reason, smp_processor_id());
-
- printk(KERN_EMERG
- "You have some hardware problem, likely on the PCI bus.\n");
-
-#if defined(CONFIG_EDAC)
- if (edac_handler_set()) {
- edac_atomic_assert_error();
- return;
- }
-#endif
-
- if (panic_on_unrecovered_nmi)
- panic("NMI: Not continuing");
-
- printk(KERN_EMERG "Dazed and confused, but trying to continue\n");
-
- /* Clear and disable the memory parity error line. */
- clear_mem_error(reason);
-}
-
-static notrace __kprobes void
-io_check_error(unsigned char reason, struct pt_regs *regs)
-{
- unsigned long i;
-
- printk(KERN_EMERG "NMI: IOCK error (debug interrupt?)\n");
- show_registers(regs);
-
- /* Re-enable the IOCK line, wait for a few seconds */
- reason = (reason & 0xf) | 8;
- outb(reason, 0x61);
-
- i = 2000;
- while (--i)
- udelay(1000);
-
- reason &= ~8;
- outb(reason, 0x61);
-}
-
-static notrace __kprobes void
-unknown_nmi_error(unsigned char reason, struct pt_regs *regs)
-{
- if (notify_die(DIE_NMIUNKNOWN, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP)
- return;
-#ifdef CONFIG_MCA
- /*
- * Might actually be able to figure out what the guilty party
- * is:
- */
- if (MCA_bus) {
- mca_handle_nmi();
- return;
- }
-#endif
- printk(KERN_EMERG
- "Uhhuh. NMI received for unknown reason %02x on CPU %d.\n",
- reason, smp_processor_id());
-
- printk(KERN_EMERG "Do you have a strange power saving mode enabled?\n");
- if (panic_on_unrecovered_nmi)
- panic("NMI: Not continuing");
-
- printk(KERN_EMERG "Dazed and confused, but trying to continue\n");
-}
-
-static DEFINE_SPINLOCK(nmi_print_lock);
-
-void notrace __kprobes die_nmi(char *str, struct pt_regs *regs, int do_panic)
-{
- if (notify_die(DIE_NMIWATCHDOG, str, regs, 0, 2, SIGINT) == NOTIFY_STOP)
- return;
-
- spin_lock(&nmi_print_lock);
- /*
- * We are in trouble anyway, lets at least try
- * to get a message out:
- */
- bust_spinlocks(1);
- printk(KERN_EMERG "%s", str);
- printk(" on CPU%d, ip %08lx, registers:\n",
- smp_processor_id(), regs->ip);
- show_registers(regs);
- if (do_panic)
- panic("Non maskable interrupt");
- console_silent();
- spin_unlock(&nmi_print_lock);
- bust_spinlocks(0);
-
- /*
- * If we are in kernel we are probably nested up pretty bad
- * and might aswell get out now while we still can:
- */
- if (!user_mode_vm(regs)) {
- current->thread.trap_no = 2;
- crash_kexec(regs);
- }
-
- do_exit(SIGSEGV);
-}
-
-static notrace __kprobes void default_do_nmi(struct pt_regs *regs)
-{
- unsigned char reason = 0;
- int cpu;
-
- cpu = smp_processor_id();
-
- /* Only the BSP gets external NMIs from the system. */
- if (!cpu)
- reason = get_nmi_reason();
-
- if (!(reason & 0xc0)) {
- if (notify_die(DIE_NMI_IPI, "nmi_ipi", regs, reason, 2, SIGINT)
- == NOTIFY_STOP)
- return;
-#ifdef CONFIG_X86_LOCAL_APIC
- /*
- * Ok, so this is none of the documented NMI sources,
- * so it must be the NMI watchdog.
- */
- if (nmi_watchdog_tick(regs, reason))
- return;
- if (!do_nmi_callback(regs, cpu))
- unknown_nmi_error(reason, regs);
-#else
- unknown_nmi_error(reason, regs);
-#endif
-
- return;
- }
- if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP)
- return;
-
- /* AK: following checks seem to be broken on modern chipsets. FIXME */
- if (reason & 0x80)
- mem_parity_error(reason, regs);
- if (reason & 0x40)
- io_check_error(reason, regs);
- /*
- * Reassert NMI in case it became active meanwhile
- * as it's edge-triggered:
- */
- reassert_nmi();
-}
-
-notrace __kprobes void do_nmi(struct pt_regs *regs, long error_code)
-{
- int cpu;
-
- nmi_enter();
-
- cpu = smp_processor_id();
-
- ++nmi_count(cpu);
-
- if (!ignore_nmis)
- default_do_nmi(regs);
-
- nmi_exit();
-}
-
-void stop_nmi(void)
-{
- acpi_nmi_disable();
- ignore_nmis++;
-}
-
-void restart_nmi(void)
-{
- ignore_nmis--;
- acpi_nmi_enable();
-}
-
-#ifdef CONFIG_KPROBES
-void __kprobes do_int3(struct pt_regs *regs, long error_code)
-{
- trace_hardirqs_fixup();
-
- if (notify_die(DIE_INT3, "int3", regs, error_code, 3, SIGTRAP)
- == NOTIFY_STOP)
- return;
- /*
- * This is an interrupt gate, because kprobes wants interrupts
- * disabled. Normal trap handlers don't.
- */
- restore_interrupts(regs);
-
- do_trap(3, SIGTRAP, "int3", 1, regs, error_code, NULL);
-}
-#endif
-
-/*
- * Our handling of the processor debug registers is non-trivial.
- * We do not clear them on entry and exit from the kernel. Therefore
- * it is possible to get a watchpoint trap here from inside the kernel.
- * However, the code in ./ptrace.c has ensured that the user can
- * only set watchpoints on userspace addresses. Therefore the in-kernel
- * watchpoint trap can only occur in code which is reading/writing
- * from user space. Such code must not hold kernel locks (since it
- * can equally take a page fault), therefore it is safe to call
- * force_sig_info even though that claims and releases locks.
- *
- * Code in ./signal.c ensures that the debug control register
- * is restored before we deliver any signal, and therefore that
- * user code runs with the correct debug control register even though
- * we clear it here.
- *
- * Being careful here means that we don't have to be as careful in a
- * lot of more complicated places (task switching can be a bit lazy
- * about restoring all the debug state, and ptrace doesn't have to
- * find every occurrence of the TF bit that could be saved away even
- * by user code)
- */
-void __kprobes do_debug(struct pt_regs *regs, long error_code)
-{
- struct task_struct *tsk = current;
- unsigned int condition;
-
- trace_hardirqs_fixup();
-
- get_debugreg(condition, 6);
-
- /*
- * The processor cleared BTF, so don't mark that we need it set.
- */
- clear_tsk_thread_flag(tsk, TIF_DEBUGCTLMSR);
- tsk->thread.debugctlmsr = 0;
-
- if (notify_die(DIE_DEBUG, "debug", regs, condition, error_code,
- SIGTRAP) == NOTIFY_STOP)
- return;
- /* It's safe to allow irq's after DR6 has been saved */
- if (regs->flags & X86_EFLAGS_IF)
- local_irq_enable();
-
- /* Mask out spurious debug traps due to lazy DR7 setting */
- if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) {
- if (!tsk->thread.debugreg7)
- goto clear_dr7;
- }
-
- if (regs->flags & X86_VM_MASK)
- goto debug_vm86;
-
- /* Save debug status register where ptrace can see it */
- tsk->thread.debugreg6 = condition;
-
- /*
- * Single-stepping through TF: make sure we ignore any events in
- * kernel space (but re-enable TF when returning to user mode).
- */
- if (condition & DR_STEP) {
- /*
- * We already checked v86 mode above, so we can
- * check for kernel mode by just checking the CPL
- * of CS.
- */
- if (!user_mode(regs))
- goto clear_TF_reenable;
- }
-
- /* Ok, finally something we can handle */
- send_sigtrap(tsk, regs, error_code);
-
- /*
- * Disable additional traps. They'll be re-enabled when
- * the signal is delivered.
- */
-clear_dr7:
- set_debugreg(0, 7);
- return;
-
-debug_vm86:
- handle_vm86_trap((struct kernel_vm86_regs *) regs, error_code, 1);
- return;
-
-clear_TF_reenable:
- set_tsk_thread_flag(tsk, TIF_SINGLESTEP);
- regs->flags &= ~X86_EFLAGS_TF;
- return;
-}
-
-/*
- * Note that we play around with the 'TS' bit in an attempt to get
- * the correct behaviour even in the presence of the asynchronous
- * IRQ13 behaviour
- */
-void math_error(void __user *ip)
-{
- struct task_struct *task;
- siginfo_t info;
- unsigned short cwd, swd;
-
- /*
- * Save the info for the exception handler and clear the error.
- */
- task = current;
- save_init_fpu(task);
- task->thread.trap_no = 16;
- task->thread.error_code = 0;
- info.si_signo = SIGFPE;
- info.si_errno = 0;
- info.si_code = __SI_FAULT;
- info.si_addr = ip;
- /*
- * (~cwd & swd) will mask out exceptions that are not set to unmasked
- * status. 0x3f is the exception bits in these regs, 0x200 is the
- * C1 reg you need in case of a stack fault, 0x040 is the stack
- * fault bit. We should only be taking one exception at a time,
- * so if this combination doesn't produce any single exception,
- * then we have a bad program that isn't synchronizing its FPU usage
- * and it will suffer the consequences since we won't be able to
- * fully reproduce the context of the exception
- */
- cwd = get_fpu_cwd(task);
- swd = get_fpu_swd(task);
- switch (swd & ~cwd & 0x3f) {
- case 0x000: /* No unmasked exception */
- return;
- default: /* Multiple exceptions */
- break;
- case 0x001: /* Invalid Op */
- /*
- * swd & 0x240 == 0x040: Stack Underflow
- * swd & 0x240 == 0x240: Stack Overflow
- * User must clear the SF bit (0x40) if set
- */
- info.si_code = FPE_FLTINV;
- break;
- case 0x002: /* Denormalize */
- case 0x010: /* Underflow */
- info.si_code = FPE_FLTUND;
- break;
- case 0x004: /* Zero Divide */
- info.si_code = FPE_FLTDIV;
- break;
- case 0x008: /* Overflow */
- info.si_code = FPE_FLTOVF;
- break;
- case 0x020: /* Precision */
- info.si_code = FPE_FLTRES;
- break;
- }
- force_sig_info(SIGFPE, &info, task);
-}
-
-void do_coprocessor_error(struct pt_regs *regs, long error_code)
-{
- ignore_fpu_irq = 1;
- math_error((void __user *)regs->ip);
-}
-
-static void simd_math_error(void __user *ip)
-{
- struct task_struct *task;
- siginfo_t info;
- unsigned short mxcsr;
-
- /*
- * Save the info for the exception handler and clear the error.
- */
- task = current;
- save_init_fpu(task);
- task->thread.trap_no = 19;
- task->thread.error_code = 0;
- info.si_signo = SIGFPE;
- info.si_errno = 0;
- info.si_code = __SI_FAULT;
- info.si_addr = ip;
- /*
- * The SIMD FPU exceptions are handled a little differently, as there
- * is only a single status/control register. Thus, to determine which
- * unmasked exception was caught we must mask the exception mask bits
- * at 0x1f80, and then use these to mask the exception bits at 0x3f.
- */
- mxcsr = get_fpu_mxcsr(task);
- switch (~((mxcsr & 0x1f80) >> 7) & (mxcsr & 0x3f)) {
- case 0x000:
- default:
- break;
- case 0x001: /* Invalid Op */
- info.si_code = FPE_FLTINV;
- break;
- case 0x002: /* Denormalize */
- case 0x010: /* Underflow */
- info.si_code = FPE_FLTUND;
- break;
- case 0x004: /* Zero Divide */
- info.si_code = FPE_FLTDIV;
- break;
- case 0x008: /* Overflow */
- info.si_code = FPE_FLTOVF;
- break;
- case 0x020: /* Precision */
- info.si_code = FPE_FLTRES;
- break;
- }
- force_sig_info(SIGFPE, &info, task);
-}
-
-void do_simd_coprocessor_error(struct pt_regs *regs, long error_code)
-{
- if (cpu_has_xmm) {
- /* Handle SIMD FPU exceptions on PIII+ processors. */
- ignore_fpu_irq = 1;
- simd_math_error((void __user *)regs->ip);
- return;
- }
- /*
- * Handle strange cache flush from user space exception
- * in all other cases. This is undocumented behaviour.
- */
- if (regs->flags & X86_VM_MASK) {
- handle_vm86_fault((struct kernel_vm86_regs *)regs, error_code);
- return;
- }
- current->thread.trap_no = 19;
- current->thread.error_code = error_code;
- die_if_kernel("cache flush denied", regs, error_code);
- force_sig(SIGSEGV, current);
-}
-
-void do_spurious_interrupt_bug(struct pt_regs *regs, long error_code)
-{
-#if 0
- /* No need to warn about this any longer. */
- printk(KERN_INFO "Ignoring P6 Local APIC Spurious Interrupt Bug...\n");
-#endif
-}
-
-unsigned long patch_espfix_desc(unsigned long uesp, unsigned long kesp)
-{
- struct desc_struct *gdt = get_cpu_gdt_table(smp_processor_id());
- unsigned long base = (kesp - uesp) & -THREAD_SIZE;
- unsigned long new_kesp = kesp - base;
- unsigned long lim_pages = (new_kesp | (THREAD_SIZE - 1)) >> PAGE_SHIFT;
- __u64 desc = *(__u64 *)&gdt[GDT_ENTRY_ESPFIX_SS];
-
- /* Set up base for espfix segment */
- desc &= 0x00f0ff0000000000ULL;
- desc |= ((((__u64)base) << 16) & 0x000000ffffff0000ULL) |
- ((((__u64)base) << 32) & 0xff00000000000000ULL) |
- ((((__u64)lim_pages) << 32) & 0x000f000000000000ULL) |
- (lim_pages & 0xffff);
- *(__u64 *)&gdt[GDT_ENTRY_ESPFIX_SS] = desc;
-
- return new_kesp;
-}
-
-/*
- * 'math_state_restore()' saves the current math information in the
- * old math state array, and gets the new ones from the current task
- *
- * Careful.. There are problems with IBM-designed IRQ13 behaviour.
- * Don't touch unless you *really* know how it works.
- *
- * Must be called with kernel preemption disabled (in this case,
- * local interrupts are disabled at the call-site in entry.S).
- */
-asmlinkage void math_state_restore(void)
-{
- struct thread_info *thread = current_thread_info();
- struct task_struct *tsk = thread->task;
-
- if (!tsk_used_math(tsk)) {
- local_irq_enable();
- /*
- * does a slab alloc which can sleep
- */
- if (init_fpu(tsk)) {
- /*
- * ran out of memory!
- */
- do_group_exit(SIGKILL);
- return;
- }
- local_irq_disable();
- }
-
- clts(); /* Allow maths ops (or we recurse) */
- restore_fpu(tsk);
- thread->status |= TS_USEDFPU; /* So we fnsave on switch_to() */
- tsk->fpu_counter++;
-}
-EXPORT_SYMBOL_GPL(math_state_restore);
-
-#ifndef CONFIG_MATH_EMULATION
-
-asmlinkage void math_emulate(long arg)
-{
- printk(KERN_EMERG
- "math-emulation not enabled and no coprocessor found.\n");
- printk(KERN_EMERG "killing %s.\n", current->comm);
- force_sig(SIGFPE, current);
- schedule();
-}
-
-#endif /* CONFIG_MATH_EMULATION */
-
-void __init trap_init(void)
-{
- int i;
-
-#ifdef CONFIG_EISA
- void __iomem *p = early_ioremap(0x0FFFD9, 4);
-
- if (readl(p) == 'E' + ('I'<<8) + ('S'<<16) + ('A'<<24))
- EISA_bus = 1;
- early_iounmap(p, 4);
-#endif
-
- set_trap_gate(0, ÷_error);
- set_intr_gate(1, &debug);
- set_intr_gate(2, &nmi);
- set_system_intr_gate(3, &int3); /* int3 can be called from all */
- set_system_gate(4, &overflow); /* int4 can be called from all */
- set_trap_gate(5, &bounds);
- set_trap_gate(6, &invalid_op);
- set_trap_gate(7, &device_not_available);
- set_task_gate(8, GDT_ENTRY_DOUBLEFAULT_TSS);
- set_trap_gate(9, &coprocessor_segment_overrun);
- set_trap_gate(10, &invalid_TSS);
- set_trap_gate(11, &segment_not_present);
- set_trap_gate(12, &stack_segment);
- set_trap_gate(13, &general_protection);
- set_intr_gate(14, &page_fault);
- set_trap_gate(15, &spurious_interrupt_bug);
- set_trap_gate(16, &coprocessor_error);
- set_trap_gate(17, &alignment_check);
-#ifdef CONFIG_X86_MCE
- set_trap_gate(18, &machine_check);
-#endif
- set_trap_gate(19, &simd_coprocessor_error);
-
- if (cpu_has_fxsr) {
- printk(KERN_INFO "Enabling fast FPU save and restore... ");
- set_in_cr4(X86_CR4_OSFXSR);
- printk("done.\n");
- }
- if (cpu_has_xmm) {
- printk(KERN_INFO
- "Enabling unmasked SIMD FPU exception support... ");
- set_in_cr4(X86_CR4_OSXMMEXCPT);
- printk("done.\n");
- }
-
- set_system_gate(SYSCALL_VECTOR, &system_call);
-
- /* Reserve all the builtin and the syscall vector: */
- for (i = 0; i < FIRST_EXTERNAL_VECTOR; i++)
- set_bit(i, used_vectors);
-
- set_bit(SYSCALL_VECTOR, used_vectors);
-
- init_thread_xstate();
- /*
- * Should be a barrier for any external CPU state:
- */
- cpu_init();
-
- trap_init_hook();
-}
-
-static int __init kstack_setup(char *s)
-{
- kstack_depth_to_print = simple_strtoul(s, NULL, 0);
-
- return 1;
-}
-__setup("kstack=", kstack_setup);
-
-static int __init code_bytes_setup(char *s)
-{
- code_bytes = simple_strtoul(s, NULL, 0);
- if (code_bytes > 8192)
- code_bytes = 8192;
-
- return 1;
-}
-__setup("code_bytes=", code_bytes_setup);
diff --git a/arch/x86/kernel/traps_64.c b/arch/x86/kernel/traps_64.c
deleted file mode 100644
index 7a31f10..0000000
--- a/arch/x86/kernel/traps_64.c
+++ /dev/null
@@ -1,1218 +0,0 @@
-/*
- * Copyright (C) 1991, 1992 Linus Torvalds
- * Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs
- *
- * Pentium III FXSR, SSE support
- * Gareth Hughes <gareth@valinux.com>, May 2000
- */
-
-/*
- * 'Traps.c' handles hardware traps and faults after we have saved some
- * state in 'entry.S'.
- */
-#include <linux/moduleparam.h>
-#include <linux/interrupt.h>
-#include <linux/kallsyms.h>
-#include <linux/spinlock.h>
-#include <linux/kprobes.h>
-#include <linux/uaccess.h>
-#include <linux/utsname.h>
-#include <linux/kdebug.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/ptrace.h>
-#include <linux/string.h>
-#include <linux/unwind.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/kexec.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/init.h>
-#include <linux/bug.h>
-#include <linux/nmi.h>
-#include <linux/mm.h>
-#include <linux/smp.h>
-#include <linux/io.h>
-
-#if defined(CONFIG_EDAC)
-#include <linux/edac.h>
-#endif
-
-#include <asm/stacktrace.h>
-#include <asm/processor.h>
-#include <asm/debugreg.h>
-#include <asm/atomic.h>
-#include <asm/system.h>
-#include <asm/unwind.h>
-#include <asm/desc.h>
-#include <asm/i387.h>
-#include <asm/pgalloc.h>
-#include <asm/proto.h>
-#include <asm/pda.h>
-#include <asm/traps.h>
-
-#include <mach_traps.h>
-
-int panic_on_unrecovered_nmi;
-int kstack_depth_to_print = 12;
-static unsigned int code_bytes = 64;
-static int ignore_nmis;
-static int die_counter;
-
-static inline void conditional_sti(struct pt_regs *regs)
-{
- if (regs->flags & X86_EFLAGS_IF)
- local_irq_enable();
-}
-
-static inline void preempt_conditional_sti(struct pt_regs *regs)
-{
- inc_preempt_count();
- if (regs->flags & X86_EFLAGS_IF)
- local_irq_enable();
-}
-
-static inline void preempt_conditional_cli(struct pt_regs *regs)
-{
- if (regs->flags & X86_EFLAGS_IF)
- local_irq_disable();
- /* Make sure to not schedule here because we could be running
- on an exception stack. */
- dec_preempt_count();
-}
-
-void printk_address(unsigned long address, int reliable)
-{
- printk(" [<%016lx>] %s%pS\n",
- address, reliable ? "" : "? ", (void *) address);
-}
-
-static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack,
- unsigned *usedp, char **idp)
-{
- static char ids[][8] = {
- [DEBUG_STACK - 1] = "#DB",
- [NMI_STACK - 1] = "NMI",
- [DOUBLEFAULT_STACK - 1] = "#DF",
- [STACKFAULT_STACK - 1] = "#SS",
- [MCE_STACK - 1] = "#MC",
-#if DEBUG_STKSZ > EXCEPTION_STKSZ
- [N_EXCEPTION_STACKS ...
- N_EXCEPTION_STACKS + DEBUG_STKSZ / EXCEPTION_STKSZ - 2] = "#DB[?]"
-#endif
- };
- unsigned k;
-
- /*
- * Iterate over all exception stacks, and figure out whether
- * 'stack' is in one of them:
- */
- for (k = 0; k < N_EXCEPTION_STACKS; k++) {
- unsigned long end = per_cpu(orig_ist, cpu).ist[k];
- /*
- * Is 'stack' above this exception frame's end?
- * If yes then skip to the next frame.
- */
- if (stack >= end)
- continue;
- /*
- * Is 'stack' above this exception frame's start address?
- * If yes then we found the right frame.
- */
- if (stack >= end - EXCEPTION_STKSZ) {
- /*
- * Make sure we only iterate through an exception
- * stack once. If it comes up for the second time
- * then there's something wrong going on - just
- * break out and return NULL:
- */
- if (*usedp & (1U << k))
- break;
- *usedp |= 1U << k;
- *idp = ids[k];
- return (unsigned long *)end;
- }
- /*
- * If this is a debug stack, and if it has a larger size than
- * the usual exception stacks, then 'stack' might still
- * be within the lower portion of the debug stack:
- */
-#if DEBUG_STKSZ > EXCEPTION_STKSZ
- if (k == DEBUG_STACK - 1 && stack >= end - DEBUG_STKSZ) {
- unsigned j = N_EXCEPTION_STACKS - 1;
-
- /*
- * Black magic. A large debug stack is composed of
- * multiple exception stack entries, which we
- * iterate through now. Dont look:
- */
- do {
- ++j;
- end -= EXCEPTION_STKSZ;
- ids[j][4] = '1' + (j - N_EXCEPTION_STACKS);
- } while (stack < end - EXCEPTION_STKSZ);
- if (*usedp & (1U << j))
- break;
- *usedp |= 1U << j;
- *idp = ids[j];
- return (unsigned long *)end;
- }
-#endif
- }
- return NULL;
-}
-
-/*
- * x86-64 can have up to three kernel stacks:
- * process stack
- * interrupt stack
- * severe exception (double fault, nmi, stack fault, debug, mce) hardware stack
- */
-
-static inline int valid_stack_ptr(struct thread_info *tinfo,
- void *p, unsigned int size, void *end)
-{
- void *t = tinfo;
- if (end) {
- if (p < end && p >= (end-THREAD_SIZE))
- return 1;
- else
- return 0;
- }
- return p > t && p < t + THREAD_SIZE - size;
-}
-
-/* The form of the top of the frame on the stack */
-struct stack_frame {
- struct stack_frame *next_frame;
- unsigned long return_address;
-};
-
-static inline unsigned long
-print_context_stack(struct thread_info *tinfo,
- unsigned long *stack, unsigned long bp,
- const struct stacktrace_ops *ops, void *data,
- unsigned long *end)
-{
- struct stack_frame *frame = (struct stack_frame *)bp;
-
- while (valid_stack_ptr(tinfo, stack, sizeof(*stack), end)) {
- unsigned long addr;
-
- addr = *stack;
- if (__kernel_text_address(addr)) {
- if ((unsigned long) stack == bp + 8) {
- ops->address(data, addr, 1);
- frame = frame->next_frame;
- bp = (unsigned long) frame;
- } else {
- ops->address(data, addr, bp == 0);
- }
- }
- stack++;
- }
- return bp;
-}
-
-void dump_trace(struct task_struct *task, struct pt_regs *regs,
- unsigned long *stack, unsigned long bp,
- const struct stacktrace_ops *ops, void *data)
-{
- const unsigned cpu = get_cpu();
- unsigned long *irqstack_end = (unsigned long *)cpu_pda(cpu)->irqstackptr;
- unsigned used = 0;
- struct thread_info *tinfo;
-
- if (!task)
- task = current;
-
- if (!stack) {
- unsigned long dummy;
- stack = &dummy;
- if (task && task != current)
- stack = (unsigned long *)task->thread.sp;
- }
-
-#ifdef CONFIG_FRAME_POINTER
- if (!bp) {
- if (task == current) {
- /* Grab bp right from our regs */
- asm("movq %%rbp, %0" : "=r" (bp) : );
- } else {
- /* bp is the last reg pushed by switch_to */
- bp = *(unsigned long *) task->thread.sp;
- }
- }
-#endif
-
- /*
- * Print function call entries in all stacks, starting at the
- * current stack address. If the stacks consist of nested
- * exceptions
- */
- tinfo = task_thread_info(task);
- for (;;) {
- char *id;
- unsigned long *estack_end;
- estack_end = in_exception_stack(cpu, (unsigned long)stack,
- &used, &id);
-
- if (estack_end) {
- if (ops->stack(data, id) < 0)
- break;
-
- bp = print_context_stack(tinfo, stack, bp, ops,
- data, estack_end);
- ops->stack(data, "<EOE>");
- /*
- * We link to the next stack via the
- * second-to-last pointer (index -2 to end) in the
- * exception stack:
- */
- stack = (unsigned long *) estack_end[-2];
- continue;
- }
- if (irqstack_end) {
- unsigned long *irqstack;
- irqstack = irqstack_end -
- (IRQSTACKSIZE - 64) / sizeof(*irqstack);
-
- if (stack >= irqstack && stack < irqstack_end) {
- if (ops->stack(data, "IRQ") < 0)
- break;
- bp = print_context_stack(tinfo, stack, bp,
- ops, data, irqstack_end);
- /*
- * We link to the next stack (which would be
- * the process stack normally) the last
- * pointer (index -1 to end) in the IRQ stack:
- */
- stack = (unsigned long *) (irqstack_end[-1]);
- irqstack_end = NULL;
- ops->stack(data, "EOI");
- continue;
- }
- }
- break;
- }
-
- /*
- * This handles the process stack:
- */
- bp = print_context_stack(tinfo, stack, bp, ops, data, NULL);
- put_cpu();
-}
-EXPORT_SYMBOL(dump_trace);
-
-static void
-print_trace_warning_symbol(void *data, char *msg, unsigned long symbol)
-{
- print_symbol(msg, symbol);
- printk("\n");
-}
-
-static void print_trace_warning(void *data, char *msg)
-{
- printk("%s\n", msg);
-}
-
-static int print_trace_stack(void *data, char *name)
-{
- printk(" <%s> ", name);
- return 0;
-}
-
-static void print_trace_address(void *data, unsigned long addr, int reliable)
-{
- touch_nmi_watchdog();
- printk_address(addr, reliable);
-}
-
-static const struct stacktrace_ops print_trace_ops = {
- .warning = print_trace_warning,
- .warning_symbol = print_trace_warning_symbol,
- .stack = print_trace_stack,
- .address = print_trace_address,
-};
-
-static void
-show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs,
- unsigned long *stack, unsigned long bp, char *log_lvl)
-{
- printk("Call Trace:\n");
- dump_trace(task, regs, stack, bp, &print_trace_ops, log_lvl);
-}
-
-void show_trace(struct task_struct *task, struct pt_regs *regs,
- unsigned long *stack, unsigned long bp)
-{
- show_trace_log_lvl(task, regs, stack, bp, "");
-}
-
-static void
-show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs,
- unsigned long *sp, unsigned long bp, char *log_lvl)
-{
- unsigned long *stack;
- int i;
- const int cpu = smp_processor_id();
- unsigned long *irqstack_end =
- (unsigned long *) (cpu_pda(cpu)->irqstackptr);
- unsigned long *irqstack =
- (unsigned long *) (cpu_pda(cpu)->irqstackptr - IRQSTACKSIZE);
-
- /*
- * debugging aid: "show_stack(NULL, NULL);" prints the
- * back trace for this cpu.
- */
-
- if (sp == NULL) {
- if (task)
- sp = (unsigned long *)task->thread.sp;
- else
- sp = (unsigned long *)&sp;
- }
-
- stack = sp;
- for (i = 0; i < kstack_depth_to_print; i++) {
- if (stack >= irqstack && stack <= irqstack_end) {
- if (stack == irqstack_end) {
- stack = (unsigned long *) (irqstack_end[-1]);
- printk(" <EOI> ");
- }
- } else {
- if (((long) stack & (THREAD_SIZE-1)) == 0)
- break;
- }
- if (i && ((i % 4) == 0))
- printk("\n");
- printk(" %016lx", *stack++);
- touch_nmi_watchdog();
- }
- printk("\n");
- show_trace_log_lvl(task, regs, sp, bp, log_lvl);
-}
-
-void show_stack(struct task_struct *task, unsigned long *sp)
-{
- show_stack_log_lvl(task, NULL, sp, 0, "");
-}
-
-/*
- * The architecture-independent dump_stack generator
- */
-void dump_stack(void)
-{
- unsigned long bp = 0;
- unsigned long stack;
-
-#ifdef CONFIG_FRAME_POINTER
- if (!bp)
- asm("movq %%rbp, %0" : "=r" (bp) : );
-#endif
-
- printk("Pid: %d, comm: %.20s %s %s %.*s\n",
- current->pid, current->comm, print_tainted(),
- init_utsname()->release,
- (int)strcspn(init_utsname()->version, " "),
- init_utsname()->version);
- show_trace(NULL, NULL, &stack, bp);
-}
-EXPORT_SYMBOL(dump_stack);
-
-void show_registers(struct pt_regs *regs)
-{
- int i;
- unsigned long sp;
- const int cpu = smp_processor_id();
- struct task_struct *cur = cpu_pda(cpu)->pcurrent;
-
- sp = regs->sp;
- printk("CPU %d ", cpu);
- __show_regs(regs);
- printk("Process %s (pid: %d, threadinfo %p, task %p)\n",
- cur->comm, cur->pid, task_thread_info(cur), cur);
-
- /*
- * When in-kernel, we also print out the stack and code at the
- * time of the fault..
- */
- if (!user_mode(regs)) {
- unsigned int code_prologue = code_bytes * 43 / 64;
- unsigned int code_len = code_bytes;
- unsigned char c;
- u8 *ip;
-
- printk("Stack: ");
- show_stack_log_lvl(NULL, regs, (unsigned long *)sp,
- regs->bp, "");
-
- printk(KERN_EMERG "Code: ");
-
- ip = (u8 *)regs->ip - code_prologue;
- if (ip < (u8 *)PAGE_OFFSET || probe_kernel_address(ip, c)) {
- /* try starting at RIP */
- ip = (u8 *)regs->ip;
- code_len = code_len - code_prologue + 1;
- }
- for (i = 0; i < code_len; i++, ip++) {
- if (ip < (u8 *)PAGE_OFFSET ||
- probe_kernel_address(ip, c)) {
- printk(" Bad RIP value.");
- break;
- }
- if (ip == (u8 *)regs->ip)
- printk("<%02x> ", c);
- else
- printk("%02x ", c);
- }
- }
- printk("\n");
-}
-
-int is_valid_bugaddr(unsigned long ip)
-{
- unsigned short ud2;
-
- if (__copy_from_user(&ud2, (const void __user *) ip, sizeof(ud2)))
- return 0;
-
- return ud2 == 0x0b0f;
-}
-
-static raw_spinlock_t die_lock = __RAW_SPIN_LOCK_UNLOCKED;
-static int die_owner = -1;
-static unsigned int die_nest_count;
-
-unsigned __kprobes long oops_begin(void)
-{
- int cpu;
- unsigned long flags;
-
- oops_enter();
-
- /* racy, but better than risking deadlock. */
- raw_local_irq_save(flags);
- cpu = smp_processor_id();
- if (!__raw_spin_trylock(&die_lock)) {
- if (cpu == die_owner)
- /* nested oops. should stop eventually */;
- else
- __raw_spin_lock(&die_lock);
- }
- die_nest_count++;
- die_owner = cpu;
- console_verbose();
- bust_spinlocks(1);
- return flags;
-}
-
-void __kprobes oops_end(unsigned long flags, struct pt_regs *regs, int signr)
-{
- die_owner = -1;
- bust_spinlocks(0);
- die_nest_count--;
- if (!die_nest_count)
- /* Nest count reaches zero, release the lock. */
- __raw_spin_unlock(&die_lock);
- raw_local_irq_restore(flags);
- if (!regs) {
- oops_exit();
- return;
- }
- if (panic_on_oops)
- panic("Fatal exception");
- oops_exit();
- do_exit(signr);
-}
-
-int __kprobes __die(const char *str, struct pt_regs *regs, long err)
-{
- printk(KERN_EMERG "%s: %04lx [%u] ", str, err & 0xffff, ++die_counter);
-#ifdef CONFIG_PREEMPT
- printk("PREEMPT ");
-#endif
-#ifdef CONFIG_SMP
- printk("SMP ");
-#endif
-#ifdef CONFIG_DEBUG_PAGEALLOC
- printk("DEBUG_PAGEALLOC");
-#endif
- printk("\n");
- if (notify_die(DIE_OOPS, str, regs, err,
- current->thread.trap_no, SIGSEGV) == NOTIFY_STOP)
- return 1;
-
- show_registers(regs);
- add_taint(TAINT_DIE);
- /* Executive summary in case the oops scrolled away */
- printk(KERN_ALERT "RIP ");
- printk_address(regs->ip, 1);
- printk(" RSP <%016lx>\n", regs->sp);
- if (kexec_should_crash(current))
- crash_kexec(regs);
- return 0;
-}
-
-void die(const char *str, struct pt_regs *regs, long err)
-{
- unsigned long flags = oops_begin();
-
- if (!user_mode(regs))
- report_bug(regs->ip, regs);
-
- if (__die(str, regs, err))
- regs = NULL;
- oops_end(flags, regs, SIGSEGV);
-}
-
-notrace __kprobes void
-die_nmi(char *str, struct pt_regs *regs, int do_panic)
-{
- unsigned long flags;
-
- if (notify_die(DIE_NMIWATCHDOG, str, regs, 0, 2, SIGINT) == NOTIFY_STOP)
- return;
-
- flags = oops_begin();
- /*
- * We are in trouble anyway, lets at least try
- * to get a message out.
- */
- printk(KERN_EMERG "%s", str);
- printk(" on CPU%d, ip %08lx, registers:\n",
- smp_processor_id(), regs->ip);
- show_registers(regs);
- if (kexec_should_crash(current))
- crash_kexec(regs);
- if (do_panic || panic_on_oops)
- panic("Non maskable interrupt");
- oops_end(flags, NULL, SIGBUS);
- nmi_exit();
- local_irq_enable();
- do_exit(SIGBUS);
-}
-
-static void __kprobes
-do_trap(int trapnr, int signr, char *str, struct pt_regs *regs,
- long error_code, siginfo_t *info)
-{
- struct task_struct *tsk = current;
-
- if (!user_mode(regs))
- goto kernel_trap;
-
- /*
- * We want error_code and trap_no set for userspace faults and
- * kernelspace faults which result in die(), but not
- * kernelspace faults which are fixed up. die() gives the
- * process no chance to handle the signal and notice the
- * kernel fault information, so that won't result in polluting
- * the information about previously queued, but not yet
- * delivered, faults. See also do_general_protection below.
- */
- tsk->thread.error_code = error_code;
- tsk->thread.trap_no = trapnr;
-
- if (show_unhandled_signals && unhandled_signal(tsk, signr) &&
- printk_ratelimit()) {
- printk(KERN_INFO
- "%s[%d] trap %s ip:%lx sp:%lx error:%lx",
- tsk->comm, tsk->pid, str,
- regs->ip, regs->sp, error_code);
- print_vma_addr(" in ", regs->ip);
- printk("\n");
- }
-
- if (info)
- force_sig_info(signr, info, tsk);
- else
- force_sig(signr, tsk);
- return;
-
-kernel_trap:
- if (!fixup_exception(regs)) {
- tsk->thread.error_code = error_code;
- tsk->thread.trap_no = trapnr;
- die(str, regs, error_code);
- }
- return;
-}
-
-#define DO_ERROR(trapnr, signr, str, name) \
-asmlinkage void do_##name(struct pt_regs *regs, long error_code) \
-{ \
- if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \
- == NOTIFY_STOP) \
- return; \
- conditional_sti(regs); \
- do_trap(trapnr, signr, str, regs, error_code, NULL); \
-}
-
-#define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \
-asmlinkage void do_##name(struct pt_regs *regs, long error_code) \
-{ \
- siginfo_t info; \
- info.si_signo = signr; \
- info.si_errno = 0; \
- info.si_code = sicode; \
- info.si_addr = (void __user *)siaddr; \
- trace_hardirqs_fixup(); \
- if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \
- == NOTIFY_STOP) \
- return; \
- conditional_sti(regs); \
- do_trap(trapnr, signr, str, regs, error_code, &info); \
-}
-
-DO_ERROR_INFO(0, SIGFPE, "divide error", divide_error, FPE_INTDIV, regs->ip)
-DO_ERROR(4, SIGSEGV, "overflow", overflow)
-DO_ERROR(5, SIGSEGV, "bounds", bounds)
-DO_ERROR_INFO(6, SIGILL, "invalid opcode", invalid_op, ILL_ILLOPN, regs->ip)
-DO_ERROR(9, SIGFPE, "coprocessor segment overrun", coprocessor_segment_overrun)
-DO_ERROR(10, SIGSEGV, "invalid TSS", invalid_TSS)
-DO_ERROR(11, SIGBUS, "segment not present", segment_not_present)
-DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, 0)
-
-/* Runs on IST stack */
-asmlinkage void do_stack_segment(struct pt_regs *regs, long error_code)
-{
- if (notify_die(DIE_TRAP, "stack segment", regs, error_code,
- 12, SIGBUS) == NOTIFY_STOP)
- return;
- preempt_conditional_sti(regs);
- do_trap(12, SIGBUS, "stack segment", regs, error_code, NULL);
- preempt_conditional_cli(regs);
-}
-
-asmlinkage void do_double_fault(struct pt_regs *regs, long error_code)
-{
- static const char str[] = "double fault";
- struct task_struct *tsk = current;
-
- /* Return not checked because double check cannot be ignored */
- notify_die(DIE_TRAP, str, regs, error_code, 8, SIGSEGV);
-
- tsk->thread.error_code = error_code;
- tsk->thread.trap_no = 8;
-
- /* This is always a kernel trap and never fixable (and thus must
- never return). */
- for (;;)
- die(str, regs, error_code);
-}
-
-asmlinkage void __kprobes
-do_general_protection(struct pt_regs *regs, long error_code)
-{
- struct task_struct *tsk;
-
- conditional_sti(regs);
-
- tsk = current;
- if (!user_mode(regs))
- goto gp_in_kernel;
-
- tsk->thread.error_code = error_code;
- tsk->thread.trap_no = 13;
-
- if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV) &&
- printk_ratelimit()) {
- printk(KERN_INFO
- "%s[%d] general protection ip:%lx sp:%lx error:%lx",
- tsk->comm, tsk->pid,
- regs->ip, regs->sp, error_code);
- print_vma_addr(" in ", regs->ip);
- printk("\n");
- }
-
- force_sig(SIGSEGV, tsk);
- return;
-
-gp_in_kernel:
- if (fixup_exception(regs))
- return;
-
- tsk->thread.error_code = error_code;
- tsk->thread.trap_no = 13;
- if (notify_die(DIE_GPF, "general protection fault", regs,
- error_code, 13, SIGSEGV) == NOTIFY_STOP)
- return;
- die("general protection fault", regs, error_code);
-}
-
-static notrace __kprobes void
-mem_parity_error(unsigned char reason, struct pt_regs *regs)
-{
- printk(KERN_EMERG "Uhhuh. NMI received for unknown reason %02x.\n",
- reason);
- printk(KERN_EMERG "You have some hardware problem, likely on the PCI bus.\n");
-
-#if defined(CONFIG_EDAC)
- if (edac_handler_set()) {
- edac_atomic_assert_error();
- return;
- }
-#endif
-
- if (panic_on_unrecovered_nmi)
- panic("NMI: Not continuing");
-
- printk(KERN_EMERG "Dazed and confused, but trying to continue\n");
-
- /* Clear and disable the memory parity error line. */
- reason = (reason & 0xf) | 4;
- outb(reason, 0x61);
-}
-
-static notrace __kprobes void
-io_check_error(unsigned char reason, struct pt_regs *regs)
-{
- printk("NMI: IOCK error (debug interrupt?)\n");
- show_registers(regs);
-
- /* Re-enable the IOCK line, wait for a few seconds */
- reason = (reason & 0xf) | 8;
- outb(reason, 0x61);
- mdelay(2000);
- reason &= ~8;
- outb(reason, 0x61);
-}
-
-static notrace __kprobes void
-unknown_nmi_error(unsigned char reason, struct pt_regs *regs)
-{
- if (notify_die(DIE_NMIUNKNOWN, "nmi", regs, reason, 2, SIGINT) ==
- NOTIFY_STOP)
- return;
- printk(KERN_EMERG "Uhhuh. NMI received for unknown reason %02x.\n",
- reason);
- printk(KERN_EMERG "Do you have a strange power saving mode enabled?\n");
-
- if (panic_on_unrecovered_nmi)
- panic("NMI: Not continuing");
-
- printk(KERN_EMERG "Dazed and confused, but trying to continue\n");
-}
-
-/* Runs on IST stack. This code must keep interrupts off all the time.
- Nested NMIs are prevented by the CPU. */
-asmlinkage notrace __kprobes void default_do_nmi(struct pt_regs *regs)
-{
- unsigned char reason = 0;
- int cpu;
-
- cpu = smp_processor_id();
-
- /* Only the BSP gets external NMIs from the system. */
- if (!cpu)
- reason = get_nmi_reason();
-
- if (!(reason & 0xc0)) {
- if (notify_die(DIE_NMI_IPI, "nmi_ipi", regs, reason, 2, SIGINT)
- == NOTIFY_STOP)
- return;
- /*
- * Ok, so this is none of the documented NMI sources,
- * so it must be the NMI watchdog.
- */
- if (nmi_watchdog_tick(regs, reason))
- return;
- if (!do_nmi_callback(regs, cpu))
- unknown_nmi_error(reason, regs);
-
- return;
- }
- if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP)
- return;
-
- /* AK: following checks seem to be broken on modern chipsets. FIXME */
- if (reason & 0x80)
- mem_parity_error(reason, regs);
- if (reason & 0x40)
- io_check_error(reason, regs);
-}
-
-asmlinkage notrace __kprobes void
-do_nmi(struct pt_regs *regs, long error_code)
-{
- nmi_enter();
-
- add_pda(__nmi_count, 1);
-
- if (!ignore_nmis)
- default_do_nmi(regs);
-
- nmi_exit();
-}
-
-void stop_nmi(void)
-{
- acpi_nmi_disable();
- ignore_nmis++;
-}
-
-void restart_nmi(void)
-{
- ignore_nmis--;
- acpi_nmi_enable();
-}
-
-/* runs on IST stack. */
-asmlinkage void __kprobes do_int3(struct pt_regs *regs, long error_code)
-{
- trace_hardirqs_fixup();
-
- if (notify_die(DIE_INT3, "int3", regs, error_code, 3, SIGTRAP)
- == NOTIFY_STOP)
- return;
-
- preempt_conditional_sti(regs);
- do_trap(3, SIGTRAP, "int3", regs, error_code, NULL);
- preempt_conditional_cli(regs);
-}
-
-/* Help handler running on IST stack to switch back to user stack
- for scheduling or signal handling. The actual stack switch is done in
- entry.S */
-asmlinkage __kprobes struct pt_regs *sync_regs(struct pt_regs *eregs)
-{
- struct pt_regs *regs = eregs;
- /* Did already sync */
- if (eregs == (struct pt_regs *)eregs->sp)
- ;
- /* Exception from user space */
- else if (user_mode(eregs))
- regs = task_pt_regs(current);
- /* Exception from kernel and interrupts are enabled. Move to
- kernel process stack. */
- else if (eregs->flags & X86_EFLAGS_IF)
- regs = (struct pt_regs *)(eregs->sp -= sizeof(struct pt_regs));
- if (eregs != regs)
- *regs = *eregs;
- return regs;
-}
-
-/* runs on IST stack. */
-asmlinkage void __kprobes do_debug(struct pt_regs *regs,
- unsigned long error_code)
-{
- struct task_struct *tsk = current;
- unsigned long condition;
- siginfo_t info;
-
- trace_hardirqs_fixup();
-
- get_debugreg(condition, 6);
-
- /*
- * The processor cleared BTF, so don't mark that we need it set.
- */
- clear_tsk_thread_flag(tsk, TIF_DEBUGCTLMSR);
- tsk->thread.debugctlmsr = 0;
-
- if (notify_die(DIE_DEBUG, "debug", regs, condition, error_code,
- SIGTRAP) == NOTIFY_STOP)
- return;
-
- preempt_conditional_sti(regs);
-
- /* Mask out spurious debug traps due to lazy DR7 setting */
- if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) {
- if (!tsk->thread.debugreg7)
- goto clear_dr7;
- }
-
- tsk->thread.debugreg6 = condition;
-
- /*
- * Single-stepping through TF: make sure we ignore any events in
- * kernel space (but re-enable TF when returning to user mode).
- */
- if (condition & DR_STEP) {
- if (!user_mode(regs))
- goto clear_TF_reenable;
- }
-
- /* Ok, finally something we can handle */
- tsk->thread.trap_no = 1;
- tsk->thread.error_code = error_code;
- info.si_signo = SIGTRAP;
- info.si_errno = 0;
- info.si_code = TRAP_BRKPT;
- info.si_addr = user_mode(regs) ? (void __user *)regs->ip : NULL;
- force_sig_info(SIGTRAP, &info, tsk);
-
-clear_dr7:
- set_debugreg(0, 7);
- preempt_conditional_cli(regs);
- return;
-
-clear_TF_reenable:
- set_tsk_thread_flag(tsk, TIF_SINGLESTEP);
- regs->flags &= ~X86_EFLAGS_TF;
- preempt_conditional_cli(regs);
- return;
-}
-
-static int kernel_math_error(struct pt_regs *regs, const char *str, int trapnr)
-{
- if (fixup_exception(regs))
- return 1;
-
- notify_die(DIE_GPF, str, regs, 0, trapnr, SIGFPE);
- /* Illegal floating point operation in the kernel */
- current->thread.trap_no = trapnr;
- die(str, regs, 0);
- return 0;
-}
-
-/*
- * Note that we play around with the 'TS' bit in an attempt to get
- * the correct behaviour even in the presence of the asynchronous
- * IRQ13 behaviour
- */
-asmlinkage void do_coprocessor_error(struct pt_regs *regs)
-{
- void __user *ip = (void __user *)(regs->ip);
- struct task_struct *task;
- siginfo_t info;
- unsigned short cwd, swd;
-
- conditional_sti(regs);
- if (!user_mode(regs) &&
- kernel_math_error(regs, "kernel x87 math error", 16))
- return;
-
- /*
- * Save the info for the exception handler and clear the error.
- */
- task = current;
- save_init_fpu(task);
- task->thread.trap_no = 16;
- task->thread.error_code = 0;
- info.si_signo = SIGFPE;
- info.si_errno = 0;
- info.si_code = __SI_FAULT;
- info.si_addr = ip;
- /*
- * (~cwd & swd) will mask out exceptions that are not set to unmasked
- * status. 0x3f is the exception bits in these regs, 0x200 is the
- * C1 reg you need in case of a stack fault, 0x040 is the stack
- * fault bit. We should only be taking one exception at a time,
- * so if this combination doesn't produce any single exception,
- * then we have a bad program that isn't synchronizing its FPU usage
- * and it will suffer the consequences since we won't be able to
- * fully reproduce the context of the exception
- */
- cwd = get_fpu_cwd(task);
- swd = get_fpu_swd(task);
- switch (swd & ~cwd & 0x3f) {
- case 0x000: /* No unmasked exception */
- default: /* Multiple exceptions */
- break;
- case 0x001: /* Invalid Op */
- /*
- * swd & 0x240 == 0x040: Stack Underflow
- * swd & 0x240 == 0x240: Stack Overflow
- * User must clear the SF bit (0x40) if set
- */
- info.si_code = FPE_FLTINV;
- break;
- case 0x002: /* Denormalize */
- case 0x010: /* Underflow */
- info.si_code = FPE_FLTUND;
- break;
- case 0x004: /* Zero Divide */
- info.si_code = FPE_FLTDIV;
- break;
- case 0x008: /* Overflow */
- info.si_code = FPE_FLTOVF;
- break;
- case 0x020: /* Precision */
- info.si_code = FPE_FLTRES;
- break;
- }
- force_sig_info(SIGFPE, &info, task);
-}
-
-asmlinkage void bad_intr(void)
-{
- printk("bad interrupt");
-}
-
-asmlinkage void do_simd_coprocessor_error(struct pt_regs *regs)
-{
- void __user *ip = (void __user *)(regs->ip);
- struct task_struct *task;
- siginfo_t info;
- unsigned short mxcsr;
-
- conditional_sti(regs);
- if (!user_mode(regs) &&
- kernel_math_error(regs, "kernel simd math error", 19))
- return;
-
- /*
- * Save the info for the exception handler and clear the error.
- */
- task = current;
- save_init_fpu(task);
- task->thread.trap_no = 19;
- task->thread.error_code = 0;
- info.si_signo = SIGFPE;
- info.si_errno = 0;
- info.si_code = __SI_FAULT;
- info.si_addr = ip;
- /*
- * The SIMD FPU exceptions are handled a little differently, as there
- * is only a single status/control register. Thus, to determine which
- * unmasked exception was caught we must mask the exception mask bits
- * at 0x1f80, and then use these to mask the exception bits at 0x3f.
- */
- mxcsr = get_fpu_mxcsr(task);
- switch (~((mxcsr & 0x1f80) >> 7) & (mxcsr & 0x3f)) {
- case 0x000:
- default:
- break;
- case 0x001: /* Invalid Op */
- info.si_code = FPE_FLTINV;
- break;
- case 0x002: /* Denormalize */
- case 0x010: /* Underflow */
- info.si_code = FPE_FLTUND;
- break;
- case 0x004: /* Zero Divide */
- info.si_code = FPE_FLTDIV;
- break;
- case 0x008: /* Overflow */
- info.si_code = FPE_FLTOVF;
- break;
- case 0x020: /* Precision */
- info.si_code = FPE_FLTRES;
- break;
- }
- force_sig_info(SIGFPE, &info, task);
-}
-
-asmlinkage void do_spurious_interrupt_bug(struct pt_regs *regs)
-{
-}
-
-asmlinkage void __attribute__((weak)) smp_thermal_interrupt(void)
-{
-}
-
-asmlinkage void __attribute__((weak)) mce_threshold_interrupt(void)
-{
-}
-
-/*
- * 'math_state_restore()' saves the current math information in the
- * old math state array, and gets the new ones from the current task
- *
- * Careful.. There are problems with IBM-designed IRQ13 behaviour.
- * Don't touch unless you *really* know how it works.
- */
-asmlinkage void math_state_restore(void)
-{
- struct task_struct *me = current;
-
- if (!used_math()) {
- local_irq_enable();
- /*
- * does a slab alloc which can sleep
- */
- if (init_fpu(me)) {
- /*
- * ran out of memory!
- */
- do_group_exit(SIGKILL);
- return;
- }
- local_irq_disable();
- }
-
- clts(); /* Allow maths ops (or we recurse) */
- /*
- * Paranoid restore. send a SIGSEGV if we fail to restore the state.
- */
- if (unlikely(restore_fpu_checking(&me->thread.xstate->fxsave))) {
- stts();
- force_sig(SIGSEGV, me);
- return;
- }
- task_thread_info(me)->status |= TS_USEDFPU;
- me->fpu_counter++;
-}
-EXPORT_SYMBOL_GPL(math_state_restore);
-
-void __init trap_init(void)
-{
- set_intr_gate(0, ÷_error);
- set_intr_gate_ist(1, &debug, DEBUG_STACK);
- set_intr_gate_ist(2, &nmi, NMI_STACK);
- /* int3 can be called from all */
- set_system_gate_ist(3, &int3, DEBUG_STACK);
- /* int4 can be called from all */
- set_system_gate(4, &overflow);
- set_intr_gate(5, &bounds);
- set_intr_gate(6, &invalid_op);
- set_intr_gate(7, &device_not_available);
- set_intr_gate_ist(8, &double_fault, DOUBLEFAULT_STACK);
- set_intr_gate(9, &coprocessor_segment_overrun);
- set_intr_gate(10, &invalid_TSS);
- set_intr_gate(11, &segment_not_present);
- set_intr_gate_ist(12, &stack_segment, STACKFAULT_STACK);
- set_intr_gate(13, &general_protection);
- set_intr_gate(14, &page_fault);
- set_intr_gate(15, &spurious_interrupt_bug);
- set_intr_gate(16, &coprocessor_error);
- set_intr_gate(17, &alignment_check);
-#ifdef CONFIG_X86_MCE
- set_intr_gate_ist(18, &machine_check, MCE_STACK);
-#endif
- set_intr_gate(19, &simd_coprocessor_error);
-
-#ifdef CONFIG_IA32_EMULATION
- set_system_gate(IA32_SYSCALL_VECTOR, ia32_syscall);
-#endif
- /*
- * initialize the per thread extended state:
- */
- init_thread_xstate();
- /*
- * Should be a barrier for any external CPU state:
- */
- cpu_init();
-}
-
-static int __init oops_setup(char *s)
-{
- if (!s)
- return -EINVAL;
- if (!strcmp(s, "panic"))
- panic_on_oops = 1;
- return 0;
-}
-early_param("oops", oops_setup);
-
-static int __init kstack_setup(char *s)
-{
- if (!s)
- return -EINVAL;
- kstack_depth_to_print = simple_strtoul(s, NULL, 0);
- return 0;
-}
-early_param("kstack", kstack_setup);
-
-static int __init code_bytes_setup(char *s)
-{
- code_bytes = simple_strtoul(s, NULL, 0);
- if (code_bytes > 8192)
- code_bytes = 8192;
-
- return 1;
-}
-__setup("code_bytes=", code_bytes_setup);
diff --git a/arch/x86/kernel/vmi_32.c b/arch/x86/kernel/vmi_32.c
index 8c9ad02..8b6c393 100644
--- a/arch/x86/kernel/vmi_32.c
+++ b/arch/x86/kernel/vmi_32.c
@@ -905,8 +905,8 @@
#endif
#ifdef CONFIG_X86_LOCAL_APIC
- para_fill(pv_apic_ops.apic_read, APICRead);
- para_fill(pv_apic_ops.apic_write, APICWrite);
+ para_fill(apic_ops->read, APICRead);
+ para_fill(apic_ops->write, APICWrite);
#endif
/*
diff --git a/arch/x86/kernel/vmlinux_32.lds.S b/arch/x86/kernel/vmlinux_32.lds.S
index af5bdad..a9b8560 100644
--- a/arch/x86/kernel/vmlinux_32.lds.S
+++ b/arch/x86/kernel/vmlinux_32.lds.S
@@ -140,10 +140,10 @@
*(.con_initcall.init)
__con_initcall_end = .;
}
- .x86cpuvendor.init : AT(ADDR(.x86cpuvendor.init) - LOAD_OFFSET) {
- __x86cpuvendor_start = .;
- *(.x86cpuvendor.init)
- __x86cpuvendor_end = .;
+ .x86_cpu_dev.init : AT(ADDR(.x86_cpu_dev.init) - LOAD_OFFSET) {
+ __x86_cpu_dev_start = .;
+ *(.x86_cpu_dev.init)
+ __x86_cpu_dev_end = .;
}
SECURITY_INIT
. = ALIGN(4);
@@ -180,6 +180,7 @@
. = ALIGN(PAGE_SIZE);
.data.percpu : AT(ADDR(.data.percpu) - LOAD_OFFSET) {
__per_cpu_start = .;
+ *(.data.percpu.page_aligned)
*(.data.percpu)
*(.data.percpu.shared_aligned)
__per_cpu_end = .;
diff --git a/arch/x86/kernel/vmlinux_64.lds.S b/arch/x86/kernel/vmlinux_64.lds.S
index 63e5c1a..46e0544 100644
--- a/arch/x86/kernel/vmlinux_64.lds.S
+++ b/arch/x86/kernel/vmlinux_64.lds.S
@@ -168,12 +168,11 @@
*(.con_initcall.init)
}
__con_initcall_end = .;
- . = ALIGN(16);
- __x86cpuvendor_start = .;
- .x86cpuvendor.init : AT(ADDR(.x86cpuvendor.init) - LOAD_OFFSET) {
- *(.x86cpuvendor.init)
+ __x86_cpu_dev_start = .;
+ .x86_cpu_dev.init : AT(ADDR(.x86_cpu_dev.init) - LOAD_OFFSET) {
+ *(.x86_cpu_dev.init)
}
- __x86cpuvendor_end = .;
+ __x86_cpu_dev_end = .;
SECURITY_INIT
. = ALIGN(8);
diff --git a/arch/x86/kernel/xsave.c b/arch/x86/kernel/xsave.c
new file mode 100644
index 0000000..9abac8a
--- /dev/null
+++ b/arch/x86/kernel/xsave.c
@@ -0,0 +1,345 @@
+/*
+ * xsave/xrstor support.
+ *
+ * Author: Suresh Siddha <suresh.b.siddha@intel.com>
+ */
+#include <linux/bootmem.h>
+#include <linux/compat.h>
+#include <asm/i387.h>
+#ifdef CONFIG_IA32_EMULATION
+#include <asm/sigcontext32.h>
+#endif
+#include <asm/xcr.h>
+
+/*
+ * Supported feature mask by the CPU and the kernel.
+ */
+u64 pcntxt_mask;
+
+struct _fpx_sw_bytes fx_sw_reserved;
+#ifdef CONFIG_IA32_EMULATION
+struct _fpx_sw_bytes fx_sw_reserved_ia32;
+#endif
+
+/*
+ * Check for the presence of extended state information in the
+ * user fpstate pointer in the sigcontext.
+ */
+int check_for_xstate(struct i387_fxsave_struct __user *buf,
+ void __user *fpstate,
+ struct _fpx_sw_bytes *fx_sw_user)
+{
+ int min_xstate_size = sizeof(struct i387_fxsave_struct) +
+ sizeof(struct xsave_hdr_struct);
+ unsigned int magic2;
+ int err;
+
+ err = __copy_from_user(fx_sw_user, &buf->sw_reserved[0],
+ sizeof(struct _fpx_sw_bytes));
+
+ if (err)
+ return err;
+
+ /*
+ * First Magic check failed.
+ */
+ if (fx_sw_user->magic1 != FP_XSTATE_MAGIC1)
+ return -1;
+
+ /*
+ * Check for error scenarios.
+ */
+ if (fx_sw_user->xstate_size < min_xstate_size ||
+ fx_sw_user->xstate_size > xstate_size ||
+ fx_sw_user->xstate_size > fx_sw_user->extended_size)
+ return -1;
+
+ err = __get_user(magic2, (__u32 *) (((void *)fpstate) +
+ fx_sw_user->extended_size -
+ FP_XSTATE_MAGIC2_SIZE));
+ /*
+ * Check for the presence of second magic word at the end of memory
+ * layout. This detects the case where the user just copied the legacy
+ * fpstate layout with out copying the extended state information
+ * in the memory layout.
+ */
+ if (err || magic2 != FP_XSTATE_MAGIC2)
+ return -1;
+
+ return 0;
+}
+
+#ifdef CONFIG_X86_64
+/*
+ * Signal frame handlers.
+ */
+
+int save_i387_xstate(void __user *buf)
+{
+ struct task_struct *tsk = current;
+ int err = 0;
+
+ if (!access_ok(VERIFY_WRITE, buf, sig_xstate_size))
+ return -EACCES;
+
+ BUG_ON(sig_xstate_size < xstate_size);
+
+ if ((unsigned long)buf % 64)
+ printk("save_i387_xstate: bad fpstate %p\n", buf);
+
+ if (!used_math())
+ return 0;
+ clear_used_math(); /* trigger finit */
+ if (task_thread_info(tsk)->status & TS_USEDFPU) {
+ /*
+ * Start with clearing the user buffer. This will present a
+ * clean context for the bytes not touched by the fxsave/xsave.
+ */
+ err = __clear_user(buf, sig_xstate_size);
+ if (err)
+ return err;
+
+ if (task_thread_info(tsk)->status & TS_XSAVE)
+ err = xsave_user(buf);
+ else
+ err = fxsave_user(buf);
+
+ if (err)
+ return err;
+ task_thread_info(tsk)->status &= ~TS_USEDFPU;
+ stts();
+ } else {
+ if (__copy_to_user(buf, &tsk->thread.xstate->fxsave,
+ xstate_size))
+ return -1;
+ }
+
+ if (task_thread_info(tsk)->status & TS_XSAVE) {
+ struct _fpstate __user *fx = buf;
+ struct _xstate __user *x = buf;
+ u64 xstate_bv;
+
+ err = __copy_to_user(&fx->sw_reserved, &fx_sw_reserved,
+ sizeof(struct _fpx_sw_bytes));
+
+ err |= __put_user(FP_XSTATE_MAGIC2,
+ (__u32 __user *) (buf + sig_xstate_size
+ - FP_XSTATE_MAGIC2_SIZE));
+
+ /*
+ * Read the xstate_bv which we copied (directly from the cpu or
+ * from the state in task struct) to the user buffers and
+ * set the FP/SSE bits.
+ */
+ err |= __get_user(xstate_bv, &x->xstate_hdr.xstate_bv);
+
+ /*
+ * For legacy compatible, we always set FP/SSE bits in the bit
+ * vector while saving the state to the user context. This will
+ * enable us capturing any changes(during sigreturn) to
+ * the FP/SSE bits by the legacy applications which don't touch
+ * xstate_bv in the xsave header.
+ *
+ * xsave aware apps can change the xstate_bv in the xsave
+ * header as well as change any contents in the memory layout.
+ * xrestore as part of sigreturn will capture all the changes.
+ */
+ xstate_bv |= XSTATE_FPSSE;
+
+ err |= __put_user(xstate_bv, &x->xstate_hdr.xstate_bv);
+
+ if (err)
+ return err;
+ }
+
+ return 1;
+}
+
+/*
+ * Restore the extended state if present. Otherwise, restore the FP/SSE
+ * state.
+ */
+int restore_user_xstate(void __user *buf)
+{
+ struct _fpx_sw_bytes fx_sw_user;
+ u64 mask;
+ int err;
+
+ if (((unsigned long)buf % 64) ||
+ check_for_xstate(buf, buf, &fx_sw_user))
+ goto fx_only;
+
+ mask = fx_sw_user.xstate_bv;
+
+ /*
+ * restore the state passed by the user.
+ */
+ err = xrestore_user(buf, mask);
+ if (err)
+ return err;
+
+ /*
+ * init the state skipped by the user.
+ */
+ mask = pcntxt_mask & ~mask;
+
+ xrstor_state(init_xstate_buf, mask);
+
+ return 0;
+
+fx_only:
+ /*
+ * couldn't find the extended state information in the
+ * memory layout. Restore just the FP/SSE and init all
+ * the other extended state.
+ */
+ xrstor_state(init_xstate_buf, pcntxt_mask & ~XSTATE_FPSSE);
+ return fxrstor_checking((__force struct i387_fxsave_struct *)buf);
+}
+
+/*
+ * This restores directly out of user space. Exceptions are handled.
+ */
+int restore_i387_xstate(void __user *buf)
+{
+ struct task_struct *tsk = current;
+ int err = 0;
+
+ if (!buf) {
+ if (used_math())
+ goto clear;
+ return 0;
+ } else
+ if (!access_ok(VERIFY_READ, buf, sig_xstate_size))
+ return -EACCES;
+
+ if (!used_math()) {
+ err = init_fpu(tsk);
+ if (err)
+ return err;
+ }
+
+ if (!(task_thread_info(current)->status & TS_USEDFPU)) {
+ clts();
+ task_thread_info(current)->status |= TS_USEDFPU;
+ }
+ if (task_thread_info(tsk)->status & TS_XSAVE)
+ err = restore_user_xstate(buf);
+ else
+ err = fxrstor_checking((__force struct i387_fxsave_struct *)
+ buf);
+ if (unlikely(err)) {
+ /*
+ * Encountered an error while doing the restore from the
+ * user buffer, clear the fpu state.
+ */
+clear:
+ clear_fpu(tsk);
+ clear_used_math();
+ }
+ return err;
+}
+#endif
+
+/*
+ * Prepare the SW reserved portion of the fxsave memory layout, indicating
+ * the presence of the extended state information in the memory layout
+ * pointed by the fpstate pointer in the sigcontext.
+ * This will be saved when ever the FP and extended state context is
+ * saved on the user stack during the signal handler delivery to the user.
+ */
+void prepare_fx_sw_frame(void)
+{
+ int size_extended = (xstate_size - sizeof(struct i387_fxsave_struct)) +
+ FP_XSTATE_MAGIC2_SIZE;
+
+ sig_xstate_size = sizeof(struct _fpstate) + size_extended;
+
+#ifdef CONFIG_IA32_EMULATION
+ sig_xstate_ia32_size = sizeof(struct _fpstate_ia32) + size_extended;
+#endif
+
+ memset(&fx_sw_reserved, 0, sizeof(fx_sw_reserved));
+
+ fx_sw_reserved.magic1 = FP_XSTATE_MAGIC1;
+ fx_sw_reserved.extended_size = sig_xstate_size;
+ fx_sw_reserved.xstate_bv = pcntxt_mask;
+ fx_sw_reserved.xstate_size = xstate_size;
+#ifdef CONFIG_IA32_EMULATION
+ memcpy(&fx_sw_reserved_ia32, &fx_sw_reserved,
+ sizeof(struct _fpx_sw_bytes));
+ fx_sw_reserved_ia32.extended_size = sig_xstate_ia32_size;
+#endif
+}
+
+/*
+ * Represents init state for the supported extended state.
+ */
+struct xsave_struct *init_xstate_buf;
+
+#ifdef CONFIG_X86_64
+unsigned int sig_xstate_size = sizeof(struct _fpstate);
+#endif
+
+/*
+ * Enable the extended processor state save/restore feature
+ */
+void __cpuinit xsave_init(void)
+{
+ if (!cpu_has_xsave)
+ return;
+
+ set_in_cr4(X86_CR4_OSXSAVE);
+
+ /*
+ * Enable all the features that the HW is capable of
+ * and the Linux kernel is aware of.
+ */
+ xsetbv(XCR_XFEATURE_ENABLED_MASK, pcntxt_mask);
+}
+
+/*
+ * setup the xstate image representing the init state
+ */
+static void __init setup_xstate_init(void)
+{
+ init_xstate_buf = alloc_bootmem(xstate_size);
+ init_xstate_buf->i387.mxcsr = MXCSR_DEFAULT;
+}
+
+/*
+ * Enable and initialize the xsave feature.
+ */
+void __init xsave_cntxt_init(void)
+{
+ unsigned int eax, ebx, ecx, edx;
+
+ cpuid_count(0xd, 0, &eax, &ebx, &ecx, &edx);
+ pcntxt_mask = eax + ((u64)edx << 32);
+
+ if ((pcntxt_mask & XSTATE_FPSSE) != XSTATE_FPSSE) {
+ printk(KERN_ERR "FP/SSE not shown under xsave features 0x%llx\n",
+ pcntxt_mask);
+ BUG();
+ }
+
+ /*
+ * for now OS knows only about FP/SSE
+ */
+ pcntxt_mask = pcntxt_mask & XCNTXT_MASK;
+ xsave_init();
+
+ /*
+ * Recompute the context size for enabled features
+ */
+ cpuid_count(0xd, 0, &eax, &ebx, &ecx, &edx);
+ xstate_size = ebx;
+
+ prepare_fx_sw_frame();
+
+ setup_xstate_init();
+
+ printk(KERN_INFO "xsave/xrstor: enabled xstate_bv 0x%llx, "
+ "cntxt size 0x%x\n",
+ pcntxt_mask, xstate_size);
+}
diff --git a/arch/x86/kvm/vmx.h b/arch/x86/kvm/vmx.h
index 23e8373..17e2599 100644
--- a/arch/x86/kvm/vmx.h
+++ b/arch/x86/kvm/vmx.h
@@ -331,21 +331,6 @@
#define AR_RESERVD_MASK 0xfffe0f00
-#define MSR_IA32_VMX_BASIC 0x480
-#define MSR_IA32_VMX_PINBASED_CTLS 0x481
-#define MSR_IA32_VMX_PROCBASED_CTLS 0x482
-#define MSR_IA32_VMX_EXIT_CTLS 0x483
-#define MSR_IA32_VMX_ENTRY_CTLS 0x484
-#define MSR_IA32_VMX_MISC 0x485
-#define MSR_IA32_VMX_CR0_FIXED0 0x486
-#define MSR_IA32_VMX_CR0_FIXED1 0x487
-#define MSR_IA32_VMX_CR4_FIXED0 0x488
-#define MSR_IA32_VMX_CR4_FIXED1 0x489
-#define MSR_IA32_VMX_VMCS_ENUM 0x48a
-#define MSR_IA32_VMX_PROCBASED_CTLS2 0x48b
-#define MSR_IA32_VMX_EPT_VPID_CAP 0x48c
-
-#define MSR_IA32_FEATURE_CONTROL 0x3a
#define MSR_IA32_FEATURE_CONTROL_LOCKED 0x1
#define MSR_IA32_FEATURE_CONTROL_VMXON_ENABLED 0x4
diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c
index d9249a8..65f0b8a 100644
--- a/arch/x86/lguest/boot.c
+++ b/arch/x86/lguest/boot.c
@@ -55,6 +55,7 @@
#include <linux/lguest_launcher.h>
#include <linux/virtio_console.h>
#include <linux/pm.h>
+#include <asm/apic.h>
#include <asm/lguest.h>
#include <asm/paravirt.h>
#include <asm/param.h>
@@ -783,14 +784,44 @@
* code qualifies for Advanced. It will also never interrupt anything. It
* does, however, allow us to get through the Linux boot code. */
#ifdef CONFIG_X86_LOCAL_APIC
-static void lguest_apic_write(unsigned long reg, u32 v)
+static void lguest_apic_write(u32 reg, u32 v)
{
}
-static u32 lguest_apic_read(unsigned long reg)
+static u32 lguest_apic_read(u32 reg)
{
return 0;
}
+
+static u64 lguest_apic_icr_read(void)
+{
+ return 0;
+}
+
+static void lguest_apic_icr_write(u32 low, u32 id)
+{
+ /* Warn to see if there's any stray references */
+ WARN_ON(1);
+}
+
+static void lguest_apic_wait_icr_idle(void)
+{
+ return;
+}
+
+static u32 lguest_apic_safe_wait_icr_idle(void)
+{
+ return 0;
+}
+
+static struct apic_ops lguest_basic_apic_ops = {
+ .read = lguest_apic_read,
+ .write = lguest_apic_write,
+ .icr_read = lguest_apic_icr_read,
+ .icr_write = lguest_apic_icr_write,
+ .wait_icr_idle = lguest_apic_wait_icr_idle,
+ .safe_wait_icr_idle = lguest_apic_safe_wait_icr_idle,
+};
#endif
/* STOP! Until an interrupt comes in. */
@@ -990,8 +1021,7 @@
#ifdef CONFIG_X86_LOCAL_APIC
/* apic read/write intercepts */
- pv_apic_ops.apic_write = lguest_apic_write;
- pv_apic_ops.apic_read = lguest_apic_read;
+ apic_ops = &lguest_basic_apic_ops;
#endif
/* time operations */
diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile
index aa3fa41..55e11aa 100644
--- a/arch/x86/lib/Makefile
+++ b/arch/x86/lib/Makefile
@@ -17,9 +17,6 @@
lib-$(CONFIG_X86_USE_3DNOW) += mmx_32.o
else
obj-y += io_64.o iomap_copy_64.o
-
- CFLAGS_csum-partial_64.o := -funroll-loops
-
lib-y += csum-partial_64.o csum-copy_64.o csum-wrappers_64.o
lib-y += thunk_64.o clear_page_64.o copy_page_64.o
lib-y += memmove_64.o memset_64.o
diff --git a/arch/x86/lib/usercopy_32.c b/arch/x86/lib/usercopy_32.c
index 24e6094..9e68075 100644
--- a/arch/x86/lib/usercopy_32.c
+++ b/arch/x86/lib/usercopy_32.c
@@ -14,6 +14,13 @@
#include <asm/uaccess.h>
#include <asm/mmx.h>
+#ifdef CONFIG_X86_INTEL_USERCOPY
+/*
+ * Alignment at which movsl is preferred for bulk memory copies.
+ */
+struct movsl_mask movsl_mask __read_mostly;
+#endif
+
static inline int __movsl_is_ok(unsigned long a1, unsigned long a2, unsigned long n)
{
#ifdef CONFIG_X86_INTEL_USERCOPY
diff --git a/arch/x86/mach-default/setup.c b/arch/x86/mach-default/setup.c
index 3f2cf11..37b9ae4 100644
--- a/arch/x86/mach-default/setup.c
+++ b/arch/x86/mach-default/setup.c
@@ -38,15 +38,6 @@
init_ISA_irqs();
}
-/*
- * IRQ2 is cascade interrupt to second interrupt controller
- */
-static struct irqaction irq2 = {
- .handler = no_action,
- .mask = CPU_MASK_NONE,
- .name = "cascade",
-};
-
/**
* intr_init_hook - post gate setup interrupt initialisation
*
@@ -62,12 +53,6 @@
if (x86_quirks->arch_intr_init())
return;
}
-#ifdef CONFIG_X86_LOCAL_APIC
- apic_intr_init();
-#endif
-
- if (!acpi_ioapic)
- setup_irq(2, &irq2);
}
/**
diff --git a/arch/x86/mach-es7000/Makefile b/arch/x86/mach-es7000/Makefile
deleted file mode 100644
index 3ef8b43..0000000
--- a/arch/x86/mach-es7000/Makefile
+++ /dev/null
@@ -1,5 +0,0 @@
-#
-# Makefile for the linux kernel.
-#
-
-obj-$(CONFIG_X86_ES7000) := es7000plat.o
diff --git a/arch/x86/mach-es7000/es7000.h b/arch/x86/mach-es7000/es7000.h
deleted file mode 100644
index c8d5aa1..0000000
--- a/arch/x86/mach-es7000/es7000.h
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Written by: Garry Forsgren, Unisys Corporation
- * Natalie Protasevich, Unisys Corporation
- * This file contains the code to configure and interface
- * with Unisys ES7000 series hardware system manager.
- *
- * Copyright (c) 2003 Unisys Corporation. All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it would be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write the Free Software Foundation, Inc., 59
- * Temple Place - Suite 330, Boston MA 02111-1307, USA.
- *
- * Contact information: Unisys Corporation, Township Line & Union Meeting
- * Roads-A, Unisys Way, Blue Bell, Pennsylvania, 19424, or:
- *
- * http://www.unisys.com
- */
-
-/*
- * ES7000 chipsets
- */
-
-#define NON_UNISYS 0
-#define ES7000_CLASSIC 1
-#define ES7000_ZORRO 2
-
-
-#define MIP_REG 1
-#define MIP_PSAI_REG 4
-
-#define MIP_BUSY 1
-#define MIP_SPIN 0xf0000
-#define MIP_VALID 0x0100000000000000ULL
-#define MIP_PORT(VALUE) ((VALUE >> 32) & 0xffff)
-
-#define MIP_RD_LO(VALUE) (VALUE & 0xffffffff)
-
-struct mip_reg_info {
- unsigned long long mip_info;
- unsigned long long delivery_info;
- unsigned long long host_reg;
- unsigned long long mip_reg;
-};
-
-struct part_info {
- unsigned char type;
- unsigned char length;
- unsigned char part_id;
- unsigned char apic_mode;
- unsigned long snum;
- char ptype[16];
- char sname[64];
- char pname[64];
-};
-
-struct psai {
- unsigned long long entry_type;
- unsigned long long addr;
- unsigned long long bep_addr;
-};
-
-struct es7000_mem_info {
- unsigned char type;
- unsigned char length;
- unsigned char resv[6];
- unsigned long long start;
- unsigned long long size;
-};
-
-struct es7000_oem_table {
- unsigned long long hdr;
- struct mip_reg_info mip;
- struct part_info pif;
- struct es7000_mem_info shm;
- struct psai psai;
-};
-
-#ifdef CONFIG_ACPI
-
-struct oem_table {
- struct acpi_table_header Header;
- u32 OEMTableAddr;
- u32 OEMTableSize;
-};
-
-extern int find_unisys_acpi_oem_table(unsigned long *oem_addr);
-#endif
-
-struct mip_reg {
- unsigned long long off_0;
- unsigned long long off_8;
- unsigned long long off_10;
- unsigned long long off_18;
- unsigned long long off_20;
- unsigned long long off_28;
- unsigned long long off_30;
- unsigned long long off_38;
-};
-
-#define MIP_SW_APIC 0x1020b
-#define MIP_FUNC(VALUE) (VALUE & 0xff)
-
-extern int parse_unisys_oem (char *oemptr);
-extern void setup_unisys(void);
-extern int es7000_start_cpu(int cpu, unsigned long eip);
-extern void es7000_sw_apic(void);
diff --git a/arch/x86/mach-generic/Makefile b/arch/x86/mach-generic/Makefile
index 0dbd780..6730f4e 100644
--- a/arch/x86/mach-generic/Makefile
+++ b/arch/x86/mach-generic/Makefile
@@ -9,4 +9,3 @@
obj-$(CONFIG_X86_SUMMIT) += summit.o
obj-$(CONFIG_X86_BIGSMP) += bigsmp.o
obj-$(CONFIG_X86_ES7000) += es7000.o
-obj-$(CONFIG_X86_ES7000) += ../../x86/mach-es7000/
diff --git a/arch/x86/mach-generic/bigsmp.c b/arch/x86/mach-generic/bigsmp.c
index 59d7717..df37fc9 100644
--- a/arch/x86/mach-generic/bigsmp.c
+++ b/arch/x86/mach-generic/bigsmp.c
@@ -5,18 +5,17 @@
#define APIC_DEFINITION 1
#include <linux/threads.h>
#include <linux/cpumask.h>
-#include <asm/smp.h>
#include <asm/mpspec.h>
#include <asm/genapic.h>
#include <asm/fixmap.h>
#include <asm/apicdef.h>
#include <linux/kernel.h>
-#include <linux/smp.h>
#include <linux/init.h>
#include <linux/dmi.h>
-#include <asm/mach-bigsmp/mach_apic.h>
-#include <asm/mach-bigsmp/mach_apicdef.h>
-#include <asm/mach-bigsmp/mach_ipi.h>
+#include <asm/bigsmp/apicdef.h>
+#include <linux/smp.h>
+#include <asm/bigsmp/apic.h>
+#include <asm/bigsmp/ipi.h>
#include <asm/mach-default/mach_mpparse.h>
static int dmi_bigsmp; /* can be set by dmi scanners */
diff --git a/arch/x86/mach-generic/es7000.c b/arch/x86/mach-generic/es7000.c
index 4742626..6513d41 100644
--- a/arch/x86/mach-generic/es7000.c
+++ b/arch/x86/mach-generic/es7000.c
@@ -4,20 +4,19 @@
#define APIC_DEFINITION 1
#include <linux/threads.h>
#include <linux/cpumask.h>
-#include <asm/smp.h>
#include <asm/mpspec.h>
#include <asm/genapic.h>
#include <asm/fixmap.h>
#include <asm/apicdef.h>
#include <linux/kernel.h>
#include <linux/string.h>
-#include <linux/smp.h>
#include <linux/init.h>
-#include <asm/mach-es7000/mach_apicdef.h>
-#include <asm/mach-es7000/mach_apic.h>
-#include <asm/mach-es7000/mach_ipi.h>
-#include <asm/mach-es7000/mach_mpparse.h>
-#include <asm/mach-es7000/mach_wakecpu.h>
+#include <asm/es7000/apicdef.h>
+#include <linux/smp.h>
+#include <asm/es7000/apic.h>
+#include <asm/es7000/ipi.h>
+#include <asm/es7000/mpparse.h>
+#include <asm/es7000/wakecpu.h>
static int probe_es7000(void)
{
@@ -48,16 +47,26 @@
/* Hook from generic ACPI tables.c */
static int __init acpi_madt_oem_check(char *oem_id, char *oem_table_id)
{
- unsigned long oem_addr;
+ unsigned long oem_addr = 0;
+ int check_dsdt;
+ int ret = 0;
+
+ /* check dsdt at first to avoid clear fix_map for oem_addr */
+ check_dsdt = es7000_check_dsdt();
+
if (!find_unisys_acpi_oem_table(&oem_addr)) {
- if (es7000_check_dsdt())
- return parse_unisys_oem((char *)oem_addr);
+ if (check_dsdt)
+ ret = parse_unisys_oem((char *)oem_addr);
else {
setup_unisys();
- return 1;
+ ret = 1;
}
+ /*
+ * we need to unmap it
+ */
+ unmap_unisys_acpi_oem_table(oem_addr);
}
- return 0;
+ return ret;
}
#else
static int __init acpi_madt_oem_check(char *oem_id, char *oem_table_id)
diff --git a/arch/x86/mach-generic/numaq.c b/arch/x86/mach-generic/numaq.c
index 8091e68..8cf5839 100644
--- a/arch/x86/mach-generic/numaq.c
+++ b/arch/x86/mach-generic/numaq.c
@@ -4,7 +4,6 @@
#define APIC_DEFINITION 1
#include <linux/threads.h>
#include <linux/cpumask.h>
-#include <linux/smp.h>
#include <asm/mpspec.h>
#include <asm/genapic.h>
#include <asm/fixmap.h>
@@ -12,11 +11,12 @@
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/init.h>
-#include <asm/mach-numaq/mach_apic.h>
-#include <asm/mach-numaq/mach_apicdef.h>
-#include <asm/mach-numaq/mach_ipi.h>
-#include <asm/mach-numaq/mach_mpparse.h>
-#include <asm/mach-numaq/mach_wakecpu.h>
+#include <asm/numaq/apicdef.h>
+#include <linux/smp.h>
+#include <asm/numaq/apic.h>
+#include <asm/numaq/ipi.h>
+#include <asm/numaq/mpparse.h>
+#include <asm/numaq/wakecpu.h>
#include <asm/numaq.h>
static int mps_oem_check(struct mp_config_table *mpc, char *oem,
diff --git a/arch/x86/mach-generic/summit.c b/arch/x86/mach-generic/summit.c
index a97ea0f..6ad6b67 100644
--- a/arch/x86/mach-generic/summit.c
+++ b/arch/x86/mach-generic/summit.c
@@ -4,19 +4,18 @@
#define APIC_DEFINITION 1
#include <linux/threads.h>
#include <linux/cpumask.h>
-#include <asm/smp.h>
#include <asm/mpspec.h>
#include <asm/genapic.h>
#include <asm/fixmap.h>
#include <asm/apicdef.h>
#include <linux/kernel.h>
#include <linux/string.h>
-#include <linux/smp.h>
#include <linux/init.h>
-#include <asm/mach-summit/mach_apic.h>
-#include <asm/mach-summit/mach_apicdef.h>
-#include <asm/mach-summit/mach_ipi.h>
-#include <asm/mach-summit/mach_mpparse.h>
+#include <asm/summit/apicdef.h>
+#include <linux/smp.h>
+#include <asm/summit/apic.h>
+#include <asm/summit/ipi.h>
+#include <asm/summit/mpparse.h>
static int probe_summit(void)
{
diff --git a/arch/x86/mm/Makefile b/arch/x86/mm/Makefile
index dfb932d..59f89b4 100644
--- a/arch/x86/mm/Makefile
+++ b/arch/x86/mm/Makefile
@@ -13,12 +13,8 @@
mmiotrace-y := pf_in.o mmio-mod.o
obj-$(CONFIG_MMIOTRACE_TEST) += testmmiotrace.o
-ifeq ($(CONFIG_X86_32),y)
-obj-$(CONFIG_NUMA) += discontig_32.o
-else
-obj-$(CONFIG_NUMA) += numa_64.o
+obj-$(CONFIG_NUMA) += numa_$(BITS).o
obj-$(CONFIG_K8_NUMA) += k8topology_64.o
-endif
obj-$(CONFIG_ACPI_NUMA) += srat_$(BITS).o
obj-$(CONFIG_MEMTEST) += memtest.o
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 8f92cac..3f2b896 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -592,11 +592,6 @@
unsigned long flags;
#endif
- /*
- * We can fault from pretty much anywhere, with unknown IRQ state.
- */
- trace_hardirqs_fixup();
-
tsk = current;
mm = tsk->mm;
prefetchw(&mm->mmap_sem);
@@ -914,15 +909,15 @@
void vmalloc_sync_all(void)
{
-#ifdef CONFIG_X86_32
- unsigned long start = VMALLOC_START & PGDIR_MASK;
unsigned long address;
+#ifdef CONFIG_X86_32
if (SHARED_KERNEL_PMD)
return;
- BUILD_BUG_ON(TASK_SIZE & ~PGDIR_MASK);
- for (address = start; address >= TASK_SIZE; address += PGDIR_SIZE) {
+ for (address = VMALLOC_START & PMD_MASK;
+ address >= TASK_SIZE && address < FIXADDR_TOP;
+ address += PMD_SIZE) {
unsigned long flags;
struct page *page;
@@ -935,10 +930,8 @@
spin_unlock_irqrestore(&pgd_lock, flags);
}
#else /* CONFIG_X86_64 */
- unsigned long start = VMALLOC_START & PGDIR_MASK;
- unsigned long address;
-
- for (address = start; address <= VMALLOC_END; address += PGDIR_SIZE) {
+ for (address = VMALLOC_START & PGDIR_MASK; address <= VMALLOC_END;
+ address += PGDIR_SIZE) {
const pgd_t *pgd_ref = pgd_offset_k(address);
unsigned long flags;
struct page *page;
diff --git a/arch/x86/mm/gup.c b/arch/x86/mm/gup.c
index 007bb06..4ba373c 100644
--- a/arch/x86/mm/gup.c
+++ b/arch/x86/mm/gup.c
@@ -82,7 +82,7 @@
pte_t pte = gup_get_pte(ptep);
struct page *page;
- if ((pte_val(pte) & (mask | _PAGE_SPECIAL)) != mask) {
+ if ((pte_flags(pte) & (mask | _PAGE_SPECIAL)) != mask) {
pte_unmap(ptep);
return 0;
}
@@ -116,10 +116,10 @@
mask = _PAGE_PRESENT|_PAGE_USER;
if (write)
mask |= _PAGE_RW;
- if ((pte_val(pte) & mask) != mask)
+ if ((pte_flags(pte) & mask) != mask)
return 0;
/* hugepages are never "special" */
- VM_BUG_ON(pte_val(pte) & _PAGE_SPECIAL);
+ VM_BUG_ON(pte_flags(pte) & _PAGE_SPECIAL);
VM_BUG_ON(!pfn_valid(pte_pfn(pte)));
refs = 0;
@@ -173,10 +173,10 @@
mask = _PAGE_PRESENT|_PAGE_USER;
if (write)
mask |= _PAGE_RW;
- if ((pte_val(pte) & mask) != mask)
+ if ((pte_flags(pte) & mask) != mask)
return 0;
/* hugepages are never "special" */
- VM_BUG_ON(pte_val(pte) & _PAGE_SPECIAL);
+ VM_BUG_ON(pte_flags(pte) & _PAGE_SPECIAL);
VM_BUG_ON(!pfn_valid(pte_pfn(pte)));
refs = 0;
diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c
index c3789bb..8396868 100644
--- a/arch/x86/mm/init_32.c
+++ b/arch/x86/mm/init_32.c
@@ -31,6 +31,7 @@
#include <linux/cpumask.h>
#include <asm/asm.h>
+#include <asm/bios_ebda.h>
#include <asm/processor.h>
#include <asm/system.h>
#include <asm/uaccess.h>
@@ -557,7 +558,7 @@
int nx_enabled;
-pteval_t __supported_pte_mask __read_mostly = ~(_PAGE_NX | _PAGE_GLOBAL);
+pteval_t __supported_pte_mask __read_mostly = ~(_PAGE_NX | _PAGE_GLOBAL | _PAGE_IOMAP);
EXPORT_SYMBOL_GPL(__supported_pte_mask);
#ifdef CONFIG_X86_PAE
@@ -969,6 +970,8 @@
int codesize, reservedpages, datasize, initsize;
int tmp;
+ start_periodic_check_for_corruption();
+
#ifdef CONFIG_FLATMEM
BUG_ON(!mem_map);
#endif
diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
index fb30486..b8e461d 100644
--- a/arch/x86/mm/init_64.c
+++ b/arch/x86/mm/init_64.c
@@ -31,6 +31,7 @@
#include <linux/nmi.h>
#include <asm/processor.h>
+#include <asm/bios_ebda.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
@@ -88,6 +89,62 @@
int after_bootmem;
+pteval_t __supported_pte_mask __read_mostly = ~_PAGE_IOMAP;
+EXPORT_SYMBOL_GPL(__supported_pte_mask);
+
+static int do_not_nx __cpuinitdata;
+
+/*
+ * noexec=on|off
+ * Control non-executable mappings for 64-bit processes.
+ *
+ * on Enable (default)
+ * off Disable
+ */
+static int __init nonx_setup(char *str)
+{
+ if (!str)
+ return -EINVAL;
+ if (!strncmp(str, "on", 2)) {
+ __supported_pte_mask |= _PAGE_NX;
+ do_not_nx = 0;
+ } else if (!strncmp(str, "off", 3)) {
+ do_not_nx = 1;
+ __supported_pte_mask &= ~_PAGE_NX;
+ }
+ return 0;
+}
+early_param("noexec", nonx_setup);
+
+void __cpuinit check_efer(void)
+{
+ unsigned long efer;
+
+ rdmsrl(MSR_EFER, efer);
+ if (!(efer & EFER_NX) || do_not_nx)
+ __supported_pte_mask &= ~_PAGE_NX;
+}
+
+int force_personality32;
+
+/*
+ * noexec32=on|off
+ * Control non executable heap for 32bit processes.
+ * To control the stack too use noexec=off
+ *
+ * on PROT_READ does not imply PROT_EXEC for 32-bit processes (default)
+ * off PROT_READ implies PROT_EXEC
+ */
+static int __init nonx32_setup(char *str)
+{
+ if (!strcmp(str, "on"))
+ force_personality32 &= ~READ_IMPLIES_EXEC;
+ else if (!strcmp(str, "off"))
+ force_personality32 |= READ_IMPLIES_EXEC;
+ return 1;
+}
+__setup("noexec32=", nonx32_setup);
+
/*
* NOTE: This function is marked __ref because it calls __init function
* (alloc_bootmem_pages). It's safe to do it ONLY when after_bootmem == 0.
@@ -139,9 +196,6 @@
}
pte = pte_offset_kernel(pmd, vaddr);
- if (!pte_none(*pte) && pte_val(new_pte) &&
- pte_val(*pte) != (pte_val(new_pte) & __supported_pte_mask))
- pte_ERROR(*pte);
set_pte(pte, new_pte);
/*
@@ -256,7 +310,7 @@
if (pfn >= table_top)
panic("alloc_low_page: ran out of memory");
- adr = early_ioremap(pfn * PAGE_SIZE, PAGE_SIZE);
+ adr = early_memremap(pfn * PAGE_SIZE, PAGE_SIZE);
memset(adr, 0, PAGE_SIZE);
*phys = pfn * PAGE_SIZE;
return adr;
@@ -692,7 +746,7 @@
old_start = mr[i].start;
memmove(&mr[i], &mr[i+1],
(nr_range - 1 - i) * sizeof (struct map_range));
- mr[i].start = old_start;
+ mr[i--].start = old_start;
nr_range--;
}
@@ -825,6 +879,8 @@
{
long codesize, reservedpages, datasize, initsize;
+ start_periodic_check_for_corruption();
+
pci_iommu_alloc();
/* clear_bss() already clear the empty_zero_page */
diff --git a/arch/x86/mm/ioremap.c b/arch/x86/mm/ioremap.c
index 6ab3196..e4c43ec 100644
--- a/arch/x86/mm/ioremap.c
+++ b/arch/x86/mm/ioremap.c
@@ -24,19 +24,48 @@
#ifdef CONFIG_X86_64
-unsigned long __phys_addr(unsigned long x)
-{
- if (x >= __START_KERNEL_map)
- return x - __START_KERNEL_map + phys_base;
- return x - PAGE_OFFSET;
-}
-EXPORT_SYMBOL(__phys_addr);
-
static inline int phys_addr_valid(unsigned long addr)
{
return addr < (1UL << boot_cpu_data.x86_phys_bits);
}
+unsigned long __phys_addr(unsigned long x)
+{
+ if (x >= __START_KERNEL_map) {
+ x -= __START_KERNEL_map;
+ VIRTUAL_BUG_ON(x >= KERNEL_IMAGE_SIZE);
+ x += phys_base;
+ } else {
+ VIRTUAL_BUG_ON(x < PAGE_OFFSET);
+ x -= PAGE_OFFSET;
+ VIRTUAL_BUG_ON(system_state == SYSTEM_BOOTING ? x > MAXMEM :
+ !phys_addr_valid(x));
+ }
+ return x;
+}
+EXPORT_SYMBOL(__phys_addr);
+
+bool __virt_addr_valid(unsigned long x)
+{
+ if (x >= __START_KERNEL_map) {
+ x -= __START_KERNEL_map;
+ if (x >= KERNEL_IMAGE_SIZE)
+ return false;
+ x += phys_base;
+ } else {
+ if (x < PAGE_OFFSET)
+ return false;
+ x -= PAGE_OFFSET;
+ if (system_state == SYSTEM_BOOTING ?
+ x > MAXMEM : !phys_addr_valid(x)) {
+ return false;
+ }
+ }
+
+ return pfn_valid(x >> PAGE_SHIFT);
+}
+EXPORT_SYMBOL(__virt_addr_valid);
+
#else
static inline int phys_addr_valid(unsigned long addr)
@@ -44,6 +73,28 @@
return 1;
}
+#ifdef CONFIG_DEBUG_VIRTUAL
+unsigned long __phys_addr(unsigned long x)
+{
+ /* VMALLOC_* aren't constants; not available at the boot time */
+ VIRTUAL_BUG_ON(x < PAGE_OFFSET);
+ VIRTUAL_BUG_ON(system_state != SYSTEM_BOOTING &&
+ is_vmalloc_addr((void *) x));
+ return x - PAGE_OFFSET;
+}
+EXPORT_SYMBOL(__phys_addr);
+#endif
+
+bool __virt_addr_valid(unsigned long x)
+{
+ if (x < PAGE_OFFSET)
+ return false;
+ if (system_state != SYSTEM_BOOTING && is_vmalloc_addr((void *) x))
+ return false;
+ return pfn_valid((x - PAGE_OFFSET) >> PAGE_SHIFT);
+}
+EXPORT_SYMBOL(__virt_addr_valid);
+
#endif
int page_is_ram(unsigned long pagenr)
@@ -223,16 +274,16 @@
switch (prot_val) {
case _PAGE_CACHE_UC:
default:
- prot = PAGE_KERNEL_NOCACHE;
+ prot = PAGE_KERNEL_IO_NOCACHE;
break;
case _PAGE_CACHE_UC_MINUS:
- prot = PAGE_KERNEL_UC_MINUS;
+ prot = PAGE_KERNEL_IO_UC_MINUS;
break;
case _PAGE_CACHE_WC:
- prot = PAGE_KERNEL_WC;
+ prot = PAGE_KERNEL_IO_WC;
break;
case _PAGE_CACHE_WB:
- prot = PAGE_KERNEL;
+ prot = PAGE_KERNEL_IO;
break;
}
@@ -549,12 +600,12 @@
}
static inline void __init early_set_fixmap(enum fixed_addresses idx,
- unsigned long phys)
+ unsigned long phys, pgprot_t prot)
{
if (after_paging_init)
- set_fixmap(idx, phys);
+ __set_fixmap(idx, phys, prot);
else
- __early_set_fixmap(idx, phys, PAGE_KERNEL);
+ __early_set_fixmap(idx, phys, prot);
}
static inline void __init early_clear_fixmap(enum fixed_addresses idx)
@@ -565,16 +616,22 @@
__early_set_fixmap(idx, 0, __pgprot(0));
}
-
-static int __initdata early_ioremap_nested;
-
+static void *prev_map[FIX_BTMAPS_SLOTS] __initdata;
+static unsigned long prev_size[FIX_BTMAPS_SLOTS] __initdata;
static int __init check_early_ioremap_leak(void)
{
- if (!early_ioremap_nested)
+ int count = 0;
+ int i;
+
+ for (i = 0; i < FIX_BTMAPS_SLOTS; i++)
+ if (prev_map[i])
+ count++;
+
+ if (!count)
return 0;
WARN(1, KERN_WARNING
"Debug warning: early ioremap leak of %d areas detected.\n",
- early_ioremap_nested);
+ count);
printk(KERN_WARNING
"please boot with early_ioremap_debug and report the dmesg.\n");
@@ -582,18 +639,33 @@
}
late_initcall(check_early_ioremap_leak);
-void __init *early_ioremap(unsigned long phys_addr, unsigned long size)
+static void __init *__early_ioremap(unsigned long phys_addr, unsigned long size, pgprot_t prot)
{
unsigned long offset, last_addr;
- unsigned int nrpages, nesting;
+ unsigned int nrpages;
enum fixed_addresses idx0, idx;
+ int i, slot;
WARN_ON(system_state != SYSTEM_BOOTING);
- nesting = early_ioremap_nested;
+ slot = -1;
+ for (i = 0; i < FIX_BTMAPS_SLOTS; i++) {
+ if (!prev_map[i]) {
+ slot = i;
+ break;
+ }
+ }
+
+ if (slot < 0) {
+ printk(KERN_INFO "early_iomap(%08lx, %08lx) not found slot\n",
+ phys_addr, size);
+ WARN_ON(1);
+ return NULL;
+ }
+
if (early_ioremap_debug) {
printk(KERN_INFO "early_ioremap(%08lx, %08lx) [%d] => ",
- phys_addr, size, nesting);
+ phys_addr, size, slot);
dump_stack();
}
@@ -604,17 +676,13 @@
return NULL;
}
- if (nesting >= FIX_BTMAPS_NESTING) {
- WARN_ON(1);
- return NULL;
- }
- early_ioremap_nested++;
+ prev_size[slot] = size;
/*
* Mappings have to be page-aligned
*/
offset = phys_addr & ~PAGE_MASK;
phys_addr &= PAGE_MASK;
- size = PAGE_ALIGN(last_addr) - phys_addr;
+ size = PAGE_ALIGN(last_addr + 1) - phys_addr;
/*
* Mappings have to fit in the FIX_BTMAP area.
@@ -628,10 +696,10 @@
/*
* Ok, go for it..
*/
- idx0 = FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*nesting;
+ idx0 = FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*slot;
idx = idx0;
while (nrpages > 0) {
- early_set_fixmap(idx, phys_addr);
+ early_set_fixmap(idx, phys_addr, prot);
phys_addr += PAGE_SIZE;
--idx;
--nrpages;
@@ -639,7 +707,20 @@
if (early_ioremap_debug)
printk(KERN_CONT "%08lx + %08lx\n", offset, fix_to_virt(idx0));
- return (void *) (offset + fix_to_virt(idx0));
+ prev_map[slot] = (void *) (offset + fix_to_virt(idx0));
+ return prev_map[slot];
+}
+
+/* Remap an IO device */
+void __init *early_ioremap(unsigned long phys_addr, unsigned long size)
+{
+ return __early_ioremap(phys_addr, size, PAGE_KERNEL_IO);
+}
+
+/* Remap memory */
+void __init *early_memremap(unsigned long phys_addr, unsigned long size)
+{
+ return __early_ioremap(phys_addr, size, PAGE_KERNEL);
}
void __init early_iounmap(void *addr, unsigned long size)
@@ -648,15 +729,33 @@
unsigned long offset;
unsigned int nrpages;
enum fixed_addresses idx;
- int nesting;
+ int i, slot;
- nesting = --early_ioremap_nested;
- if (WARN_ON(nesting < 0))
+ slot = -1;
+ for (i = 0; i < FIX_BTMAPS_SLOTS; i++) {
+ if (prev_map[i] == addr) {
+ slot = i;
+ break;
+ }
+ }
+
+ if (slot < 0) {
+ printk(KERN_INFO "early_iounmap(%p, %08lx) not found slot\n",
+ addr, size);
+ WARN_ON(1);
return;
+ }
+
+ if (prev_size[slot] != size) {
+ printk(KERN_INFO "early_iounmap(%p, %08lx) [%d] size not consistent %08lx\n",
+ addr, size, slot, prev_size[slot]);
+ WARN_ON(1);
+ return;
+ }
if (early_ioremap_debug) {
printk(KERN_INFO "early_iounmap(%p, %08lx) [%d]\n", addr,
- size, nesting);
+ size, slot);
dump_stack();
}
@@ -668,12 +767,13 @@
offset = virt_addr & ~PAGE_MASK;
nrpages = PAGE_ALIGN(offset + size - 1) >> PAGE_SHIFT;
- idx = FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*nesting;
+ idx = FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*slot;
while (nrpages > 0) {
early_clear_fixmap(idx);
--idx;
--nrpages;
}
+ prev_map[slot] = 0;
}
void __this_fixmap_does_not_exist(void)
diff --git a/arch/x86/mm/discontig_32.c b/arch/x86/mm/numa_32.c
similarity index 100%
rename from arch/x86/mm/discontig_32.c
rename to arch/x86/mm/numa_32.c
diff --git a/arch/x86/mm/srat_64.c b/arch/x86/mm/srat_64.c
index 1b4763e..51c0a2f 100644
--- a/arch/x86/mm/srat_64.c
+++ b/arch/x86/mm/srat_64.c
@@ -138,7 +138,7 @@
return;
}
- if (is_uv_system())
+ if (get_uv_system_type() >= UV_X2APIC)
apic_id = (pa->apic_id << 8) | pa->local_sapic_eid;
else
apic_id = pa->apic_id;
diff --git a/arch/x86/oprofile/Makefile b/arch/x86/oprofile/Makefile
index 30f3eb3..446902b 100644
--- a/arch/x86/oprofile/Makefile
+++ b/arch/x86/oprofile/Makefile
@@ -7,6 +7,6 @@
timer_int.o )
oprofile-y := $(DRIVER_OBJS) init.o backtrace.o
-oprofile-$(CONFIG_X86_LOCAL_APIC) += nmi_int.o op_model_athlon.o \
+oprofile-$(CONFIG_X86_LOCAL_APIC) += nmi_int.o op_model_amd.o \
op_model_ppro.o op_model_p4.o
oprofile-$(CONFIG_X86_IO_APIC) += nmi_timer_int.o
diff --git a/arch/x86/oprofile/nmi_int.c b/arch/x86/oprofile/nmi_int.c
index 8a5f161..57f6c90 100644
--- a/arch/x86/oprofile/nmi_int.c
+++ b/arch/x86/oprofile/nmi_int.c
@@ -1,10 +1,11 @@
/**
* @file nmi_int.c
*
- * @remark Copyright 2002 OProfile authors
+ * @remark Copyright 2002-2008 OProfile authors
* @remark Read the file COPYING
*
* @author John Levon <levon@movementarian.org>
+ * @author Robert Richter <robert.richter@amd.com>
*/
#include <linux/init.h>
@@ -439,6 +440,7 @@
__u8 vendor = boot_cpu_data.x86_vendor;
__u8 family = boot_cpu_data.x86;
char *cpu_type;
+ int ret = 0;
if (!cpu_has_apic)
return -ENODEV;
@@ -451,19 +453,23 @@
default:
return -ENODEV;
case 6:
- model = &op_athlon_spec;
+ model = &op_amd_spec;
cpu_type = "i386/athlon";
break;
case 0xf:
- model = &op_athlon_spec;
+ model = &op_amd_spec;
/* Actually it could be i386/hammer too, but give
user space an consistent name. */
cpu_type = "x86-64/hammer";
break;
case 0x10:
- model = &op_athlon_spec;
+ model = &op_amd_spec;
cpu_type = "x86-64/family10";
break;
+ case 0x11:
+ model = &op_amd_spec;
+ cpu_type = "x86-64/family11h";
+ break;
}
break;
@@ -490,17 +496,24 @@
return -ENODEV;
}
- init_sysfs();
#ifdef CONFIG_SMP
register_cpu_notifier(&oprofile_cpu_nb);
#endif
- using_nmi = 1;
+ /* default values, can be overwritten by model */
ops->create_files = nmi_create_files;
ops->setup = nmi_setup;
ops->shutdown = nmi_shutdown;
ops->start = nmi_start;
ops->stop = nmi_stop;
ops->cpu_type = cpu_type;
+
+ if (model->init)
+ ret = model->init(ops);
+ if (ret)
+ return ret;
+
+ init_sysfs();
+ using_nmi = 1;
printk(KERN_INFO "oprofile: using NMI interrupt.\n");
return 0;
}
@@ -513,4 +526,6 @@
unregister_cpu_notifier(&oprofile_cpu_nb);
#endif
}
+ if (model->exit)
+ model->exit();
}
diff --git a/arch/x86/oprofile/op_model_amd.c b/arch/x86/oprofile/op_model_amd.c
new file mode 100644
index 0000000..d9faf60
--- /dev/null
+++ b/arch/x86/oprofile/op_model_amd.c
@@ -0,0 +1,543 @@
+/*
+ * @file op_model_amd.c
+ * athlon / K7 / K8 / Family 10h model-specific MSR operations
+ *
+ * @remark Copyright 2002-2008 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author John Levon
+ * @author Philippe Elie
+ * @author Graydon Hoare
+ * @author Robert Richter <robert.richter@amd.com>
+ * @author Barry Kasindorf
+*/
+
+#include <linux/oprofile.h>
+#include <linux/device.h>
+#include <linux/pci.h>
+
+#include <asm/ptrace.h>
+#include <asm/msr.h>
+#include <asm/nmi.h>
+
+#include "op_x86_model.h"
+#include "op_counter.h"
+
+#define NUM_COUNTERS 4
+#define NUM_CONTROLS 4
+
+#define CTR_IS_RESERVED(msrs, c) (msrs->counters[(c)].addr ? 1 : 0)
+#define CTR_READ(l, h, msrs, c) do {rdmsr(msrs->counters[(c)].addr, (l), (h)); } while (0)
+#define CTR_WRITE(l, msrs, c) do {wrmsr(msrs->counters[(c)].addr, -(unsigned int)(l), -1); } while (0)
+#define CTR_OVERFLOWED(n) (!((n) & (1U<<31)))
+
+#define CTRL_IS_RESERVED(msrs, c) (msrs->controls[(c)].addr ? 1 : 0)
+#define CTRL_READ(l, h, msrs, c) do {rdmsr(msrs->controls[(c)].addr, (l), (h)); } while (0)
+#define CTRL_WRITE(l, h, msrs, c) do {wrmsr(msrs->controls[(c)].addr, (l), (h)); } while (0)
+#define CTRL_SET_ACTIVE(n) (n |= (1<<22))
+#define CTRL_SET_INACTIVE(n) (n &= ~(1<<22))
+#define CTRL_CLEAR_LO(x) (x &= (1<<21))
+#define CTRL_CLEAR_HI(x) (x &= 0xfffffcf0)
+#define CTRL_SET_ENABLE(val) (val |= 1<<20)
+#define CTRL_SET_USR(val, u) (val |= ((u & 1) << 16))
+#define CTRL_SET_KERN(val, k) (val |= ((k & 1) << 17))
+#define CTRL_SET_UM(val, m) (val |= (m << 8))
+#define CTRL_SET_EVENT_LOW(val, e) (val |= (e & 0xff))
+#define CTRL_SET_EVENT_HIGH(val, e) (val |= ((e >> 8) & 0xf))
+#define CTRL_SET_HOST_ONLY(val, h) (val |= ((h & 1) << 9))
+#define CTRL_SET_GUEST_ONLY(val, h) (val |= ((h & 1) << 8))
+
+static unsigned long reset_value[NUM_COUNTERS];
+
+#ifdef CONFIG_OPROFILE_IBS
+
+/* IbsFetchCtl bits/masks */
+#define IBS_FETCH_HIGH_VALID_BIT (1UL << 17) /* bit 49 */
+#define IBS_FETCH_HIGH_ENABLE (1UL << 16) /* bit 48 */
+#define IBS_FETCH_LOW_MAX_CNT_MASK 0x0000FFFFUL /* MaxCnt mask */
+
+/*IbsOpCtl bits */
+#define IBS_OP_LOW_VALID_BIT (1ULL<<18) /* bit 18 */
+#define IBS_OP_LOW_ENABLE (1ULL<<17) /* bit 17 */
+
+/* Codes used in cpu_buffer.c */
+/* This produces duplicate code, need to be fixed */
+#define IBS_FETCH_BEGIN 3
+#define IBS_OP_BEGIN 4
+
+/* The function interface needs to be fixed, something like add
+ data. Should then be added to linux/oprofile.h. */
+extern void oprofile_add_ibs_sample(struct pt_regs *const regs,
+ unsigned int * const ibs_sample, u8 code);
+
+struct ibs_fetch_sample {
+ /* MSRC001_1031 IBS Fetch Linear Address Register */
+ unsigned int ibs_fetch_lin_addr_low;
+ unsigned int ibs_fetch_lin_addr_high;
+ /* MSRC001_1030 IBS Fetch Control Register */
+ unsigned int ibs_fetch_ctl_low;
+ unsigned int ibs_fetch_ctl_high;
+ /* MSRC001_1032 IBS Fetch Physical Address Register */
+ unsigned int ibs_fetch_phys_addr_low;
+ unsigned int ibs_fetch_phys_addr_high;
+};
+
+struct ibs_op_sample {
+ /* MSRC001_1034 IBS Op Logical Address Register (IbsRIP) */
+ unsigned int ibs_op_rip_low;
+ unsigned int ibs_op_rip_high;
+ /* MSRC001_1035 IBS Op Data Register */
+ unsigned int ibs_op_data1_low;
+ unsigned int ibs_op_data1_high;
+ /* MSRC001_1036 IBS Op Data 2 Register */
+ unsigned int ibs_op_data2_low;
+ unsigned int ibs_op_data2_high;
+ /* MSRC001_1037 IBS Op Data 3 Register */
+ unsigned int ibs_op_data3_low;
+ unsigned int ibs_op_data3_high;
+ /* MSRC001_1038 IBS DC Linear Address Register (IbsDcLinAd) */
+ unsigned int ibs_dc_linear_low;
+ unsigned int ibs_dc_linear_high;
+ /* MSRC001_1039 IBS DC Physical Address Register (IbsDcPhysAd) */
+ unsigned int ibs_dc_phys_low;
+ unsigned int ibs_dc_phys_high;
+};
+
+/*
+ * unitialize the APIC for the IBS interrupts if needed on AMD Family10h+
+*/
+static void clear_ibs_nmi(void);
+
+static int ibs_allowed; /* AMD Family10h and later */
+
+struct op_ibs_config {
+ unsigned long op_enabled;
+ unsigned long fetch_enabled;
+ unsigned long max_cnt_fetch;
+ unsigned long max_cnt_op;
+ unsigned long rand_en;
+ unsigned long dispatched_ops;
+};
+
+static struct op_ibs_config ibs_config;
+
+#endif
+
+/* functions for op_amd_spec */
+
+static void op_amd_fill_in_addresses(struct op_msrs * const msrs)
+{
+ int i;
+
+ for (i = 0; i < NUM_COUNTERS; i++) {
+ if (reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i))
+ msrs->counters[i].addr = MSR_K7_PERFCTR0 + i;
+ else
+ msrs->counters[i].addr = 0;
+ }
+
+ for (i = 0; i < NUM_CONTROLS; i++) {
+ if (reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i))
+ msrs->controls[i].addr = MSR_K7_EVNTSEL0 + i;
+ else
+ msrs->controls[i].addr = 0;
+ }
+}
+
+
+static void op_amd_setup_ctrs(struct op_msrs const * const msrs)
+{
+ unsigned int low, high;
+ int i;
+
+ /* clear all counters */
+ for (i = 0 ; i < NUM_CONTROLS; ++i) {
+ if (unlikely(!CTRL_IS_RESERVED(msrs, i)))
+ continue;
+ CTRL_READ(low, high, msrs, i);
+ CTRL_CLEAR_LO(low);
+ CTRL_CLEAR_HI(high);
+ CTRL_WRITE(low, high, msrs, i);
+ }
+
+ /* avoid a false detection of ctr overflows in NMI handler */
+ for (i = 0; i < NUM_COUNTERS; ++i) {
+ if (unlikely(!CTR_IS_RESERVED(msrs, i)))
+ continue;
+ CTR_WRITE(1, msrs, i);
+ }
+
+ /* enable active counters */
+ for (i = 0; i < NUM_COUNTERS; ++i) {
+ if ((counter_config[i].enabled) && (CTR_IS_RESERVED(msrs, i))) {
+ reset_value[i] = counter_config[i].count;
+
+ CTR_WRITE(counter_config[i].count, msrs, i);
+
+ CTRL_READ(low, high, msrs, i);
+ CTRL_CLEAR_LO(low);
+ CTRL_CLEAR_HI(high);
+ CTRL_SET_ENABLE(low);
+ CTRL_SET_USR(low, counter_config[i].user);
+ CTRL_SET_KERN(low, counter_config[i].kernel);
+ CTRL_SET_UM(low, counter_config[i].unit_mask);
+ CTRL_SET_EVENT_LOW(low, counter_config[i].event);
+ CTRL_SET_EVENT_HIGH(high, counter_config[i].event);
+ CTRL_SET_HOST_ONLY(high, 0);
+ CTRL_SET_GUEST_ONLY(high, 0);
+
+ CTRL_WRITE(low, high, msrs, i);
+ } else {
+ reset_value[i] = 0;
+ }
+ }
+}
+
+#ifdef CONFIG_OPROFILE_IBS
+
+static inline int
+op_amd_handle_ibs(struct pt_regs * const regs,
+ struct op_msrs const * const msrs)
+{
+ unsigned int low, high;
+ struct ibs_fetch_sample ibs_fetch;
+ struct ibs_op_sample ibs_op;
+
+ if (!ibs_allowed)
+ return 1;
+
+ if (ibs_config.fetch_enabled) {
+ rdmsr(MSR_AMD64_IBSFETCHCTL, low, high);
+ if (high & IBS_FETCH_HIGH_VALID_BIT) {
+ ibs_fetch.ibs_fetch_ctl_high = high;
+ ibs_fetch.ibs_fetch_ctl_low = low;
+ rdmsr(MSR_AMD64_IBSFETCHLINAD, low, high);
+ ibs_fetch.ibs_fetch_lin_addr_high = high;
+ ibs_fetch.ibs_fetch_lin_addr_low = low;
+ rdmsr(MSR_AMD64_IBSFETCHPHYSAD, low, high);
+ ibs_fetch.ibs_fetch_phys_addr_high = high;
+ ibs_fetch.ibs_fetch_phys_addr_low = low;
+
+ oprofile_add_ibs_sample(regs,
+ (unsigned int *)&ibs_fetch,
+ IBS_FETCH_BEGIN);
+
+ /*reenable the IRQ */
+ rdmsr(MSR_AMD64_IBSFETCHCTL, low, high);
+ high &= ~IBS_FETCH_HIGH_VALID_BIT;
+ high |= IBS_FETCH_HIGH_ENABLE;
+ low &= IBS_FETCH_LOW_MAX_CNT_MASK;
+ wrmsr(MSR_AMD64_IBSFETCHCTL, low, high);
+ }
+ }
+
+ if (ibs_config.op_enabled) {
+ rdmsr(MSR_AMD64_IBSOPCTL, low, high);
+ if (low & IBS_OP_LOW_VALID_BIT) {
+ rdmsr(MSR_AMD64_IBSOPRIP, low, high);
+ ibs_op.ibs_op_rip_low = low;
+ ibs_op.ibs_op_rip_high = high;
+ rdmsr(MSR_AMD64_IBSOPDATA, low, high);
+ ibs_op.ibs_op_data1_low = low;
+ ibs_op.ibs_op_data1_high = high;
+ rdmsr(MSR_AMD64_IBSOPDATA2, low, high);
+ ibs_op.ibs_op_data2_low = low;
+ ibs_op.ibs_op_data2_high = high;
+ rdmsr(MSR_AMD64_IBSOPDATA3, low, high);
+ ibs_op.ibs_op_data3_low = low;
+ ibs_op.ibs_op_data3_high = high;
+ rdmsr(MSR_AMD64_IBSDCLINAD, low, high);
+ ibs_op.ibs_dc_linear_low = low;
+ ibs_op.ibs_dc_linear_high = high;
+ rdmsr(MSR_AMD64_IBSDCPHYSAD, low, high);
+ ibs_op.ibs_dc_phys_low = low;
+ ibs_op.ibs_dc_phys_high = high;
+
+ /* reenable the IRQ */
+ oprofile_add_ibs_sample(regs,
+ (unsigned int *)&ibs_op,
+ IBS_OP_BEGIN);
+ rdmsr(MSR_AMD64_IBSOPCTL, low, high);
+ high = 0;
+ low &= ~IBS_OP_LOW_VALID_BIT;
+ low |= IBS_OP_LOW_ENABLE;
+ wrmsr(MSR_AMD64_IBSOPCTL, low, high);
+ }
+ }
+
+ return 1;
+}
+
+#endif
+
+static int op_amd_check_ctrs(struct pt_regs * const regs,
+ struct op_msrs const * const msrs)
+{
+ unsigned int low, high;
+ int i;
+
+ for (i = 0 ; i < NUM_COUNTERS; ++i) {
+ if (!reset_value[i])
+ continue;
+ CTR_READ(low, high, msrs, i);
+ if (CTR_OVERFLOWED(low)) {
+ oprofile_add_sample(regs, i);
+ CTR_WRITE(reset_value[i], msrs, i);
+ }
+ }
+
+#ifdef CONFIG_OPROFILE_IBS
+ op_amd_handle_ibs(regs, msrs);
+#endif
+
+ /* See op_model_ppro.c */
+ return 1;
+}
+
+static void op_amd_start(struct op_msrs const * const msrs)
+{
+ unsigned int low, high;
+ int i;
+ for (i = 0 ; i < NUM_COUNTERS ; ++i) {
+ if (reset_value[i]) {
+ CTRL_READ(low, high, msrs, i);
+ CTRL_SET_ACTIVE(low);
+ CTRL_WRITE(low, high, msrs, i);
+ }
+ }
+
+#ifdef CONFIG_OPROFILE_IBS
+ if (ibs_allowed && ibs_config.fetch_enabled) {
+ low = (ibs_config.max_cnt_fetch >> 4) & 0xFFFF;
+ high = IBS_FETCH_HIGH_ENABLE;
+ wrmsr(MSR_AMD64_IBSFETCHCTL, low, high);
+ }
+
+ if (ibs_allowed && ibs_config.op_enabled) {
+ low = ((ibs_config.max_cnt_op >> 4) & 0xFFFF) + IBS_OP_LOW_ENABLE;
+ high = 0;
+ wrmsr(MSR_AMD64_IBSOPCTL, low, high);
+ }
+#endif
+}
+
+
+static void op_amd_stop(struct op_msrs const * const msrs)
+{
+ unsigned int low, high;
+ int i;
+
+ /* Subtle: stop on all counters to avoid race with
+ * setting our pm callback */
+ for (i = 0 ; i < NUM_COUNTERS ; ++i) {
+ if (!reset_value[i])
+ continue;
+ CTRL_READ(low, high, msrs, i);
+ CTRL_SET_INACTIVE(low);
+ CTRL_WRITE(low, high, msrs, i);
+ }
+
+#ifdef CONFIG_OPROFILE_IBS
+ if (ibs_allowed && ibs_config.fetch_enabled) {
+ low = 0; /* clear max count and enable */
+ high = 0;
+ wrmsr(MSR_AMD64_IBSFETCHCTL, low, high);
+ }
+
+ if (ibs_allowed && ibs_config.op_enabled) {
+ low = 0; /* clear max count and enable */
+ high = 0;
+ wrmsr(MSR_AMD64_IBSOPCTL, low, high);
+ }
+#endif
+}
+
+static void op_amd_shutdown(struct op_msrs const * const msrs)
+{
+ int i;
+
+ for (i = 0 ; i < NUM_COUNTERS ; ++i) {
+ if (CTR_IS_RESERVED(msrs, i))
+ release_perfctr_nmi(MSR_K7_PERFCTR0 + i);
+ }
+ for (i = 0 ; i < NUM_CONTROLS ; ++i) {
+ if (CTRL_IS_RESERVED(msrs, i))
+ release_evntsel_nmi(MSR_K7_EVNTSEL0 + i);
+ }
+}
+
+#ifndef CONFIG_OPROFILE_IBS
+
+/* no IBS support */
+
+static int op_amd_init(struct oprofile_operations *ops)
+{
+ return 0;
+}
+
+static void op_amd_exit(void) {}
+
+#else
+
+static u8 ibs_eilvt_off;
+
+static inline void apic_init_ibs_nmi_per_cpu(void *arg)
+{
+ ibs_eilvt_off = setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_NMI, 0);
+}
+
+static inline void apic_clear_ibs_nmi_per_cpu(void *arg)
+{
+ setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_FIX, 1);
+}
+
+static int pfm_amd64_setup_eilvt(void)
+{
+#define IBSCTL_LVTOFFSETVAL (1 << 8)
+#define IBSCTL 0x1cc
+ struct pci_dev *cpu_cfg;
+ int nodes;
+ u32 value = 0;
+
+ /* per CPU setup */
+ on_each_cpu(apic_init_ibs_nmi_per_cpu, NULL, 1);
+
+ nodes = 0;
+ cpu_cfg = NULL;
+ do {
+ cpu_cfg = pci_get_device(PCI_VENDOR_ID_AMD,
+ PCI_DEVICE_ID_AMD_10H_NB_MISC,
+ cpu_cfg);
+ if (!cpu_cfg)
+ break;
+ ++nodes;
+ pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off
+ | IBSCTL_LVTOFFSETVAL);
+ pci_read_config_dword(cpu_cfg, IBSCTL, &value);
+ if (value != (ibs_eilvt_off | IBSCTL_LVTOFFSETVAL)) {
+ printk(KERN_DEBUG "Failed to setup IBS LVT offset, "
+ "IBSCTL = 0x%08x", value);
+ return 1;
+ }
+ } while (1);
+
+ if (!nodes) {
+ printk(KERN_DEBUG "No CPU node configured for IBS");
+ return 1;
+ }
+
+#ifdef CONFIG_NUMA
+ /* Sanity check */
+ /* Works only for 64bit with proper numa implementation. */
+ if (nodes != num_possible_nodes()) {
+ printk(KERN_DEBUG "Failed to setup CPU node(s) for IBS, "
+ "found: %d, expected %d",
+ nodes, num_possible_nodes());
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+/*
+ * initialize the APIC for the IBS interrupts
+ * if available (AMD Family10h rev B0 and later)
+ */
+static void setup_ibs(void)
+{
+ ibs_allowed = boot_cpu_has(X86_FEATURE_IBS);
+
+ if (!ibs_allowed)
+ return;
+
+ if (pfm_amd64_setup_eilvt()) {
+ ibs_allowed = 0;
+ return;
+ }
+
+ printk(KERN_INFO "oprofile: AMD IBS detected\n");
+}
+
+
+/*
+ * unitialize the APIC for the IBS interrupts if needed on AMD Family10h
+ * rev B0 and later */
+static void clear_ibs_nmi(void)
+{
+ if (ibs_allowed)
+ on_each_cpu(apic_clear_ibs_nmi_per_cpu, NULL, 1);
+}
+
+static int (*create_arch_files)(struct super_block * sb, struct dentry * root);
+
+static int setup_ibs_files(struct super_block * sb, struct dentry * root)
+{
+ char buf[12];
+ struct dentry *dir;
+ int ret = 0;
+
+ /* architecture specific files */
+ if (create_arch_files)
+ ret = create_arch_files(sb, root);
+
+ if (ret)
+ return ret;
+
+ if (!ibs_allowed)
+ return ret;
+
+ /* model specific files */
+
+ /* setup some reasonable defaults */
+ ibs_config.max_cnt_fetch = 250000;
+ ibs_config.fetch_enabled = 0;
+ ibs_config.max_cnt_op = 250000;
+ ibs_config.op_enabled = 0;
+ ibs_config.dispatched_ops = 1;
+ snprintf(buf, sizeof(buf), "ibs_fetch");
+ dir = oprofilefs_mkdir(sb, root, buf);
+ oprofilefs_create_ulong(sb, dir, "rand_enable",
+ &ibs_config.rand_en);
+ oprofilefs_create_ulong(sb, dir, "enable",
+ &ibs_config.fetch_enabled);
+ oprofilefs_create_ulong(sb, dir, "max_count",
+ &ibs_config.max_cnt_fetch);
+ snprintf(buf, sizeof(buf), "ibs_uops");
+ dir = oprofilefs_mkdir(sb, root, buf);
+ oprofilefs_create_ulong(sb, dir, "enable",
+ &ibs_config.op_enabled);
+ oprofilefs_create_ulong(sb, dir, "max_count",
+ &ibs_config.max_cnt_op);
+ oprofilefs_create_ulong(sb, dir, "dispatched_ops",
+ &ibs_config.dispatched_ops);
+
+ return 0;
+}
+
+static int op_amd_init(struct oprofile_operations *ops)
+{
+ setup_ibs();
+ create_arch_files = ops->create_files;
+ ops->create_files = setup_ibs_files;
+ return 0;
+}
+
+static void op_amd_exit(void)
+{
+ clear_ibs_nmi();
+}
+
+#endif
+
+struct op_x86_model_spec const op_amd_spec = {
+ .init = op_amd_init,
+ .exit = op_amd_exit,
+ .num_counters = NUM_COUNTERS,
+ .num_controls = NUM_CONTROLS,
+ .fill_in_addresses = &op_amd_fill_in_addresses,
+ .setup_ctrs = &op_amd_setup_ctrs,
+ .check_ctrs = &op_amd_check_ctrs,
+ .start = &op_amd_start,
+ .stop = &op_amd_stop,
+ .shutdown = &op_amd_shutdown
+};
diff --git a/arch/x86/oprofile/op_model_athlon.c b/arch/x86/oprofile/op_model_athlon.c
deleted file mode 100644
index 3d53487..0000000
--- a/arch/x86/oprofile/op_model_athlon.c
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * @file op_model_athlon.h
- * athlon / K7 / K8 / Family 10h model-specific MSR operations
- *
- * @remark Copyright 2002 OProfile authors
- * @remark Read the file COPYING
- *
- * @author John Levon
- * @author Philippe Elie
- * @author Graydon Hoare
- */
-
-#include <linux/oprofile.h>
-#include <asm/ptrace.h>
-#include <asm/msr.h>
-#include <asm/nmi.h>
-
-#include "op_x86_model.h"
-#include "op_counter.h"
-
-#define NUM_COUNTERS 4
-#define NUM_CONTROLS 4
-
-#define CTR_IS_RESERVED(msrs, c) (msrs->counters[(c)].addr ? 1 : 0)
-#define CTR_READ(l, h, msrs, c) do {rdmsr(msrs->counters[(c)].addr, (l), (h)); } while (0)
-#define CTR_WRITE(l, msrs, c) do {wrmsr(msrs->counters[(c)].addr, -(unsigned int)(l), -1); } while (0)
-#define CTR_OVERFLOWED(n) (!((n) & (1U<<31)))
-
-#define CTRL_IS_RESERVED(msrs, c) (msrs->controls[(c)].addr ? 1 : 0)
-#define CTRL_READ(l, h, msrs, c) do {rdmsr(msrs->controls[(c)].addr, (l), (h)); } while (0)
-#define CTRL_WRITE(l, h, msrs, c) do {wrmsr(msrs->controls[(c)].addr, (l), (h)); } while (0)
-#define CTRL_SET_ACTIVE(n) (n |= (1<<22))
-#define CTRL_SET_INACTIVE(n) (n &= ~(1<<22))
-#define CTRL_CLEAR_LO(x) (x &= (1<<21))
-#define CTRL_CLEAR_HI(x) (x &= 0xfffffcf0)
-#define CTRL_SET_ENABLE(val) (val |= 1<<20)
-#define CTRL_SET_USR(val, u) (val |= ((u & 1) << 16))
-#define CTRL_SET_KERN(val, k) (val |= ((k & 1) << 17))
-#define CTRL_SET_UM(val, m) (val |= (m << 8))
-#define CTRL_SET_EVENT_LOW(val, e) (val |= (e & 0xff))
-#define CTRL_SET_EVENT_HIGH(val, e) (val |= ((e >> 8) & 0xf))
-#define CTRL_SET_HOST_ONLY(val, h) (val |= ((h & 1) << 9))
-#define CTRL_SET_GUEST_ONLY(val, h) (val |= ((h & 1) << 8))
-
-static unsigned long reset_value[NUM_COUNTERS];
-
-static void athlon_fill_in_addresses(struct op_msrs * const msrs)
-{
- int i;
-
- for (i = 0; i < NUM_COUNTERS; i++) {
- if (reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i))
- msrs->counters[i].addr = MSR_K7_PERFCTR0 + i;
- else
- msrs->counters[i].addr = 0;
- }
-
- for (i = 0; i < NUM_CONTROLS; i++) {
- if (reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i))
- msrs->controls[i].addr = MSR_K7_EVNTSEL0 + i;
- else
- msrs->controls[i].addr = 0;
- }
-}
-
-
-static void athlon_setup_ctrs(struct op_msrs const * const msrs)
-{
- unsigned int low, high;
- int i;
-
- /* clear all counters */
- for (i = 0 ; i < NUM_CONTROLS; ++i) {
- if (unlikely(!CTRL_IS_RESERVED(msrs, i)))
- continue;
- CTRL_READ(low, high, msrs, i);
- CTRL_CLEAR_LO(low);
- CTRL_CLEAR_HI(high);
- CTRL_WRITE(low, high, msrs, i);
- }
-
- /* avoid a false detection of ctr overflows in NMI handler */
- for (i = 0; i < NUM_COUNTERS; ++i) {
- if (unlikely(!CTR_IS_RESERVED(msrs, i)))
- continue;
- CTR_WRITE(1, msrs, i);
- }
-
- /* enable active counters */
- for (i = 0; i < NUM_COUNTERS; ++i) {
- if ((counter_config[i].enabled) && (CTR_IS_RESERVED(msrs, i))) {
- reset_value[i] = counter_config[i].count;
-
- CTR_WRITE(counter_config[i].count, msrs, i);
-
- CTRL_READ(low, high, msrs, i);
- CTRL_CLEAR_LO(low);
- CTRL_CLEAR_HI(high);
- CTRL_SET_ENABLE(low);
- CTRL_SET_USR(low, counter_config[i].user);
- CTRL_SET_KERN(low, counter_config[i].kernel);
- CTRL_SET_UM(low, counter_config[i].unit_mask);
- CTRL_SET_EVENT_LOW(low, counter_config[i].event);
- CTRL_SET_EVENT_HIGH(high, counter_config[i].event);
- CTRL_SET_HOST_ONLY(high, 0);
- CTRL_SET_GUEST_ONLY(high, 0);
-
- CTRL_WRITE(low, high, msrs, i);
- } else {
- reset_value[i] = 0;
- }
- }
-}
-
-
-static int athlon_check_ctrs(struct pt_regs * const regs,
- struct op_msrs const * const msrs)
-{
- unsigned int low, high;
- int i;
-
- for (i = 0 ; i < NUM_COUNTERS; ++i) {
- if (!reset_value[i])
- continue;
- CTR_READ(low, high, msrs, i);
- if (CTR_OVERFLOWED(low)) {
- oprofile_add_sample(regs, i);
- CTR_WRITE(reset_value[i], msrs, i);
- }
- }
-
- /* See op_model_ppro.c */
- return 1;
-}
-
-
-static void athlon_start(struct op_msrs const * const msrs)
-{
- unsigned int low, high;
- int i;
- for (i = 0 ; i < NUM_COUNTERS ; ++i) {
- if (reset_value[i]) {
- CTRL_READ(low, high, msrs, i);
- CTRL_SET_ACTIVE(low);
- CTRL_WRITE(low, high, msrs, i);
- }
- }
-}
-
-
-static void athlon_stop(struct op_msrs const * const msrs)
-{
- unsigned int low, high;
- int i;
-
- /* Subtle: stop on all counters to avoid race with
- * setting our pm callback */
- for (i = 0 ; i < NUM_COUNTERS ; ++i) {
- if (!reset_value[i])
- continue;
- CTRL_READ(low, high, msrs, i);
- CTRL_SET_INACTIVE(low);
- CTRL_WRITE(low, high, msrs, i);
- }
-}
-
-static void athlon_shutdown(struct op_msrs const * const msrs)
-{
- int i;
-
- for (i = 0 ; i < NUM_COUNTERS ; ++i) {
- if (CTR_IS_RESERVED(msrs, i))
- release_perfctr_nmi(MSR_K7_PERFCTR0 + i);
- }
- for (i = 0 ; i < NUM_CONTROLS ; ++i) {
- if (CTRL_IS_RESERVED(msrs, i))
- release_evntsel_nmi(MSR_K7_EVNTSEL0 + i);
- }
-}
-
-struct op_x86_model_spec const op_athlon_spec = {
- .num_counters = NUM_COUNTERS,
- .num_controls = NUM_CONTROLS,
- .fill_in_addresses = &athlon_fill_in_addresses,
- .setup_ctrs = &athlon_setup_ctrs,
- .check_ctrs = &athlon_check_ctrs,
- .start = &athlon_start,
- .stop = &athlon_stop,
- .shutdown = &athlon_shutdown
-};
diff --git a/arch/x86/oprofile/op_x86_model.h b/arch/x86/oprofile/op_x86_model.h
index 45b605f..05a0261 100644
--- a/arch/x86/oprofile/op_x86_model.h
+++ b/arch/x86/oprofile/op_x86_model.h
@@ -32,6 +32,8 @@
* various x86 CPU models' perfctr support.
*/
struct op_x86_model_spec {
+ int (*init)(struct oprofile_operations *ops);
+ void (*exit)(void);
unsigned int const num_counters;
unsigned int const num_controls;
void (*fill_in_addresses)(struct op_msrs * const msrs);
@@ -46,6 +48,6 @@
extern struct op_x86_model_spec const op_ppro_spec;
extern struct op_x86_model_spec const op_p4_spec;
extern struct op_x86_model_spec const op_p4_ht2_spec;
-extern struct op_x86_model_spec const op_athlon_spec;
+extern struct op_x86_model_spec const op_amd_spec;
#endif /* OP_X86_MODEL_H */
diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c
index 19af069..1d88d2b 100644
--- a/arch/x86/pci/acpi.c
+++ b/arch/x86/pci/acpi.c
@@ -250,10 +250,5 @@
acpi_pci_irq_enable(dev);
}
-#ifdef CONFIG_X86_IO_APIC
- if (acpi_ioapic)
- print_IO_APIC();
-#endif
-
return 0;
}
diff --git a/arch/x86/pci/fixup.c b/arch/x86/pci/fixup.c
index 4bdaa59..3c27a80 100644
--- a/arch/x86/pci/fixup.c
+++ b/arch/x86/pci/fixup.c
@@ -511,3 +511,31 @@
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1202, fam10h_pci_cfg_space_size);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1203, fam10h_pci_cfg_space_size);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1204, fam10h_pci_cfg_space_size);
+
+/*
+ * SB600: Disable BAR1 on device 14.0 to avoid HPET resources from
+ * confusing the PCI engine:
+ */
+static void sb600_disable_hpet_bar(struct pci_dev *dev)
+{
+ u8 val;
+
+ /*
+ * The SB600 and SB700 both share the same device
+ * ID, but the PM register 0x55 does something different
+ * for the SB700, so make sure we are dealing with the
+ * SB600 before touching the bit:
+ */
+
+ pci_read_config_byte(dev, 0x08, &val);
+
+ if (val < 0x2F) {
+ outb(0x55, 0xCD6);
+ val = inb(0xCD7);
+
+ /* Set bit 7 in PM register 0x55 */
+ outb(0x55, 0xCD6);
+ outb(val | 0x80, 0xCD7);
+ }
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_ATI, 0x4385, sb600_disable_hpet_bar);
diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c
index 8791fc5..844df0c 100644
--- a/arch/x86/pci/i386.c
+++ b/arch/x86/pci/i386.c
@@ -33,6 +33,7 @@
#include <linux/bootmem.h>
#include <asm/pat.h>
+#include <asm/e820.h>
#include "pci.h"
@@ -227,6 +228,8 @@
pcibios_allocate_bus_resources(&pci_root_buses);
pcibios_allocate_resources(0);
pcibios_allocate_resources(1);
+
+ e820_reserve_resources_late();
}
/**
diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c
index d963576..654a223 100644
--- a/arch/x86/pci/mmconfig-shared.c
+++ b/arch/x86/pci/mmconfig-shared.c
@@ -209,7 +209,7 @@
return name != NULL;
}
-static void __init pci_mmcfg_insert_resources(unsigned long resource_flags)
+static void __init pci_mmcfg_insert_resources(void)
{
#define PCI_MMCFG_RESOURCE_NAME_LEN 19
int i;
@@ -233,7 +233,7 @@
cfg->pci_segment);
res->start = cfg->address;
res->end = res->start + (num_buses << 20) - 1;
- res->flags = IORESOURCE_MEM | resource_flags;
+ res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
insert_resource(&iomem_resource, res);
names += PCI_MMCFG_RESOURCE_NAME_LEN;
}
@@ -434,11 +434,9 @@
(pci_mmcfg_config[0].address == 0))
return;
- if (pci_mmcfg_arch_init()) {
- if (known_bridge)
- pci_mmcfg_insert_resources(IORESOURCE_BUSY);
+ if (pci_mmcfg_arch_init())
pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF;
- } else {
+ else {
/*
* Signal not to attempt to insert mmcfg resources because
* the architecture mmcfg setup could not initialize.
@@ -475,7 +473,7 @@
* marked so it won't cause request errors when __request_region is
* called.
*/
- pci_mmcfg_insert_resources(0);
+ pci_mmcfg_insert_resources();
return 0;
}
diff --git a/arch/x86/power/cpu_32.c b/arch/x86/power/cpu_32.c
index d3e083d..274d060 100644
--- a/arch/x86/power/cpu_32.c
+++ b/arch/x86/power/cpu_32.c
@@ -11,6 +11,7 @@
#include <linux/suspend.h>
#include <asm/mtrr.h>
#include <asm/mce.h>
+#include <asm/xcr.h>
static struct saved_context saved_context;
@@ -126,6 +127,12 @@
if (boot_cpu_has(X86_FEATURE_SEP))
enable_sep_cpu();
+ /*
+ * restore XCR0 for xsave capable cpu's.
+ */
+ if (cpu_has_xsave)
+ xsetbv(XCR_XFEATURE_ENABLED_MASK, pcntxt_mask);
+
fix_processor_context();
do_fpu_end();
mtrr_ap_init();
diff --git a/arch/x86/power/cpu_64.c b/arch/x86/power/cpu_64.c
index 66bdfb5..e3b6cf7 100644
--- a/arch/x86/power/cpu_64.c
+++ b/arch/x86/power/cpu_64.c
@@ -14,6 +14,7 @@
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/mtrr.h>
+#include <asm/xcr.h>
static void fix_processor_context(void);
@@ -122,6 +123,12 @@
wrmsrl(MSR_GS_BASE, ctxt->gs_base);
wrmsrl(MSR_KERNEL_GS_BASE, ctxt->gs_kernel_base);
+ /*
+ * restore XCR0 for xsave capable cpu's.
+ */
+ if (cpu_has_xsave)
+ xsetbv(XCR_XFEATURE_ENABLED_MASK, pcntxt_mask);
+
fix_processor_context();
do_fpu_end();
diff --git a/arch/x86/xen/Kconfig b/arch/x86/xen/Kconfig
index 3815e42..87b9ab1 100644
--- a/arch/x86/xen/Kconfig
+++ b/arch/x86/xen/Kconfig
@@ -26,5 +26,13 @@
config XEN_SAVE_RESTORE
bool
- depends on PM
- default y
\ No newline at end of file
+ depends on XEN && PM
+ default y
+
+config XEN_DEBUG_FS
+ bool "Enable Xen debug and tuning parameters in debugfs"
+ depends on XEN && DEBUG_FS
+ default n
+ help
+ Enable statistics output and various tuning options in debugfs.
+ Enabling this option may incur a significant performance overhead.
diff --git a/arch/x86/xen/Makefile b/arch/x86/xen/Makefile
index 59c1e53..3139479 100644
--- a/arch/x86/xen/Makefile
+++ b/arch/x86/xen/Makefile
@@ -1,4 +1,12 @@
-obj-y := enlighten.o setup.o multicalls.o mmu.o \
+ifdef CONFIG_FTRACE
+# Do not profile debug and lowlevel utilities
+CFLAGS_REMOVE_spinlock.o = -pg
+CFLAGS_REMOVE_time.o = -pg
+CFLAGS_REMOVE_irq.o = -pg
+endif
+
+obj-y := enlighten.o setup.o multicalls.o mmu.o irq.o \
time.o xen-asm_$(BITS).o grant-table.o suspend.o
-obj-$(CONFIG_SMP) += smp.o
+obj-$(CONFIG_SMP) += smp.o spinlock.o
+obj-$(CONFIG_XEN_DEBUG_FS) += debugfs.o
\ No newline at end of file
diff --git a/arch/x86/xen/debugfs.c b/arch/x86/xen/debugfs.c
new file mode 100644
index 0000000..b53225d
--- /dev/null
+++ b/arch/x86/xen/debugfs.c
@@ -0,0 +1,123 @@
+#include <linux/init.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+
+#include "debugfs.h"
+
+static struct dentry *d_xen_debug;
+
+struct dentry * __init xen_init_debugfs(void)
+{
+ if (!d_xen_debug) {
+ d_xen_debug = debugfs_create_dir("xen", NULL);
+
+ if (!d_xen_debug)
+ pr_warning("Could not create 'xen' debugfs directory\n");
+ }
+
+ return d_xen_debug;
+}
+
+struct array_data
+{
+ void *array;
+ unsigned elements;
+};
+
+static int u32_array_open(struct inode *inode, struct file *file)
+{
+ file->private_data = NULL;
+ return nonseekable_open(inode, file);
+}
+
+static size_t format_array(char *buf, size_t bufsize, const char *fmt,
+ u32 *array, unsigned array_size)
+{
+ size_t ret = 0;
+ unsigned i;
+
+ for(i = 0; i < array_size; i++) {
+ size_t len;
+
+ len = snprintf(buf, bufsize, fmt, array[i]);
+ len++; /* ' ' or '\n' */
+ ret += len;
+
+ if (buf) {
+ buf += len;
+ bufsize -= len;
+ buf[-1] = (i == array_size-1) ? '\n' : ' ';
+ }
+ }
+
+ ret++; /* \0 */
+ if (buf)
+ *buf = '\0';
+
+ return ret;
+}
+
+static char *format_array_alloc(const char *fmt, u32 *array, unsigned array_size)
+{
+ size_t len = format_array(NULL, 0, fmt, array, array_size);
+ char *ret;
+
+ ret = kmalloc(len, GFP_KERNEL);
+ if (ret == NULL)
+ return NULL;
+
+ format_array(ret, len, fmt, array, array_size);
+ return ret;
+}
+
+static ssize_t u32_array_read(struct file *file, char __user *buf, size_t len,
+ loff_t *ppos)
+{
+ struct inode *inode = file->f_path.dentry->d_inode;
+ struct array_data *data = inode->i_private;
+ size_t size;
+
+ if (*ppos == 0) {
+ if (file->private_data) {
+ kfree(file->private_data);
+ file->private_data = NULL;
+ }
+
+ file->private_data = format_array_alloc("%u", data->array, data->elements);
+ }
+
+ size = 0;
+ if (file->private_data)
+ size = strlen(file->private_data);
+
+ return simple_read_from_buffer(buf, len, ppos, file->private_data, size);
+}
+
+static int xen_array_release(struct inode *inode, struct file *file)
+{
+ kfree(file->private_data);
+
+ return 0;
+}
+
+static struct file_operations u32_array_fops = {
+ .owner = THIS_MODULE,
+ .open = u32_array_open,
+ .release= xen_array_release,
+ .read = u32_array_read,
+};
+
+struct dentry *xen_debugfs_create_u32_array(const char *name, mode_t mode,
+ struct dentry *parent,
+ u32 *array, unsigned elements)
+{
+ struct array_data *data = kmalloc(sizeof(*data), GFP_KERNEL);
+
+ if (data == NULL)
+ return NULL;
+
+ data->array = array;
+ data->elements = elements;
+
+ return debugfs_create_file(name, mode, parent, data, &u32_array_fops);
+}
diff --git a/arch/x86/xen/debugfs.h b/arch/x86/xen/debugfs.h
new file mode 100644
index 0000000..e281320
--- /dev/null
+++ b/arch/x86/xen/debugfs.h
@@ -0,0 +1,10 @@
+#ifndef _XEN_DEBUGFS_H
+#define _XEN_DEBUGFS_H
+
+struct dentry * __init xen_init_debugfs(void);
+
+struct dentry *xen_debugfs_create_u32_array(const char *name, mode_t mode,
+ struct dentry *parent,
+ u32 *array, unsigned elements);
+
+#endif /* _XEN_DEBUGFS_H */
diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c
index 7dcd321..0013a72 100644
--- a/arch/x86/xen/enlighten.c
+++ b/arch/x86/xen/enlighten.c
@@ -30,12 +30,12 @@
#include <xen/interface/xen.h>
#include <xen/interface/physdev.h>
#include <xen/interface/vcpu.h>
-#include <xen/interface/sched.h>
#include <xen/features.h>
#include <xen/page.h>
#include <xen/hvc-console.h>
#include <asm/paravirt.h>
+#include <asm/apic.h>
#include <asm/page.h>
#include <asm/xen/hypercall.h>
#include <asm/xen/hypervisor.h>
@@ -57,6 +57,9 @@
DEFINE_PER_CPU(struct vcpu_info *, xen_vcpu);
DEFINE_PER_CPU(struct vcpu_info, xen_vcpu_info);
+enum xen_domain_type xen_domain_type = XEN_NATIVE;
+EXPORT_SYMBOL_GPL(xen_domain_type);
+
/*
* Identity map, in addition to plain kernel map. This needs to be
* large enough to allocate page table pages to allocate the rest.
@@ -110,7 +113,14 @@
*
* 0: not available, 1: available
*/
-static int have_vcpu_info_placement = 1;
+static int have_vcpu_info_placement =
+#ifdef CONFIG_X86_32
+ 1
+#else
+ 0
+#endif
+ ;
+
static void xen_vcpu_setup(int cpu)
{
@@ -226,94 +236,6 @@
return HYPERVISOR_get_debugreg(reg);
}
-static unsigned long xen_save_fl(void)
-{
- struct vcpu_info *vcpu;
- unsigned long flags;
-
- vcpu = x86_read_percpu(xen_vcpu);
-
- /* flag has opposite sense of mask */
- flags = !vcpu->evtchn_upcall_mask;
-
- /* convert to IF type flag
- -0 -> 0x00000000
- -1 -> 0xffffffff
- */
- return (-flags) & X86_EFLAGS_IF;
-}
-
-static void xen_restore_fl(unsigned long flags)
-{
- struct vcpu_info *vcpu;
-
- /* convert from IF type flag */
- flags = !(flags & X86_EFLAGS_IF);
-
- /* There's a one instruction preempt window here. We need to
- make sure we're don't switch CPUs between getting the vcpu
- pointer and updating the mask. */
- preempt_disable();
- vcpu = x86_read_percpu(xen_vcpu);
- vcpu->evtchn_upcall_mask = flags;
- preempt_enable_no_resched();
-
- /* Doesn't matter if we get preempted here, because any
- pending event will get dealt with anyway. */
-
- if (flags == 0) {
- preempt_check_resched();
- barrier(); /* unmask then check (avoid races) */
- if (unlikely(vcpu->evtchn_upcall_pending))
- force_evtchn_callback();
- }
-}
-
-static void xen_irq_disable(void)
-{
- /* There's a one instruction preempt window here. We need to
- make sure we're don't switch CPUs between getting the vcpu
- pointer and updating the mask. */
- preempt_disable();
- x86_read_percpu(xen_vcpu)->evtchn_upcall_mask = 1;
- preempt_enable_no_resched();
-}
-
-static void xen_irq_enable(void)
-{
- struct vcpu_info *vcpu;
-
- /* We don't need to worry about being preempted here, since
- either a) interrupts are disabled, so no preemption, or b)
- the caller is confused and is trying to re-enable interrupts
- on an indeterminate processor. */
-
- vcpu = x86_read_percpu(xen_vcpu);
- vcpu->evtchn_upcall_mask = 0;
-
- /* Doesn't matter if we get preempted here, because any
- pending event will get dealt with anyway. */
-
- barrier(); /* unmask then check (avoid races) */
- if (unlikely(vcpu->evtchn_upcall_pending))
- force_evtchn_callback();
-}
-
-static void xen_safe_halt(void)
-{
- /* Blocking includes an implicit local_irq_enable(). */
- if (HYPERVISOR_sched_op(SCHEDOP_block, NULL) != 0)
- BUG();
-}
-
-static void xen_halt(void)
-{
- if (irqs_disabled())
- HYPERVISOR_vcpu_op(VCPUOP_down, smp_processor_id(), NULL);
- else
- xen_safe_halt();
-}
-
static void xen_leave_lazy(void)
{
paravirt_leave_lazy(paravirt_get_lazy_mode());
@@ -325,6 +247,59 @@
return 0;
}
+/*
+ * Set the page permissions for a particular virtual address. If the
+ * address is a vmalloc mapping (or other non-linear mapping), then
+ * find the linear mapping of the page and also set its protections to
+ * match.
+ */
+static void set_aliased_prot(void *v, pgprot_t prot)
+{
+ int level;
+ pte_t *ptep;
+ pte_t pte;
+ unsigned long pfn;
+ struct page *page;
+
+ ptep = lookup_address((unsigned long)v, &level);
+ BUG_ON(ptep == NULL);
+
+ pfn = pte_pfn(*ptep);
+ page = pfn_to_page(pfn);
+
+ pte = pfn_pte(pfn, prot);
+
+ if (HYPERVISOR_update_va_mapping((unsigned long)v, pte, 0))
+ BUG();
+
+ if (!PageHighMem(page)) {
+ void *av = __va(PFN_PHYS(pfn));
+
+ if (av != v)
+ if (HYPERVISOR_update_va_mapping((unsigned long)av, pte, 0))
+ BUG();
+ } else
+ kmap_flush_unused();
+}
+
+static void xen_alloc_ldt(struct desc_struct *ldt, unsigned entries)
+{
+ const unsigned entries_per_page = PAGE_SIZE / LDT_ENTRY_SIZE;
+ int i;
+
+ for(i = 0; i < entries; i += entries_per_page)
+ set_aliased_prot(ldt + i, PAGE_KERNEL_RO);
+}
+
+static void xen_free_ldt(struct desc_struct *ldt, unsigned entries)
+{
+ const unsigned entries_per_page = PAGE_SIZE / LDT_ENTRY_SIZE;
+ int i;
+
+ for(i = 0; i < entries; i += entries_per_page)
+ set_aliased_prot(ldt + i, PAGE_KERNEL);
+}
+
static void xen_set_ldt(const void *addr, unsigned entries)
{
struct mmuext_op *op;
@@ -425,8 +400,7 @@
static void xen_write_ldt_entry(struct desc_struct *dt, int entrynum,
const void *ptr)
{
- unsigned long lp = (unsigned long)&dt[entrynum];
- xmaddr_t mach_lp = virt_to_machine(lp);
+ xmaddr_t mach_lp = arbitrary_virt_to_machine(&dt[entrynum]);
u64 entry = *(u64 *)ptr;
preempt_disable();
@@ -559,7 +533,7 @@
}
static void xen_load_sp0(struct tss_struct *tss,
- struct thread_struct *thread)
+ struct thread_struct *thread)
{
struct multicall_space mcs = xen_mc_entry(0);
MULTI_stack_switch(mcs.mc, __KERNEL_DS, thread->sp0);
@@ -580,16 +554,47 @@
}
#ifdef CONFIG_X86_LOCAL_APIC
-static u32 xen_apic_read(unsigned long reg)
+static u32 xen_apic_read(u32 reg)
{
return 0;
}
-static void xen_apic_write(unsigned long reg, u32 val)
+static void xen_apic_write(u32 reg, u32 val)
{
/* Warn to see if there's any stray references */
WARN_ON(1);
}
+
+static u64 xen_apic_icr_read(void)
+{
+ return 0;
+}
+
+static void xen_apic_icr_write(u32 low, u32 id)
+{
+ /* Warn to see if there's any stray references */
+ WARN_ON(1);
+}
+
+static void xen_apic_wait_icr_idle(void)
+{
+ return;
+}
+
+static u32 xen_safe_apic_wait_icr_idle(void)
+{
+ return 0;
+}
+
+static struct apic_ops xen_basic_apic_ops = {
+ .read = xen_apic_read,
+ .write = xen_apic_write,
+ .icr_read = xen_apic_icr_read,
+ .icr_write = xen_apic_icr_write,
+ .wait_icr_idle = xen_apic_wait_icr_idle,
+ .safe_wait_icr_idle = xen_safe_apic_wait_icr_idle,
+};
+
#endif
static void xen_flush_tlb(void)
@@ -803,6 +808,19 @@
ret = -EFAULT;
break;
#endif
+
+ case MSR_STAR:
+ case MSR_CSTAR:
+ case MSR_LSTAR:
+ case MSR_SYSCALL_MASK:
+ case MSR_IA32_SYSENTER_CS:
+ case MSR_IA32_SYSENTER_ESP:
+ case MSR_IA32_SYSENTER_EIP:
+ /* Fast syscall setup is all done in hypercalls, so
+ these are all ignored. Stub them out here to stop
+ Xen console noise. */
+ break;
+
default:
ret = native_write_msr_safe(msr, low, high);
}
@@ -846,8 +864,8 @@
SetPagePinned(page);
if (!PageHighMem(page)) {
- make_lowmem_page_readonly(__va(PFN_PHYS(pfn)));
- if (level == PT_PTE)
+ make_lowmem_page_readonly(__va(PFN_PHYS((unsigned long)pfn)));
+ if (level == PT_PTE && USE_SPLIT_PTLOCKS)
pin_pagetable_pfn(MMUEXT_PIN_L1_TABLE, pfn);
} else
/* make sure there are no stray mappings of
@@ -915,7 +933,7 @@
if (PagePinned(page)) {
if (!PageHighMem(page)) {
- if (level == PT_PTE)
+ if (level == PT_PTE && USE_SPLIT_PTLOCKS)
pin_pagetable_pfn(MMUEXT_UNPIN_TABLE, pfn);
make_lowmem_page_readwrite(__va(PFN_PHYS(pfn)));
}
@@ -962,6 +980,7 @@
}
#endif
+#ifdef CONFIG_X86_32
static __init pte_t mask_rw_pte(pte_t *ptep, pte_t pte)
{
/* If there's an existing pte, then don't allow _PAGE_RW to be set */
@@ -980,6 +999,7 @@
xen_set_pte(ptep, pte);
}
+#endif
static __init void xen_pagetable_setup_start(pgd_t *base)
{
@@ -1046,7 +1066,6 @@
/* xen_vcpu_setup managed to place the vcpu_info within the
percpu area for all cpus, so make use of it */
-#ifdef CONFIG_X86_32
if (have_vcpu_info_placement) {
printk(KERN_INFO "Xen: using vcpu_info placement\n");
@@ -1056,7 +1075,6 @@
pv_irq_ops.irq_enable = xen_irq_enable_direct;
pv_mmu_ops.read_cr2 = xen_read_cr2_direct;
}
-#endif
}
static unsigned xen_patch(u8 type, u16 clobbers, void *insnbuf,
@@ -1077,12 +1095,10 @@
goto patch_site
switch (type) {
-#ifdef CONFIG_X86_32
SITE(pv_irq_ops, irq_enable);
SITE(pv_irq_ops, irq_disable);
SITE(pv_irq_ops, save_fl);
SITE(pv_irq_ops, restore_fl);
-#endif /* CONFIG_X86_32 */
#undef SITE
patch_site:
@@ -1220,6 +1236,9 @@
.load_gs_index = xen_load_gs_index,
#endif
+ .alloc_ldt = xen_alloc_ldt,
+ .free_ldt = xen_free_ldt,
+
.store_gdt = native_store_gdt,
.store_idt = native_store_idt,
.store_tr = xen_store_tr,
@@ -1241,40 +1260,8 @@
},
};
-static void __init __xen_init_IRQ(void)
-{
-#ifdef CONFIG_X86_64
- int i;
-
- /* Create identity vector->irq map */
- for(i = 0; i < NR_VECTORS; i++) {
- int cpu;
-
- for_each_possible_cpu(cpu)
- per_cpu(vector_irq, cpu)[i] = i;
- }
-#endif /* CONFIG_X86_64 */
-
- xen_init_IRQ();
-}
-
-static const struct pv_irq_ops xen_irq_ops __initdata = {
- .init_IRQ = __xen_init_IRQ,
- .save_fl = xen_save_fl,
- .restore_fl = xen_restore_fl,
- .irq_disable = xen_irq_disable,
- .irq_enable = xen_irq_enable,
- .safe_halt = xen_safe_halt,
- .halt = xen_halt,
-#ifdef CONFIG_X86_64
- .adjust_exception_frame = xen_adjust_exception_frame,
-#endif
-};
-
static const struct pv_apic_ops xen_apic_ops __initdata = {
#ifdef CONFIG_X86_LOCAL_APIC
- .apic_write = xen_apic_write,
- .apic_read = xen_apic_read,
.setup_boot_clock = paravirt_nop,
.setup_secondary_clock = paravirt_nop,
.startup_ipi_hook = paravirt_nop,
@@ -1413,7 +1400,7 @@
if (HYPERVISOR_xen_version(XENVER_platform_parameters, &pp) == 0)
top = pp.virt_start;
- reserve_top_address(-top + 2 * PAGE_SIZE);
+ reserve_top_address(-top);
#endif /* CONFIG_X86_32 */
}
@@ -1447,48 +1434,11 @@
return __ka(m2p(maddr));
}
-#ifdef CONFIG_X86_64
-static void walk(pgd_t *pgd, unsigned long addr)
-{
- unsigned l4idx = pgd_index(addr);
- unsigned l3idx = pud_index(addr);
- unsigned l2idx = pmd_index(addr);
- unsigned l1idx = pte_index(addr);
- pgd_t l4;
- pud_t l3;
- pmd_t l2;
- pte_t l1;
-
- xen_raw_printk("walk %p, %lx -> %d %d %d %d\n",
- pgd, addr, l4idx, l3idx, l2idx, l1idx);
-
- l4 = pgd[l4idx];
- xen_raw_printk(" l4: %016lx\n", l4.pgd);
- xen_raw_printk(" %016lx\n", pgd_val(l4));
-
- l3 = ((pud_t *)(m2v(l4.pgd)))[l3idx];
- xen_raw_printk(" l3: %016lx\n", l3.pud);
- xen_raw_printk(" %016lx\n", pud_val(l3));
-
- l2 = ((pmd_t *)(m2v(l3.pud)))[l2idx];
- xen_raw_printk(" l2: %016lx\n", l2.pmd);
- xen_raw_printk(" %016lx\n", pmd_val(l2));
-
- l1 = ((pte_t *)(m2v(l2.pmd)))[l1idx];
- xen_raw_printk(" l1: %016lx\n", l1.pte);
- xen_raw_printk(" %016lx\n", pte_val(l1));
-}
-#endif
-
static void set_page_prot(void *addr, pgprot_t prot)
{
unsigned long pfn = __pa(addr) >> PAGE_SHIFT;
pte_t pte = pfn_pte(pfn, prot);
- xen_raw_printk("addr=%p pfn=%lx mfn=%lx prot=%016llx pte=%016llx\n",
- addr, pfn, get_phys_to_machine(pfn),
- pgprot_val(prot), pte.pte);
-
if (HYPERVISOR_update_va_mapping((unsigned long)addr, pte, 0))
BUG();
}
@@ -1664,6 +1614,8 @@
if (!xen_start_info)
return;
+ xen_domain_type = XEN_PV_DOMAIN;
+
BUG_ON(memcmp(xen_start_info->magic, "xen-3", 5) != 0);
xen_setup_features();
@@ -1673,10 +1625,18 @@
pv_init_ops = xen_init_ops;
pv_time_ops = xen_time_ops;
pv_cpu_ops = xen_cpu_ops;
- pv_irq_ops = xen_irq_ops;
pv_apic_ops = xen_apic_ops;
pv_mmu_ops = xen_mmu_ops;
+ xen_init_irq_ops();
+
+#ifdef CONFIG_X86_LOCAL_APIC
+ /*
+ * set up the basic apic ops.
+ */
+ apic_ops = &xen_basic_apic_ops;
+#endif
+
if (xen_feature(XENFEAT_mmu_pt_update_preserve_ad)) {
pv_mmu_ops.ptep_modify_prot_start = xen_ptep_modify_prot_start;
pv_mmu_ops.ptep_modify_prot_commit = xen_ptep_modify_prot_commit;
@@ -1700,7 +1660,7 @@
/* Prevent unwanted bits from being set in PTEs. */
__supported_pte_mask &= ~_PAGE_GLOBAL;
- if (!is_initial_xendomain())
+ if (!xen_initial_domain())
__supported_pte_mask &= ~(_PAGE_PWT | _PAGE_PCD);
/* Don't do the full vcpu_info placement stuff until we have a
@@ -1735,7 +1695,7 @@
boot_params.hdr.ramdisk_size = xen_start_info->mod_len;
boot_params.hdr.cmd_line_ptr = __pa(xen_start_info->cmd_line);
- if (!is_initial_xendomain()) {
+ if (!xen_initial_domain()) {
add_preferred_console("xenboot", 0, NULL);
add_preferred_console("tty", 0, NULL);
add_preferred_console("hvc", 0, NULL);
@@ -1743,15 +1703,6 @@
xen_raw_console_write("about to get started...\n");
-#if 0
- xen_raw_printk("&boot_params=%p __pa(&boot_params)=%lx __va(__pa(&boot_params))=%lx\n",
- &boot_params, __pa_symbol(&boot_params),
- __va(__pa_symbol(&boot_params)));
-
- walk(pgd, &boot_params);
- walk(pgd, __va(__pa(&boot_params)));
-#endif
-
/* Start the world */
#ifdef CONFIG_X86_32
i386_start_kernel();
diff --git a/arch/x86/xen/irq.c b/arch/x86/xen/irq.c
new file mode 100644
index 0000000..28b85ab
--- /dev/null
+++ b/arch/x86/xen/irq.c
@@ -0,0 +1,143 @@
+#include <linux/hardirq.h>
+
+#include <xen/interface/xen.h>
+#include <xen/interface/sched.h>
+#include <xen/interface/vcpu.h>
+
+#include <asm/xen/hypercall.h>
+#include <asm/xen/hypervisor.h>
+
+#include "xen-ops.h"
+
+/*
+ * Force a proper event-channel callback from Xen after clearing the
+ * callback mask. We do this in a very simple manner, by making a call
+ * down into Xen. The pending flag will be checked by Xen on return.
+ */
+void xen_force_evtchn_callback(void)
+{
+ (void)HYPERVISOR_xen_version(0, NULL);
+}
+
+static void __init __xen_init_IRQ(void)
+{
+#ifdef CONFIG_X86_64
+ int i;
+
+ /* Create identity vector->irq map */
+ for(i = 0; i < NR_VECTORS; i++) {
+ int cpu;
+
+ for_each_possible_cpu(cpu)
+ per_cpu(vector_irq, cpu)[i] = i;
+ }
+#endif /* CONFIG_X86_64 */
+
+ xen_init_IRQ();
+}
+
+static unsigned long xen_save_fl(void)
+{
+ struct vcpu_info *vcpu;
+ unsigned long flags;
+
+ vcpu = x86_read_percpu(xen_vcpu);
+
+ /* flag has opposite sense of mask */
+ flags = !vcpu->evtchn_upcall_mask;
+
+ /* convert to IF type flag
+ -0 -> 0x00000000
+ -1 -> 0xffffffff
+ */
+ return (-flags) & X86_EFLAGS_IF;
+}
+
+static void xen_restore_fl(unsigned long flags)
+{
+ struct vcpu_info *vcpu;
+
+ /* convert from IF type flag */
+ flags = !(flags & X86_EFLAGS_IF);
+
+ /* There's a one instruction preempt window here. We need to
+ make sure we're don't switch CPUs between getting the vcpu
+ pointer and updating the mask. */
+ preempt_disable();
+ vcpu = x86_read_percpu(xen_vcpu);
+ vcpu->evtchn_upcall_mask = flags;
+ preempt_enable_no_resched();
+
+ /* Doesn't matter if we get preempted here, because any
+ pending event will get dealt with anyway. */
+
+ if (flags == 0) {
+ preempt_check_resched();
+ barrier(); /* unmask then check (avoid races) */
+ if (unlikely(vcpu->evtchn_upcall_pending))
+ xen_force_evtchn_callback();
+ }
+}
+
+static void xen_irq_disable(void)
+{
+ /* There's a one instruction preempt window here. We need to
+ make sure we're don't switch CPUs between getting the vcpu
+ pointer and updating the mask. */
+ preempt_disable();
+ x86_read_percpu(xen_vcpu)->evtchn_upcall_mask = 1;
+ preempt_enable_no_resched();
+}
+
+static void xen_irq_enable(void)
+{
+ struct vcpu_info *vcpu;
+
+ /* We don't need to worry about being preempted here, since
+ either a) interrupts are disabled, so no preemption, or b)
+ the caller is confused and is trying to re-enable interrupts
+ on an indeterminate processor. */
+
+ vcpu = x86_read_percpu(xen_vcpu);
+ vcpu->evtchn_upcall_mask = 0;
+
+ /* Doesn't matter if we get preempted here, because any
+ pending event will get dealt with anyway. */
+
+ barrier(); /* unmask then check (avoid races) */
+ if (unlikely(vcpu->evtchn_upcall_pending))
+ xen_force_evtchn_callback();
+}
+
+static void xen_safe_halt(void)
+{
+ /* Blocking includes an implicit local_irq_enable(). */
+ if (HYPERVISOR_sched_op(SCHEDOP_block, NULL) != 0)
+ BUG();
+}
+
+static void xen_halt(void)
+{
+ if (irqs_disabled())
+ HYPERVISOR_vcpu_op(VCPUOP_down, smp_processor_id(), NULL);
+ else
+ xen_safe_halt();
+}
+
+static const struct pv_irq_ops xen_irq_ops __initdata = {
+ .init_IRQ = __xen_init_IRQ,
+ .save_fl = xen_save_fl,
+ .restore_fl = xen_restore_fl,
+ .irq_disable = xen_irq_disable,
+ .irq_enable = xen_irq_enable,
+ .safe_halt = xen_safe_halt,
+ .halt = xen_halt,
+#ifdef CONFIG_X86_64
+ .adjust_exception_frame = xen_adjust_exception_frame,
+#endif
+};
+
+void __init xen_init_irq_ops()
+{
+ pv_irq_ops = xen_irq_ops;
+}
diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c
index aa37469..ae173f6 100644
--- a/arch/x86/xen/mmu.c
+++ b/arch/x86/xen/mmu.c
@@ -40,6 +40,7 @@
*/
#include <linux/sched.h>
#include <linux/highmem.h>
+#include <linux/debugfs.h>
#include <linux/bug.h>
#include <asm/pgtable.h>
@@ -57,6 +58,61 @@
#include "multicalls.h"
#include "mmu.h"
+#include "debugfs.h"
+
+#define MMU_UPDATE_HISTO 30
+
+#ifdef CONFIG_XEN_DEBUG_FS
+
+static struct {
+ u32 pgd_update;
+ u32 pgd_update_pinned;
+ u32 pgd_update_batched;
+
+ u32 pud_update;
+ u32 pud_update_pinned;
+ u32 pud_update_batched;
+
+ u32 pmd_update;
+ u32 pmd_update_pinned;
+ u32 pmd_update_batched;
+
+ u32 pte_update;
+ u32 pte_update_pinned;
+ u32 pte_update_batched;
+
+ u32 mmu_update;
+ u32 mmu_update_extended;
+ u32 mmu_update_histo[MMU_UPDATE_HISTO];
+
+ u32 prot_commit;
+ u32 prot_commit_batched;
+
+ u32 set_pte_at;
+ u32 set_pte_at_batched;
+ u32 set_pte_at_pinned;
+ u32 set_pte_at_current;
+ u32 set_pte_at_kernel;
+} mmu_stats;
+
+static u8 zero_stats;
+
+static inline void check_zero(void)
+{
+ if (unlikely(zero_stats)) {
+ memset(&mmu_stats, 0, sizeof(mmu_stats));
+ zero_stats = 0;
+ }
+}
+
+#define ADD_STATS(elem, val) \
+ do { check_zero(); mmu_stats.elem += (val); } while(0)
+
+#else /* !CONFIG_XEN_DEBUG_FS */
+
+#define ADD_STATS(elem, val) do { (void)(val); } while(0)
+
+#endif /* CONFIG_XEN_DEBUG_FS */
/*
* Just beyond the highest usermode address. STACK_TOP_MAX has a
@@ -229,25 +285,35 @@
}
-static bool page_pinned(void *ptr)
+static bool xen_page_pinned(void *ptr)
{
struct page *page = virt_to_page(ptr);
return PagePinned(page);
}
-static void extend_mmu_update(const struct mmu_update *update)
+static void xen_extend_mmu_update(const struct mmu_update *update)
{
struct multicall_space mcs;
struct mmu_update *u;
mcs = xen_mc_extend_args(__HYPERVISOR_mmu_update, sizeof(*u));
- if (mcs.mc != NULL)
+ if (mcs.mc != NULL) {
+ ADD_STATS(mmu_update_extended, 1);
+ ADD_STATS(mmu_update_histo[mcs.mc->args[1]], -1);
+
mcs.mc->args[1]++;
- else {
+
+ if (mcs.mc->args[1] < MMU_UPDATE_HISTO)
+ ADD_STATS(mmu_update_histo[mcs.mc->args[1]], 1);
+ else
+ ADD_STATS(mmu_update_histo[0], 1);
+ } else {
+ ADD_STATS(mmu_update, 1);
mcs = __xen_mc_entry(sizeof(*u));
MULTI_mmu_update(mcs.mc, mcs.args, 1, NULL, DOMID_SELF);
+ ADD_STATS(mmu_update_histo[1], 1);
}
u = mcs.args;
@@ -265,7 +331,9 @@
/* ptr may be ioremapped for 64-bit pagetable setup */
u.ptr = arbitrary_virt_to_machine(ptr).maddr;
u.val = pmd_val_ma(val);
- extend_mmu_update(&u);
+ xen_extend_mmu_update(&u);
+
+ ADD_STATS(pmd_update_batched, paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU);
xen_mc_issue(PARAVIRT_LAZY_MMU);
@@ -274,13 +342,17 @@
void xen_set_pmd(pmd_t *ptr, pmd_t val)
{
+ ADD_STATS(pmd_update, 1);
+
/* If page is not pinned, we can just update the entry
directly */
- if (!page_pinned(ptr)) {
+ if (!xen_page_pinned(ptr)) {
*ptr = val;
return;
}
+ ADD_STATS(pmd_update_pinned, 1);
+
xen_set_pmd_hyper(ptr, val);
}
@@ -300,12 +372,18 @@
if (mm == &init_mm)
preempt_disable();
+ ADD_STATS(set_pte_at, 1);
+// ADD_STATS(set_pte_at_pinned, xen_page_pinned(ptep));
+ ADD_STATS(set_pte_at_current, mm == current->mm);
+ ADD_STATS(set_pte_at_kernel, mm == &init_mm);
+
if (mm == current->mm || mm == &init_mm) {
if (paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU) {
struct multicall_space mcs;
mcs = xen_mc_entry(0);
MULTI_update_va_mapping(mcs.mc, addr, pteval, 0);
+ ADD_STATS(set_pte_at_batched, 1);
xen_mc_issue(PARAVIRT_LAZY_MMU);
goto out;
} else
@@ -334,7 +412,10 @@
u.ptr = virt_to_machine(ptep).maddr | MMU_PT_UPDATE_PRESERVE_AD;
u.val = pte_val_ma(pte);
- extend_mmu_update(&u);
+ xen_extend_mmu_update(&u);
+
+ ADD_STATS(prot_commit, 1);
+ ADD_STATS(prot_commit_batched, paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU);
xen_mc_issue(PARAVIRT_LAZY_MMU);
}
@@ -400,7 +481,9 @@
/* ptr may be ioremapped for 64-bit pagetable setup */
u.ptr = arbitrary_virt_to_machine(ptr).maddr;
u.val = pud_val_ma(val);
- extend_mmu_update(&u);
+ xen_extend_mmu_update(&u);
+
+ ADD_STATS(pud_update_batched, paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU);
xen_mc_issue(PARAVIRT_LAZY_MMU);
@@ -409,18 +492,26 @@
void xen_set_pud(pud_t *ptr, pud_t val)
{
+ ADD_STATS(pud_update, 1);
+
/* If page is not pinned, we can just update the entry
directly */
- if (!page_pinned(ptr)) {
+ if (!xen_page_pinned(ptr)) {
*ptr = val;
return;
}
+ ADD_STATS(pud_update_pinned, 1);
+
xen_set_pud_hyper(ptr, val);
}
void xen_set_pte(pte_t *ptep, pte_t pte)
{
+ ADD_STATS(pte_update, 1);
+// ADD_STATS(pte_update_pinned, xen_page_pinned(ptep));
+ ADD_STATS(pte_update_batched, paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU);
+
#ifdef CONFIG_X86_PAE
ptep->pte_high = pte.pte_high;
smp_wmb();
@@ -490,7 +581,7 @@
u.ptr = virt_to_machine(ptr).maddr;
u.val = pgd_val_ma(val);
- extend_mmu_update(&u);
+ xen_extend_mmu_update(&u);
}
/*
@@ -517,17 +608,22 @@
{
pgd_t *user_ptr = xen_get_user_pgd(ptr);
+ ADD_STATS(pgd_update, 1);
+
/* If page is not pinned, we can just update the entry
directly */
- if (!page_pinned(ptr)) {
+ if (!xen_page_pinned(ptr)) {
*ptr = val;
if (user_ptr) {
- WARN_ON(page_pinned(user_ptr));
+ WARN_ON(xen_page_pinned(user_ptr));
*user_ptr = val;
}
return;
}
+ ADD_STATS(pgd_update_pinned, 1);
+ ADD_STATS(pgd_update_batched, paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU);
+
/* If it's pinned, then we can at least batch the kernel and
user updates together. */
xen_mc_batch();
@@ -555,9 +651,12 @@
* For 64-bit, we must skip the Xen hole in the middle of the address
* space, just after the big x86-64 virtual hole.
*/
-static int pgd_walk(pgd_t *pgd, int (*func)(struct page *, enum pt_level),
- unsigned long limit)
+static int xen_pgd_walk(struct mm_struct *mm,
+ int (*func)(struct mm_struct *mm, struct page *,
+ enum pt_level),
+ unsigned long limit)
{
+ pgd_t *pgd = mm->pgd;
int flush = 0;
unsigned hole_low, hole_high;
unsigned pgdidx_limit, pudidx_limit, pmdidx_limit;
@@ -590,8 +689,6 @@
pmdidx_limit = 0;
#endif
- flush |= (*func)(virt_to_page(pgd), PT_PGD);
-
for (pgdidx = 0; pgdidx <= pgdidx_limit; pgdidx++) {
pud_t *pud;
@@ -604,7 +701,7 @@
pud = pud_offset(&pgd[pgdidx], 0);
if (PTRS_PER_PUD > 1) /* not folded */
- flush |= (*func)(virt_to_page(pud), PT_PUD);
+ flush |= (*func)(mm, virt_to_page(pud), PT_PUD);
for (pudidx = 0; pudidx < PTRS_PER_PUD; pudidx++) {
pmd_t *pmd;
@@ -619,7 +716,7 @@
pmd = pmd_offset(&pud[pudidx], 0);
if (PTRS_PER_PMD > 1) /* not folded */
- flush |= (*func)(virt_to_page(pmd), PT_PMD);
+ flush |= (*func)(mm, virt_to_page(pmd), PT_PMD);
for (pmdidx = 0; pmdidx < PTRS_PER_PMD; pmdidx++) {
struct page *pte;
@@ -633,28 +730,34 @@
continue;
pte = pmd_page(pmd[pmdidx]);
- flush |= (*func)(pte, PT_PTE);
+ flush |= (*func)(mm, pte, PT_PTE);
}
}
}
+
out:
+ /* Do the top level last, so that the callbacks can use it as
+ a cue to do final things like tlb flushes. */
+ flush |= (*func)(mm, virt_to_page(pgd), PT_PGD);
return flush;
}
-static spinlock_t *lock_pte(struct page *page)
+/* If we're using split pte locks, then take the page's lock and
+ return a pointer to it. Otherwise return NULL. */
+static spinlock_t *xen_pte_lock(struct page *page, struct mm_struct *mm)
{
spinlock_t *ptl = NULL;
-#if NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS
+#if USE_SPLIT_PTLOCKS
ptl = __pte_lockptr(page);
- spin_lock(ptl);
+ spin_lock_nest_lock(ptl, &mm->page_table_lock);
#endif
return ptl;
}
-static void do_unlock(void *v)
+static void xen_pte_unlock(void *v)
{
spinlock_t *ptl = v;
spin_unlock(ptl);
@@ -672,7 +775,8 @@
MULTI_mmuext_op(mcs.mc, op, 1, NULL, DOMID_SELF);
}
-static int pin_page(struct page *page, enum pt_level level)
+static int xen_pin_page(struct mm_struct *mm, struct page *page,
+ enum pt_level level)
{
unsigned pgfl = TestSetPagePinned(page);
int flush;
@@ -691,21 +795,40 @@
flush = 0;
+ /*
+ * We need to hold the pagetable lock between the time
+ * we make the pagetable RO and when we actually pin
+ * it. If we don't, then other users may come in and
+ * attempt to update the pagetable by writing it,
+ * which will fail because the memory is RO but not
+ * pinned, so Xen won't do the trap'n'emulate.
+ *
+ * If we're using split pte locks, we can't hold the
+ * entire pagetable's worth of locks during the
+ * traverse, because we may wrap the preempt count (8
+ * bits). The solution is to mark RO and pin each PTE
+ * page while holding the lock. This means the number
+ * of locks we end up holding is never more than a
+ * batch size (~32 entries, at present).
+ *
+ * If we're not using split pte locks, we needn't pin
+ * the PTE pages independently, because we're
+ * protected by the overall pagetable lock.
+ */
ptl = NULL;
if (level == PT_PTE)
- ptl = lock_pte(page);
+ ptl = xen_pte_lock(page, mm);
MULTI_update_va_mapping(mcs.mc, (unsigned long)pt,
pfn_pte(pfn, PAGE_KERNEL_RO),
level == PT_PGD ? UVMF_TLB_FLUSH : 0);
- if (level == PT_PTE)
+ if (ptl) {
xen_do_pin(MMUEXT_PIN_L1_TABLE, pfn);
- if (ptl) {
/* Queue a deferred unlock for when this batch
is completed. */
- xen_mc_callback(do_unlock, ptl);
+ xen_mc_callback(xen_pte_unlock, ptl);
}
}
@@ -715,11 +838,11 @@
/* This is called just after a mm has been created, but it has not
been used yet. We need to make sure that its pagetable is all
read-only, and can be pinned. */
-void xen_pgd_pin(pgd_t *pgd)
+static void __xen_pgd_pin(struct mm_struct *mm, pgd_t *pgd)
{
xen_mc_batch();
- if (pgd_walk(pgd, pin_page, USER_LIMIT)) {
+ if (xen_pgd_walk(mm, xen_pin_page, USER_LIMIT)) {
/* re-enable interrupts for kmap_flush_unused */
xen_mc_issue(0);
kmap_flush_unused();
@@ -733,25 +856,35 @@
xen_do_pin(MMUEXT_PIN_L4_TABLE, PFN_DOWN(__pa(pgd)));
if (user_pgd) {
- pin_page(virt_to_page(user_pgd), PT_PGD);
+ xen_pin_page(mm, virt_to_page(user_pgd), PT_PGD);
xen_do_pin(MMUEXT_PIN_L4_TABLE, PFN_DOWN(__pa(user_pgd)));
}
}
#else /* CONFIG_X86_32 */
#ifdef CONFIG_X86_PAE
/* Need to make sure unshared kernel PMD is pinnable */
- pin_page(virt_to_page(pgd_page(pgd[pgd_index(TASK_SIZE)])), PT_PMD);
+ xen_pin_page(mm, virt_to_page(pgd_page(pgd[pgd_index(TASK_SIZE)])),
+ PT_PMD);
#endif
xen_do_pin(MMUEXT_PIN_L3_TABLE, PFN_DOWN(__pa(pgd)));
#endif /* CONFIG_X86_64 */
xen_mc_issue(0);
}
+static void xen_pgd_pin(struct mm_struct *mm)
+{
+ __xen_pgd_pin(mm, mm->pgd);
+}
+
/*
* On save, we need to pin all pagetables to make sure they get their
* mfns turned into pfns. Search the list for any unpinned pgds and pin
* them (unpinned pgds are not currently in use, probably because the
* process is under construction or destruction).
+ *
+ * Expected to be called in stop_machine() ("equivalent to taking
+ * every spinlock in the system"), so the locking doesn't really
+ * matter all that much.
*/
void xen_mm_pin_all(void)
{
@@ -762,7 +895,7 @@
list_for_each_entry(page, &pgd_list, lru) {
if (!PagePinned(page)) {
- xen_pgd_pin((pgd_t *)page_address(page));
+ __xen_pgd_pin(&init_mm, (pgd_t *)page_address(page));
SetPageSavePinned(page);
}
}
@@ -775,7 +908,8 @@
* that's before we have page structures to store the bits. So do all
* the book-keeping now.
*/
-static __init int mark_pinned(struct page *page, enum pt_level level)
+static __init int xen_mark_pinned(struct mm_struct *mm, struct page *page,
+ enum pt_level level)
{
SetPagePinned(page);
return 0;
@@ -783,10 +917,11 @@
void __init xen_mark_init_mm_pinned(void)
{
- pgd_walk(init_mm.pgd, mark_pinned, FIXADDR_TOP);
+ xen_pgd_walk(&init_mm, xen_mark_pinned, FIXADDR_TOP);
}
-static int unpin_page(struct page *page, enum pt_level level)
+static int xen_unpin_page(struct mm_struct *mm, struct page *page,
+ enum pt_level level)
{
unsigned pgfl = TestClearPagePinned(page);
@@ -796,10 +931,18 @@
spinlock_t *ptl = NULL;
struct multicall_space mcs;
+ /*
+ * Do the converse to pin_page. If we're using split
+ * pte locks, we must be holding the lock for while
+ * the pte page is unpinned but still RO to prevent
+ * concurrent updates from seeing it in this
+ * partially-pinned state.
+ */
if (level == PT_PTE) {
- ptl = lock_pte(page);
+ ptl = xen_pte_lock(page, mm);
- xen_do_pin(MMUEXT_UNPIN_TABLE, pfn);
+ if (ptl)
+ xen_do_pin(MMUEXT_UNPIN_TABLE, pfn);
}
mcs = __xen_mc_entry(0);
@@ -810,7 +953,7 @@
if (ptl) {
/* unlock when batch completed */
- xen_mc_callback(do_unlock, ptl);
+ xen_mc_callback(xen_pte_unlock, ptl);
}
}
@@ -818,7 +961,7 @@
}
/* Release a pagetables pages back as normal RW */
-static void xen_pgd_unpin(pgd_t *pgd)
+static void __xen_pgd_unpin(struct mm_struct *mm, pgd_t *pgd)
{
xen_mc_batch();
@@ -830,21 +973,27 @@
if (user_pgd) {
xen_do_pin(MMUEXT_UNPIN_TABLE, PFN_DOWN(__pa(user_pgd)));
- unpin_page(virt_to_page(user_pgd), PT_PGD);
+ xen_unpin_page(mm, virt_to_page(user_pgd), PT_PGD);
}
}
#endif
#ifdef CONFIG_X86_PAE
/* Need to make sure unshared kernel PMD is unpinned */
- pin_page(virt_to_page(pgd_page(pgd[pgd_index(TASK_SIZE)])), PT_PMD);
+ xen_unpin_page(mm, virt_to_page(pgd_page(pgd[pgd_index(TASK_SIZE)])),
+ PT_PMD);
#endif
- pgd_walk(pgd, unpin_page, USER_LIMIT);
+ xen_pgd_walk(mm, xen_unpin_page, USER_LIMIT);
xen_mc_issue(0);
}
+static void xen_pgd_unpin(struct mm_struct *mm)
+{
+ __xen_pgd_unpin(mm, mm->pgd);
+}
+
/*
* On resume, undo any pinning done at save, so that the rest of the
* kernel doesn't see any unexpected pinned pagetables.
@@ -859,7 +1008,7 @@
list_for_each_entry(page, &pgd_list, lru) {
if (PageSavePinned(page)) {
BUG_ON(!PagePinned(page));
- xen_pgd_unpin((pgd_t *)page_address(page));
+ __xen_pgd_unpin(&init_mm, (pgd_t *)page_address(page));
ClearPageSavePinned(page);
}
}
@@ -870,14 +1019,14 @@
void xen_activate_mm(struct mm_struct *prev, struct mm_struct *next)
{
spin_lock(&next->page_table_lock);
- xen_pgd_pin(next->pgd);
+ xen_pgd_pin(next);
spin_unlock(&next->page_table_lock);
}
void xen_dup_mmap(struct mm_struct *oldmm, struct mm_struct *mm)
{
spin_lock(&mm->page_table_lock);
- xen_pgd_pin(mm->pgd);
+ xen_pgd_pin(mm);
spin_unlock(&mm->page_table_lock);
}
@@ -907,7 +1056,7 @@
}
}
-static void drop_mm_ref(struct mm_struct *mm)
+static void xen_drop_mm_ref(struct mm_struct *mm)
{
cpumask_t mask;
unsigned cpu;
@@ -937,7 +1086,7 @@
smp_call_function_mask(mask, drop_other_mm_ref, mm, 1);
}
#else
-static void drop_mm_ref(struct mm_struct *mm)
+static void xen_drop_mm_ref(struct mm_struct *mm)
{
if (current->active_mm == mm)
load_cr3(swapper_pg_dir);
@@ -961,14 +1110,77 @@
void xen_exit_mmap(struct mm_struct *mm)
{
get_cpu(); /* make sure we don't move around */
- drop_mm_ref(mm);
+ xen_drop_mm_ref(mm);
put_cpu();
spin_lock(&mm->page_table_lock);
/* pgd may not be pinned in the error exit path of execve */
- if (page_pinned(mm->pgd))
- xen_pgd_unpin(mm->pgd);
+ if (xen_page_pinned(mm->pgd))
+ xen_pgd_unpin(mm);
spin_unlock(&mm->page_table_lock);
}
+
+#ifdef CONFIG_XEN_DEBUG_FS
+
+static struct dentry *d_mmu_debug;
+
+static int __init xen_mmu_debugfs(void)
+{
+ struct dentry *d_xen = xen_init_debugfs();
+
+ if (d_xen == NULL)
+ return -ENOMEM;
+
+ d_mmu_debug = debugfs_create_dir("mmu", d_xen);
+
+ debugfs_create_u8("zero_stats", 0644, d_mmu_debug, &zero_stats);
+
+ debugfs_create_u32("pgd_update", 0444, d_mmu_debug, &mmu_stats.pgd_update);
+ debugfs_create_u32("pgd_update_pinned", 0444, d_mmu_debug,
+ &mmu_stats.pgd_update_pinned);
+ debugfs_create_u32("pgd_update_batched", 0444, d_mmu_debug,
+ &mmu_stats.pgd_update_pinned);
+
+ debugfs_create_u32("pud_update", 0444, d_mmu_debug, &mmu_stats.pud_update);
+ debugfs_create_u32("pud_update_pinned", 0444, d_mmu_debug,
+ &mmu_stats.pud_update_pinned);
+ debugfs_create_u32("pud_update_batched", 0444, d_mmu_debug,
+ &mmu_stats.pud_update_pinned);
+
+ debugfs_create_u32("pmd_update", 0444, d_mmu_debug, &mmu_stats.pmd_update);
+ debugfs_create_u32("pmd_update_pinned", 0444, d_mmu_debug,
+ &mmu_stats.pmd_update_pinned);
+ debugfs_create_u32("pmd_update_batched", 0444, d_mmu_debug,
+ &mmu_stats.pmd_update_pinned);
+
+ debugfs_create_u32("pte_update", 0444, d_mmu_debug, &mmu_stats.pte_update);
+// debugfs_create_u32("pte_update_pinned", 0444, d_mmu_debug,
+// &mmu_stats.pte_update_pinned);
+ debugfs_create_u32("pte_update_batched", 0444, d_mmu_debug,
+ &mmu_stats.pte_update_pinned);
+
+ debugfs_create_u32("mmu_update", 0444, d_mmu_debug, &mmu_stats.mmu_update);
+ debugfs_create_u32("mmu_update_extended", 0444, d_mmu_debug,
+ &mmu_stats.mmu_update_extended);
+ xen_debugfs_create_u32_array("mmu_update_histo", 0444, d_mmu_debug,
+ mmu_stats.mmu_update_histo, 20);
+
+ debugfs_create_u32("set_pte_at", 0444, d_mmu_debug, &mmu_stats.set_pte_at);
+ debugfs_create_u32("set_pte_at_batched", 0444, d_mmu_debug,
+ &mmu_stats.set_pte_at_batched);
+ debugfs_create_u32("set_pte_at_current", 0444, d_mmu_debug,
+ &mmu_stats.set_pte_at_current);
+ debugfs_create_u32("set_pte_at_kernel", 0444, d_mmu_debug,
+ &mmu_stats.set_pte_at_kernel);
+
+ debugfs_create_u32("prot_commit", 0444, d_mmu_debug, &mmu_stats.prot_commit);
+ debugfs_create_u32("prot_commit_batched", 0444, d_mmu_debug,
+ &mmu_stats.prot_commit_batched);
+
+ return 0;
+}
+fs_initcall(xen_mmu_debugfs);
+
+#endif /* CONFIG_XEN_DEBUG_FS */
diff --git a/arch/x86/xen/mmu.h b/arch/x86/xen/mmu.h
index 0f59bd0..98d7165 100644
--- a/arch/x86/xen/mmu.h
+++ b/arch/x86/xen/mmu.h
@@ -18,9 +18,6 @@
void xen_dup_mmap(struct mm_struct *oldmm, struct mm_struct *mm);
void xen_exit_mmap(struct mm_struct *mm);
-void xen_pgd_pin(pgd_t *pgd);
-//void xen_pgd_unpin(pgd_t *pgd);
-
pteval_t xen_pte_val(pte_t);
pmdval_t xen_pmd_val(pmd_t);
pgdval_t xen_pgd_val(pgd_t);
diff --git a/arch/x86/xen/multicalls.c b/arch/x86/xen/multicalls.c
index 9efd1c6..8ea8a0d 100644
--- a/arch/x86/xen/multicalls.c
+++ b/arch/x86/xen/multicalls.c
@@ -21,16 +21,20 @@
*/
#include <linux/percpu.h>
#include <linux/hardirq.h>
+#include <linux/debugfs.h>
#include <asm/xen/hypercall.h>
#include "multicalls.h"
+#include "debugfs.h"
+
+#define MC_BATCH 32
#define MC_DEBUG 1
-#define MC_BATCH 32
#define MC_ARGS (MC_BATCH * 16)
+
struct mc_buffer {
struct multicall_entry entries[MC_BATCH];
#if MC_DEBUG
@@ -47,6 +51,76 @@
static DEFINE_PER_CPU(struct mc_buffer, mc_buffer);
DEFINE_PER_CPU(unsigned long, xen_mc_irq_flags);
+/* flush reasons 0- slots, 1- args, 2- callbacks */
+enum flush_reasons
+{
+ FL_SLOTS,
+ FL_ARGS,
+ FL_CALLBACKS,
+
+ FL_N_REASONS
+};
+
+#ifdef CONFIG_XEN_DEBUG_FS
+#define NHYPERCALLS 40 /* not really */
+
+static struct {
+ unsigned histo[MC_BATCH+1];
+
+ unsigned issued;
+ unsigned arg_total;
+ unsigned hypercalls;
+ unsigned histo_hypercalls[NHYPERCALLS];
+
+ unsigned flush[FL_N_REASONS];
+} mc_stats;
+
+static u8 zero_stats;
+
+static inline void check_zero(void)
+{
+ if (unlikely(zero_stats)) {
+ memset(&mc_stats, 0, sizeof(mc_stats));
+ zero_stats = 0;
+ }
+}
+
+static void mc_add_stats(const struct mc_buffer *mc)
+{
+ int i;
+
+ check_zero();
+
+ mc_stats.issued++;
+ mc_stats.hypercalls += mc->mcidx;
+ mc_stats.arg_total += mc->argidx;
+
+ mc_stats.histo[mc->mcidx]++;
+ for(i = 0; i < mc->mcidx; i++) {
+ unsigned op = mc->entries[i].op;
+ if (op < NHYPERCALLS)
+ mc_stats.histo_hypercalls[op]++;
+ }
+}
+
+static void mc_stats_flush(enum flush_reasons idx)
+{
+ check_zero();
+
+ mc_stats.flush[idx]++;
+}
+
+#else /* !CONFIG_XEN_DEBUG_FS */
+
+static inline void mc_add_stats(const struct mc_buffer *mc)
+{
+}
+
+static inline void mc_stats_flush(enum flush_reasons idx)
+{
+}
+#endif /* CONFIG_XEN_DEBUG_FS */
+
void xen_mc_flush(void)
{
struct mc_buffer *b = &__get_cpu_var(mc_buffer);
@@ -60,6 +134,8 @@
something in the middle */
local_irq_save(flags);
+ mc_add_stats(b);
+
if (b->mcidx) {
#if MC_DEBUG
memcpy(b->debug, b->entries,
@@ -115,6 +191,7 @@
if (b->mcidx == MC_BATCH ||
(argidx + args) > MC_ARGS) {
+ mc_stats_flush(b->mcidx == MC_BATCH ? FL_SLOTS : FL_ARGS);
xen_mc_flush();
argidx = roundup(b->argidx, sizeof(u64));
}
@@ -158,10 +235,44 @@
struct mc_buffer *b = &__get_cpu_var(mc_buffer);
struct callback *cb;
- if (b->cbidx == MC_BATCH)
+ if (b->cbidx == MC_BATCH) {
+ mc_stats_flush(FL_CALLBACKS);
xen_mc_flush();
+ }
cb = &b->callbacks[b->cbidx++];
cb->fn = fn;
cb->data = data;
}
+
+#ifdef CONFIG_XEN_DEBUG_FS
+
+static struct dentry *d_mc_debug;
+
+static int __init xen_mc_debugfs(void)
+{
+ struct dentry *d_xen = xen_init_debugfs();
+
+ if (d_xen == NULL)
+ return -ENOMEM;
+
+ d_mc_debug = debugfs_create_dir("multicalls", d_xen);
+
+ debugfs_create_u8("zero_stats", 0644, d_mc_debug, &zero_stats);
+
+ debugfs_create_u32("batches", 0444, d_mc_debug, &mc_stats.issued);
+ debugfs_create_u32("hypercalls", 0444, d_mc_debug, &mc_stats.hypercalls);
+ debugfs_create_u32("arg_total", 0444, d_mc_debug, &mc_stats.arg_total);
+
+ xen_debugfs_create_u32_array("batch_histo", 0444, d_mc_debug,
+ mc_stats.histo, MC_BATCH);
+ xen_debugfs_create_u32_array("hypercall_histo", 0444, d_mc_debug,
+ mc_stats.histo_hypercalls, NHYPERCALLS);
+ xen_debugfs_create_u32_array("flush_reasons", 0444, d_mc_debug,
+ mc_stats.flush, FL_N_REASONS);
+
+ return 0;
+}
+fs_initcall(xen_mc_debugfs);
+
+#endif /* CONFIG_XEN_DEBUG_FS */
diff --git a/arch/x86/xen/smp.c b/arch/x86/xen/smp.c
index d8faf79..d77da61 100644
--- a/arch/x86/xen/smp.c
+++ b/arch/x86/xen/smp.c
@@ -11,11 +11,8 @@
* useful topology information for the kernel to make use of. As a
* result, all CPUs are treated as if they're single-core and
* single-threaded.
- *
- * This does not handle HOTPLUG_CPU yet.
*/
#include <linux/sched.h>
-#include <linux/kernel_stat.h>
#include <linux/err.h>
#include <linux/smp.h>
@@ -36,8 +33,6 @@
#include "xen-ops.h"
#include "mmu.h"
-static void __cpuinit xen_init_lock_cpu(int cpu);
-
cpumask_t xen_cpu_initialized_map;
static DEFINE_PER_CPU(int, resched_irq);
@@ -64,11 +59,12 @@
return IRQ_HANDLED;
}
-static __cpuinit void cpu_bringup_and_idle(void)
+static __cpuinit void cpu_bringup(void)
{
int cpu = smp_processor_id();
cpu_init();
+ touch_softlockup_watchdog();
preempt_disable();
xen_enable_sysenter();
@@ -89,6 +85,11 @@
local_irq_enable();
wmb(); /* make sure everything is out */
+}
+
+static __cpuinit void cpu_bringup_and_idle(void)
+{
+ cpu_bringup();
cpu_idle();
}
@@ -212,8 +213,6 @@
cpu_set(cpu, cpu_present_map);
}
-
- //init_xenbus_allowed_cpumask();
}
static __cpuinit int
@@ -281,12 +280,6 @@
struct task_struct *idle = idle_task(cpu);
int rc;
-#if 0
- rc = cpu_up_check(cpu);
- if (rc)
- return rc;
-#endif
-
#ifdef CONFIG_X86_64
/* Allocate node local memory for AP pdas */
WARN_ON(cpu == 0);
@@ -339,6 +332,60 @@
{
}
+#ifdef CONFIG_HOTPLUG_CPU
+static int xen_cpu_disable(void)
+{
+ unsigned int cpu = smp_processor_id();
+ if (cpu == 0)
+ return -EBUSY;
+
+ cpu_disable_common();
+
+ load_cr3(swapper_pg_dir);
+ return 0;
+}
+
+static void xen_cpu_die(unsigned int cpu)
+{
+ while (HYPERVISOR_vcpu_op(VCPUOP_is_up, cpu, NULL)) {
+ current->state = TASK_UNINTERRUPTIBLE;
+ schedule_timeout(HZ/10);
+ }
+ unbind_from_irqhandler(per_cpu(resched_irq, cpu), NULL);
+ unbind_from_irqhandler(per_cpu(callfunc_irq, cpu), NULL);
+ unbind_from_irqhandler(per_cpu(debug_irq, cpu), NULL);
+ unbind_from_irqhandler(per_cpu(callfuncsingle_irq, cpu), NULL);
+ xen_uninit_lock_cpu(cpu);
+ xen_teardown_timer(cpu);
+
+ if (num_online_cpus() == 1)
+ alternatives_smp_switch(0);
+}
+
+static void xen_play_dead(void)
+{
+ play_dead_common();
+ HYPERVISOR_vcpu_op(VCPUOP_down, smp_processor_id(), NULL);
+ cpu_bringup();
+}
+
+#else /* !CONFIG_HOTPLUG_CPU */
+static int xen_cpu_disable(void)
+{
+ return -ENOSYS;
+}
+
+static void xen_cpu_die(unsigned int cpu)
+{
+ BUG();
+}
+
+static void xen_play_dead(void)
+{
+ BUG();
+}
+
+#endif
static void stop_self(void *v)
{
int cpu = smp_processor_id();
@@ -419,176 +466,16 @@
return IRQ_HANDLED;
}
-struct xen_spinlock {
- unsigned char lock; /* 0 -> free; 1 -> locked */
- unsigned short spinners; /* count of waiting cpus */
-};
-
-static int xen_spin_is_locked(struct raw_spinlock *lock)
-{
- struct xen_spinlock *xl = (struct xen_spinlock *)lock;
-
- return xl->lock != 0;
-}
-
-static int xen_spin_is_contended(struct raw_spinlock *lock)
-{
- struct xen_spinlock *xl = (struct xen_spinlock *)lock;
-
- /* Not strictly true; this is only the count of contended
- lock-takers entering the slow path. */
- return xl->spinners != 0;
-}
-
-static int xen_spin_trylock(struct raw_spinlock *lock)
-{
- struct xen_spinlock *xl = (struct xen_spinlock *)lock;
- u8 old = 1;
-
- asm("xchgb %b0,%1"
- : "+q" (old), "+m" (xl->lock) : : "memory");
-
- return old == 0;
-}
-
-static DEFINE_PER_CPU(int, lock_kicker_irq) = -1;
-static DEFINE_PER_CPU(struct xen_spinlock *, lock_spinners);
-
-static inline void spinning_lock(struct xen_spinlock *xl)
-{
- __get_cpu_var(lock_spinners) = xl;
- wmb(); /* set lock of interest before count */
- asm(LOCK_PREFIX " incw %0"
- : "+m" (xl->spinners) : : "memory");
-}
-
-static inline void unspinning_lock(struct xen_spinlock *xl)
-{
- asm(LOCK_PREFIX " decw %0"
- : "+m" (xl->spinners) : : "memory");
- wmb(); /* decrement count before clearing lock */
- __get_cpu_var(lock_spinners) = NULL;
-}
-
-static noinline int xen_spin_lock_slow(struct raw_spinlock *lock)
-{
- struct xen_spinlock *xl = (struct xen_spinlock *)lock;
- int irq = __get_cpu_var(lock_kicker_irq);
- int ret;
-
- /* If kicker interrupts not initialized yet, just spin */
- if (irq == -1)
- return 0;
-
- /* announce we're spinning */
- spinning_lock(xl);
-
- /* clear pending */
- xen_clear_irq_pending(irq);
-
- /* check again make sure it didn't become free while
- we weren't looking */
- ret = xen_spin_trylock(lock);
- if (ret)
- goto out;
-
- /* block until irq becomes pending */
- xen_poll_irq(irq);
- kstat_this_cpu.irqs[irq]++;
-
-out:
- unspinning_lock(xl);
- return ret;
-}
-
-static void xen_spin_lock(struct raw_spinlock *lock)
-{
- struct xen_spinlock *xl = (struct xen_spinlock *)lock;
- int timeout;
- u8 oldval;
-
- do {
- timeout = 1 << 10;
-
- asm("1: xchgb %1,%0\n"
- " testb %1,%1\n"
- " jz 3f\n"
- "2: rep;nop\n"
- " cmpb $0,%0\n"
- " je 1b\n"
- " dec %2\n"
- " jnz 2b\n"
- "3:\n"
- : "+m" (xl->lock), "=q" (oldval), "+r" (timeout)
- : "1" (1)
- : "memory");
-
- } while (unlikely(oldval != 0 && !xen_spin_lock_slow(lock)));
-}
-
-static noinline void xen_spin_unlock_slow(struct xen_spinlock *xl)
-{
- int cpu;
-
- for_each_online_cpu(cpu) {
- /* XXX should mix up next cpu selection */
- if (per_cpu(lock_spinners, cpu) == xl) {
- xen_send_IPI_one(cpu, XEN_SPIN_UNLOCK_VECTOR);
- break;
- }
- }
-}
-
-static void xen_spin_unlock(struct raw_spinlock *lock)
-{
- struct xen_spinlock *xl = (struct xen_spinlock *)lock;
-
- smp_wmb(); /* make sure no writes get moved after unlock */
- xl->lock = 0; /* release lock */
-
- /* make sure unlock happens before kick */
- barrier();
-
- if (unlikely(xl->spinners))
- xen_spin_unlock_slow(xl);
-}
-
-static __cpuinit void xen_init_lock_cpu(int cpu)
-{
- int irq;
- const char *name;
-
- name = kasprintf(GFP_KERNEL, "spinlock%d", cpu);
- irq = bind_ipi_to_irqhandler(XEN_SPIN_UNLOCK_VECTOR,
- cpu,
- xen_reschedule_interrupt,
- IRQF_DISABLED|IRQF_PERCPU|IRQF_NOBALANCING,
- name,
- NULL);
-
- if (irq >= 0) {
- disable_irq(irq); /* make sure it's never delivered */
- per_cpu(lock_kicker_irq, cpu) = irq;
- }
-
- printk("cpu %d spinlock event irq %d\n", cpu, irq);
-}
-
-static void __init xen_init_spinlocks(void)
-{
- pv_lock_ops.spin_is_locked = xen_spin_is_locked;
- pv_lock_ops.spin_is_contended = xen_spin_is_contended;
- pv_lock_ops.spin_lock = xen_spin_lock;
- pv_lock_ops.spin_trylock = xen_spin_trylock;
- pv_lock_ops.spin_unlock = xen_spin_unlock;
-}
-
static const struct smp_ops xen_smp_ops __initdata = {
.smp_prepare_boot_cpu = xen_smp_prepare_boot_cpu,
.smp_prepare_cpus = xen_smp_prepare_cpus,
- .cpu_up = xen_cpu_up,
.smp_cpus_done = xen_smp_cpus_done,
+ .cpu_up = xen_cpu_up,
+ .cpu_die = xen_cpu_die,
+ .cpu_disable = xen_cpu_disable,
+ .play_dead = xen_play_dead,
+
.smp_send_stop = xen_smp_send_stop,
.smp_send_reschedule = xen_smp_send_reschedule,
diff --git a/arch/x86/xen/spinlock.c b/arch/x86/xen/spinlock.c
new file mode 100644
index 0000000..dd71e3a
--- /dev/null
+++ b/arch/x86/xen/spinlock.c
@@ -0,0 +1,428 @@
+/*
+ * Split spinlock implementation out into its own file, so it can be
+ * compiled in a FTRACE-compatible way.
+ */
+#include <linux/kernel_stat.h>
+#include <linux/spinlock.h>
+#include <linux/debugfs.h>
+#include <linux/log2.h>
+
+#include <asm/paravirt.h>
+
+#include <xen/interface/xen.h>
+#include <xen/events.h>
+
+#include "xen-ops.h"
+#include "debugfs.h"
+
+#ifdef CONFIG_XEN_DEBUG_FS
+static struct xen_spinlock_stats
+{
+ u64 taken;
+ u32 taken_slow;
+ u32 taken_slow_nested;
+ u32 taken_slow_pickup;
+ u32 taken_slow_spurious;
+ u32 taken_slow_irqenable;
+
+ u64 released;
+ u32 released_slow;
+ u32 released_slow_kicked;
+
+#define HISTO_BUCKETS 30
+ u32 histo_spin_total[HISTO_BUCKETS+1];
+ u32 histo_spin_spinning[HISTO_BUCKETS+1];
+ u32 histo_spin_blocked[HISTO_BUCKETS+1];
+
+ u64 time_total;
+ u64 time_spinning;
+ u64 time_blocked;
+} spinlock_stats;
+
+static u8 zero_stats;
+
+static unsigned lock_timeout = 1 << 10;
+#define TIMEOUT lock_timeout
+
+static inline void check_zero(void)
+{
+ if (unlikely(zero_stats)) {
+ memset(&spinlock_stats, 0, sizeof(spinlock_stats));
+ zero_stats = 0;
+ }
+}
+
+#define ADD_STATS(elem, val) \
+ do { check_zero(); spinlock_stats.elem += (val); } while(0)
+
+static inline u64 spin_time_start(void)
+{
+ return xen_clocksource_read();
+}
+
+static void __spin_time_accum(u64 delta, u32 *array)
+{
+ unsigned index = ilog2(delta);
+
+ check_zero();
+
+ if (index < HISTO_BUCKETS)
+ array[index]++;
+ else
+ array[HISTO_BUCKETS]++;
+}
+
+static inline void spin_time_accum_spinning(u64 start)
+{
+ u32 delta = xen_clocksource_read() - start;
+
+ __spin_time_accum(delta, spinlock_stats.histo_spin_spinning);
+ spinlock_stats.time_spinning += delta;
+}
+
+static inline void spin_time_accum_total(u64 start)
+{
+ u32 delta = xen_clocksource_read() - start;
+
+ __spin_time_accum(delta, spinlock_stats.histo_spin_total);
+ spinlock_stats.time_total += delta;
+}
+
+static inline void spin_time_accum_blocked(u64 start)
+{
+ u32 delta = xen_clocksource_read() - start;
+
+ __spin_time_accum(delta, spinlock_stats.histo_spin_blocked);
+ spinlock_stats.time_blocked += delta;
+}
+#else /* !CONFIG_XEN_DEBUG_FS */
+#define TIMEOUT (1 << 10)
+#define ADD_STATS(elem, val) do { (void)(val); } while(0)
+
+static inline u64 spin_time_start(void)
+{
+ return 0;
+}
+
+static inline void spin_time_accum_total(u64 start)
+{
+}
+static inline void spin_time_accum_spinning(u64 start)
+{
+}
+static inline void spin_time_accum_blocked(u64 start)
+{
+}
+#endif /* CONFIG_XEN_DEBUG_FS */
+
+struct xen_spinlock {
+ unsigned char lock; /* 0 -> free; 1 -> locked */
+ unsigned short spinners; /* count of waiting cpus */
+};
+
+static int xen_spin_is_locked(struct raw_spinlock *lock)
+{
+ struct xen_spinlock *xl = (struct xen_spinlock *)lock;
+
+ return xl->lock != 0;
+}
+
+static int xen_spin_is_contended(struct raw_spinlock *lock)
+{
+ struct xen_spinlock *xl = (struct xen_spinlock *)lock;
+
+ /* Not strictly true; this is only the count of contended
+ lock-takers entering the slow path. */
+ return xl->spinners != 0;
+}
+
+static int xen_spin_trylock(struct raw_spinlock *lock)
+{
+ struct xen_spinlock *xl = (struct xen_spinlock *)lock;
+ u8 old = 1;
+
+ asm("xchgb %b0,%1"
+ : "+q" (old), "+m" (xl->lock) : : "memory");
+
+ return old == 0;
+}
+
+static DEFINE_PER_CPU(int, lock_kicker_irq) = -1;
+static DEFINE_PER_CPU(struct xen_spinlock *, lock_spinners);
+
+/*
+ * Mark a cpu as interested in a lock. Returns the CPU's previous
+ * lock of interest, in case we got preempted by an interrupt.
+ */
+static inline struct xen_spinlock *spinning_lock(struct xen_spinlock *xl)
+{
+ struct xen_spinlock *prev;
+
+ prev = __get_cpu_var(lock_spinners);
+ __get_cpu_var(lock_spinners) = xl;
+
+ wmb(); /* set lock of interest before count */
+
+ asm(LOCK_PREFIX " incw %0"
+ : "+m" (xl->spinners) : : "memory");
+
+ return prev;
+}
+
+/*
+ * Mark a cpu as no longer interested in a lock. Restores previous
+ * lock of interest (NULL for none).
+ */
+static inline void unspinning_lock(struct xen_spinlock *xl, struct xen_spinlock *prev)
+{
+ asm(LOCK_PREFIX " decw %0"
+ : "+m" (xl->spinners) : : "memory");
+ wmb(); /* decrement count before restoring lock */
+ __get_cpu_var(lock_spinners) = prev;
+}
+
+static noinline int xen_spin_lock_slow(struct raw_spinlock *lock, bool irq_enable)
+{
+ struct xen_spinlock *xl = (struct xen_spinlock *)lock;
+ struct xen_spinlock *prev;
+ int irq = __get_cpu_var(lock_kicker_irq);
+ int ret;
+ unsigned long flags;
+ u64 start;
+
+ /* If kicker interrupts not initialized yet, just spin */
+ if (irq == -1)
+ return 0;
+
+ start = spin_time_start();
+
+ /* announce we're spinning */
+ prev = spinning_lock(xl);
+
+ flags = __raw_local_save_flags();
+ if (irq_enable) {
+ ADD_STATS(taken_slow_irqenable, 1);
+ raw_local_irq_enable();
+ }
+
+ ADD_STATS(taken_slow, 1);
+ ADD_STATS(taken_slow_nested, prev != NULL);
+
+ do {
+ /* clear pending */
+ xen_clear_irq_pending(irq);
+
+ /* check again make sure it didn't become free while
+ we weren't looking */
+ ret = xen_spin_trylock(lock);
+ if (ret) {
+ ADD_STATS(taken_slow_pickup, 1);
+
+ /*
+ * If we interrupted another spinlock while it
+ * was blocking, make sure it doesn't block
+ * without rechecking the lock.
+ */
+ if (prev != NULL)
+ xen_set_irq_pending(irq);
+ goto out;
+ }
+
+ /*
+ * Block until irq becomes pending. If we're
+ * interrupted at this point (after the trylock but
+ * before entering the block), then the nested lock
+ * handler guarantees that the irq will be left
+ * pending if there's any chance the lock became free;
+ * xen_poll_irq() returns immediately if the irq is
+ * pending.
+ */
+ xen_poll_irq(irq);
+ ADD_STATS(taken_slow_spurious, !xen_test_irq_pending(irq));
+ } while (!xen_test_irq_pending(irq)); /* check for spurious wakeups */
+
+ kstat_this_cpu.irqs[irq]++;
+
+out:
+ raw_local_irq_restore(flags);
+ unspinning_lock(xl, prev);
+ spin_time_accum_blocked(start);
+
+ return ret;
+}
+
+static inline void __xen_spin_lock(struct raw_spinlock *lock, bool irq_enable)
+{
+ struct xen_spinlock *xl = (struct xen_spinlock *)lock;
+ unsigned timeout;
+ u8 oldval;
+ u64 start_spin;
+
+ ADD_STATS(taken, 1);
+
+ start_spin = spin_time_start();
+
+ do {
+ u64 start_spin_fast = spin_time_start();
+
+ timeout = TIMEOUT;
+
+ asm("1: xchgb %1,%0\n"
+ " testb %1,%1\n"
+ " jz 3f\n"
+ "2: rep;nop\n"
+ " cmpb $0,%0\n"
+ " je 1b\n"
+ " dec %2\n"
+ " jnz 2b\n"
+ "3:\n"
+ : "+m" (xl->lock), "=q" (oldval), "+r" (timeout)
+ : "1" (1)
+ : "memory");
+
+ spin_time_accum_spinning(start_spin_fast);
+
+ } while (unlikely(oldval != 0 &&
+ (TIMEOUT == ~0 || !xen_spin_lock_slow(lock, irq_enable))));
+
+ spin_time_accum_total(start_spin);
+}
+
+static void xen_spin_lock(struct raw_spinlock *lock)
+{
+ __xen_spin_lock(lock, false);
+}
+
+static void xen_spin_lock_flags(struct raw_spinlock *lock, unsigned long flags)
+{
+ __xen_spin_lock(lock, !raw_irqs_disabled_flags(flags));
+}
+
+static noinline void xen_spin_unlock_slow(struct xen_spinlock *xl)
+{
+ int cpu;
+
+ ADD_STATS(released_slow, 1);
+
+ for_each_online_cpu(cpu) {
+ /* XXX should mix up next cpu selection */
+ if (per_cpu(lock_spinners, cpu) == xl) {
+ ADD_STATS(released_slow_kicked, 1);
+ xen_send_IPI_one(cpu, XEN_SPIN_UNLOCK_VECTOR);
+ break;
+ }
+ }
+}
+
+static void xen_spin_unlock(struct raw_spinlock *lock)
+{
+ struct xen_spinlock *xl = (struct xen_spinlock *)lock;
+
+ ADD_STATS(released, 1);
+
+ smp_wmb(); /* make sure no writes get moved after unlock */
+ xl->lock = 0; /* release lock */
+
+ /* make sure unlock happens before kick */
+ barrier();
+
+ if (unlikely(xl->spinners))
+ xen_spin_unlock_slow(xl);
+}
+
+static irqreturn_t dummy_handler(int irq, void *dev_id)
+{
+ BUG();
+ return IRQ_HANDLED;
+}
+
+void __cpuinit xen_init_lock_cpu(int cpu)
+{
+ int irq;
+ const char *name;
+
+ name = kasprintf(GFP_KERNEL, "spinlock%d", cpu);
+ irq = bind_ipi_to_irqhandler(XEN_SPIN_UNLOCK_VECTOR,
+ cpu,
+ dummy_handler,
+ IRQF_DISABLED|IRQF_PERCPU|IRQF_NOBALANCING,
+ name,
+ NULL);
+
+ if (irq >= 0) {
+ disable_irq(irq); /* make sure it's never delivered */
+ per_cpu(lock_kicker_irq, cpu) = irq;
+ }
+
+ printk("cpu %d spinlock event irq %d\n", cpu, irq);
+}
+
+void xen_uninit_lock_cpu(int cpu)
+{
+ unbind_from_irqhandler(per_cpu(lock_kicker_irq, cpu), NULL);
+}
+
+void __init xen_init_spinlocks(void)
+{
+ pv_lock_ops.spin_is_locked = xen_spin_is_locked;
+ pv_lock_ops.spin_is_contended = xen_spin_is_contended;
+ pv_lock_ops.spin_lock = xen_spin_lock;
+ pv_lock_ops.spin_lock_flags = xen_spin_lock_flags;
+ pv_lock_ops.spin_trylock = xen_spin_trylock;
+ pv_lock_ops.spin_unlock = xen_spin_unlock;
+}
+
+#ifdef CONFIG_XEN_DEBUG_FS
+
+static struct dentry *d_spin_debug;
+
+static int __init xen_spinlock_debugfs(void)
+{
+ struct dentry *d_xen = xen_init_debugfs();
+
+ if (d_xen == NULL)
+ return -ENOMEM;
+
+ d_spin_debug = debugfs_create_dir("spinlocks", d_xen);
+
+ debugfs_create_u8("zero_stats", 0644, d_spin_debug, &zero_stats);
+
+ debugfs_create_u32("timeout", 0644, d_spin_debug, &lock_timeout);
+
+ debugfs_create_u64("taken", 0444, d_spin_debug, &spinlock_stats.taken);
+ debugfs_create_u32("taken_slow", 0444, d_spin_debug,
+ &spinlock_stats.taken_slow);
+ debugfs_create_u32("taken_slow_nested", 0444, d_spin_debug,
+ &spinlock_stats.taken_slow_nested);
+ debugfs_create_u32("taken_slow_pickup", 0444, d_spin_debug,
+ &spinlock_stats.taken_slow_pickup);
+ debugfs_create_u32("taken_slow_spurious", 0444, d_spin_debug,
+ &spinlock_stats.taken_slow_spurious);
+ debugfs_create_u32("taken_slow_irqenable", 0444, d_spin_debug,
+ &spinlock_stats.taken_slow_irqenable);
+
+ debugfs_create_u64("released", 0444, d_spin_debug, &spinlock_stats.released);
+ debugfs_create_u32("released_slow", 0444, d_spin_debug,
+ &spinlock_stats.released_slow);
+ debugfs_create_u32("released_slow_kicked", 0444, d_spin_debug,
+ &spinlock_stats.released_slow_kicked);
+
+ debugfs_create_u64("time_spinning", 0444, d_spin_debug,
+ &spinlock_stats.time_spinning);
+ debugfs_create_u64("time_blocked", 0444, d_spin_debug,
+ &spinlock_stats.time_blocked);
+ debugfs_create_u64("time_total", 0444, d_spin_debug,
+ &spinlock_stats.time_total);
+
+ xen_debugfs_create_u32_array("histo_total", 0444, d_spin_debug,
+ spinlock_stats.histo_spin_total, HISTO_BUCKETS + 1);
+ xen_debugfs_create_u32_array("histo_spinning", 0444, d_spin_debug,
+ spinlock_stats.histo_spin_spinning, HISTO_BUCKETS + 1);
+ xen_debugfs_create_u32_array("histo_blocked", 0444, d_spin_debug,
+ spinlock_stats.histo_spin_blocked, HISTO_BUCKETS + 1);
+
+ return 0;
+}
+fs_initcall(xen_spinlock_debugfs);
+
+#endif /* CONFIG_XEN_DEBUG_FS */
diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c
index 685b774..004ba86 100644
--- a/arch/x86/xen/time.c
+++ b/arch/x86/xen/time.c
@@ -30,8 +30,6 @@
#define TIMER_SLOP 100000
#define NS_PER_TICK (1000000000LL / HZ)
-static cycle_t xen_clocksource_read(void);
-
/* runstate info updated by Xen */
static DEFINE_PER_CPU(struct vcpu_runstate_info, runstate);
@@ -213,7 +211,7 @@
return xen_khz;
}
-static cycle_t xen_clocksource_read(void)
+cycle_t xen_clocksource_read(void)
{
struct pvclock_vcpu_time_info *src;
cycle_t ret;
@@ -452,6 +450,14 @@
setup_runstate_info(cpu);
}
+void xen_teardown_timer(int cpu)
+{
+ struct clock_event_device *evt;
+ BUG_ON(cpu == 0);
+ evt = &per_cpu(xen_clock_events, cpu);
+ unbind_from_irqhandler(evt->irq, NULL);
+}
+
void xen_setup_cpu_clockevents(void)
{
BUG_ON(preemptible());
diff --git a/arch/x86/xen/xen-asm_32.S b/arch/x86/xen/xen-asm_32.S
index 2497a30..42786f5 100644
--- a/arch/x86/xen/xen-asm_32.S
+++ b/arch/x86/xen/xen-asm_32.S
@@ -298,7 +298,7 @@
push %eax
push %ecx
push %edx
- call force_evtchn_callback
+ call xen_force_evtchn_callback
pop %edx
pop %ecx
pop %eax
diff --git a/arch/x86/xen/xen-asm_64.S b/arch/x86/xen/xen-asm_64.S
index 7f58304..05794c5 100644
--- a/arch/x86/xen/xen-asm_64.S
+++ b/arch/x86/xen/xen-asm_64.S
@@ -26,8 +26,15 @@
/* Pseudo-flag used for virtual NMI, which we don't implement yet */
#define XEN_EFLAGS_NMI 0x80000000
-#if 0
-#include <asm/percpu.h>
+#if 1
+/*
+ x86-64 does not yet support direct access to percpu variables
+ via a segment override, so we just need to make sure this code
+ never gets used
+ */
+#define BUG ud2a
+#define PER_CPU_VAR(var, off) 0xdeadbeef
+#endif
/*
Enable events. This clears the event mask and tests the pending
@@ -35,6 +42,8 @@
events, then enter the hypervisor to get them handled.
*/
ENTRY(xen_irq_enable_direct)
+ BUG
+
/* Unmask events */
movb $0, PER_CPU_VAR(xen_vcpu_info, XEN_vcpu_info_mask)
@@ -58,6 +67,8 @@
non-zero.
*/
ENTRY(xen_irq_disable_direct)
+ BUG
+
movb $1, PER_CPU_VAR(xen_vcpu_info, XEN_vcpu_info_mask)
ENDPATCH(xen_irq_disable_direct)
ret
@@ -74,6 +85,8 @@
Xen and x86 use opposite senses (mask vs enable).
*/
ENTRY(xen_save_fl_direct)
+ BUG
+
testb $0xff, PER_CPU_VAR(xen_vcpu_info, XEN_vcpu_info_mask)
setz %ah
addb %ah,%ah
@@ -91,6 +104,8 @@
if so.
*/
ENTRY(xen_restore_fl_direct)
+ BUG
+
testb $X86_EFLAGS_IF>>8, %ah
setz PER_CPU_VAR(xen_vcpu_info, XEN_vcpu_info_mask)
/* Preempt here doesn't matter because that will deal with
@@ -122,7 +137,7 @@
push %r9
push %r10
push %r11
- call force_evtchn_callback
+ call xen_force_evtchn_callback
pop %r11
pop %r10
pop %r9
@@ -133,7 +148,6 @@
pop %rcx
pop %rax
ret
-#endif
ENTRY(xen_adjust_exception_frame)
mov 8+0(%rsp),%rcx
diff --git a/arch/x86/xen/xen-ops.h b/arch/x86/xen/xen-ops.h
index dd3c231..d7422dc 100644
--- a/arch/x86/xen/xen-ops.h
+++ b/arch/x86/xen/xen-ops.h
@@ -2,6 +2,7 @@
#define XEN_OPS_H
#include <linux/init.h>
+#include <linux/clocksource.h>
#include <linux/irqreturn.h>
#include <xen/xen-ops.h>
@@ -31,7 +32,10 @@
void __init xen_build_dynamic_phys_to_machine(void);
+void xen_init_irq_ops(void);
void xen_setup_timer(int cpu);
+void xen_teardown_timer(int cpu);
+cycle_t xen_clocksource_read(void);
void xen_setup_cpu_clockevents(void);
unsigned long xen_tsc_khz(void);
void __init xen_time_init(void);
@@ -50,6 +54,10 @@
#ifdef CONFIG_SMP
void xen_smp_init(void);
+void __init xen_init_spinlocks(void);
+__cpuinit void xen_init_lock_cpu(int cpu);
+void xen_uninit_lock_cpu(int cpu);
+
extern cpumask_t xen_cpu_initialized_map;
#else
static inline void xen_smp_init(void) {}
diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c
index a00359e..9606d2b 100644
--- a/arch/xtensa/kernel/setup.c
+++ b/arch/xtensa/kernel/setup.c
@@ -53,11 +53,6 @@
struct fd_ops *fd_ops;
#endif
-#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
-extern struct ide_ops no_ide_ops;
-struct ide_ops *ide_ops;
-#endif
-
extern struct rtc_ops no_rtc_ops;
struct rtc_ops *rtc_ops;
diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c
index 8dd3336..3c578ef 100644
--- a/drivers/acpi/glue.c
+++ b/drivers/acpi/glue.c
@@ -369,7 +369,6 @@
DBG("RTC unavailable?\n");
return 0;
}
-/* do this between RTC subsys_initcall() and rtc_cmos driver_initcall() */
-fs_initcall(acpi_rtc_init);
+module_init(acpi_rtc_init);
#endif
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index f17cd4b..78fbec8 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -7,7 +7,6 @@
depends on HAS_IOMEM
depends on BLOCK
depends on !(M32R || M68K) || BROKEN
- depends on !SUN4 || BROKEN
select SCSI
---help---
If you want to use a ATA hard disk, ATA tape drive, ATA CD-ROM or
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 59fe051..5d312dc 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -503,7 +503,7 @@
scsi_cmd[0] = ATA_16;
scsi_cmd[4] = args[2];
- if (args[0] == WIN_SMART) { /* hack -- ide driver does this too... */
+ if (args[0] == ATA_CMD_SMART) { /* hack -- ide driver does this too */
scsi_cmd[6] = args[3];
scsi_cmd[8] = args[1];
scsi_cmd[10] = 0x4f;
diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c
index 41b4361..02b596b 100644
--- a/drivers/ata/pata_pcmcia.c
+++ b/drivers/ata/pata_pcmcia.c
@@ -148,6 +148,64 @@
#define CS_CHECK(fn, ret) \
do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+struct pcmcia_config_check {
+ unsigned long ctl_base;
+ int skip_vcc;
+ int is_kme;
+};
+
+static int pcmcia_check_one_config(struct pcmcia_device *pdev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ struct pcmcia_config_check *stk = priv_data;
+
+ /* Check for matching Vcc, unless we're desperate */
+ if (!stk->skip_vcc) {
+ if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
+ if (vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / 10000)
+ return -ENODEV;
+ } else if (dflt->vcc.present & (1 << CISTPL_POWER_VNOM)) {
+ if (vcc != dflt->vcc.param[CISTPL_POWER_VNOM] / 10000)
+ return -ENODEV;
+ }
+ }
+
+ if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ pdev->conf.Vpp = cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+ else if (dflt->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ pdev->conf.Vpp = dflt->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+
+ if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
+ pdev->io.BasePort1 = io->win[0].base;
+ pdev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+ if (!(io->flags & CISTPL_IO_16BIT))
+ pdev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ if (io->nwin == 2) {
+ pdev->io.NumPorts1 = 8;
+ pdev->io.BasePort2 = io->win[1].base;
+ pdev->io.NumPorts2 = (stk->is_kme) ? 2 : 1;
+ if (pcmcia_request_io(pdev, &pdev->io) != 0)
+ return -ENODEV;
+ stk->ctl_base = pdev->io.BasePort2;
+ } else if ((io->nwin == 1) && (io->win[0].len >= 16)) {
+ pdev->io.NumPorts1 = io->win[0].len;
+ pdev->io.NumPorts2 = 0;
+ if (pcmcia_request_io(pdev, &pdev->io) != 0)
+ return -ENODEV;
+ stk->ctl_base = pdev->io.BasePort1 + 0x0e;
+ } else
+ return -ENODEV;
+ /* If we've got this far, we're done */
+ return 0;
+ }
+ return -ENODEV;
+}
+
/**
* pcmcia_init_one - attach a PCMCIA interface
* @pdev: pcmcia device
@@ -161,19 +219,11 @@
struct ata_host *host;
struct ata_port *ap;
struct ata_pcmcia_info *info;
- tuple_t tuple;
- struct {
- unsigned short buf[128];
- cisparse_t parse;
- config_info_t conf;
- cistpl_cftable_entry_t dflt;
- } *stk = NULL;
- cistpl_cftable_entry_t *cfg;
- int pass, last_ret = 0, last_fn = 0, is_kme = 0, ret = -ENOMEM, p;
+ struct pcmcia_config_check *stk = NULL;
+ int last_ret = 0, last_fn = 0, is_kme = 0, ret = -ENOMEM, p;
unsigned long io_base, ctl_base;
void __iomem *io_addr, *ctl_addr;
int n_ports = 1;
-
struct ata_port_operations *ops = &pcmcia_port_ops;
info = kzalloc(sizeof(*info), GFP_KERNEL);
@@ -193,96 +243,27 @@
pdev->conf.Attributes = CONF_ENABLE_IRQ;
pdev->conf.IntType = INT_MEMORY_AND_IO;
- /* Allocate resoure probing structures */
-
- stk = kzalloc(sizeof(*stk), GFP_KERNEL);
- if (!stk)
- goto out1;
-
- cfg = &stk->parse.cftable_entry;
-
- /* Tuples we are walking */
- tuple.TupleData = (cisdata_t *)&stk->buf;
- tuple.TupleOffset = 0;
- tuple.TupleDataMax = 255;
- tuple.Attributes = 0;
-
/* See if we have a manufacturer identifier. Use it to set is_kme for
vendor quirks */
is_kme = ((pdev->manf_id == MANFID_KME) &&
((pdev->card_id == PRODID_KME_KXLC005_A) ||
(pdev->card_id == PRODID_KME_KXLC005_B)));
- /* Not sure if this is right... look up the current Vcc */
- CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(pdev, &stk->conf));
-/* link->conf.Vcc = stk->conf.Vcc; */
+ /* Allocate resoure probing structures */
- pass = io_base = ctl_base = 0;
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- tuple.Attributes = 0;
- CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(pdev, &tuple));
+ stk = kzalloc(sizeof(*stk), GFP_KERNEL);
+ if (!stk)
+ goto out1;
+ stk->is_kme = is_kme;
+ stk->skip_vcc = io_base = ctl_base = 0;
- /* Now munch the resources looking for a suitable set */
- while (1) {
- if (pcmcia_get_tuple_data(pdev, &tuple) != 0)
- goto next_entry;
- if (pcmcia_parse_tuple(pdev, &tuple, &stk->parse) != 0)
- goto next_entry;
- /* Check for matching Vcc, unless we're desperate */
- if (!pass) {
- if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
- if (stk->conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / 10000)
- goto next_entry;
- } else if (stk->dflt.vcc.present & (1 << CISTPL_POWER_VNOM)) {
- if (stk->conf.Vcc != stk->dflt.vcc.param[CISTPL_POWER_VNOM] / 10000)
- goto next_entry;
- }
- }
-
- if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
- pdev->conf.Vpp = cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
- else if (stk->dflt.vpp1.present & (1 << CISTPL_POWER_VNOM))
- pdev->conf.Vpp = stk->dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000;
-
- if ((cfg->io.nwin > 0) || (stk->dflt.io.nwin > 0)) {
- cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &stk->dflt.io;
- pdev->conf.ConfigIndex = cfg->index;
- pdev->io.BasePort1 = io->win[0].base;
- pdev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
- if (!(io->flags & CISTPL_IO_16BIT))
- pdev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
- if (io->nwin == 2) {
- pdev->io.NumPorts1 = 8;
- pdev->io.BasePort2 = io->win[1].base;
- pdev->io.NumPorts2 = (is_kme) ? 2 : 1;
- if (pcmcia_request_io(pdev, &pdev->io) != 0)
- goto next_entry;
- io_base = pdev->io.BasePort1;
- ctl_base = pdev->io.BasePort2;
- } else if ((io->nwin == 1) && (io->win[0].len >= 16)) {
- pdev->io.NumPorts1 = io->win[0].len;
- pdev->io.NumPorts2 = 0;
- if (pcmcia_request_io(pdev, &pdev->io) != 0)
- goto next_entry;
- io_base = pdev->io.BasePort1;
- ctl_base = pdev->io.BasePort1 + 0x0e;
- } else
- goto next_entry;
- /* If we've got this far, we're done */
- break;
- }
-next_entry:
- if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
- memcpy(&stk->dflt, cfg, sizeof(stk->dflt));
- if (pass) {
- CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(pdev, &tuple));
- } else if (pcmcia_get_next_tuple(pdev, &tuple) != 0) {
- CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(pdev, &tuple));
- memset(&stk->dflt, 0, sizeof(stk->dflt));
- pass++;
- }
+ if (pcmcia_loop_config(pdev, pcmcia_check_one_config, stk)) {
+ stk->skip_vcc = 1;
+ if (pcmcia_loop_config(pdev, pcmcia_check_one_config, stk))
+ goto failed; /* No suitable config found */
}
-
+ io_base = pdev->io.BasePort1;
+ ctl_base = stk->ctl_base;
CS_CHECK(RequestIRQ, pcmcia_request_irq(pdev, &pdev->irq));
CS_CHECK(RequestConfiguration, pcmcia_request_configuration(pdev, &pdev->conf));
@@ -384,6 +365,7 @@
PCMCIA_DEVICE_MANF_CARD(0x0032, 0x0704),
PCMCIA_DEVICE_MANF_CARD(0x0032, 0x2904),
PCMCIA_DEVICE_MANF_CARD(0x0045, 0x0401), /* SanDisk CFA */
+ PCMCIA_DEVICE_MANF_CARD(0x004f, 0x0000), /* Kingston */
PCMCIA_DEVICE_MANF_CARD(0x0097, 0x1620), /* TI emulated */
PCMCIA_DEVICE_MANF_CARD(0x0098, 0x0000), /* Toshiba */
PCMCIA_DEVICE_MANF_CARD(0x00a4, 0x002d),
@@ -404,9 +386,9 @@
PCMCIA_DEVICE_PROD_ID12("EXP ", "CD-ROM", 0x0a5c52fd, 0x66536591),
PCMCIA_DEVICE_PROD_ID12("EXP ", "PnPIDE", 0x0a5c52fd, 0x0c694728),
PCMCIA_DEVICE_PROD_ID12("FREECOM", "PCCARD-IDE", 0x5714cbf7, 0x48e0ab8e),
- PCMCIA_DEVICE_PROD_ID12("Hyperstone", "Model1", 0x3d5b9ef5, 0xca6ab420),
PCMCIA_DEVICE_PROD_ID12("HITACHI", "FLASH", 0xf4f43949, 0x9eb86aae),
PCMCIA_DEVICE_PROD_ID12("HITACHI", "microdrive", 0xf4f43949, 0xa6d76178),
+ PCMCIA_DEVICE_PROD_ID12("Hyperstone", "Model1", 0x3d5b9ef5, 0xca6ab420),
PCMCIA_DEVICE_PROD_ID12("IBM", "microdrive", 0xb569a6e5, 0xa6d76178),
PCMCIA_DEVICE_PROD_ID12("IBM", "IBM17JSSFP20", 0xb569a6e5, 0xf2508753),
PCMCIA_DEVICE_PROD_ID12("KINGSTON", "CF8GB", 0x2e6d1829, 0xacbe682e),
diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c
index 73338d2..937c9c0 100644
--- a/drivers/atm/fore200e.c
+++ b/drivers/atm/fore200e.c
@@ -47,8 +47,9 @@
#include <asm/atomic.h>
#ifdef CONFIG_SBUS
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <asm/idprom.h>
-#include <asm/sbus.h>
#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/pgtable.h>
@@ -661,249 +662,189 @@
#ifdef CONFIG_SBUS
-static u32
-fore200e_sba_read(volatile u32 __iomem *addr)
+static u32 fore200e_sba_read(volatile u32 __iomem *addr)
{
return sbus_readl(addr);
}
-
-static void
-fore200e_sba_write(u32 val, volatile u32 __iomem *addr)
+static void fore200e_sba_write(u32 val, volatile u32 __iomem *addr)
{
sbus_writel(val, addr);
}
-
-static u32
-fore200e_sba_dma_map(struct fore200e* fore200e, void* virt_addr, int size, int direction)
+static u32 fore200e_sba_dma_map(struct fore200e *fore200e, void* virt_addr, int size, int direction)
{
- u32 dma_addr = sbus_map_single((struct sbus_dev*)fore200e->bus_dev, virt_addr, size, direction);
+ struct of_device *op = fore200e->bus_dev;
+ u32 dma_addr;
- DPRINTK(3, "SBUS DVMA mapping: virt_addr = 0x%p, size = %d, direction = %d --> dma_addr = 0x%08x\n",
- virt_addr, size, direction, dma_addr);
+ dma_addr = dma_map_single(&op->dev, virt_addr, size, direction);
+
+ DPRINTK(3, "SBUS DVMA mapping: virt_addr = 0x%p, size = %d, direction = %d --> dma_addr = 0x%08x\n",
+ virt_addr, size, direction, dma_addr);
- return dma_addr;
+ return dma_addr;
}
-
-static void
-fore200e_sba_dma_unmap(struct fore200e* fore200e, u32 dma_addr, int size, int direction)
+static void fore200e_sba_dma_unmap(struct fore200e *fore200e, u32 dma_addr, int size, int direction)
{
- DPRINTK(3, "SBUS DVMA unmapping: dma_addr = 0x%08x, size = %d, direction = %d,\n",
- dma_addr, size, direction);
+ struct of_device *op = fore200e->bus_dev;
- sbus_unmap_single((struct sbus_dev*)fore200e->bus_dev, dma_addr, size, direction);
+ DPRINTK(3, "SBUS DVMA unmapping: dma_addr = 0x%08x, size = %d, direction = %d,\n",
+ dma_addr, size, direction);
+
+ dma_unmap_single(&op->dev, dma_addr, size, direction);
}
-
-static void
-fore200e_sba_dma_sync_for_cpu(struct fore200e* fore200e, u32 dma_addr, int size, int direction)
+static void fore200e_sba_dma_sync_for_cpu(struct fore200e *fore200e, u32 dma_addr, int size, int direction)
{
- DPRINTK(3, "SBUS DVMA sync: dma_addr = 0x%08x, size = %d, direction = %d\n", dma_addr, size, direction);
+ struct of_device *op = fore200e->bus_dev;
+
+ DPRINTK(3, "SBUS DVMA sync: dma_addr = 0x%08x, size = %d, direction = %d\n", dma_addr, size, direction);
- sbus_dma_sync_single_for_cpu((struct sbus_dev*)fore200e->bus_dev, dma_addr, size, direction);
+ dma_sync_single_for_cpu(&op->dev, dma_addr, size, direction);
}
-static void
-fore200e_sba_dma_sync_for_device(struct fore200e* fore200e, u32 dma_addr, int size, int direction)
+static void fore200e_sba_dma_sync_for_device(struct fore200e *fore200e, u32 dma_addr, int size, int direction)
{
- DPRINTK(3, "SBUS DVMA sync: dma_addr = 0x%08x, size = %d, direction = %d\n", dma_addr, size, direction);
+ struct of_device *op = fore200e->bus_dev;
- sbus_dma_sync_single_for_device((struct sbus_dev*)fore200e->bus_dev, dma_addr, size, direction);
+ DPRINTK(3, "SBUS DVMA sync: dma_addr = 0x%08x, size = %d, direction = %d\n", dma_addr, size, direction);
+
+ dma_sync_single_for_device(&op->dev, dma_addr, size, direction);
}
-
-/* allocate a DVMA consistent chunk of memory intended to act as a communication mechanism
- (to hold descriptors, status, queues, etc.) shared by the driver and the adapter */
-
-static int
-fore200e_sba_dma_chunk_alloc(struct fore200e* fore200e, struct chunk* chunk,
- int size, int nbr, int alignment)
+/* Allocate a DVMA consistent chunk of memory intended to act as a communication mechanism
+ * (to hold descriptors, status, queues, etc.) shared by the driver and the adapter.
+ */
+static int fore200e_sba_dma_chunk_alloc(struct fore200e *fore200e, struct chunk *chunk,
+ int size, int nbr, int alignment)
{
- chunk->alloc_size = chunk->align_size = size * nbr;
+ struct of_device *op = fore200e->bus_dev;
- /* returned chunks are page-aligned */
- chunk->alloc_addr = sbus_alloc_consistent((struct sbus_dev*)fore200e->bus_dev,
- chunk->alloc_size,
- &chunk->dma_addr);
+ chunk->alloc_size = chunk->align_size = size * nbr;
- if ((chunk->alloc_addr == NULL) || (chunk->dma_addr == 0))
- return -ENOMEM;
+ /* returned chunks are page-aligned */
+ chunk->alloc_addr = dma_alloc_coherent(&op->dev, chunk->alloc_size,
+ &chunk->dma_addr, GFP_ATOMIC);
- chunk->align_addr = chunk->alloc_addr;
+ if ((chunk->alloc_addr == NULL) || (chunk->dma_addr == 0))
+ return -ENOMEM;
+
+ chunk->align_addr = chunk->alloc_addr;
- return 0;
+ return 0;
}
-
/* free a DVMA consistent chunk of memory */
-
-static void
-fore200e_sba_dma_chunk_free(struct fore200e* fore200e, struct chunk* chunk)
+static void fore200e_sba_dma_chunk_free(struct fore200e *fore200e, struct chunk *chunk)
{
- sbus_free_consistent((struct sbus_dev*)fore200e->bus_dev,
- chunk->alloc_size,
- chunk->alloc_addr,
- chunk->dma_addr);
+ struct of_device *op = fore200e->bus_dev;
+
+ dma_free_coherent(&op->dev, chunk->alloc_size,
+ chunk->alloc_addr, chunk->dma_addr);
}
-
-static void
-fore200e_sba_irq_enable(struct fore200e* fore200e)
+static void fore200e_sba_irq_enable(struct fore200e *fore200e)
{
- u32 hcr = fore200e->bus->read(fore200e->regs.sba.hcr) & SBA200E_HCR_STICKY;
- fore200e->bus->write(hcr | SBA200E_HCR_INTR_ENA, fore200e->regs.sba.hcr);
+ u32 hcr = fore200e->bus->read(fore200e->regs.sba.hcr) & SBA200E_HCR_STICKY;
+ fore200e->bus->write(hcr | SBA200E_HCR_INTR_ENA, fore200e->regs.sba.hcr);
}
-
-static int
-fore200e_sba_irq_check(struct fore200e* fore200e)
+static int fore200e_sba_irq_check(struct fore200e *fore200e)
{
- return fore200e->bus->read(fore200e->regs.sba.hcr) & SBA200E_HCR_INTR_REQ;
+ return fore200e->bus->read(fore200e->regs.sba.hcr) & SBA200E_HCR_INTR_REQ;
}
-
-static void
-fore200e_sba_irq_ack(struct fore200e* fore200e)
+static void fore200e_sba_irq_ack(struct fore200e *fore200e)
{
- u32 hcr = fore200e->bus->read(fore200e->regs.sba.hcr) & SBA200E_HCR_STICKY;
- fore200e->bus->write(hcr | SBA200E_HCR_INTR_CLR, fore200e->regs.sba.hcr);
+ u32 hcr = fore200e->bus->read(fore200e->regs.sba.hcr) & SBA200E_HCR_STICKY;
+ fore200e->bus->write(hcr | SBA200E_HCR_INTR_CLR, fore200e->regs.sba.hcr);
}
-
-static void
-fore200e_sba_reset(struct fore200e* fore200e)
+static void fore200e_sba_reset(struct fore200e *fore200e)
{
- fore200e->bus->write(SBA200E_HCR_RESET, fore200e->regs.sba.hcr);
- fore200e_spin(10);
- fore200e->bus->write(0, fore200e->regs.sba.hcr);
+ fore200e->bus->write(SBA200E_HCR_RESET, fore200e->regs.sba.hcr);
+ fore200e_spin(10);
+ fore200e->bus->write(0, fore200e->regs.sba.hcr);
}
-
-static int __init
-fore200e_sba_map(struct fore200e* fore200e)
+static int __init fore200e_sba_map(struct fore200e *fore200e)
{
- struct sbus_dev* sbus_dev = (struct sbus_dev*)fore200e->bus_dev;
- unsigned int bursts;
+ struct of_device *op = fore200e->bus_dev;
+ unsigned int bursts;
- /* gain access to the SBA specific registers */
- fore200e->regs.sba.hcr = sbus_ioremap(&sbus_dev->resource[0], 0, SBA200E_HCR_LENGTH, "SBA HCR");
- fore200e->regs.sba.bsr = sbus_ioremap(&sbus_dev->resource[1], 0, SBA200E_BSR_LENGTH, "SBA BSR");
- fore200e->regs.sba.isr = sbus_ioremap(&sbus_dev->resource[2], 0, SBA200E_ISR_LENGTH, "SBA ISR");
- fore200e->virt_base = sbus_ioremap(&sbus_dev->resource[3], 0, SBA200E_RAM_LENGTH, "SBA RAM");
+ /* gain access to the SBA specific registers */
+ fore200e->regs.sba.hcr = of_ioremap(&op->resource[0], 0, SBA200E_HCR_LENGTH, "SBA HCR");
+ fore200e->regs.sba.bsr = of_ioremap(&op->resource[1], 0, SBA200E_BSR_LENGTH, "SBA BSR");
+ fore200e->regs.sba.isr = of_ioremap(&op->resource[2], 0, SBA200E_ISR_LENGTH, "SBA ISR");
+ fore200e->virt_base = of_ioremap(&op->resource[3], 0, SBA200E_RAM_LENGTH, "SBA RAM");
- if (fore200e->virt_base == NULL) {
- printk(FORE200E "unable to map RAM of device %s\n", fore200e->name);
- return -EFAULT;
- }
-
- DPRINTK(1, "device %s mapped to 0x%p\n", fore200e->name, fore200e->virt_base);
-
- fore200e->bus->write(0x02, fore200e->regs.sba.isr); /* XXX hardwired interrupt level */
-
- /* get the supported DVMA burst sizes */
- bursts = prom_getintdefault(sbus_dev->bus->prom_node, "burst-sizes", 0x00);
-
- if (sbus_can_dma_64bit(sbus_dev))
- sbus_set_sbus64(sbus_dev, bursts);
-
- fore200e->state = FORE200E_STATE_MAP;
- return 0;
-}
-
-
-static void
-fore200e_sba_unmap(struct fore200e* fore200e)
-{
- sbus_iounmap(fore200e->regs.sba.hcr, SBA200E_HCR_LENGTH);
- sbus_iounmap(fore200e->regs.sba.bsr, SBA200E_BSR_LENGTH);
- sbus_iounmap(fore200e->regs.sba.isr, SBA200E_ISR_LENGTH);
- sbus_iounmap(fore200e->virt_base, SBA200E_RAM_LENGTH);
-}
-
-
-static int __init
-fore200e_sba_configure(struct fore200e* fore200e)
-{
- fore200e->state = FORE200E_STATE_CONFIGURE;
- return 0;
-}
-
-
-static struct fore200e* __init
-fore200e_sba_detect(const struct fore200e_bus* bus, int index)
-{
- struct fore200e* fore200e;
- struct sbus_bus* sbus_bus;
- struct sbus_dev* sbus_dev = NULL;
-
- unsigned int count = 0;
-
- for_each_sbus (sbus_bus) {
- for_each_sbusdev (sbus_dev, sbus_bus) {
- if (strcmp(sbus_dev->prom_name, SBA200E_PROM_NAME) == 0) {
- if (count >= index)
- goto found;
- count++;
- }
+ if (!fore200e->virt_base) {
+ printk(FORE200E "unable to map RAM of device %s\n", fore200e->name);
+ return -EFAULT;
}
- }
- return NULL;
+
+ DPRINTK(1, "device %s mapped to 0x%p\n", fore200e->name, fore200e->virt_base);
- found:
- if (sbus_dev->num_registers != 4) {
- printk(FORE200E "this %s device has %d instead of 4 registers\n",
- bus->model_name, sbus_dev->num_registers);
- return NULL;
- }
+ fore200e->bus->write(0x02, fore200e->regs.sba.isr); /* XXX hardwired interrupt level */
- fore200e = kzalloc(sizeof(struct fore200e), GFP_KERNEL);
- if (fore200e == NULL)
- return NULL;
+ /* get the supported DVMA burst sizes */
+ bursts = of_getintprop_default(op->node->parent, "burst-sizes", 0x00);
- fore200e->bus = bus;
- fore200e->bus_dev = sbus_dev;
- fore200e->irq = sbus_dev->irqs[ 0 ];
+ if (sbus_can_dma_64bit())
+ sbus_set_sbus64(&op->dev, bursts);
- fore200e->phys_base = (unsigned long)sbus_dev;
-
- sprintf(fore200e->name, "%s-%d", bus->model_name, index - 1);
-
- return fore200e;
+ fore200e->state = FORE200E_STATE_MAP;
+ return 0;
}
-
-static int __init
-fore200e_sba_prom_read(struct fore200e* fore200e, struct prom_data* prom)
+static void fore200e_sba_unmap(struct fore200e *fore200e)
{
- struct sbus_dev* sbus_dev = (struct sbus_dev*) fore200e->bus_dev;
- int len;
+ struct of_device *op = fore200e->bus_dev;
- len = prom_getproperty(sbus_dev->prom_node, "macaddrlo2", &prom->mac_addr[ 4 ], 4);
- if (len < 0)
- return -EBUSY;
-
- len = prom_getproperty(sbus_dev->prom_node, "macaddrhi4", &prom->mac_addr[ 2 ], 4);
- if (len < 0)
- return -EBUSY;
-
- prom_getproperty(sbus_dev->prom_node, "serialnumber",
- (char*)&prom->serial_number, sizeof(prom->serial_number));
-
- prom_getproperty(sbus_dev->prom_node, "promversion",
- (char*)&prom->hw_revision, sizeof(prom->hw_revision));
-
- return 0;
+ of_iounmap(&op->resource[0], fore200e->regs.sba.hcr, SBA200E_HCR_LENGTH);
+ of_iounmap(&op->resource[1], fore200e->regs.sba.bsr, SBA200E_BSR_LENGTH);
+ of_iounmap(&op->resource[2], fore200e->regs.sba.isr, SBA200E_ISR_LENGTH);
+ of_iounmap(&op->resource[3], fore200e->virt_base, SBA200E_RAM_LENGTH);
}
-
-static int
-fore200e_sba_proc_read(struct fore200e* fore200e, char *page)
+static int __init fore200e_sba_configure(struct fore200e *fore200e)
{
- struct sbus_dev* sbus_dev = (struct sbus_dev*)fore200e->bus_dev;
+ fore200e->state = FORE200E_STATE_CONFIGURE;
+ return 0;
+}
- return sprintf(page, " SBUS slot/device:\t\t%d/'%s'\n", sbus_dev->slot, sbus_dev->prom_name);
+static int __init fore200e_sba_prom_read(struct fore200e *fore200e, struct prom_data *prom)
+{
+ struct of_device *op = fore200e->bus_dev;
+ const u8 *prop;
+ int len;
+
+ prop = of_get_property(op->node, "madaddrlo2", &len);
+ if (!prop)
+ return -ENODEV;
+ memcpy(&prom->mac_addr[4], prop, 4);
+
+ prop = of_get_property(op->node, "madaddrhi4", &len);
+ if (!prop)
+ return -ENODEV;
+ memcpy(&prom->mac_addr[2], prop, 4);
+
+ prom->serial_number = of_getintprop_default(op->node, "serialnumber", 0);
+ prom->hw_revision = of_getintprop_default(op->node, "promversion", 0);
+
+ return 0;
+}
+
+static int fore200e_sba_proc_read(struct fore200e *fore200e, char *page)
+{
+ struct of_device *op = fore200e->bus_dev;
+ const struct linux_prom_registers *regs;
+
+ regs = of_get_property(op->node, "reg", NULL);
+
+ return sprintf(page, " SBUS slot/device:\t\t%d/'%s'\n",
+ (regs ? regs->which_io : 0), op->node->name);
}
#endif /* CONFIG_SBUS */
@@ -2572,7 +2513,7 @@
device = &((struct pci_dev *) fore200e->bus_dev)->dev;
#ifdef CONFIG_SBUS
else if (strcmp(fore200e->bus->model_name, "SBA-200E") == 0)
- device = &((struct sbus_dev *) fore200e->bus_dev)->ofdev.dev;
+ device = &((struct of_device *) fore200e->bus_dev)->dev;
#endif
else
return err;
@@ -2701,6 +2642,66 @@
return 0;
}
+#ifdef CONFIG_SBUS
+static int __devinit fore200e_sba_probe(struct of_device *op,
+ const struct of_device_id *match)
+{
+ const struct fore200e_bus *bus = match->data;
+ struct fore200e *fore200e;
+ static int index = 0;
+ int err;
+
+ fore200e = kzalloc(sizeof(struct fore200e), GFP_KERNEL);
+ if (!fore200e)
+ return -ENOMEM;
+
+ fore200e->bus = bus;
+ fore200e->bus_dev = op;
+ fore200e->irq = op->irqs[0];
+ fore200e->phys_base = op->resource[0].start;
+
+ sprintf(fore200e->name, "%s-%d", bus->model_name, index);
+
+ err = fore200e_init(fore200e);
+ if (err < 0) {
+ fore200e_shutdown(fore200e);
+ kfree(fore200e);
+ return err;
+ }
+
+ index++;
+ dev_set_drvdata(&op->dev, fore200e);
+
+ return 0;
+}
+
+static int __devexit fore200e_sba_remove(struct of_device *op)
+{
+ struct fore200e *fore200e = dev_get_drvdata(&op->dev);
+
+ fore200e_shutdown(fore200e);
+ kfree(fore200e);
+
+ return 0;
+}
+
+static const struct of_device_id fore200e_sba_match[] = {
+ {
+ .name = SBA200E_PROM_NAME,
+ .data = (void *) &fore200e_bus[1],
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, fore200e_sba_match);
+
+static struct of_platform_driver fore200e_sba_driver = {
+ .name = "fore_200e",
+ .match_table = fore200e_sba_match,
+ .probe = fore200e_sba_probe,
+ .remove = __devexit_p(fore200e_sba_remove),
+};
+#endif
+
#ifdef CONFIG_PCI
static int __devinit
fore200e_pca_detect(struct pci_dev *pci_dev, const struct pci_device_id *pci_ent)
@@ -2784,66 +2785,39 @@
};
#endif
-
-static int __init
-fore200e_module_init(void)
+static int __init fore200e_module_init(void)
{
- const struct fore200e_bus* bus;
- struct fore200e* fore200e;
- int index;
+ int err;
- printk(FORE200E "FORE Systems 200E-series ATM driver - version " FORE200E_VERSION "\n");
+ printk(FORE200E "FORE Systems 200E-series ATM driver - version " FORE200E_VERSION "\n");
- /* for each configured bus interface */
- for (bus = fore200e_bus; bus->model_name; bus++) {
-
- /* detect all boards present on that bus */
- for (index = 0; bus->detect && (fore200e = bus->detect(bus, index)); index++) {
-
- printk(FORE200E "device %s found at 0x%lx, IRQ %s\n",
- fore200e->bus->model_name,
- fore200e->phys_base, fore200e_irq_itoa(fore200e->irq));
-
- sprintf(fore200e->name, "%s-%d", bus->model_name, index);
-
- if (fore200e_init(fore200e) < 0) {
-
- fore200e_shutdown(fore200e);
- break;
- }
-
- list_add(&fore200e->entry, &fore200e_boards);
- }
- }
-
-#ifdef CONFIG_PCI
- if (!pci_register_driver(&fore200e_pca_driver))
- return 0;
+#ifdef CONFIG_SBUS
+ err = of_register_driver(&fore200e_sba_driver, &of_bus_type);
+ if (err)
+ return err;
#endif
- if (!list_empty(&fore200e_boards))
- return 0;
-
- return -ENODEV;
-}
-
-
-static void __exit
-fore200e_module_cleanup(void)
-{
- struct fore200e *fore200e, *next;
-
#ifdef CONFIG_PCI
- pci_unregister_driver(&fore200e_pca_driver);
+ err = pci_register_driver(&fore200e_pca_driver);
#endif
- list_for_each_entry_safe(fore200e, next, &fore200e_boards, entry) {
- fore200e_shutdown(fore200e);
- kfree(fore200e);
- }
- DPRINTK(1, "module being removed\n");
+#ifdef CONFIG_SBUS
+ if (err)
+ of_unregister_driver(&fore200e_sba_driver);
+#endif
+
+ return err;
}
+static void __exit fore200e_module_cleanup(void)
+{
+#ifdef CONFIG_PCI
+ pci_unregister_driver(&fore200e_pca_driver);
+#endif
+#ifdef CONFIG_SBUS
+ of_unregister_driver(&fore200e_sba_driver);
+#endif
+}
static int
fore200e_proc_read(struct atm_dev *dev, loff_t* pos, char* page)
@@ -3163,7 +3137,6 @@
fore200e_pca_dma_sync_for_device,
fore200e_pca_dma_chunk_alloc,
fore200e_pca_dma_chunk_free,
- NULL,
fore200e_pca_configure,
fore200e_pca_map,
fore200e_pca_reset,
@@ -3185,7 +3158,6 @@
fore200e_sba_dma_sync_for_device,
fore200e_sba_dma_chunk_alloc,
fore200e_sba_dma_chunk_free,
- fore200e_sba_detect,
fore200e_sba_configure,
fore200e_sba_map,
fore200e_sba_reset,
diff --git a/drivers/atm/fore200e.h b/drivers/atm/fore200e.h
index 5c6e7ad..7f97c09 100644
--- a/drivers/atm/fore200e.h
+++ b/drivers/atm/fore200e.h
@@ -778,9 +778,9 @@
/* SBA-200E registers */
typedef struct fore200e_sba_regs {
- volatile u32 __iomem *hcr; /* address of host control register */
- volatile u32 __iomem *bsr; /* address of burst transfer size register */
- volatile u32 __iomem *isr; /* address of interrupt level selection register */
+ u32 __iomem *hcr; /* address of host control register */
+ u32 __iomem *bsr; /* address of burst transfer size register */
+ u32 __iomem *isr; /* address of interrupt level selection register */
} fore200e_sba_regs_t;
@@ -810,7 +810,6 @@
void (*dma_sync_for_device)(struct fore200e*, u32, int, int);
int (*dma_chunk_alloc)(struct fore200e*, struct chunk*, int, int, int);
void (*dma_chunk_free)(struct fore200e*, struct chunk*);
- struct fore200e* (*detect)(const struct fore200e_bus*, int);
int (*configure)(struct fore200e*);
int (*map)(struct fore200e*);
void (*reset)(struct fore200e*);
diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c
index 49f2741..432cf40 100644
--- a/drivers/block/ataflop.c
+++ b/drivers/block/ataflop.c
@@ -1882,10 +1882,6 @@
/* Amiga, Mac, ... don't have Atari-compatible floppy :-) */
return -ENODEV;
- if (MACH_IS_HADES)
- /* Hades doesn't have Atari-compatible floppy */
- return -ENODEV;
-
if (register_blkdev(FLOPPY_MAJOR,"fd"))
return -EBUSY;
diff --git a/drivers/block/hd.c b/drivers/block/hd.c
index 682243b..482c0c4 100644
--- a/drivers/block/hd.c
+++ b/drivers/block/hd.c
@@ -39,6 +39,7 @@
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/blkpg.h>
+#include <linux/ata.h>
#include <linux/hdreg.h>
#define REALLY_SLOW_IO
@@ -370,7 +371,7 @@
struct hd_i_struct *disk = &hd_info[i];
disk->special_op = disk->recalibrate = 1;
hd_out(disk, disk->sect, disk->sect, disk->head-1,
- disk->cyl, WIN_SPECIFY, &reset_hd);
+ disk->cyl, ATA_CMD_INIT_DEV_PARAMS, &reset_hd);
if (reset)
goto repeat;
} else
@@ -558,7 +559,7 @@
{
if (disk->recalibrate) {
disk->recalibrate = 0;
- hd_out(disk, disk->sect, 0, 0, 0, WIN_RESTORE, &recal_intr);
+ hd_out(disk, disk->sect, 0, 0, 0, ATA_CMD_RESTORE, &recal_intr);
return reset;
}
if (disk->head > 16) {
@@ -631,13 +632,13 @@
if (blk_fs_request(req)) {
switch (rq_data_dir(req)) {
case READ:
- hd_out(disk, nsect, sec, head, cyl, WIN_READ,
+ hd_out(disk, nsect, sec, head, cyl, ATA_CMD_PIO_READ,
&read_intr);
if (reset)
goto repeat;
break;
case WRITE:
- hd_out(disk, nsect, sec, head, cyl, WIN_WRITE,
+ hd_out(disk, nsect, sec, head, cyl, ATA_CMD_PIO_WRITE,
&write_intr);
if (reset)
goto repeat;
diff --git a/drivers/block/sunvdc.c b/drivers/block/sunvdc.c
index a8de037..953c0b8 100644
--- a/drivers/block/sunvdc.c
+++ b/drivers/block/sunvdc.c
@@ -1,6 +1,6 @@
/* sunvdc.c: Sun LDOM Virtual Disk Client.
*
- * Copyright (C) 2007 David S. Miller <davem@davemloft.net>
+ * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net>
*/
#include <linux/module.h>
@@ -834,7 +834,7 @@
return 0;
}
-static struct vio_device_id vdc_port_match[] = {
+static const struct vio_device_id vdc_port_match[] = {
{
.type = "vdc-port",
},
diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
index bff602c..1a50ae7 100644
--- a/drivers/block/xen-blkfront.c
+++ b/drivers/block/xen-blkfront.c
@@ -1066,7 +1066,7 @@
static int __init xlblk_init(void)
{
- if (!is_running_on_xen())
+ if (!xen_domain())
return -ENODEV;
if (register_blkdev(XENVBD_MAJOR, DEV_NAME)) {
diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c
index bcf5792..e6ee21d 100644
--- a/drivers/bluetooth/bluecard_cs.c
+++ b/drivers/bluetooth/bluecard_cs.c
@@ -901,23 +901,23 @@
for (n = 0; n < 0x400; n += 0x40) {
link->io.BasePort1 = n ^ 0x300;
i = pcmcia_request_io(link, &link->io);
- if (i == CS_SUCCESS)
+ if (i == 0)
break;
}
- if (i != CS_SUCCESS) {
+ if (i != 0) {
cs_error(link, RequestIO, i);
goto failed;
}
i = pcmcia_request_irq(link, &link->irq);
- if (i != CS_SUCCESS) {
+ if (i != 0) {
cs_error(link, RequestIRQ, i);
link->irq.AssignedIRQ = 0;
}
i = pcmcia_request_configuration(link, &link->conf);
- if (i != CS_SUCCESS) {
+ if (i != 0) {
cs_error(link, RequestConfiguration, i);
goto failed;
}
diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c
index 2705847..2cbe70b 100644
--- a/drivers/bluetooth/bt3c_cs.c
+++ b/drivers/bluetooth/bt3c_cs.c
@@ -678,101 +678,78 @@
kfree(info);
}
-static int get_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse)
+static int bt3c_check_config(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
{
- int i;
+ unsigned long try = (unsigned long) priv_data;
- i = pcmcia_get_tuple_data(handle, tuple);
- if (i != CS_SUCCESS)
- return i;
-
- return pcmcia_parse_tuple(handle, tuple, parse);
+ if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ p_dev->conf.Vpp = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+ if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) &&
+ (cf->io.win[0].base != 0)) {
+ p_dev->io.BasePort1 = cf->io.win[0].base;
+ p_dev->io.IOAddrLines = (try == 0) ? 16 :
+ cf->io.flags & CISTPL_IO_LINES_MASK;
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+ return 0;
+ }
+ return -ENODEV;
}
-static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse)
+static int bt3c_check_config_notpicky(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
{
- if (pcmcia_get_first_tuple(handle, tuple) != CS_SUCCESS)
- return CS_NO_MORE_ITEMS;
- return get_tuple(handle, tuple, parse);
-}
+ static unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
+ int j;
-static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse)
-{
- if (pcmcia_get_next_tuple(handle, tuple) != CS_SUCCESS)
- return CS_NO_MORE_ITEMS;
- return get_tuple(handle, tuple, parse);
+ if ((cf->io.nwin > 0) && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) {
+ for (j = 0; j < 5; j++) {
+ p_dev->io.BasePort1 = base[j];
+ p_dev->io.IOAddrLines = base[j] ? 16 : 3;
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+ return 0;
+ }
+ }
+ return -ENODEV;
}
static int bt3c_config(struct pcmcia_device *link)
{
- static unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
bt3c_info_t *info = link->priv;
- tuple_t tuple;
- u_short buf[256];
- cisparse_t parse;
- cistpl_cftable_entry_t *cf = &parse.cftable_entry;
- int i, j, try;
+ int i;
+ unsigned long try;
- /* First pass: look for a config entry that looks normal. */
- tuple.TupleData = (cisdata_t *)buf;
- tuple.TupleOffset = 0;
- tuple.TupleDataMax = 255;
- tuple.Attributes = 0;
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- /* Two tries: without IO aliases, then with aliases */
- for (try = 0; try < 2; try++) {
- i = first_tuple(link, &tuple, &parse);
- while (i != CS_NO_MORE_ITEMS) {
- if (i != CS_SUCCESS)
- goto next_entry;
- if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM))
- link->conf.Vpp = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
- if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && (cf->io.win[0].base != 0)) {
- link->conf.ConfigIndex = cf->index;
- link->io.BasePort1 = cf->io.win[0].base;
- link->io.IOAddrLines = (try == 0) ? 16 : cf->io.flags & CISTPL_IO_LINES_MASK;
- i = pcmcia_request_io(link, &link->io);
- if (i == CS_SUCCESS)
- goto found_port;
- }
-next_entry:
- i = next_tuple(link, &tuple, &parse);
- }
- }
+ /* First pass: look for a config entry that looks normal.
+ Two tries: without IO aliases, then with aliases */
+ for (try = 0; try < 2; try++)
+ if (!pcmcia_loop_config(link, bt3c_check_config, (void *) try))
+ goto found_port;
/* Second pass: try to find an entry that isn't picky about
its base address, then try to grab any standard serial port
address, and finally try to get any free port. */
- i = first_tuple(link, &tuple, &parse);
- while (i != CS_NO_MORE_ITEMS) {
- if ((i == CS_SUCCESS) && (cf->io.nwin > 0) && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) {
- link->conf.ConfigIndex = cf->index;
- for (j = 0; j < 5; j++) {
- link->io.BasePort1 = base[j];
- link->io.IOAddrLines = base[j] ? 16 : 3;
- i = pcmcia_request_io(link, &link->io);
- if (i == CS_SUCCESS)
- goto found_port;
- }
- }
- i = next_tuple(link, &tuple, &parse);
- }
+ if (!pcmcia_loop_config(link, bt3c_check_config_notpicky, NULL))
+ goto found_port;
+
+ BT_ERR("No usable port range found");
+ cs_error(link, RequestIO, -ENODEV);
+ goto failed;
found_port:
- if (i != CS_SUCCESS) {
- BT_ERR("No usable port range found");
- cs_error(link, RequestIO, i);
- goto failed;
- }
-
i = pcmcia_request_irq(link, &link->irq);
- if (i != CS_SUCCESS) {
+ if (i != 0) {
cs_error(link, RequestIRQ, i);
link->irq.AssignedIRQ = 0;
}
i = pcmcia_request_configuration(link, &link->conf);
- if (i != CS_SUCCESS) {
+ if (i != 0) {
cs_error(link, RequestConfiguration, i);
goto failed;
}
diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c
index 68d1d25..8e556b7 100644
--- a/drivers/bluetooth/btuart_cs.c
+++ b/drivers/bluetooth/btuart_cs.c
@@ -607,102 +607,78 @@
kfree(info);
}
-static int get_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse)
+static int btuart_check_config(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
{
- int i;
+ int *try = priv_data;
- i = pcmcia_get_tuple_data(handle, tuple);
- if (i != CS_SUCCESS)
- return i;
-
- return pcmcia_parse_tuple(handle, tuple, parse);
+ if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ p_dev->conf.Vpp = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+ if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) &&
+ (cf->io.win[0].base != 0)) {
+ p_dev->io.BasePort1 = cf->io.win[0].base;
+ p_dev->io.IOAddrLines = (*try == 0) ? 16 :
+ cf->io.flags & CISTPL_IO_LINES_MASK;
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+ return 0;
+ }
+ return -ENODEV;
}
-static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse)
+static int btuart_check_config_notpicky(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
{
- if (pcmcia_get_first_tuple(handle, tuple) != CS_SUCCESS)
- return CS_NO_MORE_ITEMS;
- return get_tuple(handle, tuple, parse);
-}
+ static unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
+ int j;
-static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse)
-{
- if (pcmcia_get_next_tuple(handle, tuple) != CS_SUCCESS)
- return CS_NO_MORE_ITEMS;
- return get_tuple(handle, tuple, parse);
+ if ((cf->io.nwin > 0) && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) {
+ for (j = 0; j < 5; j++) {
+ p_dev->io.BasePort1 = base[j];
+ p_dev->io.IOAddrLines = base[j] ? 16 : 3;
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+ return 0;
+ }
+ }
+ return -ENODEV;
}
static int btuart_config(struct pcmcia_device *link)
{
- static unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
btuart_info_t *info = link->priv;
- tuple_t tuple;
- u_short buf[256];
- cisparse_t parse;
- cistpl_cftable_entry_t *cf = &parse.cftable_entry;
- int i, j, try;
+ int i;
+ int try;
- /* First pass: look for a config entry that looks normal. */
- tuple.TupleData = (cisdata_t *) buf;
- tuple.TupleOffset = 0;
- tuple.TupleDataMax = 255;
- tuple.Attributes = 0;
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- /* Two tries: without IO aliases, then with aliases */
- for (try = 0; try < 2; try++) {
- i = first_tuple(link, &tuple, &parse);
- while (i != CS_NO_MORE_ITEMS) {
- if (i != CS_SUCCESS)
- goto next_entry;
- if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM))
- link->conf.Vpp = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
- if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && (cf->io.win[0].base != 0)) {
- link->conf.ConfigIndex = cf->index;
- link->io.BasePort1 = cf->io.win[0].base;
- link->io.IOAddrLines = (try == 0) ? 16 : cf->io.flags & CISTPL_IO_LINES_MASK;
- i = pcmcia_request_io(link, &link->io);
- if (i == CS_SUCCESS)
- goto found_port;
- }
-next_entry:
- i = next_tuple(link, &tuple, &parse);
- }
- }
+ /* First pass: look for a config entry that looks normal.
+ Two tries: without IO aliases, then with aliases */
+ for (try = 0; try < 2; try++)
+ if (!pcmcia_loop_config(link, btuart_check_config, &try))
+ goto found_port;
/* Second pass: try to find an entry that isn't picky about
its base address, then try to grab any standard serial port
address, and finally try to get any free port. */
- i = first_tuple(link, &tuple, &parse);
- while (i != CS_NO_MORE_ITEMS) {
- if ((i == CS_SUCCESS) && (cf->io.nwin > 0)
- && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) {
- link->conf.ConfigIndex = cf->index;
- for (j = 0; j < 5; j++) {
- link->io.BasePort1 = base[j];
- link->io.IOAddrLines = base[j] ? 16 : 3;
- i = pcmcia_request_io(link, &link->io);
- if (i == CS_SUCCESS)
- goto found_port;
- }
- }
- i = next_tuple(link, &tuple, &parse);
- }
+ if (!pcmcia_loop_config(link, btuart_check_config_notpicky, NULL))
+ goto found_port;
+
+ BT_ERR("No usable port range found");
+ cs_error(link, RequestIO, -ENODEV);
+ goto failed;
found_port:
- if (i != CS_SUCCESS) {
- BT_ERR("No usable port range found");
- cs_error(link, RequestIO, i);
- goto failed;
- }
-
i = pcmcia_request_irq(link, &link->irq);
- if (i != CS_SUCCESS) {
+ if (i != 0) {
cs_error(link, RequestIRQ, i);
link->irq.AssignedIRQ = 0;
}
i = pcmcia_request_configuration(link, &link->conf);
- if (i != CS_SUCCESS) {
+ if (i != 0) {
cs_error(link, RequestConfiguration, i);
goto failed;
}
diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c
index dae45cd..e6e6b03 100644
--- a/drivers/bluetooth/dtl1_cs.c
+++ b/drivers/bluetooth/dtl1_cs.c
@@ -590,75 +590,40 @@
kfree(info);
}
-static int get_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse)
+static int dtl1_confcheck(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
{
- int i;
-
- i = pcmcia_get_tuple_data(handle, tuple);
- if (i != CS_SUCCESS)
- return i;
-
- return pcmcia_parse_tuple(handle, tuple, parse);
-}
-
-static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse)
-{
- if (pcmcia_get_first_tuple(handle, tuple) != CS_SUCCESS)
- return CS_NO_MORE_ITEMS;
- return get_tuple(handle, tuple, parse);
-}
-
-static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse)
-{
- if (pcmcia_get_next_tuple(handle, tuple) != CS_SUCCESS)
- return CS_NO_MORE_ITEMS;
- return get_tuple(handle, tuple, parse);
+ if ((cf->io.nwin == 1) && (cf->io.win[0].len > 8)) {
+ p_dev->io.BasePort1 = cf->io.win[0].base;
+ p_dev->io.NumPorts1 = cf->io.win[0].len; /*yo */
+ p_dev->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK;
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+ return 0;
+ }
+ return -ENODEV;
}
static int dtl1_config(struct pcmcia_device *link)
{
dtl1_info_t *info = link->priv;
- tuple_t tuple;
- u_short buf[256];
- cisparse_t parse;
- cistpl_cftable_entry_t *cf = &parse.cftable_entry;
int i;
- tuple.TupleData = (cisdata_t *)buf;
- tuple.TupleOffset = 0;
- tuple.TupleDataMax = 255;
- tuple.Attributes = 0;
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-
/* Look for a generic full-sized window */
link->io.NumPorts1 = 8;
- i = first_tuple(link, &tuple, &parse);
- while (i != CS_NO_MORE_ITEMS) {
- if ((i == CS_SUCCESS) && (cf->io.nwin == 1) && (cf->io.win[0].len > 8)) {
- link->conf.ConfigIndex = cf->index;
- link->io.BasePort1 = cf->io.win[0].base;
- link->io.NumPorts1 = cf->io.win[0].len; /*yo */
- link->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK;
- i = pcmcia_request_io(link, &link->io);
- if (i == CS_SUCCESS)
- break;
- }
- i = next_tuple(link, &tuple, &parse);
- }
-
- if (i != CS_SUCCESS) {
- cs_error(link, RequestIO, i);
+ if (!pcmcia_loop_config(link, dtl1_confcheck, NULL))
goto failed;
- }
i = pcmcia_request_irq(link, &link->irq);
- if (i != CS_SUCCESS) {
+ if (i != 0) {
cs_error(link, RequestIRQ, i);
link->irq.AssignedIRQ = 0;
}
i = pcmcia_request_configuration(link, &link->conf);
- if (i != CS_SUCCESS) {
+ if (i != 0) {
cs_error(link, RequestConfiguration, i);
goto failed;
}
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 8dfcf77..4426bb5 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -484,7 +484,7 @@
return -EUNATCH;
default:
- err = n_tty_ioctl(tty, file, cmd, arg);
+ err = n_tty_ioctl_helper(tty, file, cmd, arg);
break;
};
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index caff851..700ff96 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -350,7 +350,7 @@
config STALLION
tristate "Stallion EasyIO or EC8/32 support"
- depends on STALDRV && BROKEN_ON_SMP && (ISA || EISA || PCI)
+ depends on STALDRV && (ISA || EISA || PCI)
help
If you have an EasyIO or EasyConnection 8/32 multiport Stallion
card, then this is for you; say Y. Make sure to read
@@ -361,7 +361,7 @@
config ISTALLION
tristate "Stallion EC8/64, ONboard, Brumby support"
- depends on STALDRV && BROKEN_ON_SMP && (ISA || EISA || PCI)
+ depends on STALDRV && (ISA || EISA || PCI)
help
If you have an EasyConnection 8/64, ONboard, Brumby or Stallion
serial multiport card, say Y here. Make sure to read
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 6850f6d..1a4247d 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -7,7 +7,7 @@
#
FONTMAPFILE = cp437.uni
-obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o
+obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o tty_buffer.o tty_port.o
obj-$(CONFIG_LEGACY_PTYS) += pty.o
obj-$(CONFIG_UNIX98_PTYS) += pty.o
diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c
index 6e763e3..98821f9 100644
--- a/drivers/char/amiserial.c
+++ b/drivers/char/amiserial.c
@@ -837,9 +837,6 @@
struct async_struct *info;
unsigned long flags;
- if (!tty)
- return 0;
-
info = tty->driver_data;
if (serial_paranoia_check(info, tty->name, "rs_put_char"))
@@ -892,9 +889,6 @@
struct async_struct *info;
unsigned long flags;
- if (!tty)
- return 0;
-
info = tty->driver_data;
if (serial_paranoia_check(info, tty->name, "rs_write"))
diff --git a/drivers/char/applicom.c b/drivers/char/applicom.c
index 31d08b6..b899d91 100644
--- a/drivers/char/applicom.c
+++ b/drivers/char/applicom.c
@@ -712,8 +712,7 @@
IndexCard = adgl->num_card-1;
- if(cmd != 0 && cmd != 6 &&
- ((IndexCard >= MAX_BOARD) || !apbs[IndexCard].RamIO)) {
+ if(cmd != 6 && ((IndexCard >= MAX_BOARD) || !apbs[IndexCard].RamIO)) {
static int warncount = 10;
if (warncount) {
printk( KERN_WARNING "APPLICOM driver IOCTL, bad board number %d\n",(int)IndexCard+1);
@@ -832,8 +831,7 @@
}
break;
default:
- printk(KERN_INFO "APPLICOM driver ioctl, unknown function code %d\n",cmd) ;
- ret = -EINVAL;
+ ret = -ENOTTY;
break;
}
Dummy = readb(apbs[IndexCard].RamIO + VERS);
diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c
index fe6d774..5e5b1dc 100644
--- a/drivers/char/cyclades.c
+++ b/drivers/char/cyclades.c
@@ -4993,12 +4993,14 @@
device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) {
card_name = "Cyclom-Y";
- addr0 = pci_iomap(pdev, 0, CyPCI_Yctl);
+ addr0 = ioremap_nocache(pci_resource_start(pdev, 0),
+ CyPCI_Yctl);
if (addr0 == NULL) {
dev_err(&pdev->dev, "can't remap ctl region\n");
goto err_reg;
}
- addr2 = pci_iomap(pdev, 2, CyPCI_Ywin);
+ addr2 = ioremap_nocache(pci_resource_start(pdev, 2),
+ CyPCI_Ywin);
if (addr2 == NULL) {
dev_err(&pdev->dev, "can't remap base region\n");
goto err_unmap;
@@ -5013,7 +5015,8 @@
} else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi) {
struct RUNTIME_9060 __iomem *ctl_addr;
- ctl_addr = addr0 = pci_iomap(pdev, 0, CyPCI_Zctl);
+ ctl_addr = addr0 = ioremap_nocache(pci_resource_start(pdev, 0),
+ CyPCI_Zctl);
if (addr0 == NULL) {
dev_err(&pdev->dev, "can't remap ctl region\n");
goto err_reg;
@@ -5026,8 +5029,8 @@
mailbox = (u32)readl(&ctl_addr->mail_box_0);
- addr2 = pci_iomap(pdev, 2, mailbox == ZE_V1 ?
- CyPCI_Ze_win : CyPCI_Zwin);
+ addr2 = ioremap_nocache(pci_resource_start(pdev, 2),
+ mailbox == ZE_V1 ? CyPCI_Ze_win : CyPCI_Zwin);
if (addr2 == NULL) {
dev_err(&pdev->dev, "can't remap base region\n");
goto err_unmap;
@@ -5159,9 +5162,9 @@
cy_card[card_no].base_addr = NULL;
free_irq(irq, &cy_card[card_no]);
err_unmap:
- pci_iounmap(pdev, addr0);
+ iounmap(addr0);
if (addr2)
- pci_iounmap(pdev, addr2);
+ iounmap(addr2);
err_reg:
pci_release_regions(pdev);
err_dis:
@@ -5186,9 +5189,9 @@
cy_writew(cinfo->ctl_addr + 0x68,
readw(cinfo->ctl_addr + 0x68) & ~0x0900);
- pci_iounmap(pdev, cinfo->base_addr);
+ iounmap(cinfo->base_addr);
if (cinfo->ctl_addr)
- pci_iounmap(pdev, cinfo->ctl_addr);
+ iounmap(cinfo->ctl_addr);
if (cinfo->irq
#ifndef CONFIG_CYZ_INTR
&& !IS_CYC_Z(*cinfo)
diff --git a/drivers/char/epca.c b/drivers/char/epca.c
index 456e4ed..4998b27 100644
--- a/drivers/char/epca.c
+++ b/drivers/char/epca.c
@@ -1376,6 +1376,7 @@
unsigned long flags;
u16 tseg, rseg;
+ tty_port_init(&ch->port);
ch->brdchan = bc;
ch->mailbox = gd;
INIT_WORK(&ch->tqueue, do_softint);
@@ -1510,10 +1511,6 @@
ch->fepstopca = 0;
ch->close_delay = 50;
- ch->port.count = 0;
- ch->port.blocked_open = 0;
- init_waitqueue_head(&ch->port.open_wait);
- init_waitqueue_head(&ch->port.close_wait);
spin_unlock_irqrestore(&epca_lock, flags);
}
diff --git a/drivers/char/generic_serial.c b/drivers/char/generic_serial.c
index 19d3afb..c6090f8 100644
--- a/drivers/char/generic_serial.c
+++ b/drivers/char/generic_serial.c
@@ -54,8 +54,6 @@
func_enter ();
- if (!tty) return 0;
-
port = tty->driver_data;
if (!port) return 0;
@@ -97,8 +95,6 @@
func_enter ();
- if (!tty) return 0;
-
port = tty->driver_data;
if (!port) return 0;
@@ -185,7 +181,6 @@
struct gs_port *port;
func_enter ();
- if (!tty) return 0;
port = tty->driver_data;
if (!port->rd) return 0;
@@ -274,8 +269,6 @@
func_enter ();
- if (!tty) return;
-
port = tty->driver_data;
if (!port) return;
@@ -296,8 +289,6 @@
func_enter ();
- if (!tty) return;
-
port = tty->driver_data;
if (!port) return;
@@ -321,8 +312,6 @@
func_enter ();
- if (!tty) return;
-
port = tty->driver_data;
if (!port) return;
@@ -341,8 +330,6 @@
{
struct gs_port *port;
- if (!tty) return;
-
port = tty->driver_data;
if (!port) return;
@@ -393,8 +380,6 @@
func_enter ();
- if (!tty) return;
-
port = tty->driver_data;
tty = port->port.tty;
if (!tty)
@@ -426,8 +411,6 @@
tty = port->port.tty;
- if (!tty) return 0;
-
gs_dprintk (GS_DEBUG_BTR, "Entering gs_block_till_ready.\n");
/*
* If the device is in the middle of being closed, then block
@@ -523,8 +506,6 @@
func_enter ();
- if (!tty) return;
-
port = (struct gs_port *) tty->driver_data;
if (!port) return;
@@ -621,8 +602,6 @@
func_enter();
- if (!tty) return;
-
port = tty->driver_data;
if (!port) return;
diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c
index b3f5dbc..f3cfb4c 100644
--- a/drivers/char/hpet.c
+++ b/drivers/char/hpet.c
@@ -53,6 +53,11 @@
#define HPET_RANGE_SIZE 1024 /* from HPET spec */
+
+/* WARNING -- don't get confused. These macros are never used
+ * to write the (single) counter, and rarely to read it.
+ * They're badly named; to fix, someday.
+ */
#if BITS_PER_LONG == 64
#define write_counter(V, MC) writeq(V, MC)
#define read_counter(MC) readq(MC)
@@ -77,7 +82,7 @@
.rating = 250,
.read = read_hpet,
.mask = CLOCKSOURCE_MASK(64),
- .mult = 0, /*to be caluclated*/
+ .mult = 0, /* to be calculated */
.shift = 10,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
@@ -86,8 +91,6 @@
/* A lock for concurrent access by app and isr hpet activity. */
static DEFINE_SPINLOCK(hpet_lock);
-/* A lock for concurrent intermodule access to hpet and isr hpet activity. */
-static DEFINE_SPINLOCK(hpet_task_lock);
#define HPET_DEV_NAME (7)
@@ -99,7 +102,6 @@
unsigned long hd_irqdata;
wait_queue_head_t hd_waitqueue;
struct fasync_struct *hd_async_queue;
- struct hpet_task *hd_task;
unsigned int hd_flags;
unsigned int hd_irq;
unsigned int hd_hdwirq;
@@ -173,11 +175,6 @@
writel(isr, &devp->hd_hpet->hpet_isr);
spin_unlock(&hpet_lock);
- spin_lock(&hpet_task_lock);
- if (devp->hd_task)
- devp->hd_task->ht_func(devp->hd_task->ht_data);
- spin_unlock(&hpet_task_lock);
-
wake_up_interruptible(&devp->hd_waitqueue);
kill_fasync(&devp->hd_async_queue, SIGIO, POLL_IN);
@@ -185,6 +182,67 @@
return IRQ_HANDLED;
}
+static void hpet_timer_set_irq(struct hpet_dev *devp)
+{
+ unsigned long v;
+ int irq, gsi;
+ struct hpet_timer __iomem *timer;
+
+ spin_lock_irq(&hpet_lock);
+ if (devp->hd_hdwirq) {
+ spin_unlock_irq(&hpet_lock);
+ return;
+ }
+
+ timer = devp->hd_timer;
+
+ /* we prefer level triggered mode */
+ v = readl(&timer->hpet_config);
+ if (!(v & Tn_INT_TYPE_CNF_MASK)) {
+ v |= Tn_INT_TYPE_CNF_MASK;
+ writel(v, &timer->hpet_config);
+ }
+ spin_unlock_irq(&hpet_lock);
+
+ v = (readq(&timer->hpet_config) & Tn_INT_ROUTE_CAP_MASK) >>
+ Tn_INT_ROUTE_CAP_SHIFT;
+
+ /*
+ * In PIC mode, skip IRQ0-4, IRQ6-9, IRQ12-15 which is always used by
+ * legacy device. In IO APIC mode, we skip all the legacy IRQS.
+ */
+ if (acpi_irq_model == ACPI_IRQ_MODEL_PIC)
+ v &= ~0xf3df;
+ else
+ v &= ~0xffff;
+
+ for (irq = find_first_bit(&v, HPET_MAX_IRQ); irq < HPET_MAX_IRQ;
+ irq = find_next_bit(&v, HPET_MAX_IRQ, 1 + irq)) {
+
+ if (irq >= NR_IRQS) {
+ irq = HPET_MAX_IRQ;
+ break;
+ }
+
+ gsi = acpi_register_gsi(irq, ACPI_LEVEL_SENSITIVE,
+ ACPI_ACTIVE_LOW);
+ if (gsi > 0)
+ break;
+
+ /* FIXME: Setup interrupt source table */
+ }
+
+ if (irq < HPET_MAX_IRQ) {
+ spin_lock_irq(&hpet_lock);
+ v = readl(&timer->hpet_config);
+ v |= irq << Tn_INT_ROUTE_CNF_SHIFT;
+ writel(v, &timer->hpet_config);
+ devp->hd_hdwirq = gsi;
+ spin_unlock_irq(&hpet_lock);
+ }
+ return;
+}
+
static int hpet_open(struct inode *inode, struct file *file)
{
struct hpet_dev *devp;
@@ -199,8 +257,7 @@
for (devp = NULL, hpetp = hpets; hpetp && !devp; hpetp = hpetp->hp_next)
for (i = 0; i < hpetp->hp_ntimer; i++)
- if (hpetp->hp_dev[i].hd_flags & HPET_OPEN
- || hpetp->hp_dev[i].hd_task)
+ if (hpetp->hp_dev[i].hd_flags & HPET_OPEN)
continue;
else {
devp = &hpetp->hp_dev[i];
@@ -219,6 +276,8 @@
spin_unlock_irq(&hpet_lock);
unlock_kernel();
+ hpet_timer_set_irq(devp);
+
return 0;
}
@@ -441,7 +500,11 @@
devp->hd_irq = irq;
t = devp->hd_ireqfreq;
v = readq(&timer->hpet_config);
- g = v | Tn_INT_ENB_CNF_MASK;
+
+ /* 64-bit comparators are not yet supported through the ioctls,
+ * so force this into 32-bit mode if it supports both modes
+ */
+ g = v | Tn_32MODE_CNF_MASK | Tn_INT_ENB_CNF_MASK;
if (devp->hd_flags & HPET_PERIODIC) {
write_counter(t, &timer->hpet_compare);
@@ -451,6 +514,12 @@
v |= Tn_VAL_SET_CNF_MASK;
writeq(v, &timer->hpet_config);
local_irq_save(flags);
+
+ /* NOTE: what we modify here is a hidden accumulator
+ * register supported by periodic-capable comparators.
+ * We never want to modify the (single) counter; that
+ * would affect all the comparators.
+ */
m = read_counter(&hpet->hpet_mc);
write_counter(t + m + hpetp->hp_delta, &timer->hpet_compare);
} else {
@@ -604,57 +673,6 @@
return 0;
}
-static inline int hpet_tpcheck(struct hpet_task *tp)
-{
- struct hpet_dev *devp;
- struct hpets *hpetp;
-
- devp = tp->ht_opaque;
-
- if (!devp)
- return -ENXIO;
-
- for (hpetp = hpets; hpetp; hpetp = hpetp->hp_next)
- if (devp >= hpetp->hp_dev
- && devp < (hpetp->hp_dev + hpetp->hp_ntimer)
- && devp->hd_hpet == hpetp->hp_hpet)
- return 0;
-
- return -ENXIO;
-}
-
-#if 0
-int hpet_unregister(struct hpet_task *tp)
-{
- struct hpet_dev *devp;
- struct hpet_timer __iomem *timer;
- int err;
-
- if ((err = hpet_tpcheck(tp)))
- return err;
-
- spin_lock_irq(&hpet_task_lock);
- spin_lock(&hpet_lock);
-
- devp = tp->ht_opaque;
- if (devp->hd_task != tp) {
- spin_unlock(&hpet_lock);
- spin_unlock_irq(&hpet_task_lock);
- return -ENXIO;
- }
-
- timer = devp->hd_timer;
- writeq((readq(&timer->hpet_config) & ~Tn_INT_ENB_CNF_MASK),
- &timer->hpet_config);
- devp->hd_flags &= ~(HPET_IE | HPET_PERIODIC);
- devp->hd_task = NULL;
- spin_unlock(&hpet_lock);
- spin_unlock_irq(&hpet_task_lock);
-
- return 0;
-}
-#endif /* 0 */
-
static ctl_table hpet_table[] = {
{
.ctl_name = CTL_UNNUMBERED,
@@ -746,6 +764,7 @@
static struct hpets *last = NULL;
unsigned long period;
unsigned long long temp;
+ u32 remainder;
/*
* hpet_alloc can be called by platform dependent code.
@@ -809,9 +828,13 @@
printk("%s %d", i > 0 ? "," : "", hdp->hd_irq[i]);
printk("\n");
- printk(KERN_INFO "hpet%u: %u %d-bit timers, %Lu Hz\n",
- hpetp->hp_which, hpetp->hp_ntimer,
- cap & HPET_COUNTER_SIZE_MASK ? 64 : 32, hpetp->hp_tick_freq);
+ temp = hpetp->hp_tick_freq;
+ remainder = do_div(temp, 1000000);
+ printk(KERN_INFO
+ "hpet%u: %u comparators, %d-bit %u.%06u MHz counter\n",
+ hpetp->hp_which, hpetp->hp_ntimer,
+ cap & HPET_COUNTER_SIZE_MASK ? 64 : 32,
+ (unsigned) temp, remainder);
mcfg = readq(&hpet->hpet_config);
if ((mcfg & HPET_ENABLE_CNF_MASK) == 0) {
@@ -874,8 +897,6 @@
hdp->hd_address = ioremap(addr.minimum, addr.address_length);
if (hpet_is_known(hdp)) {
- printk(KERN_DEBUG "%s: 0x%lx is busy\n",
- __func__, hdp->hd_phys_address);
iounmap(hdp->hd_address);
return AE_ALREADY_EXISTS;
}
@@ -891,8 +912,6 @@
HPET_RANGE_SIZE);
if (hpet_is_known(hdp)) {
- printk(KERN_DEBUG "%s: 0x%lx is busy\n",
- __func__, hdp->hd_phys_address);
iounmap(hdp->hd_address);
return AE_ALREADY_EXISTS;
}
diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c
index fd64137..ec7aded 100644
--- a/drivers/char/hvc_console.c
+++ b/drivers/char/hvc_console.c
@@ -819,11 +819,11 @@
hvc_driver = drv;
return 0;
-put_tty:
- put_tty_driver(hvc_driver);
stop_thread:
kthread_stop(hvc_task);
hvc_task = NULL;
+put_tty:
+ put_tty_driver(drv);
out:
return err;
}
diff --git a/drivers/char/hvc_xen.c b/drivers/char/hvc_xen.c
index 6b70aa6..538ceea 100644
--- a/drivers/char/hvc_xen.c
+++ b/drivers/char/hvc_xen.c
@@ -108,8 +108,8 @@
{
struct hvc_struct *hp;
- if (!is_running_on_xen() ||
- is_initial_xendomain() ||
+ if (!xen_pv_domain() ||
+ xen_initial_domain() ||
!xen_start_info->console.domU.evtchn)
return -ENODEV;
@@ -142,7 +142,7 @@
static int xen_cons_init(void)
{
- if (!is_running_on_xen())
+ if (!xen_pv_domain())
return 0;
hvc_instantiate(HVC_COOKIE, 0, &hvc_ops);
diff --git a/drivers/char/hw_random/n2-drv.c b/drivers/char/hw_random/n2-drv.c
index 5220f54..8859aea 100644
--- a/drivers/char/hw_random/n2-drv.c
+++ b/drivers/char/hw_random/n2-drv.c
@@ -736,7 +736,7 @@
return 0;
}
-static struct of_device_id n2rng_match[] = {
+static const struct of_device_id n2rng_match[] = {
{
.name = "random-number-generator",
.compatible = "SUNW,n2-rng",
diff --git a/drivers/char/ip2/Makefile b/drivers/char/ip2/Makefile
index 939618f..bc397d9 100644
--- a/drivers/char/ip2/Makefile
+++ b/drivers/char/ip2/Makefile
@@ -4,5 +4,5 @@
obj-$(CONFIG_COMPUTONE) += ip2.o
-ip2-objs := ip2base.o ip2main.o
+ip2-objs := ip2main.o
diff --git a/drivers/char/ip2/i2ellis.c b/drivers/char/ip2/i2ellis.c
index 3601017..29db44d 100644
--- a/drivers/char/ip2/i2ellis.c
+++ b/drivers/char/ip2/i2ellis.c
@@ -69,38 +69,6 @@
//=======================================================
//******************************************************************************
-// Function: iiEllisInit()
-// Parameters: None
-//
-// Returns: Nothing
-//
-// Description:
-//
-// This routine performs any required initialization of the iiEllis subsystem.
-//
-//******************************************************************************
-static void
-iiEllisInit(void)
-{
-}
-
-//******************************************************************************
-// Function: iiEllisCleanup()
-// Parameters: None
-//
-// Returns: Nothing
-//
-// Description:
-//
-// This routine performs any required cleanup of the iiEllis subsystem.
-//
-//******************************************************************************
-static void
-iiEllisCleanup(void)
-{
-}
-
-//******************************************************************************
// Function: iiSetAddress(pB, address, delay)
// Parameters: pB - pointer to the board structure
// address - the purported I/O address of the board
diff --git a/drivers/char/ip2/i2ellis.h b/drivers/char/ip2/i2ellis.h
index c88a64e..fb6df24 100644
--- a/drivers/char/ip2/i2ellis.h
+++ b/drivers/char/ip2/i2ellis.h
@@ -511,7 +511,6 @@
//
// Initialization of a board & structure is in four (five!) parts:
//
-// 0) iiEllisInit() - Initialize iiEllis subsystem.
// 1) iiSetAddress() - Define the board address & delay function for a board.
// 2) iiReset() - Reset the board (provided it exists)
// -- Note you may do this to several boards --
@@ -523,7 +522,6 @@
// loadware. To change loadware, you must begin again with step 2, resetting
// the board again (step 1 not needed).
-static void iiEllisInit(void);
static int iiSetAddress(i2eBordStrPtr, int, delayFunc_t );
static int iiReset(i2eBordStrPtr);
static int iiResetDelay(i2eBordStrPtr);
diff --git a/drivers/char/ip2/ip2base.c b/drivers/char/ip2/ip2base.c
deleted file mode 100644
index 8155e24..0000000
--- a/drivers/char/ip2/ip2base.c
+++ /dev/null
@@ -1,108 +0,0 @@
-// ip2.c
-// This is a dummy module to make the firmware available when needed
-// and allows it to be unloaded when not. Rumor is the __initdata
-// macro doesn't always works on all platforms so we use this kludge.
-// If not compiled as a module it just makes fip_firm avaliable then
-// __initdata should work as advertized
-//
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/wait.h>
-
-#ifndef __init
-#define __init
-#endif
-#ifndef __initfunc
-#define __initfunc(a) a
-#endif
-#ifndef __initdata
-#define __initdata
-#endif
-
-#include "ip2types.h"
-
-int
-ip2_loadmain(int *, int *); // ref into ip2main.c
-
-/* Note: Add compiled in defaults to these arrays, not to the structure
- in ip2.h any longer. That structure WILL get overridden
- by these values, or command line values, or insmod values!!! =mhw=
-*/
-static int io[IP2_MAX_BOARDS]= { 0, 0, 0, 0 };
-static int irq[IP2_MAX_BOARDS] = { -1, -1, -1, -1 };
-
-static int poll_only = 0;
-
-MODULE_AUTHOR("Doug McNash");
-MODULE_DESCRIPTION("Computone IntelliPort Plus Driver");
-module_param_array(irq, int, NULL, 0);
-MODULE_PARM_DESC(irq,"Interrupts for IntelliPort Cards");
-module_param_array(io, int, NULL, 0);
-MODULE_PARM_DESC(io,"I/O ports for IntelliPort Cards");
-module_param(poll_only, bool, 0);
-MODULE_PARM_DESC(poll_only,"Do not use card interrupts");
-
-
-static int __init ip2_init(void)
-{
- if( poll_only ) {
- /* Hard lock the interrupts to zero */
- irq[0] = irq[1] = irq[2] = irq[3] = 0;
- }
-
- return ip2_loadmain(io, irq);
-}
-module_init(ip2_init);
-
-MODULE_LICENSE("GPL");
-
-#ifndef MODULE
-/******************************************************************************
- * ip2_setup:
- * str: kernel command line string
- *
- * Can't autoprobe the boards so user must specify configuration on
- * kernel command line. Sane people build it modular but the others
- * come here.
- *
- * Alternating pairs of io,irq for up to 4 boards.
- * ip2=io0,irq0,io1,irq1,io2,irq2,io3,irq3
- *
- * io=0 => No board
- * io=1 => PCI
- * io=2 => EISA
- * else => ISA I/O address
- *
- * irq=0 or invalid for ISA will revert to polling mode
- *
- * Any value = -1, do not overwrite compiled in value.
- *
- ******************************************************************************/
-static int __init ip2_setup(char *str)
-{
- int ints[10]; /* 4 boards, 2 parameters + 2 */
- int i, j;
-
- str = get_options (str, ARRAY_SIZE(ints), ints);
-
- for( i = 0, j = 1; i < 4; i++ ) {
- if( j > ints[0] ) {
- break;
- }
- if( ints[j] >= 0 ) {
- io[i] = ints[j];
- }
- j++;
- if( j > ints[0] ) {
- break;
- }
- if( ints[j] >= 0 ) {
- irq[i] = ints[j];
- }
- j++;
- }
- return 1;
-}
-__setup("ip2=", ip2_setup);
-#endif /* !MODULE */
diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c
index 689f9dc..6774572 100644
--- a/drivers/char/ip2/ip2main.c
+++ b/drivers/char/ip2/ip2main.c
@@ -150,15 +150,12 @@
/*************/
/* String constants to identify ourselves */
-static char *pcName = "Computone IntelliPort Plus multiport driver";
-static char *pcVersion = "1.2.14";
+static const char pcName[] = "Computone IntelliPort Plus multiport driver";
+static const char pcVersion[] = "1.2.14";
/* String constants for port names */
-static char *pcDriver_name = "ip2";
-static char *pcIpl = "ip2ipl";
-
-// cheezy kludge or genius - you decide?
-int ip2_loadmain(int *, int *);
+static const char pcDriver_name[] = "ip2";
+static const char pcIpl[] = "ip2ipl";
/***********************/
/* Function Prototypes */
@@ -240,8 +237,8 @@
.open = ip2_ipl_open,
};
-static unsigned long irq_counter = 0;
-static unsigned long bh_counter = 0;
+static unsigned long irq_counter;
+static unsigned long bh_counter;
// Use immediate queue to service interrupts
#define USE_IQI
@@ -252,7 +249,6 @@
*/
#define POLL_TIMEOUT (jiffies + 1)
static DEFINE_TIMER(PollTimer, ip2_poll, 0, 0);
-static char TimerOn;
#ifdef IP2DEBUG_TRACE
/* Trace (debug) buffer data */
@@ -268,8 +264,8 @@
/**********/
#if defined(MODULE) && defined(IP2DEBUG_OPEN)
-#define DBG_CNT(s) printk(KERN_DEBUG "(%s): [%x] refc=%d, ttyc=%d, modc=%x -> %s\n", \
- tty->name,(pCh->flags),ip2_tty_driver->refcount, \
+#define DBG_CNT(s) printk(KERN_DEBUG "(%s): [%x] ttyc=%d, modc=%x -> %s\n", \
+ tty->name,(pCh->flags), \
tty->count,/*GET_USE_COUNT(module)*/0,s)
#else
#define DBG_CNT(s)
@@ -287,8 +283,9 @@
MODULE_AUTHOR("Doug McNash");
MODULE_DESCRIPTION("Computone IntelliPort Plus Driver");
+MODULE_LICENSE("GPL");
-static int poll_only = 0;
+static int poll_only;
static int Eisa_irq;
static int Eisa_slot;
@@ -297,34 +294,46 @@
static char rirqs[IP2_MAX_BOARDS];
static int Valid_Irqs[] = { 3, 4, 5, 7, 10, 11, 12, 15, 0};
+/* Note: Add compiled in defaults to these arrays, not to the structure
+ in ip2.h any longer. That structure WILL get overridden
+ by these values, or command line values, or insmod values!!! =mhw=
+*/
+static int io[IP2_MAX_BOARDS];
+static int irq[IP2_MAX_BOARDS] = { -1, -1, -1, -1 };
+
+MODULE_AUTHOR("Doug McNash");
+MODULE_DESCRIPTION("Computone IntelliPort Plus Driver");
+module_param_array(irq, int, NULL, 0);
+MODULE_PARM_DESC(irq, "Interrupts for IntelliPort Cards");
+module_param_array(io, int, NULL, 0);
+MODULE_PARM_DESC(io, "I/O ports for IntelliPort Cards");
+module_param(poll_only, bool, 0);
+MODULE_PARM_DESC(poll_only, "Do not use card interrupts");
+
/* for sysfs class support */
static struct class *ip2_class;
-// Some functions to keep track of what irq's we have
+/* Some functions to keep track of what irqs we have */
-static int
-is_valid_irq(int irq)
+static int __init is_valid_irq(int irq)
{
int *i = Valid_Irqs;
- while ((*i != 0) && (*i != irq)) {
+ while (*i != 0 && *i != irq)
i++;
- }
- return (*i);
+
+ return *i;
}
-static void
-mark_requested_irq( char irq )
+static void __init mark_requested_irq(char irq)
{
rirqs[iindx++] = irq;
}
-#ifdef MODULE
-static int
-clear_requested_irq( char irq )
+static int __exit clear_requested_irq(char irq)
{
int i;
- for ( i = 0; i < IP2_MAX_BOARDS; ++i ) {
+ for (i = 0; i < IP2_MAX_BOARDS; ++i) {
if (rirqs[i] == irq) {
rirqs[i] = 0;
return 1;
@@ -332,17 +341,15 @@
}
return 0;
}
-#endif
-static int
-have_requested_irq( char irq )
+static int have_requested_irq(char irq)
{
- // array init to zeros so 0 irq will not be requested as a side effect
+ /* array init to zeros so 0 irq will not be requested as a side
+ * effect */
int i;
- for ( i = 0; i < IP2_MAX_BOARDS; ++i ) {
+ for (i = 0; i < IP2_MAX_BOARDS; ++i)
if (rirqs[i] == irq)
return 1;
- }
return 0;
}
@@ -361,53 +368,45 @@
/* handle subsequent installations of the driver. All memory allocated by the */
/* driver should be returned since it may be unloaded from memory. */
/******************************************************************************/
-#ifdef MODULE
-void __exit
-ip2_cleanup_module(void)
+static void __exit ip2_cleanup_module(void)
{
int err;
int i;
-#ifdef IP2DEBUG_INIT
- printk (KERN_DEBUG "Unloading %s: version %s\n", pcName, pcVersion );
-#endif
- /* Stop poll timer if we had one. */
- if ( TimerOn ) {
- del_timer ( &PollTimer );
- TimerOn = 0;
- }
+ del_timer_sync(&PollTimer);
/* Reset the boards we have. */
- for( i = 0; i < IP2_MAX_BOARDS; ++i ) {
- if ( i2BoardPtrTable[i] ) {
- iiReset( i2BoardPtrTable[i] );
- }
- }
+ for (i = 0; i < IP2_MAX_BOARDS; i++)
+ if (i2BoardPtrTable[i])
+ iiReset(i2BoardPtrTable[i]);
/* The following is done at most once, if any boards were installed. */
- for ( i = 0; i < IP2_MAX_BOARDS; ++i ) {
- if ( i2BoardPtrTable[i] ) {
- iiResetDelay( i2BoardPtrTable[i] );
+ for (i = 0; i < IP2_MAX_BOARDS; i++) {
+ if (i2BoardPtrTable[i]) {
+ iiResetDelay(i2BoardPtrTable[i]);
/* free io addresses and Tibet */
- release_region( ip2config.addr[i], 8 );
+ release_region(ip2config.addr[i], 8);
device_destroy(ip2_class, MKDEV(IP2_IPL_MAJOR, 4 * i));
- device_destroy(ip2_class, MKDEV(IP2_IPL_MAJOR, 4 * i + 1));
+ device_destroy(ip2_class, MKDEV(IP2_IPL_MAJOR,
+ 4 * i + 1));
}
/* Disable and remove interrupt handler. */
- if ( (ip2config.irq[i] > 0) && have_requested_irq(ip2config.irq[i]) ) {
- free_irq ( ip2config.irq[i], (void *)&pcName);
- clear_requested_irq( ip2config.irq[i]);
+ if (ip2config.irq[i] > 0 &&
+ have_requested_irq(ip2config.irq[i])) {
+ free_irq(ip2config.irq[i], (void *)&pcName);
+ clear_requested_irq(ip2config.irq[i]);
}
}
class_destroy(ip2_class);
- if ( ( err = tty_unregister_driver ( ip2_tty_driver ) ) ) {
- printk(KERN_ERR "IP2: failed to unregister tty driver (%d)\n", err);
- }
+ err = tty_unregister_driver(ip2_tty_driver);
+ if (err)
+ printk(KERN_ERR "IP2: failed to unregister tty driver (%d)\n",
+ err);
put_tty_driver(ip2_tty_driver);
unregister_chrdev(IP2_IPL_MAJOR, pcIpl);
remove_proc_entry("ip2mem", NULL);
- // free memory
+ /* free memory */
for (i = 0; i < IP2_MAX_BOARDS; i++) {
void *pB;
#ifdef CONFIG_PCI
@@ -417,24 +416,18 @@
ip2config.pci_dev[i] = NULL;
}
#endif
- if ((pB = i2BoardPtrTable[i]) != 0 ) {
- kfree ( pB );
+ pB = i2BoardPtrTable[i];
+ if (pB != NULL) {
+ kfree(pB);
i2BoardPtrTable[i] = NULL;
}
- if ((DevTableMem[i]) != NULL ) {
- kfree ( DevTableMem[i] );
+ if (DevTableMem[i] != NULL) {
+ kfree(DevTableMem[i]);
DevTableMem[i] = NULL;
}
}
-
- /* Cleanup the iiEllis subsystem. */
- iiEllisCleanup();
-#ifdef IP2DEBUG_INIT
- printk (KERN_DEBUG "IP2 Unloaded\n" );
-#endif
}
module_exit(ip2_cleanup_module);
-#endif /* MODULE */
static const struct tty_operations ip2_ops = {
.open = ip2_open,
@@ -494,139 +487,168 @@
return fw;
}
-int
-ip2_loadmain(int *iop, int *irqp)
+#ifndef MODULE
+/******************************************************************************
+ * ip2_setup:
+ * str: kernel command line string
+ *
+ * Can't autoprobe the boards so user must specify configuration on
+ * kernel command line. Sane people build it modular but the others
+ * come here.
+ *
+ * Alternating pairs of io,irq for up to 4 boards.
+ * ip2=io0,irq0,io1,irq1,io2,irq2,io3,irq3
+ *
+ * io=0 => No board
+ * io=1 => PCI
+ * io=2 => EISA
+ * else => ISA I/O address
+ *
+ * irq=0 or invalid for ISA will revert to polling mode
+ *
+ * Any value = -1, do not overwrite compiled in value.
+ *
+ ******************************************************************************/
+static int __init ip2_setup(char *str)
+{
+ int j, ints[10]; /* 4 boards, 2 parameters + 2 */
+ unsigned int i;
+
+ str = get_options(str, ARRAY_SIZE(ints), ints);
+
+ for (i = 0, j = 1; i < 4; i++) {
+ if (j > ints[0])
+ break;
+ if (ints[j] >= 0)
+ io[i] = ints[j];
+ j++;
+ if (j > ints[0])
+ break;
+ if (ints[j] >= 0)
+ irq[i] = ints[j];
+ j++;
+ }
+ return 1;
+}
+__setup("ip2=", ip2_setup);
+#endif /* !MODULE */
+
+static int __init ip2_loadmain(void)
{
int i, j, box;
int err = 0;
- static int loaded;
i2eBordStrPtr pB = NULL;
int rc = -1;
- static struct pci_dev *pci_dev_i = NULL;
+ struct pci_dev *pdev = NULL;
const struct firmware *fw = NULL;
- ip2trace (ITRC_NO_PORT, ITRC_INIT, ITRC_ENTER, 0 );
+ if (poll_only) {
+ /* Hard lock the interrupts to zero */
+ irq[0] = irq[1] = irq[2] = irq[3] = poll_only = 0;
+ }
+
+ ip2trace(ITRC_NO_PORT, ITRC_INIT, ITRC_ENTER, 0);
/* process command line arguments to modprobe or
insmod i.e. iop & irqp */
/* irqp and iop should ALWAYS be specified now... But we check
them individually just to be sure, anyways... */
- for ( i = 0; i < IP2_MAX_BOARDS; ++i ) {
- if (iop) {
- ip2config.addr[i] = iop[i];
- if (irqp) {
- if( irqp[i] >= 0 ) {
- ip2config.irq[i] = irqp[i];
- } else {
- ip2config.irq[i] = 0;
- }
- // This is a little bit of a hack. If poll_only=1 on command
- // line back in ip2.c OR all IRQs on all specified boards are
- // explicitly set to 0, then drop to poll only mode and override
- // PCI or EISA interrupts. This superceeds the old hack of
- // triggering if all interrupts were zero (like da default).
- // Still a hack but less prone to random acts of terrorism.
- //
- // What we really should do, now that the IRQ default is set
- // to -1, is to use 0 as a hard coded, do not probe.
- //
- // /\/\|=mhw=|\/\/
- poll_only |= irqp[i];
- }
- }
+ for (i = 0; i < IP2_MAX_BOARDS; ++i) {
+ ip2config.addr[i] = io[i];
+ if (irq[i] >= 0)
+ ip2config.irq[i] = irq[i];
+ else
+ ip2config.irq[i] = 0;
+ /* This is a little bit of a hack. If poll_only=1 on command
+ line back in ip2.c OR all IRQs on all specified boards are
+ explicitly set to 0, then drop to poll only mode and override
+ PCI or EISA interrupts. This superceeds the old hack of
+ triggering if all interrupts were zero (like da default).
+ Still a hack but less prone to random acts of terrorism.
+
+ What we really should do, now that the IRQ default is set
+ to -1, is to use 0 as a hard coded, do not probe.
+
+ /\/\|=mhw=|\/\/
+ */
+ poll_only |= irq[i];
}
poll_only = !poll_only;
/* Announce our presence */
- printk( KERN_INFO "%s version %s\n", pcName, pcVersion );
-
- // ip2 can be unloaded and reloaded for no good reason
- // we can't let that happen here or bad things happen
- // second load hoses board but not system - fixme later
- if (loaded) {
- printk( KERN_INFO "Still loaded\n" );
- return 0;
- }
- loaded++;
+ printk(KERN_INFO "%s version %s\n", pcName, pcVersion);
ip2_tty_driver = alloc_tty_driver(IP2_MAX_PORTS);
if (!ip2_tty_driver)
return -ENOMEM;
- /* Initialise the iiEllis subsystem. */
- iiEllisInit();
-
- /* Initialize arrays. */
- memset( i2BoardPtrTable, 0, sizeof i2BoardPtrTable );
- memset( DevTable, 0, sizeof DevTable );
-
/* Initialise all the boards we can find (up to the maximum). */
- for ( i = 0; i < IP2_MAX_BOARDS; ++i ) {
- switch ( ip2config.addr[i] ) {
+ for (i = 0; i < IP2_MAX_BOARDS; ++i) {
+ switch (ip2config.addr[i]) {
case 0: /* skip this slot even if card is present */
break;
default: /* ISA */
/* ISA address must be specified */
- if ( (ip2config.addr[i] < 0x100) || (ip2config.addr[i] > 0x3f8) ) {
- printk ( KERN_ERR "IP2: Bad ISA board %d address %x\n",
- i, ip2config.addr[i] );
+ if (ip2config.addr[i] < 0x100 ||
+ ip2config.addr[i] > 0x3f8) {
+ printk(KERN_ERR "IP2: Bad ISA board %d "
+ "address %x\n", i,
+ ip2config.addr[i]);
ip2config.addr[i] = 0;
- } else {
- ip2config.type[i] = ISA;
+ break;
+ }
+ ip2config.type[i] = ISA;
- /* Check for valid irq argument, set for polling if invalid */
- if (ip2config.irq[i] && !is_valid_irq(ip2config.irq[i])) {
- printk(KERN_ERR "IP2: Bad IRQ(%d) specified\n",ip2config.irq[i]);
- ip2config.irq[i] = 0;// 0 is polling and is valid in that sense
- }
+ /* Check for valid irq argument, set for polling if
+ * invalid */
+ if (ip2config.irq[i] &&
+ !is_valid_irq(ip2config.irq[i])) {
+ printk(KERN_ERR "IP2: Bad IRQ(%d) specified\n",
+ ip2config.irq[i]);
+ /* 0 is polling and is valid in that sense */
+ ip2config.irq[i] = 0;
}
break;
case PCI:
#ifdef CONFIG_PCI
- {
- int status;
+ {
+ u32 addr;
+ int status;
- pci_dev_i = pci_get_device(PCI_VENDOR_ID_COMPUTONE,
- PCI_DEVICE_ID_COMPUTONE_IP2EX, pci_dev_i);
- if (pci_dev_i != NULL) {
- unsigned int addr;
-
- if (pci_enable_device(pci_dev_i)) {
- printk( KERN_ERR "IP2: can't enable PCI device at %s\n",
- pci_name(pci_dev_i));
- break;
- }
- ip2config.type[i] = PCI;
- ip2config.pci_dev[i] = pci_dev_get(pci_dev_i);
- status =
- pci_read_config_dword(pci_dev_i, PCI_BASE_ADDRESS_1, &addr);
- if ( addr & 1 ) {
- ip2config.addr[i]=(USHORT)(addr&0xfffe);
- } else {
- printk( KERN_ERR "IP2: PCI I/O address error\n");
- }
-
-// If the PCI BIOS assigned it, lets try and use it. If we
-// can't acquire it or it screws up, deal with it then.
-
-// if (!is_valid_irq(pci_irq)) {
-// printk( KERN_ERR "IP2: Bad PCI BIOS IRQ(%d)\n",pci_irq);
-// pci_irq = 0;
-// }
- ip2config.irq[i] = pci_dev_i->irq;
- } else { // ann error
- ip2config.addr[i] = 0;
- printk(KERN_ERR "IP2: PCI board %d not found\n", i);
- }
+ pdev = pci_get_device(PCI_VENDOR_ID_COMPUTONE,
+ PCI_DEVICE_ID_COMPUTONE_IP2EX, pdev);
+ if (pdev == NULL) {
+ ip2config.addr[i] = 0;
+ printk(KERN_ERR "IP2: PCI board %d not "
+ "found\n", i);
+ break;
}
+
+ if (pci_enable_device(pdev)) {
+ dev_err(&pdev->dev, "can't enable device\n");
+ break;
+ }
+ ip2config.type[i] = PCI;
+ ip2config.pci_dev[i] = pci_dev_get(pdev);
+ status = pci_read_config_dword(pdev, PCI_BASE_ADDRESS_1,
+ &addr);
+ if (addr & 1)
+ ip2config.addr[i] = (USHORT)(addr & 0xfffe);
+ else
+ dev_err(&pdev->dev, "I/O address error\n");
+
+ ip2config.irq[i] = pdev->irq;
+ }
#else
- printk( KERN_ERR "IP2: PCI card specified but PCI support not\n");
- printk( KERN_ERR "IP2: configured in this kernel.\n");
- printk( KERN_ERR "IP2: Recompile kernel with CONFIG_PCI defined!\n");
+ printk(KERN_ERR "IP2: PCI card specified but PCI "
+ "support not enabled.\n");
+ printk(KERN_ERR "IP2: Recompile kernel with CONFIG_PCI "
+ "defined!\n");
#endif /* CONFIG_PCI */
break;
case EISA:
- if ( (ip2config.addr[i] = find_eisa_board( Eisa_slot + 1 )) != 0) {
+ ip2config.addr[i] = find_eisa_board(Eisa_slot + 1);
+ if (ip2config.addr[i] != 0) {
/* Eisa_irq set as side effect, boo */
ip2config.type[i] = EISA;
}
@@ -634,31 +656,32 @@
break;
} /* switch */
} /* for */
- if (pci_dev_i)
- pci_dev_put(pci_dev_i);
+ pci_dev_put(pdev);
- for ( i = 0; i < IP2_MAX_BOARDS; ++i ) {
- if ( ip2config.addr[i] ) {
+ for (i = 0; i < IP2_MAX_BOARDS; ++i) {
+ if (ip2config.addr[i]) {
pB = kzalloc(sizeof(i2eBordStr), GFP_KERNEL);
if (pB) {
i2BoardPtrTable[i] = pB;
- iiSetAddress( pB, ip2config.addr[i], ii2DelayTimer );
- iiReset( pB );
- } else {
- printk(KERN_ERR "IP2: board memory allocation error\n");
- }
+ iiSetAddress(pB, ip2config.addr[i],
+ ii2DelayTimer);
+ iiReset(pB);
+ } else
+ printk(KERN_ERR "IP2: board memory allocation "
+ "error\n");
}
}
- for ( i = 0; i < IP2_MAX_BOARDS; ++i ) {
- if ( ( pB = i2BoardPtrTable[i] ) != NULL ) {
- iiResetDelay( pB );
+ for (i = 0; i < IP2_MAX_BOARDS; ++i) {
+ pB = i2BoardPtrTable[i];
+ if (pB != NULL) {
+ iiResetDelay(pB);
break;
}
}
- for ( i = 0; i < IP2_MAX_BOARDS; ++i ) {
+ for (i = 0; i < IP2_MAX_BOARDS; ++i) {
/* We don't want to request the firmware unless we have at
least one board */
- if ( i2BoardPtrTable[i] != NULL ) {
+ if (i2BoardPtrTable[i] != NULL) {
if (!fw)
fw = ip2_request_firmware();
if (!fw)
@@ -669,7 +692,7 @@
if (fw)
release_firmware(fw);
- ip2trace (ITRC_NO_PORT, ITRC_INIT, 2, 0 );
+ ip2trace(ITRC_NO_PORT, ITRC_INIT, 2, 0);
ip2_tty_driver->owner = THIS_MODULE;
ip2_tty_driver->name = "ttyF";
@@ -680,20 +703,23 @@
ip2_tty_driver->subtype = SERIAL_TYPE_NORMAL;
ip2_tty_driver->init_termios = tty_std_termios;
ip2_tty_driver->init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL;
- ip2_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+ ip2_tty_driver->flags = TTY_DRIVER_REAL_RAW |
+ TTY_DRIVER_DYNAMIC_DEV;
tty_set_operations(ip2_tty_driver, &ip2_ops);
- ip2trace (ITRC_NO_PORT, ITRC_INIT, 3, 0 );
+ ip2trace(ITRC_NO_PORT, ITRC_INIT, 3, 0);
- /* Register the tty devices. */
- if ( ( err = tty_register_driver ( ip2_tty_driver ) ) ) {
- printk(KERN_ERR "IP2: failed to register tty driver (%d)\n", err);
+ err = tty_register_driver(ip2_tty_driver);
+ if (err) {
+ printk(KERN_ERR "IP2: failed to register tty driver\n");
put_tty_driver(ip2_tty_driver);
- return -EINVAL;
- } else
- /* Register the IPL driver. */
- if ( ( err = register_chrdev ( IP2_IPL_MAJOR, pcIpl, &ip2_ipl ) ) ) {
- printk(KERN_ERR "IP2: failed to register IPL device (%d)\n", err );
+ return err; /* leaking resources */
+ }
+
+ err = register_chrdev(IP2_IPL_MAJOR, pcIpl, &ip2_ipl);
+ if (err) {
+ printk(KERN_ERR "IP2: failed to register IPL device (%d)\n",
+ err);
} else {
/* create the sysfs class */
ip2_class = class_create(THIS_MODULE, "ip2");
@@ -705,84 +731,86 @@
/* Register the read_procmem thing */
if (!proc_create("ip2mem",0,NULL,&ip2mem_proc_fops)) {
printk(KERN_ERR "IP2: failed to register read_procmem\n");
- } else {
+ return -EIO; /* leaking resources */
+ }
- ip2trace (ITRC_NO_PORT, ITRC_INIT, 4, 0 );
- /* Register the interrupt handler or poll handler, depending upon the
- * specified interrupt.
- */
+ ip2trace(ITRC_NO_PORT, ITRC_INIT, 4, 0);
+ /* Register the interrupt handler or poll handler, depending upon the
+ * specified interrupt.
+ */
- for( i = 0; i < IP2_MAX_BOARDS; ++i ) {
- if ( 0 == ip2config.addr[i] ) {
- continue;
- }
+ for (i = 0; i < IP2_MAX_BOARDS; ++i) {
+ if (ip2config.addr[i] == 0)
+ continue;
- if ( NULL != ( pB = i2BoardPtrTable[i] ) ) {
- device_create_drvdata(ip2_class, NULL,
- MKDEV(IP2_IPL_MAJOR, 4 * i),
- NULL, "ipl%d", i);
- device_create_drvdata(ip2_class, NULL,
- MKDEV(IP2_IPL_MAJOR, 4 * i + 1),
- NULL, "stat%d", i);
+ pB = i2BoardPtrTable[i];
+ if (pB != NULL) {
+ device_create_drvdata(ip2_class, NULL,
+ MKDEV(IP2_IPL_MAJOR, 4 * i),
+ NULL, "ipl%d", i);
+ device_create_drvdata(ip2_class, NULL,
+ MKDEV(IP2_IPL_MAJOR, 4 * i + 1),
+ NULL, "stat%d", i);
- for ( box = 0; box < ABS_MAX_BOXES; ++box )
- {
- for ( j = 0; j < ABS_BIGGEST_BOX; ++j )
- {
- if ( pB->i2eChannelMap[box] & (1 << j) )
- {
- tty_register_device(ip2_tty_driver,
- j + ABS_BIGGEST_BOX *
- (box+i*ABS_MAX_BOXES), NULL);
- }
- }
- }
- }
-
- if (poll_only) {
-// Poll only forces driver to only use polling and
-// to ignore the probed PCI or EISA interrupts.
- ip2config.irq[i] = CIR_POLL;
- }
- if ( ip2config.irq[i] == CIR_POLL ) {
-retry:
- if (!TimerOn) {
- PollTimer.expires = POLL_TIMEOUT;
- add_timer ( &PollTimer );
- TimerOn = 1;
- printk( KERN_INFO "IP2: polling\n");
- }
- } else {
- if (have_requested_irq(ip2config.irq[i]))
- continue;
- rc = request_irq( ip2config.irq[i], ip2_interrupt,
- IP2_SA_FLAGS | (ip2config.type[i] == PCI ? IRQF_SHARED : 0),
- pcName, i2BoardPtrTable[i]);
- if (rc) {
- printk(KERN_ERR "IP2: an request_irq failed: error %d\n",rc);
- ip2config.irq[i] = CIR_POLL;
- printk( KERN_INFO "IP2: Polling %ld/sec.\n",
- (POLL_TIMEOUT - jiffies));
- goto retry;
- }
- mark_requested_irq(ip2config.irq[i]);
- /* Initialise the interrupt handler bottom half (aka slih). */
- }
+ for (box = 0; box < ABS_MAX_BOXES; box++)
+ for (j = 0; j < ABS_BIGGEST_BOX; j++)
+ if (pB->i2eChannelMap[box] & (1 << j))
+ tty_register_device(
+ ip2_tty_driver,
+ j + ABS_BIGGEST_BOX *
+ (box+i*ABS_MAX_BOXES),
+ NULL);
}
- for( i = 0; i < IP2_MAX_BOARDS; ++i ) {
- if ( i2BoardPtrTable[i] ) {
- set_irq( i, ip2config.irq[i] ); /* set and enable board interrupt */
+
+ if (poll_only) {
+ /* Poll only forces driver to only use polling and
+ to ignore the probed PCI or EISA interrupts. */
+ ip2config.irq[i] = CIR_POLL;
+ }
+ if (ip2config.irq[i] == CIR_POLL) {
+retry:
+ if (!timer_pending(&PollTimer)) {
+ mod_timer(&PollTimer, POLL_TIMEOUT);
+ printk(KERN_INFO "IP2: polling\n");
}
+ } else {
+ if (have_requested_irq(ip2config.irq[i]))
+ continue;
+ rc = request_irq(ip2config.irq[i], ip2_interrupt,
+ IP2_SA_FLAGS |
+ (ip2config.type[i] == PCI ? IRQF_SHARED : 0),
+ pcName, i2BoardPtrTable[i]);
+ if (rc) {
+ printk(KERN_ERR "IP2: request_irq failed: "
+ "error %d\n", rc);
+ ip2config.irq[i] = CIR_POLL;
+ printk(KERN_INFO "IP2: Polling %ld/sec.\n",
+ (POLL_TIMEOUT - jiffies));
+ goto retry;
+ }
+ mark_requested_irq(ip2config.irq[i]);
+ /* Initialise the interrupt handler bottom half
+ * (aka slih). */
}
}
- ip2trace (ITRC_NO_PORT, ITRC_INIT, ITRC_RETURN, 0 );
- goto out;
+
+ for (i = 0; i < IP2_MAX_BOARDS; ++i) {
+ if (i2BoardPtrTable[i]) {
+ /* set and enable board interrupt */
+ set_irq(i, ip2config.irq[i]);
+ }
+ }
+
+ ip2trace(ITRC_NO_PORT, ITRC_INIT, ITRC_RETURN, 0);
+
+ return 0;
out_chrdev:
unregister_chrdev(IP2_IPL_MAJOR, "ip2");
-out:
+ /* unregister and put tty here */
return err;
}
+module_init(ip2_loadmain);
/******************************************************************************/
/* Function: ip2_init_board() */
@@ -1199,9 +1227,8 @@
{
int i;
i2eBordStrPtr pB;
- const int irq = 0;
- ip2trace (ITRC_NO_PORT, ITRC_INTR, 99, 1, irq );
+ ip2trace(ITRC_NO_PORT, ITRC_INTR, 99, 1, 0);
/* Service just the boards on the list using this irq */
for( i = 0; i < i2nBoards; ++i ) {
@@ -1210,9 +1237,8 @@
// Only process those boards which match our IRQ.
// IRQ = 0 for polled boards, we won't poll "IRQ" boards
- if ( pB && (pB->i2eUsingIrq == irq) ) {
+ if (pB && pB->i2eUsingIrq == 0)
ip2_irq_work(pB);
- }
}
++irq_counter;
@@ -1250,16 +1276,12 @@
{
ip2trace (ITRC_NO_PORT, ITRC_INTR, 100, 0 );
- TimerOn = 0; // it's the truth but not checked in service
-
// Just polled boards, IRQ = 0 will hit all non-interrupt boards.
// It will NOT poll boards handled by hard interrupts.
// The issue of queued BH interrupts is handled in ip2_interrupt().
ip2_polled_interrupt();
- PollTimer.expires = POLL_TIMEOUT;
- add_timer( &PollTimer );
- TimerOn = 1;
+ mod_timer(&PollTimer, POLL_TIMEOUT);
ip2trace (ITRC_NO_PORT, ITRC_INTR, ITRC_RETURN, 0 );
}
@@ -2871,7 +2893,7 @@
case 13:
switch ( cmd ) {
case 64: /* Driver - ip2stat */
- rc = put_user(ip2_tty_driver->refcount, pIndex++ );
+ rc = put_user(-1, pIndex++ );
rc = put_user(irq_counter, pIndex++ );
rc = put_user(bh_counter, pIndex++ );
break;
diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c
index 8f7cc19..7d30ee1 100644
--- a/drivers/char/isicom.c
+++ b/drivers/char/isicom.c
@@ -421,17 +421,16 @@
if (retries >= 100)
goto unlock;
+ tty = tty_port_tty_get(&port->port);
+ if (tty == NULL)
+ goto put_unlock;
+
for (; count > 0; count--, port++) {
/* port not active or tx disabled to force flow control */
if (!(port->port.flags & ASYNC_INITIALIZED) ||
!(port->status & ISI_TXOK))
continue;
- tty = port->port.tty;
-
- if (tty == NULL)
- continue;
-
txcount = min_t(short, TX_SIZE, port->xmit_cnt);
if (txcount <= 0 || tty->stopped || tty->hw_stopped)
continue;
@@ -489,6 +488,8 @@
tty_wakeup(tty);
}
+put_unlock:
+ tty_kref_put(tty);
unlock:
spin_unlock_irqrestore(&isi_card[card].card_lock, flags);
/* schedule another tx for hopefully in about 10ms */
@@ -547,7 +548,7 @@
return IRQ_HANDLED;
}
- tty = port->port.tty;
+ tty = tty_port_tty_get(&port->port);
if (tty == NULL) {
word_count = byte_count >> 1;
while (byte_count > 1) {
@@ -588,7 +589,7 @@
}
if (port->port.flags & ASYNC_CTS_FLOW) {
- if (port->port.tty->hw_stopped) {
+ if (tty->hw_stopped) {
if (header & ISI_CTS) {
port->port.tty->hw_stopped = 0;
/* start tx ing */
@@ -597,7 +598,7 @@
tty_wakeup(tty);
}
} else if (!(header & ISI_CTS)) {
- port->port.tty->hw_stopped = 1;
+ tty->hw_stopped = 1;
/* stop tx ing */
port->status &= ~(ISI_TXOK | ISI_CTS);
}
@@ -660,24 +661,21 @@
}
outw(0x0000, base+0x04); /* enable interrupts */
spin_unlock(&card->card_lock);
+ tty_kref_put(tty);
return IRQ_HANDLED;
}
-static void isicom_config_port(struct isi_port *port)
+static void isicom_config_port(struct tty_struct *tty)
{
+ struct isi_port *port = tty->driver_data;
struct isi_board *card = port->card;
- struct tty_struct *tty;
unsigned long baud;
unsigned long base = card->base;
u16 channel_setup, channel = port->channel,
shift_count = card->shift_count;
unsigned char flow_ctrl;
- tty = port->port.tty;
-
- if (tty == NULL)
- return;
/* FIXME: Switch to new tty baud API */
baud = C_BAUD(tty);
if (baud & CBAUDEX) {
@@ -690,7 +688,7 @@
/* 1,2,3,4 => 57.6, 115.2, 230, 460 kbps resp. */
if (baud < 1 || baud > 4)
- port->port.tty->termios->c_cflag &= ~CBAUDEX;
+ tty->termios->c_cflag &= ~CBAUDEX;
else
baud += 15;
}
@@ -797,8 +795,9 @@
spin_unlock_irqrestore(&bp->card_lock, flags);
}
-static int isicom_setup_port(struct isi_port *port)
+static int isicom_setup_port(struct tty_struct *tty)
{
+ struct isi_port *port = tty->driver_data;
struct isi_board *card = port->card;
unsigned long flags;
@@ -808,8 +807,7 @@
return -ENOMEM;
spin_lock_irqsave(&card->card_lock, flags);
- if (port->port.tty)
- clear_bit(TTY_IO_ERROR, &port->port.tty->flags);
+ clear_bit(TTY_IO_ERROR, &tty->flags);
if (port->port.count == 1)
card->count++;
@@ -823,7 +821,7 @@
InterruptTheCard(card->base);
}
- isicom_config_port(port);
+ isicom_config_port(tty);
port->port.flags |= ASYNC_INITIALIZED;
spin_unlock_irqrestore(&card->card_lock, flags);
@@ -934,8 +932,8 @@
port->port.count++;
tty->driver_data = port;
- port->port.tty = tty;
- error = isicom_setup_port(port);
+ tty_port_tty_set(&port->port, tty);
+ error = isicom_setup_port(tty);
if (error == 0)
error = block_til_ready(tty, filp, port);
return error;
@@ -955,15 +953,17 @@
struct isi_board *card = port->card;
struct tty_struct *tty;
- tty = port->port.tty;
+ tty = tty_port_tty_get(&port->port);
- if (!(port->port.flags & ASYNC_INITIALIZED))
+ if (!(port->port.flags & ASYNC_INITIALIZED)) {
+ tty_kref_put(tty);
return;
+ }
tty_port_free_xmit_buf(&port->port);
port->port.flags &= ~ASYNC_INITIALIZED;
/* 3rd October 2000 : Vinayak P Risbud */
- port->port.tty = NULL;
+ tty_port_tty_set(&port->port, NULL);
/*Fix done by Anil .S on 30-04-2001
remote login through isi port has dtr toggle problem
@@ -1243,9 +1243,10 @@
return 0;
}
-static int isicom_set_serial_info(struct isi_port *port,
- struct serial_struct __user *info)
+static int isicom_set_serial_info(struct tty_struct *tty,
+ struct serial_struct __user *info)
{
+ struct isi_port *port = tty->driver_data;
struct serial_struct newinfo;
int reconfig_port;
@@ -1276,7 +1277,7 @@
if (reconfig_port) {
unsigned long flags;
spin_lock_irqsave(&port->card->card_lock, flags);
- isicom_config_port(port);
+ isicom_config_port(tty);
spin_unlock_irqrestore(&port->card->card_lock, flags);
}
unlock_kernel();
@@ -1318,7 +1319,7 @@
return isicom_get_serial_info(port, argp);
case TIOCSSERIAL:
- return isicom_set_serial_info(port, argp);
+ return isicom_set_serial_info(tty, argp);
default:
return -ENOIOCTLCMD;
@@ -1341,7 +1342,7 @@
return;
spin_lock_irqsave(&port->card->card_lock, flags);
- isicom_config_port(port);
+ isicom_config_port(tty);
spin_unlock_irqrestore(&port->card->card_lock, flags);
if ((old_termios->c_cflag & CRTSCTS) &&
@@ -1419,7 +1420,7 @@
port->port.count = 0;
port->port.flags &= ~ASYNC_NORMAL_ACTIVE;
- port->port.tty = NULL;
+ tty_port_tty_set(&port->port, NULL);
wake_up_interruptible(&port->port.open_wait);
}
diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c
index 843a2af..505d7a1 100644
--- a/drivers/char/istallion.c
+++ b/drivers/char/istallion.c
@@ -623,24 +623,25 @@
static void stli_brdpoll(struct stlibrd *brdp, cdkhdr_t __iomem *hdrp);
static void stli_poll(unsigned long arg);
static int stli_hostcmd(struct stlibrd *brdp, struct stliport *portp);
-static int stli_initopen(struct stlibrd *brdp, struct stliport *portp);
+static int stli_initopen(struct tty_struct *tty, struct stlibrd *brdp, struct stliport *portp);
static int stli_rawopen(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait);
static int stli_rawclose(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait);
-static int stli_waitcarrier(struct stlibrd *brdp, struct stliport *portp, struct file *filp);
-static int stli_setport(struct stliport *portp);
+static int stli_waitcarrier(struct tty_struct *tty, struct stlibrd *brdp,
+ struct stliport *portp, struct file *filp);
+static int stli_setport(struct tty_struct *tty);
static int stli_cmdwait(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback);
static void stli_sendcmd(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback);
static void __stli_sendcmd(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback);
static void stli_dodelaycmd(struct stliport *portp, cdkctrl_t __iomem *cp);
-static void stli_mkasyport(struct stliport *portp, asyport_t *pp, struct ktermios *tiosp);
+static void stli_mkasyport(struct tty_struct *tty, struct stliport *portp, asyport_t *pp, struct ktermios *tiosp);
static void stli_mkasysigs(asysigs_t *sp, int dtr, int rts);
static long stli_mktiocm(unsigned long sigvalue);
static void stli_read(struct stlibrd *brdp, struct stliport *portp);
static int stli_getserial(struct stliport *portp, struct serial_struct __user *sp);
-static int stli_setserial(struct stliport *portp, struct serial_struct __user *sp);
+static int stli_setserial(struct tty_struct *tty, struct serial_struct __user *sp);
static int stli_getbrdstats(combrd_t __user *bp);
-static int stli_getportstats(struct stliport *portp, comstats_t __user *cp);
-static int stli_portcmdstats(struct stliport *portp);
+static int stli_getportstats(struct tty_struct *tty, struct stliport *portp, comstats_t __user *cp);
+static int stli_portcmdstats(struct tty_struct *tty, struct stliport *portp);
static int stli_clrportstats(struct stliport *portp, comstats_t __user *cp);
static int stli_getportstruct(struct stliport __user *arg);
static int stli_getbrdstruct(struct stlibrd __user *arg);
@@ -731,12 +732,16 @@
{
struct stliport *portp;
unsigned int j;
+ struct tty_struct *tty;
for (j = 0; j < STL_MAXPORTS; j++) {
portp = brdp->ports[j];
if (portp != NULL) {
- if (portp->port.tty != NULL)
- tty_hangup(portp->port.tty);
+ tty = tty_port_tty_get(&portp->port);
+ if (tty != NULL) {
+ tty_hangup(tty);
+ tty_kref_put(tty);
+ }
kfree(portp);
}
}
@@ -824,7 +829,7 @@
* requires several commands to the board we will need to wait for any
* other open that is already initializing the port.
*/
- portp->port.tty = tty;
+ tty_port_tty_set(&portp->port, tty);
tty->driver_data = portp;
portp->port.count++;
@@ -835,7 +840,7 @@
if ((portp->port.flags & ASYNC_INITIALIZED) == 0) {
set_bit(ST_INITIALIZING, &portp->state);
- if ((rc = stli_initopen(brdp, portp)) >= 0) {
+ if ((rc = stli_initopen(tty, brdp, portp)) >= 0) {
portp->port.flags |= ASYNC_INITIALIZED;
clear_bit(TTY_IO_ERROR, &tty->flags);
}
@@ -864,7 +869,7 @@
* then also we might have to wait for carrier.
*/
if (!(filp->f_flags & O_NONBLOCK)) {
- if ((rc = stli_waitcarrier(brdp, portp, filp)) != 0)
+ if ((rc = stli_waitcarrier(tty, brdp, portp, filp)) != 0)
return rc;
}
portp->port.flags |= ASYNC_NORMAL_ACTIVE;
@@ -930,7 +935,7 @@
stli_flushbuffer(tty);
tty->closing = 0;
- portp->port.tty = NULL;
+ tty_port_tty_set(&portp->port, NULL);
if (portp->openwaitcnt) {
if (portp->close_delay)
@@ -952,9 +957,9 @@
* this still all happens pretty quickly.
*/
-static int stli_initopen(struct stlibrd *brdp, struct stliport *portp)
+static int stli_initopen(struct tty_struct *tty,
+ struct stlibrd *brdp, struct stliport *portp)
{
- struct tty_struct *tty;
asynotify_t nt;
asyport_t aport;
int rc;
@@ -969,10 +974,7 @@
sizeof(asynotify_t), 0)) < 0)
return rc;
- tty = portp->port.tty;
- if (tty == NULL)
- return -ENODEV;
- stli_mkasyport(portp, &aport, tty->termios);
+ stli_mkasyport(tty, portp, &aport, tty->termios);
if ((rc = stli_cmdwait(brdp, portp, A_SETPORT, &aport,
sizeof(asyport_t), 0)) < 0)
return rc;
@@ -1161,22 +1163,21 @@
* waiting for the command to complete - so must have user context.
*/
-static int stli_setport(struct stliport *portp)
+static int stli_setport(struct tty_struct *tty)
{
+ struct stliport *portp = tty->driver_data;
struct stlibrd *brdp;
asyport_t aport;
if (portp == NULL)
return -ENODEV;
- if (portp->port.tty == NULL)
- return -ENODEV;
if (portp->brdnr >= stli_nrbrds)
return -ENODEV;
brdp = stli_brds[portp->brdnr];
if (brdp == NULL)
return -ENODEV;
- stli_mkasyport(portp, &aport, portp->port.tty->termios);
+ stli_mkasyport(tty, portp, &aport, tty->termios);
return(stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0));
}
@@ -1187,7 +1188,8 @@
* maybe because if we are clocal then we don't need to wait...
*/
-static int stli_waitcarrier(struct stlibrd *brdp, struct stliport *portp, struct file *filp)
+static int stli_waitcarrier(struct tty_struct *tty, struct stlibrd *brdp,
+ struct stliport *portp, struct file *filp)
{
unsigned long flags;
int rc, doclocal;
@@ -1195,7 +1197,7 @@
rc = 0;
doclocal = 0;
- if (portp->port.tty->termios->c_cflag & CLOCAL)
+ if (tty->termios->c_cflag & CLOCAL)
doclocal++;
spin_lock_irqsave(&stli_lock, flags);
@@ -1373,8 +1375,6 @@
stli_txcookrealsize = 0;
stli_txcooktty = NULL;
- if (tty == NULL)
- return;
if (cooktty == NULL)
return;
if (tty != cooktty)
@@ -1572,10 +1572,11 @@
* just quietly ignore any requests to change irq, etc.
*/
-static int stli_setserial(struct stliport *portp, struct serial_struct __user *sp)
+static int stli_setserial(struct tty_struct *tty, struct serial_struct __user *sp)
{
struct serial_struct sio;
int rc;
+ struct stliport *portp = tty->driver_data;
if (copy_from_user(&sio, sp, sizeof(struct serial_struct)))
return -EFAULT;
@@ -1594,7 +1595,7 @@
portp->closing_wait = sio.closing_wait;
portp->custom_divisor = sio.custom_divisor;
- if ((rc = stli_setport(portp)) < 0)
+ if ((rc = stli_setport(tty)) < 0)
return rc;
return 0;
}
@@ -1685,17 +1686,17 @@
rc = stli_getserial(portp, argp);
break;
case TIOCSSERIAL:
- rc = stli_setserial(portp, argp);
+ rc = stli_setserial(tty, argp);
break;
case STL_GETPFLAG:
rc = put_user(portp->pflag, (unsigned __user *)argp);
break;
case STL_SETPFLAG:
if ((rc = get_user(portp->pflag, (unsigned __user *)argp)) == 0)
- stli_setport(portp);
+ stli_setport(tty);
break;
case COM_GETPORTSTATS:
- rc = stli_getportstats(portp, argp);
+ rc = stli_getportstats(tty, portp, argp);
break;
case COM_CLRPORTSTATS:
rc = stli_clrportstats(portp, argp);
@@ -1729,8 +1730,6 @@
struct ktermios *tiosp;
asyport_t aport;
- if (tty == NULL)
- return;
portp = tty->driver_data;
if (portp == NULL)
return;
@@ -1742,7 +1741,7 @@
tiosp = tty->termios;
- stli_mkasyport(portp, &aport, tiosp);
+ stli_mkasyport(tty, portp, &aport, tiosp);
stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0);
stli_mkasysigs(&portp->asig, ((tiosp->c_cflag & CBAUD) ? 1 : 0), -1);
stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig,
@@ -1854,7 +1853,7 @@
clear_bit(ST_TXBUSY, &portp->state);
clear_bit(ST_RXSTOP, &portp->state);
set_bit(TTY_IO_ERROR, &tty->flags);
- portp->port.tty = NULL;
+ tty_port_tty_set(&portp->port, NULL);
portp->port.flags &= ~ASYNC_NORMAL_ACTIVE;
portp->port.count = 0;
spin_unlock_irqrestore(&stli_lock, flags);
@@ -1935,8 +1934,6 @@
struct stliport *portp;
unsigned long tend;
- if (tty == NULL)
- return;
portp = tty->driver_data;
if (portp == NULL)
return;
@@ -1998,7 +1995,7 @@
char *sp, *uart;
int rc, cnt;
- rc = stli_portcmdstats(portp);
+ rc = stli_portcmdstats(NULL, portp);
uart = "UNKNOWN";
if (brdp->state & BST_STARTED) {
@@ -2188,7 +2185,7 @@
if (test_bit(ST_RXSTOP, &portp->state))
return;
- tty = portp->port.tty;
+ tty = tty_port_tty_get(&portp->port);
if (tty == NULL)
return;
@@ -2230,6 +2227,7 @@
set_bit(ST_RXING, &portp->state);
tty_schedule_flip(tty);
+ tty_kref_put(tty);
}
/*****************************************************************************/
@@ -2362,7 +2360,7 @@
if (ap->notify) {
nt = ap->changed;
ap->notify = 0;
- tty = portp->port.tty;
+ tty = tty_port_tty_get(&portp->port);
if (nt.signal & SG_DCD) {
oldsigs = portp->sigs;
@@ -2399,6 +2397,7 @@
tty_schedule_flip(tty);
}
}
+ tty_kref_put(tty);
if (nt.data & DT_RXBUSY) {
donerx++;
@@ -2535,14 +2534,15 @@
* the slave.
*/
-static void stli_mkasyport(struct stliport *portp, asyport_t *pp, struct ktermios *tiosp)
+static void stli_mkasyport(struct tty_struct *tty, struct stliport *portp,
+ asyport_t *pp, struct ktermios *tiosp)
{
memset(pp, 0, sizeof(asyport_t));
/*
* Start of by setting the baud, char size, parity and stop bit info.
*/
- pp->baudout = tty_get_baud_rate(portp->port.tty);
+ pp->baudout = tty_get_baud_rate(tty);
if ((tiosp->c_cflag & CBAUD) == B38400) {
if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
pp->baudout = 57600;
@@ -2695,7 +2695,7 @@
printk("STALLION: failed to allocate port structure\n");
continue;
}
-
+ tty_port_init(&portp->port);
portp->magic = STLI_PORTMAGIC;
portp->portnr = i;
portp->brdnr = brdp->brdnr;
@@ -4220,7 +4220,7 @@
* what port to get stats for (used through board control device).
*/
-static int stli_portcmdstats(struct stliport *portp)
+static int stli_portcmdstats(struct tty_struct *tty, struct stliport *portp)
{
unsigned long flags;
struct stlibrd *brdp;
@@ -4249,15 +4249,15 @@
stli_comstats.flags = portp->port.flags;
spin_lock_irqsave(&brd_lock, flags);
- if (portp->port.tty != NULL) {
- if (portp->port.tty->driver_data == portp) {
- stli_comstats.ttystate = portp->port.tty->flags;
+ if (tty != NULL) {
+ if (portp->port.tty == tty) {
+ stli_comstats.ttystate = tty->flags;
stli_comstats.rxbuffered = -1;
- if (portp->port.tty->termios != NULL) {
- stli_comstats.cflags = portp->port.tty->termios->c_cflag;
- stli_comstats.iflags = portp->port.tty->termios->c_iflag;
- stli_comstats.oflags = portp->port.tty->termios->c_oflag;
- stli_comstats.lflags = portp->port.tty->termios->c_lflag;
+ if (tty->termios != NULL) {
+ stli_comstats.cflags = tty->termios->c_cflag;
+ stli_comstats.iflags = tty->termios->c_iflag;
+ stli_comstats.oflags = tty->termios->c_oflag;
+ stli_comstats.lflags = tty->termios->c_lflag;
}
}
}
@@ -4294,7 +4294,8 @@
* what port to get stats for (used through board control device).
*/
-static int stli_getportstats(struct stliport *portp, comstats_t __user *cp)
+static int stli_getportstats(struct tty_struct *tty, struct stliport *portp,
+ comstats_t __user *cp)
{
struct stlibrd *brdp;
int rc;
@@ -4312,7 +4313,7 @@
if (!brdp)
return -ENODEV;
- if ((rc = stli_portcmdstats(portp)) < 0)
+ if ((rc = stli_portcmdstats(tty, portp)) < 0)
return rc;
return copy_to_user(cp, &stli_comstats, sizeof(comstats_t)) ?
@@ -4427,7 +4428,7 @@
switch (cmd) {
case COM_GETPORTSTATS:
- rc = stli_getportstats(NULL, argp);
+ rc = stli_getportstats(NULL, NULL, argp);
done++;
break;
case COM_CLRPORTSTATS:
diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c
index d3d7864e..5df4003 100644
--- a/drivers/char/moxa.c
+++ b/drivers/char/moxa.c
@@ -205,7 +205,7 @@
static void moxa_poll(unsigned long);
static void moxa_set_tty_param(struct tty_struct *, struct ktermios *);
static void moxa_setup_empty_event(struct tty_struct *);
-static void moxa_shut_down(struct moxa_port *);
+static void moxa_shut_down(struct tty_struct *);
/*
* moxa board interface functions:
*/
@@ -217,7 +217,7 @@
static void MoxaPortFlowCtrl(struct moxa_port *, int, int, int, int, int);
static int MoxaPortLineStatus(struct moxa_port *);
static void MoxaPortFlushData(struct moxa_port *, int);
-static int MoxaPortWriteData(struct moxa_port *, const unsigned char *, int);
+static int MoxaPortWriteData(struct tty_struct *, const unsigned char *, int);
static int MoxaPortReadData(struct moxa_port *);
static int MoxaPortTxQueue(struct moxa_port *);
static int MoxaPortRxQueue(struct moxa_port *);
@@ -332,6 +332,7 @@
for (i = 0; i < MAX_BOARDS; i++) {
p = moxa_boards[i].ports;
for (j = 0; j < MAX_PORTS_PER_BOARD; j++, p++, argm++) {
+ struct tty_struct *ttyp;
memset(&tmp, 0, sizeof(tmp));
if (!moxa_boards[i].ready)
goto copy;
@@ -344,10 +345,12 @@
if (status & 4)
tmp.dcd = 1;
- if (!p->port.tty || !p->port.tty->termios)
+ ttyp = tty_port_tty_get(&p->port);
+ if (!ttyp || !ttyp->termios)
tmp.cflag = p->cflag;
else
- tmp.cflag = p->port.tty->termios->c_cflag;
+ tmp.cflag = ttyp->termios->c_cflag;
+ tty_kref_put(tty);
copy:
if (copy_to_user(argm, &tmp, sizeof(tmp))) {
mutex_unlock(&moxa_openlock);
@@ -880,8 +883,14 @@
/* pci hot-un-plug support */
for (a = 0; a < brd->numPorts; a++)
- if (brd->ports[a].port.flags & ASYNC_INITIALIZED)
- tty_hangup(brd->ports[a].port.tty);
+ if (brd->ports[a].port.flags & ASYNC_INITIALIZED) {
+ struct tty_struct *tty = tty_port_tty_get(
+ &brd->ports[a].port);
+ if (tty) {
+ tty_hangup(tty);
+ tty_kref_put(tty);
+ }
+ }
while (1) {
opened = 0;
for (a = 0; a < brd->numPorts; a++)
@@ -1096,13 +1105,14 @@
module_init(moxa_init);
module_exit(moxa_exit);
-static void moxa_close_port(struct moxa_port *ch)
+static void moxa_close_port(struct tty_struct *tty)
{
- moxa_shut_down(ch);
+ struct moxa_port *ch = tty->driver_data;
+ moxa_shut_down(tty);
MoxaPortFlushData(ch, 2);
ch->port.flags &= ~ASYNC_NORMAL_ACTIVE;
- ch->port.tty->driver_data = NULL;
- ch->port.tty = NULL;
+ tty->driver_data = NULL;
+ tty_port_tty_set(&ch->port, NULL);
}
static int moxa_block_till_ready(struct tty_struct *tty, struct file *filp,
@@ -1161,7 +1171,7 @@
ch = &brd->ports[port % MAX_PORTS_PER_BOARD];
ch->port.count++;
tty->driver_data = ch;
- ch->port.tty = tty;
+ tty_port_tty_set(&ch->port, tty);
if (!(ch->port.flags & ASYNC_INITIALIZED)) {
ch->statusflags = 0;
moxa_set_tty_param(tty, tty->termios);
@@ -1179,7 +1189,7 @@
if (retval) {
if (ch->port.count) /* 0 means already hung up... */
if (--ch->port.count == 0)
- moxa_close_port(ch);
+ moxa_close_port(tty);
} else
ch->port.flags |= ASYNC_NORMAL_ACTIVE;
mutex_unlock(&moxa_openlock);
@@ -1219,7 +1229,7 @@
tty_wait_until_sent(tty, 30 * HZ); /* 30 seconds timeout */
}
- moxa_close_port(ch);
+ moxa_close_port(tty);
unlock:
mutex_unlock(&moxa_openlock);
}
@@ -1234,7 +1244,7 @@
return 0;
spin_lock_bh(&moxa_lock);
- len = MoxaPortWriteData(ch, buf, count);
+ len = MoxaPortWriteData(tty, buf, count);
spin_unlock_bh(&moxa_lock);
ch->statusflags |= LOWWAIT;
@@ -1409,7 +1419,7 @@
return;
}
ch->port.count = 0;
- moxa_close_port(ch);
+ moxa_close_port(tty);
mutex_unlock(&moxa_openlock);
wake_up_interruptible(&ch->port.open_wait);
@@ -1417,11 +1427,14 @@
static void moxa_new_dcdstate(struct moxa_port *p, u8 dcd)
{
+ struct tty_struct *tty;
dcd = !!dcd;
- if (dcd != p->DCDState && p->port.tty && C_CLOCAL(p->port.tty)) {
- if (!dcd)
- tty_hangup(p->port.tty);
+ if (dcd != p->DCDState) {
+ tty = tty_port_tty_get(&p->port);
+ if (tty && C_CLOCAL(tty) && !dcd)
+ tty_hangup(tty);
+ tty_kref_put(tty);
}
p->DCDState = dcd;
}
@@ -1429,7 +1442,7 @@
static int moxa_poll_port(struct moxa_port *p, unsigned int handle,
u16 __iomem *ip)
{
- struct tty_struct *tty = p->port.tty;
+ struct tty_struct *tty = tty_port_tty_get(&p->port);
void __iomem *ofsAddr;
unsigned int inited = p->port.flags & ASYNC_INITIALIZED;
u16 intr;
@@ -1476,6 +1489,7 @@
tty_insert_flip_char(tty, 0, TTY_BREAK);
tty_schedule_flip(tty);
}
+ tty_kref_put(tty);
if (intr & IntrLine)
moxa_new_dcdstate(p, readb(ofsAddr + FlagStat) & DCD_state);
@@ -1560,9 +1574,9 @@
spin_unlock_bh(&moxa_lock);
}
-static void moxa_shut_down(struct moxa_port *ch)
+static void moxa_shut_down(struct tty_struct *tty)
{
- struct tty_struct *tp = ch->port.tty;
+ struct moxa_port *ch = tty->driver_data;
if (!(ch->port.flags & ASYNC_INITIALIZED))
return;
@@ -1572,7 +1586,7 @@
/*
* If we're a modem control device and HUPCL is on, drop RTS & DTR.
*/
- if (C_HUPCL(tp))
+ if (C_HUPCL(tty))
MoxaPortLineCtrl(ch, 0, 0);
spin_lock_bh(&moxa_lock);
@@ -1953,9 +1967,10 @@
return val;
}
-static int MoxaPortWriteData(struct moxa_port *port,
+static int MoxaPortWriteData(struct tty_struct *tty,
const unsigned char *buffer, int len)
{
+ struct moxa_port *port = tty->driver_data;
void __iomem *baseAddr, *ofsAddr, *ofs;
unsigned int c, total;
u16 head, tail, tx_mask, spage, epage;
diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c
index b638403..8beef50 100644
--- a/drivers/char/mxser.c
+++ b/drivers/char/mxser.c
@@ -610,15 +610,13 @@
return 0;
}
-static int mxser_set_baud(struct mxser_port *info, long newspd)
+static int mxser_set_baud(struct tty_struct *tty, long newspd)
{
+ struct mxser_port *info = tty->driver_data;
int quot = 0, baud;
unsigned char cval;
- if (!info->port.tty || !info->port.tty->termios)
- return -1;
-
- if (!(info->ioaddr))
+ if (!info->ioaddr)
return -1;
if (newspd > info->max_baud)
@@ -626,13 +624,13 @@
if (newspd == 134) {
quot = 2 * info->baud_base / 269;
- tty_encode_baud_rate(info->port.tty, 134, 134);
+ tty_encode_baud_rate(tty, 134, 134);
} else if (newspd) {
quot = info->baud_base / newspd;
if (quot == 0)
quot = 1;
baud = info->baud_base/quot;
- tty_encode_baud_rate(info->port.tty, baud, baud);
+ tty_encode_baud_rate(tty, baud, baud);
} else {
quot = 0;
}
@@ -658,7 +656,7 @@
outb(cval, info->ioaddr + UART_LCR); /* reset DLAB */
#ifdef BOTHER
- if (C_BAUD(info->port.tty) == BOTHER) {
+ if (C_BAUD(tty) == BOTHER) {
quot = info->baud_base % newspd;
quot *= 8;
if (quot % newspd > newspd / 2) {
@@ -679,21 +677,20 @@
* This routine is called to set the UART divisor registers to match
* the specified baud rate for a serial port.
*/
-static int mxser_change_speed(struct mxser_port *info,
- struct ktermios *old_termios)
+static int mxser_change_speed(struct tty_struct *tty,
+ struct ktermios *old_termios)
{
+ struct mxser_port *info = tty->driver_data;
unsigned cflag, cval, fcr;
int ret = 0;
unsigned char status;
- if (!info->port.tty || !info->port.tty->termios)
- return ret;
- cflag = info->port.tty->termios->c_cflag;
- if (!(info->ioaddr))
+ cflag = tty->termios->c_cflag;
+ if (!info->ioaddr)
return ret;
- if (mxser_set_baud_method[info->port.tty->index] == 0)
- mxser_set_baud(info, tty_get_baud_rate(info->port.tty));
+ if (mxser_set_baud_method[tty->index] == 0)
+ mxser_set_baud(tty, tty_get_baud_rate(tty));
/* byte size and parity */
switch (cflag & CSIZE) {
@@ -762,9 +759,9 @@
info->MCR |= UART_MCR_AFE;
} else {
status = inb(info->ioaddr + UART_MSR);
- if (info->port.tty->hw_stopped) {
+ if (tty->hw_stopped) {
if (status & UART_MSR_CTS) {
- info->port.tty->hw_stopped = 0;
+ tty->hw_stopped = 0;
if (info->type != PORT_16550A &&
!info->board->chip_flag) {
outb(info->IER & ~UART_IER_THRI,
@@ -774,11 +771,11 @@
outb(info->IER, info->ioaddr +
UART_IER);
}
- tty_wakeup(info->port.tty);
+ tty_wakeup(tty);
}
} else {
if (!(status & UART_MSR_CTS)) {
- info->port.tty->hw_stopped = 1;
+ tty->hw_stopped = 1;
if ((info->type != PORT_16550A) &&
(!info->board->chip_flag)) {
info->IER &= ~UART_IER_THRI;
@@ -804,21 +801,21 @@
* Set up parity check flag
*/
info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
- if (I_INPCK(info->port.tty))
+ if (I_INPCK(tty))
info->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
- if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty))
+ if (I_BRKINT(tty) || I_PARMRK(tty))
info->read_status_mask |= UART_LSR_BI;
info->ignore_status_mask = 0;
- if (I_IGNBRK(info->port.tty)) {
+ if (I_IGNBRK(tty)) {
info->ignore_status_mask |= UART_LSR_BI;
info->read_status_mask |= UART_LSR_BI;
/*
* If we're ignore parity and break indicators, ignore
* overruns too. (For real raw support).
*/
- if (I_IGNPAR(info->port.tty)) {
+ if (I_IGNPAR(tty)) {
info->ignore_status_mask |=
UART_LSR_OE |
UART_LSR_PE |
@@ -830,16 +827,16 @@
}
}
if (info->board->chip_flag) {
- mxser_set_must_xon1_value(info->ioaddr, START_CHAR(info->port.tty));
- mxser_set_must_xoff1_value(info->ioaddr, STOP_CHAR(info->port.tty));
- if (I_IXON(info->port.tty)) {
+ mxser_set_must_xon1_value(info->ioaddr, START_CHAR(tty));
+ mxser_set_must_xoff1_value(info->ioaddr, STOP_CHAR(tty));
+ if (I_IXON(tty)) {
mxser_enable_must_rx_software_flow_control(
info->ioaddr);
} else {
mxser_disable_must_rx_software_flow_control(
info->ioaddr);
}
- if (I_IXOFF(info->port.tty)) {
+ if (I_IXOFF(tty)) {
mxser_enable_must_tx_software_flow_control(
info->ioaddr);
} else {
@@ -855,7 +852,8 @@
return ret;
}
-static void mxser_check_modem_status(struct mxser_port *port, int status)
+static void mxser_check_modem_status(struct tty_struct *tty,
+ struct mxser_port *port, int status)
{
/* update input line counters */
if (status & UART_MSR_TERI)
@@ -874,10 +872,11 @@
wake_up_interruptible(&port->port.open_wait);
}
+ tty = tty_port_tty_get(&port->port);
if (port->port.flags & ASYNC_CTS_FLOW) {
- if (port->port.tty->hw_stopped) {
+ if (tty->hw_stopped) {
if (status & UART_MSR_CTS) {
- port->port.tty->hw_stopped = 0;
+ tty->hw_stopped = 0;
if ((port->type != PORT_16550A) &&
(!port->board->chip_flag)) {
@@ -887,11 +886,11 @@
outb(port->IER, port->ioaddr +
UART_IER);
}
- tty_wakeup(port->port.tty);
+ tty_wakeup(tty);
}
} else {
if (!(status & UART_MSR_CTS)) {
- port->port.tty->hw_stopped = 1;
+ tty->hw_stopped = 1;
if (port->type != PORT_16550A &&
!port->board->chip_flag) {
port->IER &= ~UART_IER_THRI;
@@ -903,8 +902,9 @@
}
}
-static int mxser_startup(struct mxser_port *info)
+static int mxser_startup(struct tty_struct *tty)
{
+ struct mxser_port *info = tty->driver_data;
unsigned long page;
unsigned long flags;
@@ -921,8 +921,7 @@
}
if (!info->ioaddr || !info->type) {
- if (info->port.tty)
- set_bit(TTY_IO_ERROR, &info->port.tty->flags);
+ set_bit(TTY_IO_ERROR, &tty->flags);
free_page(page);
spin_unlock_irqrestore(&info->slock, flags);
return 0;
@@ -952,8 +951,8 @@
if (inb(info->ioaddr + UART_LSR) == 0xff) {
spin_unlock_irqrestore(&info->slock, flags);
if (capable(CAP_SYS_ADMIN)) {
- if (info->port.tty)
- set_bit(TTY_IO_ERROR, &info->port.tty->flags);
+ if (tty)
+ set_bit(TTY_IO_ERROR, &tty->flags);
return 0;
} else
return -ENODEV;
@@ -991,14 +990,13 @@
(void) inb(info->ioaddr + UART_IIR);
(void) inb(info->ioaddr + UART_MSR);
- if (info->port.tty)
- clear_bit(TTY_IO_ERROR, &info->port.tty->flags);
+ clear_bit(TTY_IO_ERROR, &tty->flags);
info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
/*
* and set the speed of the serial port
*/
- mxser_change_speed(info, NULL);
+ mxser_change_speed(tty, NULL);
info->port.flags |= ASYNC_INITIALIZED;
spin_unlock_irqrestore(&info->slock, flags);
@@ -1009,8 +1007,9 @@
* This routine will shutdown a serial port; interrupts maybe disabled, and
* DTR is dropped if the hangup on close termio flag is on.
*/
-static void mxser_shutdown(struct mxser_port *info)
+static void mxser_shutdown(struct tty_struct *tty)
{
+ struct mxser_port *info = tty->driver_data;
unsigned long flags;
if (!(info->port.flags & ASYNC_INITIALIZED))
@@ -1035,7 +1034,7 @@
info->IER = 0;
outb(0x00, info->ioaddr + UART_IER);
- if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL))
+ if (tty->termios->c_cflag & HUPCL)
info->MCR &= ~(UART_MCR_DTR | UART_MCR_RTS);
outb(info->MCR, info->ioaddr + UART_MCR);
@@ -1051,8 +1050,7 @@
/* read data port to reset things */
(void) inb(info->ioaddr + UART_RX);
- if (info->port.tty)
- set_bit(TTY_IO_ERROR, &info->port.tty->flags);
+ set_bit(TTY_IO_ERROR, &tty->flags);
info->port.flags &= ~ASYNC_INITIALIZED;
@@ -1084,14 +1082,14 @@
return -ENODEV;
tty->driver_data = info;
- info->port.tty = tty;
+ tty_port_tty_set(&info->port, tty);
/*
* Start up serial port
*/
spin_lock_irqsave(&info->slock, flags);
info->port.count++;
spin_unlock_irqrestore(&info->slock, flags);
- retval = mxser_startup(info);
+ retval = mxser_startup(tty);
if (retval)
return retval;
@@ -1209,13 +1207,13 @@
break;
}
}
- mxser_shutdown(info);
+ mxser_shutdown(tty);
mxser_flush_buffer(tty);
tty_ldisc_flush(tty);
tty->closing = 0;
- info->port.tty = NULL;
+ tty_port_tty_set(&info->port, NULL);
if (info->port.blocked_open) {
if (info->port.close_delay)
schedule_timeout_interruptible(info->port.close_delay);
@@ -1337,12 +1335,13 @@
* friends of mxser_ioctl()
* ------------------------------------------------------------
*/
-static int mxser_get_serial_info(struct mxser_port *info,
+static int mxser_get_serial_info(struct tty_struct *tty,
struct serial_struct __user *retinfo)
{
+ struct mxser_port *info = tty->driver_data;
struct serial_struct tmp = {
.type = info->type,
- .line = info->port.tty->index,
+ .line = tty->index,
.port = info->ioaddr,
.irq = info->board->irq,
.flags = info->port.flags,
@@ -1357,9 +1356,10 @@
return 0;
}
-static int mxser_set_serial_info(struct mxser_port *info,
+static int mxser_set_serial_info(struct tty_struct *tty,
struct serial_struct __user *new_info)
{
+ struct mxser_port *info = tty->driver_data;
struct serial_struct new_serial;
speed_t baud;
unsigned long sl_flags;
@@ -1393,14 +1393,14 @@
(new_serial.flags & ASYNC_FLAGS));
info->port.close_delay = new_serial.close_delay * HZ / 100;
info->port.closing_wait = new_serial.closing_wait * HZ / 100;
- info->port.tty->low_latency =
- (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+ tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY)
+ ? 1 : 0;
if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST &&
(new_serial.baud_base != info->baud_base ||
new_serial.custom_divisor !=
info->custom_divisor)) {
baud = new_serial.baud_base / new_serial.custom_divisor;
- tty_encode_baud_rate(info->port.tty, baud, baud);
+ tty_encode_baud_rate(tty, baud, baud);
}
}
@@ -1411,11 +1411,11 @@
if (info->port.flags & ASYNC_INITIALIZED) {
if (flags != (info->port.flags & ASYNC_SPD_MASK)) {
spin_lock_irqsave(&info->slock, sl_flags);
- mxser_change_speed(info, NULL);
+ mxser_change_speed(tty, NULL);
spin_unlock_irqrestore(&info->slock, sl_flags);
}
} else
- retval = mxser_startup(info);
+ retval = mxser_startup(tty);
return retval;
}
@@ -1461,7 +1461,7 @@
spin_lock_irqsave(&info->slock, flags);
status = inb(info->ioaddr + UART_MSR);
if (status & UART_MSR_ANY_DELTA)
- mxser_check_modem_status(info, status);
+ mxser_check_modem_status(tty, info, status);
spin_unlock_irqrestore(&info->slock, flags);
return ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) |
((control & UART_MCR_DTR) ? TIOCM_DTR : 0) |
@@ -1606,6 +1606,7 @@
static int mxser_ioctl_special(unsigned int cmd, void __user *argp)
{
struct mxser_port *port;
+ struct tty_struct *tty;
int result, status;
unsigned int i, j;
int ret = 0;
@@ -1643,12 +1644,14 @@
if (!port->ioaddr)
goto copy;
+
+ tty = tty_port_tty_get(&port->port);
- if (!port->port.tty || !port->port.tty->termios)
+ if (!tty || !tty->termios)
ms.cflag = port->normal_termios.c_cflag;
else
- ms.cflag = port->port.tty->termios->c_cflag;
-
+ ms.cflag = tty->termios->c_cflag;
+ tty_kref_put(tty);
status = inb(port->ioaddr + UART_MSR);
if (status & UART_MSR_DCD)
ms.dcd = 1;
@@ -1704,15 +1707,18 @@
me->up_txcnt[p] = port->mon_data.up_txcnt;
me->modem_status[p] =
port->mon_data.modem_status;
- me->baudrate[p] = tty_get_baud_rate(port->port.tty);
+ tty = tty_port_tty_get(&port->port);
- if (!port->port.tty || !port->port.tty->termios) {
+ if (!tty || !tty->termios) {
cflag = port->normal_termios.c_cflag;
iflag = port->normal_termios.c_iflag;
+ me->baudrate[p] = tty_termios_baud_rate(&port->normal_termios);
} else {
- cflag = port->port.tty->termios->c_cflag;
- iflag = port->port.tty->termios->c_iflag;
+ cflag = tty->termios->c_cflag;
+ iflag = tty->termios->c_iflag;
+ me->baudrate[p] = tty_get_baud_rate(tty);
}
+ tty_kref_put(tty);
me->databits[p] = cflag & CSIZE;
me->stopbits[p] = cflag & CSTOPB;
@@ -1822,12 +1828,12 @@
switch (cmd) {
case TIOCGSERIAL:
lock_kernel();
- retval = mxser_get_serial_info(info, argp);
+ retval = mxser_get_serial_info(tty, argp);
unlock_kernel();
return retval;
case TIOCSSERIAL:
lock_kernel();
- retval = mxser_set_serial_info(info, argp);
+ retval = mxser_set_serial_info(tty, argp);
unlock_kernel();
return retval;
case TIOCSERGETLSR: /* Get line status register */
@@ -1896,7 +1902,7 @@
lock_kernel();
status = mxser_get_msr(info->ioaddr, 1, tty->index);
- mxser_check_modem_status(info, status);
+ mxser_check_modem_status(tty, info, status);
mcr = inb(info->ioaddr + UART_MCR);
if (mcr & MOXA_MUST_MCR_XON_FLAG)
@@ -1909,7 +1915,7 @@
else
info->mon_data.hold_reason |= NPPI_NOTIFY_XOFFXENT;
- if (info->port.tty->hw_stopped)
+ if (tty->hw_stopped)
info->mon_data.hold_reason |= NPPI_NOTIFY_CTSHOLD;
else
info->mon_data.hold_reason &= ~NPPI_NOTIFY_CTSHOLD;
@@ -1958,7 +1964,7 @@
}
}
- if (info->port.tty->termios->c_cflag & CRTSCTS) {
+ if (tty->termios->c_cflag & CRTSCTS) {
info->MCR &= ~UART_MCR_RTS;
outb(info->MCR, info->ioaddr + UART_MCR);
}
@@ -1995,7 +2001,7 @@
}
}
- if (info->port.tty->termios->c_cflag & CRTSCTS) {
+ if (tty->termios->c_cflag & CRTSCTS) {
info->MCR |= UART_MCR_RTS;
outb(info->MCR, info->ioaddr + UART_MCR);
}
@@ -2040,7 +2046,7 @@
unsigned long flags;
spin_lock_irqsave(&info->slock, flags);
- mxser_change_speed(info, old_termios);
+ mxser_change_speed(tty, old_termios);
spin_unlock_irqrestore(&info->slock, flags);
if ((old_termios->c_cflag & CRTSCTS) &&
@@ -2138,10 +2144,10 @@
struct mxser_port *info = tty->driver_data;
mxser_flush_buffer(tty);
- mxser_shutdown(info);
+ mxser_shutdown(tty);
info->port.count = 0;
info->port.flags &= ~ASYNC_NORMAL_ACTIVE;
- info->port.tty = NULL;
+ tty_port_tty_set(&info->port, NULL);
wake_up_interruptible(&info->port.open_wait);
}
@@ -2164,9 +2170,9 @@
return 0;
}
-static void mxser_receive_chars(struct mxser_port *port, int *status)
+static void mxser_receive_chars(struct tty_struct *tty,
+ struct mxser_port *port, int *status)
{
- struct tty_struct *tty = port->port.tty;
unsigned char ch, gdl;
int ignored = 0;
int cnt = 0;
@@ -2174,9 +2180,8 @@
int max = 256;
recv_room = tty->receive_room;
- if ((recv_room == 0) && (!port->ldisc_stop_rx))
+ if (recv_room == 0 && !port->ldisc_stop_rx)
mxser_stoprx(tty);
-
if (port->board->chip_flag != MOXA_OTHER_UART) {
if (*status & UART_LSR_SPECIAL)
@@ -2253,7 +2258,7 @@
} while (*status & UART_LSR_DR);
end_intr:
- mxvar_log.rxcnt[port->port.tty->index] += cnt;
+ mxvar_log.rxcnt[tty->index] += cnt;
port->mon_data.rxcnt += cnt;
port->mon_data.up_rxcnt += cnt;
@@ -2267,14 +2272,14 @@
spin_lock(&port->slock);
}
-static void mxser_transmit_chars(struct mxser_port *port)
+static void mxser_transmit_chars(struct tty_struct *tty, struct mxser_port *port)
{
int count, cnt;
if (port->x_char) {
outb(port->x_char, port->ioaddr + UART_TX);
port->x_char = 0;
- mxvar_log.txcnt[port->port.tty->index]++;
+ mxvar_log.txcnt[tty->index]++;
port->mon_data.txcnt++;
port->mon_data.up_txcnt++;
port->icount.tx++;
@@ -2284,8 +2289,8 @@
if (port->port.xmit_buf == NULL)
return;
- if ((port->xmit_cnt <= 0) || port->port.tty->stopped ||
- (port->port.tty->hw_stopped &&
+ if (port->xmit_cnt <= 0 || tty->stopped ||
+ (tty->hw_stopped &&
(port->type != PORT_16550A) &&
(!port->board->chip_flag))) {
port->IER &= ~UART_IER_THRI;
@@ -2302,14 +2307,14 @@
if (--port->xmit_cnt <= 0)
break;
} while (--count > 0);
- mxvar_log.txcnt[port->port.tty->index] += (cnt - port->xmit_cnt);
+ mxvar_log.txcnt[tty->index] += (cnt - port->xmit_cnt);
port->mon_data.txcnt += (cnt - port->xmit_cnt);
port->mon_data.up_txcnt += (cnt - port->xmit_cnt);
port->icount.tx += (cnt - port->xmit_cnt);
- if (port->xmit_cnt < WAKEUP_CHARS)
- tty_wakeup(port->port.tty);
+ if (port->xmit_cnt < WAKEUP_CHARS && tty)
+ tty_wakeup(tty);
if (port->xmit_cnt <= 0) {
port->IER &= ~UART_IER_THRI;
@@ -2328,6 +2333,7 @@
int max, irqbits, bits, msr;
unsigned int int_cnt, pass_counter = 0;
int handled = IRQ_NONE;
+ struct tty_struct *tty;
for (i = 0; i < MXSER_BOARDS; i++)
if (dev_id == &mxser_boards[i]) {
@@ -2360,13 +2366,15 @@
if (iir & UART_IIR_NO_INT)
break;
iir &= MOXA_MUST_IIR_MASK;
- if (!port->port.tty ||
+ tty = tty_port_tty_get(&port->port);
+ if (!tty ||
(port->port.flags & ASYNC_CLOSING) ||
!(port->port.flags &
ASYNC_INITIALIZED)) {
status = inb(port->ioaddr + UART_LSR);
outb(0x27, port->ioaddr + UART_FCR);
inb(port->ioaddr + UART_MSR);
+ tty_kref_put(tty);
break;
}
@@ -2387,27 +2395,28 @@
iir == MOXA_MUST_IIR_RDA ||
iir == MOXA_MUST_IIR_RTO ||
iir == MOXA_MUST_IIR_LSR)
- mxser_receive_chars(port,
+ mxser_receive_chars(tty, port,
&status);
} else {
status &= port->read_status_mask;
if (status & UART_LSR_DR)
- mxser_receive_chars(port,
+ mxser_receive_chars(tty, port,
&status);
}
msr = inb(port->ioaddr + UART_MSR);
if (msr & UART_MSR_ANY_DELTA)
- mxser_check_modem_status(port, msr);
+ mxser_check_modem_status(tty, port, msr);
if (port->board->chip_flag) {
if (iir == 0x02 && (status &
UART_LSR_THRE))
- mxser_transmit_chars(port);
+ mxser_transmit_chars(tty, port);
} else {
if (status & UART_LSR_THRE)
- mxser_transmit_chars(port);
+ mxser_transmit_chars(tty, port);
}
+ tty_kref_put(tty);
} while (int_cnt++ < MXSER_ISR_PASS_LIMIT);
spin_unlock(&port->slock);
}
diff --git a/drivers/char/n_hdlc.c b/drivers/char/n_hdlc.c
index 69ec639..bacb3e2 100644
--- a/drivers/char/n_hdlc.c
+++ b/drivers/char/n_hdlc.c
@@ -764,7 +764,7 @@
break;
default:
- error = n_tty_ioctl (tty, file, cmd, arg);
+ error = n_tty_ioctl_helper(tty, file, cmd, arg);
break;
}
return error;
diff --git a/drivers/char/n_r3964.c b/drivers/char/n_r3964.c
index ae377aa..4a8215a 100644
--- a/drivers/char/n_r3964.c
+++ b/drivers/char/n_r3964.c
@@ -372,14 +372,8 @@
static void put_char(struct r3964_info *pInfo, unsigned char ch)
{
struct tty_struct *tty = pInfo->tty;
-
- if (tty == NULL)
- return;
-
/* FIXME: put_char should not be called from an IRQ */
- if (tty->ops->put_char) {
- tty->ops->put_char(tty, ch);
- }
+ tty_put_char(tty, ch);
pInfo->bcc ^= ch;
}
diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c
index 708c2b1..efbfe961 100644
--- a/drivers/char/n_tty.c
+++ b/drivers/char/n_tty.c
@@ -26,7 +26,7 @@
*
* 2002/03/18 Implemented n_tty_wakeup to send SIGIO POLL_OUTs to
* waiting writing processes-Sapan Bhatia <sapan@corewars.org>.
- * Also fixed a bug in BLOCKING mode where write_chan returns
+ * Also fixed a bug in BLOCKING mode where n_tty_write returns
* EAGAIN
*/
@@ -99,6 +99,7 @@
static void n_tty_set_room(struct tty_struct *tty)
{
+ /* tty->read_cnt is not read locked ? */
int left = N_TTY_BUF_SIZE - tty->read_cnt - 1;
/*
@@ -121,6 +122,16 @@
}
}
+/**
+ * put_tty_queue - add character to tty
+ * @c: character
+ * @tty: tty device
+ *
+ * Add a character to the tty read_buf queue. This is done under the
+ * read_lock to serialize character addition and also to protect us
+ * against parallel reads or flushes
+ */
+
static void put_tty_queue(unsigned char c, struct tty_struct *tty)
{
unsigned long flags;
@@ -137,14 +148,11 @@
* check_unthrottle - allow new receive data
* @tty; tty device
*
- * Check whether to call the driver.unthrottle function.
- * We test the TTY_THROTTLED bit first so that it always
- * indicates the current state. The decision about whether
- * it is worth allowing more input has been taken by the caller.
+ * Check whether to call the driver unthrottle functions
+ *
* Can sleep, may be called under the atomic_read_lock mutex but
* this is not guaranteed.
*/
-
static void check_unthrottle(struct tty_struct *tty)
{
if (tty->count)
@@ -158,6 +166,8 @@
* Reset the read buffer counters, clear the flags,
* and make sure the driver is unthrottled. Called
* from n_tty_open() and n_tty_flush_buffer().
+ *
+ * Locking: tty_read_lock for read fields.
*/
static void reset_buffer_flags(struct tty_struct *tty)
{
@@ -181,7 +191,7 @@
* at hangup) or when the N_TTY line discipline internally has to
* clean the pending queue (for example some signals).
*
- * Locking: ctrl_lock
+ * Locking: ctrl_lock, read_lock.
*/
static void n_tty_flush_buffer(struct tty_struct *tty)
@@ -207,6 +217,8 @@
*
* Report the number of characters buffered to be delivered to user
* at this instant in time.
+ *
+ * Locking: read_lock
*/
static ssize_t n_tty_chars_in_buffer(struct tty_struct *tty)
@@ -346,7 +358,7 @@
* the simple cases normally found and helps to generate blocks of
* symbols for the console driver and thus improve performance.
*
- * Called from write_chan under the tty layer write lock. Relies
+ * Called from n_tty_write under the tty layer write lock. Relies
* on lock_kernel for the tty->column state.
*/
@@ -410,6 +422,8 @@
*
* Echo user input back onto the screen. This must be called only when
* L_ECHO(tty) is true. Called from the driver receive_buf path.
+ *
+ * Relies on BKL for tty column locking
*/
static void echo_char(unsigned char c, struct tty_struct *tty)
@@ -422,6 +436,12 @@
opost(c, tty);
}
+/**
+ * finsh_erasing - complete erase
+ * @tty: tty doing the erase
+ *
+ * Relies on BKL for tty column locking
+ */
static inline void finish_erasing(struct tty_struct *tty)
{
if (tty->erasing) {
@@ -439,6 +459,8 @@
* Perform erase and necessary output when an erase character is
* present in the stream from the driver layer. Handles the complexities
* of UTF-8 multibyte symbols.
+ *
+ * Locking: read_lock for tty buffers, BKL for column/erasing state
*/
static void eraser(unsigned char c, struct tty_struct *tty)
@@ -447,6 +469,7 @@
int head, seen_alnums, cnt;
unsigned long flags;
+ /* FIXME: locking needed ? */
if (tty->read_head == tty->canon_head) {
/* opost('\a', tty); */ /* what do you think? */
return;
@@ -481,6 +504,7 @@
}
seen_alnums = 0;
+ /* FIXME: Locking ?? */
while (tty->read_head != tty->canon_head) {
head = tty->read_head;
@@ -583,6 +607,8 @@
* may caus terminal flushing to take place according to the termios
* settings and character used. Called from the driver receive_buf
* path so serialized.
+ *
+ * Locking: ctrl_lock, read_lock (both via flush buffer)
*/
static inline void isig(int sig, struct tty_struct *tty, int flush)
@@ -1007,12 +1033,26 @@
* and is protected from re-entry by the tty layer. The user is
* guaranteed that this function will not be re-entered or in progress
* when the ldisc is closed.
+ *
+ * Locking: Caller holds tty->termios_mutex
*/
static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
{
- if (!tty)
- return;
+ int canon_change = 1;
+ BUG_ON(!tty);
+
+ if (old)
+ canon_change = (old->c_lflag ^ tty->termios->c_lflag) & ICANON;
+ if (canon_change) {
+ memset(&tty->read_flags, 0, sizeof tty->read_flags);
+ tty->canon_head = tty->read_tail;
+ tty->canon_data = 0;
+ tty->erasing = 0;
+ }
+
+ if (canon_change && !L_ICANON(tty) && tty->read_cnt)
+ wake_up_interruptible(&tty->read_wait);
tty->icanon = (L_ICANON(tty) != 0);
if (test_bit(TTY_HW_COOK_IN, &tty->flags)) {
@@ -1143,7 +1183,7 @@
* @b: user data
* @nr: size of data
*
- * Helper function to speed up read_chan. It is only called when
+ * Helper function to speed up n_tty_read. It is only called when
* ICANON is off; it copies characters straight from the tty queue to
* user space directly. It can be profitably called twice; once to
* drain the space from the tail pointer to the (physical) end of the
@@ -1210,7 +1250,7 @@
if (file->f_op->write != redirected_tty_write &&
current->signal->tty == tty) {
if (!tty->pgrp)
- printk(KERN_ERR "read_chan: no tty->pgrp!\n");
+ printk(KERN_ERR "n_tty_read: no tty->pgrp!\n");
else if (task_pgrp(current) != tty->pgrp) {
if (is_ignored(SIGTTIN) ||
is_current_pgrp_orphaned())
@@ -1225,7 +1265,7 @@
/**
- * read_chan - read function for tty
+ * n_tty_read - read function for tty
* @tty: tty device
* @file: file object
* @buf: userspace buffer pointer
@@ -1239,7 +1279,7 @@
* This code must be sure never to sleep through a hangup.
*/
-static ssize_t read_chan(struct tty_struct *tty, struct file *file,
+static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
unsigned char __user *buf, size_t nr)
{
unsigned char __user *b = buf;
@@ -1254,10 +1294,7 @@
do_it_again:
- if (!tty->read_buf) {
- printk(KERN_ERR "n_tty_read_chan: read_buf == NULL?!?\n");
- return -EIO;
- }
+ BUG_ON(!tty->read_buf);
c = job_control(tty, file);
if (c < 0)
@@ -1444,7 +1481,7 @@
}
/**
- * write_chan - write function for tty
+ * n_tty_write - write function for tty
* @tty: tty device
* @file: file object
* @buf: userspace buffer pointer
@@ -1458,7 +1495,7 @@
* This code must be sure never to sleep through a hangup.
*/
-static ssize_t write_chan(struct tty_struct *tty, struct file *file,
+static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
const unsigned char *buf, size_t nr)
{
const unsigned char *b = buf;
@@ -1532,7 +1569,7 @@
}
/**
- * normal_poll - poll method for N_TTY
+ * n_tty_poll - poll method for N_TTY
* @tty: terminal device
* @file: file accessing it
* @wait: poll table
@@ -1545,7 +1582,7 @@
* Called without the kernel lock held - fine
*/
-static unsigned int normal_poll(struct tty_struct *tty, struct file *file,
+static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file,
poll_table *wait)
{
unsigned int mask = 0;
@@ -1573,6 +1610,44 @@
return mask;
}
+static unsigned long inq_canon(struct tty_struct *tty)
+{
+ int nr, head, tail;
+
+ if (!tty->canon_data)
+ return 0;
+ head = tty->canon_head;
+ tail = tty->read_tail;
+ nr = (head - tail) & (N_TTY_BUF_SIZE-1);
+ /* Skip EOF-chars.. */
+ while (head != tail) {
+ if (test_bit(tail, tty->read_flags) &&
+ tty->read_buf[tail] == __DISABLED_CHAR)
+ nr--;
+ tail = (tail+1) & (N_TTY_BUF_SIZE-1);
+ }
+ return nr;
+}
+
+static int n_tty_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int retval;
+
+ switch (cmd) {
+ case TIOCOUTQ:
+ return put_user(tty_chars_in_buffer(tty), (int __user *) arg);
+ case TIOCINQ:
+ /* FIXME: Locking */
+ retval = tty->read_cnt;
+ if (L_ICANON(tty))
+ retval = inq_canon(tty);
+ return put_user(retval, (unsigned int __user *) arg);
+ default:
+ return n_tty_ioctl_helper(tty, file, cmd, arg);
+ }
+}
+
struct tty_ldisc_ops tty_ldisc_N_TTY = {
.magic = TTY_LDISC_MAGIC,
.name = "n_tty",
@@ -1580,11 +1655,11 @@
.close = n_tty_close,
.flush_buffer = n_tty_flush_buffer,
.chars_in_buffer = n_tty_chars_in_buffer,
- .read = read_chan,
- .write = write_chan,
+ .read = n_tty_read,
+ .write = n_tty_write,
.ioctl = n_tty_ioctl,
.set_termios = n_tty_set_termios,
- .poll = normal_poll,
+ .poll = n_tty_poll,
.receive_buf = n_tty_receive_buf,
.write_wakeup = n_tty_write_wakeup
};
diff --git a/drivers/char/nozomi.c b/drivers/char/nozomi.c
index 66a0f93..9a34a19 100644
--- a/drivers/char/nozomi.c
+++ b/drivers/char/nozomi.c
@@ -1599,7 +1599,10 @@
return 0;
}
-/* Called when the userspace process close the tty, /dev/noz*. */
+/* Called when the userspace process close the tty, /dev/noz*. Also
+ called immediately if ntty_open fails in which case tty->driver_data
+ will be NULL an we exit by the first return */
+
static void ntty_close(struct tty_struct *tty, struct file *file)
{
struct nozomi *dc = get_dc_by_tty(tty);
diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c
index f070ae7..1c5bf99 100644
--- a/drivers/char/pcmcia/cm4000_cs.c
+++ b/drivers/char/pcmcia/cm4000_cs.c
@@ -1759,65 +1759,40 @@
/*==== Interface to PCMCIA Layer =======================================*/
+static int cm4000_config_check(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ if (!cfg->io.nwin)
+ return -ENODEV;
+
+ /* Get the IOaddr */
+ p_dev->io.BasePort1 = cfg->io.win[0].base;
+ p_dev->io.NumPorts1 = cfg->io.win[0].len;
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(cfg->io.flags & CISTPL_IO_8BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(cfg->io.flags & CISTPL_IO_16BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ p_dev->io.IOAddrLines = cfg->io.flags & CISTPL_IO_LINES_MASK;
+
+ return pcmcia_request_io(p_dev, &p_dev->io);
+}
+
static int cm4000_config(struct pcmcia_device * link, int devno)
{
struct cm4000_dev *dev;
- tuple_t tuple;
- cisparse_t parse;
- u_char buf[64];
- int fail_fn, fail_rc;
- int rc;
/* read the config-tuples */
- tuple.Attributes = 0;
- tuple.TupleData = buf;
- tuple.TupleDataMax = sizeof(buf);
- tuple.TupleOffset = 0;
-
- link->io.BasePort2 = 0;
- link->io.NumPorts2 = 0;
- link->io.Attributes2 = 0;
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- for (rc = pcmcia_get_first_tuple(link, &tuple);
- rc == CS_SUCCESS; rc = pcmcia_get_next_tuple(link, &tuple)) {
-
- rc = pcmcia_get_tuple_data(link, &tuple);
- if (rc != CS_SUCCESS)
- continue;
- rc = pcmcia_parse_tuple(link, &tuple, &parse);
- if (rc != CS_SUCCESS)
- continue;
-
- link->conf.ConfigIndex = parse.cftable_entry.index;
-
- if (!parse.cftable_entry.io.nwin)
- continue;
-
- /* Get the IOaddr */
- link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
- link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
- if (!(parse.cftable_entry.io.flags & CISTPL_IO_8BIT))
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
- if (!(parse.cftable_entry.io.flags & CISTPL_IO_16BIT))
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
- link->io.IOAddrLines = parse.cftable_entry.io.flags
- & CISTPL_IO_LINES_MASK;
-
- rc = pcmcia_request_io(link, &link->io);
- if (rc == CS_SUCCESS)
- break; /* we are done */
- }
- if (rc != CS_SUCCESS)
+ if (pcmcia_loop_config(link, cm4000_config_check, NULL))
goto cs_release;
link->conf.IntType = 00000002;
- if ((fail_rc =
- pcmcia_request_configuration(link, &link->conf)) != CS_SUCCESS) {
- fail_fn = RequestConfiguration;
+ if (pcmcia_request_configuration(link, &link->conf))
goto cs_release;
- }
dev = link->priv;
sprintf(dev->node.dev_name, DEVICE_NAME "%d", devno);
diff --git a/drivers/char/pcmcia/cm4040_cs.c b/drivers/char/pcmcia/cm4040_cs.c
index 0b5934b..2d7c906 100644
--- a/drivers/char/pcmcia/cm4040_cs.c
+++ b/drivers/char/pcmcia/cm4040_cs.c
@@ -526,65 +526,49 @@
return;
}
+static int cm4040_config_check(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ int rc;
+ if (!cfg->io.nwin)
+ return -ENODEV;
+
+ /* Get the IOaddr */
+ p_dev->io.BasePort1 = cfg->io.win[0].base;
+ p_dev->io.NumPorts1 = cfg->io.win[0].len;
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(cfg->io.flags & CISTPL_IO_8BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(cfg->io.flags & CISTPL_IO_16BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ p_dev->io.IOAddrLines = cfg->io.flags & CISTPL_IO_LINES_MASK;
+
+ rc = pcmcia_request_io(p_dev, &p_dev->io);
+ dev_printk(KERN_INFO, &handle_to_dev(p_dev),
+ "pcmcia_request_io returned 0x%x\n", rc);
+ return rc;
+}
+
+
static int reader_config(struct pcmcia_device *link, int devno)
{
struct reader_dev *dev;
- tuple_t tuple;
- cisparse_t parse;
- u_char buf[64];
- int fail_fn, fail_rc;
- int rc;
-
- tuple.Attributes = 0;
- tuple.TupleData = buf;
- tuple.TupleDataMax = sizeof(buf);
- tuple.TupleOffset = 0;
+ int fail_rc;
link->io.BasePort2 = 0;
link->io.NumPorts2 = 0;
link->io.Attributes2 = 0;
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- for (rc = pcmcia_get_first_tuple(link, &tuple);
- rc == CS_SUCCESS;
- rc = pcmcia_get_next_tuple(link, &tuple)) {
- rc = pcmcia_get_tuple_data(link, &tuple);
- if (rc != CS_SUCCESS)
- continue;
- rc = pcmcia_parse_tuple(link, &tuple, &parse);
- if (rc != CS_SUCCESS)
- continue;
- link->conf.ConfigIndex = parse.cftable_entry.index;
-
- if (!parse.cftable_entry.io.nwin)
- continue;
-
- link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
- link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
- if (!(parse.cftable_entry.io.flags & CISTPL_IO_8BIT))
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
- if (!(parse.cftable_entry.io.flags & CISTPL_IO_16BIT))
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
- link->io.IOAddrLines = parse.cftable_entry.io.flags
- & CISTPL_IO_LINES_MASK;
- rc = pcmcia_request_io(link, &link->io);
-
- dev_printk(KERN_INFO, &handle_to_dev(link), "foo");
- if (rc == CS_SUCCESS)
- break;
- else
- dev_printk(KERN_INFO, &handle_to_dev(link),
- "pcmcia_request_io failed 0x%x\n", rc);
- }
- if (rc != CS_SUCCESS)
+ if (pcmcia_loop_config(link, cm4040_config_check, NULL))
goto cs_release;
link->conf.IntType = 00000002;
- if ((fail_rc = pcmcia_request_configuration(link,&link->conf))
- !=CS_SUCCESS) {
- fail_fn = RequestConfiguration;
+ fail_rc = pcmcia_request_configuration(link, &link->conf);
+ if (fail_rc != 0) {
dev_printk(KERN_INFO, &handle_to_dev(link),
"pcmcia_request_configuration failed 0x%x\n",
fail_rc);
diff --git a/drivers/char/pcmcia/ipwireless/main.c b/drivers/char/pcmcia/ipwireless/main.c
index 5eca7a9..5216fce 100644
--- a/drivers/char/pcmcia/ipwireless/main.c
+++ b/drivers/char/pcmcia/ipwireless/main.c
@@ -65,9 +65,9 @@
struct ipw_dev *ipw = container_of(work_reboot, struct ipw_dev,
work_reboot);
struct pcmcia_device *link = ipw->link;
- int ret = pccard_reset_card(link->socket);
+ int ret = pcmcia_reset_card(link->socket);
- if (ret != CS_SUCCESS)
+ if (ret != 0)
cs_error(link, ResetCard, ret);
}
@@ -83,7 +83,6 @@
{
struct pcmcia_device *link = ipw->link;
int ret;
- config_info_t conf;
tuple_t tuple;
unsigned short buf[64];
cisparse_t parse;
@@ -105,7 +104,7 @@
while (ret == 0) {
ret = pcmcia_get_tuple_data(link, &tuple);
- if (ret != CS_SUCCESS) {
+ if (ret != 0) {
cs_error(link, GetTupleData, ret);
goto exit0;
}
@@ -116,21 +115,21 @@
ret = pcmcia_get_first_tuple(link, &tuple);
- if (ret != CS_SUCCESS) {
+ if (ret != 0) {
cs_error(link, GetFirstTuple, ret);
goto exit0;
}
ret = pcmcia_get_tuple_data(link, &tuple);
- if (ret != CS_SUCCESS) {
+ if (ret != 0) {
cs_error(link, GetTupleData, ret);
goto exit0;
}
- ret = pcmcia_parse_tuple(link, &tuple, &parse);
+ ret = pcmcia_parse_tuple(&tuple, &parse);
- if (ret != CS_SUCCESS) {
+ if (ret != 0) {
cs_error(link, ParseTuple, ret);
goto exit0;
}
@@ -152,21 +151,21 @@
ret = pcmcia_get_first_tuple(link, &tuple);
- if (ret != CS_SUCCESS) {
+ if (ret != 0) {
cs_error(link, GetFirstTuple, ret);
goto exit0;
}
ret = pcmcia_get_tuple_data(link, &tuple);
- if (ret != CS_SUCCESS) {
+ if (ret != 0) {
cs_error(link, GetTupleData, ret);
goto exit0;
}
- ret = pcmcia_parse_tuple(link, &tuple, &parse);
+ ret = pcmcia_parse_tuple(&tuple, &parse);
- if (ret != CS_SUCCESS) {
+ if (ret != 0) {
cs_error(link, GetTupleData, ret);
goto exit0;
}
@@ -181,7 +180,7 @@
ret = pcmcia_request_io(link, &link->io);
- if (ret != CS_SUCCESS) {
+ if (ret != 0) {
cs_error(link, RequestIO, ret);
goto exit0;
}
@@ -195,21 +194,21 @@
ret = pcmcia_get_first_tuple(link, &tuple);
- if (ret != CS_SUCCESS) {
+ if (ret != 0) {
cs_error(link, GetFirstTuple, ret);
goto exit1;
}
ret = pcmcia_get_tuple_data(link, &tuple);
- if (ret != CS_SUCCESS) {
+ if (ret != 0) {
cs_error(link, GetTupleData, ret);
goto exit1;
}
- ret = pcmcia_parse_tuple(link, &tuple, &parse);
+ ret = pcmcia_parse_tuple(&tuple, &parse);
- if (ret != CS_SUCCESS) {
+ if (ret != 0) {
cs_error(link, ParseTuple, ret);
goto exit1;
}
@@ -227,7 +226,7 @@
ret = pcmcia_request_window(&link, &ipw->request_common_memory,
&ipw->handle_common_memory);
- if (ret != CS_SUCCESS) {
+ if (ret != 0) {
cs_error(link, RequestWindow, ret);
goto exit1;
}
@@ -239,7 +238,7 @@
ret = pcmcia_map_mem_page(ipw->handle_common_memory,
&memreq_common_memory);
- if (ret != CS_SUCCESS) {
+ if (ret != 0) {
cs_error(link, MapMemPage, ret);
goto exit1;
}
@@ -261,7 +260,7 @@
ret = pcmcia_request_window(&link, &ipw->request_attr_memory,
&ipw->handle_attr_memory);
- if (ret != CS_SUCCESS) {
+ if (ret != 0) {
cs_error(link, RequestWindow, ret);
goto exit2;
}
@@ -272,7 +271,7 @@
ret = pcmcia_map_mem_page(ipw->handle_attr_memory,
&memreq_attr_memory);
- if (ret != CS_SUCCESS) {
+ if (ret != 0) {
cs_error(link, MapMemPage, ret);
goto exit2;
}
@@ -292,20 +291,11 @@
ret = pcmcia_request_irq(link, &link->irq);
- if (ret != CS_SUCCESS) {
+ if (ret != 0) {
cs_error(link, RequestIRQ, ret);
goto exit3;
}
- /* Look up current Vcc */
-
- ret = pcmcia_get_configuration_info(link, &conf);
-
- if (ret != CS_SUCCESS) {
- cs_error(link, GetConfigurationInfo, ret);
- goto exit4;
- }
-
printk(KERN_INFO IPWIRELESS_PCCARD_NAME ": Card type %s\n",
ipw->is_v2_card ? "V2/V3" : "V1");
printk(KERN_INFO IPWIRELESS_PCCARD_NAME
@@ -341,7 +331,7 @@
*/
ret = pcmcia_request_configuration(link, &link->conf);
- if (ret != CS_SUCCESS) {
+ if (ret != 0) {
cs_error(link, RequestConfiguration, ret);
goto exit4;
}
diff --git a/drivers/char/pcmcia/ipwireless/tty.c b/drivers/char/pcmcia/ipwireless/tty.c
index 3a23e76..569f2f7 100644
--- a/drivers/char/pcmcia/ipwireless/tty.c
+++ b/drivers/char/pcmcia/ipwireless/tty.c
@@ -276,6 +276,7 @@
struct ipw_tty *tty = linux_tty->driver_data;
int room;
+ /* FIXME: Exactly how is the tty object locked here .. */
if (!tty)
return -ENODEV;
@@ -397,6 +398,7 @@
static int ipw_tiocmget(struct tty_struct *linux_tty, struct file *file)
{
struct ipw_tty *tty = linux_tty->driver_data;
+ /* FIXME: Exactly how is the tty object locked here .. */
if (!tty)
return -ENODEV;
@@ -412,6 +414,7 @@
unsigned int set, unsigned int clear)
{
struct ipw_tty *tty = linux_tty->driver_data;
+ /* FIXME: Exactly how is the tty object locked here .. */
if (!tty)
return -ENODEV;
@@ -433,6 +436,8 @@
if (!tty->open_count)
return -EINVAL;
+ /* FIXME: Exactly how is the tty object locked here .. */
+
switch (cmd) {
case TIOCGSERIAL:
return ipwireless_get_serial_info(tty, (void __user *) arg);
@@ -467,13 +472,6 @@
}
return 0;
- case TCGETS:
- case TCGETA:
- return n_tty_ioctl(linux_tty, file, cmd, arg);
-
- case TCFLSH:
- return n_tty_ioctl(linux_tty, file, cmd, arg);
-
case FIONREAD:
{
int val = 0;
@@ -482,10 +480,11 @@
return -EFAULT;
}
return 0;
+ case TCFLSH:
+ return tty_perform_flush(linux_tty, arg);
}
}
-
- return -ENOIOCTLCMD;
+ return tty_mode_ioctl(linux_tty, file, cmd , arg);
}
static int add_tty(dev_node_t *nodesp, int j,
@@ -588,6 +587,8 @@
tty_hangup(ttyj->linux_tty);
/* Wait till the tty_hangup has completed */
flush_scheduled_work();
+ /* FIXME: Exactly how is the tty object locked here
+ against a parallel ioctl etc */
mutex_lock(&ttyj->ipw_tty_mutex);
}
while (ttyj->open_count)
diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c
index c240562..9a626e5 100644
--- a/drivers/char/pcmcia/synclink_cs.c
+++ b/drivers/char/pcmcia/synclink_cs.c
@@ -601,7 +601,7 @@
cfg = &(parse.cftable_entry);
CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
- CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
+ CS_CHECK(ParseTuple, pcmcia_parse_tuple(&tuple, &parse));
if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg;
if (cfg->index == 0)
diff --git a/drivers/char/pty.c b/drivers/char/pty.c
index 76b2793..6d45827 100644
--- a/drivers/char/pty.c
+++ b/drivers/char/pty.c
@@ -8,10 +8,12 @@
* Added TTY_DO_WRITE_WAKEUP to enable n_tty to send POLL_OUT to
* waiting writers -- Sapan Bhatia <sapan@corewars.org>
*
- *
+ * When reading this code see also fs/devpts. In particular note that the
+ * driver_data field is used by the devpts side as a binding to the devpts
+ * inode.
*/
-#include <linux/module.h> /* For EXPORT_SYMBOL */
+#include <linux/module.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
@@ -23,26 +25,25 @@
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/sysctl.h>
-
-#include <asm/uaccess.h>
-#include <asm/system.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
#include <linux/bitops.h>
#include <linux/devpts_fs.h>
+#include <asm/system.h>
+
/* These are global because they are accessed in tty_io.c */
#ifdef CONFIG_UNIX98_PTYS
struct tty_driver *ptm_driver;
static struct tty_driver *pts_driver;
#endif
-static void pty_close(struct tty_struct * tty, struct file * filp)
+static void pty_close(struct tty_struct *tty, struct file *filp)
{
- if (!tty)
- return;
- if (tty->driver->subtype == PTY_TYPE_MASTER) {
- if (tty->count > 1)
- printk("master pty_close: count = %d!!\n", tty->count);
- } else {
+ BUG_ON(!tty);
+ if (tty->driver->subtype == PTY_TYPE_MASTER)
+ WARN_ON(tty->count > 1);
+ else {
if (tty->count > 2)
return;
}
@@ -59,7 +60,7 @@
set_bit(TTY_OTHER_CLOSED, &tty->flags);
#ifdef CONFIG_UNIX98_PTYS
if (tty->driver == ptm_driver)
- devpts_pty_kill(tty->index);
+ devpts_pty_kill(tty->link);
#endif
tty_vhangup(tty->link);
}
@@ -69,13 +70,13 @@
* The unthrottle routine is called by the line discipline to signal
* that it can receive more characters. For PTY's, the TTY_THROTTLED
* flag is always set, to force the line discipline to always call the
- * unthrottle routine when there are fewer than TTY_THRESHOLD_UNTHROTTLE
+ * unthrottle routine when there are fewer than TTY_THRESHOLD_UNTHROTTLE
* characters in the queue. This is necessary since each time this
* happens, we need to wake up any sleeping processes that could be
* (1) trying to send data to the pty, or (2) waiting in wait_until_sent()
* for the pty buffer to be drained.
*/
-static void pty_unthrottle(struct tty_struct * tty)
+static void pty_unthrottle(struct tty_struct *tty)
{
struct tty_struct *o_tty = tty->link;
@@ -87,7 +88,7 @@
}
/*
- * WSH 05/24/97: modified to
+ * WSH 05/24/97: modified to
* (1) use space in tty->flip instead of a shared temp buffer
* The flip buffers aren't being used for a pty, so there's lots
* of space available. The buffer is protected by a per-pty
@@ -100,7 +101,8 @@
* not our partners. We can't just take the other one blindly without
* risking deadlocks.
*/
-static int pty_write(struct tty_struct * tty, const unsigned char *buf, int count)
+static int pty_write(struct tty_struct *tty, const unsigned char *buf,
+ int count)
{
struct tty_struct *to = tty->link;
int c;
@@ -112,7 +114,7 @@
if (c > count)
c = count;
to->ldisc.ops->receive_buf(to, buf, NULL, c);
-
+
return c;
}
@@ -128,17 +130,17 @@
/*
* WSH 05/24/97: Modified for asymmetric MASTER/SLAVE behavior
- * The chars_in_buffer() value is used by the ldisc select() function
+ * The chars_in_buffer() value is used by the ldisc select() function
* to hold off writing when chars_in_buffer > WAKEUP_CHARS (== 256).
* The pty driver chars_in_buffer() Master/Slave must behave differently:
*
* The Master side needs to allow typed-ahead commands to accumulate
* while being canonicalized, so we report "our buffer" as empty until
* some threshold is reached, and then report the count. (Any count >
- * WAKEUP_CHARS is regarded by select() as "full".) To avoid deadlock
- * the count returned must be 0 if no canonical data is available to be
+ * WAKEUP_CHARS is regarded by select() as "full".) To avoid deadlock
+ * the count returned must be 0 if no canonical data is available to be
* read. (The N_TTY ldisc.chars_in_buffer now knows this.)
- *
+ *
* The Slave side passes all characters in raw mode to the Master side's
* buffer where they can be read immediately, so in this case we can
* return the true count in the buffer.
@@ -155,21 +157,22 @@
/* The ldisc must report 0 if no characters available to be read */
count = to->ldisc.ops->chars_in_buffer(to);
- if (tty->driver->subtype == PTY_TYPE_SLAVE) return count;
+ if (tty->driver->subtype == PTY_TYPE_SLAVE)
+ return count;
- /* Master side driver ... if the other side's read buffer is less than
+ /* Master side driver ... if the other side's read buffer is less than
* half full, return 0 to allow writers to proceed; otherwise return
- * the count. This leaves a comfortable margin to avoid overflow,
+ * the count. This leaves a comfortable margin to avoid overflow,
* and still allows half a buffer's worth of typed-ahead commands.
*/
- return ((count < N_TTY_BUF_SIZE/2) ? 0 : count);
+ return (count < N_TTY_BUF_SIZE/2) ? 0 : count;
}
/* Set the lock flag on a pty */
-static int pty_set_lock(struct tty_struct *tty, int __user * arg)
+static int pty_set_lock(struct tty_struct *tty, int __user *arg)
{
int val;
- if (get_user(val,arg))
+ if (get_user(val, arg))
return -EFAULT;
if (val)
set_bit(TTY_PTY_LOCK, &tty->flags);
@@ -182,13 +185,13 @@
{
struct tty_struct *to = tty->link;
unsigned long flags;
-
+
if (!to)
return;
-
+
if (to->ldisc.ops->flush_buffer)
to->ldisc.ops->flush_buffer(to);
-
+
if (to->packet) {
spin_lock_irqsave(&tty->ctrl_lock, flags);
tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
@@ -197,7 +200,7 @@
}
}
-static int pty_open(struct tty_struct *tty, struct file * filp)
+static int pty_open(struct tty_struct *tty, struct file *filp)
{
int retval = -ENODEV;
@@ -220,13 +223,65 @@
return retval;
}
-static void pty_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
+static void pty_set_termios(struct tty_struct *tty,
+ struct ktermios *old_termios)
{
- tty->termios->c_cflag &= ~(CSIZE | PARENB);
- tty->termios->c_cflag |= (CS8 | CREAD);
+ tty->termios->c_cflag &= ~(CSIZE | PARENB);
+ tty->termios->c_cflag |= (CS8 | CREAD);
}
+static int pty_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+ struct tty_struct *o_tty;
+ int idx = tty->index;
+ int retval;
+
+ o_tty = alloc_tty_struct();
+ if (!o_tty)
+ return -ENOMEM;
+ if (!try_module_get(driver->other->owner)) {
+ /* This cannot in fact currently happen */
+ free_tty_struct(o_tty);
+ return -ENOMEM;
+ }
+ initialize_tty_struct(o_tty, driver->other, idx);
+
+ /* We always use new tty termios data so we can do this
+ the easy way .. */
+ retval = tty_init_termios(tty);
+ if (retval)
+ goto free_mem_out;
+
+ retval = tty_init_termios(o_tty);
+ if (retval) {
+ tty_free_termios(tty);
+ goto free_mem_out;
+ }
+
+ /*
+ * Everything allocated ... set up the o_tty structure.
+ */
+ driver->other->ttys[idx] = o_tty;
+ tty_driver_kref_get(driver->other);
+ if (driver->subtype == PTY_TYPE_MASTER)
+ o_tty->count++;
+ /* Establish the links in both directions */
+ tty->link = o_tty;
+ o_tty->link = tty;
+
+ tty_driver_kref_get(driver);
+ tty->count++;
+ driver->ttys[idx] = tty;
+ return 0;
+free_mem_out:
+ module_put(o_tty->driver->owner);
+ free_tty_struct(o_tty);
+ return -ENOMEM;
+}
+
+
static const struct tty_operations pty_ops = {
+ .install = pty_install,
.open = pty_open,
.close = pty_close,
.write = pty_write,
@@ -329,8 +384,11 @@
* Otherwise one can eat up all kernel memory by opening /dev/ptmx repeatedly.
*/
int pty_limit = NR_UNIX98_PTY_DEFAULT;
-static int pty_limit_min = 0;
+static int pty_limit_min;
static int pty_limit_max = NR_UNIX98_PTY_MAX;
+static int pty_count;
+
+static struct cdev ptmx_cdev;
static struct ctl_table pty_table[] = {
{
@@ -348,6 +406,7 @@
.procname = "nr",
.maxlen = sizeof(int),
.mode = 0444,
+ .data = &pty_count,
.proc_handler = &proc_dointvec,
}, {
.ctl_name = 0
@@ -388,7 +447,111 @@
return -ENOIOCTLCMD;
}
-static const struct tty_operations pty_unix98_ops = {
+/**
+ * ptm_unix98_lookup - find a pty master
+ * @driver: ptm driver
+ * @idx: tty index
+ *
+ * Look up a pty master device. Called under the tty_mutex for now.
+ * This provides our locking.
+ */
+
+static struct tty_struct *ptm_unix98_lookup(struct tty_driver *driver,
+ struct inode *ptm_inode, int idx)
+{
+ struct tty_struct *tty = devpts_get_tty(ptm_inode, idx);
+ if (tty)
+ tty = tty->link;
+ return tty;
+}
+
+/**
+ * pts_unix98_lookup - find a pty slave
+ * @driver: pts driver
+ * @idx: tty index
+ *
+ * Look up a pty master device. Called under the tty_mutex for now.
+ * This provides our locking.
+ */
+
+static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver,
+ struct inode *pts_inode, int idx)
+{
+ struct tty_struct *tty = devpts_get_tty(pts_inode, idx);
+ /* Master must be open before slave */
+ if (!tty)
+ return ERR_PTR(-EIO);
+ return tty;
+}
+
+static void pty_unix98_shutdown(struct tty_struct *tty)
+{
+ /* We have our own method as we don't use the tty index */
+ kfree(tty->termios);
+}
+
+/* We have no need to install and remove our tty objects as devpts does all
+ the work for us */
+
+static int pty_unix98_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+ struct tty_struct *o_tty;
+ int idx = tty->index;
+
+ o_tty = alloc_tty_struct();
+ if (!o_tty)
+ return -ENOMEM;
+ if (!try_module_get(driver->other->owner)) {
+ /* This cannot in fact currently happen */
+ free_tty_struct(o_tty);
+ return -ENOMEM;
+ }
+ initialize_tty_struct(o_tty, driver->other, idx);
+
+ tty->termios = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL);
+ if (tty->termios == NULL)
+ goto free_mem_out;
+ *tty->termios = driver->init_termios;
+ tty->termios_locked = tty->termios + 1;
+
+ o_tty->termios = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL);
+ if (o_tty->termios == NULL)
+ goto free_mem_out;
+ *o_tty->termios = driver->other->init_termios;
+ o_tty->termios_locked = o_tty->termios + 1;
+
+ tty_driver_kref_get(driver->other);
+ if (driver->subtype == PTY_TYPE_MASTER)
+ o_tty->count++;
+ /* Establish the links in both directions */
+ tty->link = o_tty;
+ o_tty->link = tty;
+ /*
+ * All structures have been allocated, so now we install them.
+ * Failures after this point use release_tty to clean up, so
+ * there's no need to null out the local pointers.
+ */
+ tty_driver_kref_get(driver);
+ tty->count++;
+ pty_count++;
+ return 0;
+free_mem_out:
+ kfree(o_tty->termios);
+ module_put(o_tty->driver->owner);
+ free_tty_struct(o_tty);
+ kfree(tty->termios);
+ return -ENOMEM;
+}
+
+static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty)
+{
+ pty_count--;
+}
+
+static const struct tty_operations ptm_unix98_ops = {
+ .lookup = ptm_unix98_lookup,
+ .install = pty_unix98_install,
+ .remove = pty_unix98_remove,
.open = pty_open,
.close = pty_close,
.write = pty_write,
@@ -397,9 +560,89 @@
.chars_in_buffer = pty_chars_in_buffer,
.unthrottle = pty_unthrottle,
.set_termios = pty_set_termios,
- .ioctl = pty_unix98_ioctl
+ .ioctl = pty_unix98_ioctl,
+ .shutdown = pty_unix98_shutdown
};
+static const struct tty_operations pty_unix98_ops = {
+ .lookup = pts_unix98_lookup,
+ .install = pty_unix98_install,
+ .remove = pty_unix98_remove,
+ .open = pty_open,
+ .close = pty_close,
+ .write = pty_write,
+ .write_room = pty_write_room,
+ .flush_buffer = pty_flush_buffer,
+ .chars_in_buffer = pty_chars_in_buffer,
+ .unthrottle = pty_unthrottle,
+ .set_termios = pty_set_termios,
+ .shutdown = pty_unix98_shutdown
+};
+
+/**
+ * ptmx_open - open a unix 98 pty master
+ * @inode: inode of device file
+ * @filp: file pointer to tty
+ *
+ * Allocate a unix98 pty master device from the ptmx driver.
+ *
+ * Locking: tty_mutex protects the init_dev work. tty->count should
+ * protect the rest.
+ * allocated_ptys_lock handles the list of free pty numbers
+ */
+
+static int __ptmx_open(struct inode *inode, struct file *filp)
+{
+ struct tty_struct *tty;
+ int retval;
+ int index;
+
+ nonseekable_open(inode, filp);
+
+ /* find a device that is not in use. */
+ index = devpts_new_index(inode);
+ if (index < 0)
+ return index;
+
+ mutex_lock(&tty_mutex);
+ tty = tty_init_dev(ptm_driver, index, 1);
+ mutex_unlock(&tty_mutex);
+
+ if (IS_ERR(tty)) {
+ retval = PTR_ERR(tty);
+ goto out;
+ }
+
+ set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
+ filp->private_data = tty;
+ file_move(filp, &tty->tty_files);
+
+ retval = devpts_pty_new(inode, tty->link);
+ if (retval)
+ goto out1;
+
+ retval = ptm_driver->ops->open(tty, filp);
+ if (!retval)
+ return 0;
+out1:
+ tty_release_dev(filp);
+ return retval;
+out:
+ devpts_kill_index(inode, index);
+ return retval;
+}
+
+static int ptmx_open(struct inode *inode, struct file *filp)
+{
+ int ret;
+
+ lock_kernel();
+ ret = __ptmx_open(inode, filp);
+ unlock_kernel();
+ return ret;
+}
+
+static struct file_operations ptmx_fops;
static void __init unix98_pty_init(void)
{
@@ -427,7 +670,7 @@
ptm_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW |
TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM;
ptm_driver->other = pts_driver;
- tty_set_operations(ptm_driver, &pty_unix98_ops);
+ tty_set_operations(ptm_driver, &ptm_unix98_ops);
pts_driver->owner = THIS_MODULE;
pts_driver->driver_name = "pty_slave";
@@ -443,16 +686,26 @@
pts_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW |
TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM;
pts_driver->other = ptm_driver;
- tty_set_operations(pts_driver, &pty_ops);
-
+ tty_set_operations(pts_driver, &pty_unix98_ops);
+
if (tty_register_driver(ptm_driver))
panic("Couldn't register Unix98 ptm driver");
if (tty_register_driver(pts_driver))
panic("Couldn't register Unix98 pts driver");
- pty_table[1].data = &ptm_driver->refcount;
register_sysctl_table(pty_root_table);
+
+ /* Now create the /dev/ptmx special device */
+ tty_default_fops(&ptmx_fops);
+ ptmx_fops.open = ptmx_open;
+
+ cdev_init(&ptmx_cdev, &ptmx_fops);
+ if (cdev_add(&ptmx_cdev, MKDEV(TTYAUX_MAJOR, 2), 1) ||
+ register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, "/dev/ptmx") < 0)
+ panic("Couldn't register /dev/ptmx driver\n");
+ device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx");
}
+
#else
static inline void unix98_pty_init(void) { }
#endif
diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c
index f53d4d0..b47710c 100644
--- a/drivers/char/rtc.c
+++ b/drivers/char/rtc.c
@@ -88,12 +88,12 @@
#endif
#ifdef CONFIG_SPARC32
-#include <linux/pci.h>
-#include <linux/jiffies.h>
-#include <asm/ebus.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <asm/io.h>
static unsigned long rtc_port;
-static int rtc_irq = PCI_IRQ_NONE;
+static int rtc_irq;
#endif
#ifdef CONFIG_HPET_RTC_IRQ
@@ -973,8 +973,8 @@
char *guess = NULL;
#endif
#ifdef CONFIG_SPARC32
- struct linux_ebus *ebus;
- struct linux_ebus_device *edev;
+ struct device_node *ebus_dp;
+ struct of_device *op;
#else
void *r;
#ifdef RTC_IRQ
@@ -983,12 +983,16 @@
#endif
#ifdef CONFIG_SPARC32
- for_each_ebus(ebus) {
- for_each_ebusdev(edev, ebus) {
- if (strcmp(edev->prom_node->name, "rtc") == 0) {
- rtc_port = edev->resource[0].start;
- rtc_irq = edev->irqs[0];
- goto found;
+ for_each_node_by_name(ebus_dp, "ebus") {
+ struct device_node *dp;
+ for (dp = ebus_dp; dp; dp = dp->sibling) {
+ if (!strcmp(dp->name, "rtc")) {
+ op = of_find_device_by_node(dp);
+ if (op) {
+ rtc_port = op->resource[0].start;
+ rtc_irq = op->irqs[0];
+ goto found;
+ }
}
}
}
@@ -997,7 +1001,7 @@
return -EIO;
found:
- if (rtc_irq == PCI_IRQ_NONE) {
+ if (!rtc_irq) {
rtc_has_irq = 0;
goto no_irq;
}
diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c
index 19db1eb..8b8f07a 100644
--- a/drivers/char/stallion.c
+++ b/drivers/char/stallion.c
@@ -405,9 +405,9 @@
static int stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg);
static int stl_brdinit(struct stlbrd *brdp);
-static int stl_getportstats(struct stlport *portp, comstats_t __user *cp);
+static int stl_getportstats(struct tty_struct *tty, struct stlport *portp, comstats_t __user *cp);
static int stl_clrportstats(struct stlport *portp, comstats_t __user *cp);
-static int stl_waitcarrier(struct stlport *portp, struct file *filp);
+static int stl_waitcarrier(struct tty_struct *tty, struct stlport *portp, struct file *filp);
/*
* CD1400 uart specific handling functions.
@@ -612,8 +612,9 @@
static void stl_cd_change(struct stlport *portp)
{
unsigned int oldsigs = portp->sigs;
+ struct tty_struct *tty = tty_port_tty_get(&portp->port);
- if (!portp->port.tty)
+ if (!tty)
return;
portp->sigs = stl_getsignals(portp);
@@ -623,7 +624,8 @@
if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0))
if (portp->port.flags & ASYNC_CHECK_CD)
- tty_hangup(portp->port.tty);
+ tty_hangup(tty);
+ tty_kref_put(tty);
}
/*
@@ -734,7 +736,7 @@
* On the first open of the device setup the port hardware, and
* initialize the per port data structure.
*/
- portp->port.tty = tty;
+ tty_port_tty_set(&portp->port, tty);
tty->driver_data = portp;
portp->port.count++;
@@ -774,7 +776,7 @@
* then also we might have to wait for carrier.
*/
if (!(filp->f_flags & O_NONBLOCK))
- if ((rc = stl_waitcarrier(portp, filp)) != 0)
+ if ((rc = stl_waitcarrier(tty, portp, filp)) != 0)
return rc;
portp->port.flags |= ASYNC_NORMAL_ACTIVE;
@@ -789,7 +791,8 @@
* maybe because if we are clocal then we don't need to wait...
*/
-static int stl_waitcarrier(struct stlport *portp, struct file *filp)
+static int stl_waitcarrier(struct tty_struct *tty, struct stlport *portp,
+ struct file *filp)
{
unsigned long flags;
int rc, doclocal;
@@ -801,7 +804,7 @@
spin_lock_irqsave(&stallion_lock, flags);
- if (portp->port.tty->termios->c_cflag & CLOCAL)
+ if (tty->termios->c_cflag & CLOCAL)
doclocal++;
portp->openwaitcnt++;
@@ -846,8 +849,6 @@
pr_debug("stl_flushbuffer(tty=%p)\n", tty);
- if (tty == NULL)
- return;
portp = tty->driver_data;
if (portp == NULL)
return;
@@ -865,8 +866,6 @@
pr_debug("stl_waituntilsent(tty=%p,timeout=%d)\n", tty, timeout);
- if (tty == NULL)
- return;
portp = tty->driver_data;
if (portp == NULL)
return;
@@ -949,7 +948,7 @@
tty_ldisc_flush(tty);
tty->closing = 0;
- portp->port.tty = NULL;
+ tty_port_tty_set(&portp->port, NULL);
if (portp->openwaitcnt) {
if (portp->close_delay)
@@ -1033,8 +1032,6 @@
pr_debug("stl_putchar(tty=%p,ch=%x)\n", tty, ch);
- if (tty == NULL)
- return -EINVAL;
portp = tty->driver_data;
if (portp == NULL)
return -EINVAL;
@@ -1070,8 +1067,6 @@
pr_debug("stl_flushchars(tty=%p)\n", tty);
- if (tty == NULL)
- return;
portp = tty->driver_data;
if (portp == NULL)
return;
@@ -1090,8 +1085,6 @@
pr_debug("stl_writeroom(tty=%p)\n", tty);
- if (tty == NULL)
- return 0;
portp = tty->driver_data;
if (portp == NULL)
return 0;
@@ -1122,8 +1115,6 @@
pr_debug("stl_charsinbuffer(tty=%p)\n", tty);
- if (tty == NULL)
- return 0;
portp = tty->driver_data;
if (portp == NULL)
return 0;
@@ -1183,8 +1174,9 @@
* just quietly ignore any requests to change irq, etc.
*/
-static int stl_setserial(struct stlport *portp, struct serial_struct __user *sp)
+static int stl_setserial(struct tty_struct *tty, struct serial_struct __user *sp)
{
+ struct stlport * portp = tty->driver_data;
struct serial_struct sio;
pr_debug("stl_setserial(portp=%p,sp=%p)\n", portp, sp);
@@ -1205,7 +1197,7 @@
portp->close_delay = sio.close_delay;
portp->closing_wait = sio.closing_wait;
portp->custom_divisor = sio.custom_divisor;
- stl_setport(portp, portp->port.tty->termios);
+ stl_setport(portp, tty->termios);
return 0;
}
@@ -1215,8 +1207,6 @@
{
struct stlport *portp;
- if (tty == NULL)
- return -ENODEV;
portp = tty->driver_data;
if (portp == NULL)
return -ENODEV;
@@ -1232,8 +1222,6 @@
struct stlport *portp;
int rts = -1, dtr = -1;
- if (tty == NULL)
- return -ENODEV;
portp = tty->driver_data;
if (portp == NULL)
return -ENODEV;
@@ -1262,8 +1250,6 @@
pr_debug("stl_ioctl(tty=%p,file=%p,cmd=%x,arg=%lx)\n", tty, file, cmd,
arg);
- if (tty == NULL)
- return -ENODEV;
portp = tty->driver_data;
if (portp == NULL)
return -ENODEV;
@@ -1282,10 +1268,10 @@
rc = stl_getserial(portp, argp);
break;
case TIOCSSERIAL:
- rc = stl_setserial(portp, argp);
+ rc = stl_setserial(tty, argp);
break;
case COM_GETPORTSTATS:
- rc = stl_getportstats(portp, argp);
+ rc = stl_getportstats(tty, portp, argp);
break;
case COM_CLRPORTSTATS:
rc = stl_clrportstats(portp, argp);
@@ -1317,8 +1303,6 @@
pr_debug("stl_start(tty=%p)\n", tty);
- if (tty == NULL)
- return;
portp = tty->driver_data;
if (portp == NULL)
return;
@@ -1334,8 +1318,6 @@
pr_debug("stl_settermios(tty=%p,old=%p)\n", tty, old);
- if (tty == NULL)
- return;
portp = tty->driver_data;
if (portp == NULL)
return;
@@ -1369,8 +1351,6 @@
pr_debug("stl_throttle(tty=%p)\n", tty);
- if (tty == NULL)
- return;
portp = tty->driver_data;
if (portp == NULL)
return;
@@ -1389,8 +1369,6 @@
pr_debug("stl_unthrottle(tty=%p)\n", tty);
- if (tty == NULL)
- return;
portp = tty->driver_data;
if (portp == NULL)
return;
@@ -1410,8 +1388,6 @@
pr_debug("stl_stop(tty=%p)\n", tty);
- if (tty == NULL)
- return;
portp = tty->driver_data;
if (portp == NULL)
return;
@@ -1432,8 +1408,6 @@
pr_debug("stl_hangup(tty=%p)\n", tty);
- if (tty == NULL)
- return;
portp = tty->driver_data;
if (portp == NULL)
return;
@@ -1452,7 +1426,7 @@
portp->tx.head = NULL;
portp->tx.tail = NULL;
}
- portp->port.tty = NULL;
+ tty_port_tty_set(&portp->port, NULL);
portp->port.flags &= ~ASYNC_NORMAL_ACTIVE;
portp->port.count = 0;
wake_up_interruptible(&portp->port.open_wait);
@@ -1466,8 +1440,6 @@
pr_debug("stl_breakctl(tty=%p,state=%d)\n", tty, state);
- if (tty == NULL)
- return -EINVAL;
portp = tty->driver_data;
if (portp == NULL)
return -EINVAL;
@@ -1484,8 +1456,6 @@
pr_debug("stl_sendxchar(tty=%p,ch=%x)\n", tty, ch);
- if (tty == NULL)
- return;
portp = tty->driver_data;
if (portp == NULL)
return;
@@ -1805,7 +1775,7 @@
"(size=%Zd)\n", sizeof(struct stlport));
break;
}
-
+ tty_port_init(&portp->port);
portp->magic = STL_PORTMAGIC;
portp->portnr = i;
portp->brdnr = panelp->brdnr;
@@ -1832,6 +1802,7 @@
struct stlpanel *panelp;
struct stlport *portp;
unsigned int j, k;
+ struct tty_struct *tty;
for (j = 0; j < STL_MAXPANELS; j++) {
panelp = brdp->panels[j];
@@ -1841,8 +1812,11 @@
portp = panelp->ports[k];
if (portp == NULL)
continue;
- if (portp->port.tty != NULL)
- stl_hangup(portp->port.tty);
+ tty = tty_port_tty_get(&portp->port);
+ if (tty != NULL) {
+ stl_hangup(tty);
+ tty_kref_put(tty);
+ }
kfree(portp->tx.buf);
kfree(portp);
}
@@ -2498,7 +2472,7 @@
* what port to get stats for (used through board control device).
*/
-static int stl_getportstats(struct stlport *portp, comstats_t __user *cp)
+static int stl_getportstats(struct tty_struct *tty, struct stlport *portp, comstats_t __user *cp)
{
comstats_t stl_comstats;
unsigned char *head, *tail;
@@ -2525,18 +2499,17 @@
portp->stats.rxbuffered = 0;
spin_lock_irqsave(&stallion_lock, flags);
- if (portp->port.tty != NULL)
- if (portp->port.tty->driver_data == portp) {
- portp->stats.ttystate = portp->port.tty->flags;
- /* No longer available as a statistic */
- portp->stats.rxbuffered = 1; /*portp->port.tty->flip.count; */
- if (portp->port.tty->termios != NULL) {
- portp->stats.cflags = portp->port.tty->termios->c_cflag;
- portp->stats.iflags = portp->port.tty->termios->c_iflag;
- portp->stats.oflags = portp->port.tty->termios->c_oflag;
- portp->stats.lflags = portp->port.tty->termios->c_lflag;
- }
+ if (tty != NULL && portp->port.tty == tty) {
+ portp->stats.ttystate = tty->flags;
+ /* No longer available as a statistic */
+ portp->stats.rxbuffered = 1; /*tty->flip.count; */
+ if (tty->termios != NULL) {
+ portp->stats.cflags = tty->termios->c_cflag;
+ portp->stats.iflags = tty->termios->c_iflag;
+ portp->stats.oflags = tty->termios->c_oflag;
+ portp->stats.lflags = tty->termios->c_lflag;
}
+ }
spin_unlock_irqrestore(&stallion_lock, flags);
head = portp->tx.head;
@@ -2640,7 +2613,7 @@
switch (cmd) {
case COM_GETPORTSTATS:
- rc = stl_getportstats(NULL, argp);
+ rc = stl_getportstats(NULL, NULL, argp);
break;
case COM_CLRPORTSTATS:
rc = stl_clrportstats(NULL, argp);
@@ -3243,7 +3216,7 @@
if (portp == NULL)
return;
- tty = portp->port.tty;
+ tty = tty_port_tty_get(&portp->port);
if (tty == NULL)
return;
@@ -3288,6 +3261,7 @@
BRDDISABLE(portp->brdnr);
spin_unlock_irqrestore(&brd_lock, flags);
+ tty_kref_put(tty);
}
/*****************************************************************************/
@@ -3305,7 +3279,7 @@
if (portp == NULL)
return;
- tty = portp->port.tty;
+ tty = tty_port_tty_get(&portp->port);
if (tty == NULL)
return;
@@ -3325,6 +3299,7 @@
}
BRDDISABLE(portp->brdnr);
spin_unlock_irqrestore(&brd_lock, flags);
+ tty_kref_put(tty);
}
/*****************************************************************************/
@@ -3478,6 +3453,7 @@
int len, stlen;
char *head, *tail;
unsigned char ioack, srer;
+ struct tty_struct *tty;
pr_debug("stl_cd1400txisr(panelp=%p,ioaddr=%x)\n", panelp, ioaddr);
@@ -3504,8 +3480,11 @@
if ((len == 0) || ((len < STL_TXBUFLOW) &&
(test_bit(ASYI_TXLOW, &portp->istate) == 0))) {
set_bit(ASYI_TXLOW, &portp->istate);
- if (portp->port.tty)
- tty_wakeup(portp->port.tty);
+ tty = tty_port_tty_get(&portp->port);
+ if (tty) {
+ tty_wakeup(tty);
+ tty_kref_put(tty);
+ }
}
if (len == 0) {
@@ -3569,7 +3548,7 @@
return;
}
portp = panelp->ports[(ioack >> 3)];
- tty = portp->port.tty;
+ tty = tty_port_tty_get(&portp->port);
if ((ioack & ACK_TYPMASK) == ACK_TYPRXGOOD) {
outb((RDCR + portp->uartaddr), ioaddr);
@@ -3633,10 +3612,12 @@
}
} else {
printk("STALLION: bad RX interrupt ack value=%x\n", ioack);
+ tty_kref_put(tty);
return;
}
stl_rxalldone:
+ tty_kref_put(tty);
outb((EOSRR + portp->uartaddr), ioaddr);
outb(0, (ioaddr + EREG_DATA));
}
@@ -4175,7 +4156,7 @@
if (portp == NULL)
return;
- tty = portp->port.tty;
+ tty = tty_port_tty_get(&portp->port);
if (tty == NULL)
return;
@@ -4226,6 +4207,7 @@
BRDDISABLE(portp->brdnr);
spin_unlock_irqrestore(&brd_lock, flags);
+ tty_kref_put(tty);
}
/*****************************************************************************/
@@ -4244,7 +4226,7 @@
if (portp == NULL)
return;
- tty = portp->port.tty;
+ tty = tty_port_tty_get(&portp->port);
if (tty == NULL)
return;
@@ -4269,6 +4251,7 @@
}
BRDDISABLE(portp->brdnr);
spin_unlock_irqrestore(&brd_lock, flags);
+ tty_kref_put(tty);
}
/*****************************************************************************/
@@ -4408,6 +4391,7 @@
static void stl_sc26198txisr(struct stlport *portp)
{
+ struct tty_struct *tty;
unsigned int ioaddr;
unsigned char mr0;
int len, stlen;
@@ -4422,8 +4406,11 @@
if ((len == 0) || ((len < STL_TXBUFLOW) &&
(test_bit(ASYI_TXLOW, &portp->istate) == 0))) {
set_bit(ASYI_TXLOW, &portp->istate);
- if (portp->port.tty)
- tty_wakeup(portp->port.tty);
+ tty = tty_port_tty_get(&portp->port);
+ if (tty) {
+ tty_wakeup(tty);
+ tty_kref_put(tty);
+ }
}
if (len == 0) {
@@ -4476,7 +4463,7 @@
pr_debug("stl_sc26198rxisr(portp=%p,iack=%x)\n", portp, iack);
- tty = portp->port.tty;
+ tty = tty_port_tty_get(&portp->port);
ioaddr = portp->ioaddr;
outb(GIBCR, (ioaddr + XP_ADDR));
len = inb(ioaddr + XP_DATA) + 1;
@@ -4515,6 +4502,7 @@
stl_sc26198txunflow(portp, tty);
}
}
+ tty_kref_put(tty);
}
/*****************************************************************************/
@@ -4528,7 +4516,7 @@
struct tty_struct *tty;
unsigned int ioaddr;
- tty = portp->port.tty;
+ tty = tty_port_tty_get(&portp->port);
ioaddr = portp->ioaddr;
if (status & SR_RXPARITY)
@@ -4566,6 +4554,7 @@
if (status == 0)
portp->stats.rxtotal++;
}
+ tty_kref_put(tty);
}
/*****************************************************************************/
diff --git a/drivers/char/sx.c b/drivers/char/sx.c
index c385206..5b8d7a1 100644
--- a/drivers/char/sx.c
+++ b/drivers/char/sx.c
@@ -2504,7 +2504,7 @@
del_timer(&board->timer);
if (pdev) {
#ifdef CONFIG_PCI
- pci_iounmap(pdev, board->base);
+ pci_iounmap(pdev, board->base2);
pci_release_region(pdev, IS_CF_BOARD(board) ? 3 : 2);
#endif
} else {
@@ -2703,7 +2703,7 @@
return 0;
err_unmap:
- pci_iounmap(pdev, board->base);
+ pci_iounmap(pdev, board->base2);
err_reg:
pci_release_region(pdev, reg);
err_flag:
diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c
index ae766d8..1fee703 100644
--- a/drivers/char/tpm/tpm.c
+++ b/drivers/char/tpm/tpm.c
@@ -954,72 +954,63 @@
/*
* Device file system interface to the TPM
+ *
+ * It's assured that the chip will be opened just once,
+ * by the check of is_open variable, which is protected
+ * by driver_lock.
*/
int tpm_open(struct inode *inode, struct file *file)
{
- int rc = 0, minor = iminor(inode);
+ int minor = iminor(inode);
struct tpm_chip *chip = NULL, *pos;
- lock_kernel();
- spin_lock(&driver_lock);
-
- list_for_each_entry(pos, &tpm_chip_list, list) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(pos, &tpm_chip_list, list) {
if (pos->vendor.miscdev.minor == minor) {
chip = pos;
+ get_device(chip->dev);
break;
}
}
+ rcu_read_unlock();
- if (chip == NULL) {
- rc = -ENODEV;
- goto err_out;
- }
+ if (!chip)
+ return -ENODEV;
- if (chip->num_opens) {
+ if (test_and_set_bit(0, &chip->is_open)) {
dev_dbg(chip->dev, "Another process owns this TPM\n");
- rc = -EBUSY;
- goto err_out;
+ put_device(chip->dev);
+ return -EBUSY;
}
- chip->num_opens++;
- get_device(chip->dev);
-
- spin_unlock(&driver_lock);
-
chip->data_buffer = kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL);
if (chip->data_buffer == NULL) {
- chip->num_opens--;
+ clear_bit(0, &chip->is_open);
put_device(chip->dev);
- unlock_kernel();
return -ENOMEM;
}
atomic_set(&chip->data_pending, 0);
file->private_data = chip;
- unlock_kernel();
return 0;
-
-err_out:
- spin_unlock(&driver_lock);
- unlock_kernel();
- return rc;
}
EXPORT_SYMBOL_GPL(tpm_open);
+/*
+ * Called on file close
+ */
int tpm_release(struct inode *inode, struct file *file)
{
struct tpm_chip *chip = file->private_data;
- flush_scheduled_work();
- spin_lock(&driver_lock);
- file->private_data = NULL;
del_singleshot_timer_sync(&chip->user_read_timer);
+ flush_scheduled_work();
+ file->private_data = NULL;
atomic_set(&chip->data_pending, 0);
- chip->num_opens--;
- put_device(chip->dev);
kfree(chip->data_buffer);
- spin_unlock(&driver_lock);
+ clear_bit(0, &chip->is_open);
+ put_device(chip->dev);
return 0;
}
EXPORT_SYMBOL_GPL(tpm_release);
@@ -1093,13 +1084,11 @@
}
spin_lock(&driver_lock);
-
- list_del(&chip->list);
-
+ list_del_rcu(&chip->list);
spin_unlock(&driver_lock);
+ synchronize_rcu();
misc_deregister(&chip->vendor.miscdev);
-
sysfs_remove_group(&dev->kobj, chip->vendor.attr_group);
tpm_bios_log_teardown(chip->bios_dir);
@@ -1144,25 +1133,33 @@
}
EXPORT_SYMBOL_GPL(tpm_pm_resume);
+/* In case vendor provided release function, call it too.*/
+
+void tpm_dev_vendor_release(struct tpm_chip *chip)
+{
+ if (chip->vendor.release)
+ chip->vendor.release(chip->dev);
+
+ clear_bit(chip->dev_num, dev_mask);
+ kfree(chip->vendor.miscdev.name);
+}
+EXPORT_SYMBOL_GPL(tpm_dev_vendor_release);
+
+
/*
* Once all references to platform device are down to 0,
* release all allocated structures.
- * In case vendor provided release function,
- * call it too.
*/
static void tpm_dev_release(struct device *dev)
{
struct tpm_chip *chip = dev_get_drvdata(dev);
- if (chip->vendor.release)
- chip->vendor.release(dev);
+ tpm_dev_vendor_release(chip);
chip->release(dev);
-
- clear_bit(chip->dev_num, dev_mask);
- kfree(chip->vendor.miscdev.name);
kfree(chip);
}
+EXPORT_SYMBOL_GPL(tpm_dev_release);
/*
* Called from tpm_<specific>.c probe function only for devices
@@ -1171,8 +1168,8 @@
* upon errant exit from this function specific probe function should call
* pci_disable_device
*/
-struct tpm_chip *tpm_register_hardware(struct device *dev, const struct tpm_vendor_specific
- *entry)
+struct tpm_chip *tpm_register_hardware(struct device *dev,
+ const struct tpm_vendor_specific *entry)
{
#define DEVNAME_SIZE 7
@@ -1231,21 +1228,20 @@
return NULL;
}
- spin_lock(&driver_lock);
-
- list_add(&chip->list, &tpm_chip_list);
-
- spin_unlock(&driver_lock);
-
if (sysfs_create_group(&dev->kobj, chip->vendor.attr_group)) {
- list_del(&chip->list);
misc_deregister(&chip->vendor.miscdev);
put_device(chip->dev);
+
return NULL;
}
chip->bios_dir = tpm_bios_log_setup(devname);
+ /* Make chip available */
+ spin_lock(&driver_lock);
+ list_add_rcu(&chip->list, &tpm_chip_list);
+ spin_unlock(&driver_lock);
+
return chip;
}
EXPORT_SYMBOL_GPL(tpm_register_hardware);
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index e885148..8e30df4 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -90,7 +90,7 @@
struct device *dev; /* Device stuff */
int dev_num; /* /dev/tpm# */
- int num_opens; /* only one allowed */
+ unsigned long is_open; /* only one allowed */
int time_expired;
/* Data passed to and from the tpm via the read/write calls */
@@ -132,6 +132,7 @@
const struct tpm_vendor_specific *);
extern int tpm_open(struct inode *, struct file *);
extern int tpm_release(struct inode *, struct file *);
+extern void tpm_dev_vendor_release(struct tpm_chip *);
extern ssize_t tpm_write(struct file *, const char __user *, size_t,
loff_t *);
extern ssize_t tpm_read(struct file *, char __user *, size_t, loff_t *);
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index ed1879c..717af7a 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -630,12 +630,23 @@
{"", 0} /* Terminator */
};
+static __devexit void tpm_tis_pnp_remove(struct pnp_dev *dev)
+{
+ struct tpm_chip *chip = pnp_get_drvdata(dev);
+
+ tpm_dev_vendor_release(chip);
+
+ kfree(chip);
+}
+
+
static struct pnp_driver tis_pnp_driver = {
.name = "tpm_tis",
.id_table = tpm_pnp_tbl,
.probe = tpm_tis_pnp_init,
.suspend = tpm_tis_pnp_suspend,
.resume = tpm_tis_pnp_resume,
+ .remove = tpm_tis_pnp_remove,
};
#define TIS_HID_USR_IDX sizeof(tpm_pnp_tbl)/sizeof(struct pnp_device_id) -2
@@ -683,6 +694,7 @@
spin_lock(&tis_lock);
list_for_each_entry_safe(i, j, &tis_chips, list) {
chip = to_tpm_chip(i);
+ tpm_remove_hardware(chip->dev);
iowrite32(~TPM_GLOBAL_INT_ENABLE &
ioread32(chip->vendor.iobase +
TPM_INT_ENABLE(chip->vendor.
@@ -694,9 +706,9 @@
free_irq(chip->vendor.irq, chip);
iounmap(i->iobase);
list_del(&i->list);
- tpm_remove_hardware(chip->dev);
}
spin_unlock(&tis_lock);
+
if (force) {
platform_device_unregister(pdev);
driver_unregister(&tis_drv);
diff --git a/drivers/char/tty_audit.c b/drivers/char/tty_audit.c
index 3582f43..5787249 100644
--- a/drivers/char/tty_audit.c
+++ b/drivers/char/tty_audit.c
@@ -93,7 +93,7 @@
get_task_comm(name, tsk);
audit_log_untrustedstring(ab, name);
audit_log_format(ab, " data=");
- audit_log_n_untrustedstring(ab, buf->data, buf->valid);
+ audit_log_n_hex(ab, buf->data, buf->valid);
audit_log_end(ab);
}
buf->valid = 0;
diff --git a/drivers/char/tty_buffer.c b/drivers/char/tty_buffer.c
new file mode 100644
index 0000000..810ee25
--- /dev/null
+++ b/drivers/char/tty_buffer.c
@@ -0,0 +1,511 @@
+/*
+ * Tty buffer allocation management
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+
+/**
+ * tty_buffer_free_all - free buffers used by a tty
+ * @tty: tty to free from
+ *
+ * Remove all the buffers pending on a tty whether queued with data
+ * or in the free ring. Must be called when the tty is no longer in use
+ *
+ * Locking: none
+ */
+
+void tty_buffer_free_all(struct tty_struct *tty)
+{
+ struct tty_buffer *thead;
+ while ((thead = tty->buf.head) != NULL) {
+ tty->buf.head = thead->next;
+ kfree(thead);
+ }
+ while ((thead = tty->buf.free) != NULL) {
+ tty->buf.free = thead->next;
+ kfree(thead);
+ }
+ tty->buf.tail = NULL;
+ tty->buf.memory_used = 0;
+}
+
+/**
+ * tty_buffer_alloc - allocate a tty buffer
+ * @tty: tty device
+ * @size: desired size (characters)
+ *
+ * Allocate a new tty buffer to hold the desired number of characters.
+ * Return NULL if out of memory or the allocation would exceed the
+ * per device queue
+ *
+ * Locking: Caller must hold tty->buf.lock
+ */
+
+static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size)
+{
+ struct tty_buffer *p;
+
+ if (tty->buf.memory_used + size > 65536)
+ return NULL;
+ p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC);
+ if (p == NULL)
+ return NULL;
+ p->used = 0;
+ p->size = size;
+ p->next = NULL;
+ p->commit = 0;
+ p->read = 0;
+ p->char_buf_ptr = (char *)(p->data);
+ p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size;
+ tty->buf.memory_used += size;
+ return p;
+}
+
+/**
+ * tty_buffer_free - free a tty buffer
+ * @tty: tty owning the buffer
+ * @b: the buffer to free
+ *
+ * Free a tty buffer, or add it to the free list according to our
+ * internal strategy
+ *
+ * Locking: Caller must hold tty->buf.lock
+ */
+
+static void tty_buffer_free(struct tty_struct *tty, struct tty_buffer *b)
+{
+ /* Dumb strategy for now - should keep some stats */
+ tty->buf.memory_used -= b->size;
+ WARN_ON(tty->buf.memory_used < 0);
+
+ if (b->size >= 512)
+ kfree(b);
+ else {
+ b->next = tty->buf.free;
+ tty->buf.free = b;
+ }
+}
+
+/**
+ * __tty_buffer_flush - flush full tty buffers
+ * @tty: tty to flush
+ *
+ * flush all the buffers containing receive data. Caller must
+ * hold the buffer lock and must have ensured no parallel flush to
+ * ldisc is running.
+ *
+ * Locking: Caller must hold tty->buf.lock
+ */
+
+static void __tty_buffer_flush(struct tty_struct *tty)
+{
+ struct tty_buffer *thead;
+
+ while ((thead = tty->buf.head) != NULL) {
+ tty->buf.head = thead->next;
+ tty_buffer_free(tty, thead);
+ }
+ tty->buf.tail = NULL;
+}
+
+/**
+ * tty_buffer_flush - flush full tty buffers
+ * @tty: tty to flush
+ *
+ * flush all the buffers containing receive data. If the buffer is
+ * being processed by flush_to_ldisc then we defer the processing
+ * to that function
+ *
+ * Locking: none
+ */
+
+void tty_buffer_flush(struct tty_struct *tty)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&tty->buf.lock, flags);
+
+ /* If the data is being pushed to the tty layer then we can't
+ process it here. Instead set a flag and the flush_to_ldisc
+ path will process the flush request before it exits */
+ if (test_bit(TTY_FLUSHING, &tty->flags)) {
+ set_bit(TTY_FLUSHPENDING, &tty->flags);
+ spin_unlock_irqrestore(&tty->buf.lock, flags);
+ wait_event(tty->read_wait,
+ test_bit(TTY_FLUSHPENDING, &tty->flags) == 0);
+ return;
+ } else
+ __tty_buffer_flush(tty);
+ spin_unlock_irqrestore(&tty->buf.lock, flags);
+}
+
+/**
+ * tty_buffer_find - find a free tty buffer
+ * @tty: tty owning the buffer
+ * @size: characters wanted
+ *
+ * Locate an existing suitable tty buffer or if we are lacking one then
+ * allocate a new one. We round our buffers off in 256 character chunks
+ * to get better allocation behaviour.
+ *
+ * Locking: Caller must hold tty->buf.lock
+ */
+
+static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size)
+{
+ struct tty_buffer **tbh = &tty->buf.free;
+ while ((*tbh) != NULL) {
+ struct tty_buffer *t = *tbh;
+ if (t->size >= size) {
+ *tbh = t->next;
+ t->next = NULL;
+ t->used = 0;
+ t->commit = 0;
+ t->read = 0;
+ tty->buf.memory_used += t->size;
+ return t;
+ }
+ tbh = &((*tbh)->next);
+ }
+ /* Round the buffer size out */
+ size = (size + 0xFF) & ~0xFF;
+ return tty_buffer_alloc(tty, size);
+ /* Should possibly check if this fails for the largest buffer we
+ have queued and recycle that ? */
+}
+
+/**
+ * tty_buffer_request_room - grow tty buffer if needed
+ * @tty: tty structure
+ * @size: size desired
+ *
+ * Make at least size bytes of linear space available for the tty
+ * buffer. If we fail return the size we managed to find.
+ *
+ * Locking: Takes tty->buf.lock
+ */
+int tty_buffer_request_room(struct tty_struct *tty, size_t size)
+{
+ struct tty_buffer *b, *n;
+ int left;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tty->buf.lock, flags);
+
+ /* OPTIMISATION: We could keep a per tty "zero" sized buffer to
+ remove this conditional if its worth it. This would be invisible
+ to the callers */
+ if ((b = tty->buf.tail) != NULL)
+ left = b->size - b->used;
+ else
+ left = 0;
+
+ if (left < size) {
+ /* This is the slow path - looking for new buffers to use */
+ if ((n = tty_buffer_find(tty, size)) != NULL) {
+ if (b != NULL) {
+ b->next = n;
+ b->commit = b->used;
+ } else
+ tty->buf.head = n;
+ tty->buf.tail = n;
+ } else
+ size = left;
+ }
+
+ spin_unlock_irqrestore(&tty->buf.lock, flags);
+ return size;
+}
+EXPORT_SYMBOL_GPL(tty_buffer_request_room);
+
+/**
+ * tty_insert_flip_string - Add characters to the tty buffer
+ * @tty: tty structure
+ * @chars: characters
+ * @size: size
+ *
+ * Queue a series of bytes to the tty buffering. All the characters
+ * passed are marked as without error. Returns the number added.
+ *
+ * Locking: Called functions may take tty->buf.lock
+ */
+
+int tty_insert_flip_string(struct tty_struct *tty, const unsigned char *chars,
+ size_t size)
+{
+ int copied = 0;
+ do {
+ int space = tty_buffer_request_room(tty, size - copied);
+ struct tty_buffer *tb = tty->buf.tail;
+ /* If there is no space then tb may be NULL */
+ if (unlikely(space == 0))
+ break;
+ memcpy(tb->char_buf_ptr + tb->used, chars, space);
+ memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space);
+ tb->used += space;
+ copied += space;
+ chars += space;
+ /* There is a small chance that we need to split the data over
+ several buffers. If this is the case we must loop */
+ } while (unlikely(size > copied));
+ return copied;
+}
+EXPORT_SYMBOL(tty_insert_flip_string);
+
+/**
+ * tty_insert_flip_string_flags - Add characters to the tty buffer
+ * @tty: tty structure
+ * @chars: characters
+ * @flags: flag bytes
+ * @size: size
+ *
+ * Queue a series of bytes to the tty buffering. For each character
+ * the flags array indicates the status of the character. Returns the
+ * number added.
+ *
+ * Locking: Called functions may take tty->buf.lock
+ */
+
+int tty_insert_flip_string_flags(struct tty_struct *tty,
+ const unsigned char *chars, const char *flags, size_t size)
+{
+ int copied = 0;
+ do {
+ int space = tty_buffer_request_room(tty, size - copied);
+ struct tty_buffer *tb = tty->buf.tail;
+ /* If there is no space then tb may be NULL */
+ if (unlikely(space == 0))
+ break;
+ memcpy(tb->char_buf_ptr + tb->used, chars, space);
+ memcpy(tb->flag_buf_ptr + tb->used, flags, space);
+ tb->used += space;
+ copied += space;
+ chars += space;
+ flags += space;
+ /* There is a small chance that we need to split the data over
+ several buffers. If this is the case we must loop */
+ } while (unlikely(size > copied));
+ return copied;
+}
+EXPORT_SYMBOL(tty_insert_flip_string_flags);
+
+/**
+ * tty_schedule_flip - push characters to ldisc
+ * @tty: tty to push from
+ *
+ * Takes any pending buffers and transfers their ownership to the
+ * ldisc side of the queue. It then schedules those characters for
+ * processing by the line discipline.
+ *
+ * Locking: Takes tty->buf.lock
+ */
+
+void tty_schedule_flip(struct tty_struct *tty)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&tty->buf.lock, flags);
+ if (tty->buf.tail != NULL)
+ tty->buf.tail->commit = tty->buf.tail->used;
+ spin_unlock_irqrestore(&tty->buf.lock, flags);
+ schedule_delayed_work(&tty->buf.work, 1);
+}
+EXPORT_SYMBOL(tty_schedule_flip);
+
+/**
+ * tty_prepare_flip_string - make room for characters
+ * @tty: tty
+ * @chars: return pointer for character write area
+ * @size: desired size
+ *
+ * Prepare a block of space in the buffer for data. Returns the length
+ * available and buffer pointer to the space which is now allocated and
+ * accounted for as ready for normal characters. This is used for drivers
+ * that need their own block copy routines into the buffer. There is no
+ * guarantee the buffer is a DMA target!
+ *
+ * Locking: May call functions taking tty->buf.lock
+ */
+
+int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars,
+ size_t size)
+{
+ int space = tty_buffer_request_room(tty, size);
+ if (likely(space)) {
+ struct tty_buffer *tb = tty->buf.tail;
+ *chars = tb->char_buf_ptr + tb->used;
+ memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space);
+ tb->used += space;
+ }
+ return space;
+}
+EXPORT_SYMBOL_GPL(tty_prepare_flip_string);
+
+/**
+ * tty_prepare_flip_string_flags - make room for characters
+ * @tty: tty
+ * @chars: return pointer for character write area
+ * @flags: return pointer for status flag write area
+ * @size: desired size
+ *
+ * Prepare a block of space in the buffer for data. Returns the length
+ * available and buffer pointer to the space which is now allocated and
+ * accounted for as ready for characters. This is used for drivers
+ * that need their own block copy routines into the buffer. There is no
+ * guarantee the buffer is a DMA target!
+ *
+ * Locking: May call functions taking tty->buf.lock
+ */
+
+int tty_prepare_flip_string_flags(struct tty_struct *tty,
+ unsigned char **chars, char **flags, size_t size)
+{
+ int space = tty_buffer_request_room(tty, size);
+ if (likely(space)) {
+ struct tty_buffer *tb = tty->buf.tail;
+ *chars = tb->char_buf_ptr + tb->used;
+ *flags = tb->flag_buf_ptr + tb->used;
+ tb->used += space;
+ }
+ return space;
+}
+EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags);
+
+
+
+/**
+ * flush_to_ldisc
+ * @work: tty structure passed from work queue.
+ *
+ * This routine is called out of the software interrupt to flush data
+ * from the buffer chain to the line discipline.
+ *
+ * Locking: holds tty->buf.lock to guard buffer list. Drops the lock
+ * while invoking the line discipline receive_buf method. The
+ * receive_buf method is single threaded for each tty instance.
+ */
+
+static void flush_to_ldisc(struct work_struct *work)
+{
+ struct tty_struct *tty =
+ container_of(work, struct tty_struct, buf.work.work);
+ unsigned long flags;
+ struct tty_ldisc *disc;
+ struct tty_buffer *tbuf, *head;
+ char *char_buf;
+ unsigned char *flag_buf;
+
+ disc = tty_ldisc_ref(tty);
+ if (disc == NULL) /* !TTY_LDISC */
+ return;
+
+ spin_lock_irqsave(&tty->buf.lock, flags);
+ /* So we know a flush is running */
+ set_bit(TTY_FLUSHING, &tty->flags);
+ head = tty->buf.head;
+ if (head != NULL) {
+ tty->buf.head = NULL;
+ for (;;) {
+ int count = head->commit - head->read;
+ if (!count) {
+ if (head->next == NULL)
+ break;
+ tbuf = head;
+ head = head->next;
+ tty_buffer_free(tty, tbuf);
+ continue;
+ }
+ /* Ldisc or user is trying to flush the buffers
+ we are feeding to the ldisc, stop feeding the
+ line discipline as we want to empty the queue */
+ if (test_bit(TTY_FLUSHPENDING, &tty->flags))
+ break;
+ if (!tty->receive_room) {
+ schedule_delayed_work(&tty->buf.work, 1);
+ break;
+ }
+ if (count > tty->receive_room)
+ count = tty->receive_room;
+ char_buf = head->char_buf_ptr + head->read;
+ flag_buf = head->flag_buf_ptr + head->read;
+ head->read += count;
+ spin_unlock_irqrestore(&tty->buf.lock, flags);
+ disc->ops->receive_buf(tty, char_buf,
+ flag_buf, count);
+ spin_lock_irqsave(&tty->buf.lock, flags);
+ }
+ /* Restore the queue head */
+ tty->buf.head = head;
+ }
+ /* We may have a deferred request to flush the input buffer,
+ if so pull the chain under the lock and empty the queue */
+ if (test_bit(TTY_FLUSHPENDING, &tty->flags)) {
+ __tty_buffer_flush(tty);
+ clear_bit(TTY_FLUSHPENDING, &tty->flags);
+ wake_up(&tty->read_wait);
+ }
+ clear_bit(TTY_FLUSHING, &tty->flags);
+ spin_unlock_irqrestore(&tty->buf.lock, flags);
+
+ tty_ldisc_deref(disc);
+}
+
+/**
+ * tty_flip_buffer_push - terminal
+ * @tty: tty to push
+ *
+ * Queue a push of the terminal flip buffers to the line discipline. This
+ * function must not be called from IRQ context if tty->low_latency is set.
+ *
+ * In the event of the queue being busy for flipping the work will be
+ * held off and retried later.
+ *
+ * Locking: tty buffer lock. Driver locks in low latency mode.
+ */
+
+void tty_flip_buffer_push(struct tty_struct *tty)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&tty->buf.lock, flags);
+ if (tty->buf.tail != NULL)
+ tty->buf.tail->commit = tty->buf.tail->used;
+ spin_unlock_irqrestore(&tty->buf.lock, flags);
+
+ if (tty->low_latency)
+ flush_to_ldisc(&tty->buf.work.work);
+ else
+ schedule_delayed_work(&tty->buf.work, 1);
+}
+EXPORT_SYMBOL(tty_flip_buffer_push);
+
+/**
+ * tty_buffer_init - prepare a tty buffer structure
+ * @tty: tty to initialise
+ *
+ * Set up the initial state of the buffer management for a tty device.
+ * Must be called before the other tty buffer functions are used.
+ *
+ * Locking: none
+ */
+
+void tty_buffer_init(struct tty_struct *tty)
+{
+ spin_lock_init(&tty->buf.lock);
+ tty->buf.head = NULL;
+ tty->buf.tail = NULL;
+ tty->buf.free = NULL;
+ tty->buf.memory_used = 0;
+ INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc);
+}
+
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index e4dce87..7053d63 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -49,7 +49,7 @@
* implement CONFIG_VT and generalize console device interface.
* -- Marko Kohtala <Marko.Kohtala@hut.fi>, March 97
*
- * Rewrote init_dev and release_dev to eliminate races.
+ * Rewrote tty_init_dev and tty_release_dev to eliminate races.
* -- Bill Hawes <whawes@star.net>, June 97
*
* Added devfs support.
@@ -136,13 +136,6 @@
DEFINE_MUTEX(tty_mutex);
EXPORT_SYMBOL(tty_mutex);
-#ifdef CONFIG_UNIX98_PTYS
-extern struct tty_driver *ptm_driver; /* Unix98 pty masters; for /dev/ptmx */
-static int ptmx_open(struct inode *, struct file *);
-#endif
-
-static void initialize_tty_struct(struct tty_struct *tty);
-
static ssize_t tty_read(struct file *, char __user *, size_t, loff_t *);
static ssize_t tty_write(struct file *, const char __user *, size_t, loff_t *);
ssize_t redirected_tty_write(struct file *, const char __user *,
@@ -171,13 +164,11 @@
* Locking: none
*/
-static struct tty_struct *alloc_tty_struct(void)
+struct tty_struct *alloc_tty_struct(void)
{
return kzalloc(sizeof(struct tty_struct), GFP_KERNEL);
}
-static void tty_buffer_free_all(struct tty_struct *);
-
/**
* free_tty_struct - free a disused tty
* @tty: tty struct to free
@@ -187,7 +178,7 @@
* Locking: none. Must be called after tty is definitely unused
*/
-static inline void free_tty_struct(struct tty_struct *tty)
+void free_tty_struct(struct tty_struct *tty)
{
kfree(tty->write_buf);
tty_buffer_free_all(tty);
@@ -263,398 +254,6 @@
return 0;
}
-/*
- * Tty buffer allocation management
- */
-
-/**
- * tty_buffer_free_all - free buffers used by a tty
- * @tty: tty to free from
- *
- * Remove all the buffers pending on a tty whether queued with data
- * or in the free ring. Must be called when the tty is no longer in use
- *
- * Locking: none
- */
-
-static void tty_buffer_free_all(struct tty_struct *tty)
-{
- struct tty_buffer *thead;
- while ((thead = tty->buf.head) != NULL) {
- tty->buf.head = thead->next;
- kfree(thead);
- }
- while ((thead = tty->buf.free) != NULL) {
- tty->buf.free = thead->next;
- kfree(thead);
- }
- tty->buf.tail = NULL;
- tty->buf.memory_used = 0;
-}
-
-/**
- * tty_buffer_init - prepare a tty buffer structure
- * @tty: tty to initialise
- *
- * Set up the initial state of the buffer management for a tty device.
- * Must be called before the other tty buffer functions are used.
- *
- * Locking: none
- */
-
-static void tty_buffer_init(struct tty_struct *tty)
-{
- spin_lock_init(&tty->buf.lock);
- tty->buf.head = NULL;
- tty->buf.tail = NULL;
- tty->buf.free = NULL;
- tty->buf.memory_used = 0;
-}
-
-/**
- * tty_buffer_alloc - allocate a tty buffer
- * @tty: tty device
- * @size: desired size (characters)
- *
- * Allocate a new tty buffer to hold the desired number of characters.
- * Return NULL if out of memory or the allocation would exceed the
- * per device queue
- *
- * Locking: Caller must hold tty->buf.lock
- */
-
-static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size)
-{
- struct tty_buffer *p;
-
- if (tty->buf.memory_used + size > 65536)
- return NULL;
- p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC);
- if (p == NULL)
- return NULL;
- p->used = 0;
- p->size = size;
- p->next = NULL;
- p->commit = 0;
- p->read = 0;
- p->char_buf_ptr = (char *)(p->data);
- p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size;
- tty->buf.memory_used += size;
- return p;
-}
-
-/**
- * tty_buffer_free - free a tty buffer
- * @tty: tty owning the buffer
- * @b: the buffer to free
- *
- * Free a tty buffer, or add it to the free list according to our
- * internal strategy
- *
- * Locking: Caller must hold tty->buf.lock
- */
-
-static void tty_buffer_free(struct tty_struct *tty, struct tty_buffer *b)
-{
- /* Dumb strategy for now - should keep some stats */
- tty->buf.memory_used -= b->size;
- WARN_ON(tty->buf.memory_used < 0);
-
- if (b->size >= 512)
- kfree(b);
- else {
- b->next = tty->buf.free;
- tty->buf.free = b;
- }
-}
-
-/**
- * __tty_buffer_flush - flush full tty buffers
- * @tty: tty to flush
- *
- * flush all the buffers containing receive data. Caller must
- * hold the buffer lock and must have ensured no parallel flush to
- * ldisc is running.
- *
- * Locking: Caller must hold tty->buf.lock
- */
-
-static void __tty_buffer_flush(struct tty_struct *tty)
-{
- struct tty_buffer *thead;
-
- while ((thead = tty->buf.head) != NULL) {
- tty->buf.head = thead->next;
- tty_buffer_free(tty, thead);
- }
- tty->buf.tail = NULL;
-}
-
-/**
- * tty_buffer_flush - flush full tty buffers
- * @tty: tty to flush
- *
- * flush all the buffers containing receive data. If the buffer is
- * being processed by flush_to_ldisc then we defer the processing
- * to that function
- *
- * Locking: none
- */
-
-static void tty_buffer_flush(struct tty_struct *tty)
-{
- unsigned long flags;
- spin_lock_irqsave(&tty->buf.lock, flags);
-
- /* If the data is being pushed to the tty layer then we can't
- process it here. Instead set a flag and the flush_to_ldisc
- path will process the flush request before it exits */
- if (test_bit(TTY_FLUSHING, &tty->flags)) {
- set_bit(TTY_FLUSHPENDING, &tty->flags);
- spin_unlock_irqrestore(&tty->buf.lock, flags);
- wait_event(tty->read_wait,
- test_bit(TTY_FLUSHPENDING, &tty->flags) == 0);
- return;
- } else
- __tty_buffer_flush(tty);
- spin_unlock_irqrestore(&tty->buf.lock, flags);
-}
-
-/**
- * tty_buffer_find - find a free tty buffer
- * @tty: tty owning the buffer
- * @size: characters wanted
- *
- * Locate an existing suitable tty buffer or if we are lacking one then
- * allocate a new one. We round our buffers off in 256 character chunks
- * to get better allocation behaviour.
- *
- * Locking: Caller must hold tty->buf.lock
- */
-
-static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size)
-{
- struct tty_buffer **tbh = &tty->buf.free;
- while ((*tbh) != NULL) {
- struct tty_buffer *t = *tbh;
- if (t->size >= size) {
- *tbh = t->next;
- t->next = NULL;
- t->used = 0;
- t->commit = 0;
- t->read = 0;
- tty->buf.memory_used += t->size;
- return t;
- }
- tbh = &((*tbh)->next);
- }
- /* Round the buffer size out */
- size = (size + 0xFF) & ~0xFF;
- return tty_buffer_alloc(tty, size);
- /* Should possibly check if this fails for the largest buffer we
- have queued and recycle that ? */
-}
-
-/**
- * tty_buffer_request_room - grow tty buffer if needed
- * @tty: tty structure
- * @size: size desired
- *
- * Make at least size bytes of linear space available for the tty
- * buffer. If we fail return the size we managed to find.
- *
- * Locking: Takes tty->buf.lock
- */
-int tty_buffer_request_room(struct tty_struct *tty, size_t size)
-{
- struct tty_buffer *b, *n;
- int left;
- unsigned long flags;
-
- spin_lock_irqsave(&tty->buf.lock, flags);
-
- /* OPTIMISATION: We could keep a per tty "zero" sized buffer to
- remove this conditional if its worth it. This would be invisible
- to the callers */
- if ((b = tty->buf.tail) != NULL)
- left = b->size - b->used;
- else
- left = 0;
-
- if (left < size) {
- /* This is the slow path - looking for new buffers to use */
- if ((n = tty_buffer_find(tty, size)) != NULL) {
- if (b != NULL) {
- b->next = n;
- b->commit = b->used;
- } else
- tty->buf.head = n;
- tty->buf.tail = n;
- } else
- size = left;
- }
-
- spin_unlock_irqrestore(&tty->buf.lock, flags);
- return size;
-}
-EXPORT_SYMBOL_GPL(tty_buffer_request_room);
-
-/**
- * tty_insert_flip_string - Add characters to the tty buffer
- * @tty: tty structure
- * @chars: characters
- * @size: size
- *
- * Queue a series of bytes to the tty buffering. All the characters
- * passed are marked as without error. Returns the number added.
- *
- * Locking: Called functions may take tty->buf.lock
- */
-
-int tty_insert_flip_string(struct tty_struct *tty, const unsigned char *chars,
- size_t size)
-{
- int copied = 0;
- do {
- int space = tty_buffer_request_room(tty, size - copied);
- struct tty_buffer *tb = tty->buf.tail;
- /* If there is no space then tb may be NULL */
- if (unlikely(space == 0))
- break;
- memcpy(tb->char_buf_ptr + tb->used, chars, space);
- memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space);
- tb->used += space;
- copied += space;
- chars += space;
- /* There is a small chance that we need to split the data over
- several buffers. If this is the case we must loop */
- } while (unlikely(size > copied));
- return copied;
-}
-EXPORT_SYMBOL(tty_insert_flip_string);
-
-/**
- * tty_insert_flip_string_flags - Add characters to the tty buffer
- * @tty: tty structure
- * @chars: characters
- * @flags: flag bytes
- * @size: size
- *
- * Queue a series of bytes to the tty buffering. For each character
- * the flags array indicates the status of the character. Returns the
- * number added.
- *
- * Locking: Called functions may take tty->buf.lock
- */
-
-int tty_insert_flip_string_flags(struct tty_struct *tty,
- const unsigned char *chars, const char *flags, size_t size)
-{
- int copied = 0;
- do {
- int space = tty_buffer_request_room(tty, size - copied);
- struct tty_buffer *tb = tty->buf.tail;
- /* If there is no space then tb may be NULL */
- if (unlikely(space == 0))
- break;
- memcpy(tb->char_buf_ptr + tb->used, chars, space);
- memcpy(tb->flag_buf_ptr + tb->used, flags, space);
- tb->used += space;
- copied += space;
- chars += space;
- flags += space;
- /* There is a small chance that we need to split the data over
- several buffers. If this is the case we must loop */
- } while (unlikely(size > copied));
- return copied;
-}
-EXPORT_SYMBOL(tty_insert_flip_string_flags);
-
-/**
- * tty_schedule_flip - push characters to ldisc
- * @tty: tty to push from
- *
- * Takes any pending buffers and transfers their ownership to the
- * ldisc side of the queue. It then schedules those characters for
- * processing by the line discipline.
- *
- * Locking: Takes tty->buf.lock
- */
-
-void tty_schedule_flip(struct tty_struct *tty)
-{
- unsigned long flags;
- spin_lock_irqsave(&tty->buf.lock, flags);
- if (tty->buf.tail != NULL)
- tty->buf.tail->commit = tty->buf.tail->used;
- spin_unlock_irqrestore(&tty->buf.lock, flags);
- schedule_delayed_work(&tty->buf.work, 1);
-}
-EXPORT_SYMBOL(tty_schedule_flip);
-
-/**
- * tty_prepare_flip_string - make room for characters
- * @tty: tty
- * @chars: return pointer for character write area
- * @size: desired size
- *
- * Prepare a block of space in the buffer for data. Returns the length
- * available and buffer pointer to the space which is now allocated and
- * accounted for as ready for normal characters. This is used for drivers
- * that need their own block copy routines into the buffer. There is no
- * guarantee the buffer is a DMA target!
- *
- * Locking: May call functions taking tty->buf.lock
- */
-
-int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars,
- size_t size)
-{
- int space = tty_buffer_request_room(tty, size);
- if (likely(space)) {
- struct tty_buffer *tb = tty->buf.tail;
- *chars = tb->char_buf_ptr + tb->used;
- memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space);
- tb->used += space;
- }
- return space;
-}
-
-EXPORT_SYMBOL_GPL(tty_prepare_flip_string);
-
-/**
- * tty_prepare_flip_string_flags - make room for characters
- * @tty: tty
- * @chars: return pointer for character write area
- * @flags: return pointer for status flag write area
- * @size: desired size
- *
- * Prepare a block of space in the buffer for data. Returns the length
- * available and buffer pointer to the space which is now allocated and
- * accounted for as ready for characters. This is used for drivers
- * that need their own block copy routines into the buffer. There is no
- * guarantee the buffer is a DMA target!
- *
- * Locking: May call functions taking tty->buf.lock
- */
-
-int tty_prepare_flip_string_flags(struct tty_struct *tty,
- unsigned char **chars, char **flags, size_t size)
-{
- int space = tty_buffer_request_room(tty, size);
- if (likely(space)) {
- struct tty_buffer *tb = tty->buf.tail;
- *chars = tb->char_buf_ptr + tb->used;
- *flags = tb->flag_buf_ptr + tb->used;
- tb->used += space;
- }
- return space;
-}
-
-EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags);
-
-
-
/**
* get_tty_driver - find device of a tty
* @dev_t: device identifier
@@ -675,7 +274,7 @@
if (device < base || device >= base + p->num)
continue;
*index = device - base;
- return p;
+ return tty_driver_kref_get(p);
}
return NULL;
}
@@ -719,7 +318,7 @@
if (tty_line >= 0 && tty_line <= p->num && p->ops &&
p->ops->poll_init && !p->ops->poll_init(p, tty_line, str)) {
- res = p;
+ res = tty_driver_kref_get(p);
*line = tty_line;
break;
}
@@ -819,20 +418,6 @@
.fasync = tty_fasync,
};
-#ifdef CONFIG_UNIX98_PTYS
-static const struct file_operations ptmx_fops = {
- .llseek = no_llseek,
- .read = tty_read,
- .write = tty_write,
- .poll = tty_poll,
- .unlocked_ioctl = tty_ioctl,
- .compat_ioctl = tty_compat_ioctl,
- .open = ptmx_open,
- .release = tty_release,
- .fasync = tty_fasync,
-};
-#endif
-
static const struct file_operations console_fops = {
.llseek = no_llseek,
.read = tty_read,
@@ -953,6 +538,7 @@
struct tty_ldisc *ld;
int closecount = 0, n;
unsigned long flags;
+ int refs = 0;
if (!tty)
return;
@@ -1019,8 +605,12 @@
if (tty->session) {
do_each_pid_task(tty->session, PIDTYPE_SID, p) {
spin_lock_irq(&p->sighand->siglock);
- if (p->signal->tty == tty)
+ if (p->signal->tty == tty) {
p->signal->tty = NULL;
+ /* We defer the dereferences outside fo
+ the tasklist lock */
+ refs++;
+ }
if (!p->signal->leader) {
spin_unlock_irq(&p->sighand->siglock);
continue;
@@ -1046,6 +636,10 @@
tty->ctrl_status = 0;
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+ /* Account for the p->signal references we killed */
+ while (refs--)
+ tty_kref_put(tty);
+
/*
* If one of the devices matches a console pointer, we
* cannot just call hangup() because that will cause
@@ -1115,6 +709,23 @@
EXPORT_SYMBOL(tty_vhangup);
/**
+ * tty_vhangup_self - process vhangup for own ctty
+ *
+ * Perform a vhangup on the current controlling tty
+ */
+
+void tty_vhangup_self(void)
+{
+ struct tty_struct *tty;
+
+ tty = get_current_tty();
+ if (tty) {
+ tty_vhangup(tty);
+ tty_kref_put(tty);
+ }
+}
+
+/**
* tty_hung_up_p - was tty hung up
* @filp: file pointer of tty
*
@@ -1167,16 +778,14 @@
struct pid *tty_pgrp = NULL;
- mutex_lock(&tty_mutex);
tty = get_current_tty();
if (tty) {
tty_pgrp = get_pid(tty->pgrp);
lock_kernel();
- mutex_unlock(&tty_mutex);
- /* XXX: here we race, there is nothing protecting tty */
if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY)
tty_vhangup(tty);
unlock_kernel();
+ tty_kref_put(tty);
} else if (on_exit) {
struct pid *old_pgrp;
spin_lock_irq(¤t->sighand->siglock);
@@ -1188,7 +797,6 @@
kill_pgrp(old_pgrp, SIGCONT, on_exit);
put_pid(old_pgrp);
}
- mutex_unlock(&tty_mutex);
return;
}
if (tty_pgrp) {
@@ -1203,8 +811,6 @@
current->signal->tty_old_pgrp = NULL;
spin_unlock_irq(¤t->sighand->siglock);
- mutex_lock(&tty_mutex);
- /* It is possible that do_tty_hangup has free'd this tty */
tty = get_current_tty();
if (tty) {
unsigned long flags;
@@ -1214,13 +820,13 @@
tty->session = NULL;
tty->pgrp = NULL;
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+ tty_kref_put(tty);
} else {
#ifdef TTY_DEBUG_HANGUP
printk(KERN_DEBUG "error attempted to write to tty [0x%p]"
" = NULL", tty);
#endif
}
- mutex_unlock(&tty_mutex);
/* Now clear signal->tty under the lock */
read_lock(&tasklist_lock);
@@ -1420,19 +1026,19 @@
/* write_buf/write_cnt is protected by the atomic_write_lock mutex */
if (tty->write_cnt < chunk) {
- unsigned char *buf;
+ unsigned char *buf_chunk;
if (chunk < 1024)
chunk = 1024;
- buf = kmalloc(chunk, GFP_KERNEL);
- if (!buf) {
+ buf_chunk = kmalloc(chunk, GFP_KERNEL);
+ if (!buf_chunk) {
ret = -ENOMEM;
goto out;
}
kfree(tty->write_buf);
tty->write_cnt = chunk;
- tty->write_buf = buf;
+ tty->write_buf = buf_chunk;
}
/* Do the write .. */
@@ -1466,6 +1072,31 @@
return ret;
}
+/**
+ * tty_write_message - write a message to a certain tty, not just the console.
+ * @tty: the destination tty_struct
+ * @msg: the message to write
+ *
+ * This is used for messages that need to be redirected to a specific tty.
+ * We don't put it into the syslog queue right now maybe in the future if
+ * really needed.
+ *
+ * We must still hold the BKL and test the CLOSING flag for the moment.
+ */
+
+void tty_write_message(struct tty_struct *tty, char *msg)
+{
+ lock_kernel();
+ if (tty) {
+ mutex_lock(&tty->atomic_write_lock);
+ if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags))
+ tty->ops->write(tty, msg, strlen(msg));
+ tty_write_unlock(tty);
+ }
+ unlock_kernel();
+ return;
+}
+
/**
* tty_write - write method for tty device file
@@ -1533,42 +1164,6 @@
return tty_write(file, buf, count, ppos);
}
-void tty_port_init(struct tty_port *port)
-{
- memset(port, 0, sizeof(*port));
- init_waitqueue_head(&port->open_wait);
- init_waitqueue_head(&port->close_wait);
- mutex_init(&port->mutex);
- port->close_delay = (50 * HZ) / 100;
- port->closing_wait = (3000 * HZ) / 100;
-}
-EXPORT_SYMBOL(tty_port_init);
-
-int tty_port_alloc_xmit_buf(struct tty_port *port)
-{
- /* We may sleep in get_zeroed_page() */
- mutex_lock(&port->mutex);
- if (port->xmit_buf == NULL)
- port->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL);
- mutex_unlock(&port->mutex);
- if (port->xmit_buf == NULL)
- return -ENOMEM;
- return 0;
-}
-EXPORT_SYMBOL(tty_port_alloc_xmit_buf);
-
-void tty_port_free_xmit_buf(struct tty_port *port)
-{
- mutex_lock(&port->mutex);
- if (port->xmit_buf != NULL) {
- free_page((unsigned long)port->xmit_buf);
- port->xmit_buf = NULL;
- }
- mutex_unlock(&port->mutex);
-}
-EXPORT_SYMBOL(tty_port_free_xmit_buf);
-
-
static char ptychar[] = "pqrstuvwxyzabcde";
/**
@@ -1592,7 +1187,7 @@
}
/**
- * pty_line_name - generate name for a tty
+ * tty_line_name - generate name for a tty
* @driver: the tty driver in use
* @index: the minor number
* @p: output buffer of at least 7 bytes
@@ -1608,10 +1203,148 @@
}
/**
- * init_dev - initialise a tty device
+ * tty_driver_lookup_tty() - find an existing tty, if any
+ * @driver: the driver for the tty
+ * @idx: the minor number
+ *
+ * Return the tty, if found or ERR_PTR() otherwise.
+ *
+ * Locking: tty_mutex must be held. If tty is found, the mutex must
+ * be held until the 'fast-open' is also done. Will change once we
+ * have refcounting in the driver and per driver locking
+ */
+struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver,
+ struct inode *inode, int idx)
+{
+ struct tty_struct *tty;
+
+ if (driver->ops->lookup)
+ return driver->ops->lookup(driver, inode, idx);
+
+ tty = driver->ttys[idx];
+ return tty;
+}
+
+/**
+ * tty_init_termios - helper for termios setup
+ * @tty: the tty to set up
+ *
+ * Initialise the termios structures for this tty. Thus runs under
+ * the tty_mutex currently so we can be relaxed about ordering.
+ */
+
+int tty_init_termios(struct tty_struct *tty)
+{
+ struct ktermios *tp;
+ int idx = tty->index;
+
+ tp = tty->driver->termios[idx];
+ if (tp == NULL) {
+ tp = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL);
+ if (tp == NULL)
+ return -ENOMEM;
+ memcpy(tp, &tty->driver->init_termios,
+ sizeof(struct ktermios));
+ tty->driver->termios[idx] = tp;
+ }
+ tty->termios = tp;
+ tty->termios_locked = tp + 1;
+
+ /* Compatibility until drivers always set this */
+ tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios);
+ tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios);
+ return 0;
+}
+
+/**
+ * tty_driver_install_tty() - install a tty entry in the driver
+ * @driver: the driver for the tty
+ * @tty: the tty
+ *
+ * Install a tty object into the driver tables. The tty->index field
+ * will be set by the time this is called. This method is responsible
+ * for ensuring any need additional structures are allocated and
+ * configured.
+ *
+ * Locking: tty_mutex for now
+ */
+static int tty_driver_install_tty(struct tty_driver *driver,
+ struct tty_struct *tty)
+{
+ int idx = tty->index;
+
+ if (driver->ops->install)
+ return driver->ops->install(driver, tty);
+
+ if (tty_init_termios(tty) == 0) {
+ tty_driver_kref_get(driver);
+ tty->count++;
+ driver->ttys[idx] = tty;
+ return 0;
+ }
+ return -ENOMEM;
+}
+
+/**
+ * tty_driver_remove_tty() - remove a tty from the driver tables
+ * @driver: the driver for the tty
+ * @idx: the minor number
+ *
+ * Remvoe a tty object from the driver tables. The tty->index field
+ * will be set by the time this is called.
+ *
+ * Locking: tty_mutex for now
+ */
+static void tty_driver_remove_tty(struct tty_driver *driver,
+ struct tty_struct *tty)
+{
+ if (driver->ops->remove)
+ driver->ops->remove(driver, tty);
+ else
+ driver->ttys[tty->index] = NULL;
+}
+
+/*
+ * tty_reopen() - fast re-open of an open tty
+ * @tty - the tty to open
+ *
+ * Return 0 on success, -errno on error.
+ *
+ * Locking: tty_mutex must be held from the time the tty was found
+ * till this open completes.
+ */
+static int tty_reopen(struct tty_struct *tty)
+{
+ struct tty_driver *driver = tty->driver;
+
+ if (test_bit(TTY_CLOSING, &tty->flags))
+ return -EIO;
+
+ if (driver->type == TTY_DRIVER_TYPE_PTY &&
+ driver->subtype == PTY_TYPE_MASTER) {
+ /*
+ * special case for PTY masters: only one open permitted,
+ * and the slave side open count is incremented as well.
+ */
+ if (tty->count)
+ return -EIO;
+
+ tty->link->count++;
+ }
+ tty->count++;
+ tty->driver = driver; /* N.B. why do this every time?? */
+
+ WARN_ON(!test_bit(TTY_LDISC, &tty->flags));
+
+ return 0;
+}
+
+/**
+ * tty_init_dev - initialise a tty device
* @driver: tty driver we are opening a device on
* @idx: device index
- * @tty: returned tty structure
+ * @ret_tty: returned tty structure
+ * @first_ok: ok to open a new device (used by ptmx)
*
* Prepare a tty device. This may not be a "new" clean device but
* could also be an active device. The pty drivers require special
@@ -1631,37 +1364,16 @@
* relaxed for the (most common) case of reopening a tty.
*/
-static int init_dev(struct tty_driver *driver, int idx,
- struct tty_struct **ret_tty)
+struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
+ int first_ok)
{
- struct tty_struct *tty, *o_tty;
- struct ktermios *tp, **tp_loc, *o_tp, **o_tp_loc;
- struct ktermios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc;
- int retval = 0;
+ struct tty_struct *tty;
+ int retval;
- /* check whether we're reopening an existing tty */
- if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {
- tty = devpts_get_tty(idx);
- /*
- * If we don't have a tty here on a slave open, it's because
- * the master already started the close process and there's
- * no relation between devpts file and tty anymore.
- */
- if (!tty && driver->subtype == PTY_TYPE_SLAVE) {
- retval = -EIO;
- goto end_init;
- }
- /*
- * It's safe from now on because init_dev() is called with
- * tty_mutex held and release_dev() won't change tty->count
- * or tty->flags without having to grab tty_mutex
- */
- if (tty && driver->subtype == PTY_TYPE_MASTER)
- tty = tty->link;
- } else {
- tty = driver->ttys[idx];
- }
- if (tty) goto fast_track;
+ /* Check if pty master is being opened multiple times */
+ if (driver->subtype == PTY_TYPE_MASTER &&
+ (driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok)
+ return ERR_PTR(-EIO);
/*
* First time open is complex, especially for PTY devices.
@@ -1671,189 +1383,69 @@
* and locked termios may be retained.)
*/
- if (!try_module_get(driver->owner)) {
- retval = -ENODEV;
- goto end_init;
- }
-
- o_tty = NULL;
- tp = o_tp = NULL;
- ltp = o_ltp = NULL;
+ if (!try_module_get(driver->owner))
+ return ERR_PTR(-ENODEV);
tty = alloc_tty_struct();
if (!tty)
goto fail_no_mem;
- initialize_tty_struct(tty);
- tty->driver = driver;
- tty->ops = driver->ops;
- tty->index = idx;
- tty_line_name(driver, idx, tty->name);
+ initialize_tty_struct(tty, driver, idx);
- if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {
- tp_loc = &tty->termios;
- ltp_loc = &tty->termios_locked;
- } else {
- tp_loc = &driver->termios[idx];
- ltp_loc = &driver->termios_locked[idx];
+ retval = tty_driver_install_tty(driver, tty);
+ if (retval < 0) {
+ free_tty_struct(tty);
+ module_put(driver->owner);
+ return ERR_PTR(retval);
}
- if (!*tp_loc) {
- tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL);
- if (!tp)
- goto free_mem_out;
- *tp = driver->init_termios;
- }
-
- if (!*ltp_loc) {
- ltp = kzalloc(sizeof(struct ktermios), GFP_KERNEL);
- if (!ltp)
- goto free_mem_out;
- }
-
- if (driver->type == TTY_DRIVER_TYPE_PTY) {
- o_tty = alloc_tty_struct();
- if (!o_tty)
- goto free_mem_out;
- initialize_tty_struct(o_tty);
- o_tty->driver = driver->other;
- o_tty->ops = driver->ops;
- o_tty->index = idx;
- tty_line_name(driver->other, idx, o_tty->name);
-
- if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {
- o_tp_loc = &o_tty->termios;
- o_ltp_loc = &o_tty->termios_locked;
- } else {
- o_tp_loc = &driver->other->termios[idx];
- o_ltp_loc = &driver->other->termios_locked[idx];
- }
-
- if (!*o_tp_loc) {
- o_tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL);
- if (!o_tp)
- goto free_mem_out;
- *o_tp = driver->other->init_termios;
- }
-
- if (!*o_ltp_loc) {
- o_ltp = kzalloc(sizeof(struct ktermios), GFP_KERNEL);
- if (!o_ltp)
- goto free_mem_out;
- }
-
- /*
- * Everything allocated ... set up the o_tty structure.
- */
- if (!(driver->other->flags & TTY_DRIVER_DEVPTS_MEM))
- driver->other->ttys[idx] = o_tty;
- if (!*o_tp_loc)
- *o_tp_loc = o_tp;
- if (!*o_ltp_loc)
- *o_ltp_loc = o_ltp;
- o_tty->termios = *o_tp_loc;
- o_tty->termios_locked = *o_ltp_loc;
- driver->other->refcount++;
- if (driver->subtype == PTY_TYPE_MASTER)
- o_tty->count++;
-
- /* Establish the links in both directions */
- tty->link = o_tty;
- o_tty->link = tty;
- }
-
- /*
- * All structures have been allocated, so now we install them.
- * Failures after this point use release_tty to clean up, so
- * there's no need to null out the local pointers.
- */
- if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM))
- driver->ttys[idx] = tty;
-
- if (!*tp_loc)
- *tp_loc = tp;
- if (!*ltp_loc)
- *ltp_loc = ltp;
- tty->termios = *tp_loc;
- tty->termios_locked = *ltp_loc;
- /* Compatibility until drivers always set this */
- tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios);
- tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios);
- driver->refcount++;
- tty->count++;
-
/*
* Structures all installed ... call the ldisc open routines.
* If we fail here just call release_tty to clean up. No need
* to decrement the use counts, as release_tty doesn't care.
*/
- retval = tty_ldisc_setup(tty, o_tty);
-
+ retval = tty_ldisc_setup(tty, tty->link);
if (retval)
goto release_mem_out;
- goto success;
-
- /*
- * This fast open can be used if the tty is already open.
- * No memory is allocated, and the only failures are from
- * attempting to open a closing tty or attempting multiple
- * opens on a pty master.
- */
-fast_track:
- if (test_bit(TTY_CLOSING, &tty->flags)) {
- retval = -EIO;
- goto end_init;
- }
- if (driver->type == TTY_DRIVER_TYPE_PTY &&
- driver->subtype == PTY_TYPE_MASTER) {
- /*
- * special case for PTY masters: only one open permitted,
- * and the slave side open count is incremented as well.
- */
- if (tty->count) {
- retval = -EIO;
- goto end_init;
- }
- tty->link->count++;
- }
- tty->count++;
- tty->driver = driver; /* N.B. why do this every time?? */
-
- /* FIXME */
- if (!test_bit(TTY_LDISC, &tty->flags))
- printk(KERN_ERR "init_dev but no ldisc\n");
-success:
- *ret_tty = tty;
-
- /* All paths come through here to release the mutex */
-end_init:
- return retval;
-
- /* Release locally allocated memory ... nothing placed in slots */
-free_mem_out:
- kfree(o_tp);
- if (o_tty)
- free_tty_struct(o_tty);
- kfree(ltp);
- kfree(tp);
- free_tty_struct(tty);
+ return tty;
fail_no_mem:
module_put(driver->owner);
- retval = -ENOMEM;
- goto end_init;
+ return ERR_PTR(-ENOMEM);
/* call the tty release_tty routine to clean out this slot */
release_mem_out:
if (printk_ratelimit())
- printk(KERN_INFO "init_dev: ldisc open failed, "
+ printk(KERN_INFO "tty_init_dev: ldisc open failed, "
"clearing slot %d\n", idx);
release_tty(tty, idx);
- goto end_init;
+ return ERR_PTR(retval);
}
+void tty_free_termios(struct tty_struct *tty)
+{
+ struct ktermios *tp;
+ int idx = tty->index;
+ /* Kill this flag and push into drivers for locking etc */
+ if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
+ /* FIXME: Locking on ->termios array */
+ tp = tty->termios;
+ tty->driver->termios[idx] = NULL;
+ kfree(tp);
+ }
+}
+EXPORT_SYMBOL(tty_free_termios);
+
+void tty_shutdown(struct tty_struct *tty)
+{
+ tty_driver_remove_tty(tty->driver, tty);
+ tty_free_termios(tty);
+}
+EXPORT_SYMBOL(tty_shutdown);
+
/**
* release_one_tty - release tty structure memory
+ * @kref: kref of tty we are obliterating
*
* Releases memory associated with a tty structure, and clears out the
* driver table slots. This function is called when a device is no longer
@@ -1863,31 +1455,19 @@
* tty_mutex - sometimes only
* takes the file list lock internally when working on the list
* of ttys that the driver keeps.
- * FIXME: should we require tty_mutex is held here ??
*/
-static void release_one_tty(struct tty_struct *tty, int idx)
+static void release_one_tty(struct kref *kref)
{
- int devpts = tty->driver->flags & TTY_DRIVER_DEVPTS_MEM;
- struct ktermios *tp;
+ struct tty_struct *tty = container_of(kref, struct tty_struct, kref);
+ struct tty_driver *driver = tty->driver;
- if (!devpts)
- tty->driver->ttys[idx] = NULL;
-
- if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
- tp = tty->termios;
- if (!devpts)
- tty->driver->termios[idx] = NULL;
- kfree(tp);
-
- tp = tty->termios_locked;
- if (!devpts)
- tty->driver->termios_locked[idx] = NULL;
- kfree(tp);
- }
-
-
+ if (tty->ops->shutdown)
+ tty->ops->shutdown(tty);
+ else
+ tty_shutdown(tty);
tty->magic = 0;
- tty->driver->refcount--;
+ tty_driver_kref_put(driver);
+ module_put(driver->owner);
file_list_lock();
list_del_init(&tty->tty_files);
@@ -1897,6 +1477,21 @@
}
/**
+ * tty_kref_put - release a tty kref
+ * @tty: tty device
+ *
+ * Release a reference to a tty device and if need be let the kref
+ * layer destruct the object for us
+ */
+
+void tty_kref_put(struct tty_struct *tty)
+{
+ if (tty)
+ kref_put(&tty->kref, release_one_tty);
+}
+EXPORT_SYMBOL(tty_kref_put);
+
+/**
* release_tty - release tty structure memory
*
* Release both @tty and a possible linked partner (think pty pair),
@@ -1907,15 +1502,16 @@
* takes the file list lock internally when working on the list
* of ttys that the driver keeps.
* FIXME: should we require tty_mutex is held here ??
+ *
*/
static void release_tty(struct tty_struct *tty, int idx)
{
- struct tty_driver *driver = tty->driver;
+ /* This should always be true but check for the moment */
+ WARN_ON(tty->index != idx);
if (tty->link)
- release_one_tty(tty->link, idx);
- release_one_tty(tty, idx);
- module_put(driver->owner);
+ tty_kref_put(tty->link);
+ tty_kref_put(tty);
}
/*
@@ -1926,20 +1522,21 @@
* WSH 09/09/97: rewritten to avoid some nasty race conditions that could
* lead to double frees or releasing memory still in use.
*/
-static void release_dev(struct file *filp)
+void tty_release_dev(struct file *filp)
{
struct tty_struct *tty, *o_tty;
int pty_master, tty_closing, o_tty_closing, do_sleep;
int devpts;
int idx;
char buf[64];
+ struct inode *inode;
+ inode = filp->f_path.dentry->d_inode;
tty = (struct tty_struct *)filp->private_data;
- if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode,
- "release_dev"))
+ if (tty_paranoia_check(tty, inode, "tty_release_dev"))
return;
- check_tty_count(tty, "release_dev");
+ check_tty_count(tty, "tty_release_dev");
tty_fasync(-1, filp, 0);
@@ -1951,33 +1548,27 @@
#ifdef TTY_PARANOIA_CHECK
if (idx < 0 || idx >= tty->driver->num) {
- printk(KERN_DEBUG "release_dev: bad idx when trying to "
+ printk(KERN_DEBUG "tty_release_dev: bad idx when trying to "
"free (%s)\n", tty->name);
return;
}
- if (!(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
+ if (!devpts) {
if (tty != tty->driver->ttys[idx]) {
- printk(KERN_DEBUG "release_dev: driver.table[%d] not tty "
+ printk(KERN_DEBUG "tty_release_dev: driver.table[%d] not tty "
"for (%s)\n", idx, tty->name);
return;
}
if (tty->termios != tty->driver->termios[idx]) {
- printk(KERN_DEBUG "release_dev: driver.termios[%d] not termios "
+ printk(KERN_DEBUG "tty_release_dev: driver.termios[%d] not termios "
"for (%s)\n",
idx, tty->name);
return;
}
- if (tty->termios_locked != tty->driver->termios_locked[idx]) {
- printk(KERN_DEBUG "release_dev: driver.termios_locked[%d] not "
- "termios_locked for (%s)\n",
- idx, tty->name);
- return;
- }
}
#endif
#ifdef TTY_DEBUG_HANGUP
- printk(KERN_DEBUG "release_dev of %s (tty count=%d)...",
+ printk(KERN_DEBUG "tty_release_dev of %s (tty count=%d)...",
tty_name(tty, buf), tty->count);
#endif
@@ -1985,26 +1576,19 @@
if (tty->driver->other &&
!(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
if (o_tty != tty->driver->other->ttys[idx]) {
- printk(KERN_DEBUG "release_dev: other->table[%d] "
+ printk(KERN_DEBUG "tty_release_dev: other->table[%d] "
"not o_tty for (%s)\n",
idx, tty->name);
return;
}
if (o_tty->termios != tty->driver->other->termios[idx]) {
- printk(KERN_DEBUG "release_dev: other->termios[%d] "
+ printk(KERN_DEBUG "tty_release_dev: other->termios[%d] "
"not o_termios for (%s)\n",
idx, tty->name);
return;
}
- if (o_tty->termios_locked !=
- tty->driver->other->termios_locked[idx]) {
- printk(KERN_DEBUG "release_dev: other->termios_locked["
- "%d] not o_termios_locked for (%s)\n",
- idx, tty->name);
- return;
- }
if (o_tty->link != tty) {
- printk(KERN_DEBUG "release_dev: bad pty pointers\n");
+ printk(KERN_DEBUG "tty_release_dev: bad pty pointers\n");
return;
}
}
@@ -2062,7 +1646,7 @@
if (!do_sleep)
break;
- printk(KERN_WARNING "release_dev: %s: read/write wait queue "
+ printk(KERN_WARNING "tty_release_dev: %s: read/write wait queue "
"active!\n", tty_name(tty, buf));
mutex_unlock(&tty_mutex);
schedule();
@@ -2075,14 +1659,14 @@
*/
if (pty_master) {
if (--o_tty->count < 0) {
- printk(KERN_WARNING "release_dev: bad pty slave count "
+ printk(KERN_WARNING "tty_release_dev: bad pty slave count "
"(%d) for %s\n",
o_tty->count, tty_name(o_tty, buf));
o_tty->count = 0;
}
}
if (--tty->count < 0) {
- printk(KERN_WARNING "release_dev: bad tty->count (%d) for %s\n",
+ printk(KERN_WARNING "tty_release_dev: bad tty->count (%d) for %s\n",
tty->count, tty_name(tty, buf));
tty->count = 0;
}
@@ -2145,11 +1729,11 @@
/* Make this pty number available for reallocation */
if (devpts)
- devpts_kill_index(idx);
+ devpts_kill_index(inode, idx);
}
/**
- * tty_open - open a tty device
+ * __tty_open - open a tty device
* @inode: inode of device file
* @filp: file pointer to tty
*
@@ -2164,14 +1748,14 @@
* The termios state of a pty is reset on first open so that
* settings don't persist across reuse.
*
- * Locking: tty_mutex protects tty, get_tty_driver and init_dev work.
+ * Locking: tty_mutex protects tty, get_tty_driver and tty_init_dev work.
* tty->count should protect the rest.
* ->siglock protects ->signal/->sighand
*/
static int __tty_open(struct inode *inode, struct file *filp)
{
- struct tty_struct *tty;
+ struct tty_struct *tty = NULL;
int noctty, retval;
struct tty_driver *driver;
int index;
@@ -2193,23 +1777,25 @@
mutex_unlock(&tty_mutex);
return -ENXIO;
}
- driver = tty->driver;
+ driver = tty_driver_kref_get(tty->driver);
index = tty->index;
filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
/* noctty = 1; */
+ /* FIXME: Should we take a driver reference ? */
+ tty_kref_put(tty);
goto got_driver;
}
#ifdef CONFIG_VT
if (device == MKDEV(TTY_MAJOR, 0)) {
extern struct tty_driver *console_driver;
- driver = console_driver;
+ driver = tty_driver_kref_get(console_driver);
index = fg_console;
noctty = 1;
goto got_driver;
}
#endif
if (device == MKDEV(TTYAUX_MAJOR, 1)) {
- driver = console_device(&index);
+ driver = tty_driver_kref_get(console_device(&index));
if (driver) {
/* Don't let /dev/console block */
filp->f_flags |= O_NONBLOCK;
@@ -2226,10 +1812,25 @@
return -ENODEV;
}
got_driver:
- retval = init_dev(driver, index, &tty);
+ if (!tty) {
+ /* check whether we're reopening an existing tty */
+ tty = tty_driver_lookup_tty(driver, inode, index);
+
+ if (IS_ERR(tty))
+ return PTR_ERR(tty);
+ }
+
+ if (tty) {
+ retval = tty_reopen(tty);
+ if (retval)
+ tty = ERR_PTR(retval);
+ } else
+ tty = tty_init_dev(driver, index, 0);
+
mutex_unlock(&tty_mutex);
- if (retval)
- return retval;
+ tty_driver_kref_put(driver);
+ if (IS_ERR(tty))
+ return PTR_ERR(tty);
filp->private_data = tty;
file_move(filp, &tty->tty_files);
@@ -2257,7 +1858,7 @@
printk(KERN_DEBUG "error %d in opening %s...", retval,
tty->name);
#endif
- release_dev(filp);
+ tty_release_dev(filp);
if (retval != -ERESTARTSYS)
return retval;
if (signal_pending(current))
@@ -2296,69 +1897,6 @@
-#ifdef CONFIG_UNIX98_PTYS
-/**
- * ptmx_open - open a unix 98 pty master
- * @inode: inode of device file
- * @filp: file pointer to tty
- *
- * Allocate a unix98 pty master device from the ptmx driver.
- *
- * Locking: tty_mutex protects theinit_dev work. tty->count should
- * protect the rest.
- * allocated_ptys_lock handles the list of free pty numbers
- */
-
-static int __ptmx_open(struct inode *inode, struct file *filp)
-{
- struct tty_struct *tty;
- int retval;
- int index;
-
- nonseekable_open(inode, filp);
-
- /* find a device that is not in use. */
- index = devpts_new_index();
- if (index < 0)
- return index;
-
- mutex_lock(&tty_mutex);
- retval = init_dev(ptm_driver, index, &tty);
- mutex_unlock(&tty_mutex);
-
- if (retval)
- goto out;
-
- set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
- filp->private_data = tty;
- file_move(filp, &tty->tty_files);
-
- retval = devpts_pty_new(tty->link);
- if (retval)
- goto out1;
-
- check_tty_count(tty, "ptmx_open");
- retval = ptm_driver->ops->open(tty, filp);
- if (!retval)
- return 0;
-out1:
- release_dev(filp);
- return retval;
-out:
- devpts_kill_index(index);
- return retval;
-}
-
-static int ptmx_open(struct inode *inode, struct file *filp)
-{
- int ret;
-
- lock_kernel();
- ret = __ptmx_open(inode, filp);
- unlock_kernel();
- return ret;
-}
-#endif
/**
* tty_release - vfs callback for close
@@ -2369,13 +1907,13 @@
* this tty. There may however be several such references.
*
* Locking:
- * Takes bkl. See release_dev
+ * Takes bkl. See tty_release_dev
*/
static int tty_release(struct inode *inode, struct file *filp)
{
lock_kernel();
- release_dev(filp);
+ tty_release_dev(filp);
unlock_kernel();
return 0;
}
@@ -2524,7 +2062,7 @@
/* For a PTY we need to lock the tty side */
mutex_lock(&real_tty->termios_mutex);
- if (!memcmp(ws, &tty->winsize, sizeof(*ws)))
+ if (!memcmp(ws, &real_tty->winsize, sizeof(*ws)))
goto done;
/* Get the PID values and reference them so we can
avoid holding the tty ctrl lock while sending signals */
@@ -2996,7 +2534,7 @@
case TIOCSTI:
return tiocsti(tty, p);
case TIOCGWINSZ:
- return tiocgwinsz(tty, p);
+ return tiocgwinsz(real_tty, p);
case TIOCSWINSZ:
return tiocswinsz(tty, real_tty, p);
case TIOCCONS:
@@ -3026,10 +2564,6 @@
return put_user(tty->ldisc.ops->num, (int __user *)p);
case TIOCSETD:
return tiocsetd(tty, p);
-#ifdef CONFIG_VT
- case TIOCLINUX:
- return tioclinux(tty, arg);
-#endif
/*
* Break handling
*/
@@ -3220,113 +2754,6 @@
EXPORT_SYMBOL(do_SAK);
/**
- * flush_to_ldisc
- * @work: tty structure passed from work queue.
- *
- * This routine is called out of the software interrupt to flush data
- * from the buffer chain to the line discipline.
- *
- * Locking: holds tty->buf.lock to guard buffer list. Drops the lock
- * while invoking the line discipline receive_buf method. The
- * receive_buf method is single threaded for each tty instance.
- */
-
-static void flush_to_ldisc(struct work_struct *work)
-{
- struct tty_struct *tty =
- container_of(work, struct tty_struct, buf.work.work);
- unsigned long flags;
- struct tty_ldisc *disc;
- struct tty_buffer *tbuf, *head;
- char *char_buf;
- unsigned char *flag_buf;
-
- disc = tty_ldisc_ref(tty);
- if (disc == NULL) /* !TTY_LDISC */
- return;
-
- spin_lock_irqsave(&tty->buf.lock, flags);
- /* So we know a flush is running */
- set_bit(TTY_FLUSHING, &tty->flags);
- head = tty->buf.head;
- if (head != NULL) {
- tty->buf.head = NULL;
- for (;;) {
- int count = head->commit - head->read;
- if (!count) {
- if (head->next == NULL)
- break;
- tbuf = head;
- head = head->next;
- tty_buffer_free(tty, tbuf);
- continue;
- }
- /* Ldisc or user is trying to flush the buffers
- we are feeding to the ldisc, stop feeding the
- line discipline as we want to empty the queue */
- if (test_bit(TTY_FLUSHPENDING, &tty->flags))
- break;
- if (!tty->receive_room) {
- schedule_delayed_work(&tty->buf.work, 1);
- break;
- }
- if (count > tty->receive_room)
- count = tty->receive_room;
- char_buf = head->char_buf_ptr + head->read;
- flag_buf = head->flag_buf_ptr + head->read;
- head->read += count;
- spin_unlock_irqrestore(&tty->buf.lock, flags);
- disc->ops->receive_buf(tty, char_buf,
- flag_buf, count);
- spin_lock_irqsave(&tty->buf.lock, flags);
- }
- /* Restore the queue head */
- tty->buf.head = head;
- }
- /* We may have a deferred request to flush the input buffer,
- if so pull the chain under the lock and empty the queue */
- if (test_bit(TTY_FLUSHPENDING, &tty->flags)) {
- __tty_buffer_flush(tty);
- clear_bit(TTY_FLUSHPENDING, &tty->flags);
- wake_up(&tty->read_wait);
- }
- clear_bit(TTY_FLUSHING, &tty->flags);
- spin_unlock_irqrestore(&tty->buf.lock, flags);
-
- tty_ldisc_deref(disc);
-}
-
-/**
- * tty_flip_buffer_push - terminal
- * @tty: tty to push
- *
- * Queue a push of the terminal flip buffers to the line discipline. This
- * function must not be called from IRQ context if tty->low_latency is set.
- *
- * In the event of the queue being busy for flipping the work will be
- * held off and retried later.
- *
- * Locking: tty buffer lock. Driver locks in low latency mode.
- */
-
-void tty_flip_buffer_push(struct tty_struct *tty)
-{
- unsigned long flags;
- spin_lock_irqsave(&tty->buf.lock, flags);
- if (tty->buf.tail != NULL)
- tty->buf.tail->commit = tty->buf.tail->used;
- spin_unlock_irqrestore(&tty->buf.lock, flags);
-
- if (tty->low_latency)
- flush_to_ldisc(&tty->buf.work.work);
- else
- schedule_delayed_work(&tty->buf.work, 1);
-}
-
-EXPORT_SYMBOL(tty_flip_buffer_push);
-
-
-/**
* initialize_tty_struct
* @tty: tty to initialize
*
@@ -3336,9 +2763,11 @@
* Locking: none - tty in question must not be exposed at this point
*/
-static void initialize_tty_struct(struct tty_struct *tty)
+void initialize_tty_struct(struct tty_struct *tty,
+ struct tty_driver *driver, int idx)
{
memset(tty, 0, sizeof(struct tty_struct));
+ kref_init(&tty->kref);
tty->magic = TTY_MAGIC;
tty_ldisc_init(tty);
tty->session = NULL;
@@ -3346,7 +2775,6 @@
tty->overrun_time = jiffies;
tty->buf.head = tty->buf.tail = NULL;
tty_buffer_init(tty);
- INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc);
mutex_init(&tty->termios_mutex);
init_waitqueue_head(&tty->write_wait);
init_waitqueue_head(&tty->read_wait);
@@ -3357,6 +2785,11 @@
spin_lock_init(&tty->ctrl_lock);
INIT_LIST_HEAD(&tty->tty_files);
INIT_WORK(&tty->SAK_work, do_SAK_work);
+
+ tty->driver = driver;
+ tty->ops = driver->ops;
+ tty->index = idx;
+ tty_line_name(driver, idx, tty->name);
}
/**
@@ -3377,10 +2810,9 @@
return tty->ops->put_char(tty, ch);
return tty->ops->write(tty, &ch, 1);
}
-
EXPORT_SYMBOL_GPL(tty_put_char);
-static struct class *tty_class;
+struct class *tty_class;
/**
* tty_register_device - register a tty device
@@ -3420,6 +2852,7 @@
return device_create_drvdata(tty_class, device, dev, NULL, name);
}
+EXPORT_SYMBOL(tty_register_device);
/**
* tty_unregister_device - unregister a tty device
@@ -3437,8 +2870,6 @@
device_destroy(tty_class,
MKDEV(driver->major, driver->minor_start) + index);
}
-
-EXPORT_SYMBOL(tty_register_device);
EXPORT_SYMBOL(tty_unregister_device);
struct tty_driver *alloc_tty_driver(int lines)
@@ -3447,28 +2878,66 @@
driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL);
if (driver) {
+ kref_init(&driver->kref);
driver->magic = TTY_DRIVER_MAGIC;
driver->num = lines;
/* later we'll move allocation of tables here */
}
return driver;
}
+EXPORT_SYMBOL(alloc_tty_driver);
-void put_tty_driver(struct tty_driver *driver)
+static void destruct_tty_driver(struct kref *kref)
{
+ struct tty_driver *driver = container_of(kref, struct tty_driver, kref);
+ int i;
+ struct ktermios *tp;
+ void *p;
+
+ if (driver->flags & TTY_DRIVER_INSTALLED) {
+ /*
+ * Free the termios and termios_locked structures because
+ * we don't want to get memory leaks when modular tty
+ * drivers are removed from the kernel.
+ */
+ for (i = 0; i < driver->num; i++) {
+ tp = driver->termios[i];
+ if (tp) {
+ driver->termios[i] = NULL;
+ kfree(tp);
+ }
+ if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV))
+ tty_unregister_device(driver, i);
+ }
+ p = driver->ttys;
+ proc_tty_unregister_driver(driver);
+ driver->ttys = NULL;
+ driver->termios = NULL;
+ kfree(p);
+ cdev_del(&driver->cdev);
+ }
kfree(driver);
}
+void tty_driver_kref_put(struct tty_driver *driver)
+{
+ kref_put(&driver->kref, destruct_tty_driver);
+}
+EXPORT_SYMBOL(tty_driver_kref_put);
+
void tty_set_operations(struct tty_driver *driver,
const struct tty_operations *op)
{
driver->ops = op;
};
-
-EXPORT_SYMBOL(alloc_tty_driver);
-EXPORT_SYMBOL(put_tty_driver);
EXPORT_SYMBOL(tty_set_operations);
+void put_tty_driver(struct tty_driver *d)
+{
+ tty_driver_kref_put(d);
+}
+EXPORT_SYMBOL(put_tty_driver);
+
/*
* Called by a tty driver to register itself.
*/
@@ -3479,11 +2948,8 @@
dev_t dev;
void **p = NULL;
- if (driver->flags & TTY_DRIVER_INSTALLED)
- return 0;
-
if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {
- p = kzalloc(driver->num * 3 * sizeof(void *), GFP_KERNEL);
+ p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL);
if (!p)
return -ENOMEM;
}
@@ -3507,12 +2973,9 @@
if (p) {
driver->ttys = (struct tty_struct **)p;
driver->termios = (struct ktermios **)(p + driver->num);
- driver->termios_locked = (struct ktermios **)
- (p + driver->num * 2);
} else {
driver->ttys = NULL;
driver->termios = NULL;
- driver->termios_locked = NULL;
}
cdev_init(&driver->cdev, &tty_fops);
@@ -3521,7 +2984,7 @@
if (error) {
unregister_chrdev_region(dev, driver->num);
driver->ttys = NULL;
- driver->termios = driver->termios_locked = NULL;
+ driver->termios = NULL;
kfree(p);
return error;
}
@@ -3535,6 +2998,7 @@
tty_register_device(driver, i, NULL);
}
proc_tty_register_driver(driver);
+ driver->flags |= TTY_DRIVER_INSTALLED;
return 0;
}
@@ -3545,46 +3009,19 @@
*/
int tty_unregister_driver(struct tty_driver *driver)
{
- int i;
- struct ktermios *tp;
- void *p;
-
+#if 0
+ /* FIXME */
if (driver->refcount)
return -EBUSY;
-
+#endif
unregister_chrdev_region(MKDEV(driver->major, driver->minor_start),
driver->num);
mutex_lock(&tty_mutex);
list_del(&driver->tty_drivers);
mutex_unlock(&tty_mutex);
-
- /*
- * Free the termios and termios_locked structures because
- * we don't want to get memory leaks when modular tty
- * drivers are removed from the kernel.
- */
- for (i = 0; i < driver->num; i++) {
- tp = driver->termios[i];
- if (tp) {
- driver->termios[i] = NULL;
- kfree(tp);
- }
- tp = driver->termios_locked[i];
- if (tp) {
- driver->termios_locked[i] = NULL;
- kfree(tp);
- }
- if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV))
- tty_unregister_device(driver, i);
- }
- p = driver->ttys;
- proc_tty_unregister_driver(driver);
- driver->ttys = NULL;
- driver->termios = driver->termios_locked = NULL;
- kfree(p);
- cdev_del(&driver->cdev);
return 0;
}
+
EXPORT_SYMBOL(tty_unregister_driver);
dev_t tty_devnum(struct tty_struct *tty)
@@ -3595,9 +3032,12 @@
void proc_clear_tty(struct task_struct *p)
{
+ struct tty_struct *tty;
spin_lock_irq(&p->sighand->siglock);
+ tty = p->signal->tty;
p->signal->tty = NULL;
spin_unlock_irq(&p->sighand->siglock);
+ tty_kref_put(tty);
}
/* Called under the sighand lock */
@@ -3613,9 +3053,13 @@
tty->pgrp = get_pid(task_pgrp(tsk));
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
tty->session = get_pid(task_session(tsk));
+ if (tsk->signal->tty) {
+ printk(KERN_DEBUG "tty not NULL!!\n");
+ tty_kref_put(tsk->signal->tty);
+ }
}
put_pid(tsk->signal->tty_old_pgrp);
- tsk->signal->tty = tty;
+ tsk->signal->tty = tty_kref_get(tty);
tsk->signal->tty_old_pgrp = NULL;
}
@@ -3629,18 +3073,20 @@
struct tty_struct *get_current_tty(void)
{
struct tty_struct *tty;
- WARN_ON_ONCE(!mutex_is_locked(&tty_mutex));
- tty = current->signal->tty;
- /*
- * session->tty can be changed/cleared from under us, make sure we
- * issue the load. The obtained pointer, when not NULL, is valid as
- * long as we hold tty_mutex.
- */
- barrier();
+ unsigned long flags;
+
+ spin_lock_irqsave(¤t->sighand->siglock, flags);
+ tty = tty_kref_get(current->signal->tty);
+ spin_unlock_irqrestore(¤t->sighand->siglock, flags);
return tty;
}
EXPORT_SYMBOL_GPL(get_current_tty);
+void tty_default_fops(struct file_operations *fops)
+{
+ *fops = tty_fops;
+}
+
/*
* Initialize the console device. This is called *early*, so
* we can't necessarily depend on lots of kernel help here.
@@ -3678,12 +3124,6 @@
/* 3/2004 jmc: why do these devices exist? */
static struct cdev tty_cdev, console_cdev;
-#ifdef CONFIG_UNIX98_PTYS
-static struct cdev ptmx_cdev;
-#endif
-#ifdef CONFIG_VT
-static struct cdev vc0_cdev;
-#endif
/*
* Ok, now we can initialize the rest of the tty devices and can count
@@ -3695,32 +3135,18 @@
if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)
panic("Couldn't register /dev/tty driver\n");
- device_create_drvdata(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL,
+ device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL,
"tty");
cdev_init(&console_cdev, &console_fops);
if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) ||
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0)
panic("Couldn't register /dev/console driver\n");
- device_create_drvdata(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL,
+ device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL,
"console");
-#ifdef CONFIG_UNIX98_PTYS
- cdev_init(&ptmx_cdev, &ptmx_fops);
- if (cdev_add(&ptmx_cdev, MKDEV(TTYAUX_MAJOR, 2), 1) ||
- register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, "/dev/ptmx") < 0)
- panic("Couldn't register /dev/ptmx driver\n");
- device_create_drvdata(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx");
-#endif
-
#ifdef CONFIG_VT
- cdev_init(&vc0_cdev, &console_fops);
- if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) ||
- register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0)
- panic("Couldn't register /dev/tty0 driver\n");
- device_create_drvdata(tty_class, NULL, MKDEV(TTY_MAJOR, 0), NULL, "tty0");
-
- vty_init();
+ vty_init(&console_fops);
#endif
return 0;
}
diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c
index bf34e45..a408c8e 100644
--- a/drivers/char/tty_ioctl.c
+++ b/drivers/char/tty_ioctl.c
@@ -40,6 +40,15 @@
#define TERMIOS_OLD 8
+/**
+ * tty_chars_in_buffer - characters pending
+ * @tty: terminal
+ *
+ * Return the number of bytes of data in the device private
+ * output queue. If no private method is supplied there is assumed
+ * to be no queue on the device.
+ */
+
int tty_chars_in_buffer(struct tty_struct *tty)
{
if (tty->ops->chars_in_buffer)
@@ -47,26 +56,49 @@
else
return 0;
}
-
EXPORT_SYMBOL(tty_chars_in_buffer);
+/**
+ * tty_write_room - write queue space
+ * @tty: terminal
+ *
+ * Return the number of bytes that can be queued to this device
+ * at the present time. The result should be treated as a guarantee
+ * and the driver cannot offer a value it later shrinks by more than
+ * the number of bytes written. If no method is provided 2K is always
+ * returned and data may be lost as there will be no flow control.
+ */
+
int tty_write_room(struct tty_struct *tty)
{
if (tty->ops->write_room)
return tty->ops->write_room(tty);
return 2048;
}
-
EXPORT_SYMBOL(tty_write_room);
+/**
+ * tty_driver_flush_buffer - discard internal buffer
+ * @tty: terminal
+ *
+ * Discard the internal output buffer for this device. If no method
+ * is provided then either the buffer cannot be hardware flushed or
+ * there is no buffer driver side.
+ */
void tty_driver_flush_buffer(struct tty_struct *tty)
{
if (tty->ops->flush_buffer)
tty->ops->flush_buffer(tty);
}
-
EXPORT_SYMBOL(tty_driver_flush_buffer);
+/**
+ * tty_throttle - flow control
+ * @tty: terminal
+ *
+ * Indicate that a tty should stop transmitting data down the stack.
+ */
+
void tty_throttle(struct tty_struct *tty)
{
/* check TTY_THROTTLED first so it indicates our state */
@@ -76,6 +108,13 @@
}
EXPORT_SYMBOL(tty_throttle);
+/**
+ * tty_unthrottle - flow control
+ * @tty: terminal
+ *
+ * Indicate that a tty may continue transmitting data down the stack.
+ */
+
void tty_unthrottle(struct tty_struct *tty)
{
if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) &&
@@ -112,6 +151,11 @@
}
EXPORT_SYMBOL(tty_wait_until_sent);
+
+/*
+ * Termios Helper Methods
+ */
+
static void unset_locked_termios(struct ktermios *termios,
struct ktermios *old,
struct ktermios *locked)
@@ -346,6 +390,16 @@
}
EXPORT_SYMBOL_GPL(tty_termios_encode_baud_rate);
+/**
+ * tty_encode_baud_rate - set baud rate of the tty
+ * @ibaud: input baud rate
+ * @obad: output baud rate
+ *
+ * Update the current termios data for the tty with the new speed
+ * settings. The caller must hold the termios_mutex for the tty in
+ * question.
+ */
+
void tty_encode_baud_rate(struct tty_struct *tty, speed_t ibaud, speed_t obaud)
{
tty_termios_encode_baud_rate(tty->termios, ibaud, obaud);
@@ -430,12 +484,11 @@
* is a bit of layering violation here with n_tty in terms of the
* internal knowledge of this function.
*
- * Locking: termios_sem
+ * Locking: termios_mutex
*/
static void change_termios(struct tty_struct *tty, struct ktermios *new_termios)
{
- int canon_change;
struct ktermios old_termios;
struct tty_ldisc *ld;
unsigned long flags;
@@ -451,18 +504,6 @@
old_termios = *tty->termios;
*tty->termios = *new_termios;
unset_locked_termios(tty->termios, &old_termios, tty->termios_locked);
- canon_change = (old_termios.c_lflag ^ tty->termios->c_lflag) & ICANON;
- if (canon_change) {
- memset(&tty->read_flags, 0, sizeof tty->read_flags);
- tty->canon_head = tty->read_tail;
- tty->canon_data = 0;
- tty->erasing = 0;
- }
-
- /* This bit should be in the ldisc code */
- if (canon_change && !L_ICANON(tty) && tty->read_cnt)
- /* Get characters left over from canonical mode. */
- wake_up_interruptible(&tty->read_wait);
/* See if packet mode change of state. */
if (tty->link && tty->link->packet) {
@@ -508,7 +549,7 @@
* functions before using change_termios to do the actual changes.
*
* Locking:
- * Called functions take ldisc and termios_sem locks
+ * Called functions take ldisc and termios_mutex locks
*/
static int set_termios(struct tty_struct *tty, void __user *arg, int opt)
@@ -579,25 +620,51 @@
return 0;
}
-static unsigned long inq_canon(struct tty_struct *tty)
-{
- int nr, head, tail;
- if (!tty->canon_data || !tty->read_buf)
- return 0;
- head = tty->canon_head;
- tail = tty->read_tail;
- nr = (head - tail) & (N_TTY_BUF_SIZE-1);
- /* Skip EOF-chars.. */
- while (head != tail) {
- if (test_bit(tail, tty->read_flags) &&
- tty->read_buf[tail] == __DISABLED_CHAR)
- nr--;
- tail = (tail+1) & (N_TTY_BUF_SIZE-1);
+#ifdef TCGETX
+
+/**
+ * set_termiox - set termiox fields if possible
+ * @tty: terminal
+ * @arg: termiox structure from user
+ * @opt: option flags for ioctl type
+ *
+ * Implement the device calling points for the SYS5 termiox ioctl
+ * interface in Linux
+ */
+
+static int set_termiox(struct tty_struct *tty, void __user *arg, int opt)
+{
+ struct termiox tnew;
+ struct tty_ldisc *ld;
+
+ if (tty->termiox == NULL)
+ return -EINVAL;
+ if (copy_from_user(&tnew, arg, sizeof(struct termiox)))
+ return -EFAULT;
+
+ ld = tty_ldisc_ref(tty);
+ if (ld != NULL) {
+ if ((opt & TERMIOS_FLUSH) && ld->ops->flush_buffer)
+ ld->ops->flush_buffer(tty);
+ tty_ldisc_deref(ld);
}
- return nr;
+ if (opt & TERMIOS_WAIT) {
+ tty_wait_until_sent(tty, 0);
+ if (signal_pending(current))
+ return -EINTR;
+ }
+
+ mutex_lock(&tty->termios_mutex);
+ if (tty->ops->set_termiox)
+ tty->ops->set_termiox(tty, &tnew);
+ mutex_unlock(&tty->termios_mutex);
+ return 0;
}
+#endif
+
+
#ifdef TIOCGETP
/*
* These are deprecated, but there is limited support..
@@ -671,7 +738,7 @@
* Updates a terminal from the legacy BSD style terminal information
* structure.
*
- * Locking: termios_sem
+ * Locking: termios_mutex
*/
static int set_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb)
@@ -849,6 +916,7 @@
{
struct tty_struct *real_tty;
void __user *p = (void __user *)arg;
+ int ret = 0;
if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
tty->driver->subtype == PTY_TYPE_MASTER)
@@ -884,18 +952,24 @@
return set_termios(real_tty, p, TERMIOS_OLD);
#ifndef TCGETS2
case TCGETS:
+ mutex_lock(&real_tty->termios_mutex);
if (kernel_termios_to_user_termios((struct termios __user *)arg, real_tty->termios))
- return -EFAULT;
- return 0;
+ ret = -EFAULT;
+ mutex_unlock(&real_tty->termios_mutex);
+ return ret;
#else
case TCGETS:
+ mutex_lock(&real_tty->termios_mutex);
if (kernel_termios_to_user_termios_1((struct termios __user *)arg, real_tty->termios))
- return -EFAULT;
- return 0;
+ ret = -EFAULT;
+ mutex_unlock(&real_tty->termios_mutex);
+ return ret;
case TCGETS2:
+ mutex_lock(&real_tty->termios_mutex);
if (kernel_termios_to_user_termios((struct termios2 __user *)arg, real_tty->termios))
- return -EFAULT;
- return 0;
+ ret = -EFAULT;
+ mutex_unlock(&real_tty->termios_mutex);
+ return ret;
case TCSETSF2:
return set_termios(real_tty, p, TERMIOS_FLUSH | TERMIOS_WAIT);
case TCSETSW2:
@@ -913,34 +987,59 @@
return set_termios(real_tty, p, TERMIOS_TERMIO);
#ifndef TCGETS2
case TIOCGLCKTRMIOS:
+ mutex_lock(&real_tty->termios_mutex);
if (kernel_termios_to_user_termios((struct termios __user *)arg, real_tty->termios_locked))
- return -EFAULT;
- return 0;
+ ret = -EFAULT;
+ mutex_unlock(&real_tty->termios_mutex);
+ return ret;
case TIOCSLCKTRMIOS:
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
+ mutex_lock(&real_tty->termios_mutex);
if (user_termios_to_kernel_termios(real_tty->termios_locked,
(struct termios __user *) arg))
- return -EFAULT;
- return 0;
+ ret = -EFAULT;
+ mutex_unlock(&real_tty->termios_mutex);
+ return ret;
#else
case TIOCGLCKTRMIOS:
+ mutex_lock(&real_tty->termios_mutex);
if (kernel_termios_to_user_termios_1((struct termios __user *)arg, real_tty->termios_locked))
- return -EFAULT;
- return 0;
+ ret = -EFAULT;
+ mutex_unlock(&real_tty->termios_mutex);
+ return ret;
case TIOCSLCKTRMIOS:
if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
+ ret = -EPERM;
+ mutex_lock(&real_tty->termios_mutex);
if (user_termios_to_kernel_termios_1(real_tty->termios_locked,
(struct termios __user *) arg))
- return -EFAULT;
- return 0;
+ ret = -EFAULT;
+ mutex_unlock(&real_tty->termios_mutex);
+ return ret;
#endif
+#ifdef TCGETX
+ case TCGETX:
+ if (real_tty->termiox == NULL)
+ return -EINVAL;
+ mutex_lock(&real_tty->termios_mutex);
+ if (copy_to_user(p, real_tty->termiox, sizeof(struct termiox)))
+ ret = -EFAULT;
+ mutex_unlock(&real_tty->termios_mutex);
+ return ret;
+ case TCSETX:
+ return set_termiox(real_tty, p, 0);
+ case TCSETXW:
+ return set_termiox(real_tty, p, TERMIOS_WAIT);
+ case TCSETXF:
+ return set_termiox(real_tty, p, TERMIOS_FLUSH);
+#endif
case TIOCGSOFTCAR:
- /* FIXME: for correctness we may need to take the termios
- lock here - review */
- return put_user(C_CLOCAL(real_tty) ? 1 : 0,
+ mutex_lock(&real_tty->termios_mutex);
+ ret = put_user(C_CLOCAL(real_tty) ? 1 : 0,
(int __user *)arg);
+ mutex_unlock(&real_tty->termios_mutex);
+ return ret;
case TIOCSSOFTCAR:
if (get_user(arg, (unsigned int __user *) arg))
return -EFAULT;
@@ -980,7 +1079,7 @@
}
EXPORT_SYMBOL_GPL(tty_perform_flush);
-int n_tty_ioctl(struct tty_struct *tty, struct file *file,
+int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
unsigned long flags;
@@ -1018,13 +1117,6 @@
return 0;
case TCFLSH:
return tty_perform_flush(tty, arg);
- case TIOCOUTQ:
- return put_user(tty_chars_in_buffer(tty), (int __user *) arg);
- case TIOCINQ:
- retval = tty->read_cnt;
- if (L_ICANON(tty))
- retval = inq_canon(tty);
- return put_user(retval, (unsigned int __user *) arg);
case TIOCPKT:
{
int pktmode;
@@ -1050,4 +1142,4 @@
return tty_mode_ioctl(tty, file, cmd, arg);
}
}
-EXPORT_SYMBOL(n_tty_ioctl);
+EXPORT_SYMBOL(n_tty_ioctl_helper);
diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c
new file mode 100644
index 0000000..553b0e9
--- /dev/null
+++ b/drivers/char/tty_port.c
@@ -0,0 +1,96 @@
+/*
+ * Tty port functions
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+
+void tty_port_init(struct tty_port *port)
+{
+ memset(port, 0, sizeof(*port));
+ init_waitqueue_head(&port->open_wait);
+ init_waitqueue_head(&port->close_wait);
+ mutex_init(&port->mutex);
+ spin_lock_init(&port->lock);
+ port->close_delay = (50 * HZ) / 100;
+ port->closing_wait = (3000 * HZ) / 100;
+}
+EXPORT_SYMBOL(tty_port_init);
+
+int tty_port_alloc_xmit_buf(struct tty_port *port)
+{
+ /* We may sleep in get_zeroed_page() */
+ mutex_lock(&port->mutex);
+ if (port->xmit_buf == NULL)
+ port->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL);
+ mutex_unlock(&port->mutex);
+ if (port->xmit_buf == NULL)
+ return -ENOMEM;
+ return 0;
+}
+EXPORT_SYMBOL(tty_port_alloc_xmit_buf);
+
+void tty_port_free_xmit_buf(struct tty_port *port)
+{
+ mutex_lock(&port->mutex);
+ if (port->xmit_buf != NULL) {
+ free_page((unsigned long)port->xmit_buf);
+ port->xmit_buf = NULL;
+ }
+ mutex_unlock(&port->mutex);
+}
+EXPORT_SYMBOL(tty_port_free_xmit_buf);
+
+
+/**
+ * tty_port_tty_get - get a tty reference
+ * @port: tty port
+ *
+ * Return a refcount protected tty instance or NULL if the port is not
+ * associated with a tty (eg due to close or hangup)
+ */
+
+struct tty_struct *tty_port_tty_get(struct tty_port *port)
+{
+ unsigned long flags;
+ struct tty_struct *tty;
+
+ spin_lock_irqsave(&port->lock, flags);
+ tty = tty_kref_get(port->tty);
+ spin_unlock_irqrestore(&port->lock, flags);
+ return tty;
+}
+EXPORT_SYMBOL(tty_port_tty_get);
+
+/**
+ * tty_port_tty_set - set the tty of a port
+ * @port: tty port
+ * @tty: the tty
+ *
+ * Associate the port and tty pair. Manages any internal refcounts.
+ * Pass NULL to deassociate a port
+ */
+
+void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ if (port->tty)
+ tty_kref_put(port->tty);
+ port->tty = tty;
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+EXPORT_SYMBOL(tty_port_tty_set);
diff --git a/drivers/char/vt.c b/drivers/char/vt.c
index 60359c3..57029fe 100644
--- a/drivers/char/vt.c
+++ b/drivers/char/vt.c
@@ -100,10 +100,10 @@
#include <linux/font.h>
#include <linux/bitops.h>
#include <linux/notifier.h>
-
-#include <asm/io.h>
+#include <linux/device.h>
+#include <linux/io.h>
#include <asm/system.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#define MAX_NR_CON_DRIVER 16
@@ -2136,27 +2136,9 @@
release_console_sem();
return 0;
}
- release_console_sem();
-
orig_buf = buf;
orig_count = count;
- /* At this point 'buf' is guaranteed to be a kernel buffer
- * and therefore no access to userspace (and therefore sleeping)
- * will be needed. The con_buf_mtx serializes all tty based
- * console rendering and vcs write/read operations. We hold
- * the console spinlock during the entire write.
- */
-
- acquire_console_sem();
-
- vc = tty->driver_data;
- if (vc == NULL) {
- printk(KERN_ERR "vt: argh, driver_data _became_ NULL !\n");
- release_console_sem();
- goto out;
- }
-
himask = vc->vc_hi_font_mask;
charmask = himask ? 0x1ff : 0xff;
@@ -2370,8 +2352,6 @@
FLUSH
console_conditional_schedule();
release_console_sem();
-
-out:
notify_update(vc);
return n;
#undef FLUSH
@@ -2583,8 +2563,6 @@
int lines;
int ret;
- if (tty->driver->type != TTY_DRIVER_TYPE_CONSOLE)
- return -EINVAL;
if (current->signal->tty != tty && !capable(CAP_SYS_ADMIN))
return -EPERM;
if (get_user(type, p))
@@ -2778,6 +2756,12 @@
ret = vc_allocate(currcons);
if (ret == 0) {
struct vc_data *vc = vc_cons[currcons].d;
+
+ /* Still being freed */
+ if (vc->vc_tty) {
+ release_console_sem();
+ return -ERESTARTSYS;
+ }
tty->driver_data = vc;
vc->vc_tty = tty;
@@ -2798,34 +2782,20 @@
return ret;
}
-/*
- * We take tty_mutex in here to prevent another thread from coming in via init_dev
- * and taking a ref against the tty while we're in the process of forgetting
- * about it and cleaning things up.
- *
- * This is because vcs_remove_sysfs() can sleep and will drop the BKL.
- */
static void con_close(struct tty_struct *tty, struct file *filp)
{
- mutex_lock(&tty_mutex);
- acquire_console_sem();
- if (tty && tty->count == 1) {
- struct vc_data *vc = tty->driver_data;
+ /* Nothing to do - we defer to shutdown */
+}
- if (vc)
- vc->vc_tty = NULL;
- tty->driver_data = NULL;
- vcs_remove_sysfs(tty);
- release_console_sem();
- mutex_unlock(&tty_mutex);
- /*
- * tty_mutex is released, but we still hold BKL, so there is
- * still exclusion against init_dev()
- */
- return;
- }
+static void con_shutdown(struct tty_struct *tty)
+{
+ struct vc_data *vc = tty->driver_data;
+ BUG_ON(vc == NULL);
+ acquire_console_sem();
+ vc->vc_tty = NULL;
+ vcs_remove_sysfs(tty);
release_console_sem();
- mutex_unlock(&tty_mutex);
+ tty_shutdown(tty);
}
static int default_italic_color = 2; // green (ASCII)
@@ -2950,10 +2920,19 @@
.throttle = con_throttle,
.unthrottle = con_unthrottle,
.resize = vt_resize,
+ .shutdown = con_shutdown
};
-int __init vty_init(void)
+static struct cdev vc0_cdev;
+
+int __init vty_init(const struct file_operations *console_fops)
{
+ cdev_init(&vc0_cdev, console_fops);
+ if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) ||
+ register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0)
+ panic("Couldn't register /dev/tty0 driver\n");
+ device_create(tty_class, NULL, MKDEV(TTY_MAJOR, 0), NULL, "tty0");
+
vcs_init();
console_driver = alloc_tty_driver(MAX_NR_CONSOLES);
@@ -2972,7 +2951,6 @@
tty_set_operations(console_driver, &con_ops);
if (tty_register_driver(console_driver))
panic("Couldn't register console driver\n");
-
kbd_init();
console_map_init();
#ifdef CONFIG_PROM_CONSOLE
@@ -3466,7 +3444,7 @@
if (retval)
goto err;
- con_driver->dev = device_create_drvdata(vtconsole_class, NULL,
+ con_driver->dev = device_create(vtconsole_class, NULL,
MKDEV(0, con_driver->node),
NULL, "vtcon%i",
con_driver->node);
@@ -3577,7 +3555,7 @@
struct con_driver *con = ®istered_con_driver[i];
if (con->con && !con->dev) {
- con->dev = device_create_drvdata(vtconsole_class, NULL,
+ con->dev = device_create(vtconsole_class, NULL,
MKDEV(0, con->node),
NULL, "vtcon%i",
con->node);
diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c
index c904e9a..8944ce5 100644
--- a/drivers/char/vt_ioctl.c
+++ b/drivers/char/vt_ioctl.c
@@ -395,6 +395,8 @@
kbd = kbd_table + console;
switch (cmd) {
+ case TIOCLINUX:
+ return tioclinux(tty, arg);
case KIOCSOUND:
if (!perm)
goto eperm;
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 3309e86..ebacc0a 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -800,6 +800,13 @@
This driver can also be built as a module. If so, the module
will be called w83627ehf.
+config SENSORS_ULTRA45
+ tristate "Sun Ultra45 PIC16F747"
+ depends on SPARC64
+ help
+ This driver provides support for the Ultra45 workstation environmental
+ sensors.
+
config SENSORS_HDAPS
tristate "IBM Hard Drive Active Protection System (hdaps)"
depends on INPUT && X86
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 6babc80..042d5a7 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -41,6 +41,7 @@
obj-$(CONFIG_SENSORS_FSCPOS) += fscpos.o
obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o
+obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o
obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o
obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o
diff --git a/drivers/hwmon/ultra45_env.c b/drivers/hwmon/ultra45_env.c
new file mode 100644
index 0000000..68e90ab
--- /dev/null
+++ b/drivers/hwmon/ultra45_env.c
@@ -0,0 +1,320 @@
+/* ultra45_env.c: Driver for Ultra45 PIC16F747 environmental monitor.
+ *
+ * Copyright (C) 2008 David S. Miller <davem@davemloft.net>
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <linux/io.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+#define DRV_MODULE_VERSION "0.1"
+
+MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
+MODULE_DESCRIPTION("Ultra45 environmental monitor driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_MODULE_VERSION);
+
+/* PIC device registers */
+#define REG_CMD 0x00UL
+#define REG_CMD_RESET 0x80
+#define REG_CMD_ESTAR 0x01
+#define REG_STAT 0x01UL
+#define REG_STAT_FWVER 0xf0
+#define REG_STAT_TGOOD 0x08
+#define REG_STAT_STALE 0x04
+#define REG_STAT_BUSY 0x02
+#define REG_STAT_FAULT 0x01
+#define REG_DATA 0x40UL
+#define REG_ADDR 0x41UL
+#define REG_SIZE 0x42UL
+
+/* Registers accessed indirectly via REG_DATA/REG_ADDR */
+#define IREG_FAN0 0x00
+#define IREG_FAN1 0x01
+#define IREG_FAN2 0x02
+#define IREG_FAN3 0x03
+#define IREG_FAN4 0x04
+#define IREG_FAN5 0x05
+#define IREG_LCL_TEMP 0x06
+#define IREG_RMT1_TEMP 0x07
+#define IREG_RMT2_TEMP 0x08
+#define IREG_RMT3_TEMP 0x09
+#define IREG_LM95221_TEMP 0x0a
+#define IREG_FIRE_TEMP 0x0b
+#define IREG_LSI1064_TEMP 0x0c
+#define IREG_FRONT_TEMP 0x0d
+#define IREG_FAN_STAT 0x0e
+#define IREG_VCORE0 0x0f
+#define IREG_VCORE1 0x10
+#define IREG_VMEM0 0x11
+#define IREG_VMEM1 0x12
+#define IREG_PSU_TEMP 0x13
+
+struct env {
+ void __iomem *regs;
+ spinlock_t lock;
+
+ struct device *hwmon_dev;
+};
+
+static u8 env_read(struct env *p, u8 ireg)
+{
+ u8 ret;
+
+ spin_lock(&p->lock);
+ writeb(ireg, p->regs + REG_ADDR);
+ ret = readb(p->regs + REG_DATA);
+ spin_unlock(&p->lock);
+
+ return ret;
+}
+
+static void env_write(struct env *p, u8 ireg, u8 val)
+{
+ spin_lock(&p->lock);
+ writeb(ireg, p->regs + REG_ADDR);
+ writeb(val, p->regs + REG_DATA);
+ spin_unlock(&p->lock);
+}
+
+/* There seems to be a adr7462 providing these values, thus a lot
+ * of these calculations are borrowed from the adt7470 driver.
+ */
+#define FAN_PERIOD_TO_RPM(x) ((90000 * 60) / (x))
+#define FAN_RPM_TO_PERIOD FAN_PERIOD_TO_RPM
+#define FAN_PERIOD_INVALID (0xff << 8)
+#define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID)
+
+static ssize_t show_fan_speed(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int fan_nr = to_sensor_dev_attr(attr)->index;
+ struct env *p = dev_get_drvdata(dev);
+ int rpm, period;
+ u8 val;
+
+ val = env_read(p, IREG_FAN0 + fan_nr);
+ period = (int) val << 8;
+ if (FAN_DATA_VALID(period))
+ rpm = FAN_PERIOD_TO_RPM(period);
+ else
+ rpm = 0;
+
+ return sprintf(buf, "%d\n", rpm);
+}
+
+static ssize_t set_fan_speed(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int fan_nr = to_sensor_dev_attr(attr)->index;
+ int rpm = simple_strtol(buf, NULL, 10);
+ struct env *p = dev_get_drvdata(dev);
+ int period;
+ u8 val;
+
+ if (!rpm)
+ return -EINVAL;
+
+ period = FAN_RPM_TO_PERIOD(rpm);
+ val = period >> 8;
+ env_write(p, IREG_FAN0 + fan_nr, val);
+
+ return count;
+}
+
+static ssize_t show_fan_fault(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int fan_nr = to_sensor_dev_attr(attr)->index;
+ struct env *p = dev_get_drvdata(dev);
+ u8 val = env_read(p, IREG_FAN_STAT);
+ return sprintf(buf, "%d\n", (val & (1 << fan_nr)) ? 1 : 0);
+}
+
+#define fan(index) \
+static SENSOR_DEVICE_ATTR(fan##index##_speed, S_IRUGO | S_IWUSR, \
+ show_fan_speed, set_fan_speed, index); \
+static SENSOR_DEVICE_ATTR(fan##index##_fault, S_IRUGO, \
+ show_fan_fault, NULL, index)
+
+fan(0);
+fan(1);
+fan(2);
+fan(3);
+fan(4);
+
+static SENSOR_DEVICE_ATTR(psu_fan_fault, S_IRUGO, show_fan_fault, NULL, 6);
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int temp_nr = to_sensor_dev_attr(attr)->index;
+ struct env *p = dev_get_drvdata(dev);
+ s8 val;
+
+ val = env_read(p, IREG_LCL_TEMP + temp_nr);
+ return sprintf(buf, "%d\n", ((int) val) - 64);
+}
+
+static SENSOR_DEVICE_ATTR(adt7462_local_temp, S_IRUGO, show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(cpu0_temp, S_IRUGO, show_temp, NULL, 1);
+static SENSOR_DEVICE_ATTR(cpu1_temp, S_IRUGO, show_temp, NULL, 2);
+static SENSOR_DEVICE_ATTR(motherboard_temp, S_IRUGO, show_temp, NULL, 3);
+static SENSOR_DEVICE_ATTR(lm95221_local_temp, S_IRUGO, show_temp, NULL, 4);
+static SENSOR_DEVICE_ATTR(fire_temp, S_IRUGO, show_temp, NULL, 5);
+static SENSOR_DEVICE_ATTR(lsi1064_local_temp, S_IRUGO, show_temp, NULL, 6);
+static SENSOR_DEVICE_ATTR(front_panel_temp, S_IRUGO, show_temp, NULL, 7);
+static SENSOR_DEVICE_ATTR(psu_temp, S_IRUGO, show_temp, NULL, 13);
+
+static ssize_t show_stat_bit(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int index = to_sensor_dev_attr(attr)->index;
+ struct env *p = dev_get_drvdata(dev);
+ u8 val;
+
+ val = readb(p->regs + REG_STAT);
+ return sprintf(buf, "%d\n", (val & (1 << index)) ? 1 : 0);
+}
+
+static SENSOR_DEVICE_ATTR(fan_failure, S_IRUGO, show_stat_bit, NULL, 0);
+static SENSOR_DEVICE_ATTR(env_bus_busy, S_IRUGO, show_stat_bit, NULL, 1);
+static SENSOR_DEVICE_ATTR(env_data_stale, S_IRUGO, show_stat_bit, NULL, 2);
+static SENSOR_DEVICE_ATTR(tpm_self_test_passed, S_IRUGO, show_stat_bit, NULL, 3);
+
+static ssize_t show_fwver(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct env *p = dev_get_drvdata(dev);
+ u8 val;
+
+ val = readb(p->regs + REG_STAT);
+ return sprintf(buf, "%d\n", val >> 4);
+}
+
+static SENSOR_DEVICE_ATTR(firmware_version, S_IRUGO, show_fwver, NULL, 0);
+
+static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "ultra45\n");
+}
+
+static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
+
+static struct attribute *env_attributes[] = {
+ &sensor_dev_attr_fan0_speed.dev_attr.attr,
+ &sensor_dev_attr_fan0_fault.dev_attr.attr,
+ &sensor_dev_attr_fan1_speed.dev_attr.attr,
+ &sensor_dev_attr_fan1_fault.dev_attr.attr,
+ &sensor_dev_attr_fan2_speed.dev_attr.attr,
+ &sensor_dev_attr_fan2_fault.dev_attr.attr,
+ &sensor_dev_attr_fan3_speed.dev_attr.attr,
+ &sensor_dev_attr_fan3_fault.dev_attr.attr,
+ &sensor_dev_attr_fan4_speed.dev_attr.attr,
+ &sensor_dev_attr_fan4_fault.dev_attr.attr,
+ &sensor_dev_attr_psu_fan_fault.dev_attr.attr,
+ &sensor_dev_attr_adt7462_local_temp.dev_attr.attr,
+ &sensor_dev_attr_cpu0_temp.dev_attr.attr,
+ &sensor_dev_attr_cpu1_temp.dev_attr.attr,
+ &sensor_dev_attr_motherboard_temp.dev_attr.attr,
+ &sensor_dev_attr_lm95221_local_temp.dev_attr.attr,
+ &sensor_dev_attr_fire_temp.dev_attr.attr,
+ &sensor_dev_attr_lsi1064_local_temp.dev_attr.attr,
+ &sensor_dev_attr_front_panel_temp.dev_attr.attr,
+ &sensor_dev_attr_psu_temp.dev_attr.attr,
+ &sensor_dev_attr_fan_failure.dev_attr.attr,
+ &sensor_dev_attr_env_bus_busy.dev_attr.attr,
+ &sensor_dev_attr_env_data_stale.dev_attr.attr,
+ &sensor_dev_attr_tpm_self_test_passed.dev_attr.attr,
+ &sensor_dev_attr_firmware_version.dev_attr.attr,
+ &sensor_dev_attr_name.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group env_group = {
+ .attrs = env_attributes,
+};
+
+static int __devinit env_probe(struct of_device *op,
+ const struct of_device_id *match)
+{
+ struct env *p = kzalloc(sizeof(*p), GFP_KERNEL);
+ int err = -ENOMEM;
+
+ if (!p)
+ goto out;
+
+ spin_lock_init(&p->lock);
+
+ p->regs = of_ioremap(&op->resource[0], 0, REG_SIZE, "pic16f747");
+ if (!p->regs)
+ goto out_free;
+
+ err = sysfs_create_group(&op->dev.kobj, &env_group);
+ if (err)
+ goto out_iounmap;
+
+ p->hwmon_dev = hwmon_device_register(&op->dev);
+ if (IS_ERR(p->hwmon_dev)) {
+ err = PTR_ERR(p->hwmon_dev);
+ goto out_sysfs_remove_group;
+ }
+
+ dev_set_drvdata(&op->dev, p);
+ err = 0;
+
+out:
+ return err;
+
+out_sysfs_remove_group:
+ sysfs_remove_group(&op->dev.kobj, &env_group);
+
+out_iounmap:
+ of_iounmap(&op->resource[0], p->regs, REG_SIZE);
+
+out_free:
+ kfree(p);
+ goto out;
+}
+
+static int __devexit env_remove(struct of_device *op)
+{
+ struct env *p = dev_get_drvdata(&op->dev);
+
+ if (p) {
+ sysfs_remove_group(&op->dev.kobj, &env_group);
+ hwmon_device_unregister(p->hwmon_dev);
+ of_iounmap(&op->resource[0], p->regs, REG_SIZE);
+ kfree(p);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id env_match[] = {
+ {
+ .name = "env-monitor",
+ .compatible = "SUNW,ebus-pic16f747-env",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, env_match);
+
+static struct of_platform_driver env_driver = {
+ .name = "ultra45_env",
+ .match_table = env_match,
+ .probe = env_probe,
+ .remove = __devexit_p(env_remove),
+};
+
+static int __init env_init(void)
+{
+ return of_register_driver(&env_driver, &of_bus_type);
+}
+
+static void __exit env_exit(void)
+{
+ of_unregister_driver(&env_driver);
+}
+
+module_init(env_init);
+module_exit(env_exit);
diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig
index 052879a..6c6dd2f 100644
--- a/drivers/ide/Kconfig
+++ b/drivers/ide/Kconfig
@@ -54,38 +54,6 @@
if IDE
-config BLK_DEV_IDE
- tristate "Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support"
- ---help---
- If you say Y here, you will use the full-featured IDE driver to
- control up to ten ATA/IDE interfaces, each being able to serve a
- "master" and a "slave" device, for a total of up to twenty ATA/IDE
- disk/cdrom/tape/floppy drives.
-
- Useful information about large (>540 MB) IDE disks, multiple
- interfaces, what to do if ATA/IDE devices are not automatically
- detected, sound card ATA/IDE ports, module support, and other
- topics, is contained in <file:Documentation/ide/ide.txt>. For detailed
- information about hard drives, consult the Disk-HOWTO and the
- Multi-Disk-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>.
-
- To fine-tune ATA/IDE drive/interface parameters for improved
- performance, look for the hdparm package at
- <ftp://ibiblio.org/pub/Linux/system/hardware/>.
-
- To compile this driver as a module, choose M here and read
- <file:Documentation/ide/ide.txt>. The module will be called ide-mod.
- Do not compile this driver as a module if your root file system (the
- one containing the directory /) is located on an IDE device.
-
- If you have one or more IDE drives, say Y or M here. If your system
- has no IDE drives, or if memory requirements are really tight, you
- could say N here, and select the "Old hard disk driver" below
- instead to save about 13 KB of memory in the kernel.
-
-if BLK_DEV_IDE
-
comment "Please see Documentation/ide/ide.txt for help/info on IDE drives"
config IDE_TIMINGS
@@ -131,29 +99,6 @@
If unsure, say Y.
-config IDEDISK_MULTI_MODE
- bool "Use multiple sector mode for Programmed Input/Output by default"
- help
- This setting is irrelevant for most IDE disks, with direct memory
- access, to which multiple sector mode does not apply. Multiple sector
- mode is a feature of most modern IDE hard drives, permitting the
- transfer of multiple sectors per Programmed Input/Output interrupt,
- rather than the usual one sector per interrupt. When this feature is
- enabled, it can reduce operating system overhead for disk Programmed
- Input/Output. On some systems, it also can increase the data
- throughput of Programmed Input/Output. Some drives, however, seemed
- to run slower with multiple sector mode enabled. Some drives claimed
- to support multiple sector mode, but lost data at some settings.
- Under rare circumstances, such failures could result in massive
- filesystem corruption.
-
- If you get the following error, try to say Y here:
-
- hda: set_multmode: status=0x51 { DriveReady SeekComplete Error }
- hda: set_multmode: error=0x04 { DriveStatusError }
-
- If in doubt, say N.
-
config BLK_DEV_IDECS
tristate "PCMCIA IDE support"
depends on PCMCIA
@@ -371,7 +316,7 @@
config IDEPCI_PCIBUS_ORDER
bool "Probe IDE PCI devices in the PCI bus order (DEPRECATED)"
- depends on BLK_DEV_IDE=y && BLK_DEV_IDEPCI
+ depends on IDE=y && BLK_DEV_IDEPCI
default y
help
Probe IDE PCI devices in the order in which they appear on the
@@ -752,7 +697,7 @@
config BLK_DEV_IDE_PMAC
tristate "PowerMac on-board IDE support"
- depends on PPC_PMAC && IDE=y && BLK_DEV_IDE=y
+ depends on PPC_PMAC && IDE=y
select IDE_TIMINGS
help
This driver provides support for the on-board IDE controller on
@@ -986,6 +931,4 @@
def_bool BLK_DEV_IDEDMA_SFF || BLK_DEV_IDEDMA_PMAC || \
BLK_DEV_IDEDMA_ICS || BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA
-endif
-
endif # IDE
diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile
index 64e0ecd..ceaf779 100644
--- a/drivers/ide/Makefile
+++ b/drivers/ide/Makefile
@@ -4,25 +4,26 @@
EXTRA_CFLAGS += -Idrivers/ide
-ide-core-y += ide.o ide-io.o ide-iops.o ide-lib.o ide-probe.o ide-taskfile.o \
- ide-pio-blacklist.o
+ide-core-y += ide.o ide-ioctls.o ide-io.o ide-iops.o ide-lib.o ide-probe.o \
+ ide-taskfile.o ide-park.o ide-pio-blacklist.o
# core IDE code
ide-core-$(CONFIG_IDE_TIMINGS) += ide-timings.o
ide-core-$(CONFIG_IDE_ATAPI) += ide-atapi.o
ide-core-$(CONFIG_BLK_DEV_IDEPCI) += setup-pci.o
ide-core-$(CONFIG_BLK_DEV_IDEDMA) += ide-dma.o
+ide-core-$(CONFIG_BLK_DEV_IDEDMA_SFF) += ide-dma-sff.o
ide-core-$(CONFIG_IDE_PROC_FS) += ide-proc.o
ide-core-$(CONFIG_BLK_DEV_IDEACPI) += ide-acpi.o
-obj-$(CONFIG_BLK_DEV_IDE) += ide-core.o
+obj-$(CONFIG_IDE) += ide-core.o
ifeq ($(CONFIG_IDE_ARM), y)
ide-arm-core-y += arm/ide_arm.o
obj-y += ide-arm-core.o
endif
-obj-$(CONFIG_BLK_DEV_IDE) += legacy/ pci/
+obj-$(CONFIG_IDE) += legacy/ pci/
obj-$(CONFIG_IDEPCI_PCIBUS_ORDER) += ide-scan-pci.o
@@ -31,17 +32,24 @@
obj-y += cmd640-core.o
endif
-obj-$(CONFIG_BLK_DEV_IDE) += ppc/
+obj-$(CONFIG_IDE) += ppc/
obj-$(CONFIG_IDE_H8300) += h8300/
obj-$(CONFIG_IDE_GENERIC) += ide-generic.o
obj-$(CONFIG_BLK_DEV_IDEPNP) += ide-pnp.o
+ide-disk_mod-y += ide-disk.o ide-disk_ioctl.o
ide-cd_mod-y += ide-cd.o ide-cd_ioctl.o ide-cd_verbose.o
+ide-floppy_mod-y += ide-floppy.o ide-floppy_ioctl.o
-obj-$(CONFIG_BLK_DEV_IDEDISK) += ide-disk.o
+ifeq ($(CONFIG_IDE_PROC_FS), y)
+ ide-disk_mod-y += ide-disk_proc.o
+ ide-floppy_mod-y += ide-floppy_proc.o
+endif
+
+obj-$(CONFIG_BLK_DEV_IDEDISK) += ide-disk_mod.o
obj-$(CONFIG_BLK_DEV_IDECD) += ide-cd_mod.o
+obj-$(CONFIG_BLK_DEV_IDEFLOPPY) += ide-floppy_mod.o
obj-$(CONFIG_BLK_DEV_IDETAPE) += ide-tape.o
-obj-$(CONFIG_BLK_DEV_IDEFLOPPY) += ide-floppy.o
ifeq ($(CONFIG_BLK_DEV_IDECS), y)
ide-cs-core-y += legacy/ide-cs.o
@@ -53,4 +61,4 @@
obj-y += ide-platform-core.o
endif
-obj-$(CONFIG_BLK_DEV_IDE) += arm/ mips/
+obj-$(CONFIG_IDE) += arm/ mips/
diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c
index df4af40..76bdc9a 100644
--- a/drivers/ide/arm/icside.c
+++ b/drivers/ide/arm/icside.c
@@ -10,7 +10,6 @@
#include <linux/slab.h>
#include <linux/blkdev.h>
#include <linux/errno.h>
-#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/dma-mapping.h>
#include <linux/device.h>
@@ -265,8 +264,8 @@
* If we're going to be doing MW_DMA_1 or MW_DMA_2, we should
* take care to note the values in the ID...
*/
- if (use_dma_info && drive->id->eide_dma_time > cycle_time)
- cycle_time = drive->id->eide_dma_time;
+ if (use_dma_info && drive->id[ATA_ID_EIDE_DMA_TIME] > cycle_time)
+ cycle_time = drive->id[ATA_ID_EIDE_DMA_TIME];
drive->drive_data = cycle_time;
@@ -373,25 +372,6 @@
ICS_ARCIN_V6_INTRSTAT_1)) & 1;
}
-static void icside_dma_timeout(ide_drive_t *drive)
-{
- ide_hwif_t *hwif = drive->hwif;
-
- printk(KERN_ERR "%s: DMA timeout occurred: ", drive->name);
-
- if (icside_dma_test_irq(drive))
- return;
-
- ide_dump_status(drive, "DMA timeout", hwif->tp_ops->read_status(hwif));
-
- icside_dma_end(drive);
-}
-
-static void icside_dma_lost_irq(ide_drive_t *drive)
-{
- printk(KERN_ERR "%s: IRQ lost\n", drive->name);
-}
-
static int icside_dma_init(ide_hwif_t *hwif, const struct ide_port_info *d)
{
hwif->dmatable_cpu = NULL;
@@ -407,8 +387,8 @@
.dma_start = icside_dma_start,
.dma_end = icside_dma_end,
.dma_test_irq = icside_dma_test_irq,
- .dma_timeout = icside_dma_timeout,
- .dma_lost_irq = icside_dma_lost_irq,
+ .dma_timeout = ide_dma_timeout,
+ .dma_lost_irq = ide_dma_lost_irq,
};
#else
#define icside_v6_dma_ops NULL
diff --git a/drivers/ide/arm/palm_bk3710.c b/drivers/ide/arm/palm_bk3710.c
index 4fd91dc..122ed3c 100644
--- a/drivers/ide/arm/palm_bk3710.c
+++ b/drivers/ide/arm/palm_bk3710.c
@@ -27,7 +27,6 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
-#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/delay.h>
#include <linux/init.h>
@@ -180,7 +179,7 @@
val32 |= (t2i << (dev ? 8 : 0));
writel(val32, base + BK3710_DATRCVR);
- if (mate && mate->present) {
+ if (mate) {
u8 mode2 = ide_get_best_pio_mode(mate, 255, 4);
if (mode2 < mode)
@@ -213,7 +212,8 @@
palm_bk3710_setudmamode(base, is_slave,
xferspeed - XFER_UDMA_0);
} else {
- palm_bk3710_setdmamode(base, is_slave, drive->id->eide_dma_min,
+ palm_bk3710_setdmamode(base, is_slave,
+ drive->id[ATA_ID_EIDE_DMA_MIN],
xferspeed);
}
}
@@ -229,7 +229,7 @@
* Obtain the drive PIO data for tuning the Palm Chip registers
*/
cycle_time = ide_pio_cycle_time(drive, pio);
- mate = ide_get_paired_drive(drive);
+ mate = ide_get_pair_dev(drive);
palm_bk3710_setpiomode(base, mate, is_slave, cycle_time, pio);
}
diff --git a/drivers/ide/h8300/ide-h8300.c b/drivers/ide/h8300/ide-h8300.c
index bde7a58..e2cdd2e 100644
--- a/drivers/ide/h8300/ide-h8300.c
+++ b/drivers/ide/h8300/ide-h8300.c
@@ -80,7 +80,7 @@
outb(tf->lbah, io_ports->lbah_addr);
if (task->tf_flags & IDE_TFLAG_OUT_DEVICE)
- outb((tf->device & HIHI) | drive->select.all,
+ outb((tf->device & HIHI) | drive->select,
io_ports->device_addr);
}
diff --git a/drivers/ide/ide-acpi.c b/drivers/ide/ide-acpi.c
index 6f70462..244a8a0 100644
--- a/drivers/ide/ide-acpi.c
+++ b/drivers/ide/ide-acpi.c
@@ -290,7 +290,7 @@
DEBPRINT("ENTER: %s at %s, port#: %d, hard_port#: %d\n",
hwif->name, dev->bus_id, port, hwif->channel);
- if (!drive->present) {
+ if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0) {
DEBPRINT("%s drive %d:%d not present\n",
hwif->name, hwif->channel, port);
goto out;
@@ -420,8 +420,9 @@
DEBPRINT("ENTER: %s, hard_port#: %d\n", drive->name, drive->dn);
- if (!drive->present)
+ if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0)
goto out;
+
if (!gtf_count) /* shouldn't be here */
goto out;
@@ -584,7 +585,7 @@
* This function executes the _STM ACPI method for the target channel.
*
* _STM requires Identify Drive data, which has to passed as an argument.
- * Unfortunately hd_driveid is a mangled version which we can't readily
+ * Unfortunately drive->id is a mangled version which we can't readily
* use; hence we'll get the information afresh.
*/
void ide_acpi_push_timing(ide_hwif_t *hwif)
@@ -614,10 +615,10 @@
in_params[0].buffer.length = sizeof(struct GTM_buffer);
in_params[0].buffer.pointer = (u8 *)&hwif->acpidata->gtm;
in_params[1].type = ACPI_TYPE_BUFFER;
- in_params[1].buffer.length = sizeof(struct hd_driveid);
+ in_params[1].buffer.length = sizeof(ATA_ID_WORDS * 2);
in_params[1].buffer.pointer = (u8 *)&master->idbuff;
in_params[2].type = ACPI_TYPE_BUFFER;
- in_params[2].buffer.length = sizeof(struct hd_driveid);
+ in_params[2].buffer.length = sizeof(ATA_ID_WORDS * 2);
in_params[2].buffer.pointer = (u8 *)&slave->idbuff;
/* Output buffer: _STM has no output */
@@ -660,7 +661,8 @@
if (!drive->acpidata->obj_handle)
drive->acpidata->obj_handle = ide_acpi_drive_get_handle(drive);
- if (drive->acpidata->obj_handle && drive->present) {
+ if (drive->acpidata->obj_handle &&
+ (drive->dev_flags & IDE_DFLAG_PRESENT)) {
acpi_bus_set_power(drive->acpidata->obj_handle,
on? ACPI_STATE_D0: ACPI_STATE_D3);
}
@@ -720,7 +722,7 @@
memset(drive->acpidata, 0, sizeof(*drive->acpidata));
- if (!drive->present)
+ if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0)
continue;
err = taskfile_lib_get_identify(drive, drive->acpidata->idbuff);
@@ -745,7 +747,7 @@
for (i = 0; i < MAX_DRIVES; i++) {
drive = &hwif->drives[i];
- if (drive->present)
+ if (drive->dev_flags & IDE_DFLAG_PRESENT)
/* Execute ACPI startup code */
ide_acpi_exec_tfs(drive);
}
diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c
index adf04f9..2e30571 100644
--- a/drivers/ide/ide-atapi.c
+++ b/drivers/ide/ide-atapi.c
@@ -14,25 +14,268 @@
#define debug_log(fmt, args...) do {} while (0)
#endif
-/* TODO: unify the code thus making some arguments go away */
-ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc,
- ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry,
- void (*update_buffers)(ide_drive_t *, struct ide_atapi_pc *),
- void (*retry_pc)(ide_drive_t *), void (*dsc_handle)(ide_drive_t *),
- void (*io_buffers)(ide_drive_t *, struct ide_atapi_pc *, unsigned, int))
+/*
+ * Check whether we can support a device,
+ * based on the ATAPI IDENTIFY command results.
+ */
+int ide_check_atapi_device(ide_drive_t *drive, const char *s)
{
+ u16 *id = drive->id;
+ u8 gcw[2], protocol, device_type, removable, drq_type, packet_size;
+
+ *((u16 *)&gcw) = id[ATA_ID_CONFIG];
+
+ protocol = (gcw[1] & 0xC0) >> 6;
+ device_type = gcw[1] & 0x1F;
+ removable = (gcw[0] & 0x80) >> 7;
+ drq_type = (gcw[0] & 0x60) >> 5;
+ packet_size = gcw[0] & 0x03;
+
+#ifdef CONFIG_PPC
+ /* kludge for Apple PowerBook internal zip */
+ if (drive->media == ide_floppy && device_type == 5 &&
+ !strstr((char *)&id[ATA_ID_PROD], "CD-ROM") &&
+ strstr((char *)&id[ATA_ID_PROD], "ZIP"))
+ device_type = 0;
+#endif
+
+ if (protocol != 2)
+ printk(KERN_ERR "%s: %s: protocol (0x%02x) is not ATAPI\n",
+ s, drive->name, protocol);
+ else if ((drive->media == ide_floppy && device_type != 0) ||
+ (drive->media == ide_tape && device_type != 1))
+ printk(KERN_ERR "%s: %s: invalid device type (0x%02x)\n",
+ s, drive->name, device_type);
+ else if (removable == 0)
+ printk(KERN_ERR "%s: %s: the removable flag is not set\n",
+ s, drive->name);
+ else if (drive->media == ide_floppy && drq_type == 3)
+ printk(KERN_ERR "%s: %s: sorry, DRQ type (0x%02x) not "
+ "supported\n", s, drive->name, drq_type);
+ else if (packet_size != 0)
+ printk(KERN_ERR "%s: %s: packet size (0x%02x) is not 12 "
+ "bytes\n", s, drive->name, packet_size);
+ else
+ return 1;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ide_check_atapi_device);
+
+/* PIO data transfer routine using the scatter gather table. */
+int ide_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc,
+ unsigned int bcount, int write)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ const struct ide_tp_ops *tp_ops = hwif->tp_ops;
+ xfer_func_t *xf = write ? tp_ops->output_data : tp_ops->input_data;
+ struct scatterlist *sg = pc->sg;
+ char *buf;
+ int count, done = 0;
+
+ while (bcount) {
+ count = min(sg->length - pc->b_count, bcount);
+
+ if (PageHighMem(sg_page(sg))) {
+ unsigned long flags;
+
+ local_irq_save(flags);
+ buf = kmap_atomic(sg_page(sg), KM_IRQ0) + sg->offset;
+ xf(drive, NULL, buf + pc->b_count, count);
+ kunmap_atomic(buf - sg->offset, KM_IRQ0);
+ local_irq_restore(flags);
+ } else {
+ buf = sg_virt(sg);
+ xf(drive, NULL, buf + pc->b_count, count);
+ }
+
+ bcount -= count;
+ pc->b_count += count;
+ done += count;
+
+ if (pc->b_count == sg->length) {
+ if (!--pc->sg_cnt)
+ break;
+ pc->sg = sg = sg_next(sg);
+ pc->b_count = 0;
+ }
+ }
+
+ if (bcount) {
+ printk(KERN_ERR "%s: %d leftover bytes, %s\n", drive->name,
+ bcount, write ? "padding with zeros"
+ : "discarding data");
+ ide_pad_transfer(drive, write, bcount);
+ }
+
+ return done;
+}
+EXPORT_SYMBOL_GPL(ide_io_buffers);
+
+void ide_init_pc(struct ide_atapi_pc *pc)
+{
+ memset(pc, 0, sizeof(*pc));
+ pc->buf = pc->pc_buf;
+ pc->buf_size = IDE_PC_BUFFER_SIZE;
+}
+EXPORT_SYMBOL_GPL(ide_init_pc);
+
+/*
+ * Generate a new packet command request in front of the request queue, before
+ * the current request, so that it will be processed immediately, on the next
+ * pass through the driver.
+ */
+static void ide_queue_pc_head(ide_drive_t *drive, struct gendisk *disk,
+ struct ide_atapi_pc *pc, struct request *rq)
+{
+ blk_rq_init(NULL, rq);
+ rq->cmd_type = REQ_TYPE_SPECIAL;
+ rq->cmd_flags |= REQ_PREEMPT;
+ rq->buffer = (char *)pc;
+ rq->rq_disk = disk;
+ memcpy(rq->cmd, pc->c, 12);
+ if (drive->media == ide_tape)
+ rq->cmd[13] = REQ_IDETAPE_PC1;
+ ide_do_drive_cmd(drive, rq);
+}
+
+/*
+ * Add a special packet command request to the tail of the request queue,
+ * and wait for it to be serviced.
+ */
+int ide_queue_pc_tail(ide_drive_t *drive, struct gendisk *disk,
+ struct ide_atapi_pc *pc)
+{
+ struct request *rq;
+ int error;
+
+ rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
+ rq->cmd_type = REQ_TYPE_SPECIAL;
+ rq->buffer = (char *)pc;
+ memcpy(rq->cmd, pc->c, 12);
+ if (drive->media == ide_tape)
+ rq->cmd[13] = REQ_IDETAPE_PC1;
+ error = blk_execute_rq(drive->queue, disk, rq, 0);
+ blk_put_request(rq);
+
+ return error;
+}
+EXPORT_SYMBOL_GPL(ide_queue_pc_tail);
+
+int ide_do_test_unit_ready(ide_drive_t *drive, struct gendisk *disk)
+{
+ struct ide_atapi_pc pc;
+
+ ide_init_pc(&pc);
+ pc.c[0] = TEST_UNIT_READY;
+
+ return ide_queue_pc_tail(drive, disk, &pc);
+}
+EXPORT_SYMBOL_GPL(ide_do_test_unit_ready);
+
+int ide_do_start_stop(ide_drive_t *drive, struct gendisk *disk, int start)
+{
+ struct ide_atapi_pc pc;
+
+ ide_init_pc(&pc);
+ pc.c[0] = START_STOP;
+ pc.c[4] = start;
+
+ if (drive->media == ide_tape)
+ pc.flags |= PC_FLAG_WAIT_FOR_DSC;
+
+ return ide_queue_pc_tail(drive, disk, &pc);
+}
+EXPORT_SYMBOL_GPL(ide_do_start_stop);
+
+int ide_set_media_lock(ide_drive_t *drive, struct gendisk *disk, int on)
+{
+ struct ide_atapi_pc pc;
+
+ if (drive->atapi_flags & IDE_AFLAG_NO_DOORLOCK)
+ return 0;
+
+ ide_init_pc(&pc);
+ pc.c[0] = ALLOW_MEDIUM_REMOVAL;
+ pc.c[4] = on;
+
+ return ide_queue_pc_tail(drive, disk, &pc);
+}
+EXPORT_SYMBOL_GPL(ide_set_media_lock);
+
+void ide_create_request_sense_cmd(ide_drive_t *drive, struct ide_atapi_pc *pc)
+{
+ ide_init_pc(pc);
+ pc->c[0] = REQUEST_SENSE;
+ if (drive->media == ide_floppy) {
+ pc->c[4] = 255;
+ pc->req_xfer = 18;
+ } else {
+ pc->c[4] = 20;
+ pc->req_xfer = 20;
+ }
+}
+EXPORT_SYMBOL_GPL(ide_create_request_sense_cmd);
+
+/*
+ * Called when an error was detected during the last packet command.
+ * We queue a request sense packet command in the head of the request list.
+ */
+void ide_retry_pc(ide_drive_t *drive, struct gendisk *disk)
+{
+ struct request *rq = &drive->request_sense_rq;
+ struct ide_atapi_pc *pc = &drive->request_sense_pc;
+
+ (void)ide_read_error(drive);
+ ide_create_request_sense_cmd(drive, pc);
+ if (drive->media == ide_tape)
+ set_bit(IDE_AFLAG_IGNORE_DSC, &drive->atapi_flags);
+ ide_queue_pc_head(drive, disk, pc, rq);
+}
+EXPORT_SYMBOL_GPL(ide_retry_pc);
+
+int ide_scsi_expiry(ide_drive_t *drive)
+{
+ struct ide_atapi_pc *pc = drive->pc;
+
+ debug_log("%s called for %lu at %lu\n", __func__,
+ pc->scsi_cmd->serial_number, jiffies);
+
+ pc->flags |= PC_FLAG_TIMEDOUT;
+
+ return 0; /* we do not want the IDE subsystem to retry */
+}
+EXPORT_SYMBOL_GPL(ide_scsi_expiry);
+
+/*
+ * This is the usual interrupt handler which will be called during a packet
+ * command. We will transfer some of the data (as requested by the drive)
+ * and will re-point interrupt handler to us.
+ */
+static ide_startstop_t ide_pc_intr(ide_drive_t *drive)
+{
+ struct ide_atapi_pc *pc = drive->pc;
ide_hwif_t *hwif = drive->hwif;
struct request *rq = hwif->hwgroup->rq;
const struct ide_tp_ops *tp_ops = hwif->tp_ops;
xfer_func_t *xferfunc;
- unsigned int temp;
+ ide_expiry_t *expiry;
+ unsigned int timeout, temp;
u16 bcount;
- u8 stat, ireason, scsi = drive->scsi;
+ u8 stat, ireason, scsi = !!(drive->dev_flags & IDE_DFLAG_SCSI), dsc = 0;
debug_log("Enter %s - interrupt handler\n", __func__);
+ if (scsi) {
+ timeout = ide_scsi_get_timeout(pc);
+ expiry = ide_scsi_expiry;
+ } else {
+ timeout = (drive->media == ide_floppy) ? WAIT_FLOPPY_CMD
+ : WAIT_TAPE_CMD;
+ expiry = NULL;
+ }
+
if (pc->flags & PC_FLAG_TIMEDOUT) {
- drive->pc_callback(drive);
+ drive->pc_callback(drive, 0);
return ide_stopped;
}
@@ -41,7 +284,7 @@
if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) {
if (hwif->dma_ops->dma_end(drive) ||
- (drive->media == ide_tape && !scsi && (stat & ERR_STAT))) {
+ (drive->media == ide_tape && !scsi && (stat & ATA_ERR))) {
if (drive->media == ide_floppy && !scsi)
printk(KERN_ERR "%s: DMA %s error\n",
drive->name, rq_data_dir(pc->rq)
@@ -49,14 +292,14 @@
pc->flags |= PC_FLAG_DMA_ERROR;
} else {
pc->xferred = pc->req_xfer;
- if (update_buffers)
- update_buffers(drive, pc);
+ if (drive->pc_update_buffers)
+ drive->pc_update_buffers(drive, pc);
}
debug_log("%s: DMA finished\n", drive->name);
}
/* No more interrupts */
- if ((stat & DRQ_STAT) == 0) {
+ if ((stat & ATA_DRQ) == 0) {
debug_log("Packet command completed, %d bytes transferred\n",
pc->xferred);
@@ -65,10 +308,10 @@
local_irq_enable_in_hardirq();
if (drive->media == ide_tape && !scsi &&
- (stat & ERR_STAT) && rq->cmd[0] == REQUEST_SENSE)
- stat &= ~ERR_STAT;
+ (stat & ATA_ERR) && rq->cmd[0] == REQUEST_SENSE)
+ stat &= ~ATA_ERR;
- if ((stat & ERR_STAT) || (pc->flags & PC_FLAG_DMA_ERROR)) {
+ if ((stat & ATA_ERR) || (pc->flags & PC_FLAG_DMA_ERROR)) {
/* Error detected */
debug_log("%s: I/O error\n", drive->name);
@@ -87,21 +330,19 @@
debug_log("[cmd %x]: check condition\n", rq->cmd[0]);
/* Retry operation */
- retry_pc(drive);
+ ide_retry_pc(drive, rq->rq_disk);
/* queued, but not started */
return ide_stopped;
}
cmd_finished:
pc->error = 0;
- if ((pc->flags & PC_FLAG_WAIT_FOR_DSC) &&
- (stat & SEEK_STAT) == 0) {
- dsc_handle(drive);
- return ide_stopped;
- }
+
+ if ((pc->flags & PC_FLAG_WAIT_FOR_DSC) && (stat & ATA_DSC) == 0)
+ dsc = 1;
/* Command finished - Call the callback function */
- drive->pc_callback(drive);
+ drive->pc_callback(drive, dsc);
return ide_stopped;
}
@@ -117,17 +358,18 @@
/* Get the number of bytes to transfer on this interrupt. */
ide_read_bcount_and_ireason(drive, &bcount, &ireason);
- if (ireason & CD) {
+ if (ireason & ATAPI_COD) {
printk(KERN_ERR "%s: CoD != 0 in %s\n", drive->name, __func__);
return ide_do_reset(drive);
}
- if (((ireason & IO) == IO) == !!(pc->flags & PC_FLAG_WRITING)) {
+ if (((ireason & ATAPI_IO) == ATAPI_IO) ==
+ !!(pc->flags & PC_FLAG_WRITING)) {
/* Hopefully, we will never get here */
printk(KERN_ERR "%s: We wanted to %s, but the device wants us "
"to %s!\n", drive->name,
- (ireason & IO) ? "Write" : "Read",
- (ireason & IO) ? "Read" : "Write");
+ (ireason & ATAPI_IO) ? "Write" : "Read",
+ (ireason & ATAPI_IO) ? "Read" : "Write");
return ide_do_reset(drive);
}
@@ -146,7 +388,8 @@
temp = 0;
if (temp) {
if (pc->sg)
- io_buffers(drive, pc, temp, 0);
+ drive->pc_io_buffers(drive, pc,
+ temp, 0);
else
tp_ops->input_data(drive, NULL,
pc->cur_pos, temp);
@@ -158,9 +401,7 @@
pc->xferred += temp;
pc->cur_pos += temp;
ide_pad_transfer(drive, 0, bcount - temp);
- ide_set_handler(drive, handler, timeout,
- expiry);
- return ide_started;
+ goto next_irq;
}
debug_log("The device wants to send us more data than "
"expected - allowing transfer\n");
@@ -171,9 +412,14 @@
if ((drive->media == ide_floppy && !scsi && !pc->buf) ||
(drive->media == ide_tape && !scsi && pc->bh) ||
- (scsi && pc->sg))
- io_buffers(drive, pc, bcount, !!(pc->flags & PC_FLAG_WRITING));
- else
+ (scsi && pc->sg)) {
+ int done = drive->pc_io_buffers(drive, pc, bcount,
+ !!(pc->flags & PC_FLAG_WRITING));
+
+ /* FIXME: don't do partial completions */
+ if (drive->media == ide_floppy && !scsi)
+ ide_end_request(drive, 1, done >> 9);
+ } else
xferfunc(drive, NULL, pc->cur_pos, bcount);
/* Update the current position */
@@ -182,12 +428,11 @@
debug_log("[cmd %x] transferred %d bytes on that intr.\n",
rq->cmd[0], bcount);
-
+next_irq:
/* And set the interrupt handler again */
- ide_set_handler(drive, handler, timeout, expiry);
+ ide_set_handler(drive, ide_pc_intr, timeout, expiry);
return ide_started;
}
-EXPORT_SYMBOL_GPL(ide_pc_intr);
static u8 ide_read_ireason(ide_drive_t *drive)
{
@@ -205,7 +450,8 @@
{
int retries = 100;
- while (retries-- && ((ireason & CD) == 0 || (ireason & IO))) {
+ while (retries-- && ((ireason & ATAPI_COD) == 0 ||
+ (ireason & ATAPI_IO))) {
printk(KERN_ERR "%s: (IO,CoD != (0,1) while issuing "
"a packet command, retrying\n", drive->name);
udelay(100);
@@ -214,41 +460,71 @@
printk(KERN_ERR "%s: (IO,CoD != (0,1) while issuing "
"a packet command, ignoring\n",
drive->name);
- ireason |= CD;
- ireason &= ~IO;
+ ireason |= ATAPI_COD;
+ ireason &= ~ATAPI_IO;
}
}
return ireason;
}
-ide_startstop_t ide_transfer_pc(ide_drive_t *drive, struct ide_atapi_pc *pc,
- ide_handler_t *handler, unsigned int timeout,
- ide_expiry_t *expiry)
+static int ide_delayed_transfer_pc(ide_drive_t *drive)
{
+ /* Send the actual packet */
+ drive->hwif->tp_ops->output_data(drive, NULL, drive->pc->c, 12);
+
+ /* Timeout for the packet command */
+ return WAIT_FLOPPY_CMD;
+}
+
+static ide_startstop_t ide_transfer_pc(ide_drive_t *drive)
+{
+ struct ide_atapi_pc *pc = drive->pc;
ide_hwif_t *hwif = drive->hwif;
struct request *rq = hwif->hwgroup->rq;
+ ide_expiry_t *expiry;
+ unsigned int timeout;
ide_startstop_t startstop;
u8 ireason;
- if (ide_wait_stat(&startstop, drive, DRQ_STAT, BUSY_STAT, WAIT_READY)) {
+ if (ide_wait_stat(&startstop, drive, ATA_DRQ, ATA_BUSY, WAIT_READY)) {
printk(KERN_ERR "%s: Strange, packet command initiated yet "
"DRQ isn't asserted\n", drive->name);
return startstop;
}
ireason = ide_read_ireason(drive);
- if (drive->media == ide_tape && !drive->scsi)
+ if (drive->media == ide_tape &&
+ (drive->dev_flags & IDE_DFLAG_SCSI) == 0)
ireason = ide_wait_ireason(drive, ireason);
- if ((ireason & CD) == 0 || (ireason & IO)) {
+ if ((ireason & ATAPI_COD) == 0 || (ireason & ATAPI_IO)) {
printk(KERN_ERR "%s: (IO,CoD) != (0,1) while issuing "
"a packet command\n", drive->name);
return ide_do_reset(drive);
}
+ /*
+ * If necessary schedule the packet transfer to occur 'timeout'
+ * miliseconds later in ide_delayed_transfer_pc() after the device
+ * says it's ready for a packet.
+ */
+ if (drive->atapi_flags & IDE_AFLAG_ZIP_DRIVE) {
+ timeout = drive->pc_delay;
+ expiry = &ide_delayed_transfer_pc;
+ } else {
+ if (drive->dev_flags & IDE_DFLAG_SCSI) {
+ timeout = ide_scsi_get_timeout(pc);
+ expiry = ide_scsi_expiry;
+ } else {
+ timeout = (drive->media == ide_floppy) ? WAIT_FLOPPY_CMD
+ : WAIT_TAPE_CMD;
+ expiry = NULL;
+ }
+ }
+
/* Set the interrupt routine */
- ide_set_handler(drive, handler, timeout, expiry);
+ ide_set_handler(drive, ide_pc_intr, timeout, expiry);
/* Begin DMA, if necessary */
if (pc->flags & PC_FLAG_DMA_OK) {
@@ -262,22 +538,22 @@
return ide_started;
}
-EXPORT_SYMBOL_GPL(ide_transfer_pc);
-ide_startstop_t ide_issue_pc(ide_drive_t *drive, struct ide_atapi_pc *pc,
- ide_handler_t *handler, unsigned int timeout,
+ide_startstop_t ide_issue_pc(ide_drive_t *drive, unsigned int timeout,
ide_expiry_t *expiry)
{
+ struct ide_atapi_pc *pc = drive->pc;
ide_hwif_t *hwif = drive->hwif;
+ u32 tf_flags;
u16 bcount;
- u8 dma = 0;
+ u8 scsi = !!(drive->dev_flags & IDE_DFLAG_SCSI);
/* We haven't transferred any data yet */
pc->xferred = 0;
pc->cur_pos = pc->buf;
/* Request to transfer the entire buffer at once */
- if (drive->media == ide_tape && !drive->scsi)
+ if (drive->media == ide_tape && scsi == 0)
bcount = pc->req_xfer;
else
bcount = min(pc->req_xfer, 63 * 1024);
@@ -287,28 +563,35 @@
ide_dma_off(drive);
}
- if ((pc->flags & PC_FLAG_DMA_OK) && drive->using_dma) {
- if (drive->scsi)
+ if ((pc->flags & PC_FLAG_DMA_OK) &&
+ (drive->dev_flags & IDE_DFLAG_USING_DMA)) {
+ if (scsi)
hwif->sg_mapped = 1;
- dma = !hwif->dma_ops->dma_setup(drive);
- if (drive->scsi)
+ drive->dma = !hwif->dma_ops->dma_setup(drive);
+ if (scsi)
hwif->sg_mapped = 0;
}
- if (!dma)
+ if (!drive->dma)
pc->flags &= ~PC_FLAG_DMA_OK;
- ide_pktcmd_tf_load(drive, drive->scsi ? 0 : IDE_TFLAG_OUT_DEVICE,
- bcount, dma);
+ if (scsi)
+ tf_flags = 0;
+ else if (drive->media == ide_cdrom || drive->media == ide_optical)
+ tf_flags = IDE_TFLAG_OUT_NSECT | IDE_TFLAG_OUT_LBAL;
+ else
+ tf_flags = IDE_TFLAG_OUT_DEVICE;
+
+ ide_pktcmd_tf_load(drive, tf_flags, bcount, drive->dma);
/* Issue the packet command */
if (drive->atapi_flags & IDE_AFLAG_DRQ_INTERRUPT) {
- ide_execute_command(drive, WIN_PACKETCMD, handler,
+ ide_execute_command(drive, ATA_CMD_PACKET, ide_transfer_pc,
timeout, NULL);
return ide_started;
} else {
ide_execute_pkt_cmd(drive);
- return (*handler)(drive);
+ return ide_transfer_pc(drive);
}
}
EXPORT_SYMBOL_GPL(ide_issue_pc);
diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c
index 03c2cb6..3308b1c 100644
--- a/drivers/ide/ide-cd.c
+++ b/drivers/ide/ide-cd.c
@@ -23,6 +23,9 @@
* Documentation/ide/ChangeLog.ide-cd.1994-2004
*/
+#define DRV_NAME "ide-cd"
+#define PFX DRV_NAME ": "
+
#define IDECD_VERSION "5.00"
#include <linux/module.h>
@@ -50,13 +53,16 @@
#include "ide-cd.h"
+#define IDECD_DEBUG_LOG 1
+
+#if IDECD_DEBUG_LOG
+#define ide_debug_log(lvl, fmt, args...) __ide_debug_log(lvl, fmt, args)
+#else
+#define ide_debug_log(lvl, fmt, args...) do {} while (0)
+#endif
+
static DEFINE_MUTEX(idecd_ref_mutex);
-#define to_ide_cd(obj) container_of(obj, struct cdrom_info, kref)
-
-#define ide_cd_g(disk) \
- container_of((disk)->private_data, struct cdrom_info, driver)
-
static void ide_cd_release(struct kref *);
static struct cdrom_info *ide_cd_get(struct gendisk *disk)
@@ -64,7 +70,7 @@
struct cdrom_info *cd = NULL;
mutex_lock(&idecd_ref_mutex);
- cd = ide_cd_g(disk);
+ cd = ide_drv_g(disk, cdrom_info);
if (cd) {
if (ide_device_get(cd->drive))
cd = NULL;
@@ -102,6 +108,9 @@
{
int log = 0;
+ ide_debug_log(IDE_DBG_SENSE, "Call %s, sense_key: 0x%x\n", __func__,
+ sense->sense_key);
+
if (!sense || !rq || (rq->cmd_flags & REQ_QUIET))
return 0;
@@ -150,6 +159,14 @@
unsigned long bio_sectors;
struct cdrom_info *info = drive->driver_data;
+ ide_debug_log(IDE_DBG_SENSE, "Call %s, error_code: 0x%x, "
+ "sense_key: 0x%x\n", __func__, sense->error_code,
+ sense->sense_key);
+
+ if (failed_command)
+ ide_debug_log(IDE_DBG_SENSE, "%s: failed cmd: 0x%x\n",
+ __func__, failed_command->cmd[0]);
+
if (!cdrom_log_sense(drive, failed_command, sense))
return;
@@ -200,6 +217,8 @@
struct cdrom_info *info = drive->driver_data;
struct request *rq = &info->request_sense_request;
+ ide_debug_log(IDE_DBG_SENSE, "Call %s\n", __func__);
+
if (sense == NULL)
sense = &info->sense_data;
@@ -219,6 +238,10 @@
/* NOTE! Save the failed command in "rq->buffer" */
rq->buffer = (void *) failed_command;
+ if (failed_command)
+ ide_debug_log(IDE_DBG_SENSE, "failed_cmd: 0x%x\n",
+ failed_command->cmd[0]);
+
ide_do_drive_cmd(drive, rq);
}
@@ -227,6 +250,10 @@
struct request *rq = HWGROUP(drive)->rq;
int nsectors = rq->hard_cur_sectors;
+ ide_debug_log(IDE_DBG_FUNC, "Call %s, cmd: 0x%x, uptodate: 0x%x, "
+ "nsectors: %d\n", __func__, rq->cmd[0], uptodate,
+ nsectors);
+
if (blk_sense_request(rq) && uptodate) {
/*
* For REQ_TYPE_SENSE, "rq->buffer" points to the original
@@ -269,6 +296,9 @@
if (!nsectors)
nsectors = 1;
+ ide_debug_log(IDE_DBG_FUNC, "Exit %s, uptodate: 0x%x, nsectors: %d\n",
+ __func__, uptodate, nsectors);
+
ide_end_request(drive, uptodate, nsectors);
}
@@ -304,11 +334,15 @@
sense_key = err >> 4;
if (rq == NULL) {
- printk(KERN_ERR "%s: missing rq in %s\n",
+ printk(KERN_ERR PFX "%s: missing rq in %s\n",
drive->name, __func__);
return 1;
}
+ ide_debug_log(IDE_DBG_RQ, "%s: stat: 0x%x, good_stat: 0x%x, "
+ "rq->cmd_type: 0x%x, err: 0x%x\n", __func__, stat,
+ good_stat, rq->cmd_type, err);
+
if (blk_sense_request(rq)) {
/*
* We got an error trying to get sense info from the drive
@@ -374,7 +408,8 @@
cdrom_saw_media_change(drive);
/* fail the request */
- printk(KERN_ERR "%s: tray open\n", drive->name);
+ printk(KERN_ERR PFX "%s: tray open\n",
+ drive->name);
do_end_request = 1;
} else {
struct cdrom_info *info = drive->driver_data;
@@ -436,7 +471,7 @@
ide_dump_status_no_sense(drive, "media error (blank)",
stat);
do_end_request = 1;
- } else if ((err & ~ABRT_ERR) != 0) {
+ } else if ((err & ~ATA_ABORTED) != 0) {
/* go to the default handler for other errors */
ide_error(drive, "cdrom_decode_status", stat);
return 1;
@@ -457,10 +492,10 @@
* If we got a CHECK_CONDITION status, queue
* a request sense command.
*/
- if (stat & ERR_STAT)
+ if (stat & ATA_ERR)
cdrom_queue_request_sense(drive, NULL, NULL);
} else {
- blk_dump_rq_flags(rq, "ide-cd: bad rq");
+ blk_dump_rq_flags(rq, PFX "bad rq");
cdrom_end_request(drive, 0);
}
@@ -468,7 +503,7 @@
return 1;
end_request:
- if (stat & ERR_STAT) {
+ if (stat & ATA_ERR) {
unsigned long flags;
spin_lock_irqsave(&ide_lock, flags);
@@ -488,6 +523,9 @@
struct request *rq = HWGROUP(drive)->rq;
unsigned long wait = 0;
+ ide_debug_log(IDE_DBG_RQ, "Call %s: rq->cmd[0]: 0x%x\n", __func__,
+ rq->cmd[0]);
+
/*
* Some commands are *slow* and normally take a long time to complete.
* Usually we can use the ATAPI "disconnect" to bypass this, but not all
@@ -504,7 +542,7 @@
break;
default:
if (!(rq->cmd_flags & REQ_QUIET))
- printk(KERN_INFO "ide-cd: cmd 0x%x timed out\n",
+ printk(KERN_INFO PFX "cmd 0x%x timed out\n",
rq->cmd[0]);
wait = 0;
break;
@@ -524,24 +562,25 @@
int xferlen,
ide_handler_t *handler)
{
- struct cdrom_info *info = drive->driver_data;
ide_hwif_t *hwif = drive->hwif;
+ ide_debug_log(IDE_DBG_PC, "Call %s, xferlen: %d\n", __func__, xferlen);
+
/* FIXME: for Virtual DMA we must check harder */
- if (info->dma)
- info->dma = !hwif->dma_ops->dma_setup(drive);
+ if (drive->dma)
+ drive->dma = !hwif->dma_ops->dma_setup(drive);
/* set up the controller registers */
ide_pktcmd_tf_load(drive, IDE_TFLAG_OUT_NSECT | IDE_TFLAG_OUT_LBAL,
- xferlen, info->dma);
+ xferlen, drive->dma);
if (drive->atapi_flags & IDE_AFLAG_DRQ_INTERRUPT) {
/* waiting for CDB interrupt, not DMA yet. */
- if (info->dma)
+ if (drive->dma)
drive->waiting_for_dma = 0;
/* packet command */
- ide_execute_command(drive, WIN_PACKETCMD, handler,
+ ide_execute_command(drive, ATA_CMD_PACKET, handler,
ATAPI_WAIT_PC, cdrom_timer_expiry);
return ide_started;
} else {
@@ -564,9 +603,10 @@
{
ide_hwif_t *hwif = drive->hwif;
int cmd_len;
- struct cdrom_info *info = drive->driver_data;
ide_startstop_t startstop;
+ ide_debug_log(IDE_DBG_PC, "Call %s\n", __func__);
+
if (drive->atapi_flags & IDE_AFLAG_DRQ_INTERRUPT) {
/*
* Here we should have been called after receiving an interrupt
@@ -574,16 +614,16 @@
*/
/* check for errors */
- if (cdrom_decode_status(drive, DRQ_STAT, NULL))
+ if (cdrom_decode_status(drive, ATA_DRQ, NULL))
return ide_stopped;
/* ok, next interrupt will be DMA interrupt */
- if (info->dma)
+ if (drive->dma)
drive->waiting_for_dma = 1;
} else {
/* otherwise, we must wait for DRQ to get set */
- if (ide_wait_stat(&startstop, drive, DRQ_STAT,
- BUSY_STAT, WAIT_READY))
+ if (ide_wait_stat(&startstop, drive, ATA_DRQ,
+ ATA_BUSY, WAIT_READY))
return startstop;
}
@@ -599,7 +639,7 @@
hwif->tp_ops->output_data(drive, NULL, rq->cmd, cmd_len);
/* start the DMA if need be */
- if (info->dma)
+ if (drive->dma)
hwif->dma_ops->dma_start(drive);
return ide_started;
@@ -615,6 +655,9 @@
{
ide_hwif_t *hwif = drive->hwif;
+ ide_debug_log(IDE_DBG_FUNC, "Call %s, ireason: 0x%x, rw: 0x%x\n",
+ __func__, ireason, rw);
+
/*
* ireason == 0: the drive wants to receive data from us
* ireason == 2: the drive is expecting to transfer data to us
@@ -624,7 +667,7 @@
else if (ireason == (rw << 1)) {
/* whoops... */
- printk(KERN_ERR "%s: %s: wrong transfer direction!\n",
+ printk(KERN_ERR PFX "%s: %s: wrong transfer direction!\n",
drive->name, __func__);
ide_pad_transfer(drive, rw, len);
@@ -637,7 +680,7 @@
return 0;
} else {
/* drive wants a command packet, or invalid ireason... */
- printk(KERN_ERR "%s: %s: bad interrupt reason 0x%02x\n",
+ printk(KERN_ERR PFX "%s: %s: bad interrupt reason 0x%02x\n",
drive->name, __func__, ireason);
}
@@ -654,17 +697,19 @@
*/
static int ide_cd_check_transfer_size(ide_drive_t *drive, int len)
{
+ ide_debug_log(IDE_DBG_FUNC, "Call %s, len: %d\n", __func__, len);
+
if ((len % SECTOR_SIZE) == 0)
return 0;
- printk(KERN_ERR "%s: %s: Bad transfer size %d\n",
- drive->name, __func__, len);
+ printk(KERN_ERR PFX "%s: %s: Bad transfer size %d\n", drive->name,
+ __func__, len);
if (drive->atapi_flags & IDE_AFLAG_LIMIT_NFRAMES)
- printk(KERN_ERR " This drive is not supported by "
- "this version of the driver\n");
+ printk(KERN_ERR PFX "This drive is not supported by this "
+ "version of the driver\n");
else {
- printk(KERN_ERR " Trying to limit transfer sizes\n");
+ printk(KERN_ERR PFX "Trying to limit transfer sizes\n");
drive->atapi_flags |= IDE_AFLAG_LIMIT_NFRAMES;
}
@@ -676,6 +721,9 @@
static ide_startstop_t ide_cd_prepare_rw_request(ide_drive_t *drive,
struct request *rq)
{
+ ide_debug_log(IDE_DBG_RQ, "Call %s: rq->cmd_flags: 0x%x\n", __func__,
+ rq->cmd_flags);
+
if (rq_data_dir(rq) == READ) {
unsigned short sectors_per_frame =
queue_hardsect_size(drive->queue) >> SECTOR_BITS;
@@ -695,7 +743,7 @@
/* sanity check... */
if (rq->current_nr_sectors !=
bio_cur_sectors(rq->bio)) {
- printk(KERN_ERR "%s: %s: buffer botch (%u)\n",
+ printk(KERN_ERR PFX "%s: %s: buffer botch (%u)\n",
drive->name, __func__,
rq->current_nr_sectors);
cdrom_end_request(drive, 0);
@@ -704,11 +752,7 @@
rq->current_nr_sectors += nskip;
}
}
-#if 0
- else
- /* the immediate bit */
- rq->cmd[1] = 1 << 3;
-#endif
+
/* set up the command */
rq->timeout = ATAPI_WAIT_PC;
@@ -739,6 +783,8 @@
int stat;
static int retry = 10;
+ ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__);
+
if (cdrom_decode_status(drive, 0, &stat))
return ide_stopped;
@@ -746,7 +792,7 @@
if (retry && time_after(jiffies, info->start_seek + IDECD_SEEK_TIMER)) {
if (--retry == 0)
- drive->dsc_overlap = 0;
+ drive->dev_flags &= ~IDE_DFLAG_DSC_OVERLAP;
}
return ide_stopped;
}
@@ -755,6 +801,8 @@
{
sector_t frame = rq->sector;
+ ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__);
+
sector_div(frame, queue_hardsect_size(drive->queue) >> SECTOR_BITS);
memset(rq->cmd, 0, BLK_MAX_CDB);
@@ -775,8 +823,11 @@
* Fix up a possibly partially-processed request so that we can start it over
* entirely, or even put it back on the request queue.
*/
-static void restore_request(struct request *rq)
+static void ide_cd_restore_request(ide_drive_t *drive, struct request *rq)
{
+
+ ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__);
+
if (rq->buffer != bio_data(rq->bio)) {
sector_t n =
(rq->buffer - (char *)bio_data(rq->bio)) / SECTOR_SIZE;
@@ -795,8 +846,11 @@
/*
* All other packet commands.
*/
-static void ide_cd_request_sense_fixup(struct request *rq)
+static void ide_cd_request_sense_fixup(ide_drive_t *drive, struct request *rq)
{
+
+ ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__);
+
/*
* Some of the trailing request sense fields are optional,
* and some drives don't send them. Sigh.
@@ -822,6 +876,10 @@
if (!sense)
sense = &local_sense;
+ ide_debug_log(IDE_DBG_PC, "Call %s, rq->cmd[0]: 0x%x, write: 0x%x, "
+ "timeout: %d, cmd_flags: 0x%x\n", __func__, cmd[0], write,
+ timeout, cmd_flags);
+
/* start of retry loop */
do {
struct request *rq;
@@ -895,7 +953,6 @@
static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
- struct cdrom_info *info = drive->driver_data;
struct request *rq = HWGROUP(drive)->rq;
xfer_func_t *xferfunc;
ide_expiry_t *expiry = NULL;
@@ -905,13 +962,16 @@
u16 len;
u8 ireason;
+ ide_debug_log(IDE_DBG_PC, "Call %s, rq->cmd[0]: 0x%x, write: 0x%x\n",
+ __func__, rq->cmd[0], write);
+
/* check for errors */
- dma = info->dma;
+ dma = drive->dma;
if (dma) {
- info->dma = 0;
+ drive->dma = 0;
dma_error = hwif->dma_ops->dma_end(drive);
if (dma_error) {
- printk(KERN_ERR "%s: DMA %s error\n", drive->name,
+ printk(KERN_ERR PFX "%s: DMA %s error\n", drive->name,
write ? "write" : "read");
ide_dma_off(drive);
}
@@ -937,8 +997,11 @@
if (thislen > len)
thislen = len;
+ ide_debug_log(IDE_DBG_PC, "%s: DRQ: stat: 0x%x, thislen: %d\n",
+ __func__, stat, thislen);
+
/* If DRQ is clear, the command has completed. */
- if ((stat & DRQ_STAT) == 0) {
+ if ((stat & ATA_DRQ) == 0) {
if (blk_fs_request(rq)) {
/*
* If we're not done reading/writing, complain.
@@ -946,7 +1009,7 @@
*/
uptodate = 1;
if (rq->current_nr_sectors > 0) {
- printk(KERN_ERR "%s: %s: data underrun "
+ printk(KERN_ERR PFX "%s: %s: data underrun "
"(%d blocks)\n",
drive->name, __func__,
rq->current_nr_sectors);
@@ -957,7 +1020,7 @@
cdrom_end_request(drive, uptodate);
return ide_stopped;
} else if (!blk_pc_request(rq)) {
- ide_cd_request_sense_fixup(rq);
+ ide_cd_request_sense_fixup(drive, rq);
/* complain if we still have data left to transfer */
uptodate = rq->data_len ? 0 : 1;
}
@@ -1000,6 +1063,9 @@
xferfunc = hwif->tp_ops->input_data;
}
+ ide_debug_log(IDE_DBG_PC, "%s: data transfer, rq->cmd_type: 0x%x, "
+ "ireason: 0x%x\n", __func__, rq->cmd_type, ireason);
+
/* transfer data */
while (thislen > 0) {
u8 *ptr = blk_fs_request(rq) ? NULL : rq->data;
@@ -1024,7 +1090,7 @@
*/
ide_pad_transfer(drive, 0, thislen);
else {
- printk(KERN_ERR "%s: confused, missing data\n",
+ printk(KERN_ERR PFX "%s: confused, missing data\n",
drive->name);
blk_dump_rq_flags(rq, rq_data_dir(rq)
? "cdrom_newpc_intr, write"
@@ -1111,6 +1177,9 @@
unsigned short sectors_per_frame =
queue_hardsect_size(drive->queue) >> SECTOR_BITS;
+ ide_debug_log(IDE_DBG_RQ, "Call %s, write: 0x%x, secs_per_frame: %u\n",
+ __func__, write, sectors_per_frame);
+
if (write) {
/* disk has become write protected */
if (get_disk_ro(cd->disk)) {
@@ -1122,7 +1191,7 @@
* We may be retrying this request after an error. Fix up any
* weirdness which might be present in the request packet.
*/
- restore_request(rq);
+ ide_cd_restore_request(drive, rq);
}
/* use DMA, if possible / writes *must* be hardware frame aligned */
@@ -1132,9 +1201,9 @@
cdrom_end_request(drive, 0);
return ide_stopped;
}
- cd->dma = 0;
+ drive->dma = 0;
} else
- cd->dma = drive->using_dma;
+ drive->dma = !!(drive->dev_flags & IDE_DFLAG_USING_DMA);
if (write)
cd->devinfo.media_written = 1;
@@ -1151,28 +1220,29 @@
static void cdrom_do_block_pc(ide_drive_t *drive, struct request *rq)
{
- struct cdrom_info *info = drive->driver_data;
+
+ ide_debug_log(IDE_DBG_PC, "Call %s, rq->cmd_type: 0x%x\n", __func__,
+ rq->cmd_type);
if (blk_pc_request(rq))
rq->cmd_flags |= REQ_QUIET;
else
rq->cmd_flags &= ~REQ_FAILED;
- info->dma = 0;
+ drive->dma = 0;
/* sg request */
if (rq->bio || ((rq->cmd_type == REQ_TYPE_ATA_PC) && rq->data_len)) {
struct request_queue *q = drive->queue;
unsigned int alignment;
- unsigned long addr;
- unsigned long stack_mask = ~(THREAD_SIZE - 1);
+ char *buf;
if (rq->bio)
- addr = (unsigned long)bio_data(rq->bio);
+ buf = bio_data(rq->bio);
else
- addr = (unsigned long)rq->data;
+ buf = rq->data;
- info->dma = drive->using_dma;
+ drive->dma = !!(drive->dev_flags & IDE_DFLAG_USING_DMA);
/*
* check if dma is safe
@@ -1181,12 +1251,9 @@
* separate masks.
*/
alignment = queue_dma_alignment(q) | q->dma_pad_mask;
- if (addr & alignment || rq->data_len & alignment)
- info->dma = 0;
-
- if (!((addr & stack_mask) ^
- ((unsigned long)current->stack & stack_mask)))
- info->dma = 0;
+ if ((unsigned long)buf & alignment || rq->data_len & alignment
+ || object_is_on_stack(buf))
+ drive->dma = 0;
}
}
@@ -1200,19 +1267,22 @@
ide_handler_t *fn;
int xferlen;
+ ide_debug_log(IDE_DBG_RQ, "Call %s, rq->cmd_type: 0x%x, block: %llu\n",
+ __func__, rq->cmd_type, (unsigned long long)block);
+
if (blk_fs_request(rq)) {
if (drive->atapi_flags & IDE_AFLAG_SEEKING) {
ide_hwif_t *hwif = drive->hwif;
unsigned long elapsed = jiffies - info->start_seek;
int stat = hwif->tp_ops->read_status(hwif);
- if ((stat & SEEK_STAT) != SEEK_STAT) {
+ if ((stat & ATA_DSC) != ATA_DSC) {
if (elapsed < IDECD_SEEK_TIMEOUT) {
ide_stall_queue(drive,
IDECD_SEEK_TIMER);
return ide_stopped;
}
- printk(KERN_ERR "%s: DSC timeout\n",
+ printk(KERN_ERR PFX "%s: DSC timeout\n",
drive->name);
}
drive->atapi_flags &= ~IDE_AFLAG_SEEKING;
@@ -1220,11 +1290,11 @@
if (rq_data_dir(rq) == READ &&
IDE_LARGE_SEEK(info->last_block, block,
IDECD_SEEK_THRESHOLD) &&
- drive->dsc_overlap) {
+ (drive->dev_flags & IDE_DFLAG_DSC_OVERLAP)) {
xferlen = 0;
fn = cdrom_start_seek_continuation;
- info->dma = 0;
+ drive->dma = 0;
info->start_seek = jiffies;
ide_cd_prepare_seek_request(drive, rq);
@@ -1253,7 +1323,7 @@
cdrom_end_request(drive, 1);
return ide_stopped;
} else {
- blk_dump_rq_flags(rq, "ide-cd bad flags");
+ blk_dump_rq_flags(rq, DRV_NAME " bad flags");
cdrom_end_request(drive, 0);
return ide_stopped;
}
@@ -1283,6 +1353,8 @@
struct cdrom_device_info *cdi = &info->devinfo;
unsigned char cmd[BLK_MAX_CDB];
+ ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__);
+
memset(cmd, 0, BLK_MAX_CDB);
cmd[0] = GPCMD_TEST_UNIT_READY;
@@ -1309,6 +1381,8 @@
unsigned len = sizeof(capbuf);
u32 blocklen;
+ ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__);
+
memset(cmd, 0, BLK_MAX_CDB);
cmd[0] = GPCMD_READ_CDVD_CAPACITY;
@@ -1328,10 +1402,10 @@
case 4096:
break;
default:
- printk(KERN_ERR "%s: weird block size %u\n",
- drive->name, blocklen);
- printk(KERN_ERR "%s: default to 2kb block size\n",
- drive->name);
+ printk(KERN_ERR PFX "%s: weird block size %u\n",
+ drive->name, blocklen);
+ printk(KERN_ERR PFX "%s: default to 2kb block size\n",
+ drive->name);
blocklen = 2048;
break;
}
@@ -1347,6 +1421,8 @@
{
unsigned char cmd[BLK_MAX_CDB];
+ ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__);
+
memset(cmd, 0, BLK_MAX_CDB);
cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
@@ -1375,11 +1451,13 @@
long last_written;
unsigned long sectors_per_frame = SECTORS_PER_FRAME;
+ ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__);
+
if (toc == NULL) {
/* try to allocate space */
toc = kmalloc(sizeof(struct atapi_toc), GFP_KERNEL);
if (toc == NULL) {
- printk(KERN_ERR "%s: No cdrom TOC buffer!\n",
+ printk(KERN_ERR PFX "%s: No cdrom TOC buffer!\n",
drive->name);
return -ENOMEM;
}
@@ -1535,6 +1613,8 @@
struct packet_command cgc;
int stat, attempts = 3, size = ATAPI_CAPABILITIES_PAGE_SIZE;
+ ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__);
+
if ((drive->atapi_flags & IDE_AFLAG_FULL_CAPS_PAGE) == 0)
size -= ATAPI_CAPABILITIES_PAGE_PAD_SIZE;
@@ -1553,6 +1633,8 @@
struct cdrom_info *cd = drive->driver_data;
u16 curspeed, maxspeed;
+ ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__);
+
if (drive->atapi_flags & IDE_AFLAG_LE_SPEED_FIELDS) {
curspeed = le16_to_cpup((__le16 *)&buf[8 + 14]);
maxspeed = le16_to_cpup((__le16 *)&buf[8 + 8]);
@@ -1593,6 +1675,8 @@
struct cdrom_info *info = drive->driver_data;
struct cdrom_device_info *devinfo = &info->devinfo;
+ ide_debug_log(IDE_DBG_PROBE, "Call %s, nslots: %d\n", __func__, nslots);
+
devinfo->ops = &ide_cdrom_dops;
devinfo->speed = info->current_speed;
devinfo->capacity = nslots;
@@ -1614,13 +1698,17 @@
mechtype_t mechtype;
int nslots = 1;
+ ide_debug_log(IDE_DBG_PROBE, "Call %s, drive->media: 0x%x, "
+ "drive->atapi_flags: 0x%lx\n", __func__, drive->media,
+ drive->atapi_flags);
+
cdi->mask = (CDC_CD_R | CDC_CD_RW | CDC_DVD | CDC_DVD_R |
CDC_DVD_RAM | CDC_SELECT_DISC | CDC_PLAY_AUDIO |
CDC_MO_DRIVE | CDC_RAM);
if (drive->media == ide_optical) {
cdi->mask &= ~(CDC_MO_DRIVE | CDC_RAM);
- printk(KERN_ERR "%s: ATAPI magneto-optical drive\n",
+ printk(KERN_ERR PFX "%s: ATAPI magneto-optical drive\n",
drive->name);
return nslots;
}
@@ -1678,7 +1766,7 @@
ide_cdrom_update_speed(drive, buf);
- printk(KERN_INFO "%s: ATAPI", drive->name);
+ printk(KERN_INFO PFX "%s: ATAPI", drive->name);
/* don't print speed if the drive reported 0 */
if (cd->max_speed)
@@ -1701,7 +1789,8 @@
else
printk(KERN_CONT " drive");
- printk(KERN_CONT ", %dkB Cache\n", be16_to_cpup((__be16 *)&buf[8 + 12]));
+ printk(KERN_CONT ", %dkB Cache\n",
+ be16_to_cpup((__be16 *)&buf[8 + 12]));
return nslots;
}
@@ -1813,13 +1902,12 @@
{ NULL, 0, NULL, NULL }
};
-static void ide_cdrom_add_settings(ide_drive_t *drive)
-{
- ide_add_setting(drive, "dsc_overlap", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1,
- &drive->dsc_overlap, NULL);
-}
-#else
-static inline void ide_cdrom_add_settings(ide_drive_t *drive) { ; }
+ide_devset_rw_flag(dsc_overlap, IDE_DFLAG_DSC_OVERLAP);
+
+static const struct ide_proc_devset idecd_settings[] = {
+ IDE_PROC_DEVSET(dsc_overlap, 0, 1),
+ { 0 },
+};
#endif
static const struct cd_list_entry ide_cd_quirks_list[] = {
@@ -1866,14 +1954,14 @@
{ NULL, NULL, 0 }
};
-static unsigned int ide_cd_flags(struct hd_driveid *id)
+static unsigned int ide_cd_flags(u16 *id)
{
const struct cd_list_entry *cle = ide_cd_quirks_list;
while (cle->id_model) {
- if (strcmp(cle->id_model, id->model) == 0 &&
+ if (strcmp(cle->id_model, (char *)&id[ATA_ID_PROD]) == 0 &&
(cle->id_firmware == NULL ||
- strstr(id->fw_rev, cle->id_firmware)))
+ strstr((char *)&id[ATA_ID_FW_REV], cle->id_firmware)))
return cle->cd_flags;
cle++;
}
@@ -1885,9 +1973,12 @@
{
struct cdrom_info *cd = drive->driver_data;
struct cdrom_device_info *cdi = &cd->devinfo;
- struct hd_driveid *id = drive->id;
+ u16 *id = drive->id;
+ char *fw_rev = (char *)&id[ATA_ID_FW_REV];
int nslots;
+ ide_debug_log(IDE_DBG_PROBE, "Call %s\n", __func__);
+
blk_queue_prep_rq(drive->queue, ide_cdrom_prep_fn);
blk_queue_dma_alignment(drive->queue, 31);
blk_queue_update_dma_pad(drive->queue, 15);
@@ -1895,20 +1986,15 @@
if (!drive->queue->unplug_delay)
drive->queue->unplug_delay = 1;
- drive->special.all = 0;
-
drive->atapi_flags = IDE_AFLAG_MEDIA_CHANGED | IDE_AFLAG_NO_EJECT |
ide_cd_flags(id);
- if ((id->config & 0x0060) == 0x20)
- drive->atapi_flags |= IDE_AFLAG_DRQ_INTERRUPT;
-
if ((drive->atapi_flags & IDE_AFLAG_VERTOS_300_SSD) &&
- id->fw_rev[4] == '1' && id->fw_rev[6] <= '2')
+ fw_rev[4] == '1' && fw_rev[6] <= '2')
drive->atapi_flags |= (IDE_AFLAG_TOCTRACKS_AS_BCD |
IDE_AFLAG_TOCADDR_AS_BCD);
else if ((drive->atapi_flags & IDE_AFLAG_VERTOS_600_ESD) &&
- id->fw_rev[4] == '1' && id->fw_rev[6] <= '2')
+ fw_rev[4] == '1' && fw_rev[6] <= '2')
drive->atapi_flags |= IDE_AFLAG_TOCTRACKS_AS_BCD;
else if (drive->atapi_flags & IDE_AFLAG_SANYO_3CD)
/* 3 => use CD in slot 0 */
@@ -1919,15 +2005,19 @@
/* set correct block size */
blk_queue_hardsect_size(drive->queue, CD_FRAMESIZE);
- drive->dsc_overlap = (drive->next != drive);
+ if (drive->next != drive)
+ drive->dev_flags |= IDE_DFLAG_DSC_OVERLAP;
+ else
+ drive->dev_flags &= ~IDE_DFLAG_DSC_OVERLAP;
if (ide_cdrom_register(drive, nslots)) {
- printk(KERN_ERR "%s: %s failed to register device with the"
+ printk(KERN_ERR PFX "%s: %s failed to register device with the"
" cdrom driver.\n", drive->name, __func__);
cd->devinfo.handle = NULL;
return 1;
}
- ide_cdrom_add_settings(drive);
+
+ ide_proc_register_driver(drive, cd->driver);
return 0;
}
@@ -1935,6 +2025,8 @@
{
struct cdrom_info *info = drive->driver_data;
+ ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__);
+
ide_proc_unregister_driver(drive, info->driver);
del_gendisk(info->disk);
@@ -1944,15 +2036,17 @@
static void ide_cd_release(struct kref *kref)
{
- struct cdrom_info *info = to_ide_cd(kref);
+ struct cdrom_info *info = to_ide_drv(kref, cdrom_info);
struct cdrom_device_info *devinfo = &info->devinfo;
ide_drive_t *drive = info->drive;
struct gendisk *g = info->disk;
+ ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__);
+
kfree(info->toc);
if (devinfo->handle == drive)
unregister_cdrom(devinfo);
- drive->dsc_overlap = 0;
+ drive->dev_flags &= ~IDE_DFLAG_DSC_OVERLAP;
drive->driver_data = NULL;
blk_queue_prep_rq(drive->queue, NULL);
g->private_data = NULL;
@@ -1971,13 +2065,12 @@
.probe = ide_cd_probe,
.remove = ide_cd_remove,
.version = IDECD_VERSION,
- .media = ide_cdrom,
- .supports_dsc_overlap = 1,
.do_request = ide_cd_do_request,
.end_request = ide_end_request,
.error = __ide_error,
#ifdef CONFIG_IDE_PROC_FS
.proc = idecd_proc,
+ .settings = idecd_settings,
#endif
};
@@ -2002,7 +2095,7 @@
static int idecd_release(struct inode *inode, struct file *file)
{
struct gendisk *disk = inode->i_bdev->bd_disk;
- struct cdrom_info *info = ide_cd_g(disk);
+ struct cdrom_info *info = ide_drv_g(disk, cdrom_info);
cdrom_release(&info->devinfo, file);
@@ -2054,7 +2147,7 @@
unsigned int cmd, unsigned long arg)
{
struct block_device *bdev = inode->i_bdev;
- struct cdrom_info *info = ide_cd_g(bdev->bd_disk);
+ struct cdrom_info *info = ide_drv_g(bdev->bd_disk, cdrom_info);
int err;
switch (cmd) {
@@ -2075,13 +2168,13 @@
static int idecd_media_changed(struct gendisk *disk)
{
- struct cdrom_info *info = ide_cd_g(disk);
+ struct cdrom_info *info = ide_drv_g(disk, cdrom_info);
return cdrom_media_changed(&info->devinfo);
}
static int idecd_revalidate_disk(struct gendisk *disk)
{
- struct cdrom_info *info = ide_cd_g(disk);
+ struct cdrom_info *info = ide_drv_g(disk, cdrom_info);
struct request_sense sense;
ide_cd_read_toc(info->drive, &sense);
@@ -2100,8 +2193,11 @@
/* module options */
static char *ignore;
-
module_param(ignore, charp, 0400);
+
+static unsigned long debug_mask;
+module_param(debug_mask, ulong, 0644);
+
MODULE_DESCRIPTION("ATAPI CD-ROM Driver");
static int ide_cd_probe(ide_drive_t *drive)
@@ -2110,23 +2206,30 @@
struct gendisk *g;
struct request_sense sense;
+ ide_debug_log(IDE_DBG_PROBE, "Call %s, drive->driver_req: %s, "
+ "drive->media: 0x%x\n", __func__, drive->driver_req,
+ drive->media);
+
if (!strstr("ide-cdrom", drive->driver_req))
goto failed;
- if (!drive->present)
- goto failed;
+
if (drive->media != ide_cdrom && drive->media != ide_optical)
goto failed;
+
/* skip drives that we were told to ignore */
if (ignore != NULL) {
if (strstr(ignore, drive->name)) {
- printk(KERN_INFO "ide-cd: ignoring drive %s\n",
+ printk(KERN_INFO PFX "ignoring drive %s\n",
drive->name);
goto failed;
}
}
+
+ drive->debug_mask = debug_mask;
+
info = kzalloc(sizeof(struct cdrom_info), GFP_KERNEL);
if (info == NULL) {
- printk(KERN_ERR "%s: Can't allocate a cdrom structure\n",
+ printk(KERN_ERR PFX "%s: Can't allocate a cdrom structure\n",
drive->name);
goto failed;
}
@@ -2137,8 +2240,6 @@
ide_init_disk(g, drive);
- ide_proc_register_driver(drive, &ide_cdrom_driver);
-
kref_init(&info->kref);
info->drive = drive;
@@ -2153,7 +2254,6 @@
g->driverfs_dev = &drive->gendev;
g->flags = GENHD_FL_CD | GENHD_FL_REMOVABLE;
if (ide_cdrom_setup(drive)) {
- ide_proc_unregister_driver(drive, &ide_cdrom_driver);
ide_cd_release(&info->kref);
goto failed;
}
@@ -2177,6 +2277,7 @@
static int __init ide_cdrom_init(void)
{
+ printk(KERN_INFO DRV_NAME " driver " IDECD_VERSION "\n");
return driver_register(&ide_cdrom_driver.gen_driver);
}
diff --git a/drivers/ide/ide-cd.h b/drivers/ide/ide-cd.h
index 61a4599..5882b9a 100644
--- a/drivers/ide/ide-cd.h
+++ b/drivers/ide/ide-cd.h
@@ -88,7 +88,6 @@
struct request_sense sense_data;
struct request request_sense_request;
- int dma;
unsigned long last_block;
unsigned long start_seek;
diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c
index 33ea8c0..3853bde 100644
--- a/drivers/ide/ide-disk.c
+++ b/drivers/ide/ide-disk.c
@@ -30,10 +30,8 @@
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/leds.h>
-
-#define _IDE_DISK
-
#include <linux/ide.h>
+#include <linux/hdreg.h>
#include <asm/byteorder.h>
#include <asm/irq.h>
@@ -47,21 +45,12 @@
#define IDE_DISK_MINORS 0
#endif
-struct ide_disk_obj {
- ide_drive_t *drive;
- ide_driver_t *driver;
- struct gendisk *disk;
- struct kref kref;
- unsigned int openers; /* protected by BKL for now */
-};
+#include "ide-disk.h"
static DEFINE_MUTEX(idedisk_ref_mutex);
#define to_ide_disk(obj) container_of(obj, struct ide_disk_obj, kref)
-#define ide_disk_g(disk) \
- container_of((disk)->private_data, struct ide_disk_obj, driver)
-
static void ide_disk_release(struct kref *);
static struct ide_disk_obj *ide_disk_get(struct gendisk *disk)
@@ -90,68 +79,19 @@
mutex_unlock(&idedisk_ref_mutex);
}
-/*
- * lba_capacity_is_ok() performs a sanity check on the claimed "lba_capacity"
- * value for this drive (from its reported identification information).
- *
- * Returns: 1 if lba_capacity looks sensible
- * 0 otherwise
- *
- * It is called only once for each drive.
- */
-static int lba_capacity_is_ok(struct hd_driveid *id)
-{
- unsigned long lba_sects, chs_sects, head, tail;
-
- /* No non-LBA info .. so valid! */
- if (id->cyls == 0)
- return 1;
-
- /*
- * The ATA spec tells large drives to return
- * C/H/S = 16383/16/63 independent of their size.
- * Some drives can be jumpered to use 15 heads instead of 16.
- * Some drives can be jumpered to use 4092 cyls instead of 16383.
- */
- if ((id->cyls == 16383
- || (id->cyls == 4092 && id->cur_cyls == 16383)) &&
- id->sectors == 63 &&
- (id->heads == 15 || id->heads == 16) &&
- (id->lba_capacity >= 16383*63*id->heads))
- return 1;
-
- lba_sects = id->lba_capacity;
- chs_sects = id->cyls * id->heads * id->sectors;
-
- /* perform a rough sanity check on lba_sects: within 10% is OK */
- if ((lba_sects - chs_sects) < chs_sects/10)
- return 1;
-
- /* some drives have the word order reversed */
- head = ((lba_sects >> 16) & 0xffff);
- tail = (lba_sects & 0xffff);
- lba_sects = (head | (tail << 16));
- if ((lba_sects - chs_sects) < chs_sects/10) {
- id->lba_capacity = lba_sects;
- return 1; /* lba_capacity is (now) good */
- }
-
- return 0; /* lba_capacity value may be bad */
-}
-
static const u8 ide_rw_cmds[] = {
- WIN_MULTREAD,
- WIN_MULTWRITE,
- WIN_MULTREAD_EXT,
- WIN_MULTWRITE_EXT,
- WIN_READ,
- WIN_WRITE,
- WIN_READ_EXT,
- WIN_WRITE_EXT,
- WIN_READDMA,
- WIN_WRITEDMA,
- WIN_READDMA_EXT,
- WIN_WRITEDMA_EXT,
+ ATA_CMD_READ_MULTI,
+ ATA_CMD_WRITE_MULTI,
+ ATA_CMD_READ_MULTI_EXT,
+ ATA_CMD_WRITE_MULTI_EXT,
+ ATA_CMD_PIO_READ,
+ ATA_CMD_PIO_WRITE,
+ ATA_CMD_PIO_READ_EXT,
+ ATA_CMD_PIO_WRITE_EXT,
+ ATA_CMD_READ,
+ ATA_CMD_WRITE,
+ ATA_CMD_READ_EXT,
+ ATA_CMD_WRITE_EXT,
};
static const u8 ide_data_phases[] = {
@@ -191,9 +131,9 @@
sector_t block)
{
ide_hwif_t *hwif = HWIF(drive);
- unsigned int dma = drive->using_dma;
u16 nsectors = (u16)rq->nr_sectors;
- u8 lba48 = (drive->addressing == 1) ? 1 : 0;
+ u8 lba48 = !!(drive->dev_flags & IDE_DFLAG_LBA48);
+ u8 dma = !!(drive->dev_flags & IDE_DFLAG_USING_DMA);
ide_task_t task;
struct ide_taskfile *tf = &task.tf;
ide_startstop_t rc;
@@ -213,7 +153,7 @@
memset(&task, 0, sizeof(task));
task.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
- if (drive->select.b.lba) {
+ if (drive->dev_flags & IDE_DFLAG_LBA) {
if (lba48) {
pr_debug("%s: LBA=0x%012llx\n", drive->name,
(unsigned long long)block);
@@ -238,6 +178,8 @@
tf->lbah = block >>= 8;
tf->device = (block >> 8) & 0xf;
}
+
+ tf->device |= ATA_LBA;
} else {
unsigned int sect, head, cyl, track;
@@ -288,7 +230,7 @@
{
ide_hwif_t *hwif = HWIF(drive);
- BUG_ON(drive->blocked);
+ BUG_ON(drive->dev_flags & IDE_DFLAG_BLOCKED);
if (!blk_fs_request(rq)) {
blk_dump_rq_flags(rq, "ide_do_rw_disk - bad command");
@@ -322,9 +264,9 @@
/* Create IDE/ATA command request structure */
memset(&args, 0, sizeof(ide_task_t));
if (lba48)
- tf->command = WIN_READ_NATIVE_MAX_EXT;
+ tf->command = ATA_CMD_READ_NATIVE_MAX_EXT;
else
- tf->command = WIN_READ_NATIVE_MAX;
+ tf->command = ATA_CMD_READ_NATIVE_MAX;
tf->device = ATA_LBA;
args.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
if (lba48)
@@ -359,10 +301,10 @@
tf->hob_lbal = (addr_req >>= 8) & 0xff;
tf->hob_lbam = (addr_req >>= 8) & 0xff;
tf->hob_lbah = (addr_req >>= 8) & 0xff;
- tf->command = WIN_SET_MAX_EXT;
+ tf->command = ATA_CMD_SET_MAX_EXT;
} else {
tf->device = (addr_req >>= 8) & 0x0f;
- tf->command = WIN_SET_MAX;
+ tf->command = ATA_CMD_SET_MAX;
}
tf->device |= ATA_LBA;
args.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
@@ -385,25 +327,6 @@
}
/*
- * Bits 10 of command_set_1 and cfs_enable_1 must be equal,
- * so on non-buggy drives we need test only one.
- * However, we should also check whether these fields are valid.
- */
-static inline int idedisk_supports_hpa(const struct hd_driveid *id)
-{
- return (id->command_set_1 & 0x0400) && (id->cfs_enable_1 & 0x0400);
-}
-
-/*
- * The same here.
- */
-static inline int idedisk_supports_lba48(const struct hd_driveid *id)
-{
- return (id->command_set_2 & 0x0400) && (id->cfs_enable_2 & 0x0400)
- && id->lba_capacity_2;
-}
-
-/*
* Some disks report total number of sectors instead of
* maximum sector address. We list them here.
*/
@@ -417,7 +340,7 @@
static void idedisk_check_hpa(ide_drive_t *drive)
{
unsigned long long capacity, set_max;
- int lba48 = idedisk_supports_lba48(drive->id);
+ int lba48 = ata_id_lba48_enabled(drive->id);
capacity = drive->capacity64;
@@ -453,139 +376,40 @@
static void init_idedisk_capacity(ide_drive_t *drive)
{
- struct hd_driveid *id = drive->id;
- /*
- * If this drive supports the Host Protected Area feature set,
- * then we may need to change our opinion about the drive's capacity.
- */
- int hpa = idedisk_supports_hpa(id);
+ u16 *id = drive->id;
+ int lba;
- if (idedisk_supports_lba48(id)) {
+ if (ata_id_lba48_enabled(id)) {
/* drive speaks 48-bit LBA */
- drive->select.b.lba = 1;
- drive->capacity64 = id->lba_capacity_2;
- if (hpa)
- idedisk_check_hpa(drive);
- } else if ((id->capability & 2) && lba_capacity_is_ok(id)) {
+ lba = 1;
+ drive->capacity64 = ata_id_u64(id, ATA_ID_LBA_CAPACITY_2);
+ } else if (ata_id_has_lba(id) && ata_id_is_lba_capacity_ok(id)) {
/* drive speaks 28-bit LBA */
- drive->select.b.lba = 1;
- drive->capacity64 = id->lba_capacity;
- if (hpa)
- idedisk_check_hpa(drive);
+ lba = 1;
+ drive->capacity64 = ata_id_u32(id, ATA_ID_LBA_CAPACITY);
} else {
/* drive speaks boring old 28-bit CHS */
+ lba = 0;
drive->capacity64 = drive->cyl * drive->head * drive->sect;
}
-}
-static sector_t idedisk_capacity(ide_drive_t *drive)
-{
- return drive->capacity64 - drive->sect0;
-}
+ if (lba) {
+ drive->dev_flags |= IDE_DFLAG_LBA;
-#ifdef CONFIG_IDE_PROC_FS
-static int smart_enable(ide_drive_t *drive)
-{
- ide_task_t args;
- struct ide_taskfile *tf = &args.tf;
-
- memset(&args, 0, sizeof(ide_task_t));
- tf->feature = SMART_ENABLE;
- tf->lbam = SMART_LCYL_PASS;
- tf->lbah = SMART_HCYL_PASS;
- tf->command = WIN_SMART;
- args.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
- return ide_no_data_taskfile(drive, &args);
-}
-
-static int get_smart_data(ide_drive_t *drive, u8 *buf, u8 sub_cmd)
-{
- ide_task_t args;
- struct ide_taskfile *tf = &args.tf;
-
- memset(&args, 0, sizeof(ide_task_t));
- tf->feature = sub_cmd;
- tf->nsect = 0x01;
- tf->lbam = SMART_LCYL_PASS;
- tf->lbah = SMART_HCYL_PASS;
- tf->command = WIN_SMART;
- args.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
- args.data_phase = TASKFILE_IN;
- (void) smart_enable(drive);
- return ide_raw_taskfile(drive, &args, buf, 1);
-}
-
-static int proc_idedisk_read_cache
- (char *page, char **start, off_t off, int count, int *eof, void *data)
-{
- ide_drive_t *drive = (ide_drive_t *) data;
- char *out = page;
- int len;
-
- if (drive->id_read)
- len = sprintf(out, "%i\n", drive->id->buf_size / 2);
- else
- len = sprintf(out, "(none)\n");
-
- PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
-}
-
-static int proc_idedisk_read_capacity
- (char *page, char **start, off_t off, int count, int *eof, void *data)
-{
- ide_drive_t*drive = (ide_drive_t *)data;
- int len;
-
- len = sprintf(page, "%llu\n", (long long)idedisk_capacity(drive));
-
- PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
-}
-
-static int proc_idedisk_read_smart(char *page, char **start, off_t off,
- int count, int *eof, void *data, u8 sub_cmd)
-{
- ide_drive_t *drive = (ide_drive_t *)data;
- int len = 0, i = 0;
-
- if (get_smart_data(drive, page, sub_cmd) == 0) {
- unsigned short *val = (unsigned short *) page;
- char *out = ((char *)val) + (SECTOR_WORDS * 4);
- page = out;
- do {
- out += sprintf(out, "%04x%c", le16_to_cpu(*val),
- (++i & 7) ? ' ' : '\n');
- val += 1;
- } while (i < (SECTOR_WORDS * 2));
- len = out - page;
+ /*
+ * If this device supports the Host Protected Area feature set,
+ * then we may need to change our opinion about its capacity.
+ */
+ if (ata_id_hpa_enabled(id))
+ idedisk_check_hpa(drive);
}
-
- PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
}
-static int proc_idedisk_read_sv
- (char *page, char **start, off_t off, int count, int *eof, void *data)
+sector_t ide_disk_capacity(ide_drive_t *drive)
{
- return proc_idedisk_read_smart(page, start, off, count, eof, data,
- SMART_READ_VALUES);
+ return drive->capacity64;
}
-static int proc_idedisk_read_st
- (char *page, char **start, off_t off, int count, int *eof, void *data)
-{
- return proc_idedisk_read_smart(page, start, off, count, eof, data,
- SMART_READ_THRESHOLDS);
-}
-
-static ide_proc_entry_t idedisk_proc[] = {
- { "cache", S_IFREG|S_IRUGO, proc_idedisk_read_cache, NULL },
- { "capacity", S_IFREG|S_IRUGO, proc_idedisk_read_capacity, NULL },
- { "geometry", S_IFREG|S_IRUGO, proc_ide_read_geometry, NULL },
- { "smart_values", S_IFREG|S_IRUSR, proc_idedisk_read_sv, NULL },
- { "smart_thresholds", S_IFREG|S_IRUSR, proc_idedisk_read_st, NULL },
- { NULL, 0, NULL, NULL }
-};
-#endif /* CONFIG_IDE_PROC_FS */
-
static void idedisk_prepare_flush(struct request_queue *q, struct request *rq)
{
ide_drive_t *drive = q->queuedata;
@@ -595,11 +419,11 @@
BUG_ON(task == NULL);
memset(task, 0, sizeof(*task));
- if (ide_id_has_flush_cache_ext(drive->id) &&
+ if (ata_id_flush_ext_enabled(drive->id) &&
(drive->capacity64 >= (1UL << 28)))
- task->tf.command = WIN_FLUSH_CACHE_EXT;
+ task->tf.command = ATA_CMD_FLUSH_EXT;
else
- task->tf.command = WIN_FLUSH_CACHE;
+ task->tf.command = ATA_CMD_FLUSH;
task->tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_OUT_DEVICE |
IDE_TFLAG_DYN;
task->data_phase = TASKFILE_NO_DATA;
@@ -609,6 +433,8 @@
rq->special = task;
}
+ide_devset_get(multcount, mult_count);
+
/*
* This is tightly woven into the driver->do_special can not touch.
* DON'T do it again until a total personality rewrite is committed.
@@ -618,7 +444,7 @@
struct request *rq;
int error;
- if (arg < 0 || arg > drive->id->max_multsect)
+ if (arg < 0 || arg > (drive->id[ATA_ID_MAX_MULTSECT] & 0xff))
return -EINVAL;
if (drive->special.b.set_multmode)
@@ -635,26 +461,43 @@
return (drive->mult_count == arg) ? 0 : -EIO;
}
+ide_devset_get_flag(nowerr, IDE_DFLAG_NOWERR);
+
static int set_nowerr(ide_drive_t *drive, int arg)
{
if (arg < 0 || arg > 1)
return -EINVAL;
- if (ide_spin_wait_hwgroup(drive))
- return -EBUSY;
- drive->nowerr = arg;
+ if (arg)
+ drive->dev_flags |= IDE_DFLAG_NOWERR;
+ else
+ drive->dev_flags &= ~IDE_DFLAG_NOWERR;
+
drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT;
- spin_unlock_irq(&ide_lock);
+
return 0;
}
+static int ide_do_setfeature(ide_drive_t *drive, u8 feature, u8 nsect)
+{
+ ide_task_t task;
+
+ memset(&task, 0, sizeof(task));
+ task.tf.feature = feature;
+ task.tf.nsect = nsect;
+ task.tf.command = ATA_CMD_SET_FEATURES;
+ task.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
+
+ return ide_no_data_taskfile(drive, &task);
+}
+
static void update_ordered(ide_drive_t *drive)
{
- struct hd_driveid *id = drive->id;
+ u16 *id = drive->id;
unsigned ordered = QUEUE_ORDERED_NONE;
prepare_flush_fn *prep_fn = NULL;
- if (drive->wcache) {
+ if (drive->dev_flags & IDE_DFLAG_WCACHE) {
unsigned long long capacity;
int barrier;
/*
@@ -665,10 +508,12 @@
* time we have trimmed the drive capacity if LBA48 is
* not available so we don't need to recheck that.
*/
- capacity = idedisk_capacity(drive);
- barrier = ide_id_has_flush_cache(id) && !drive->noflush &&
- (drive->addressing == 0 || capacity <= (1ULL << 28) ||
- ide_id_has_flush_cache_ext(id));
+ capacity = ide_disk_capacity(drive);
+ barrier = ata_id_flush_enabled(id) &&
+ (drive->dev_flags & IDE_DFLAG_NOFLUSH) == 0 &&
+ ((drive->dev_flags & IDE_DFLAG_LBA48) == 0 ||
+ capacity <= (1ULL << 28) ||
+ ata_id_flush_ext_enabled(id));
printk(KERN_INFO "%s: cache flushes %ssupported\n",
drive->name, barrier ? "" : "not ");
@@ -683,23 +528,24 @@
blk_queue_ordered(drive->queue, ordered, prep_fn);
}
-static int write_cache(ide_drive_t *drive, int arg)
+ide_devset_get_flag(wcache, IDE_DFLAG_WCACHE);
+
+static int set_wcache(ide_drive_t *drive, int arg)
{
- ide_task_t args;
int err = 1;
if (arg < 0 || arg > 1)
return -EINVAL;
- if (ide_id_has_flush_cache(drive->id)) {
- memset(&args, 0, sizeof(ide_task_t));
- args.tf.feature = arg ?
- SETFEATURES_EN_WCACHE : SETFEATURES_DIS_WCACHE;
- args.tf.command = WIN_SETFEATURES;
- args.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
- err = ide_no_data_taskfile(drive, &args);
- if (err == 0)
- drive->wcache = arg;
+ if (ata_id_flush_enabled(drive->id)) {
+ err = ide_do_setfeature(drive,
+ arg ? SETFEATURES_WC_ON : SETFEATURES_WC_OFF, 0);
+ if (err == 0) {
+ if (arg)
+ drive->dev_flags |= IDE_DFLAG_WCACHE;
+ else
+ drive->dev_flags &= ~IDE_DFLAG_WCACHE;
+ }
}
update_ordered(drive);
@@ -712,108 +558,88 @@
ide_task_t args;
memset(&args, 0, sizeof(ide_task_t));
- if (ide_id_has_flush_cache_ext(drive->id))
- args.tf.command = WIN_FLUSH_CACHE_EXT;
+ if (ata_id_flush_ext_enabled(drive->id))
+ args.tf.command = ATA_CMD_FLUSH_EXT;
else
- args.tf.command = WIN_FLUSH_CACHE;
+ args.tf.command = ATA_CMD_FLUSH;
args.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
return ide_no_data_taskfile(drive, &args);
}
+ide_devset_get(acoustic, acoustic);
+
static int set_acoustic(ide_drive_t *drive, int arg)
{
- ide_task_t args;
-
if (arg < 0 || arg > 254)
return -EINVAL;
- memset(&args, 0, sizeof(ide_task_t));
- args.tf.feature = arg ? SETFEATURES_EN_AAM : SETFEATURES_DIS_AAM;
- args.tf.nsect = arg;
- args.tf.command = WIN_SETFEATURES;
- args.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
- ide_no_data_taskfile(drive, &args);
+ ide_do_setfeature(drive,
+ arg ? SETFEATURES_AAM_ON : SETFEATURES_AAM_OFF, arg);
+
drive->acoustic = arg;
+
return 0;
}
+ide_devset_get_flag(addressing, IDE_DFLAG_LBA48);
+
/*
* drive->addressing:
* 0: 28-bit
* 1: 48-bit
* 2: 48-bit capable doing 28-bit
*/
-static int set_lba_addressing(ide_drive_t *drive, int arg)
+static int set_addressing(ide_drive_t *drive, int arg)
{
if (arg < 0 || arg > 2)
return -EINVAL;
- drive->addressing = 0;
-
- if (drive->hwif->host_flags & IDE_HFLAG_NO_LBA48)
- return 0;
-
- if (!idedisk_supports_lba48(drive->id))
+ if (arg && ((drive->hwif->host_flags & IDE_HFLAG_NO_LBA48) ||
+ ata_id_lba48_enabled(drive->id) == 0))
return -EIO;
- drive->addressing = arg;
+
+ if (arg == 2)
+ arg = 0;
+
+ if (arg)
+ drive->dev_flags |= IDE_DFLAG_LBA48;
+ else
+ drive->dev_flags &= ~IDE_DFLAG_LBA48;
+
return 0;
}
-#ifdef CONFIG_IDE_PROC_FS
-static void idedisk_add_settings(ide_drive_t *drive)
-{
- struct hd_driveid *id = drive->id;
+ide_ext_devset_rw(acoustic, acoustic);
+ide_ext_devset_rw(address, addressing);
+ide_ext_devset_rw(multcount, multcount);
+ide_ext_devset_rw(wcache, wcache);
- ide_add_setting(drive, "bios_cyl", SETTING_RW, TYPE_INT, 0, 65535, 1, 1,
- &drive->bios_cyl, NULL);
- ide_add_setting(drive, "bios_head", SETTING_RW, TYPE_BYTE, 0, 255, 1, 1,
- &drive->bios_head, NULL);
- ide_add_setting(drive, "bios_sect", SETTING_RW, TYPE_BYTE, 0, 63, 1, 1,
- &drive->bios_sect, NULL);
- ide_add_setting(drive, "address", SETTING_RW, TYPE_BYTE, 0, 2, 1, 1,
- &drive->addressing, set_lba_addressing);
- ide_add_setting(drive, "multcount", SETTING_RW, TYPE_BYTE, 0,
- id->max_multsect, 1, 1, &drive->mult_count,
- set_multcount);
- ide_add_setting(drive, "nowerr", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1,
- &drive->nowerr, set_nowerr);
- ide_add_setting(drive, "lun", SETTING_RW, TYPE_INT, 0, 7, 1, 1,
- &drive->lun, NULL);
- ide_add_setting(drive, "wcache", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1,
- &drive->wcache, write_cache);
- ide_add_setting(drive, "acoustic", SETTING_RW, TYPE_BYTE, 0, 254, 1, 1,
- &drive->acoustic, set_acoustic);
- ide_add_setting(drive, "failures", SETTING_RW, TYPE_INT, 0, 65535, 1, 1,
- &drive->failures, NULL);
- ide_add_setting(drive, "max_failures", SETTING_RW, TYPE_INT, 0, 65535,
- 1, 1, &drive->max_failures, NULL);
-}
-#else
-static inline void idedisk_add_settings(ide_drive_t *drive) { ; }
-#endif
+ide_ext_devset_rw_sync(nowerr, nowerr);
static void idedisk_setup(ide_drive_t *drive)
{
+ struct ide_disk_obj *idkp = drive->driver_data;
ide_hwif_t *hwif = drive->hwif;
- struct hd_driveid *id = drive->id;
+ u16 *id = drive->id;
+ char *m = (char *)&id[ATA_ID_PROD];
unsigned long long capacity;
- idedisk_add_settings(drive);
+ ide_proc_register_driver(drive, idkp->driver);
- if (drive->id_read == 0)
+ if ((drive->dev_flags & IDE_DFLAG_ID_READ) == 0)
return;
- if (drive->removable) {
+ if (drive->dev_flags & IDE_DFLAG_REMOVABLE) {
/*
* Removable disks (eg. SYQUEST); ignore 'WD' drives
*/
- if (id->model[0] != 'W' || id->model[1] != 'D')
- drive->doorlocking = 1;
+ if (m[0] != 'W' || m[1] != 'D')
+ drive->dev_flags |= IDE_DFLAG_DOORLOCKING;
}
- (void)set_lba_addressing(drive, 1);
+ (void)set_addressing(drive, 1);
- if (drive->addressing == 1) {
+ if (drive->dev_flags & IDE_DFLAG_LBA48) {
int max_s = 2048;
if (max_s > hwif->rqsize)
@@ -829,7 +655,8 @@
init_idedisk_capacity(drive);
/* limit drive capacity to 137GB if LBA48 cannot be used */
- if (drive->addressing == 0 && drive->capacity64 > 1ULL << 28) {
+ if ((drive->dev_flags & IDE_DFLAG_LBA48) == 0 &&
+ drive->capacity64 > 1ULL << 28) {
printk(KERN_WARNING "%s: cannot use LBA48 - full capacity "
"%llu sectors (%llu MB)\n",
drive->name, (unsigned long long)drive->capacity64,
@@ -837,24 +664,24 @@
drive->capacity64 = 1ULL << 28;
}
- if ((hwif->host_flags & IDE_HFLAG_NO_LBA48_DMA) && drive->addressing) {
+ if ((hwif->host_flags & IDE_HFLAG_NO_LBA48_DMA) &&
+ (drive->dev_flags & IDE_DFLAG_LBA48)) {
if (drive->capacity64 > 1ULL << 28) {
printk(KERN_INFO "%s: cannot use LBA48 DMA - PIO mode"
" will be used for accessing sectors "
"> %u\n", drive->name, 1 << 28);
} else
- drive->addressing = 0;
+ drive->dev_flags &= ~IDE_DFLAG_LBA48;
}
/*
* if possible, give fdisk access to more of the drive,
* by correcting bios_cyls:
*/
- capacity = idedisk_capacity(drive);
+ capacity = ide_disk_capacity(drive);
- if (!drive->forced_geom) {
-
- if (idedisk_supports_lba48(drive->id)) {
+ if ((drive->dev_flags & IDE_DFLAG_FORCED_GEOM) == 0) {
+ if (ata_id_lba48_enabled(drive->id)) {
/* compatibility */
drive->bios_sect = 63;
drive->bios_head = 255;
@@ -880,22 +707,23 @@
drive->name, capacity, sectors_to_MB(capacity));
/* Only print cache size when it was specified */
- if (id->buf_size)
- printk(KERN_CONT " w/%dKiB Cache", id->buf_size / 2);
+ if (id[ATA_ID_BUF_SIZE])
+ printk(KERN_CONT " w/%dKiB Cache", id[ATA_ID_BUF_SIZE] / 2);
printk(KERN_CONT ", CHS=%d/%d/%d\n",
drive->bios_cyl, drive->bios_head, drive->bios_sect);
/* write cache enabled? */
- if ((id->csfo & 1) || (id->cfs_enable_1 & (1 << 5)))
- drive->wcache = 1;
+ if ((id[ATA_ID_CSFO] & 1) || ata_id_wcache_enabled(id))
+ drive->dev_flags |= IDE_DFLAG_WCACHE;
- write_cache(drive, 1);
+ set_wcache(drive, 1);
}
static void ide_cacheflush_p(ide_drive_t *drive)
{
- if (!drive->wcache || !ide_id_has_flush_cache(drive->id))
+ if (ata_id_flush_enabled(drive->id) == 0 ||
+ (drive->dev_flags & IDE_DFLAG_WCACHE) == 0)
return;
if (do_idedisk_flushcache(drive))
@@ -937,7 +765,7 @@
*/
static void ide_disk_resume(ide_drive_t *drive)
{
- if (idedisk_supports_hpa(drive->id))
+ if (ata_id_hpa_enabled(drive->id))
init_idedisk_capacity(drive);
}
@@ -979,13 +807,12 @@
.resume = ide_disk_resume,
.shutdown = ide_device_shutdown,
.version = IDEDISK_VERSION,
- .media = ide_disk,
- .supports_dsc_overlap = 0,
.do_request = ide_do_rw_disk,
.end_request = ide_end_request,
.error = __ide_error,
#ifdef CONFIG_IDE_PROC_FS
- .proc = idedisk_proc,
+ .proc = ide_disk_proc,
+ .settings = ide_disk_settings,
#endif
};
@@ -994,7 +821,7 @@
ide_task_t task;
memset(&task, 0, sizeof(task));
- task.tf.command = on ? WIN_DOORLOCK : WIN_DOORUNLOCK;
+ task.tf.command = on ? ATA_CMD_MEDIA_LOCK : ATA_CMD_MEDIA_UNLOCK;
task.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
return ide_no_data_taskfile(drive, &task);
@@ -1014,15 +841,16 @@
idkp->openers++;
- if (drive->removable && idkp->openers == 1) {
+ if ((drive->dev_flags & IDE_DFLAG_REMOVABLE) && idkp->openers == 1) {
check_disk_change(inode->i_bdev);
/*
* Ignore the return code from door_lock,
* since the open() has already succeeded,
* and the door_lock is irrelevant at this point.
*/
- if (drive->doorlocking && idedisk_set_doorlock(drive, 1))
- drive->doorlocking = 0;
+ if ((drive->dev_flags & IDE_DFLAG_DOORLOCKING) &&
+ idedisk_set_doorlock(drive, 1))
+ drive->dev_flags &= ~IDE_DFLAG_DOORLOCKING;
}
return 0;
}
@@ -1036,9 +864,10 @@
if (idkp->openers == 1)
ide_cacheflush_p(drive);
- if (drive->removable && idkp->openers == 1) {
- if (drive->doorlocking && idedisk_set_doorlock(drive, 0))
- drive->doorlocking = 0;
+ if ((drive->dev_flags & IDE_DFLAG_REMOVABLE) && idkp->openers == 1) {
+ if ((drive->dev_flags & IDE_DFLAG_DOORLOCKING) &&
+ idedisk_set_doorlock(drive, 0))
+ drive->dev_flags &= ~IDE_DFLAG_DOORLOCKING;
}
idkp->openers--;
@@ -1059,72 +888,25 @@
return 0;
}
-static int idedisk_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- unsigned long flags;
- struct block_device *bdev = inode->i_bdev;
- struct ide_disk_obj *idkp = ide_disk_g(bdev->bd_disk);
- ide_drive_t *drive = idkp->drive;
- int err, (*setfunc)(ide_drive_t *, int);
- u8 *val;
-
- switch (cmd) {
- case HDIO_GET_ADDRESS: val = &drive->addressing; goto read_val;
- case HDIO_GET_MULTCOUNT: val = &drive->mult_count; goto read_val;
- case HDIO_GET_NOWERR: val = &drive->nowerr; goto read_val;
- case HDIO_GET_WCACHE: val = &drive->wcache; goto read_val;
- case HDIO_GET_ACOUSTIC: val = &drive->acoustic; goto read_val;
- case HDIO_SET_ADDRESS: setfunc = set_lba_addressing; goto set_val;
- case HDIO_SET_MULTCOUNT: setfunc = set_multcount; goto set_val;
- case HDIO_SET_NOWERR: setfunc = set_nowerr; goto set_val;
- case HDIO_SET_WCACHE: setfunc = write_cache; goto set_val;
- case HDIO_SET_ACOUSTIC: setfunc = set_acoustic; goto set_val;
- }
-
- return generic_ide_ioctl(drive, file, bdev, cmd, arg);
-
-read_val:
- mutex_lock(&ide_setting_mtx);
- spin_lock_irqsave(&ide_lock, flags);
- err = *val;
- spin_unlock_irqrestore(&ide_lock, flags);
- mutex_unlock(&ide_setting_mtx);
- return err >= 0 ? put_user(err, (long __user *)arg) : err;
-
-set_val:
- if (bdev != bdev->bd_contains)
- err = -EINVAL;
- else {
- if (!capable(CAP_SYS_ADMIN))
- err = -EACCES;
- else {
- mutex_lock(&ide_setting_mtx);
- err = setfunc(drive, arg);
- mutex_unlock(&ide_setting_mtx);
- }
- }
- return err;
-}
-
static int idedisk_media_changed(struct gendisk *disk)
{
struct ide_disk_obj *idkp = ide_disk_g(disk);
ide_drive_t *drive = idkp->drive;
/* do not scan partitions twice if this is a removable device */
- if (drive->attach) {
- drive->attach = 0;
+ if (drive->dev_flags & IDE_DFLAG_ATTACH) {
+ drive->dev_flags &= ~IDE_DFLAG_ATTACH;
return 0;
}
+
/* if removable, always assume it was changed */
- return drive->removable;
+ return !!(drive->dev_flags & IDE_DFLAG_REMOVABLE);
}
static int idedisk_revalidate_disk(struct gendisk *disk)
{
struct ide_disk_obj *idkp = ide_disk_g(disk);
- set_capacity(disk, idedisk_capacity(idkp->drive));
+ set_capacity(disk, ide_disk_capacity(idkp->drive));
return 0;
}
@@ -1132,7 +914,7 @@
.owner = THIS_MODULE,
.open = idedisk_open,
.release = idedisk_release,
- .ioctl = idedisk_ioctl,
+ .ioctl = ide_disk_ioctl,
.getgeo = idedisk_getgeo,
.media_changed = idedisk_media_changed,
.revalidate_disk = idedisk_revalidate_disk
@@ -1148,8 +930,7 @@
/* strstr("foo", "") is non-NULL */
if (!strstr("ide-disk", drive->driver_req))
goto failed;
- if (!drive->present)
- goto failed;
+
if (drive->media != ide_disk)
goto failed;
@@ -1163,8 +944,6 @@
ide_init_disk(g, drive);
- ide_proc_register_driver(drive, &idedisk_driver);
-
kref_init(&idkp->kref);
idkp->drive = drive;
@@ -1176,19 +955,20 @@
drive->driver_data = idkp;
idedisk_setup(drive);
- if ((!drive->head || drive->head > 16) && !drive->select.b.lba) {
+ if ((drive->dev_flags & IDE_DFLAG_LBA) == 0 &&
+ (drive->head == 0 || drive->head > 16)) {
printk(KERN_ERR "%s: INVALID GEOMETRY: %d PHYSICAL HEADS?\n",
drive->name, drive->head);
- drive->attach = 0;
+ drive->dev_flags &= ~IDE_DFLAG_ATTACH;
} else
- drive->attach = 1;
+ drive->dev_flags |= IDE_DFLAG_ATTACH;
g->minors = IDE_DISK_MINORS;
g->driverfs_dev = &drive->gendev;
g->flags |= GENHD_FL_EXT_DEVT;
- if (drive->removable)
- g->flags |= GENHD_FL_REMOVABLE;
- set_capacity(g, idedisk_capacity(drive));
+ if (drive->dev_flags & IDE_DFLAG_REMOVABLE)
+ g->flags = GENHD_FL_REMOVABLE;
+ set_capacity(g, ide_disk_capacity(drive));
g->fops = &idedisk_ops;
add_disk(g);
return 0;
@@ -1210,6 +990,7 @@
}
MODULE_ALIAS("ide:*m-disk*");
+MODULE_ALIAS("ide-disk");
module_init(idedisk_init);
module_exit(idedisk_exit);
MODULE_LICENSE("GPL");
diff --git a/drivers/ide/ide-disk.h b/drivers/ide/ide-disk.h
new file mode 100644
index 0000000..a82fa43
--- /dev/null
+++ b/drivers/ide/ide-disk.h
@@ -0,0 +1,32 @@
+#ifndef __IDE_DISK_H
+#define __IDE_DISK_H
+
+struct ide_disk_obj {
+ ide_drive_t *drive;
+ ide_driver_t *driver;
+ struct gendisk *disk;
+ struct kref kref;
+ unsigned int openers; /* protected by BKL for now */
+};
+
+#define ide_disk_g(disk) \
+ container_of((disk)->private_data, struct ide_disk_obj, driver)
+
+/* ide-disk.c */
+sector_t ide_disk_capacity(ide_drive_t *);
+ide_decl_devset(address);
+ide_decl_devset(multcount);
+ide_decl_devset(nowerr);
+ide_decl_devset(wcache);
+ide_decl_devset(acoustic);
+
+/* ide-disk_ioctl.c */
+int ide_disk_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+
+#ifdef CONFIG_IDE_PROC_FS
+/* ide-disk_proc.c */
+extern ide_proc_entry_t ide_disk_proc[];
+extern const struct ide_proc_devset ide_disk_settings[];
+#endif
+
+#endif /* __IDE_DISK_H */
diff --git a/drivers/ide/ide-disk_ioctl.c b/drivers/ide/ide-disk_ioctl.c
new file mode 100644
index 0000000..a6cf1a0
--- /dev/null
+++ b/drivers/ide/ide-disk_ioctl.c
@@ -0,0 +1,29 @@
+#include <linux/kernel.h>
+#include <linux/ide.h>
+#include <linux/hdreg.h>
+
+#include "ide-disk.h"
+
+static const struct ide_ioctl_devset ide_disk_ioctl_settings[] = {
+{ HDIO_GET_ADDRESS, HDIO_SET_ADDRESS, &ide_devset_address },
+{ HDIO_GET_MULTCOUNT, HDIO_SET_MULTCOUNT, &ide_devset_multcount },
+{ HDIO_GET_NOWERR, HDIO_SET_NOWERR, &ide_devset_nowerr },
+{ HDIO_GET_WCACHE, HDIO_SET_WCACHE, &ide_devset_wcache },
+{ HDIO_GET_ACOUSTIC, HDIO_SET_ACOUSTIC, &ide_devset_acoustic },
+{ 0 }
+};
+
+int ide_disk_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct block_device *bdev = inode->i_bdev;
+ struct ide_disk_obj *idkp = ide_disk_g(bdev->bd_disk);
+ ide_drive_t *drive = idkp->drive;
+ int err;
+
+ err = ide_setting_ioctl(drive, bdev, cmd, arg, ide_disk_ioctl_settings);
+ if (err != -EOPNOTSUPP)
+ return err;
+
+ return generic_ide_ioctl(drive, file, bdev, cmd, arg);
+}
diff --git a/drivers/ide/ide-disk_proc.c b/drivers/ide/ide-disk_proc.c
new file mode 100644
index 0000000..4724976
--- /dev/null
+++ b/drivers/ide/ide-disk_proc.c
@@ -0,0 +1,129 @@
+#include <linux/kernel.h>
+#include <linux/ide.h>
+#include <linux/hdreg.h>
+
+#include "ide-disk.h"
+
+static int smart_enable(ide_drive_t *drive)
+{
+ ide_task_t args;
+ struct ide_taskfile *tf = &args.tf;
+
+ memset(&args, 0, sizeof(ide_task_t));
+ tf->feature = ATA_SMART_ENABLE;
+ tf->lbam = ATA_SMART_LBAM_PASS;
+ tf->lbah = ATA_SMART_LBAH_PASS;
+ tf->command = ATA_CMD_SMART;
+ args.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
+ return ide_no_data_taskfile(drive, &args);
+}
+
+static int get_smart_data(ide_drive_t *drive, u8 *buf, u8 sub_cmd)
+{
+ ide_task_t args;
+ struct ide_taskfile *tf = &args.tf;
+
+ memset(&args, 0, sizeof(ide_task_t));
+ tf->feature = sub_cmd;
+ tf->nsect = 0x01;
+ tf->lbam = ATA_SMART_LBAM_PASS;
+ tf->lbah = ATA_SMART_LBAH_PASS;
+ tf->command = ATA_CMD_SMART;
+ args.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
+ args.data_phase = TASKFILE_IN;
+ (void) smart_enable(drive);
+ return ide_raw_taskfile(drive, &args, buf, 1);
+}
+
+static int proc_idedisk_read_cache
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_drive_t *drive = (ide_drive_t *) data;
+ char *out = page;
+ int len;
+
+ if (drive->dev_flags & IDE_DFLAG_ID_READ)
+ len = sprintf(out, "%i\n", drive->id[ATA_ID_BUF_SIZE] / 2);
+ else
+ len = sprintf(out, "(none)\n");
+
+ PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
+}
+
+static int proc_idedisk_read_capacity
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_drive_t*drive = (ide_drive_t *)data;
+ int len;
+
+ len = sprintf(page, "%llu\n", (long long)ide_disk_capacity(drive));
+
+ PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
+}
+
+static int proc_idedisk_read_smart(char *page, char **start, off_t off,
+ int count, int *eof, void *data, u8 sub_cmd)
+{
+ ide_drive_t *drive = (ide_drive_t *)data;
+ int len = 0, i = 0;
+
+ if (get_smart_data(drive, page, sub_cmd) == 0) {
+ unsigned short *val = (unsigned short *) page;
+ char *out = (char *)val + SECTOR_SIZE;
+
+ page = out;
+ do {
+ out += sprintf(out, "%04x%c", le16_to_cpu(*val),
+ (++i & 7) ? ' ' : '\n');
+ val += 1;
+ } while (i < SECTOR_SIZE / 2);
+ len = out - page;
+ }
+
+ PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
+}
+
+static int proc_idedisk_read_sv
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ return proc_idedisk_read_smart(page, start, off, count, eof, data,
+ ATA_SMART_READ_VALUES);
+}
+
+static int proc_idedisk_read_st
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ return proc_idedisk_read_smart(page, start, off, count, eof, data,
+ ATA_SMART_READ_THRESHOLDS);
+}
+
+ide_proc_entry_t ide_disk_proc[] = {
+ { "cache", S_IFREG|S_IRUGO, proc_idedisk_read_cache, NULL },
+ { "capacity", S_IFREG|S_IRUGO, proc_idedisk_read_capacity, NULL },
+ { "geometry", S_IFREG|S_IRUGO, proc_ide_read_geometry, NULL },
+ { "smart_values", S_IFREG|S_IRUSR, proc_idedisk_read_sv, NULL },
+ { "smart_thresholds", S_IFREG|S_IRUSR, proc_idedisk_read_st, NULL },
+ { NULL, 0, NULL, NULL }
+};
+
+ide_devset_rw_field(bios_cyl, bios_cyl);
+ide_devset_rw_field(bios_head, bios_head);
+ide_devset_rw_field(bios_sect, bios_sect);
+ide_devset_rw_field(failures, failures);
+ide_devset_rw_field(lun, lun);
+ide_devset_rw_field(max_failures, max_failures);
+
+const struct ide_proc_devset ide_disk_settings[] = {
+ IDE_PROC_DEVSET(acoustic, 0, 254),
+ IDE_PROC_DEVSET(address, 0, 2),
+ IDE_PROC_DEVSET(bios_cyl, 0, 65535),
+ IDE_PROC_DEVSET(bios_head, 0, 255),
+ IDE_PROC_DEVSET(bios_sect, 0, 63),
+ IDE_PROC_DEVSET(failures, 0, 65535),
+ IDE_PROC_DEVSET(lun, 0, 7),
+ IDE_PROC_DEVSET(max_failures, 0, 65535),
+ IDE_PROC_DEVSET(multcount, 0, 16),
+ IDE_PROC_DEVSET(nowerr, 0, 1),
+ IDE_PROC_DEVSET(wcache, 0, 1),
+ { 0 },
+};
diff --git a/drivers/ide/ide-dma-sff.c b/drivers/ide/ide-dma-sff.c
new file mode 100644
index 0000000..0903782
--- /dev/null
+++ b/drivers/ide/ide-dma-sff.c
@@ -0,0 +1,356 @@
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/ide.h>
+#include <linux/scatterlist.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+
+/**
+ * config_drive_for_dma - attempt to activate IDE DMA
+ * @drive: the drive to place in DMA mode
+ *
+ * If the drive supports at least mode 2 DMA or UDMA of any kind
+ * then attempt to place it into DMA mode. Drives that are known to
+ * support DMA but predate the DMA properties or that are known
+ * to have DMA handling bugs are also set up appropriately based
+ * on the good/bad drive lists.
+ */
+
+int config_drive_for_dma(ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ u16 *id = drive->id;
+
+ if (drive->media != ide_disk) {
+ if (hwif->host_flags & IDE_HFLAG_NO_ATAPI_DMA)
+ return 0;
+ }
+
+ /*
+ * Enable DMA on any drive that has
+ * UltraDMA (mode 0/1/2/3/4/5/6) enabled
+ */
+ if ((id[ATA_ID_FIELD_VALID] & 4) &&
+ ((id[ATA_ID_UDMA_MODES] >> 8) & 0x7f))
+ return 1;
+
+ /*
+ * Enable DMA on any drive that has mode2 DMA
+ * (multi or single) enabled
+ */
+ if (id[ATA_ID_FIELD_VALID] & 2) /* regular DMA */
+ if ((id[ATA_ID_MWDMA_MODES] & 0x404) == 0x404 ||
+ (id[ATA_ID_SWDMA_MODES] & 0x404) == 0x404)
+ return 1;
+
+ /* Consult the list of known "good" drives */
+ if (ide_dma_good_drive(drive))
+ return 1;
+
+ return 0;
+}
+
+/**
+ * ide_dma_host_set - Enable/disable DMA on a host
+ * @drive: drive to control
+ *
+ * Enable/disable DMA on an IDE controller following generic
+ * bus-mastering IDE controller behaviour.
+ */
+
+void ide_dma_host_set(ide_drive_t *drive, int on)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ u8 unit = drive->dn & 1;
+ u8 dma_stat = hwif->tp_ops->read_sff_dma_status(hwif);
+
+ if (on)
+ dma_stat |= (1 << (5 + unit));
+ else
+ dma_stat &= ~(1 << (5 + unit));
+
+ if (hwif->host_flags & IDE_HFLAG_MMIO)
+ writeb(dma_stat,
+ (void __iomem *)(hwif->dma_base + ATA_DMA_STATUS));
+ else
+ outb(dma_stat, hwif->dma_base + ATA_DMA_STATUS);
+}
+EXPORT_SYMBOL_GPL(ide_dma_host_set);
+
+/**
+ * ide_build_dmatable - build IDE DMA table
+ *
+ * ide_build_dmatable() prepares a dma request. We map the command
+ * to get the pci bus addresses of the buffers and then build up
+ * the PRD table that the IDE layer wants to be fed.
+ *
+ * Most chipsets correctly interpret a length of 0x0000 as 64KB,
+ * but at least one (e.g. CS5530) misinterprets it as zero (!).
+ * So we break the 64KB entry into two 32KB entries instead.
+ *
+ * Returns the number of built PRD entries if all went okay,
+ * returns 0 otherwise.
+ *
+ * May also be invoked from trm290.c
+ */
+
+int ide_build_dmatable(ide_drive_t *drive, struct request *rq)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ __le32 *table = (__le32 *)hwif->dmatable_cpu;
+ unsigned int is_trm290 = (hwif->chipset == ide_trm290) ? 1 : 0;
+ unsigned int count = 0;
+ int i;
+ struct scatterlist *sg;
+
+ hwif->sg_nents = ide_build_sglist(drive, rq);
+ if (hwif->sg_nents == 0)
+ return 0;
+
+ for_each_sg(hwif->sg_table, sg, hwif->sg_nents, i) {
+ u32 cur_addr, cur_len, xcount, bcount;
+
+ cur_addr = sg_dma_address(sg);
+ cur_len = sg_dma_len(sg);
+
+ /*
+ * Fill in the dma table, without crossing any 64kB boundaries.
+ * Most hardware requires 16-bit alignment of all blocks,
+ * but the trm290 requires 32-bit alignment.
+ */
+
+ while (cur_len) {
+ if (count++ >= PRD_ENTRIES)
+ goto use_pio_instead;
+
+ bcount = 0x10000 - (cur_addr & 0xffff);
+ if (bcount > cur_len)
+ bcount = cur_len;
+ *table++ = cpu_to_le32(cur_addr);
+ xcount = bcount & 0xffff;
+ if (is_trm290)
+ xcount = ((xcount >> 2) - 1) << 16;
+ if (xcount == 0x0000) {
+ if (count++ >= PRD_ENTRIES)
+ goto use_pio_instead;
+ *table++ = cpu_to_le32(0x8000);
+ *table++ = cpu_to_le32(cur_addr + 0x8000);
+ xcount = 0x8000;
+ }
+ *table++ = cpu_to_le32(xcount);
+ cur_addr += bcount;
+ cur_len -= bcount;
+ }
+ }
+
+ if (count) {
+ if (!is_trm290)
+ *--table |= cpu_to_le32(0x80000000);
+ return count;
+ }
+
+use_pio_instead:
+ printk(KERN_ERR "%s: %s\n", drive->name,
+ count ? "DMA table too small" : "empty DMA table?");
+
+ ide_destroy_dmatable(drive);
+
+ return 0; /* revert to PIO for this request */
+}
+EXPORT_SYMBOL_GPL(ide_build_dmatable);
+
+/**
+ * ide_dma_setup - begin a DMA phase
+ * @drive: target device
+ *
+ * Build an IDE DMA PRD (IDE speak for scatter gather table)
+ * and then set up the DMA transfer registers for a device
+ * that follows generic IDE PCI DMA behaviour. Controllers can
+ * override this function if they need to
+ *
+ * Returns 0 on success. If a PIO fallback is required then 1
+ * is returned.
+ */
+
+int ide_dma_setup(ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct request *rq = hwif->hwgroup->rq;
+ unsigned int reading;
+ u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0;
+ u8 dma_stat;
+
+ if (rq_data_dir(rq))
+ reading = 0;
+ else
+ reading = 1 << 3;
+
+ /* fall back to pio! */
+ if (!ide_build_dmatable(drive, rq)) {
+ ide_map_sg(drive, rq);
+ return 1;
+ }
+
+ /* PRD table */
+ if (hwif->host_flags & IDE_HFLAG_MMIO)
+ writel(hwif->dmatable_dma,
+ (void __iomem *)(hwif->dma_base + ATA_DMA_TABLE_OFS));
+ else
+ outl(hwif->dmatable_dma, hwif->dma_base + ATA_DMA_TABLE_OFS);
+
+ /* specify r/w */
+ if (mmio)
+ writeb(reading, (void __iomem *)(hwif->dma_base + ATA_DMA_CMD));
+ else
+ outb(reading, hwif->dma_base + ATA_DMA_CMD);
+
+ /* read DMA status for INTR & ERROR flags */
+ dma_stat = hwif->tp_ops->read_sff_dma_status(hwif);
+
+ /* clear INTR & ERROR flags */
+ if (mmio)
+ writeb(dma_stat | 6,
+ (void __iomem *)(hwif->dma_base + ATA_DMA_STATUS));
+ else
+ outb(dma_stat | 6, hwif->dma_base + ATA_DMA_STATUS);
+
+ drive->waiting_for_dma = 1;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ide_dma_setup);
+
+/**
+ * dma_timer_expiry - handle a DMA timeout
+ * @drive: Drive that timed out
+ *
+ * An IDE DMA transfer timed out. In the event of an error we ask
+ * the driver to resolve the problem, if a DMA transfer is still
+ * in progress we continue to wait (arguably we need to add a
+ * secondary 'I don't care what the drive thinks' timeout here)
+ * Finally if we have an interrupt we let it complete the I/O.
+ * But only one time - we clear expiry and if it's still not
+ * completed after WAIT_CMD, we error and retry in PIO.
+ * This can occur if an interrupt is lost or due to hang or bugs.
+ */
+
+static int dma_timer_expiry(ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ u8 dma_stat = hwif->tp_ops->read_sff_dma_status(hwif);
+
+ printk(KERN_WARNING "%s: %s: DMA status (0x%02x)\n",
+ drive->name, __func__, dma_stat);
+
+ if ((dma_stat & 0x18) == 0x18) /* BUSY Stupid Early Timer !! */
+ return WAIT_CMD;
+
+ hwif->hwgroup->expiry = NULL; /* one free ride for now */
+
+ /* 1 dmaing, 2 error, 4 intr */
+ if (dma_stat & 2) /* ERROR */
+ return -1;
+
+ if (dma_stat & 1) /* DMAing */
+ return WAIT_CMD;
+
+ if (dma_stat & 4) /* Got an Interrupt */
+ return WAIT_CMD;
+
+ return 0; /* Status is unknown -- reset the bus */
+}
+
+void ide_dma_exec_cmd(ide_drive_t *drive, u8 command)
+{
+ /* issue cmd to drive */
+ ide_execute_command(drive, command, &ide_dma_intr, 2 * WAIT_CMD,
+ dma_timer_expiry);
+}
+EXPORT_SYMBOL_GPL(ide_dma_exec_cmd);
+
+void ide_dma_start(ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ u8 dma_cmd;
+
+ /* Note that this is done *after* the cmd has
+ * been issued to the drive, as per the BM-IDE spec.
+ * The Promise Ultra33 doesn't work correctly when
+ * we do this part before issuing the drive cmd.
+ */
+ if (hwif->host_flags & IDE_HFLAG_MMIO) {
+ dma_cmd = readb((void __iomem *)(hwif->dma_base + ATA_DMA_CMD));
+ /* start DMA */
+ writeb(dma_cmd | 1,
+ (void __iomem *)(hwif->dma_base + ATA_DMA_CMD));
+ } else {
+ dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD);
+ outb(dma_cmd | 1, hwif->dma_base + ATA_DMA_CMD);
+ }
+
+ wmb();
+}
+EXPORT_SYMBOL_GPL(ide_dma_start);
+
+/* returns 1 on error, 0 otherwise */
+int ide_dma_end(ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0;
+ u8 dma_stat = 0, dma_cmd = 0;
+
+ drive->waiting_for_dma = 0;
+
+ if (mmio) {
+ /* get DMA command mode */
+ dma_cmd = readb((void __iomem *)(hwif->dma_base + ATA_DMA_CMD));
+ /* stop DMA */
+ writeb(dma_cmd & ~1,
+ (void __iomem *)(hwif->dma_base + ATA_DMA_CMD));
+ } else {
+ dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD);
+ outb(dma_cmd & ~1, hwif->dma_base + ATA_DMA_CMD);
+ }
+
+ /* get DMA status */
+ dma_stat = hwif->tp_ops->read_sff_dma_status(hwif);
+
+ if (mmio)
+ /* clear the INTR & ERROR bits */
+ writeb(dma_stat | 6,
+ (void __iomem *)(hwif->dma_base + ATA_DMA_STATUS));
+ else
+ outb(dma_stat | 6, hwif->dma_base + ATA_DMA_STATUS);
+
+ /* purge DMA mappings */
+ ide_destroy_dmatable(drive);
+ /* verify good DMA status */
+ wmb();
+ return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0;
+}
+EXPORT_SYMBOL_GPL(ide_dma_end);
+
+/* returns 1 if dma irq issued, 0 otherwise */
+int ide_dma_test_irq(ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ u8 dma_stat = hwif->tp_ops->read_sff_dma_status(hwif);
+
+ /* return 1 if INTR asserted */
+ if ((dma_stat & 4) == 4)
+ return 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ide_dma_test_irq);
+
+const struct ide_dma_ops sff_dma_ops = {
+ .dma_host_set = ide_dma_host_set,
+ .dma_setup = ide_dma_setup,
+ .dma_exec_cmd = ide_dma_exec_cmd,
+ .dma_start = ide_dma_start,
+ .dma_end = ide_dma_end,
+ .dma_test_irq = ide_dma_test_irq,
+ .dma_timeout = ide_dma_timeout,
+ .dma_lost_irq = ide_dma_lost_irq,
+};
+EXPORT_SYMBOL_GPL(sff_dma_ops);
diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c
index 3fa07c0..fffd117 100644
--- a/drivers/ide/ide-dma.c
+++ b/drivers/ide/ide-dma.c
@@ -28,24 +28,13 @@
* for supplying a Promise UDMA board & WD UDMA drive for this work!
*/
-#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/init.h>
#include <linux/ide.h>
-#include <linux/delay.h>
#include <linux/scatterlist.h>
#include <linux/dma-mapping.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-
-static const struct drive_list_entry drive_whitelist [] = {
-
+static const struct drive_list_entry drive_whitelist[] = {
{ "Micropolis 2112A" , NULL },
{ "CONNER CTMA 4000" , NULL },
{ "CONNER CTT8000-A" , NULL },
@@ -53,8 +42,7 @@
{ NULL , NULL }
};
-static const struct drive_list_entry drive_blacklist [] = {
-
+static const struct drive_list_entry drive_blacklist[] = {
{ "WDC AC11000H" , NULL },
{ "WDC AC22100H" , NULL },
{ "WDC AC32500H" , NULL },
@@ -94,11 +82,11 @@
* ide_dma_intr - IDE DMA interrupt handler
* @drive: the drive the interrupt is for
*
- * Handle an interrupt completing a read/write DMA transfer on an
+ * Handle an interrupt completing a read/write DMA transfer on an
* IDE device
*/
-
-ide_startstop_t ide_dma_intr (ide_drive_t *drive)
+
+ide_startstop_t ide_dma_intr(ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
u8 stat = 0, dma_stat = 0;
@@ -106,22 +94,21 @@
dma_stat = hwif->dma_ops->dma_end(drive);
stat = hwif->tp_ops->read_status(hwif);
- if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) {
+ if (OK_STAT(stat, DRIVE_READY, drive->bad_wstat | ATA_DRQ)) {
if (!dma_stat) {
- struct request *rq = HWGROUP(drive)->rq;
+ struct request *rq = hwif->hwgroup->rq;
task_end_request(drive, rq, stat);
return ide_stopped;
}
- printk(KERN_ERR "%s: dma_intr: bad DMA status (dma_stat=%x)\n",
- drive->name, dma_stat);
+ printk(KERN_ERR "%s: %s: bad DMA status (0x%02x)\n",
+ drive->name, __func__, dma_stat);
}
return ide_error(drive, "dma_intr", stat);
}
-
EXPORT_SYMBOL_GPL(ide_dma_intr);
-static int ide_dma_good_drive(ide_drive_t *drive)
+int ide_dma_good_drive(ide_drive_t *drive)
{
return ide_in_drive_list(drive->id, drive_whitelist);
}
@@ -139,7 +126,7 @@
int ide_build_sglist(ide_drive_t *drive, struct request *rq)
{
- ide_hwif_t *hwif = HWIF(drive);
+ ide_hwif_t *hwif = drive->hwif;
struct scatterlist *sg = hwif->sg_table;
ide_map_sg(drive, rq);
@@ -152,106 +139,8 @@
return dma_map_sg(hwif->dev, sg, hwif->sg_nents,
hwif->sg_dma_direction);
}
-
EXPORT_SYMBOL_GPL(ide_build_sglist);
-#ifdef CONFIG_BLK_DEV_IDEDMA_SFF
-/**
- * ide_build_dmatable - build IDE DMA table
- *
- * ide_build_dmatable() prepares a dma request. We map the command
- * to get the pci bus addresses of the buffers and then build up
- * the PRD table that the IDE layer wants to be fed. The code
- * knows about the 64K wrap bug in the CS5530.
- *
- * Returns the number of built PRD entries if all went okay,
- * returns 0 otherwise.
- *
- * May also be invoked from trm290.c
- */
-
-int ide_build_dmatable (ide_drive_t *drive, struct request *rq)
-{
- ide_hwif_t *hwif = HWIF(drive);
- __le32 *table = (__le32 *)hwif->dmatable_cpu;
- unsigned int is_trm290 = (hwif->chipset == ide_trm290) ? 1 : 0;
- unsigned int count = 0;
- int i;
- struct scatterlist *sg;
-
- hwif->sg_nents = i = ide_build_sglist(drive, rq);
-
- if (!i)
- return 0;
-
- sg = hwif->sg_table;
- while (i) {
- u32 cur_addr;
- u32 cur_len;
-
- cur_addr = sg_dma_address(sg);
- cur_len = sg_dma_len(sg);
-
- /*
- * Fill in the dma table, without crossing any 64kB boundaries.
- * Most hardware requires 16-bit alignment of all blocks,
- * but the trm290 requires 32-bit alignment.
- */
-
- while (cur_len) {
- if (count++ >= PRD_ENTRIES) {
- printk(KERN_ERR "%s: DMA table too small\n", drive->name);
- goto use_pio_instead;
- } else {
- u32 xcount, bcount = 0x10000 - (cur_addr & 0xffff);
-
- if (bcount > cur_len)
- bcount = cur_len;
- *table++ = cpu_to_le32(cur_addr);
- xcount = bcount & 0xffff;
- if (is_trm290)
- xcount = ((xcount >> 2) - 1) << 16;
- else if (xcount == 0x0000) {
- /*
- * Most chipsets correctly interpret a length of 0x0000 as 64KB,
- * but at least one (e.g. CS5530) misinterprets it as zero (!).
- * So here we break the 64KB entry into two 32KB entries instead.
- */
- if (count++ >= PRD_ENTRIES) {
- printk(KERN_ERR "%s: DMA table too small\n", drive->name);
- goto use_pio_instead;
- }
- *table++ = cpu_to_le32(0x8000);
- *table++ = cpu_to_le32(cur_addr + 0x8000);
- xcount = 0x8000;
- }
- *table++ = cpu_to_le32(xcount);
- cur_addr += bcount;
- cur_len -= bcount;
- }
- }
-
- sg = sg_next(sg);
- i--;
- }
-
- if (count) {
- if (!is_trm290)
- *--table |= cpu_to_le32(0x80000000);
- return count;
- }
-
- printk(KERN_ERR "%s: empty DMA table?\n", drive->name);
-
-use_pio_instead:
- ide_destroy_dmatable(drive);
-
- return 0; /* revert to PIO for this request */
-}
-
-EXPORT_SYMBOL_GPL(ide_build_dmatable);
-#endif
-
/**
* ide_destroy_dmatable - clean up DMA mapping
* @drive: The drive to unmap
@@ -262,146 +151,30 @@
* an oops as only one mapping can be live for each target at a given
* time.
*/
-
-void ide_destroy_dmatable (ide_drive_t *drive)
+
+void ide_destroy_dmatable(ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
dma_unmap_sg(hwif->dev, hwif->sg_table, hwif->sg_nents,
hwif->sg_dma_direction);
}
-
EXPORT_SYMBOL_GPL(ide_destroy_dmatable);
-#ifdef CONFIG_BLK_DEV_IDEDMA_SFF
-/**
- * config_drive_for_dma - attempt to activate IDE DMA
- * @drive: the drive to place in DMA mode
- *
- * If the drive supports at least mode 2 DMA or UDMA of any kind
- * then attempt to place it into DMA mode. Drives that are known to
- * support DMA but predate the DMA properties or that are known
- * to have DMA handling bugs are also set up appropriately based
- * on the good/bad drive lists.
- */
-
-static int config_drive_for_dma (ide_drive_t *drive)
-{
- ide_hwif_t *hwif = drive->hwif;
- struct hd_driveid *id = drive->id;
-
- if (drive->media != ide_disk) {
- if (hwif->host_flags & IDE_HFLAG_NO_ATAPI_DMA)
- return 0;
- }
-
- /*
- * Enable DMA on any drive that has
- * UltraDMA (mode 0/1/2/3/4/5/6) enabled
- */
- if ((id->field_valid & 4) && ((id->dma_ultra >> 8) & 0x7f))
- return 1;
-
- /*
- * Enable DMA on any drive that has mode2 DMA
- * (multi or single) enabled
- */
- if (id->field_valid & 2) /* regular DMA */
- if ((id->dma_mword & 0x404) == 0x404 ||
- (id->dma_1word & 0x404) == 0x404)
- return 1;
-
- /* Consult the list of known "good" drives */
- if (ide_dma_good_drive(drive))
- return 1;
-
- return 0;
-}
-
-/**
- * dma_timer_expiry - handle a DMA timeout
- * @drive: Drive that timed out
- *
- * An IDE DMA transfer timed out. In the event of an error we ask
- * the driver to resolve the problem, if a DMA transfer is still
- * in progress we continue to wait (arguably we need to add a
- * secondary 'I don't care what the drive thinks' timeout here)
- * Finally if we have an interrupt we let it complete the I/O.
- * But only one time - we clear expiry and if it's still not
- * completed after WAIT_CMD, we error and retry in PIO.
- * This can occur if an interrupt is lost or due to hang or bugs.
- */
-
-static int dma_timer_expiry (ide_drive_t *drive)
-{
- ide_hwif_t *hwif = HWIF(drive);
- u8 dma_stat = hwif->tp_ops->read_sff_dma_status(hwif);
-
- printk(KERN_WARNING "%s: dma_timer_expiry: dma status == 0x%02x\n",
- drive->name, dma_stat);
-
- if ((dma_stat & 0x18) == 0x18) /* BUSY Stupid Early Timer !! */
- return WAIT_CMD;
-
- HWGROUP(drive)->expiry = NULL; /* one free ride for now */
-
- /* 1 dmaing, 2 error, 4 intr */
- if (dma_stat & 2) /* ERROR */
- return -1;
-
- if (dma_stat & 1) /* DMAing */
- return WAIT_CMD;
-
- if (dma_stat & 4) /* Got an Interrupt */
- return WAIT_CMD;
-
- return 0; /* Status is unknown -- reset the bus */
-}
-
-/**
- * ide_dma_host_set - Enable/disable DMA on a host
- * @drive: drive to control
- *
- * Enable/disable DMA on an IDE controller following generic
- * bus-mastering IDE controller behaviour.
- */
-
-void ide_dma_host_set(ide_drive_t *drive, int on)
-{
- ide_hwif_t *hwif = HWIF(drive);
- u8 unit = (drive->select.b.unit & 0x01);
- u8 dma_stat = hwif->tp_ops->read_sff_dma_status(hwif);
-
- if (on)
- dma_stat |= (1 << (5 + unit));
- else
- dma_stat &= ~(1 << (5 + unit));
-
- if (hwif->host_flags & IDE_HFLAG_MMIO)
- writeb(dma_stat,
- (void __iomem *)(hwif->dma_base + ATA_DMA_STATUS));
- else
- outb(dma_stat, hwif->dma_base + ATA_DMA_STATUS);
-}
-
-EXPORT_SYMBOL_GPL(ide_dma_host_set);
-#endif /* CONFIG_BLK_DEV_IDEDMA_SFF */
-
/**
* ide_dma_off_quietly - Generic DMA kill
* @drive: drive to control
*
- * Turn off the current DMA on this IDE controller.
+ * Turn off the current DMA on this IDE controller.
*/
void ide_dma_off_quietly(ide_drive_t *drive)
{
- drive->using_dma = 0;
+ drive->dev_flags &= ~IDE_DFLAG_USING_DMA;
ide_toggle_bounce(drive, 0);
drive->hwif->dma_ops->dma_host_set(drive, 0);
}
-
EXPORT_SYMBOL(ide_dma_off_quietly);
/**
@@ -417,7 +190,6 @@
printk(KERN_INFO "%s: DMA disabled\n", drive->name);
ide_dma_off_quietly(drive);
}
-
EXPORT_SYMBOL(ide_dma_off);
/**
@@ -429,179 +201,24 @@
void ide_dma_on(ide_drive_t *drive)
{
- drive->using_dma = 1;
+ drive->dev_flags |= IDE_DFLAG_USING_DMA;
ide_toggle_bounce(drive, 1);
drive->hwif->dma_ops->dma_host_set(drive, 1);
}
-#ifdef CONFIG_BLK_DEV_IDEDMA_SFF
-/**
- * ide_dma_setup - begin a DMA phase
- * @drive: target device
- *
- * Build an IDE DMA PRD (IDE speak for scatter gather table)
- * and then set up the DMA transfer registers for a device
- * that follows generic IDE PCI DMA behaviour. Controllers can
- * override this function if they need to
- *
- * Returns 0 on success. If a PIO fallback is required then 1
- * is returned.
- */
-
-int ide_dma_setup(ide_drive_t *drive)
+int __ide_dma_bad_drive(ide_drive_t *drive)
{
- ide_hwif_t *hwif = drive->hwif;
- struct request *rq = HWGROUP(drive)->rq;
- unsigned int reading;
- u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0;
- u8 dma_stat;
-
- if (rq_data_dir(rq))
- reading = 0;
- else
- reading = 1 << 3;
-
- /* fall back to pio! */
- if (!ide_build_dmatable(drive, rq)) {
- ide_map_sg(drive, rq);
- return 1;
- }
-
- /* PRD table */
- if (hwif->host_flags & IDE_HFLAG_MMIO)
- writel(hwif->dmatable_dma,
- (void __iomem *)(hwif->dma_base + ATA_DMA_TABLE_OFS));
- else
- outl(hwif->dmatable_dma, hwif->dma_base + ATA_DMA_TABLE_OFS);
-
- /* specify r/w */
- if (mmio)
- writeb(reading, (void __iomem *)(hwif->dma_base + ATA_DMA_CMD));
- else
- outb(reading, hwif->dma_base + ATA_DMA_CMD);
-
- /* read DMA status for INTR & ERROR flags */
- dma_stat = hwif->tp_ops->read_sff_dma_status(hwif);
-
- /* clear INTR & ERROR flags */
- if (mmio)
- writeb(dma_stat | 6,
- (void __iomem *)(hwif->dma_base + ATA_DMA_STATUS));
- else
- outb(dma_stat | 6, hwif->dma_base + ATA_DMA_STATUS);
-
- drive->waiting_for_dma = 1;
- return 0;
-}
-
-EXPORT_SYMBOL_GPL(ide_dma_setup);
-
-void ide_dma_exec_cmd(ide_drive_t *drive, u8 command)
-{
- /* issue cmd to drive */
- ide_execute_command(drive, command, &ide_dma_intr, 2*WAIT_CMD, dma_timer_expiry);
-}
-EXPORT_SYMBOL_GPL(ide_dma_exec_cmd);
-
-void ide_dma_start(ide_drive_t *drive)
-{
- ide_hwif_t *hwif = drive->hwif;
- u8 dma_cmd;
-
- /* Note that this is done *after* the cmd has
- * been issued to the drive, as per the BM-IDE spec.
- * The Promise Ultra33 doesn't work correctly when
- * we do this part before issuing the drive cmd.
- */
- if (hwif->host_flags & IDE_HFLAG_MMIO) {
- dma_cmd = readb((void __iomem *)(hwif->dma_base + ATA_DMA_CMD));
- /* start DMA */
- writeb(dma_cmd | 1,
- (void __iomem *)(hwif->dma_base + ATA_DMA_CMD));
- } else {
- dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD);
- outb(dma_cmd | 1, hwif->dma_base + ATA_DMA_CMD);
- }
-
- hwif->dma = 1;
- wmb();
-}
-
-EXPORT_SYMBOL_GPL(ide_dma_start);
-
-/* returns 1 on error, 0 otherwise */
-int __ide_dma_end (ide_drive_t *drive)
-{
- ide_hwif_t *hwif = drive->hwif;
- u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0;
- u8 dma_stat = 0, dma_cmd = 0;
-
- drive->waiting_for_dma = 0;
-
- if (mmio) {
- /* get DMA command mode */
- dma_cmd = readb((void __iomem *)(hwif->dma_base + ATA_DMA_CMD));
- /* stop DMA */
- writeb(dma_cmd & ~1,
- (void __iomem *)(hwif->dma_base + ATA_DMA_CMD));
- } else {
- dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD);
- outb(dma_cmd & ~1, hwif->dma_base + ATA_DMA_CMD);
- }
-
- /* get DMA status */
- dma_stat = hwif->tp_ops->read_sff_dma_status(hwif);
-
- if (mmio)
- /* clear the INTR & ERROR bits */
- writeb(dma_stat | 6,
- (void __iomem *)(hwif->dma_base + ATA_DMA_STATUS));
- else
- outb(dma_stat | 6, hwif->dma_base + ATA_DMA_STATUS);
-
- /* purge DMA mappings */
- ide_destroy_dmatable(drive);
- /* verify good DMA status */
- hwif->dma = 0;
- wmb();
- return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0;
-}
-
-EXPORT_SYMBOL(__ide_dma_end);
-
-/* returns 1 if dma irq issued, 0 otherwise */
-int ide_dma_test_irq(ide_drive_t *drive)
-{
- ide_hwif_t *hwif = HWIF(drive);
- u8 dma_stat = hwif->tp_ops->read_sff_dma_status(hwif);
-
- /* return 1 if INTR asserted */
- if ((dma_stat & 4) == 4)
- return 1;
- if (!drive->waiting_for_dma)
- printk(KERN_WARNING "%s: (%s) called while not waiting\n",
- drive->name, __func__);
- return 0;
-}
-EXPORT_SYMBOL_GPL(ide_dma_test_irq);
-#else
-static inline int config_drive_for_dma(ide_drive_t *drive) { return 0; }
-#endif /* CONFIG_BLK_DEV_IDEDMA_SFF */
-
-int __ide_dma_bad_drive (ide_drive_t *drive)
-{
- struct hd_driveid *id = drive->id;
+ u16 *id = drive->id;
int blacklist = ide_in_drive_list(id, drive_blacklist);
if (blacklist) {
printk(KERN_WARNING "%s: Disabling (U)DMA for %s (blacklisted)\n",
- drive->name, id->model);
+ drive->name, (char *)&id[ATA_ID_PROD]);
return blacklist;
}
return 0;
}
-
EXPORT_SYMBOL(__ide_dma_bad_drive);
static const u8 xfer_mode_bases[] = {
@@ -612,21 +229,21 @@
static unsigned int ide_get_mode_mask(ide_drive_t *drive, u8 base, u8 req_mode)
{
- struct hd_driveid *id = drive->id;
+ u16 *id = drive->id;
ide_hwif_t *hwif = drive->hwif;
const struct ide_port_ops *port_ops = hwif->port_ops;
unsigned int mask = 0;
- switch(base) {
+ switch (base) {
case XFER_UDMA_0:
- if ((id->field_valid & 4) == 0)
+ if ((id[ATA_ID_FIELD_VALID] & 4) == 0)
break;
if (port_ops && port_ops->udma_filter)
mask = port_ops->udma_filter(drive);
else
mask = hwif->ultra_mask;
- mask &= id->dma_ultra;
+ mask &= id[ATA_ID_UDMA_MODES];
/*
* avoid false cable warning from eighty_ninty_three()
@@ -637,19 +254,19 @@
}
break;
case XFER_MW_DMA_0:
- if ((id->field_valid & 2) == 0)
+ if ((id[ATA_ID_FIELD_VALID] & 2) == 0)
break;
if (port_ops && port_ops->mdma_filter)
mask = port_ops->mdma_filter(drive);
else
mask = hwif->mwdma_mask;
- mask &= id->dma_mword;
+ mask &= id[ATA_ID_MWDMA_MODES];
break;
case XFER_SW_DMA_0:
- if (id->field_valid & 2) {
- mask = id->dma_1word & hwif->swdma_mask;
- } else if (id->tDMA) {
- u8 mode = id->tDMA;
+ if (id[ATA_ID_FIELD_VALID] & 2) {
+ mask = id[ATA_ID_SWDMA_MODES] & hwif->swdma_mask;
+ } else if (id[ATA_ID_OLD_DMA_MODES] >> 8) {
+ u8 mode = id[ATA_ID_OLD_DMA_MODES] >> 8;
/*
* if the mode is valid convert it to the mask
@@ -706,7 +323,8 @@
/*
* is this correct?
*/
- if (ide_dma_good_drive(drive) && drive->id->eide_dma_time < 150)
+ if (ide_dma_good_drive(drive) &&
+ drive->id[ATA_ID_EIDE_DMA_TIME] < 150)
mode = XFER_MW_DMA_1;
}
@@ -717,7 +335,6 @@
return mode;
}
-
EXPORT_SYMBOL_GPL(ide_find_dma_mode);
static int ide_tune_dma(ide_drive_t *drive)
@@ -725,7 +342,8 @@
ide_hwif_t *hwif = drive->hwif;
u8 speed;
- if (drive->nodma || (drive->id->capability & 1) == 0)
+ if (ata_id_has_dma(drive->id) == 0 ||
+ (drive->dev_flags & IDE_DFLAG_NODMA))
return 0;
/* consult the list of known "bad" drives */
@@ -767,13 +385,15 @@
int ide_id_dma_bug(ide_drive_t *drive)
{
- struct hd_driveid *id = drive->id;
+ u16 *id = drive->id;
- if (id->field_valid & 4) {
- if ((id->dma_ultra >> 8) && (id->dma_mword >> 8))
+ if (id[ATA_ID_FIELD_VALID] & 4) {
+ if ((id[ATA_ID_UDMA_MODES] >> 8) &&
+ (id[ATA_ID_MWDMA_MODES] >> 8))
goto err_out;
- } else if (id->field_valid & 2) {
- if ((id->dma_mword >> 8) && (id->dma_1word >> 8))
+ } else if (id[ATA_ID_FIELD_VALID] & 2) {
+ if ((id[ATA_ID_MWDMA_MODES] >> 8) &&
+ (id[ATA_ID_SWDMA_MODES] >> 8))
goto err_out;
}
return 0;
@@ -823,66 +443,59 @@
ide_dma_on(drive);
}
-#ifdef CONFIG_BLK_DEV_IDEDMA_SFF
-void ide_dma_lost_irq (ide_drive_t *drive)
+void ide_dma_lost_irq(ide_drive_t *drive)
{
- printk("%s: DMA interrupt recovery\n", drive->name);
+ printk(KERN_ERR "%s: DMA interrupt recovery\n", drive->name);
}
+EXPORT_SYMBOL_GPL(ide_dma_lost_irq);
-EXPORT_SYMBOL(ide_dma_lost_irq);
-
-void ide_dma_timeout (ide_drive_t *drive)
+void ide_dma_timeout(ide_drive_t *drive)
{
- ide_hwif_t *hwif = HWIF(drive);
+ ide_hwif_t *hwif = drive->hwif;
printk(KERN_ERR "%s: timeout waiting for DMA\n", drive->name);
if (hwif->dma_ops->dma_test_irq(drive))
return;
+ ide_dump_status(drive, "DMA timeout", hwif->tp_ops->read_status(hwif));
+
hwif->dma_ops->dma_end(drive);
}
-
-EXPORT_SYMBOL(ide_dma_timeout);
+EXPORT_SYMBOL_GPL(ide_dma_timeout);
void ide_release_dma_engine(ide_hwif_t *hwif)
{
if (hwif->dmatable_cpu) {
- struct pci_dev *pdev = to_pci_dev(hwif->dev);
+ int prd_size = hwif->prd_max_nents * hwif->prd_ent_size;
- pci_free_consistent(pdev, PRD_ENTRIES * PRD_BYTES,
- hwif->dmatable_cpu, hwif->dmatable_dma);
+ dma_free_coherent(hwif->dev, prd_size,
+ hwif->dmatable_cpu, hwif->dmatable_dma);
hwif->dmatable_cpu = NULL;
}
}
+EXPORT_SYMBOL_GPL(ide_release_dma_engine);
int ide_allocate_dma_engine(ide_hwif_t *hwif)
{
- struct pci_dev *pdev = to_pci_dev(hwif->dev);
+ int prd_size;
- hwif->dmatable_cpu = pci_alloc_consistent(pdev,
- PRD_ENTRIES * PRD_BYTES,
- &hwif->dmatable_dma);
+ if (hwif->prd_max_nents == 0)
+ hwif->prd_max_nents = PRD_ENTRIES;
+ if (hwif->prd_ent_size == 0)
+ hwif->prd_ent_size = PRD_BYTES;
- if (hwif->dmatable_cpu)
- return 0;
+ prd_size = hwif->prd_max_nents * hwif->prd_ent_size;
- printk(KERN_ERR "%s: -- Error, unable to allocate DMA table.\n",
+ hwif->dmatable_cpu = dma_alloc_coherent(hwif->dev, prd_size,
+ &hwif->dmatable_dma,
+ GFP_ATOMIC);
+ if (hwif->dmatable_cpu == NULL) {
+ printk(KERN_ERR "%s: unable to allocate PRD table\n",
hwif->name);
+ return -ENOMEM;
+ }
- return 1;
+ return 0;
}
EXPORT_SYMBOL_GPL(ide_allocate_dma_engine);
-
-const struct ide_dma_ops sff_dma_ops = {
- .dma_host_set = ide_dma_host_set,
- .dma_setup = ide_dma_setup,
- .dma_exec_cmd = ide_dma_exec_cmd,
- .dma_start = ide_dma_start,
- .dma_end = __ide_dma_end,
- .dma_test_irq = ide_dma_test_irq,
- .dma_timeout = ide_dma_timeout,
- .dma_lost_irq = ide_dma_lost_irq,
-};
-EXPORT_SYMBOL_GPL(sff_dma_ops);
-#endif /* CONFIG_BLK_DEV_IDEDMA_SFF */
diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c
index e9034c0..cf0aa25 100644
--- a/drivers/ide/ide-floppy.c
+++ b/drivers/ide/ide-floppy.c
@@ -15,6 +15,9 @@
* Documentation/ide/ChangeLog.ide-floppy.1996-2002
*/
+#define DRV_NAME "ide-floppy"
+#define PFX DRV_NAME ": "
+
#define IDEFLOPPY_VERSION "1.00"
#include <linux/module.h>
@@ -31,8 +34,10 @@
#include <linux/slab.h>
#include <linux/cdrom.h>
#include <linux/ide.h>
+#include <linux/hdreg.h>
#include <linux/bitops.h>
#include <linux/mutex.h>
+#include <linux/scatterlist.h>
#include <scsi/scsi_ioctl.h>
@@ -42,42 +47,27 @@
#include <linux/io.h>
#include <asm/unaligned.h>
-/* define to see debug info */
-#define IDEFLOPPY_DEBUG_LOG 0
+#include "ide-floppy.h"
-/* #define IDEFLOPPY_DEBUG(fmt, args...) printk(KERN_INFO fmt, ## args) */
-#define IDEFLOPPY_DEBUG(fmt, args...)
+/* module parameters */
+static unsigned long debug_mask;
+module_param(debug_mask, ulong, 0644);
+
+/* define to see debug info */
+#define IDEFLOPPY_DEBUG_LOG 0
#if IDEFLOPPY_DEBUG_LOG
-#define debug_log(fmt, args...) \
- printk(KERN_INFO "ide-floppy: " fmt, ## args)
+#define ide_debug_log(lvl, fmt, args...) __ide_debug_log(lvl, fmt, args)
#else
-#define debug_log(fmt, args...) do {} while (0)
+#define ide_debug_log(lvl, fmt, args...) do {} while (0)
#endif
-
-/* Some drives require a longer irq timeout. */
-#define IDEFLOPPY_WAIT_CMD (5 * WAIT_CMD)
-
/*
* After each failed packet command we issue a request sense command and retry
* the packet command IDEFLOPPY_MAX_PC_RETRIES times.
*/
#define IDEFLOPPY_MAX_PC_RETRIES 3
-/*
- * With each packet command, we allocate a buffer of IDEFLOPPY_PC_BUFFER_SIZE
- * bytes.
- */
-#define IDEFLOPPY_PC_BUFFER_SIZE 256
-
-/*
- * In various places in the driver, we need to allocate storage for packet
- * commands and requests, which will remain valid while we leave the driver to
- * wait for an interrupt or a timeout event.
- */
-#define IDEFLOPPY_PC_STACK (10 + IDEFLOPPY_MAX_PC_RETRIES)
-
/* format capacities descriptor codes */
#define CAPACITY_INVALID 0x00
#define CAPACITY_UNFORMATTED 0x01
@@ -85,79 +75,16 @@
#define CAPACITY_NO_CARTRIDGE 0x03
/*
- * Most of our global data which we need to save even as we leave the driver
- * due to an interrupt or a timer event is stored in a variable of type
- * idefloppy_floppy_t, defined below.
+ * The following delay solves a problem with ATAPI Zip 100 drive where BSY bit
+ * was apparently being deasserted before the unit was ready to receive data.
*/
-typedef struct ide_floppy_obj {
- ide_drive_t *drive;
- ide_driver_t *driver;
- struct gendisk *disk;
- struct kref kref;
- unsigned int openers; /* protected by BKL for now */
-
- /* Current packet command */
- struct ide_atapi_pc *pc;
- /* Last failed packet command */
- struct ide_atapi_pc *failed_pc;
- /* Packet command stack */
- struct ide_atapi_pc pc_stack[IDEFLOPPY_PC_STACK];
- /* Next free packet command storage space */
- int pc_stack_index;
- struct request rq_stack[IDEFLOPPY_PC_STACK];
- /* We implement a circular array */
- int rq_stack_index;
-
- /* Last error information */
- u8 sense_key, asc, ascq;
- /* delay this long before sending packet command */
- u8 ticks;
- int progress_indication;
-
- /* Device information */
- /* Current format */
- int blocks, block_size, bs_factor;
- /* Last format capacity descriptor */
- u8 cap_desc[8];
- /* Copy of the flexible disk page */
- u8 flexible_disk_page[32];
- /* Write protect */
- int wp;
- /* Supports format progress report */
- int srfp;
-} idefloppy_floppy_t;
-
-#define IDEFLOPPY_TICKS_DELAY HZ/20 /* default delay for ZIP 100 (50ms) */
-
-/* Defines for the MODE SENSE command */
-#define MODE_SENSE_CURRENT 0x00
-#define MODE_SENSE_CHANGEABLE 0x01
-#define MODE_SENSE_DEFAULT 0x02
-#define MODE_SENSE_SAVED 0x03
-
-/* IOCTLs used in low-level formatting. */
-#define IDEFLOPPY_IOCTL_FORMAT_SUPPORTED 0x4600
-#define IDEFLOPPY_IOCTL_FORMAT_GET_CAPACITY 0x4601
-#define IDEFLOPPY_IOCTL_FORMAT_START 0x4602
-#define IDEFLOPPY_IOCTL_FORMAT_GET_PROGRESS 0x4603
+#define IDEFLOPPY_PC_DELAY (HZ/20) /* default delay for ZIP 100 (50ms) */
/* Error code returned in rq->errors to the higher part of the driver. */
#define IDEFLOPPY_ERROR_GENERAL 101
-/*
- * Pages of the SELECT SENSE / MODE SENSE packet commands.
- * See SFF-8070i spec.
- */
-#define IDEFLOPPY_CAPABILITIES_PAGE 0x1b
-#define IDEFLOPPY_FLEXIBLE_DISK_PAGE 0x05
-
static DEFINE_MUTEX(idefloppy_ref_mutex);
-#define to_ide_floppy(obj) container_of(obj, struct ide_floppy_obj, kref)
-
-#define ide_floppy_g(disk) \
- container_of((disk)->private_data, struct ide_floppy_obj, driver)
-
static void idefloppy_cleanup_obj(struct kref *);
static struct ide_floppy_obj *ide_floppy_get(struct gendisk *disk)
@@ -165,7 +92,7 @@
struct ide_floppy_obj *floppy = NULL;
mutex_lock(&idefloppy_ref_mutex);
- floppy = ide_floppy_g(disk);
+ floppy = ide_drv_g(disk, ide_floppy_obj);
if (floppy) {
if (ide_device_get(floppy->drive))
floppy = NULL;
@@ -196,13 +123,21 @@
struct request *rq = HWGROUP(drive)->rq;
int error;
- debug_log("Reached %s\n", __func__);
+ ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__);
switch (uptodate) {
- case 0: error = IDEFLOPPY_ERROR_GENERAL; break;
- case 1: error = 0; break;
- default: error = uptodate;
+ case 0:
+ error = IDEFLOPPY_ERROR_GENERAL;
+ break;
+
+ case 1:
+ error = 0;
+ break;
+
+ default:
+ error = uptodate;
}
+
if (error)
floppy->failed_pc = NULL;
/* Why does this happen? */
@@ -219,44 +154,6 @@
return 0;
}
-static void ide_floppy_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc,
- unsigned int bcount, int direction)
-{
- ide_hwif_t *hwif = drive->hwif;
- struct request *rq = pc->rq;
- struct req_iterator iter;
- struct bio_vec *bvec;
- unsigned long flags;
- int count, done = 0;
- char *data;
-
- rq_for_each_segment(bvec, rq, iter) {
- if (!bcount)
- break;
-
- count = min(bvec->bv_len, bcount);
-
- data = bvec_kmap_irq(bvec, &flags);
- if (direction)
- hwif->tp_ops->output_data(drive, NULL, data, count);
- else
- hwif->tp_ops->input_data(drive, NULL, data, count);
- bvec_kunmap_irq(data, &flags);
-
- bcount -= count;
- pc->b_count += count;
- done += count;
- }
-
- idefloppy_end_request(drive, 1, done >> 9);
-
- if (bcount) {
- printk(KERN_ERR "%s: leftover data in %s, bcount == %d\n",
- drive->name, __func__, bcount);
- ide_pad_transfer(drive, direction, bcount);
- }
-}
-
static void idefloppy_update_buffers(ide_drive_t *drive,
struct ide_atapi_pc *pc)
{
@@ -267,50 +164,13 @@
idefloppy_end_request(drive, 1, 0);
}
-/*
- * Generate a new packet command request in front of the request queue, before
- * the current request so that it will be processed immediately, on the next
- * pass through the driver.
- */
-static void idefloppy_queue_pc_head(ide_drive_t *drive, struct ide_atapi_pc *pc,
- struct request *rq)
-{
- struct ide_floppy_obj *floppy = drive->driver_data;
-
- blk_rq_init(NULL, rq);
- rq->buffer = (char *) pc;
- rq->cmd_type = REQ_TYPE_SPECIAL;
- rq->cmd_flags |= REQ_PREEMPT;
- rq->rq_disk = floppy->disk;
- memcpy(rq->cmd, pc->c, 12);
- ide_do_drive_cmd(drive, rq);
-}
-
-static struct ide_atapi_pc *idefloppy_next_pc_storage(ide_drive_t *drive)
+static void ide_floppy_callback(ide_drive_t *drive, int dsc)
{
idefloppy_floppy_t *floppy = drive->driver_data;
-
- if (floppy->pc_stack_index == IDEFLOPPY_PC_STACK)
- floppy->pc_stack_index = 0;
- return (&floppy->pc_stack[floppy->pc_stack_index++]);
-}
-
-static struct request *idefloppy_next_rq_storage(ide_drive_t *drive)
-{
- idefloppy_floppy_t *floppy = drive->driver_data;
-
- if (floppy->rq_stack_index == IDEFLOPPY_PC_STACK)
- floppy->rq_stack_index = 0;
- return (&floppy->rq_stack[floppy->rq_stack_index++]);
-}
-
-static void ide_floppy_callback(ide_drive_t *drive)
-{
- idefloppy_floppy_t *floppy = drive->driver_data;
- struct ide_atapi_pc *pc = floppy->pc;
+ struct ide_atapi_pc *pc = drive->pc;
int uptodate = pc->error ? 0 : 1;
- debug_log("Reached %s\n", __func__);
+ ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__);
if (floppy->failed_pc == pc)
floppy->failed_pc = NULL;
@@ -319,7 +179,7 @@
(pc->rq && blk_pc_request(pc->rq)))
uptodate = 1; /* FIXME */
else if (pc->c[0] == GPCMD_REQUEST_SENSE) {
- u8 *buf = floppy->pc->buf;
+ u8 *buf = pc->buf;
if (!pc->error) {
floppy->sense_key = buf[2] & 0x0F;
@@ -329,108 +189,20 @@
(u16)get_unaligned((u16 *)&buf[16]) : 0x10000;
if (floppy->failed_pc)
- debug_log("pc = %x, ", floppy->failed_pc->c[0]);
+ ide_debug_log(IDE_DBG_PC, "pc = %x, ",
+ floppy->failed_pc->c[0]);
- debug_log("sense key = %x, asc = %x, ascq = %x\n",
- floppy->sense_key, floppy->asc, floppy->ascq);
+ ide_debug_log(IDE_DBG_SENSE, "sense key = %x, asc = %x,"
+ "ascq = %x\n", floppy->sense_key,
+ floppy->asc, floppy->ascq);
} else
- printk(KERN_ERR "Error in REQUEST SENSE itself - "
- "Aborting request!\n");
+ printk(KERN_ERR PFX "Error in REQUEST SENSE itself - "
+ "Aborting request!\n");
}
idefloppy_end_request(drive, uptodate, 0);
}
-static void idefloppy_init_pc(struct ide_atapi_pc *pc)
-{
- memset(pc, 0, sizeof(*pc));
- pc->buf = pc->pc_buf;
- pc->buf_size = IDEFLOPPY_PC_BUFFER_SIZE;
-}
-
-static void idefloppy_create_request_sense_cmd(struct ide_atapi_pc *pc)
-{
- idefloppy_init_pc(pc);
- pc->c[0] = GPCMD_REQUEST_SENSE;
- pc->c[4] = 255;
- pc->req_xfer = 18;
-}
-
-/*
- * Called when an error was detected during the last packet command. We queue a
- * request sense packet command in the head of the request list.
- */
-static void idefloppy_retry_pc(ide_drive_t *drive)
-{
- struct ide_atapi_pc *pc;
- struct request *rq;
-
- (void)ide_read_error(drive);
- pc = idefloppy_next_pc_storage(drive);
- rq = idefloppy_next_rq_storage(drive);
- idefloppy_create_request_sense_cmd(pc);
- idefloppy_queue_pc_head(drive, pc, rq);
-}
-
-/* The usual interrupt handler called during a packet command. */
-static ide_startstop_t idefloppy_pc_intr(ide_drive_t *drive)
-{
- idefloppy_floppy_t *floppy = drive->driver_data;
-
- return ide_pc_intr(drive, floppy->pc, idefloppy_pc_intr,
- IDEFLOPPY_WAIT_CMD, NULL, idefloppy_update_buffers,
- idefloppy_retry_pc, NULL, ide_floppy_io_buffers);
-}
-
-/*
- * What we have here is a classic case of a top half / bottom half interrupt
- * service routine. In interrupt mode, the device sends an interrupt to signal
- * that it is ready to receive a packet. However, we need to delay about 2-3
- * ticks before issuing the packet or we gets in trouble.
- */
-static int idefloppy_transfer_pc(ide_drive_t *drive)
-{
- idefloppy_floppy_t *floppy = drive->driver_data;
-
- /* Send the actual packet */
- drive->hwif->tp_ops->output_data(drive, NULL, floppy->pc->c, 12);
-
- /* Timeout for the packet command */
- return IDEFLOPPY_WAIT_CMD;
-}
-
-
-/*
- * Called as an interrupt (or directly). When the device says it's ready for a
- * packet, we schedule the packet transfer to occur about 2-3 ticks later in
- * transfer_pc.
- */
-static ide_startstop_t idefloppy_start_pc_transfer(ide_drive_t *drive)
-{
- idefloppy_floppy_t *floppy = drive->driver_data;
- struct ide_atapi_pc *pc = floppy->pc;
- ide_expiry_t *expiry;
- unsigned int timeout;
-
- /*
- * The following delay solves a problem with ATAPI Zip 100 drives
- * where the Busy flag was apparently being deasserted before the
- * unit was ready to receive data. This was happening on a
- * 1200 MHz Athlon system. 10/26/01 25msec is too short,
- * 40 and 50msec work well. idefloppy_pc_intr will not be actually
- * used until after the packet is moved in about 50 msec.
- */
- if (drive->atapi_flags & IDE_AFLAG_ZIP_DRIVE) {
- timeout = floppy->ticks;
- expiry = &idefloppy_transfer_pc;
- } else {
- timeout = IDEFLOPPY_WAIT_CMD;
- expiry = NULL;
- }
-
- return ide_transfer_pc(drive, pc, idefloppy_pc_intr, timeout, expiry);
-}
-
static void ide_floppy_report_error(idefloppy_floppy_t *floppy,
struct ide_atapi_pc *pc)
{
@@ -440,7 +212,7 @@
floppy->ascq == 0x00)
return;
- printk(KERN_ERR "ide-floppy: %s: I/O error, pc = %2x, key = %2x, "
+ printk(KERN_ERR PFX "%s: I/O error, pc = %2x, key = %2x, "
"asc = %2x, ascq = %2x\n",
floppy->drive->name, pc->c[0], floppy->sense_key,
floppy->asc, floppy->ascq);
@@ -455,8 +227,9 @@
if (floppy->failed_pc == NULL &&
pc->c[0] != GPCMD_REQUEST_SENSE)
floppy->failed_pc = pc;
+
/* Set the current packet command */
- floppy->pc = pc;
+ drive->pc = pc;
if (pc->retries > IDEFLOPPY_MAX_PC_RETRIES) {
if (!(pc->flags & PC_FLAG_SUPPRESS_ERROR))
@@ -465,67 +238,35 @@
pc->error = IDEFLOPPY_ERROR_GENERAL;
floppy->failed_pc = NULL;
- drive->pc_callback(drive);
+ drive->pc_callback(drive, 0);
return ide_stopped;
}
- debug_log("Retry number - %d\n", pc->retries);
+ ide_debug_log(IDE_DBG_FUNC, "%s: Retry #%d\n", __func__, pc->retries);
pc->retries++;
- return ide_issue_pc(drive, pc, idefloppy_start_pc_transfer,
- IDEFLOPPY_WAIT_CMD, NULL);
+ return ide_issue_pc(drive, WAIT_FLOPPY_CMD, NULL);
}
-static void idefloppy_create_prevent_cmd(struct ide_atapi_pc *pc, int prevent)
+void ide_floppy_create_read_capacity_cmd(struct ide_atapi_pc *pc)
{
- debug_log("creating prevent removal command, prevent = %d\n", prevent);
-
- idefloppy_init_pc(pc);
- pc->c[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL;
- pc->c[4] = prevent;
-}
-
-static void idefloppy_create_read_capacity_cmd(struct ide_atapi_pc *pc)
-{
- idefloppy_init_pc(pc);
+ ide_init_pc(pc);
pc->c[0] = GPCMD_READ_FORMAT_CAPACITIES;
pc->c[7] = 255;
pc->c[8] = 255;
pc->req_xfer = 255;
}
-static void idefloppy_create_format_unit_cmd(struct ide_atapi_pc *pc, int b,
- int l, int flags)
-{
- idefloppy_init_pc(pc);
- pc->c[0] = GPCMD_FORMAT_UNIT;
- pc->c[1] = 0x17;
-
- memset(pc->buf, 0, 12);
- pc->buf[1] = 0xA2;
- /* Default format list header, u8 1: FOV/DCRT/IMM bits set */
-
- if (flags & 1) /* Verify bit on... */
- pc->buf[1] ^= 0x20; /* ... turn off DCRT bit */
- pc->buf[3] = 8;
-
- put_unaligned(cpu_to_be32(b), (unsigned int *)(&pc->buf[4]));
- put_unaligned(cpu_to_be32(l), (unsigned int *)(&pc->buf[8]));
- pc->buf_size = 12;
- pc->flags |= PC_FLAG_WRITING;
-}
-
/* A mode sense command is used to "sense" floppy parameters. */
-static void idefloppy_create_mode_sense_cmd(struct ide_atapi_pc *pc,
- u8 page_code, u8 type)
+void ide_floppy_create_mode_sense_cmd(struct ide_atapi_pc *pc, u8 page_code)
{
u16 length = 8; /* sizeof(Mode Parameter Header) = 8 Bytes */
- idefloppy_init_pc(pc);
+ ide_init_pc(pc);
pc->c[0] = GPCMD_MODE_SENSE_10;
pc->c[1] = 0;
- pc->c[2] = page_code + (type << 6);
+ pc->c[2] = page_code;
switch (page_code) {
case IDEFLOPPY_CAPABILITIES_PAGE:
@@ -535,32 +276,25 @@
length += 32;
break;
default:
- printk(KERN_ERR "ide-floppy: unsupported page code "
- "in create_mode_sense_cmd\n");
+ printk(KERN_ERR PFX "unsupported page code in %s\n", __func__);
}
put_unaligned(cpu_to_be16(length), (u16 *) &pc->c[7]);
pc->req_xfer = length;
}
-static void idefloppy_create_start_stop_cmd(struct ide_atapi_pc *pc, int start)
-{
- idefloppy_init_pc(pc);
- pc->c[0] = GPCMD_START_STOP_UNIT;
- pc->c[4] = start;
-}
-
-static void idefloppy_create_rw_cmd(idefloppy_floppy_t *floppy,
+static void idefloppy_create_rw_cmd(ide_drive_t *drive,
struct ide_atapi_pc *pc, struct request *rq,
unsigned long sector)
{
+ idefloppy_floppy_t *floppy = drive->driver_data;
int block = sector / floppy->bs_factor;
int blocks = rq->nr_sectors / floppy->bs_factor;
int cmd = rq_data_dir(rq);
- debug_log("create_rw10_cmd: block == %d, blocks == %d\n",
- block, blocks);
+ ide_debug_log(IDE_DBG_FUNC, "%s: block: %d, blocks: %d\n", __func__,
+ block, blocks);
- idefloppy_init_pc(pc);
+ ide_init_pc(pc);
pc->c[0] = cmd == READ ? GPCMD_READ_10 : GPCMD_WRITE_10;
put_unaligned(cpu_to_be16(blocks), (unsigned short *)&pc->c[7]);
put_unaligned(cpu_to_be32(block), (unsigned int *) &pc->c[2]);
@@ -568,7 +302,7 @@
memcpy(rq->cmd, pc->c, 12);
pc->rq = rq;
- pc->b_count = cmd == READ ? 0 : rq->bio->bi_size;
+ pc->b_count = 0;
if (rq->cmd_flags & REQ_RW)
pc->flags |= PC_FLAG_WRITING;
pc->buf = NULL;
@@ -579,10 +313,10 @@
static void idefloppy_blockpc_cmd(idefloppy_floppy_t *floppy,
struct ide_atapi_pc *pc, struct request *rq)
{
- idefloppy_init_pc(pc);
+ ide_init_pc(pc);
memcpy(pc->c, rq->cmd, sizeof(pc->c));
pc->rq = rq;
- pc->b_count = rq->data_len;
+ pc->b_count = 0;
if (rq->data_len && rq_data_dir(rq) == WRITE)
pc->flags |= PC_FLAG_WRITING;
pc->buf = rq->data;
@@ -599,95 +333,89 @@
struct request *rq, sector_t block_s)
{
idefloppy_floppy_t *floppy = drive->driver_data;
+ ide_hwif_t *hwif = drive->hwif;
struct ide_atapi_pc *pc;
unsigned long block = (unsigned long)block_s;
- debug_log("dev: %s, cmd_type: %x, errors: %d\n",
- rq->rq_disk ? rq->rq_disk->disk_name : "?",
- rq->cmd_type, rq->errors);
- debug_log("sector: %ld, nr_sectors: %ld, "
- "current_nr_sectors: %d\n", (long)rq->sector,
- rq->nr_sectors, rq->current_nr_sectors);
+ ide_debug_log(IDE_DBG_FUNC, "%s: dev: %s, cmd: 0x%x, cmd_type: %x, "
+ "errors: %d\n",
+ __func__, rq->rq_disk ? rq->rq_disk->disk_name : "?",
+ rq->cmd[0], rq->cmd_type, rq->errors);
+
+ ide_debug_log(IDE_DBG_FUNC, "%s: sector: %ld, nr_sectors: %ld, "
+ "current_nr_sectors: %d\n",
+ __func__, (long)rq->sector, rq->nr_sectors,
+ rq->current_nr_sectors);
if (rq->errors >= ERROR_MAX) {
if (floppy->failed_pc)
ide_floppy_report_error(floppy, floppy->failed_pc);
else
- printk(KERN_ERR "ide-floppy: %s: I/O error\n",
- drive->name);
+ printk(KERN_ERR PFX "%s: I/O error\n", drive->name);
+
idefloppy_end_request(drive, 0, 0);
return ide_stopped;
}
if (blk_fs_request(rq)) {
if (((long)rq->sector % floppy->bs_factor) ||
(rq->nr_sectors % floppy->bs_factor)) {
- printk(KERN_ERR "%s: unsupported r/w request size\n",
- drive->name);
+ printk(KERN_ERR PFX "%s: unsupported r/w rq size\n",
+ drive->name);
idefloppy_end_request(drive, 0, 0);
return ide_stopped;
}
- pc = idefloppy_next_pc_storage(drive);
- idefloppy_create_rw_cmd(floppy, pc, rq, block);
+ pc = &floppy->queued_pc;
+ idefloppy_create_rw_cmd(drive, pc, rq, block);
} else if (blk_special_request(rq)) {
pc = (struct ide_atapi_pc *) rq->buffer;
} else if (blk_pc_request(rq)) {
- pc = idefloppy_next_pc_storage(drive);
+ pc = &floppy->queued_pc;
idefloppy_blockpc_cmd(floppy, pc, rq);
} else {
- blk_dump_rq_flags(rq,
- "ide-floppy: unsupported command in queue");
+ blk_dump_rq_flags(rq, PFX "unsupported command in queue");
idefloppy_end_request(drive, 0, 0);
return ide_stopped;
}
+ ide_init_sg_cmd(drive, rq);
+ ide_map_sg(drive, rq);
+
+ pc->sg = hwif->sg_table;
+ pc->sg_cnt = hwif->sg_nents;
+
pc->rq = rq;
return idefloppy_issue_pc(drive, pc);
}
/*
- * Add a special packet command request to the tail of the request queue,
- * and wait for it to be serviced.
- */
-static int idefloppy_queue_pc_tail(ide_drive_t *drive, struct ide_atapi_pc *pc)
-{
- struct ide_floppy_obj *floppy = drive->driver_data;
- struct request *rq;
- int error;
-
- rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
- rq->buffer = (char *) pc;
- rq->cmd_type = REQ_TYPE_SPECIAL;
- memcpy(rq->cmd, pc->c, 12);
- error = blk_execute_rq(drive->queue, floppy->disk, rq, 0);
- blk_put_request(rq);
-
- return error;
-}
-
-/*
* Look at the flexible disk page parameters. We ignore the CHS capacity
* parameters and use the LBA parameters instead.
*/
static int ide_floppy_get_flexible_disk_page(ide_drive_t *drive)
{
idefloppy_floppy_t *floppy = drive->driver_data;
+ struct gendisk *disk = floppy->disk;
struct ide_atapi_pc pc;
u8 *page;
int capacity, lba_capacity;
u16 transfer_rate, sector_size, cyls, rpm;
u8 heads, sectors;
- idefloppy_create_mode_sense_cmd(&pc, IDEFLOPPY_FLEXIBLE_DISK_PAGE,
- MODE_SENSE_CURRENT);
+ ide_floppy_create_mode_sense_cmd(&pc, IDEFLOPPY_FLEXIBLE_DISK_PAGE);
- if (idefloppy_queue_pc_tail(drive, &pc)) {
- printk(KERN_ERR "ide-floppy: Can't get flexible disk page"
- " parameters\n");
+ if (ide_queue_pc_tail(drive, disk, &pc)) {
+ printk(KERN_ERR PFX "Can't get flexible disk page params\n");
return 1;
}
- floppy->wp = !!(pc.buf[3] & 0x80);
- set_disk_ro(floppy->disk, floppy->wp);
+
+ if (pc.buf[3] & 0x80)
+ drive->atapi_flags |= IDE_AFLAG_WP;
+ else
+ drive->atapi_flags &= ~IDE_AFLAG_WP;
+
+ set_disk_ro(disk, !!(drive->atapi_flags & IDE_AFLAG_WP));
+
page = &pc.buf[8];
transfer_rate = be16_to_cpup((__be16 *)&pc.buf[8 + 2]);
@@ -700,7 +428,7 @@
capacity = cyls * heads * sectors * sector_size;
if (memcmp(page, &floppy->flexible_disk_page, 32))
- printk(KERN_INFO "%s: %dkB, %d/%d/%d CHS, %d kBps, "
+ printk(KERN_INFO PFX "%s: %dkB, %d/%d/%d CHS, %d kBps, "
"%d sector size, %d rpm\n",
drive->name, capacity / 1024, cyls, heads,
sectors, transfer_rate / 8, sector_size, rpm);
@@ -712,7 +440,7 @@
lba_capacity = floppy->blocks * floppy->block_size;
if (capacity < lba_capacity) {
- printk(KERN_NOTICE "%s: The disk reports a capacity of %d "
+ printk(KERN_NOTICE PFX "%s: The disk reports a capacity of %d "
"bytes, but the drive only handles %d\n",
drive->name, lba_capacity, capacity);
floppy->blocks = floppy->block_size ?
@@ -721,23 +449,6 @@
return 0;
}
-static int idefloppy_get_sfrp_bit(ide_drive_t *drive)
-{
- idefloppy_floppy_t *floppy = drive->driver_data;
- struct ide_atapi_pc pc;
-
- floppy->srfp = 0;
- idefloppy_create_mode_sense_cmd(&pc, IDEFLOPPY_CAPABILITIES_PAGE,
- MODE_SENSE_CURRENT);
-
- pc.flags |= PC_FLAG_SUPPRESS_ERROR;
- if (idefloppy_queue_pc_tail(drive, &pc))
- return 1;
-
- floppy->srfp = pc.buf[8 + 2] & 0x40;
- return (0);
-}
-
/*
* Determine if a media is present in the floppy drive, and if so, its LBA
* capacity.
@@ -745,6 +456,7 @@
static int ide_floppy_get_capacity(ide_drive_t *drive)
{
idefloppy_floppy_t *floppy = drive->driver_data;
+ struct gendisk *disk = floppy->disk;
struct ide_atapi_pc pc;
u8 *cap_desc;
u8 header_len, desc_cnt;
@@ -756,9 +468,9 @@
floppy->bs_factor = 1;
set_capacity(floppy->disk, 0);
- idefloppy_create_read_capacity_cmd(&pc);
- if (idefloppy_queue_pc_tail(drive, &pc)) {
- printk(KERN_ERR "ide-floppy: Can't get floppy parameters\n");
+ ide_floppy_create_read_capacity_cmd(&pc);
+ if (ide_queue_pc_tail(drive, disk, &pc)) {
+ printk(KERN_ERR PFX "Can't get floppy parameters\n");
return 1;
}
header_len = pc.buf[3];
@@ -771,8 +483,9 @@
blocks = be32_to_cpup((__be32 *)&pc.buf[desc_start]);
length = be16_to_cpup((__be16 *)&pc.buf[desc_start + 6]);
- debug_log("Descriptor %d: %dkB, %d blocks, %d sector size\n",
- i, blocks * length / 1024, blocks, length);
+ ide_debug_log(IDE_DBG_PROBE, "Descriptor %d: %dkB, %d blocks, "
+ "%d sector size\n",
+ i, blocks * length / 1024, blocks, length);
if (i)
continue;
@@ -792,23 +505,24 @@
case CAPACITY_CURRENT:
/* Normal Zip/LS-120 disks */
if (memcmp(cap_desc, &floppy->cap_desc, 8))
- printk(KERN_INFO "%s: %dkB, %d blocks, %d "
- "sector size\n", drive->name,
- blocks * length / 1024, blocks, length);
+ printk(KERN_INFO PFX "%s: %dkB, %d blocks, %d "
+ "sector size\n",
+ drive->name, blocks * length / 1024,
+ blocks, length);
memcpy(&floppy->cap_desc, cap_desc, 8);
if (!length || length % 512) {
- printk(KERN_NOTICE "%s: %d bytes block size "
- "not supported\n", drive->name, length);
+ printk(KERN_NOTICE PFX "%s: %d bytes block size"
+ " not supported\n", drive->name, length);
} else {
floppy->blocks = blocks;
floppy->block_size = length;
floppy->bs_factor = length / 512;
if (floppy->bs_factor != 1)
- printk(KERN_NOTICE "%s: warning: non "
- "512 bytes block size not "
- "fully supported\n",
- drive->name);
+ printk(KERN_NOTICE PFX "%s: Warning: "
+ "non 512 bytes block size not "
+ "fully supported\n",
+ drive->name);
rc = 0;
}
break;
@@ -817,143 +531,28 @@
* This is a KERN_ERR so it appears on screen
* for the user to see
*/
- printk(KERN_ERR "%s: No disk in drive\n", drive->name);
+ printk(KERN_ERR PFX "%s: No disk in drive\n",
+ drive->name);
break;
case CAPACITY_INVALID:
- printk(KERN_ERR "%s: Invalid capacity for disk "
+ printk(KERN_ERR PFX "%s: Invalid capacity for disk "
"in drive\n", drive->name);
break;
}
- debug_log("Descriptor 0 Code: %d\n",
- pc.buf[desc_start + 4] & 0x03);
+ ide_debug_log(IDE_DBG_PROBE, "Descriptor 0 Code: %d\n",
+ pc.buf[desc_start + 4] & 0x03);
}
/* Clik! disk does not support get_flexible_disk_page */
if (!(drive->atapi_flags & IDE_AFLAG_CLIK_DRIVE))
(void) ide_floppy_get_flexible_disk_page(drive);
- set_capacity(floppy->disk, floppy->blocks * floppy->bs_factor);
+ set_capacity(disk, floppy->blocks * floppy->bs_factor);
+
return rc;
}
-/*
- * Obtain the list of formattable capacities.
- * Very similar to ide_floppy_get_capacity, except that we push the capacity
- * descriptors to userland, instead of our own structures.
- *
- * Userland gives us the following structure:
- *
- * struct idefloppy_format_capacities {
- * int nformats;
- * struct {
- * int nblocks;
- * int blocksize;
- * } formats[];
- * };
- *
- * userland initializes nformats to the number of allocated formats[] records.
- * On exit we set nformats to the number of records we've actually initialized.
- */
-
-static int ide_floppy_get_format_capacities(ide_drive_t *drive, int __user *arg)
-{
- struct ide_atapi_pc pc;
- u8 header_len, desc_cnt;
- int i, blocks, length, u_array_size, u_index;
- int __user *argp;
-
- if (get_user(u_array_size, arg))
- return (-EFAULT);
-
- if (u_array_size <= 0)
- return (-EINVAL);
-
- idefloppy_create_read_capacity_cmd(&pc);
- if (idefloppy_queue_pc_tail(drive, &pc)) {
- printk(KERN_ERR "ide-floppy: Can't get floppy parameters\n");
- return (-EIO);
- }
- header_len = pc.buf[3];
- desc_cnt = header_len / 8; /* capacity descriptor of 8 bytes */
-
- u_index = 0;
- argp = arg + 1;
-
- /*
- * We always skip the first capacity descriptor. That's the current
- * capacity. We are interested in the remaining descriptors, the
- * formattable capacities.
- */
- for (i = 1; i < desc_cnt; i++) {
- unsigned int desc_start = 4 + i*8;
-
- if (u_index >= u_array_size)
- break; /* User-supplied buffer too small */
-
- blocks = be32_to_cpup((__be32 *)&pc.buf[desc_start]);
- length = be16_to_cpup((__be16 *)&pc.buf[desc_start + 6]);
-
- if (put_user(blocks, argp))
- return(-EFAULT);
- ++argp;
-
- if (put_user(length, argp))
- return (-EFAULT);
- ++argp;
-
- ++u_index;
- }
-
- if (put_user(u_index, arg))
- return (-EFAULT);
- return (0);
-}
-
-/*
- * Get ATAPI_FORMAT_UNIT progress indication.
- *
- * Userland gives a pointer to an int. The int is set to a progress
- * indicator 0-65536, with 65536=100%.
- *
- * If the drive does not support format progress indication, we just check
- * the dsc bit, and return either 0 or 65536.
- */
-
-static int idefloppy_get_format_progress(ide_drive_t *drive, int __user *arg)
-{
- idefloppy_floppy_t *floppy = drive->driver_data;
- struct ide_atapi_pc pc;
- int progress_indication = 0x10000;
-
- if (floppy->srfp) {
- idefloppy_create_request_sense_cmd(&pc);
- if (idefloppy_queue_pc_tail(drive, &pc))
- return (-EIO);
-
- if (floppy->sense_key == 2 &&
- floppy->asc == 4 &&
- floppy->ascq == 4)
- progress_indication = floppy->progress_indication;
-
- /* Else assume format_unit has finished, and we're at 0x10000 */
- } else {
- ide_hwif_t *hwif = drive->hwif;
- unsigned long flags;
- u8 stat;
-
- local_irq_save(flags);
- stat = hwif->tp_ops->read_status(hwif);
- local_irq_restore(flags);
-
- progress_indication = ((stat & SEEK_STAT) == 0) ? 0 : 0x10000;
- }
- if (put_user(progress_indication, arg))
- return (-EFAULT);
-
- return (0);
-}
-
-static sector_t idefloppy_capacity(ide_drive_t *drive)
+sector_t ide_floppy_capacity(ide_drive_t *drive)
{
idefloppy_floppy_t *floppy = drive->driver_data;
unsigned long capacity = floppy->blocks * floppy->bs_factor;
@@ -961,77 +560,14 @@
return capacity;
}
-/*
- * Check whether we can support a drive, based on the ATAPI IDENTIFY command
- * results.
- */
-static int idefloppy_identify_device(ide_drive_t *drive, struct hd_driveid *id)
-{
- u8 gcw[2];
- u8 device_type, protocol, removable, drq_type, packet_size;
-
- *((u16 *) &gcw) = id->config;
-
- device_type = gcw[1] & 0x1F;
- removable = (gcw[0] & 0x80) >> 7;
- protocol = (gcw[1] & 0xC0) >> 6;
- drq_type = (gcw[0] & 0x60) >> 5;
- packet_size = gcw[0] & 0x03;
-
-#ifdef CONFIG_PPC
- /* kludge for Apple PowerBook internal zip */
- if (device_type == 5 &&
- !strstr(id->model, "CD-ROM") && strstr(id->model, "ZIP"))
- device_type = 0;
-#endif
-
- if (protocol != 2)
- printk(KERN_ERR "ide-floppy: Protocol (0x%02x) is not ATAPI\n",
- protocol);
- else if (device_type != 0)
- printk(KERN_ERR "ide-floppy: Device type (0x%02x) is not set "
- "to floppy\n", device_type);
- else if (!removable)
- printk(KERN_ERR "ide-floppy: The removable flag is not set\n");
- else if (drq_type == 3)
- printk(KERN_ERR "ide-floppy: Sorry, DRQ type (0x%02x) not "
- "supported\n", drq_type);
- else if (packet_size != 0)
- printk(KERN_ERR "ide-floppy: Packet size (0x%02x) is not 12 "
- "bytes\n", packet_size);
- else
- return 1;
- return 0;
-}
-
-#ifdef CONFIG_IDE_PROC_FS
-static void idefloppy_add_settings(ide_drive_t *drive)
-{
- idefloppy_floppy_t *floppy = drive->driver_data;
-
- ide_add_setting(drive, "bios_cyl", SETTING_RW, TYPE_INT, 0, 1023, 1, 1,
- &drive->bios_cyl, NULL);
- ide_add_setting(drive, "bios_head", SETTING_RW, TYPE_BYTE, 0, 255, 1, 1,
- &drive->bios_head, NULL);
- ide_add_setting(drive, "bios_sect", SETTING_RW, TYPE_BYTE, 0, 63, 1, 1,
- &drive->bios_sect, NULL);
- ide_add_setting(drive, "ticks", SETTING_RW, TYPE_BYTE, 0, 255, 1, 1,
- &floppy->ticks, NULL);
-}
-#else
-static inline void idefloppy_add_settings(ide_drive_t *drive) { ; }
-#endif
-
static void idefloppy_setup(ide_drive_t *drive, idefloppy_floppy_t *floppy)
{
- u8 gcw[2];
+ u16 *id = drive->id;
- *((u16 *) &gcw) = drive->id->config;
- floppy->pc = floppy->pc_stack;
- drive->pc_callback = ide_floppy_callback;
+ drive->pc_callback = ide_floppy_callback;
+ drive->pc_update_buffers = idefloppy_update_buffers;
+ drive->pc_io_buffers = ide_io_buffers;
- if (((gcw[0] & 0x60) >> 5) == 1)
- drive->atapi_flags |= IDE_AFLAG_DRQ_INTERRUPT;
/*
* We used to check revisions here. At this point however I'm giving up.
* Just assume they are all broken, its easier.
@@ -1041,10 +577,10 @@
* it. It should be fixed as of version 1.9, but to be on the safe side
* we'll leave the limitation below for the 2.2.x tree.
*/
- if (!strncmp(drive->id->model, "IOMEGA ZIP 100 ATAPI", 20)) {
+ if (!strncmp((char *)&id[ATA_ID_PROD], "IOMEGA ZIP 100 ATAPI", 20)) {
drive->atapi_flags |= IDE_AFLAG_ZIP_DRIVE;
/* This value will be visible in the /proc/ide/hdx/settings */
- floppy->ticks = IDEFLOPPY_TICKS_DELAY;
+ drive->pc_delay = IDEFLOPPY_PC_DELAY;
blk_queue_max_sectors(drive->queue, 64);
}
@@ -1052,13 +588,16 @@
* Guess what? The IOMEGA Clik! drive also needs the above fix. It makes
* nasty clicking noises without it, so please don't remove this.
*/
- if (strncmp(drive->id->model, "IOMEGA Clik!", 11) == 0) {
+ if (strncmp((char *)&id[ATA_ID_PROD], "IOMEGA Clik!", 11) == 0) {
blk_queue_max_sectors(drive->queue, 64);
drive->atapi_flags |= IDE_AFLAG_CLIK_DRIVE;
+ /* IOMEGA Clik! drives do not support lock/unlock commands */
+ drive->atapi_flags |= IDE_AFLAG_NO_DOORLOCK;
}
(void) ide_floppy_get_capacity(drive);
- idefloppy_add_settings(drive);
+
+ ide_proc_register_driver(drive, floppy->driver);
}
static void ide_floppy_remove(ide_drive_t *drive)
@@ -1075,7 +614,7 @@
static void idefloppy_cleanup_obj(struct kref *kref)
{
- struct ide_floppy_obj *floppy = to_ide_floppy(kref);
+ struct ide_floppy_obj *floppy = to_ide_drv(kref, ide_floppy_obj);
ide_drive_t *drive = floppy->drive;
struct gendisk *g = floppy->disk;
@@ -1085,24 +624,6 @@
kfree(floppy);
}
-#ifdef CONFIG_IDE_PROC_FS
-static int proc_idefloppy_read_capacity(char *page, char **start, off_t off,
- int count, int *eof, void *data)
-{
- ide_drive_t*drive = (ide_drive_t *)data;
- int len;
-
- len = sprintf(page, "%llu\n", (long long)idefloppy_capacity(drive));
- PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
-}
-
-static ide_proc_entry_t idefloppy_proc[] = {
- { "capacity", S_IFREG|S_IRUGO, proc_idefloppy_read_capacity, NULL },
- { "geometry", S_IFREG|S_IRUGO, proc_ide_read_geometry, NULL },
- { NULL, 0, NULL, NULL }
-};
-#endif /* CONFIG_IDE_PROC_FS */
-
static int ide_floppy_probe(ide_drive_t *);
static ide_driver_t idefloppy_driver = {
@@ -1114,13 +635,12 @@
.probe = ide_floppy_probe,
.remove = ide_floppy_remove,
.version = IDEFLOPPY_VERSION,
- .media = ide_floppy,
- .supports_dsc_overlap = 0,
.do_request = idefloppy_do_request,
.end_request = idefloppy_end_request,
.error = __ide_error,
#ifdef CONFIG_IDE_PROC_FS
- .proc = idefloppy_proc,
+ .proc = ide_floppy_proc,
+ .settings = ide_floppy_settings,
#endif
};
@@ -1129,30 +649,24 @@
struct gendisk *disk = inode->i_bdev->bd_disk;
struct ide_floppy_obj *floppy;
ide_drive_t *drive;
- struct ide_atapi_pc pc;
int ret = 0;
- debug_log("Reached %s\n", __func__);
-
floppy = ide_floppy_get(disk);
if (!floppy)
return -ENXIO;
drive = floppy->drive;
+ ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__);
+
floppy->openers++;
if (floppy->openers == 1) {
drive->atapi_flags &= ~IDE_AFLAG_FORMAT_IN_PROGRESS;
/* Just in case */
- idefloppy_init_pc(&pc);
- pc.c[0] = GPCMD_TEST_UNIT_READY;
-
- if (idefloppy_queue_pc_tail(drive, &pc)) {
- idefloppy_create_start_stop_cmd(&pc, 1);
- (void) idefloppy_queue_pc_tail(drive, &pc);
- }
+ if (ide_do_test_unit_ready(drive, disk))
+ ide_do_start_stop(drive, disk, 1);
if (ide_floppy_get_capacity(drive)
&& (filp->f_flags & O_NDELAY) == 0
@@ -1166,16 +680,13 @@
goto out_put_floppy;
}
- if (floppy->wp && (filp->f_mode & 2)) {
+ if ((drive->atapi_flags & IDE_AFLAG_WP) && (filp->f_mode & 2)) {
ret = -EROFS;
goto out_put_floppy;
}
+
drive->atapi_flags |= IDE_AFLAG_MEDIA_CHANGED;
- /* IOMEGA Clik! drives do not support lock/unlock commands */
- if (!(drive->atapi_flags & IDE_AFLAG_CLIK_DRIVE)) {
- idefloppy_create_prevent_cmd(&pc, 1);
- (void) idefloppy_queue_pc_tail(drive, &pc);
- }
+ ide_set_media_lock(drive, disk, 1);
check_disk_change(inode->i_bdev);
} else if (drive->atapi_flags & IDE_AFLAG_FORMAT_IN_PROGRESS) {
ret = -EBUSY;
@@ -1192,19 +703,13 @@
static int idefloppy_release(struct inode *inode, struct file *filp)
{
struct gendisk *disk = inode->i_bdev->bd_disk;
- struct ide_floppy_obj *floppy = ide_floppy_g(disk);
+ struct ide_floppy_obj *floppy = ide_drv_g(disk, ide_floppy_obj);
ide_drive_t *drive = floppy->drive;
- struct ide_atapi_pc pc;
- debug_log("Reached %s\n", __func__);
+ ide_debug_log(IDE_DBG_FUNC, "Call %s\n", __func__);
if (floppy->openers == 1) {
- /* IOMEGA Clik! drives do not support lock/unlock commands */
- if (!(drive->atapi_flags & IDE_AFLAG_CLIK_DRIVE)) {
- idefloppy_create_prevent_cmd(&pc, 0);
- (void) idefloppy_queue_pc_tail(drive, &pc);
- }
-
+ ide_set_media_lock(drive, disk, 0);
drive->atapi_flags &= ~IDE_AFLAG_FORMAT_IN_PROGRESS;
}
@@ -1217,7 +722,8 @@
static int idefloppy_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
- struct ide_floppy_obj *floppy = ide_floppy_g(bdev->bd_disk);
+ struct ide_floppy_obj *floppy = ide_drv_g(bdev->bd_disk,
+ ide_floppy_obj);
ide_drive_t *drive = floppy->drive;
geo->heads = drive->bios_head;
@@ -1226,137 +732,15 @@
return 0;
}
-static int ide_floppy_lockdoor(ide_drive_t *drive, struct ide_atapi_pc *pc,
- unsigned long arg, unsigned int cmd)
-{
- idefloppy_floppy_t *floppy = drive->driver_data;
-
- if (floppy->openers > 1)
- return -EBUSY;
-
- /* The IOMEGA Clik! Drive doesn't support this command -
- * no room for an eject mechanism */
- if (!(drive->atapi_flags & IDE_AFLAG_CLIK_DRIVE)) {
- int prevent = arg ? 1 : 0;
-
- if (cmd == CDROMEJECT)
- prevent = 0;
-
- idefloppy_create_prevent_cmd(pc, prevent);
- (void) idefloppy_queue_pc_tail(floppy->drive, pc);
- }
-
- if (cmd == CDROMEJECT) {
- idefloppy_create_start_stop_cmd(pc, 2);
- (void) idefloppy_queue_pc_tail(floppy->drive, pc);
- }
-
- return 0;
-}
-
-static int ide_floppy_format_unit(idefloppy_floppy_t *floppy,
- int __user *arg)
-{
- struct ide_atapi_pc pc;
- ide_drive_t *drive = floppy->drive;
- int blocks, length, flags, err = 0;
-
- if (floppy->openers > 1) {
- /* Don't format if someone is using the disk */
- drive->atapi_flags &= ~IDE_AFLAG_FORMAT_IN_PROGRESS;
- return -EBUSY;
- }
-
- drive->atapi_flags |= IDE_AFLAG_FORMAT_IN_PROGRESS;
-
- /*
- * Send ATAPI_FORMAT_UNIT to the drive.
- *
- * Userland gives us the following structure:
- *
- * struct idefloppy_format_command {
- * int nblocks;
- * int blocksize;
- * int flags;
- * } ;
- *
- * flags is a bitmask, currently, the only defined flag is:
- *
- * 0x01 - verify media after format.
- */
- if (get_user(blocks, arg) ||
- get_user(length, arg+1) ||
- get_user(flags, arg+2)) {
- err = -EFAULT;
- goto out;
- }
-
- (void) idefloppy_get_sfrp_bit(drive);
- idefloppy_create_format_unit_cmd(&pc, blocks, length, flags);
-
- if (idefloppy_queue_pc_tail(drive, &pc))
- err = -EIO;
-
-out:
- if (err)
- drive->atapi_flags &= ~IDE_AFLAG_FORMAT_IN_PROGRESS;
- return err;
-}
-
-
-static int idefloppy_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- struct block_device *bdev = inode->i_bdev;
- struct ide_floppy_obj *floppy = ide_floppy_g(bdev->bd_disk);
- ide_drive_t *drive = floppy->drive;
- struct ide_atapi_pc pc;
- void __user *argp = (void __user *)arg;
- int err;
-
- switch (cmd) {
- case CDROMEJECT:
- /* fall through */
- case CDROM_LOCKDOOR:
- return ide_floppy_lockdoor(drive, &pc, arg, cmd);
- case IDEFLOPPY_IOCTL_FORMAT_SUPPORTED:
- return 0;
- case IDEFLOPPY_IOCTL_FORMAT_GET_CAPACITY:
- return ide_floppy_get_format_capacities(drive, argp);
- case IDEFLOPPY_IOCTL_FORMAT_START:
- if (!(file->f_mode & 2))
- return -EPERM;
-
- return ide_floppy_format_unit(floppy, (int __user *)arg);
- case IDEFLOPPY_IOCTL_FORMAT_GET_PROGRESS:
- return idefloppy_get_format_progress(drive, argp);
- }
-
- /*
- * skip SCSI_IOCTL_SEND_COMMAND (deprecated)
- * and CDROM_SEND_PACKET (legacy) ioctls
- */
- if (cmd != CDROM_SEND_PACKET && cmd != SCSI_IOCTL_SEND_COMMAND)
- err = scsi_cmd_ioctl(file, bdev->bd_disk->queue,
- bdev->bd_disk, cmd, argp);
- else
- err = -ENOTTY;
-
- if (err == -ENOTTY)
- err = generic_ide_ioctl(drive, file, bdev, cmd, arg);
-
- return err;
-}
-
static int idefloppy_media_changed(struct gendisk *disk)
{
- struct ide_floppy_obj *floppy = ide_floppy_g(disk);
+ struct ide_floppy_obj *floppy = ide_drv_g(disk, ide_floppy_obj);
ide_drive_t *drive = floppy->drive;
int ret;
/* do not scan partitions twice if this is a removable device */
- if (drive->attach) {
- drive->attach = 0;
+ if (drive->dev_flags & IDE_DFLAG_ATTACH) {
+ drive->dev_flags &= ~IDE_DFLAG_ATTACH;
return 0;
}
ret = !!(drive->atapi_flags & IDE_AFLAG_MEDIA_CHANGED);
@@ -1366,8 +750,8 @@
static int idefloppy_revalidate_disk(struct gendisk *disk)
{
- struct ide_floppy_obj *floppy = ide_floppy_g(disk);
- set_capacity(disk, idefloppy_capacity(floppy->drive));
+ struct ide_floppy_obj *floppy = ide_drv_g(disk, ide_floppy_obj);
+ set_capacity(disk, ide_floppy_capacity(floppy->drive));
return 0;
}
@@ -1375,7 +759,7 @@
.owner = THIS_MODULE,
.open = idefloppy_open,
.release = idefloppy_release,
- .ioctl = idefloppy_ioctl,
+ .ioctl = ide_floppy_ioctl,
.getgeo = idefloppy_getgeo,
.media_changed = idefloppy_media_changed,
.revalidate_disk = idefloppy_revalidate_disk
@@ -1388,19 +772,19 @@
if (!strstr("ide-floppy", drive->driver_req))
goto failed;
- if (!drive->present)
- goto failed;
+
if (drive->media != ide_floppy)
goto failed;
- if (!idefloppy_identify_device(drive, drive->id)) {
- printk(KERN_ERR "ide-floppy: %s: not supported by this version"
- " of ide-floppy\n", drive->name);
+
+ if (!ide_check_atapi_device(drive, DRV_NAME)) {
+ printk(KERN_ERR PFX "%s: not supported by this version of "
+ DRV_NAME "\n", drive->name);
goto failed;
}
floppy = kzalloc(sizeof(idefloppy_floppy_t), GFP_KERNEL);
if (!floppy) {
- printk(KERN_ERR "ide-floppy: %s: Can't allocate a floppy"
- " structure\n", drive->name);
+ printk(KERN_ERR PFX "%s: Can't allocate a floppy structure\n",
+ drive->name);
goto failed;
}
@@ -1410,8 +794,6 @@
ide_init_disk(g, drive);
- ide_proc_register_driver(drive, &idefloppy_driver);
-
kref_init(&floppy->kref);
floppy->drive = drive;
@@ -1422,13 +804,16 @@
drive->driver_data = floppy;
+ drive->debug_mask = debug_mask;
+
idefloppy_setup(drive, floppy);
+ drive->dev_flags |= IDE_DFLAG_ATTACH;
g->minors = 1 << PARTN_BITS;
g->driverfs_dev = &drive->gendev;
- g->flags = drive->removable ? GENHD_FL_REMOVABLE : 0;
+ if (drive->dev_flags & IDE_DFLAG_REMOVABLE)
+ g->flags = GENHD_FL_REMOVABLE;
g->fops = &idefloppy_ops;
- drive->attach = 1;
add_disk(g);
return 0;
@@ -1445,11 +830,12 @@
static int __init idefloppy_init(void)
{
- printk("ide-floppy driver " IDEFLOPPY_VERSION "\n");
+ printk(KERN_INFO DRV_NAME " driver " IDEFLOPPY_VERSION "\n");
return driver_register(&idefloppy_driver.gen_driver);
}
MODULE_ALIAS("ide:*m-floppy*");
+MODULE_ALIAS("ide-floppy");
module_init(idefloppy_init);
module_exit(idefloppy_exit);
MODULE_LICENSE("GPL");
diff --git a/drivers/ide/ide-floppy.h b/drivers/ide/ide-floppy.h
new file mode 100644
index 0000000..17cf865
--- /dev/null
+++ b/drivers/ide/ide-floppy.h
@@ -0,0 +1,62 @@
+#ifndef __IDE_FLOPPY_H
+#define __IDE_FLOPPY_H
+
+/*
+ * Most of our global data which we need to save even as we leave the driver
+ * due to an interrupt or a timer event is stored in a variable of type
+ * idefloppy_floppy_t, defined below.
+ */
+typedef struct ide_floppy_obj {
+ ide_drive_t *drive;
+ ide_driver_t *driver;
+ struct gendisk *disk;
+ struct kref kref;
+ unsigned int openers; /* protected by BKL for now */
+
+ /* Last failed packet command */
+ struct ide_atapi_pc *failed_pc;
+ /* used for blk_{fs,pc}_request() requests */
+ struct ide_atapi_pc queued_pc;
+
+ /* Last error information */
+ u8 sense_key, asc, ascq;
+
+ int progress_indication;
+
+ /* Device information */
+ /* Current format */
+ int blocks, block_size, bs_factor;
+ /* Last format capacity descriptor */
+ u8 cap_desc[8];
+ /* Copy of the flexible disk page */
+ u8 flexible_disk_page[32];
+} idefloppy_floppy_t;
+
+/*
+ * Pages of the SELECT SENSE / MODE SENSE packet commands.
+ * See SFF-8070i spec.
+ */
+#define IDEFLOPPY_CAPABILITIES_PAGE 0x1b
+#define IDEFLOPPY_FLEXIBLE_DISK_PAGE 0x05
+
+/* IOCTLs used in low-level formatting. */
+#define IDEFLOPPY_IOCTL_FORMAT_SUPPORTED 0x4600
+#define IDEFLOPPY_IOCTL_FORMAT_GET_CAPACITY 0x4601
+#define IDEFLOPPY_IOCTL_FORMAT_START 0x4602
+#define IDEFLOPPY_IOCTL_FORMAT_GET_PROGRESS 0x4603
+
+/* ide-floppy.c */
+void ide_floppy_create_mode_sense_cmd(struct ide_atapi_pc *, u8);
+void ide_floppy_create_read_capacity_cmd(struct ide_atapi_pc *);
+sector_t ide_floppy_capacity(ide_drive_t *);
+
+/* ide-floppy_ioctl.c */
+int ide_floppy_ioctl(struct inode *, struct file *, unsigned, unsigned long);
+
+#ifdef CONFIG_IDE_PROC_FS
+/* ide-floppy_proc.c */
+extern ide_proc_entry_t ide_floppy_proc[];
+extern const struct ide_proc_devset ide_floppy_settings[];
+#endif
+
+#endif /*__IDE_FLOPPY_H */
diff --git a/drivers/ide/ide-floppy_ioctl.c b/drivers/ide/ide-floppy_ioctl.c
new file mode 100644
index 0000000..a3a7a08
--- /dev/null
+++ b/drivers/ide/ide-floppy_ioctl.c
@@ -0,0 +1,293 @@
+/*
+ * ide-floppy IOCTLs handling.
+ */
+
+#include <linux/kernel.h>
+#include <linux/ide.h>
+#include <linux/cdrom.h>
+
+#include <asm/unaligned.h>
+
+#include <scsi/scsi_ioctl.h>
+
+#include "ide-floppy.h"
+
+/*
+ * Obtain the list of formattable capacities.
+ * Very similar to ide_floppy_get_capacity, except that we push the capacity
+ * descriptors to userland, instead of our own structures.
+ *
+ * Userland gives us the following structure:
+ *
+ * struct idefloppy_format_capacities {
+ * int nformats;
+ * struct {
+ * int nblocks;
+ * int blocksize;
+ * } formats[];
+ * };
+ *
+ * userland initializes nformats to the number of allocated formats[] records.
+ * On exit we set nformats to the number of records we've actually initialized.
+ */
+
+static int ide_floppy_get_format_capacities(ide_drive_t *drive, int __user *arg)
+{
+ struct ide_floppy_obj *floppy = drive->driver_data;
+ struct ide_atapi_pc pc;
+ u8 header_len, desc_cnt;
+ int i, blocks, length, u_array_size, u_index;
+ int __user *argp;
+
+ if (get_user(u_array_size, arg))
+ return -EFAULT;
+
+ if (u_array_size <= 0)
+ return -EINVAL;
+
+ ide_floppy_create_read_capacity_cmd(&pc);
+ if (ide_queue_pc_tail(drive, floppy->disk, &pc)) {
+ printk(KERN_ERR "ide-floppy: Can't get floppy parameters\n");
+ return -EIO;
+ }
+
+ header_len = pc.buf[3];
+ desc_cnt = header_len / 8; /* capacity descriptor of 8 bytes */
+
+ u_index = 0;
+ argp = arg + 1;
+
+ /*
+ * We always skip the first capacity descriptor. That's the current
+ * capacity. We are interested in the remaining descriptors, the
+ * formattable capacities.
+ */
+ for (i = 1; i < desc_cnt; i++) {
+ unsigned int desc_start = 4 + i*8;
+
+ if (u_index >= u_array_size)
+ break; /* User-supplied buffer too small */
+
+ blocks = be32_to_cpup((__be32 *)&pc.buf[desc_start]);
+ length = be16_to_cpup((__be16 *)&pc.buf[desc_start + 6]);
+
+ if (put_user(blocks, argp))
+ return -EFAULT;
+
+ ++argp;
+
+ if (put_user(length, argp))
+ return -EFAULT;
+
+ ++argp;
+
+ ++u_index;
+ }
+
+ if (put_user(u_index, arg))
+ return -EFAULT;
+
+ return 0;
+}
+
+static void ide_floppy_create_format_unit_cmd(struct ide_atapi_pc *pc, int b,
+ int l, int flags)
+{
+ ide_init_pc(pc);
+ pc->c[0] = GPCMD_FORMAT_UNIT;
+ pc->c[1] = 0x17;
+
+ memset(pc->buf, 0, 12);
+ pc->buf[1] = 0xA2;
+ /* Default format list header, u8 1: FOV/DCRT/IMM bits set */
+
+ if (flags & 1) /* Verify bit on... */
+ pc->buf[1] ^= 0x20; /* ... turn off DCRT bit */
+ pc->buf[3] = 8;
+
+ put_unaligned(cpu_to_be32(b), (unsigned int *)(&pc->buf[4]));
+ put_unaligned(cpu_to_be32(l), (unsigned int *)(&pc->buf[8]));
+ pc->buf_size = 12;
+ pc->flags |= PC_FLAG_WRITING;
+}
+
+static int ide_floppy_get_sfrp_bit(ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+ struct ide_atapi_pc pc;
+
+ drive->atapi_flags &= ~IDE_AFLAG_SRFP;
+
+ ide_floppy_create_mode_sense_cmd(&pc, IDEFLOPPY_CAPABILITIES_PAGE);
+ pc.flags |= PC_FLAG_SUPPRESS_ERROR;
+
+ if (ide_queue_pc_tail(drive, floppy->disk, &pc))
+ return 1;
+
+ if (pc.buf[8 + 2] & 0x40)
+ drive->atapi_flags |= IDE_AFLAG_SRFP;
+
+ return 0;
+}
+
+static int ide_floppy_format_unit(ide_drive_t *drive, int __user *arg)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+ struct ide_atapi_pc pc;
+ int blocks, length, flags, err = 0;
+
+ if (floppy->openers > 1) {
+ /* Don't format if someone is using the disk */
+ drive->atapi_flags &= ~IDE_AFLAG_FORMAT_IN_PROGRESS;
+ return -EBUSY;
+ }
+
+ drive->atapi_flags |= IDE_AFLAG_FORMAT_IN_PROGRESS;
+
+ /*
+ * Send ATAPI_FORMAT_UNIT to the drive.
+ *
+ * Userland gives us the following structure:
+ *
+ * struct idefloppy_format_command {
+ * int nblocks;
+ * int blocksize;
+ * int flags;
+ * } ;
+ *
+ * flags is a bitmask, currently, the only defined flag is:
+ *
+ * 0x01 - verify media after format.
+ */
+ if (get_user(blocks, arg) ||
+ get_user(length, arg+1) ||
+ get_user(flags, arg+2)) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ (void)ide_floppy_get_sfrp_bit(drive);
+ ide_floppy_create_format_unit_cmd(&pc, blocks, length, flags);
+
+ if (ide_queue_pc_tail(drive, floppy->disk, &pc))
+ err = -EIO;
+
+out:
+ if (err)
+ drive->atapi_flags &= ~IDE_AFLAG_FORMAT_IN_PROGRESS;
+ return err;
+}
+
+/*
+ * Get ATAPI_FORMAT_UNIT progress indication.
+ *
+ * Userland gives a pointer to an int. The int is set to a progress
+ * indicator 0-65536, with 65536=100%.
+ *
+ * If the drive does not support format progress indication, we just check
+ * the dsc bit, and return either 0 or 65536.
+ */
+
+static int ide_floppy_get_format_progress(ide_drive_t *drive, int __user *arg)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+ struct ide_atapi_pc pc;
+ int progress_indication = 0x10000;
+
+ if (drive->atapi_flags & IDE_AFLAG_SRFP) {
+ ide_create_request_sense_cmd(drive, &pc);
+ if (ide_queue_pc_tail(drive, floppy->disk, &pc))
+ return -EIO;
+
+ if (floppy->sense_key == 2 &&
+ floppy->asc == 4 &&
+ floppy->ascq == 4)
+ progress_indication = floppy->progress_indication;
+
+ /* Else assume format_unit has finished, and we're at 0x10000 */
+ } else {
+ ide_hwif_t *hwif = drive->hwif;
+ unsigned long flags;
+ u8 stat;
+
+ local_irq_save(flags);
+ stat = hwif->tp_ops->read_status(hwif);
+ local_irq_restore(flags);
+
+ progress_indication = ((stat & ATA_DSC) == 0) ? 0 : 0x10000;
+ }
+
+ if (put_user(progress_indication, arg))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int ide_floppy_lockdoor(ide_drive_t *drive, struct ide_atapi_pc *pc,
+ unsigned long arg, unsigned int cmd)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+ struct gendisk *disk = floppy->disk;
+ int prevent = (arg && cmd != CDROMEJECT) ? 1 : 0;
+
+ if (floppy->openers > 1)
+ return -EBUSY;
+
+ ide_set_media_lock(drive, disk, prevent);
+
+ if (cmd == CDROMEJECT)
+ ide_do_start_stop(drive, disk, 2);
+
+ return 0;
+}
+
+static int ide_floppy_format_ioctl(ide_drive_t *drive, struct file *file,
+ unsigned int cmd, void __user *argp)
+{
+ switch (cmd) {
+ case IDEFLOPPY_IOCTL_FORMAT_SUPPORTED:
+ return 0;
+ case IDEFLOPPY_IOCTL_FORMAT_GET_CAPACITY:
+ return ide_floppy_get_format_capacities(drive, argp);
+ case IDEFLOPPY_IOCTL_FORMAT_START:
+ if (!(file->f_mode & 2))
+ return -EPERM;
+ return ide_floppy_format_unit(drive, (int __user *)argp);
+ case IDEFLOPPY_IOCTL_FORMAT_GET_PROGRESS:
+ return ide_floppy_get_format_progress(drive, argp);
+ default:
+ return -ENOTTY;
+ }
+}
+
+int ide_floppy_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct block_device *bdev = inode->i_bdev;
+ struct ide_floppy_obj *floppy = ide_drv_g(bdev->bd_disk,
+ ide_floppy_obj);
+ ide_drive_t *drive = floppy->drive;
+ struct ide_atapi_pc pc;
+ void __user *argp = (void __user *)arg;
+ int err;
+
+ if (cmd == CDROMEJECT || cmd == CDROM_LOCKDOOR)
+ return ide_floppy_lockdoor(drive, &pc, arg, cmd);
+
+ err = ide_floppy_format_ioctl(drive, file, cmd, argp);
+ if (err != -ENOTTY)
+ return err;
+
+ /*
+ * skip SCSI_IOCTL_SEND_COMMAND (deprecated)
+ * and CDROM_SEND_PACKET (legacy) ioctls
+ */
+ if (cmd != CDROM_SEND_PACKET && cmd != SCSI_IOCTL_SEND_COMMAND)
+ err = scsi_cmd_ioctl(file, bdev->bd_disk->queue,
+ bdev->bd_disk, cmd, argp);
+
+ if (err == -ENOTTY)
+ err = generic_ide_ioctl(drive, file, bdev, cmd, arg);
+
+ return err;
+}
diff --git a/drivers/ide/ide-floppy_proc.c b/drivers/ide/ide-floppy_proc.c
new file mode 100644
index 0000000..76f0c6c
--- /dev/null
+++ b/drivers/ide/ide-floppy_proc.c
@@ -0,0 +1,33 @@
+#include <linux/kernel.h>
+#include <linux/ide.h>
+
+#include "ide-floppy.h"
+
+static int proc_idefloppy_read_capacity(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ ide_drive_t*drive = (ide_drive_t *)data;
+ int len;
+
+ len = sprintf(page, "%llu\n", (long long)ide_floppy_capacity(drive));
+ PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
+}
+
+ide_proc_entry_t ide_floppy_proc[] = {
+ { "capacity", S_IFREG|S_IRUGO, proc_idefloppy_read_capacity, NULL },
+ { "geometry", S_IFREG|S_IRUGO, proc_ide_read_geometry, NULL },
+ { NULL, 0, NULL, NULL }
+};
+
+ide_devset_rw_field(bios_cyl, bios_cyl);
+ide_devset_rw_field(bios_head, bios_head);
+ide_devset_rw_field(bios_sect, bios_sect);
+ide_devset_rw_field(ticks, pc_delay);
+
+const struct ide_proc_devset ide_floppy_settings[] = {
+ IDE_PROC_DEVSET(bios_cyl, 0, 1023),
+ IDE_PROC_DEVSET(bios_head, 0, 255),
+ IDE_PROC_DEVSET(bios_sect, 0, 63),
+ IDE_PROC_DEVSET(ticks, 0, 255),
+ { 0 },
+};
diff --git a/drivers/ide/ide-generic.c b/drivers/ide/ide-generic.c
index 8fe8b5b..81a5282 100644
--- a/drivers/ide/ide-generic.c
+++ b/drivers/ide/ide-generic.c
@@ -19,6 +19,7 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/ide.h>
+#include <linux/pci_ids.h>
/* FIXME: convert m32r to use ide_platform host driver */
#ifdef CONFIG_M32R
@@ -27,7 +28,7 @@
#define DRV_NAME "ide_generic"
-static int probe_mask = 0x03;
+static int probe_mask;
module_param(probe_mask, int, 0);
MODULE_PARM_DESC(probe_mask, "probe mask for legacy ISA IDE ports");
@@ -100,27 +101,64 @@
static const int legacy_irqs[] = { 14, 15, 11, 10, 8, 12 };
#endif
+static void ide_generic_check_pci_legacy_iobases(int *primary, int *secondary)
+{
+ struct pci_dev *p = NULL;
+ u16 val;
+
+ for_each_pci_dev(p) {
+
+ if (pci_resource_start(p, 0) == 0x1f0)
+ *primary = 1;
+ if (pci_resource_start(p, 2) == 0x170)
+ *secondary = 1;
+
+ /* Cyrix CS55{1,2}0 pre SFF MWDMA ATA on the bridge */
+ if (p->vendor == PCI_VENDOR_ID_CYRIX &&
+ (p->device == PCI_DEVICE_ID_CYRIX_5510 ||
+ p->device == PCI_DEVICE_ID_CYRIX_5520))
+ *primary = *secondary = 1;
+
+ /* Intel MPIIX - PIO ATA on non PCI side of bridge */
+ if (p->vendor == PCI_VENDOR_ID_INTEL &&
+ p->device == PCI_DEVICE_ID_INTEL_82371MX) {
+
+ pci_read_config_word(p, 0x6C, &val);
+ if (val & 0x8000) {
+ /* ATA port enabled */
+ if (val & 0x4000)
+ *secondary = 1;
+ else
+ *primary = 1;
+ }
+ }
+ }
+}
+
static int __init ide_generic_init(void)
{
- hw_regs_t hw[MAX_HWIFS], *hws[MAX_HWIFS];
- struct ide_host *host;
+ hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
unsigned long io_addr;
- int i, rc;
+ int i, rc = 0, primary = 0, secondary = 0;
-#ifdef CONFIG_MIPS
- if (!ide_probe_legacy())
- return -ENODEV;
-#endif
- printk(KERN_INFO DRV_NAME ": please use \"probe_mask=0x3f\" module "
- "parameter for probing all legacy ISA IDE ports\n");
+ ide_generic_check_pci_legacy_iobases(&primary, &secondary);
- memset(hws, 0, sizeof(hw_regs_t *) * MAX_HWIFS);
+ if (!probe_mask) {
+ printk(KERN_INFO DRV_NAME ": please use \"probe_mask=0x3f\" "
+ "module parameter for probing all legacy ISA IDE ports\n");
+
+ if (primary == 0)
+ probe_mask |= 0x1;
+
+ if (secondary == 0)
+ probe_mask |= 0x2;
+ } else
+ printk(KERN_INFO DRV_NAME ": enforcing probing of I/O ports "
+ "upon user request\n");
for (i = 0; i < ARRAY_SIZE(legacy_bases); i++) {
io_addr = legacy_bases[i];
- hws[i] = NULL;
-
if ((probe_mask & (1 << i)) && io_addr) {
if (!request_region(io_addr, 8, DRV_NAME)) {
printk(KERN_ERR "%s: I/O resource 0x%lX-0x%lX "
@@ -137,45 +175,27 @@
continue;
}
- memset(&hw[i], 0, sizeof(hw[i]));
- ide_std_init_ports(&hw[i], io_addr, io_addr + 0x206);
+ memset(&hw, 0, sizeof(hw));
+ ide_std_init_ports(&hw, io_addr, io_addr + 0x206);
#ifdef CONFIG_IA64
- hw[i].irq = isa_irq_to_vector(legacy_irqs[i]);
+ hw.irq = isa_irq_to_vector(legacy_irqs[i]);
#else
- hw[i].irq = legacy_irqs[i];
+ hw.irq = legacy_irqs[i];
#endif
- hw[i].chipset = ide_generic;
+ hw.chipset = ide_generic;
- hws[i] = &hw[i];
+ rc = ide_host_add(NULL, hws, NULL);
+ if (rc) {
+ release_region(io_addr + 0x206, 1);
+ release_region(io_addr, 8);
+ }
}
}
- host = ide_host_alloc_all(NULL, hws);
- if (host == NULL) {
- rc = -ENOMEM;
- goto err;
- }
-
- rc = ide_host_register(host, NULL, hws);
- if (rc)
- goto err_free;
-
if (ide_generic_sysfs_init())
printk(KERN_ERR DRV_NAME ": failed to create ide_generic "
"class\n");
- return 0;
-err_free:
- ide_host_free(host);
-err:
- for (i = 0; i < MAX_HWIFS; i++) {
- if (hws[i] == NULL)
- continue;
-
- io_addr = hws[i]->io_ports.data_addr;
- release_region(io_addr + 0x206, 1);
- release_region(io_addr, 8);
- }
return rc;
}
diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c
index a896a28..77c6eae 100644
--- a/drivers/ide/ide-io.c
+++ b/drivers/ide/ide-io.c
@@ -40,6 +40,7 @@
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/ide.h>
+#include <linux/hdreg.h>
#include <linux/completion.h>
#include <linux/reboot.h>
#include <linux/cdrom.h>
@@ -77,8 +78,9 @@
* decide whether to reenable DMA -- 3 is a random magic for now,
* if we DMA timeout more than 3 times, just stay in PIO
*/
- if (drive->state == DMA_PIO_RETRY && drive->retry_pio <= 3) {
- drive->state = 0;
+ if ((drive->dev_flags & IDE_DFLAG_DMA_PIO_RETRY) &&
+ drive->retry_pio <= 3) {
+ drive->dev_flags &= ~IDE_DFLAG_DMA_PIO_RETRY;
ide_dma_on(drive);
}
@@ -130,21 +132,6 @@
}
EXPORT_SYMBOL(ide_end_request);
-/*
- * Power Management state machine. This one is rather trivial for now,
- * we should probably add more, like switching back to PIO on suspend
- * to help some BIOSes, re-do the door locking on resume, etc...
- */
-
-enum {
- ide_pm_flush_cache = ide_pm_state_start_suspend,
- idedisk_pm_standby,
-
- idedisk_pm_restore_pio = ide_pm_state_start_resume,
- idedisk_pm_idle,
- ide_pm_restore_dma,
-};
-
static void ide_complete_power_step(ide_drive_t *drive, struct request *rq, u8 stat, u8 error)
{
struct request_pm_state *pm = rq->data;
@@ -153,20 +140,20 @@
return;
switch (pm->pm_step) {
- case ide_pm_flush_cache: /* Suspend step 1 (flush cache) complete */
+ case IDE_PM_FLUSH_CACHE: /* Suspend step 1 (flush cache) */
if (pm->pm_state == PM_EVENT_FREEZE)
- pm->pm_step = ide_pm_state_completed;
+ pm->pm_step = IDE_PM_COMPLETED;
else
- pm->pm_step = idedisk_pm_standby;
+ pm->pm_step = IDE_PM_STANDBY;
break;
- case idedisk_pm_standby: /* Suspend step 2 (standby) complete */
- pm->pm_step = ide_pm_state_completed;
+ case IDE_PM_STANDBY: /* Suspend step 2 (standby) */
+ pm->pm_step = IDE_PM_COMPLETED;
break;
- case idedisk_pm_restore_pio: /* Resume step 1 complete */
- pm->pm_step = idedisk_pm_idle;
+ case IDE_PM_RESTORE_PIO: /* Resume step 1 (restore PIO) */
+ pm->pm_step = IDE_PM_IDLE;
break;
- case idedisk_pm_idle: /* Resume step 2 (idle) complete */
- pm->pm_step = ide_pm_restore_dma;
+ case IDE_PM_IDLE: /* Resume step 2 (idle)*/
+ pm->pm_step = IDE_PM_RESTORE_DMA;
break;
}
}
@@ -179,40 +166,37 @@
memset(args, 0, sizeof(*args));
switch (pm->pm_step) {
- case ide_pm_flush_cache: /* Suspend step 1 (flush cache) */
+ case IDE_PM_FLUSH_CACHE: /* Suspend step 1 (flush cache) */
if (drive->media != ide_disk)
break;
/* Not supported? Switch to next step now. */
- if (!drive->wcache || !ide_id_has_flush_cache(drive->id)) {
+ if (ata_id_flush_enabled(drive->id) == 0 ||
+ (drive->dev_flags & IDE_DFLAG_WCACHE) == 0) {
ide_complete_power_step(drive, rq, 0, 0);
return ide_stopped;
}
- if (ide_id_has_flush_cache_ext(drive->id))
- args->tf.command = WIN_FLUSH_CACHE_EXT;
+ if (ata_id_flush_ext_enabled(drive->id))
+ args->tf.command = ATA_CMD_FLUSH_EXT;
else
- args->tf.command = WIN_FLUSH_CACHE;
+ args->tf.command = ATA_CMD_FLUSH;
goto out_do_tf;
-
- case idedisk_pm_standby: /* Suspend step 2 (standby) */
- args->tf.command = WIN_STANDBYNOW1;
+ case IDE_PM_STANDBY: /* Suspend step 2 (standby) */
+ args->tf.command = ATA_CMD_STANDBYNOW1;
goto out_do_tf;
-
- case idedisk_pm_restore_pio: /* Resume step 1 (restore PIO) */
+ case IDE_PM_RESTORE_PIO: /* Resume step 1 (restore PIO) */
ide_set_max_pio(drive);
/*
- * skip idedisk_pm_idle for ATAPI devices
+ * skip IDE_PM_IDLE for ATAPI devices
*/
if (drive->media != ide_disk)
- pm->pm_step = ide_pm_restore_dma;
+ pm->pm_step = IDE_PM_RESTORE_DMA;
else
ide_complete_power_step(drive, rq, 0, 0);
return ide_stopped;
-
- case idedisk_pm_idle: /* Resume step 2 (idle) */
- args->tf.command = WIN_IDLEIMMEDIATE;
+ case IDE_PM_IDLE: /* Resume step 2 (idle) */
+ args->tf.command = ATA_CMD_IDLEIMMEDIATE;
goto out_do_tf;
-
- case ide_pm_restore_dma: /* Resume step 3 (restore DMA) */
+ case IDE_PM_RESTORE_DMA: /* Resume step 3 (restore DMA) */
/*
* Right now, all we do is call ide_set_dma(drive),
* we could be smarter and check for current xfer_speed
@@ -221,12 +205,13 @@
if (drive->hwif->dma_ops == NULL)
break;
/*
- * TODO: respect ->using_dma setting
+ * TODO: respect IDE_DFLAG_USING_DMA
*/
ide_set_dma(drive);
break;
}
- pm->pm_step = ide_pm_state_completed;
+
+ pm->pm_step = IDE_PM_COMPLETED;
return ide_stopped;
out_do_tf:
@@ -286,7 +271,7 @@
if (blk_pm_suspend_request(rq)) {
blk_stop_queue(drive->queue);
} else {
- drive->blocked = 0;
+ drive->dev_flags &= ~IDE_DFLAG_BLOCKED;
blk_start_queue(drive->queue);
}
HWGROUP(drive)->rq = NULL;
@@ -322,7 +307,7 @@
ide_task_t *task = (ide_task_t *)rq->special;
if (rq->errors == 0)
- rq->errors = !OK_STAT(stat, READY_STAT, BAD_STAT);
+ rq->errors = !OK_STAT(stat, ATA_DRDY, BAD_STAT);
if (task) {
struct ide_taskfile *tf = &task->tf;
@@ -342,7 +327,7 @@
drive->name, rq->pm->pm_step, stat, err);
#endif
ide_complete_power_step(drive, rq, stat, err);
- if (pm->pm_step == ide_pm_state_completed)
+ if (pm->pm_step == IDE_PM_COMPLETED)
ide_complete_pm_request(drive, rq);
return;
}
@@ -373,29 +358,30 @@
{
ide_hwif_t *hwif = drive->hwif;
- if (stat & BUSY_STAT || ((stat & WRERR_STAT) && !drive->nowerr)) {
+ if ((stat & ATA_BUSY) ||
+ ((stat & ATA_DF) && (drive->dev_flags & IDE_DFLAG_NOWERR) == 0)) {
/* other bits are useless when BUSY */
rq->errors |= ERROR_RESET;
- } else if (stat & ERR_STAT) {
+ } else if (stat & ATA_ERR) {
/* err has different meaning on cdrom and tape */
- if (err == ABRT_ERR) {
- if (drive->select.b.lba &&
- /* some newer drives don't support WIN_SPECIFY */
- hwif->tp_ops->read_status(hwif) == WIN_SPECIFY)
+ if (err == ATA_ABORTED) {
+ if ((drive->dev_flags & IDE_DFLAG_LBA) &&
+ /* some newer drives don't support ATA_CMD_INIT_DEV_PARAMS */
+ hwif->tp_ops->read_status(hwif) == ATA_CMD_INIT_DEV_PARAMS)
return ide_stopped;
} else if ((err & BAD_CRC) == BAD_CRC) {
/* UDMA crc error, just retry the operation */
drive->crc_count++;
- } else if (err & (BBD_ERR | ECC_ERR)) {
+ } else if (err & (ATA_BBK | ATA_UNC)) {
/* retries won't help these */
rq->errors = ERROR_MAX;
- } else if (err & TRK0_ERR) {
+ } else if (err & ATA_TRK0NF) {
/* help it find track zero */
rq->errors |= ERROR_RECAL;
}
}
- if ((stat & DRQ_STAT) && rq_data_dir(rq) == READ &&
+ if ((stat & ATA_DRQ) && rq_data_dir(rq) == READ &&
(hwif->host_flags & IDE_HFLAG_ERROR_STOPS_FIFO) == 0) {
int nsect = drive->mult_count ? drive->mult_count : 1;
@@ -407,7 +393,7 @@
return ide_stopped;
}
- if (hwif->tp_ops->read_status(hwif) & (BUSY_STAT | DRQ_STAT))
+ if (hwif->tp_ops->read_status(hwif) & (ATA_BUSY | ATA_DRQ))
rq->errors |= ERROR_RESET;
if ((rq->errors & ERROR_RESET) == ERROR_RESET) {
@@ -427,16 +413,17 @@
{
ide_hwif_t *hwif = drive->hwif;
- if (stat & BUSY_STAT || ((stat & WRERR_STAT) && !drive->nowerr)) {
+ if ((stat & ATA_BUSY) ||
+ ((stat & ATA_DF) && (drive->dev_flags & IDE_DFLAG_NOWERR) == 0)) {
/* other bits are useless when BUSY */
rq->errors |= ERROR_RESET;
} else {
/* add decoding error stuff */
}
- if (hwif->tp_ops->read_status(hwif) & (BUSY_STAT | DRQ_STAT))
+ if (hwif->tp_ops->read_status(hwif) & (ATA_BUSY | ATA_DRQ))
/* force an abort */
- hwif->tp_ops->exec_command(hwif, WIN_IDLEIMMEDIATE);
+ hwif->tp_ops->exec_command(hwif, ATA_CMD_IDLEIMMEDIATE);
if (rq->errors >= ERROR_MAX) {
ide_kill_rq(drive, rq);
@@ -508,20 +495,20 @@
tf->lbal = drive->sect;
tf->lbam = drive->cyl;
tf->lbah = drive->cyl >> 8;
- tf->device = ((drive->head - 1) | drive->select.all) & ~ATA_LBA;
- tf->command = WIN_SPECIFY;
+ tf->device = (drive->head - 1) | drive->select;
+ tf->command = ATA_CMD_INIT_DEV_PARAMS;
}
static void ide_tf_set_restore_cmd(ide_drive_t *drive, struct ide_taskfile *tf)
{
tf->nsect = drive->sect;
- tf->command = WIN_RESTORE;
+ tf->command = ATA_CMD_RESTORE;
}
static void ide_tf_set_setmult_cmd(ide_drive_t *drive, struct ide_taskfile *tf)
{
tf->nsect = drive->mult_req;
- tf->command = WIN_SETMULT;
+ tf->command = ATA_CMD_SET_MULTI;
}
static ide_startstop_t ide_disk_special(ide_drive_t *drive)
@@ -540,8 +527,6 @@
ide_tf_set_restore_cmd(drive, &args.tf);
} else if (s->b.set_multmode) {
s->b.set_multmode = 0;
- if (drive->mult_req > drive->id->max_multsect)
- drive->mult_req = drive->id->max_multsect;
ide_tf_set_setmult_cmd(drive, &args.tf);
} else if (s->all) {
int special = s->all;
@@ -558,37 +543,14 @@
return ide_started;
}
-/*
- * handle HDIO_SET_PIO_MODE ioctl abusers here, eventually it will go away
- */
-static int set_pio_mode_abuse(ide_hwif_t *hwif, u8 req_pio)
-{
- switch (req_pio) {
- case 202:
- case 201:
- case 200:
- case 102:
- case 101:
- case 100:
- return (hwif->host_flags & IDE_HFLAG_ABUSE_DMA_MODES) ? 1 : 0;
- case 9:
- case 8:
- return (hwif->host_flags & IDE_HFLAG_ABUSE_PREFETCH) ? 1 : 0;
- case 7:
- case 6:
- return (hwif->host_flags & IDE_HFLAG_ABUSE_FAST_DEVSEL) ? 1 : 0;
- default:
- return 0;
- }
-}
-
/**
* do_special - issue some special commands
* @drive: drive the command is for
*
- * do_special() is used to issue WIN_SPECIFY, WIN_RESTORE, and WIN_SETMULT
- * commands to a drive. It used to do much more, but has been scaled
- * back.
+ * do_special() is used to issue ATA_CMD_INIT_DEV_PARAMS,
+ * ATA_CMD_RESTORE and ATA_CMD_SET_MULTI commands to a drive.
+ *
+ * It used to do much more, but has been scaled back.
*/
static ide_startstop_t do_special (ide_drive_t *drive)
@@ -598,45 +560,12 @@
#ifdef DEBUG
printk("%s: do_special: 0x%02x\n", drive->name, s->all);
#endif
- if (s->b.set_tune) {
- ide_hwif_t *hwif = drive->hwif;
- const struct ide_port_ops *port_ops = hwif->port_ops;
- u8 req_pio = drive->tune_req;
+ if (drive->media == ide_disk)
+ return ide_disk_special(drive);
- s->b.set_tune = 0;
-
- if (set_pio_mode_abuse(drive->hwif, req_pio)) {
- /*
- * take ide_lock for drive->[no_]unmask/[no_]io_32bit
- */
- if (req_pio == 8 || req_pio == 9) {
- unsigned long flags;
-
- spin_lock_irqsave(&ide_lock, flags);
- port_ops->set_pio_mode(drive, req_pio);
- spin_unlock_irqrestore(&ide_lock, flags);
- } else
- port_ops->set_pio_mode(drive, req_pio);
- } else {
- int keep_dma = drive->using_dma;
-
- ide_set_pio(drive, req_pio);
-
- if (hwif->host_flags & IDE_HFLAG_SET_PIO_MODE_KEEP_DMA) {
- if (keep_dma)
- ide_dma_on(drive);
- }
- }
-
- return ide_stopped;
- } else {
- if (drive->media == ide_disk)
- return ide_disk_special(drive);
-
- s->all = 0;
- drive->mult_req = 0;
- return ide_stopped;
- }
+ s->all = 0;
+ drive->mult_req = 0;
+ return ide_stopped;
}
void ide_map_sg(ide_drive_t *drive, struct request *rq)
@@ -716,9 +645,71 @@
return ide_stopped;
}
+int ide_devset_execute(ide_drive_t *drive, const struct ide_devset *setting,
+ int arg)
+{
+ struct request_queue *q = drive->queue;
+ struct request *rq;
+ int ret = 0;
+
+ if (!(setting->flags & DS_SYNC))
+ return setting->set(drive, arg);
+
+ rq = blk_get_request(q, READ, __GFP_WAIT);
+ rq->cmd_type = REQ_TYPE_SPECIAL;
+ rq->cmd_len = 5;
+ rq->cmd[0] = REQ_DEVSET_EXEC;
+ *(int *)&rq->cmd[1] = arg;
+ rq->special = setting->set;
+
+ if (blk_execute_rq(q, NULL, rq, 0))
+ ret = rq->errors;
+ blk_put_request(rq);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ide_devset_execute);
+
static ide_startstop_t ide_special_rq(ide_drive_t *drive, struct request *rq)
{
- switch (rq->cmd[0]) {
+ u8 cmd = rq->cmd[0];
+
+ if (cmd == REQ_PARK_HEADS || cmd == REQ_UNPARK_HEADS) {
+ ide_task_t task;
+ struct ide_taskfile *tf = &task.tf;
+
+ memset(&task, 0, sizeof(task));
+ if (cmd == REQ_PARK_HEADS) {
+ drive->sleep = *(unsigned long *)rq->special;
+ drive->dev_flags |= IDE_DFLAG_SLEEPING;
+ tf->command = ATA_CMD_IDLEIMMEDIATE;
+ tf->feature = 0x44;
+ tf->lbal = 0x4c;
+ tf->lbam = 0x4e;
+ tf->lbah = 0x55;
+ task.tf_flags |= IDE_TFLAG_CUSTOM_HANDLER;
+ } else /* cmd == REQ_UNPARK_HEADS */
+ tf->command = ATA_CMD_CHK_POWER;
+
+ task.tf_flags |= IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
+ task.rq = rq;
+ drive->hwif->data_phase = task.data_phase = TASKFILE_NO_DATA;
+ return do_rw_taskfile(drive, &task);
+ }
+
+ switch (cmd) {
+ case REQ_DEVSET_EXEC:
+ {
+ int err, (*setfunc)(ide_drive_t *, int) = rq->special;
+
+ err = setfunc(drive, *(int *)&rq->cmd[1]);
+ if (err)
+ rq->errors = err;
+ else
+ err = 1;
+ ide_end_request(drive, err, 0);
+ return ide_stopped;
+ }
case REQ_DRIVE_RESET:
return ide_do_reset(drive);
default:
@@ -733,11 +724,11 @@
struct request_pm_state *pm = rq->data;
if (blk_pm_suspend_request(rq) &&
- pm->pm_step == ide_pm_state_start_suspend)
+ pm->pm_step == IDE_PM_START_SUSPEND)
/* Mark drive blocked when starting the suspend sequence. */
- drive->blocked = 1;
+ drive->dev_flags |= IDE_DFLAG_BLOCKED;
else if (blk_pm_resume_request(rq) &&
- pm->pm_step == ide_pm_state_start_resume) {
+ pm->pm_step == IDE_PM_START_RESUME) {
/*
* The first thing we do on wakeup is to wait for BSY bit to
* go away (with a looong timeout) as a drive on this hwif may
@@ -766,9 +757,7 @@
* start_request - start of I/O and command issuing for IDE
*
* start_request() initiates handling of a new I/O request. It
- * accepts commands and I/O (read/write) requests. It also does
- * the final remapping for weird stuff like EZDrive. Once
- * device mapper can work sector level the EZDrive stuff can go away
+ * accepts commands and I/O (read/write) requests.
*
* FIXME: this function needs a rename
*/
@@ -776,7 +765,6 @@
static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq)
{
ide_startstop_t startstop;
- sector_t block;
BUG_ON(!blk_rq_started(rq));
@@ -791,21 +779,12 @@
goto kill_rq;
}
- block = rq->sector;
- if (blk_fs_request(rq) &&
- (drive->media == ide_disk || drive->media == ide_floppy)) {
- block += drive->sect0;
- }
- /* Yecch - this will shift the entire interval,
- possibly killing some innocent following sector */
- if (block == 0 && drive->remap_0_to_1 == 1)
- block = 1; /* redirect MBR access to EZ-Drive partn table */
-
if (blk_pm_request(rq))
ide_check_pm_state(drive, rq);
SELECT_DRIVE(drive);
- if (ide_wait_stat(&startstop, drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) {
+ if (ide_wait_stat(&startstop, drive, drive->ready_stat,
+ ATA_BUSY | ATA_DRQ, WAIT_READY)) {
printk(KERN_ERR "%s: drive not ready for command\n", drive->name);
return startstop;
}
@@ -829,7 +808,7 @@
#endif
startstop = ide_start_power_step(drive, rq);
if (startstop == ide_stopped &&
- pm->pm_step == ide_pm_state_completed)
+ pm->pm_step == IDE_PM_COMPLETED)
ide_complete_pm_request(drive, rq);
return startstop;
} else if (!rq->rq_disk && blk_special_request(rq))
@@ -844,7 +823,8 @@
return ide_special_rq(drive, rq);
drv = *(ide_driver_t **)rq->rq_disk->private_data;
- return drv->do_request(drive, rq, block);
+
+ return drv->do_request(drive, rq, rq->sector);
}
return do_special(drive);
kill_rq:
@@ -866,7 +846,7 @@
if (timeout > WAIT_WORSTCASE)
timeout = WAIT_WORSTCASE;
drive->sleep = timeout + jiffies;
- drive->sleeping = 1;
+ drive->dev_flags |= IDE_DFLAG_SLEEPING;
}
EXPORT_SYMBOL(ide_stall_queue);
@@ -906,18 +886,23 @@
}
do {
- if ((!drive->sleeping || time_after_eq(jiffies, drive->sleep))
- && !elv_queue_empty(drive->queue)) {
- if (!best
- || (drive->sleeping && (!best->sleeping || time_before(drive->sleep, best->sleep)))
- || (!best->sleeping && time_before(WAKEUP(drive), WAKEUP(best))))
- {
+ u8 dev_s = !!(drive->dev_flags & IDE_DFLAG_SLEEPING);
+ u8 best_s = (best && !!(best->dev_flags & IDE_DFLAG_SLEEPING));
+
+ if ((dev_s == 0 || time_after_eq(jiffies, drive->sleep)) &&
+ !elv_queue_empty(drive->queue)) {
+ if (best == NULL ||
+ (dev_s && (best_s == 0 || time_before(drive->sleep, best->sleep))) ||
+ (best_s == 0 && time_before(WAKEUP(drive), WAKEUP(best)))) {
if (!blk_queue_plugged(drive->queue))
best = drive;
}
}
} while ((drive = drive->next) != hwgroup->drive);
- if (best && best->nice1 && !best->sleeping && best != hwgroup->drive && best->service_time > WAIT_MIN_SLEEP) {
+
+ if (best && (best->dev_flags & IDE_DFLAG_NICE1) &&
+ (best->dev_flags & IDE_DFLAG_SLEEPING) == 0 &&
+ best != hwgroup->drive && best->service_time > WAIT_MIN_SLEEP) {
long t = (signed long)(WAKEUP(best) - jiffies);
if (t >= WAIT_MIN_SLEEP) {
/*
@@ -926,7 +911,7 @@
*/
drive = best->next;
do {
- if (!drive->sleeping
+ if ((drive->dev_flags & IDE_DFLAG_SLEEPING) == 0
&& time_before(jiffies - best->service_time, WAKEUP(drive))
&& time_before(WAKEUP(drive), jiffies + t))
{
@@ -997,7 +982,9 @@
hwgroup->rq = NULL;
drive = hwgroup->drive;
do {
- if (drive->sleeping && (!sleeping || time_before(drive->sleep, sleep))) {
+ if ((drive->dev_flags & IDE_DFLAG_SLEEPING) &&
+ (sleeping == 0 ||
+ time_before(drive->sleep, sleep))) {
sleeping = 1;
sleep = drive->sleep;
}
@@ -1046,7 +1033,7 @@
}
hwgroup->hwif = hwif;
hwgroup->drive = drive;
- drive->sleeping = 0;
+ drive->dev_flags &= ~(IDE_DFLAG_SLEEPING | IDE_DFLAG_PARKED);
drive->service_start = jiffies;
if (blk_queue_plugged(drive->queue)) {
@@ -1080,7 +1067,9 @@
* We count how many times we loop here to make sure we service
* all drives in the hwgroup without looping for ever
*/
- if (drive->blocked && !blk_pm_request(rq) && !(rq->cmd_flags & REQ_PREEMPT)) {
+ if ((drive->dev_flags & IDE_DFLAG_BLOCKED) &&
+ blk_pm_request(rq) == 0 &&
+ (rq->cmd_flags & REQ_PREEMPT) == 0) {
drive = drive->next ? drive->next : hwgroup->drive;
if (loops++ < 4 && !blk_queue_plugged(drive->queue))
goto again;
@@ -1153,8 +1142,8 @@
* a timeout -- we'll reenable after we finish this next request
* (or rather the first chunk of it) in pio.
*/
+ drive->dev_flags |= IDE_DFLAG_DMA_PIO_RETRY;
drive->retry_pio++;
- drive->state = DMA_PIO_RETRY;
ide_dma_off_quietly(drive);
/*
@@ -1325,7 +1314,7 @@
if (hwif->irq == irq) {
stat = hwif->tp_ops->read_status(hwif);
- if (!OK_STAT(stat, READY_STAT, BAD_STAT)) {
+ if (!OK_STAT(stat, ATA_DRDY, BAD_STAT)) {
/* Try to not flood the console with msgs */
static unsigned long last_msgtime, count;
++count;
@@ -1451,23 +1440,16 @@
del_timer(&hwgroup->timer);
spin_unlock(&ide_lock);
- /* Some controllers might set DMA INTR no matter DMA or PIO;
- * bmdma status might need to be cleared even for
- * PIO interrupts to prevent spurious/lost irq.
- */
- if (hwif->ide_dma_clear_irq && !(drive->waiting_for_dma))
- /* ide_dma_end() needs bmdma status for error checking.
- * So, skip clearing bmdma status here and leave it
- * to ide_dma_end() if this is dma interrupt.
- */
- hwif->ide_dma_clear_irq(drive);
+ if (hwif->port_ops && hwif->port_ops->clear_irq)
+ hwif->port_ops->clear_irq(drive);
- if (drive->unmask)
+ if (drive->dev_flags & IDE_DFLAG_UNMASK)
local_irq_enable_in_hardirq();
+
/* service this interrupt, may set handler for next interrupt */
startstop = handler(drive);
- spin_lock_irq(&ide_lock);
+ spin_lock_irq(&ide_lock);
/*
* Note that handler() may have set things up for another
* interrupt to occur soon, but it cannot happen until
diff --git a/drivers/ide/ide-ioctls.c b/drivers/ide/ide-ioctls.c
new file mode 100644
index 0000000..a90945f
--- /dev/null
+++ b/drivers/ide/ide-ioctls.c
@@ -0,0 +1,299 @@
+/*
+ * IDE ioctls handling.
+ */
+
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+static const struct ide_ioctl_devset ide_ioctl_settings[] = {
+{ HDIO_GET_32BIT, HDIO_SET_32BIT, &ide_devset_io_32bit },
+{ HDIO_GET_KEEPSETTINGS, HDIO_SET_KEEPSETTINGS, &ide_devset_keepsettings },
+{ HDIO_GET_UNMASKINTR, HDIO_SET_UNMASKINTR, &ide_devset_unmaskirq },
+{ HDIO_GET_DMA, HDIO_SET_DMA, &ide_devset_using_dma },
+{ -1, HDIO_SET_PIO_MODE, &ide_devset_pio_mode },
+{ 0 }
+};
+
+int ide_setting_ioctl(ide_drive_t *drive, struct block_device *bdev,
+ unsigned int cmd, unsigned long arg,
+ const struct ide_ioctl_devset *s)
+{
+ const struct ide_devset *ds;
+ unsigned long flags;
+ int err = -EOPNOTSUPP;
+
+ for (; (ds = s->setting); s++) {
+ if (ds->get && s->get_ioctl == cmd)
+ goto read_val;
+ else if (ds->set && s->set_ioctl == cmd)
+ goto set_val;
+ }
+
+ return err;
+
+read_val:
+ mutex_lock(&ide_setting_mtx);
+ spin_lock_irqsave(&ide_lock, flags);
+ err = ds->get(drive);
+ spin_unlock_irqrestore(&ide_lock, flags);
+ mutex_unlock(&ide_setting_mtx);
+ return err >= 0 ? put_user(err, (long __user *)arg) : err;
+
+set_val:
+ if (bdev != bdev->bd_contains)
+ err = -EINVAL;
+ else {
+ if (!capable(CAP_SYS_ADMIN))
+ err = -EACCES;
+ else {
+ mutex_lock(&ide_setting_mtx);
+ err = ide_devset_execute(drive, ds, arg);
+ mutex_unlock(&ide_setting_mtx);
+ }
+ }
+ return err;
+}
+EXPORT_SYMBOL_GPL(ide_setting_ioctl);
+
+static int ide_get_identity_ioctl(ide_drive_t *drive, unsigned int cmd,
+ unsigned long arg)
+{
+ u16 *id = NULL;
+ int size = (cmd == HDIO_GET_IDENTITY) ? (ATA_ID_WORDS * 2) : 142;
+ int rc = 0;
+
+ if ((drive->dev_flags & IDE_DFLAG_ID_READ) == 0) {
+ rc = -ENOMSG;
+ goto out;
+ }
+
+ id = kmalloc(size, GFP_KERNEL);
+ if (id == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(id, drive->id, size);
+ ata_id_to_hd_driveid(id);
+
+ if (copy_to_user((void __user *)arg, id, size))
+ rc = -EFAULT;
+
+ kfree(id);
+out:
+ return rc;
+}
+
+static int ide_get_nice_ioctl(ide_drive_t *drive, unsigned long arg)
+{
+ return put_user((!!(drive->dev_flags & IDE_DFLAG_DSC_OVERLAP)
+ << IDE_NICE_DSC_OVERLAP) |
+ (!!(drive->dev_flags & IDE_DFLAG_NICE1)
+ << IDE_NICE_1), (long __user *)arg);
+}
+
+static int ide_set_nice_ioctl(ide_drive_t *drive, unsigned long arg)
+{
+ if (arg != (arg & ((1 << IDE_NICE_DSC_OVERLAP) | (1 << IDE_NICE_1))))
+ return -EPERM;
+
+ if (((arg >> IDE_NICE_DSC_OVERLAP) & 1) &&
+ (drive->media == ide_disk || drive->media == ide_floppy ||
+ (drive->dev_flags & IDE_DFLAG_SCSI)))
+ return -EPERM;
+
+ if ((arg >> IDE_NICE_DSC_OVERLAP) & 1)
+ drive->dev_flags |= IDE_DFLAG_DSC_OVERLAP;
+ else
+ drive->dev_flags &= ~IDE_DFLAG_DSC_OVERLAP;
+
+ if ((arg >> IDE_NICE_1) & 1)
+ drive->dev_flags |= IDE_DFLAG_NICE1;
+ else
+ drive->dev_flags &= ~IDE_DFLAG_NICE1;
+
+ return 0;
+}
+
+static int ide_cmd_ioctl(ide_drive_t *drive, unsigned cmd, unsigned long arg)
+{
+ u8 *buf = NULL;
+ int bufsize = 0, err = 0;
+ u8 args[4], xfer_rate = 0;
+ ide_task_t tfargs;
+ struct ide_taskfile *tf = &tfargs.tf;
+ u16 *id = drive->id;
+
+ if (NULL == (void *) arg) {
+ struct request *rq;
+
+ rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
+ rq->cmd_type = REQ_TYPE_ATA_TASKFILE;
+ err = blk_execute_rq(drive->queue, NULL, rq, 0);
+ blk_put_request(rq);
+
+ return err;
+ }
+
+ if (copy_from_user(args, (void __user *)arg, 4))
+ return -EFAULT;
+
+ memset(&tfargs, 0, sizeof(ide_task_t));
+ tf->feature = args[2];
+ if (args[0] == ATA_CMD_SMART) {
+ tf->nsect = args[3];
+ tf->lbal = args[1];
+ tf->lbam = 0x4f;
+ tf->lbah = 0xc2;
+ tfargs.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_IN_NSECT;
+ } else {
+ tf->nsect = args[1];
+ tfargs.tf_flags = IDE_TFLAG_OUT_FEATURE |
+ IDE_TFLAG_OUT_NSECT | IDE_TFLAG_IN_NSECT;
+ }
+ tf->command = args[0];
+ tfargs.data_phase = args[3] ? TASKFILE_IN : TASKFILE_NO_DATA;
+
+ if (args[3]) {
+ tfargs.tf_flags |= IDE_TFLAG_IO_16BIT;
+ bufsize = SECTOR_SIZE * args[3];
+ buf = kzalloc(bufsize, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+ }
+
+ if (tf->command == ATA_CMD_SET_FEATURES &&
+ tf->feature == SETFEATURES_XFER &&
+ tf->nsect >= XFER_SW_DMA_0 &&
+ (id[ATA_ID_UDMA_MODES] ||
+ id[ATA_ID_MWDMA_MODES] ||
+ id[ATA_ID_SWDMA_MODES])) {
+ xfer_rate = args[1];
+ if (tf->nsect > XFER_UDMA_2 && !eighty_ninty_three(drive)) {
+ printk(KERN_WARNING "%s: UDMA speeds >UDMA33 cannot "
+ "be set\n", drive->name);
+ goto abort;
+ }
+ }
+
+ err = ide_raw_taskfile(drive, &tfargs, buf, args[3]);
+
+ args[0] = tf->status;
+ args[1] = tf->error;
+ args[2] = tf->nsect;
+
+ if (!err && xfer_rate) {
+ /* active-retuning-calls future */
+ ide_set_xfer_rate(drive, xfer_rate);
+ ide_driveid_update(drive);
+ }
+abort:
+ if (copy_to_user((void __user *)arg, &args, 4))
+ err = -EFAULT;
+ if (buf) {
+ if (copy_to_user((void __user *)(arg + 4), buf, bufsize))
+ err = -EFAULT;
+ kfree(buf);
+ }
+ return err;
+}
+
+static int ide_task_ioctl(ide_drive_t *drive, unsigned cmd, unsigned long arg)
+{
+ void __user *p = (void __user *)arg;
+ int err = 0;
+ u8 args[7];
+ ide_task_t task;
+
+ if (copy_from_user(args, p, 7))
+ return -EFAULT;
+
+ memset(&task, 0, sizeof(task));
+ memcpy(&task.tf_array[7], &args[1], 6);
+ task.tf.command = args[0];
+ task.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
+
+ err = ide_no_data_taskfile(drive, &task);
+
+ args[0] = task.tf.command;
+ memcpy(&args[1], &task.tf_array[7], 6);
+
+ if (copy_to_user(p, args, 7))
+ err = -EFAULT;
+
+ return err;
+}
+
+static int generic_drive_reset(ide_drive_t *drive)
+{
+ struct request *rq;
+ int ret = 0;
+
+ rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
+ rq->cmd_type = REQ_TYPE_SPECIAL;
+ rq->cmd_len = 1;
+ rq->cmd[0] = REQ_DRIVE_RESET;
+ rq->cmd_flags |= REQ_SOFTBARRIER;
+ if (blk_execute_rq(drive->queue, NULL, rq, 1))
+ ret = rq->errors;
+ blk_put_request(rq);
+ return ret;
+}
+
+int generic_ide_ioctl(ide_drive_t *drive, struct file *file,
+ struct block_device *bdev,
+ unsigned int cmd, unsigned long arg)
+{
+ int err;
+
+ err = ide_setting_ioctl(drive, bdev, cmd, arg, ide_ioctl_settings);
+ if (err != -EOPNOTSUPP)
+ return err;
+
+ switch (cmd) {
+ case HDIO_OBSOLETE_IDENTITY:
+ case HDIO_GET_IDENTITY:
+ if (bdev != bdev->bd_contains)
+ return -EINVAL;
+ return ide_get_identity_ioctl(drive, cmd, arg);
+ case HDIO_GET_NICE:
+ return ide_get_nice_ioctl(drive, arg);
+ case HDIO_SET_NICE:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ return ide_set_nice_ioctl(drive, arg);
+#ifdef CONFIG_IDE_TASK_IOCTL
+ case HDIO_DRIVE_TASKFILE:
+ if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
+ return -EACCES;
+ if (drive->media == ide_disk)
+ return ide_taskfile_ioctl(drive, cmd, arg);
+ return -ENOMSG;
+#endif
+ case HDIO_DRIVE_CMD:
+ if (!capable(CAP_SYS_RAWIO))
+ return -EACCES;
+ return ide_cmd_ioctl(drive, cmd, arg);
+ case HDIO_DRIVE_TASK:
+ if (!capable(CAP_SYS_RAWIO))
+ return -EACCES;
+ return ide_task_ioctl(drive, cmd, arg);
+ case HDIO_DRIVE_RESET:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ return generic_drive_reset(drive);
+ case HDIO_GET_BUSSTATE:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ if (put_user(BUSSTATE_ON, (long __user *)arg))
+ return -EFAULT;
+ return 0;
+ case HDIO_SET_BUSSTATE:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ return -EOPNOTSUPP;
+ default:
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL(generic_ide_ioctl);
diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c
index 2cbadff..b762deb 100644
--- a/drivers/ide/ide-iops.c
+++ b/drivers/ide/ide-iops.c
@@ -18,7 +18,6 @@
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/delay.h>
-#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/bitops.h>
#include <linux/nmi.h>
@@ -182,7 +181,7 @@
tf_outb(tf->lbah, io_ports->lbah_addr);
if (task->tf_flags & IDE_TFLAG_OUT_DEVICE)
- tf_outb((tf->device & HIHI) | drive->select.all,
+ tf_outb((tf->device & HIHI) | drive->select,
io_ports->device_addr);
}
EXPORT_SYMBOL_GPL(ide_tf_load);
@@ -400,97 +399,14 @@
.output_data = ide_output_data,
};
-void ide_fix_driveid (struct hd_driveid *id)
+void ide_fix_driveid(u16 *id)
{
#ifndef __LITTLE_ENDIAN
# ifdef __BIG_ENDIAN
int i;
- u16 *stringcast;
- id->config = __le16_to_cpu(id->config);
- id->cyls = __le16_to_cpu(id->cyls);
- id->reserved2 = __le16_to_cpu(id->reserved2);
- id->heads = __le16_to_cpu(id->heads);
- id->track_bytes = __le16_to_cpu(id->track_bytes);
- id->sector_bytes = __le16_to_cpu(id->sector_bytes);
- id->sectors = __le16_to_cpu(id->sectors);
- id->vendor0 = __le16_to_cpu(id->vendor0);
- id->vendor1 = __le16_to_cpu(id->vendor1);
- id->vendor2 = __le16_to_cpu(id->vendor2);
- stringcast = (u16 *)&id->serial_no[0];
- for (i = 0; i < (20/2); i++)
- stringcast[i] = __le16_to_cpu(stringcast[i]);
- id->buf_type = __le16_to_cpu(id->buf_type);
- id->buf_size = __le16_to_cpu(id->buf_size);
- id->ecc_bytes = __le16_to_cpu(id->ecc_bytes);
- stringcast = (u16 *)&id->fw_rev[0];
- for (i = 0; i < (8/2); i++)
- stringcast[i] = __le16_to_cpu(stringcast[i]);
- stringcast = (u16 *)&id->model[0];
- for (i = 0; i < (40/2); i++)
- stringcast[i] = __le16_to_cpu(stringcast[i]);
- id->dword_io = __le16_to_cpu(id->dword_io);
- id->reserved50 = __le16_to_cpu(id->reserved50);
- id->field_valid = __le16_to_cpu(id->field_valid);
- id->cur_cyls = __le16_to_cpu(id->cur_cyls);
- id->cur_heads = __le16_to_cpu(id->cur_heads);
- id->cur_sectors = __le16_to_cpu(id->cur_sectors);
- id->cur_capacity0 = __le16_to_cpu(id->cur_capacity0);
- id->cur_capacity1 = __le16_to_cpu(id->cur_capacity1);
- id->lba_capacity = __le32_to_cpu(id->lba_capacity);
- id->dma_1word = __le16_to_cpu(id->dma_1word);
- id->dma_mword = __le16_to_cpu(id->dma_mword);
- id->eide_pio_modes = __le16_to_cpu(id->eide_pio_modes);
- id->eide_dma_min = __le16_to_cpu(id->eide_dma_min);
- id->eide_dma_time = __le16_to_cpu(id->eide_dma_time);
- id->eide_pio = __le16_to_cpu(id->eide_pio);
- id->eide_pio_iordy = __le16_to_cpu(id->eide_pio_iordy);
- for (i = 0; i < 2; ++i)
- id->words69_70[i] = __le16_to_cpu(id->words69_70[i]);
- for (i = 0; i < 4; ++i)
- id->words71_74[i] = __le16_to_cpu(id->words71_74[i]);
- id->queue_depth = __le16_to_cpu(id->queue_depth);
- for (i = 0; i < 4; ++i)
- id->words76_79[i] = __le16_to_cpu(id->words76_79[i]);
- id->major_rev_num = __le16_to_cpu(id->major_rev_num);
- id->minor_rev_num = __le16_to_cpu(id->minor_rev_num);
- id->command_set_1 = __le16_to_cpu(id->command_set_1);
- id->command_set_2 = __le16_to_cpu(id->command_set_2);
- id->cfsse = __le16_to_cpu(id->cfsse);
- id->cfs_enable_1 = __le16_to_cpu(id->cfs_enable_1);
- id->cfs_enable_2 = __le16_to_cpu(id->cfs_enable_2);
- id->csf_default = __le16_to_cpu(id->csf_default);
- id->dma_ultra = __le16_to_cpu(id->dma_ultra);
- id->trseuc = __le16_to_cpu(id->trseuc);
- id->trsEuc = __le16_to_cpu(id->trsEuc);
- id->CurAPMvalues = __le16_to_cpu(id->CurAPMvalues);
- id->mprc = __le16_to_cpu(id->mprc);
- id->hw_config = __le16_to_cpu(id->hw_config);
- id->acoustic = __le16_to_cpu(id->acoustic);
- id->msrqs = __le16_to_cpu(id->msrqs);
- id->sxfert = __le16_to_cpu(id->sxfert);
- id->sal = __le16_to_cpu(id->sal);
- id->spg = __le32_to_cpu(id->spg);
- id->lba_capacity_2 = __le64_to_cpu(id->lba_capacity_2);
- for (i = 0; i < 22; i++)
- id->words104_125[i] = __le16_to_cpu(id->words104_125[i]);
- id->last_lun = __le16_to_cpu(id->last_lun);
- id->word127 = __le16_to_cpu(id->word127);
- id->dlf = __le16_to_cpu(id->dlf);
- id->csfo = __le16_to_cpu(id->csfo);
- for (i = 0; i < 26; i++)
- id->words130_155[i] = __le16_to_cpu(id->words130_155[i]);
- id->word156 = __le16_to_cpu(id->word156);
- for (i = 0; i < 3; i++)
- id->words157_159[i] = __le16_to_cpu(id->words157_159[i]);
- id->cfa_power = __le16_to_cpu(id->cfa_power);
- for (i = 0; i < 15; i++)
- id->words161_175[i] = __le16_to_cpu(id->words161_175[i]);
- for (i = 0; i < 30; i++)
- id->words176_205[i] = __le16_to_cpu(id->words176_205[i]);
- for (i = 0; i < 49; i++)
- id->words206_254[i] = __le16_to_cpu(id->words206_254[i]);
- id->integrity_word = __le16_to_cpu(id->integrity_word);
+ for (i = 0; i < 256; i++)
+ id[i] = __le16_to_cpu(id[i]);
# else
# error "Please fix <asm/byteorder.h>"
# endif
@@ -501,19 +417,21 @@
* ide_fixstring() cleans up and (optionally) byte-swaps a text string,
* removing leading/trailing blanks and compressing internal blanks.
* It is primarily used to tidy up the model name/number fields as
- * returned by the WIN_[P]IDENTIFY commands.
+ * returned by the ATA_CMD_ID_ATA[PI] commands.
*/
void ide_fixstring (u8 *s, const int bytecount, const int byteswap)
{
- u8 *p = s, *end = &s[bytecount & ~1]; /* bytecount must be even */
+ u8 *p, *end = &s[bytecount & ~1]; /* bytecount must be even */
if (byteswap) {
/* convert from big-endian to host byte order */
- for (p = end ; p != s;)
- be16_to_cpus((u16 *)(p -= 2));
+ for (p = s ; p != end ; p += 2)
+ be16_to_cpus((u16 *) p);
}
+
/* strip leading blanks */
+ p = s;
while (s != end && *s == ' ')
++s;
/* compress internal blanks and strip trailing blanks */
@@ -556,7 +474,7 @@
/* Note: this may clear a pending IRQ!! */
stat = hwif->tp_ops->read_status(hwif);
- if (stat & BUSY_STAT)
+ if (stat & ATA_BUSY)
/* drive busy: definitely not interrupting */
return 0;
@@ -588,10 +506,10 @@
udelay(1); /* spec allows drive 400ns to assert "BUSY" */
stat = tp_ops->read_status(hwif);
- if (stat & BUSY_STAT) {
+ if (stat & ATA_BUSY) {
local_irq_set(flags);
timeout += jiffies;
- while ((stat = tp_ops->read_status(hwif)) & BUSY_STAT) {
+ while ((stat = tp_ops->read_status(hwif)) & ATA_BUSY) {
if (time_after(jiffies, timeout)) {
/*
* One last read after the timeout in case
@@ -599,7 +517,7 @@
* progress during the timeout..
*/
stat = tp_ops->read_status(hwif);
- if (!(stat & BUSY_STAT))
+ if ((stat & ATA_BUSY) == 0)
break;
local_irq_restore(flags);
@@ -660,18 +578,18 @@
/**
* ide_in_drive_list - look for drive in black/white list
* @id: drive identifier
- * @drive_table: list to inspect
+ * @table: list to inspect
*
* Look for a drive in the blacklist and the whitelist tables
* Returns 1 if the drive is found in the table.
*/
-int ide_in_drive_list(struct hd_driveid *id, const struct drive_list_entry *drive_table)
+int ide_in_drive_list(u16 *id, const struct drive_list_entry *table)
{
- for ( ; drive_table->id_model; drive_table++)
- if ((!strcmp(drive_table->id_model, id->model)) &&
- (!drive_table->id_firmware ||
- strstr(id->fw_rev, drive_table->id_firmware)))
+ for ( ; table->id_model; table++)
+ if ((!strcmp(table->id_model, (char *)&id[ATA_ID_PROD])) &&
+ (!table->id_firmware ||
+ strstr((char *)&id[ATA_ID_FW_REV], table->id_firmware)))
return 1;
return 0;
}
@@ -702,7 +620,7 @@
u8 eighty_ninty_three (ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
- struct hd_driveid *id = drive->id;
+ u16 *id = drive->id;
int ivb = ide_in_drive_list(id, ivb_list);
if (hwif->cbl == ATA_CBL_PATA40_SHORT)
@@ -712,7 +630,7 @@
printk(KERN_DEBUG "%s: skipping word 93 validity check\n",
drive->name);
- if (ide_dev_is_sata(id) && !ivb)
+ if (ata_id_is_sata(id) && !ivb)
return 1;
if (hwif->cbl != ATA_CBL_PATA80 && !ivb)
@@ -724,11 +642,12 @@
* - force bit13 (80c cable present) check also for !ivb devices
* (unless the slave device is pre-ATA3)
*/
- if ((id->hw_config & 0x4000) || (ivb && (id->hw_config & 0x2000)))
+ if ((id[ATA_ID_HW_CONFIG] & 0x4000) ||
+ (ivb && (id[ATA_ID_HW_CONFIG] & 0x2000)))
return 1;
no_80w:
- if (drive->udma33_warned == 1)
+ if (drive->dev_flags & IDE_DFLAG_UDMA33_WARNED)
return 0;
printk(KERN_WARNING "%s: %s side 80-wire cable detection failed, "
@@ -736,7 +655,7 @@
drive->name,
hwif->cbl == ATA_CBL_PATA80 ? "drive" : "host");
- drive->udma33_warned = 1;
+ drive->dev_flags |= IDE_DFLAG_UDMA33_WARNED;
return 0;
}
@@ -745,8 +664,8 @@
{
ide_hwif_t *hwif = drive->hwif;
const struct ide_tp_ops *tp_ops = hwif->tp_ops;
- struct hd_driveid *id;
- unsigned long timeout, flags;
+ u16 *id;
+ unsigned long flags;
u8 stat;
/*
@@ -757,29 +676,24 @@
SELECT_MASK(drive, 1);
tp_ops->set_irq(hwif, 0);
msleep(50);
- tp_ops->exec_command(hwif, WIN_IDENTIFY);
- timeout = jiffies + WAIT_WORSTCASE;
- do {
- if (time_after(jiffies, timeout)) {
- SELECT_MASK(drive, 0);
- return 0; /* drive timed-out */
- }
+ tp_ops->exec_command(hwif, ATA_CMD_ID_ATA);
- msleep(50); /* give drive a breather */
- stat = tp_ops->read_altstatus(hwif);
- } while (stat & BUSY_STAT);
+ if (ide_busy_sleep(hwif, WAIT_WORSTCASE, 1)) {
+ SELECT_MASK(drive, 0);
+ return 0;
+ }
- msleep(50); /* wait for IRQ and DRQ_STAT */
+ msleep(50); /* wait for IRQ and ATA_DRQ */
stat = tp_ops->read_status(hwif);
- if (!OK_STAT(stat, DRQ_STAT, BAD_R_STAT)) {
+ if (!OK_STAT(stat, ATA_DRQ, BAD_R_STAT)) {
SELECT_MASK(drive, 0);
printk("%s: CHECK for good STATUS\n", drive->name);
return 0;
}
local_irq_save(flags);
SELECT_MASK(drive, 0);
- id = kmalloc(SECTOR_WORDS*4, GFP_ATOMIC);
+ id = kmalloc(SECTOR_SIZE, GFP_ATOMIC);
if (!id) {
local_irq_restore(flags);
return 0;
@@ -789,16 +703,16 @@
local_irq_enable();
local_irq_restore(flags);
ide_fix_driveid(id);
- if (id) {
- drive->id->dma_ultra = id->dma_ultra;
- drive->id->dma_mword = id->dma_mword;
- drive->id->dma_1word = id->dma_1word;
- /* anything more ? */
- kfree(id);
- if (drive->using_dma && ide_id_dma_bug(drive))
- ide_dma_off(drive);
- }
+ drive->id[ATA_ID_UDMA_MODES] = id[ATA_ID_UDMA_MODES];
+ drive->id[ATA_ID_MWDMA_MODES] = id[ATA_ID_MWDMA_MODES];
+ drive->id[ATA_ID_SWDMA_MODES] = id[ATA_ID_SWDMA_MODES];
+ /* anything more ? */
+
+ kfree(id);
+
+ if ((drive->dev_flags & IDE_DFLAG_USING_DMA) && ide_id_dma_bug(drive))
+ ide_dma_off(drive);
return 1;
}
@@ -807,6 +721,7 @@
{
ide_hwif_t *hwif = drive->hwif;
const struct ide_tp_ops *tp_ops = hwif->tp_ops;
+ u16 *id = drive->id, i;
int error = 0;
u8 stat;
ide_task_t task;
@@ -817,7 +732,7 @@
#endif
/* Skip setting PIO flow-control modes on pre-EIDE drives */
- if ((speed & 0xf8) == XFER_PIO_0 && !(drive->id->capability & 0x08))
+ if ((speed & 0xf8) == XFER_PIO_0 && ata_id_has_iordy(drive->id) == 0)
goto skip;
/*
@@ -851,13 +766,13 @@
tp_ops->tf_load(drive, &task);
- tp_ops->exec_command(hwif, WIN_SETFEATURES);
+ tp_ops->exec_command(hwif, ATA_CMD_SET_FEATURES);
if (drive->quirk_list == 2)
tp_ops->set_irq(hwif, 1);
error = __ide_wait_stat(drive, drive->ready_stat,
- BUSY_STAT|DRQ_STAT|ERR_STAT,
+ ATA_BUSY | ATA_DRQ | ATA_ERR,
WAIT_CMD, &stat);
SELECT_MASK(drive, 0);
@@ -869,35 +784,29 @@
return error;
}
- drive->id->dma_ultra &= ~0xFF00;
- drive->id->dma_mword &= ~0x0F00;
- drive->id->dma_1word &= ~0x0F00;
+ id[ATA_ID_UDMA_MODES] &= ~0xFF00;
+ id[ATA_ID_MWDMA_MODES] &= ~0x0F00;
+ id[ATA_ID_SWDMA_MODES] &= ~0x0F00;
skip:
#ifdef CONFIG_BLK_DEV_IDEDMA
- if (speed >= XFER_SW_DMA_0 && drive->using_dma)
+ if (speed >= XFER_SW_DMA_0 && (drive->dev_flags & IDE_DFLAG_USING_DMA))
hwif->dma_ops->dma_host_set(drive, 1);
else if (hwif->dma_ops) /* check if host supports DMA */
ide_dma_off_quietly(drive);
#endif
- switch(speed) {
- case XFER_UDMA_7: drive->id->dma_ultra |= 0x8080; break;
- case XFER_UDMA_6: drive->id->dma_ultra |= 0x4040; break;
- case XFER_UDMA_5: drive->id->dma_ultra |= 0x2020; break;
- case XFER_UDMA_4: drive->id->dma_ultra |= 0x1010; break;
- case XFER_UDMA_3: drive->id->dma_ultra |= 0x0808; break;
- case XFER_UDMA_2: drive->id->dma_ultra |= 0x0404; break;
- case XFER_UDMA_1: drive->id->dma_ultra |= 0x0202; break;
- case XFER_UDMA_0: drive->id->dma_ultra |= 0x0101; break;
- case XFER_MW_DMA_2: drive->id->dma_mword |= 0x0404; break;
- case XFER_MW_DMA_1: drive->id->dma_mword |= 0x0202; break;
- case XFER_MW_DMA_0: drive->id->dma_mword |= 0x0101; break;
- case XFER_SW_DMA_2: drive->id->dma_1word |= 0x0404; break;
- case XFER_SW_DMA_1: drive->id->dma_1word |= 0x0202; break;
- case XFER_SW_DMA_0: drive->id->dma_1word |= 0x0101; break;
- default: break;
+ if (speed >= XFER_UDMA_0) {
+ i = 1 << (speed - XFER_UDMA_0);
+ id[ATA_ID_UDMA_MODES] |= (i << 8 | i);
+ } else if (speed >= XFER_MW_DMA_0) {
+ i = 1 << (speed - XFER_MW_DMA_0);
+ id[ATA_ID_MWDMA_MODES] |= (i << 8 | i);
+ } else if (speed >= XFER_SW_DMA_0) {
+ i = 1 << (speed - XFER_SW_DMA_0);
+ id[ATA_ID_SWDMA_MODES] |= (i << 8 | i);
}
+
if (!drive->init_speed)
drive->init_speed = speed;
drive->current_speed = speed;
@@ -977,7 +886,7 @@
unsigned long flags;
spin_lock_irqsave(&ide_lock, flags);
- hwif->tp_ops->exec_command(hwif, WIN_PACKETCMD);
+ hwif->tp_ops->exec_command(hwif, ATA_CMD_PACKET);
ndelay(400);
spin_unlock_irqrestore(&ide_lock, flags);
}
@@ -1010,7 +919,7 @@
udelay (10);
stat = hwif->tp_ops->read_status(hwif);
- if (OK_STAT(stat, 0, BUSY_STAT))
+ if (OK_STAT(stat, 0, ATA_BUSY))
printk("%s: ATAPI reset complete\n", drive->name);
else {
if (time_before(jiffies, hwgroup->poll_timeout)) {
@@ -1031,6 +940,25 @@
return ide_stopped;
}
+static void ide_reset_report_error(ide_hwif_t *hwif, u8 err)
+{
+ static const char *err_master_vals[] =
+ { NULL, "passed", "formatter device error",
+ "sector buffer error", "ECC circuitry error",
+ "controlling MPU error" };
+
+ u8 err_master = err & 0x7f;
+
+ printk(KERN_ERR "%s: reset: master: ", hwif->name);
+ if (err_master && err_master < 6)
+ printk(KERN_CONT "%s", err_master_vals[err_master]);
+ else
+ printk(KERN_CONT "error (0x%02x?)", err);
+ if (err & 0x80)
+ printk(KERN_CONT "; slave: failed");
+ printk(KERN_CONT "\n");
+}
+
/*
* reset_pollfunc() gets invoked to poll the interface for completion every 50ms
* during an ide reset operation. If the drives have not yet responded,
@@ -1056,7 +984,7 @@
tmp = hwif->tp_ops->read_status(hwif);
- if (!OK_STAT(tmp, 0, BUSY_STAT)) {
+ if (!OK_STAT(tmp, 0, ATA_BUSY)) {
if (time_before(jiffies, hwgroup->poll_timeout)) {
ide_set_handler(drive, &reset_pollfunc, HZ/20, NULL);
/* continue polling */
@@ -1066,31 +994,14 @@
drive->failures++;
err = -EIO;
} else {
- printk("%s: reset: ", hwif->name);
tmp = ide_read_error(drive);
if (tmp == 1) {
- printk("success\n");
+ printk(KERN_INFO "%s: reset: success\n", hwif->name);
drive->failures = 0;
} else {
+ ide_reset_report_error(hwif, tmp);
drive->failures++;
- printk("master: ");
- switch (tmp & 0x7f) {
- case 1: printk("passed");
- break;
- case 2: printk("formatter device error");
- break;
- case 3: printk("sector buffer error");
- break;
- case 4: printk("ECC circuitry error");
- break;
- case 5: printk("controlling MPU error");
- break;
- default:printk("error (0x%02x?)", tmp);
- }
- if (tmp & 0x80)
- printk("; slave: failed");
- printk("\n");
err = -EIO;
}
}
@@ -1102,14 +1013,19 @@
static void ide_disk_pre_reset(ide_drive_t *drive)
{
- int legacy = (drive->id->cfs_enable_2 & 0x0400) ? 0 : 1;
+ int legacy = (drive->id[ATA_ID_CFS_ENABLE_2] & 0x0400) ? 0 : 1;
drive->special.all = 0;
drive->special.b.set_geometry = legacy;
drive->special.b.recalibrate = legacy;
+
drive->mult_count = 0;
- if (!drive->keep_settings && !drive->using_dma)
+ drive->dev_flags &= ~IDE_DFLAG_PARKED;
+
+ if ((drive->dev_flags & IDE_DFLAG_KEEP_SETTINGS) == 0 &&
+ (drive->dev_flags & IDE_DFLAG_USING_DMA) == 0)
drive->mult_req = 0;
+
if (drive->mult_req != drive->mult_count)
drive->special.b.set_multmode = 1;
}
@@ -1121,18 +1037,18 @@
if (drive->media == ide_disk)
ide_disk_pre_reset(drive);
else
- drive->post_reset = 1;
+ drive->dev_flags |= IDE_DFLAG_POST_RESET;
- if (drive->using_dma) {
+ if (drive->dev_flags & IDE_DFLAG_USING_DMA) {
if (drive->crc_count)
ide_check_dma_crc(drive);
else
ide_dma_off(drive);
}
- if (!drive->keep_settings) {
- if (!drive->using_dma) {
- drive->unmask = 0;
+ if ((drive->dev_flags & IDE_DFLAG_KEEP_SETTINGS) == 0) {
+ if ((drive->dev_flags & IDE_DFLAG_USING_DMA) == 0) {
+ drive->dev_flags &= ~IDE_DFLAG_UNMASK;
drive->io_32bit = 0;
}
return;
@@ -1164,12 +1080,13 @@
static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
{
unsigned int unit;
- unsigned long flags;
+ unsigned long flags, timeout;
ide_hwif_t *hwif;
ide_hwgroup_t *hwgroup;
struct ide_io_ports *io_ports;
const struct ide_tp_ops *tp_ops;
const struct ide_port_ops *port_ops;
+ DEFINE_WAIT(wait);
spin_lock_irqsave(&ide_lock, flags);
hwif = HWIF(drive);
@@ -1187,7 +1104,7 @@
pre_reset(drive);
SELECT_DRIVE(drive);
udelay (20);
- tp_ops->exec_command(hwif, WIN_SRST);
+ tp_ops->exec_command(hwif, ATA_CMD_DEV_RESET);
ndelay(400);
hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
hwgroup->polling = 1;
@@ -1196,6 +1113,31 @@
return ide_started;
}
+ /* We must not disturb devices in the IDE_DFLAG_PARKED state. */
+ do {
+ unsigned long now;
+
+ prepare_to_wait(&ide_park_wq, &wait, TASK_UNINTERRUPTIBLE);
+ timeout = jiffies;
+ for (unit = 0; unit < MAX_DRIVES; unit++) {
+ ide_drive_t *tdrive = &hwif->drives[unit];
+
+ if (tdrive->dev_flags & IDE_DFLAG_PRESENT &&
+ tdrive->dev_flags & IDE_DFLAG_PARKED &&
+ time_after(tdrive->sleep, timeout))
+ timeout = tdrive->sleep;
+ }
+
+ now = jiffies;
+ if (time_before_eq(timeout, now))
+ break;
+
+ spin_unlock_irqrestore(&ide_lock, flags);
+ timeout = schedule_timeout_uninterruptible(timeout - now);
+ spin_lock_irqsave(&ide_lock, flags);
+ } while (timeout);
+ finish_wait(&ide_park_wq, &wait);
+
/*
* First, reset any device state data we were maintaining
* for any of the drives on this interface.
@@ -1270,7 +1212,7 @@
*/
mdelay(1);
stat = hwif->tp_ops->read_status(hwif);
- if ((stat & BUSY_STAT) == 0)
+ if ((stat & ATA_BUSY) == 0)
return 0;
/*
* Assume a value of 0xff means nothing is connected to
diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c
index 97fefab..9fc4cfb 100644
--- a/drivers/ide/ide-lib.c
+++ b/drivers/ide/ide-lib.c
@@ -2,7 +2,6 @@
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
-#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/bitops.h>
@@ -90,29 +89,31 @@
u8 ide_get_best_pio_mode (ide_drive_t *drive, u8 mode_wanted, u8 max_mode)
{
- int pio_mode;
- struct hd_driveid* id = drive->id;
- int overridden = 0;
+ u16 *id = drive->id;
+ int pio_mode = -1, overridden = 0;
if (mode_wanted != 255)
return min_t(u8, mode_wanted, max_mode);
- if ((drive->hwif->host_flags & IDE_HFLAG_PIO_NO_BLACKLIST) == 0 &&
- (pio_mode = ide_scan_pio_blacklist(id->model)) != -1) {
+ if ((drive->hwif->host_flags & IDE_HFLAG_PIO_NO_BLACKLIST) == 0)
+ pio_mode = ide_scan_pio_blacklist((char *)&id[ATA_ID_PROD]);
+
+ if (pio_mode != -1) {
printk(KERN_INFO "%s: is on PIO blacklist\n", drive->name);
} else {
- pio_mode = id->tPIO;
+ pio_mode = id[ATA_ID_OLD_PIO_MODES] >> 8;
if (pio_mode > 2) { /* 2 is maximum allowed tPIO value */
pio_mode = 2;
overridden = 1;
}
- if (id->field_valid & 2) { /* drive implements ATA2? */
- if (id->capability & 8) { /* IORDY supported? */
- if (id->eide_pio_modes & 7) {
+
+ if (id[ATA_ID_FIELD_VALID] & 2) { /* ATA2? */
+ if (ata_id_has_iordy(id)) {
+ if (id[ATA_ID_PIO_MODES] & 7) {
overridden = 0;
- if (id->eide_pio_modes & 4)
+ if (id[ATA_ID_PIO_MODES] & 4)
pio_mode = 5;
- else if (id->eide_pio_modes & 2)
+ else if (id[ATA_ID_PIO_MODES] & 2)
pio_mode = 4;
else
pio_mode = 3;
@@ -316,7 +317,7 @@
{
ide_task_t task;
struct ide_taskfile *tf = &task.tf;
- int lba48 = (drive->addressing == 1) ? 1 : 0;
+ u8 lba48 = !!(drive->dev_flags & IDE_DFLAG_LBA48);
memset(&task, 0, sizeof(task));
if (lba48)
@@ -338,16 +339,16 @@
static void ide_dump_ata_error(ide_drive_t *drive, u8 err)
{
printk("{ ");
- if (err & ABRT_ERR) printk("DriveStatusError ");
- if (err & ICRC_ERR)
- printk((err & ABRT_ERR) ? "BadCRC " : "BadSector ");
- if (err & ECC_ERR) printk("UncorrectableError ");
- if (err & ID_ERR) printk("SectorIdNotFound ");
- if (err & TRK0_ERR) printk("TrackZeroNotFound ");
- if (err & MARK_ERR) printk("AddrMarkNotFound ");
+ if (err & ATA_ABORTED) printk("DriveStatusError ");
+ if (err & ATA_ICRC)
+ printk((err & ATA_ABORTED) ? "BadCRC " : "BadSector ");
+ if (err & ATA_UNC) printk("UncorrectableError ");
+ if (err & ATA_IDNF) printk("SectorIdNotFound ");
+ if (err & ATA_TRK0NF) printk("TrackZeroNotFound ");
+ if (err & ATA_AMNF) printk("AddrMarkNotFound ");
printk("}");
- if ((err & (BBD_ERR | ABRT_ERR)) == BBD_ERR ||
- (err & (ECC_ERR|ID_ERR|MARK_ERR))) {
+ if ((err & (ATA_BBK | ATA_ABORTED)) == ATA_BBK ||
+ (err & (ATA_UNC | ATA_IDNF | ATA_AMNF))) {
ide_dump_sector(drive);
if (HWGROUP(drive) && HWGROUP(drive)->rq)
printk(", sector=%llu",
@@ -359,12 +360,12 @@
static void ide_dump_atapi_error(ide_drive_t *drive, u8 err)
{
printk("{ ");
- if (err & ILI_ERR) printk("IllegalLengthIndication ");
- if (err & EOM_ERR) printk("EndOfMedia ");
- if (err & ABRT_ERR) printk("AbortedCommand ");
- if (err & MCR_ERR) printk("MediaChangeRequested ");
- if (err & LFS_ERR) printk("LastFailedSense=0x%02x ",
- (err & LFS_ERR) >> 4);
+ if (err & ATAPI_ILI) printk("IllegalLengthIndication ");
+ if (err & ATAPI_EOM) printk("EndOfMedia ");
+ if (err & ATA_ABORTED) printk("AbortedCommand ");
+ if (err & ATA_MCR) printk("MediaChangeRequested ");
+ if (err & ATAPI_LFS) printk("LastFailedSense=0x%02x ",
+ (err & ATAPI_LFS) >> 4);
printk("}\n");
}
@@ -386,19 +387,19 @@
local_irq_save(flags);
printk("%s: %s: status=0x%02x { ", drive->name, msg, stat);
- if (stat & BUSY_STAT)
+ if (stat & ATA_BUSY)
printk("Busy ");
else {
- if (stat & READY_STAT) printk("DriveReady ");
- if (stat & WRERR_STAT) printk("DeviceFault ");
- if (stat & SEEK_STAT) printk("SeekComplete ");
- if (stat & DRQ_STAT) printk("DataRequest ");
- if (stat & ECC_STAT) printk("CorrectedError ");
- if (stat & INDEX_STAT) printk("Index ");
- if (stat & ERR_STAT) printk("Error ");
+ if (stat & ATA_DRDY) printk("DriveReady ");
+ if (stat & ATA_DF) printk("DeviceFault ");
+ if (stat & ATA_DSC) printk("SeekComplete ");
+ if (stat & ATA_DRQ) printk("DataRequest ");
+ if (stat & ATA_CORR) printk("CorrectedError ");
+ if (stat & ATA_IDX) printk("Index ");
+ if (stat & ATA_ERR) printk("Error ");
}
printk("}\n");
- if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) {
+ if ((stat & (ATA_BUSY | ATA_ERR)) == ATA_ERR) {
err = ide_read_error(drive);
printk("%s: %s: error=0x%02x ", drive->name, msg, err);
if (drive->media == ide_disk)
diff --git a/drivers/ide/ide-park.c b/drivers/ide/ide-park.c
new file mode 100644
index 0000000..03b00e5
--- /dev/null
+++ b/drivers/ide/ide-park.c
@@ -0,0 +1,121 @@
+#include <linux/kernel.h>
+#include <linux/ide.h>
+#include <linux/jiffies.h>
+#include <linux/blkdev.h>
+
+DECLARE_WAIT_QUEUE_HEAD(ide_park_wq);
+
+static void issue_park_cmd(ide_drive_t *drive, unsigned long timeout)
+{
+ struct request_queue *q = drive->queue;
+ struct request *rq;
+ int rc;
+
+ timeout += jiffies;
+ spin_lock_irq(&ide_lock);
+ if (drive->dev_flags & IDE_DFLAG_PARKED) {
+ ide_hwgroup_t *hwgroup = drive->hwif->hwgroup;
+ int reset_timer;
+
+ reset_timer = time_before(timeout, drive->sleep);
+ drive->sleep = timeout;
+ wake_up_all(&ide_park_wq);
+ if (reset_timer && hwgroup->sleeping &&
+ del_timer(&hwgroup->timer)) {
+ hwgroup->sleeping = 0;
+ hwgroup->busy = 0;
+ blk_start_queueing(q);
+ }
+ spin_unlock_irq(&ide_lock);
+ return;
+ }
+ spin_unlock_irq(&ide_lock);
+
+ rq = blk_get_request(q, READ, __GFP_WAIT);
+ rq->cmd[0] = REQ_PARK_HEADS;
+ rq->cmd_len = 1;
+ rq->cmd_type = REQ_TYPE_SPECIAL;
+ rq->special = &timeout;
+ rc = blk_execute_rq(q, NULL, rq, 1);
+ blk_put_request(rq);
+ if (rc)
+ goto out;
+
+ /*
+ * Make sure that *some* command is sent to the drive after the
+ * timeout has expired, so power management will be reenabled.
+ */
+ rq = blk_get_request(q, READ, GFP_NOWAIT);
+ if (unlikely(!rq))
+ goto out;
+
+ rq->cmd[0] = REQ_UNPARK_HEADS;
+ rq->cmd_len = 1;
+ rq->cmd_type = REQ_TYPE_SPECIAL;
+ elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 1);
+
+out:
+ return;
+}
+
+ssize_t ide_park_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ ide_drive_t *drive = to_ide_device(dev);
+ unsigned long now;
+ unsigned int msecs;
+
+ if (drive->dev_flags & IDE_DFLAG_NO_UNLOAD)
+ return -EOPNOTSUPP;
+
+ spin_lock_irq(&ide_lock);
+ now = jiffies;
+ if (drive->dev_flags & IDE_DFLAG_PARKED &&
+ time_after(drive->sleep, now))
+ msecs = jiffies_to_msecs(drive->sleep - now);
+ else
+ msecs = 0;
+ spin_unlock_irq(&ide_lock);
+
+ return snprintf(buf, 20, "%u\n", msecs);
+}
+
+ssize_t ide_park_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+#define MAX_PARK_TIMEOUT 30000
+ ide_drive_t *drive = to_ide_device(dev);
+ long int input;
+ int rc;
+
+ rc = strict_strtol(buf, 10, &input);
+ if (rc || input < -2)
+ return -EINVAL;
+ if (input > MAX_PARK_TIMEOUT) {
+ input = MAX_PARK_TIMEOUT;
+ rc = -EOVERFLOW;
+ }
+
+ mutex_lock(&ide_setting_mtx);
+ if (input >= 0) {
+ if (drive->dev_flags & IDE_DFLAG_NO_UNLOAD)
+ rc = -EOPNOTSUPP;
+ else if (input || drive->dev_flags & IDE_DFLAG_PARKED)
+ issue_park_cmd(drive, msecs_to_jiffies(input));
+ } else {
+ if (drive->media == ide_disk)
+ switch (input) {
+ case -1:
+ drive->dev_flags &= ~IDE_DFLAG_NO_UNLOAD;
+ break;
+ case -2:
+ drive->dev_flags |= IDE_DFLAG_NO_UNLOAD;
+ break;
+ }
+ else
+ rc = -EOPNOTSUPP;
+ }
+ mutex_unlock(&ide_setting_mtx);
+
+ return rc ? rc : len;
+}
diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c
index 70aa86c..f27baa5 100644
--- a/drivers/ide/ide-probe.c
+++ b/drivers/ide/ide-probe.c
@@ -50,59 +50,54 @@
static void generic_id(ide_drive_t *drive)
{
- drive->id->cyls = drive->cyl;
- drive->id->heads = drive->head;
- drive->id->sectors = drive->sect;
- drive->id->cur_cyls = drive->cyl;
- drive->id->cur_heads = drive->head;
- drive->id->cur_sectors = drive->sect;
+ u16 *id = drive->id;
+
+ id[ATA_ID_CUR_CYLS] = id[ATA_ID_CYLS] = drive->cyl;
+ id[ATA_ID_CUR_HEADS] = id[ATA_ID_HEADS] = drive->head;
+ id[ATA_ID_CUR_SECTORS] = id[ATA_ID_SECTORS] = drive->sect;
}
static void ide_disk_init_chs(ide_drive_t *drive)
{
- struct hd_driveid *id = drive->id;
+ u16 *id = drive->id;
/* Extract geometry if we did not already have one for the drive */
if (!drive->cyl || !drive->head || !drive->sect) {
- drive->cyl = drive->bios_cyl = id->cyls;
- drive->head = drive->bios_head = id->heads;
- drive->sect = drive->bios_sect = id->sectors;
+ drive->cyl = drive->bios_cyl = id[ATA_ID_CYLS];
+ drive->head = drive->bios_head = id[ATA_ID_HEADS];
+ drive->sect = drive->bios_sect = id[ATA_ID_SECTORS];
}
/* Handle logical geometry translation by the drive */
- if ((id->field_valid & 1) && id->cur_cyls &&
- id->cur_heads && (id->cur_heads <= 16) && id->cur_sectors) {
- drive->cyl = id->cur_cyls;
- drive->head = id->cur_heads;
- drive->sect = id->cur_sectors;
+ if (ata_id_current_chs_valid(id)) {
+ drive->cyl = id[ATA_ID_CUR_CYLS];
+ drive->head = id[ATA_ID_CUR_HEADS];
+ drive->sect = id[ATA_ID_CUR_SECTORS];
}
/* Use physical geometry if what we have still makes no sense */
- if (drive->head > 16 && id->heads && id->heads <= 16) {
- drive->cyl = id->cyls;
- drive->head = id->heads;
- drive->sect = id->sectors;
+ if (drive->head > 16 && id[ATA_ID_HEADS] && id[ATA_ID_HEADS] <= 16) {
+ drive->cyl = id[ATA_ID_CYLS];
+ drive->head = id[ATA_ID_HEADS];
+ drive->sect = id[ATA_ID_SECTORS];
}
}
static void ide_disk_init_mult_count(ide_drive_t *drive)
{
- struct hd_driveid *id = drive->id;
+ u16 *id = drive->id;
+ u8 max_multsect = id[ATA_ID_MAX_MULTSECT] & 0xff;
- drive->mult_count = 0;
- if (id->max_multsect) {
-#ifdef CONFIG_IDEDISK_MULTI_MODE
- id->multsect = ((id->max_multsect/2) > 1) ? id->max_multsect : 0;
- id->multsect_valid = id->multsect ? 1 : 0;
- drive->mult_req = id->multsect_valid ? id->max_multsect : 0;
- drive->special.b.set_multmode = drive->mult_req ? 1 : 0;
-#else /* original, pre IDE-NFG, per request of AC */
- drive->mult_req = 0;
- if (drive->mult_req > id->max_multsect)
- drive->mult_req = id->max_multsect;
- if (drive->mult_req || ((id->multsect_valid & 1) && id->multsect))
+ if (max_multsect) {
+ if ((max_multsect / 2) > 1)
+ id[ATA_ID_MULTSECT] = max_multsect | 0x100;
+ else
+ id[ATA_ID_MULTSECT] &= ~0x1ff;
+
+ drive->mult_req = id[ATA_ID_MULTSECT] & 0xff;
+
+ if (drive->mult_req)
drive->special.b.set_multmode = 1;
-#endif
}
}
@@ -119,14 +114,15 @@
static inline void do_identify (ide_drive_t *drive, u8 cmd)
{
ide_hwif_t *hwif = HWIF(drive);
- int bswap = 1;
- struct hd_driveid *id;
+ u16 *id = drive->id;
+ char *m = (char *)&id[ATA_ID_PROD];
+ int bswap = 1, is_cfa;
- id = drive->id;
/* read 512 bytes of id info */
hwif->tp_ops->input_data(drive, NULL, id, SECTOR_SIZE);
- drive->id_read = 1;
+ drive->dev_flags |= IDE_DFLAG_ID_READ;
+
local_irq_enable();
#ifdef DEBUG
printk(KERN_INFO "%s: dumping identify data\n", drive->name);
@@ -135,59 +131,59 @@
ide_fix_driveid(id);
/*
- * WIN_IDENTIFY returns little-endian info,
- * WIN_PIDENTIFY *usually* returns little-endian info.
+ * ATA_CMD_ID_ATA returns little-endian info,
+ * ATA_CMD_ID_ATAPI *usually* returns little-endian info.
*/
- if (cmd == WIN_PIDENTIFY) {
- if ((id->model[0] == 'N' && id->model[1] == 'E') /* NEC */
- || (id->model[0] == 'F' && id->model[1] == 'X') /* Mitsumi */
- || (id->model[0] == 'P' && id->model[1] == 'i'))/* Pioneer */
+ if (cmd == ATA_CMD_ID_ATAPI) {
+ if ((m[0] == 'N' && m[1] == 'E') || /* NEC */
+ (m[0] == 'F' && m[1] == 'X') || /* Mitsumi */
+ (m[0] == 'P' && m[1] == 'i')) /* Pioneer */
/* Vertos drives may still be weird */
- bswap ^= 1;
+ bswap ^= 1;
}
- ide_fixstring(id->model, sizeof(id->model), bswap);
- ide_fixstring(id->fw_rev, sizeof(id->fw_rev), bswap);
- ide_fixstring(id->serial_no, sizeof(id->serial_no), bswap);
+
+ ide_fixstring(m, ATA_ID_PROD_LEN, bswap);
+ ide_fixstring((char *)&id[ATA_ID_FW_REV], ATA_ID_FW_REV_LEN, bswap);
+ ide_fixstring((char *)&id[ATA_ID_SERNO], ATA_ID_SERNO_LEN, bswap);
/* we depend on this a lot! */
- id->model[sizeof(id->model)-1] = '\0';
+ m[ATA_ID_PROD_LEN - 1] = '\0';
- if (strstr(id->model, "E X A B Y T E N E S T"))
+ if (strstr(m, "E X A B Y T E N E S T"))
goto err_misc;
- printk(KERN_INFO "%s: %s, ", drive->name, id->model);
+ printk(KERN_INFO "%s: %s, ", drive->name, m);
- drive->present = 1;
- drive->dead = 0;
+ drive->dev_flags |= IDE_DFLAG_PRESENT;
+ drive->dev_flags &= ~IDE_DFLAG_DEAD;
/*
* Check for an ATAPI device
*/
- if (cmd == WIN_PIDENTIFY) {
- u8 type = (id->config >> 8) & 0x1f;
+ if (cmd == ATA_CMD_ID_ATAPI) {
+ u8 type = (id[ATA_ID_CONFIG] >> 8) & 0x1f;
printk(KERN_CONT "ATAPI ");
switch (type) {
case ide_floppy:
- if (!strstr(id->model, "CD-ROM")) {
- if (!strstr(id->model, "oppy") &&
- !strstr(id->model, "poyp") &&
- !strstr(id->model, "ZIP"))
+ if (!strstr(m, "CD-ROM")) {
+ if (!strstr(m, "oppy") &&
+ !strstr(m, "poyp") &&
+ !strstr(m, "ZIP"))
printk(KERN_CONT "cdrom or floppy?, assuming ");
if (drive->media != ide_cdrom) {
printk(KERN_CONT "FLOPPY");
- drive->removable = 1;
+ drive->dev_flags |= IDE_DFLAG_REMOVABLE;
break;
}
}
/* Early cdrom models used zero */
type = ide_cdrom;
case ide_cdrom:
- drive->removable = 1;
+ drive->dev_flags |= IDE_DFLAG_REMOVABLE;
#ifdef CONFIG_PPC
/* kludge for Apple PowerBook internal zip */
- if (!strstr(id->model, "CD-ROM") &&
- strstr(id->model, "ZIP")) {
+ if (!strstr(m, "CD-ROM") && strstr(m, "ZIP")) {
printk(KERN_CONT "FLOPPY");
type = ide_floppy;
break;
@@ -200,7 +196,7 @@
break;
case ide_optical:
printk(KERN_CONT "OPTICAL");
- drive->removable = 1;
+ drive->dev_flags |= IDE_DFLAG_REMOVABLE;
break;
default:
printk(KERN_CONT "UNKNOWN (type %d)", type);
@@ -210,6 +206,10 @@
drive->media = type;
/* an ATAPI device ignores DRDY */
drive->ready_stat = 0;
+ if (ata_id_cdb_intr(id))
+ drive->atapi_flags |= IDE_AFLAG_DRQ_INTERRUPT;
+ /* we don't do head unloading on ATAPI devices */
+ drive->dev_flags |= IDE_DFLAG_NO_UNLOAD;
return;
}
@@ -217,24 +217,24 @@
* Not an ATAPI device: looks like a "regular" hard disk
*/
- /*
- * 0x848a = CompactFlash device
- * These are *not* removable in Linux definition of the term
- */
+ is_cfa = ata_id_is_cfa(id);
- if ((id->config != 0x848a) && (id->config & (1<<7)))
- drive->removable = 1;
+ /* CF devices are *not* removable in Linux definition of the term */
+ if (is_cfa == 0 && (id[ATA_ID_CONFIG] & (1 << 7)))
+ drive->dev_flags |= IDE_DFLAG_REMOVABLE;
drive->media = ide_disk;
- printk(KERN_CONT "%s DISK drive\n",
- (id->config == 0x848a) ? "CFA" : "ATA");
+ if (!ata_id_has_unload(drive->id))
+ drive->dev_flags |= IDE_DFLAG_NO_UNLOAD;
+
+ printk(KERN_CONT "%s DISK drive\n", is_cfa ? "CFA" : "ATA");
return;
err_misc:
kfree(id);
- drive->present = 0;
+ drive->dev_flags &= ~IDE_DFLAG_PRESENT;
return;
}
@@ -268,7 +268,7 @@
if (io_ports->ctl_addr) {
a = tp_ops->read_altstatus(hwif);
s = tp_ops->read_status(hwif);
- if ((a ^ s) & ~INDEX_STAT)
+ if ((a ^ s) & ~ATA_IDX)
/* ancient Seagate drives, broken interfaces */
printk(KERN_INFO "%s: probing with STATUS(0x%02x) "
"instead of ALTSTATUS(0x%02x)\n",
@@ -281,7 +281,7 @@
/* set features register for atapi
* identify command to be sure of reply
*/
- if (cmd == WIN_PIDENTIFY) {
+ if (cmd == ATA_CMD_ID_ATAPI) {
ide_task_t task;
memset(&task, 0, sizeof(task));
@@ -294,24 +294,16 @@
/* ask drive for ID */
tp_ops->exec_command(hwif, cmd);
- timeout = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2;
- timeout += jiffies;
- do {
- if (time_after(jiffies, timeout)) {
- /* drive timed-out */
- return 1;
- }
- /* give drive a breather */
- msleep(50);
- s = use_altstatus ? tp_ops->read_altstatus(hwif)
- : tp_ops->read_status(hwif);
- } while (s & BUSY_STAT);
+ timeout = ((cmd == ATA_CMD_ID_ATA) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2;
- /* wait for IRQ and DRQ_STAT */
+ if (ide_busy_sleep(hwif, timeout, use_altstatus))
+ return 1;
+
+ /* wait for IRQ and ATA_DRQ */
msleep(50);
s = tp_ops->read_status(hwif);
- if (OK_STAT(s, DRQ_STAT, BAD_R_STAT)) {
+ if (OK_STAT(s, ATA_DRQ, BAD_R_STAT)) {
unsigned long flags;
/* local CPU only; some systems need this */
@@ -387,19 +379,21 @@
return retval;
}
-static int ide_busy_sleep(ide_hwif_t *hwif)
+int ide_busy_sleep(ide_hwif_t *hwif, unsigned long timeout, int altstatus)
{
- unsigned long timeout = jiffies + WAIT_WORSTCASE;
u8 stat;
+ timeout += jiffies;
+
do {
- msleep(50);
- stat = hwif->tp_ops->read_status(hwif);
- if ((stat & BUSY_STAT) == 0)
+ msleep(50); /* give drive a breather */
+ stat = altstatus ? hwif->tp_ops->read_altstatus(hwif)
+ : hwif->tp_ops->read_status(hwif);
+ if ((stat & ATA_BUSY) == 0)
return 0;
} while (time_before(jiffies, timeout));
- return 1;
+ return 1; /* drive timed-out */
}
static u8 ide_read_device(ide_drive_t *drive)
@@ -440,17 +434,16 @@
ide_hwif_t *hwif = HWIF(drive);
const struct ide_tp_ops *tp_ops = hwif->tp_ops;
int rc;
- u8 stat;
+ u8 present = !!(drive->dev_flags & IDE_DFLAG_PRESENT), stat;
- if (drive->present) {
- /* avoid waiting for inappropriate probes */
- if ((drive->media != ide_disk) && (cmd == WIN_IDENTIFY))
- return 4;
- }
+ /* avoid waiting for inappropriate probes */
+ if (present && drive->media != ide_disk && cmd == ATA_CMD_ID_ATA)
+ return 4;
+
#ifdef DEBUG
printk(KERN_INFO "probing for %s: present=%d, media=%d, probetype=%s\n",
- drive->name, drive->present, drive->media,
- (cmd == WIN_IDENTIFY) ? "ATA" : "ATAPI");
+ drive->name, present, drive->media,
+ (cmd == ATA_CMD_ID_ATA) ? "ATA" : "ATAPI");
#endif
/* needed for some systems
@@ -460,11 +453,11 @@
SELECT_DRIVE(drive);
msleep(50);
- if (ide_read_device(drive) != drive->select.all && !drive->present) {
- if (drive->select.b.unit != 0) {
+ if (ide_read_device(drive) != drive->select && present == 0) {
+ if (drive->dn & 1) {
/* exit with drive0 selected */
SELECT_DRIVE(&hwif->drives[0]);
- /* allow BUSY_STAT to assert & clear */
+ /* allow ATA_BUSY to assert & clear */
msleep(50);
}
/* no i/f present: mmm.. this should be a 4 -ml */
@@ -473,8 +466,8 @@
stat = tp_ops->read_status(hwif);
- if (OK_STAT(stat, READY_STAT, BUSY_STAT) ||
- drive->present || cmd == WIN_PIDENTIFY) {
+ if (OK_STAT(stat, ATA_DRDY, ATA_BUSY) ||
+ present || cmd == ATA_CMD_ID_ATAPI) {
/* send cmd and wait */
if ((rc = try_to_identify(drive, cmd))) {
/* failed: try again */
@@ -483,17 +476,17 @@
stat = tp_ops->read_status(hwif);
- if (stat == (BUSY_STAT | READY_STAT))
+ if (stat == (ATA_BUSY | ATA_DRDY))
return 4;
- if (rc == 1 && cmd == WIN_PIDENTIFY) {
+ if (rc == 1 && cmd == ATA_CMD_ID_ATAPI) {
printk(KERN_ERR "%s: no response (status = 0x%02x), "
"resetting drive\n", drive->name, stat);
msleep(50);
SELECT_DRIVE(drive);
msleep(50);
- tp_ops->exec_command(hwif, WIN_SRST);
- (void)ide_busy_sleep(hwif);
+ tp_ops->exec_command(hwif, ATA_CMD_DEV_RESET);
+ (void)ide_busy_sleep(hwif, WAIT_WORSTCASE, 0);
rc = try_to_identify(drive, cmd);
}
@@ -507,7 +500,7 @@
/* not present or maybe ATAPI */
rc = 3;
}
- if (drive->select.b.unit != 0) {
+ if (drive->dn & 1) {
/* exit with drive0 selected */
SELECT_DRIVE(&hwif->drives[0]);
msleep(50);
@@ -526,13 +519,14 @@
const struct ide_tp_ops *tp_ops = hwif->tp_ops;
u8 stat;
- printk(KERN_INFO "%s: enabling %s -- ", hwif->name, drive->id->model);
+ printk(KERN_INFO "%s: enabling %s -- ",
+ hwif->name, (char *)&drive->id[ATA_ID_PROD]);
SELECT_DRIVE(drive);
msleep(50);
- tp_ops->exec_command(hwif, EXABYTE_ENABLE_NEST);
+ tp_ops->exec_command(hwif, ATA_EXABYTE_ENABLE_NEST);
- if (ide_busy_sleep(hwif)) {
+ if (ide_busy_sleep(hwif, WAIT_WORSTCASE, 0)) {
printk(KERN_CONT "failed (timeout)\n");
return;
}
@@ -545,12 +539,6 @@
printk(KERN_CONT "failed (status = 0x%02x)\n", stat);
else
printk(KERN_CONT "success\n");
-
- /* if !(success||timed-out) */
- if (do_probe(drive, WIN_IDENTIFY) >= 2) {
- /* look for ATAPI device */
- (void) do_probe(drive, WIN_PIDENTIFY);
- }
}
/**
@@ -561,12 +549,14 @@
* and presents things to the user as needed.
*
* Returns: 0 no device was found
- * 1 device was found (note: drive->present might
- * still be 0)
+ * 1 device was found
+ * (note: IDE_DFLAG_PRESENT might still be not set)
*/
static inline u8 probe_for_drive (ide_drive_t *drive)
{
+ char *m;
+
/*
* In order to keep things simple we have an id
* block for all drives at all times. If the device
@@ -576,31 +566,36 @@
* Also note that 0 everywhere means "can't do X"
*/
- drive->id = kzalloc(SECTOR_WORDS *4, GFP_KERNEL);
- drive->id_read = 0;
- if(drive->id == NULL)
- {
+ drive->dev_flags &= ~IDE_DFLAG_ID_READ;
+
+ drive->id = kzalloc(SECTOR_SIZE, GFP_KERNEL);
+ if (drive->id == NULL) {
printk(KERN_ERR "ide: out of memory for id data.\n");
return 0;
}
- strcpy(drive->id->model, "UNKNOWN");
-
+
+ m = (char *)&drive->id[ATA_ID_PROD];
+ strcpy(m, "UNKNOWN");
+
/* skip probing? */
- if (!drive->noprobe)
- {
+ if ((drive->dev_flags & IDE_DFLAG_NOPROBE) == 0) {
+retry:
/* if !(success||timed-out) */
- if (do_probe(drive, WIN_IDENTIFY) >= 2) {
+ if (do_probe(drive, ATA_CMD_ID_ATA) >= 2)
/* look for ATAPI device */
- (void) do_probe(drive, WIN_PIDENTIFY);
- }
- if (!drive->present)
+ (void)do_probe(drive, ATA_CMD_ID_ATAPI);
+
+ if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0)
/* drive not found */
return 0;
- if (strstr(drive->id->model, "E X A B Y T E N E S T"))
+
+ if (strstr(m, "E X A B Y T E N E S T")) {
enable_nest(drive);
-
+ goto retry;
+ }
+
/* identification failed? */
- if (!drive->id_read) {
+ if ((drive->dev_flags & IDE_DFLAG_ID_READ) == 0) {
if (drive->media == ide_disk) {
printk(KERN_INFO "%s: non-IDE drive, CHS=%d/%d/%d\n",
drive->name, drive->cyl,
@@ -610,15 +605,17 @@
} else {
/* nuke it */
printk(KERN_WARNING "%s: Unknown device on bus refused identification. Ignoring.\n", drive->name);
- drive->present = 0;
+ drive->dev_flags &= ~IDE_DFLAG_PRESENT;
}
}
/* drive was found */
}
- if(!drive->present)
+
+ if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0)
return 0;
+
/* The drive wasn't being helpful. Add generic info only */
- if (drive->id_read == 0) {
+ if ((drive->dev_flags & IDE_DFLAG_ID_READ) == 0) {
generic_id(drive);
return 1;
}
@@ -628,7 +625,7 @@
ide_disk_init_mult_count(drive);
}
- return drive->present;
+ return !!(drive->dev_flags & IDE_DFLAG_PRESENT);
}
static void hwif_release_dev(struct device *dev)
@@ -719,7 +716,8 @@
ide_drive_t *drive = &hwif->drives[unit];
/* Ignore disks that we will not probe for later. */
- if (!drive->noprobe || drive->present) {
+ if ((drive->dev_flags & IDE_DFLAG_NOPROBE) == 0 ||
+ (drive->dev_flags & IDE_DFLAG_PRESENT)) {
SELECT_DRIVE(drive);
hwif->tp_ops->set_irq(hwif, 1);
mdelay(2);
@@ -740,36 +738,38 @@
/**
* ide_undecoded_slave - look for bad CF adapters
- * @drive1: drive
+ * @dev1: slave device
*
* Analyse the drives on the interface and attempt to decide if we
* have the same drive viewed twice. This occurs with crap CF adapters
* and PCMCIA sometimes.
*/
-void ide_undecoded_slave(ide_drive_t *drive1)
+void ide_undecoded_slave(ide_drive_t *dev1)
{
- ide_drive_t *drive0 = &drive1->hwif->drives[0];
+ ide_drive_t *dev0 = &dev1->hwif->drives[0];
- if ((drive1->dn & 1) == 0 || drive0->present == 0)
+ if ((dev1->dn & 1) == 0 || (dev0->dev_flags & IDE_DFLAG_PRESENT) == 0)
return;
/* If the models don't match they are not the same product */
- if (strcmp(drive0->id->model, drive1->id->model))
+ if (strcmp((char *)&dev0->id[ATA_ID_PROD],
+ (char *)&dev1->id[ATA_ID_PROD]))
return;
/* Serial numbers do not match */
- if (strncmp(drive0->id->serial_no, drive1->id->serial_no, 20))
+ if (strncmp((char *)&dev0->id[ATA_ID_SERNO],
+ (char *)&dev1->id[ATA_ID_SERNO], ATA_ID_SERNO_LEN))
return;
/* No serial number, thankfully very rare for CF */
- if (drive0->id->serial_no[0] == 0)
+ if (*(char *)&dev0->id[ATA_ID_SERNO] == 0)
return;
/* Appears to be an IDE flash adapter with decode bugs */
printk(KERN_WARNING "ide-probe: ignoring undecoded slave\n");
- drive1->present = 0;
+ dev1->dev_flags &= ~IDE_DFLAG_PRESENT;
}
EXPORT_SYMBOL_GPL(ide_undecoded_slave);
@@ -782,7 +782,8 @@
BUG_ON(hwif->present);
- if (hwif->drives[0].noprobe && hwif->drives[1].noprobe)
+ if ((hwif->drives[0].dev_flags & IDE_DFLAG_NOPROBE) &&
+ (hwif->drives[1].dev_flags & IDE_DFLAG_NOPROBE))
return -EACCES;
/*
@@ -804,9 +805,9 @@
*/
for (unit = 0; unit < MAX_DRIVES; ++unit) {
ide_drive_t *drive = &hwif->drives[unit];
- drive->dn = (hwif->channel ? 2 : 0) + unit;
+
(void) probe_for_drive(drive);
- if (drive->present)
+ if (drive->dev_flags & IDE_DFLAG_PRESENT)
rc = 0;
}
@@ -830,17 +831,19 @@
for (unit = 0; unit < MAX_DRIVES; unit++) {
ide_drive_t *drive = &hwif->drives[unit];
- if (drive->present && port_ops && port_ops->quirkproc)
- port_ops->quirkproc(drive);
+ if (drive->dev_flags & IDE_DFLAG_PRESENT) {
+ if (port_ops && port_ops->quirkproc)
+ port_ops->quirkproc(drive);
+ }
}
for (unit = 0; unit < MAX_DRIVES; ++unit) {
ide_drive_t *drive = &hwif->drives[unit];
- if (drive->present) {
+ if (drive->dev_flags & IDE_DFLAG_PRESENT) {
ide_set_max_pio(drive);
- drive->nice1 = 1;
+ drive->dev_flags |= IDE_DFLAG_NICE1;
if (hwif->dma_ops)
ide_set_dma(drive);
@@ -850,14 +853,14 @@
for (unit = 0; unit < MAX_DRIVES; ++unit) {
ide_drive_t *drive = &hwif->drives[unit];
- if (hwif->host_flags & IDE_HFLAG_NO_IO_32BIT)
- drive->no_io_32bit = 1;
+ if ((hwif->host_flags & IDE_HFLAG_NO_IO_32BIT) ||
+ drive->id[ATA_ID_DWORD_IO])
+ drive->dev_flags |= IDE_DFLAG_NO_IO_32BIT;
else
- drive->no_io_32bit = drive->id->dword_io ? 1 : 0;
+ drive->dev_flags &= ~IDE_DFLAG_NO_IO_32BIT;
}
}
-#if MAX_HWIFS > 1
/*
* save_match() is used to simplify logic in init_irq() below.
*
@@ -882,7 +885,6 @@
if (!m || m->irq != hwif->irq) /* don't undo a prior perfect match */
*match = new;
}
-#endif /* MAX_HWIFS > 1 */
/*
* init request queue
@@ -961,26 +963,33 @@
* - allocate the block device queue
* - link drive into the hwgroup
*/
-static void ide_port_setup_devices(ide_hwif_t *hwif)
+static int ide_port_setup_devices(ide_hwif_t *hwif)
{
- int i;
+ int i, j = 0;
mutex_lock(&ide_cfg_mtx);
for (i = 0; i < MAX_DRIVES; i++) {
ide_drive_t *drive = &hwif->drives[i];
- if (!drive->present)
+ if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0)
continue;
if (ide_init_queue(drive)) {
printk(KERN_ERR "ide: failed to init %s\n",
drive->name);
+ kfree(drive->id);
+ drive->id = NULL;
+ drive->dev_flags &= ~IDE_DFLAG_PRESENT;
continue;
}
+ j++;
+
ide_add_drive_to_hwgroup(drive);
}
mutex_unlock(&ide_cfg_mtx);
+
+ return j;
}
static ide_hwif_t *ide_ports[MAX_HWIFS];
@@ -1037,14 +1046,9 @@
ide_hwgroup_t *hwgroup;
ide_hwif_t *match = NULL;
-
- BUG_ON(in_interrupt());
- BUG_ON(irqs_disabled());
- BUG_ON(hwif == NULL);
-
mutex_lock(&ide_cfg_mtx);
hwif->hwgroup = NULL;
-#if MAX_HWIFS > 1
+
/*
* Group up with any other hwifs that share our irq(s).
*/
@@ -1069,7 +1073,7 @@
}
}
}
-#endif /* MAX_HWIFS > 1 */
+
/*
* If we are still without a hwgroup, then form a new one
*/
@@ -1116,7 +1120,8 @@
sa = IRQF_SHARED;
#endif /* __mc68000__ */
- if (IDE_CHIPSET_IS_PCI(hwif->chipset))
+ if (hwif->chipset == ide_pci || hwif->chipset == ide_cmd646 ||
+ hwif->chipset == ide_ali14xx)
sa = IRQF_SHARED;
if (io_ports->ctl_addr)
@@ -1167,12 +1172,13 @@
ide_hwif_t *hwif = data;
int unit = *part >> PARTN_BITS;
ide_drive_t *drive = &hwif->drives[unit];
- if (!drive->present)
+
+ if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0)
return NULL;
if (drive->media == ide_disk)
request_module("ide-disk");
- if (drive->scsi)
+ if (drive->dev_flags & IDE_DFLAG_SCSI)
request_module("ide-scsi");
if (drive->media == ide_cdrom || drive->media == ide_optical)
request_module("ide-cd");
@@ -1219,7 +1225,7 @@
void ide_init_disk(struct gendisk *disk, ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
- unsigned int unit = (drive->select.all >> 4) & 1;
+ unsigned int unit = drive->dn & 1;
disk->major = hwif->major;
disk->first_minor = unit << PARTN_BITS;
@@ -1262,7 +1268,7 @@
ide_remove_drive_from_hwgroup(drive);
kfree(drive->id);
drive->id = NULL;
- drive->present = 0;
+ drive->dev_flags &= ~IDE_DFLAG_PRESENT;
/* Messed up locking ... */
spin_unlock_irq(&ide_lock);
blk_cleanup_queue(drive->queue);
@@ -1341,11 +1347,9 @@
struct device *dev = &drive->gendev;
int ret;
- if (!drive->present)
+ if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0)
continue;
- ide_add_generic_settings(drive);
-
snprintf(dev->bus_id, BUS_ID_SIZE, "%u.%u", hwif->index, i);
dev->parent = &hwif->gendev;
dev->bus = &ide_bus_type;
@@ -1367,12 +1371,14 @@
for (i = 0; i < MAX_DRIVES; i++) {
ide_drive_t *drive = &hwif->drives[i];
+ drive->dn = i + hwif->channel * 2;
+
if (hwif->host_flags & IDE_HFLAG_IO_32BIT)
drive->io_32bit = 1;
if (hwif->host_flags & IDE_HFLAG_UNMASK_IRQS)
- drive->unmask = 1;
+ drive->dev_flags |= IDE_DFLAG_UNMASK;
if (hwif->host_flags & IDE_HFLAG_NO_UNMASK_IRQS)
- drive->no_unmask = 1;
+ drive->dev_flags |= IDE_DFLAG_NO_UNMASK;
if (port_ops && port_ops->init_dev)
port_ops->init_dev(drive);
@@ -1529,19 +1535,14 @@
* ports 0x1f0/0x170 (the ide0/ide1 defaults).
*/
mutex_lock(&ide_cfg_mtx);
- if (MAX_HWIFS == 1) {
- if (ide_indexes == 0 && i == 0)
- idx = 1;
+ if (bootable) {
+ if ((ide_indexes | i) != (1 << MAX_HWIFS) - 1)
+ idx = ffz(ide_indexes | i);
} else {
- if (bootable) {
- if ((ide_indexes | i) != (1 << MAX_HWIFS) - 1)
- idx = ffz(ide_indexes | i);
- } else {
- if ((ide_indexes | 3) != (1 << MAX_HWIFS) - 1)
- idx = ffz(ide_indexes | 3);
- else if ((ide_indexes & 3) != 3)
- idx = ffz(ide_indexes);
- }
+ if ((ide_indexes | 3) != (1 << MAX_HWIFS) - 1)
+ idx = ffz(ide_indexes | 3);
+ else if ((ide_indexes & 3) != 3)
+ idx = ffz(ide_indexes);
}
if (idx >= 0)
ide_indexes |= (1 << idx);
@@ -1557,8 +1558,7 @@
mutex_unlock(&ide_cfg_mtx);
}
-struct ide_host *ide_host_alloc_all(const struct ide_port_info *d,
- hw_regs_t **hws)
+struct ide_host *ide_host_alloc(const struct ide_port_info *d, hw_regs_t **hws)
{
struct ide_host *host;
int i;
@@ -1567,7 +1567,7 @@
if (host == NULL)
return NULL;
- for (i = 0; i < MAX_HWIFS; i++) {
+ for (i = 0; i < MAX_HOST_PORTS; i++) {
ide_hwif_t *hwif;
int idx;
@@ -1602,23 +1602,13 @@
if (hws[0])
host->dev[0] = hws[0]->dev;
- if (d)
+ if (d) {
+ host->init_chipset = d->init_chipset;
host->host_flags = d->host_flags;
+ }
return host;
}
-EXPORT_SYMBOL_GPL(ide_host_alloc_all);
-
-struct ide_host *ide_host_alloc(const struct ide_port_info *d, hw_regs_t **hws)
-{
- hw_regs_t *hws_all[MAX_HWIFS];
- int i;
-
- for (i = 0; i < MAX_HWIFS; i++)
- hws_all[i] = (i < 4) ? hws[i] : NULL;
-
- return ide_host_alloc_all(d, hws_all);
-}
EXPORT_SYMBOL_GPL(ide_host_alloc);
int ide_host_register(struct ide_host *host, const struct ide_port_info *d,
@@ -1627,7 +1617,7 @@
ide_hwif_t *hwif, *mate = NULL;
int i, j = 0;
- for (i = 0; i < MAX_HWIFS; i++) {
+ for (i = 0; i < MAX_HOST_PORTS; i++) {
hwif = host->ports[i];
if (hwif == NULL) {
@@ -1640,22 +1630,22 @@
if (d == NULL) {
mate = NULL;
- continue;
+ } else {
+ if ((i & 1) && mate) {
+ hwif->mate = mate;
+ mate->mate = hwif;
+ }
+
+ mate = (i & 1) ? NULL : hwif;
+
+ ide_init_port(hwif, i & 1, d);
+ ide_port_cable_detect(hwif);
}
- if ((i & 1) && mate) {
- hwif->mate = mate;
- mate->mate = hwif;
- }
-
- mate = (i & 1) ? NULL : hwif;
-
- ide_init_port(hwif, i & 1, d);
- ide_port_cable_detect(hwif);
ide_port_init_devices(hwif);
}
- for (i = 0; i < MAX_HWIFS; i++) {
+ for (i = 0; i < MAX_HOST_PORTS; i++) {
hwif = host->ports[i];
if (hwif == NULL)
@@ -1672,7 +1662,7 @@
ide_port_tune_devices(hwif);
}
- for (i = 0; i < MAX_HWIFS; i++) {
+ for (i = 0; i < MAX_HOST_PORTS; i++) {
hwif = host->ports[i];
if (hwif == NULL)
@@ -1685,10 +1675,13 @@
continue;
}
- j++;
-
if (hwif->present)
- ide_port_setup_devices(hwif);
+ if (ide_port_setup_devices(hwif) == 0) {
+ hwif->present = 0;
+ continue;
+ }
+
+ j++;
ide_acpi_init(hwif);
@@ -1696,7 +1689,7 @@
ide_acpi_port_init_devices(hwif);
}
- for (i = 0; i < MAX_HWIFS; i++) {
+ for (i = 0; i < MAX_HOST_PORTS; i++) {
hwif = host->ports[i];
if (hwif == NULL)
@@ -1709,7 +1702,7 @@
hwif_register_devices(hwif);
}
- for (i = 0; i < MAX_HWIFS; i++) {
+ for (i = 0; i < MAX_HOST_PORTS; i++) {
hwif = host->ports[i];
if (hwif == NULL)
@@ -1754,7 +1747,7 @@
ide_hwif_t *hwif;
int i;
- for (i = 0; i < MAX_HWIFS; i++) {
+ for (i = 0; i < MAX_HOST_PORTS; i++) {
hwif = host->ports[i];
if (hwif == NULL)
@@ -1772,7 +1765,7 @@
{
int i;
- for (i = 0; i < MAX_HWIFS; i++) {
+ for (i = 0; i < MAX_HOST_PORTS; i++) {
if (host->ports[i])
ide_unregister(host->ports[i]);
}
diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c
index f66c9c3..b269264 100644
--- a/drivers/ide/ide-proc.c
+++ b/drivers/ide/ide-proc.c
@@ -12,14 +12,6 @@
* "settings" files. e.g. "cat /proc/ide0/hda/settings"
* To write a new value "val" into a specific setting "name", use:
* echo "name:val" >/proc/ide/ide0/hda/settings
- *
- * Also useful, "cat /proc/ide0/hda/[identify, smart_values,
- * smart_thresholds, capabilities]" will issue an IDENTIFY /
- * PACKET_IDENTIFY / SMART_READ_VALUES / SMART_READ_THRESHOLDS /
- * SENSE CAPABILITIES command to /dev/hda, and then dump out the
- * returned data as 256 16-bit words. The "hdparm" utility will
- * be updated someday soon to use this mechanism.
- *
*/
#include <linux/module.h>
@@ -31,7 +23,6 @@
#include <linux/mm.h>
#include <linux/pci.h>
#include <linux/ctype.h>
-#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/seq_file.h>
@@ -109,13 +100,14 @@
err = taskfile_lib_get_identify(drive, page);
if (!err) {
- char *out = ((char *)page) + (SECTOR_WORDS * 4);
+ char *out = (char *)page + SECTOR_SIZE;
+
page = out;
do {
out += sprintf(out, "%04x%c",
le16_to_cpup(val), (++i & 7) ? ' ' : '\n');
val += 1;
- } while (i < (SECTOR_WORDS * 2));
+ } while (i < SECTOR_SIZE / 2);
len = out - page;
}
}
@@ -123,140 +115,25 @@
}
/**
- * __ide_add_setting - add an ide setting option
- * @drive: drive to use
- * @name: setting name
- * @rw: true if the function is read write
- * @data_type: type of data
- * @min: range minimum
- * @max: range maximum
- * @mul_factor: multiplication scale
- * @div_factor: divison scale
- * @data: private data field
- * @set: setting
- * @auto_remove: setting auto removal flag
- *
- * Removes the setting named from the device if it is present.
- * The function takes the settings_lock to protect against
- * parallel changes. This function must not be called from IRQ
- * context. Returns 0 on success or -1 on failure.
- *
- * BUGS: This code is seriously over-engineered. There is also
- * magic about how the driver specific features are setup. If
- * a driver is attached we assume the driver settings are auto
- * remove.
- */
-
-static int __ide_add_setting(ide_drive_t *drive, const char *name, int rw, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set, int auto_remove)
-{
- ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting = NULL;
-
- mutex_lock(&ide_setting_mtx);
- while ((*p) && strcmp((*p)->name, name) < 0)
- p = &((*p)->next);
- if ((setting = kzalloc(sizeof(*setting), GFP_KERNEL)) == NULL)
- goto abort;
- if ((setting->name = kmalloc(strlen(name) + 1, GFP_KERNEL)) == NULL)
- goto abort;
- strcpy(setting->name, name);
- setting->rw = rw;
- setting->data_type = data_type;
- setting->min = min;
- setting->max = max;
- setting->mul_factor = mul_factor;
- setting->div_factor = div_factor;
- setting->data = data;
- setting->set = set;
-
- setting->next = *p;
- if (auto_remove)
- setting->auto_remove = 1;
- *p = setting;
- mutex_unlock(&ide_setting_mtx);
- return 0;
-abort:
- mutex_unlock(&ide_setting_mtx);
- kfree(setting);
- return -1;
-}
-
-int ide_add_setting(ide_drive_t *drive, const char *name, int rw, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set)
-{
- return __ide_add_setting(drive, name, rw, data_type, min, max, mul_factor, div_factor, data, set, 1);
-}
-
-EXPORT_SYMBOL(ide_add_setting);
-
-/**
- * __ide_remove_setting - remove an ide setting option
- * @drive: drive to use
+ * ide_find_setting - find a specific setting
+ * @st: setting table pointer
* @name: setting name
*
- * Removes the setting named from the device if it is present.
- * The caller must hold the setting semaphore.
- */
-
-static void __ide_remove_setting(ide_drive_t *drive, char *name)
-{
- ide_settings_t **p, *setting;
-
- p = (ide_settings_t **) &drive->settings;
-
- while ((*p) && strcmp((*p)->name, name))
- p = &((*p)->next);
- setting = (*p);
- if (setting == NULL)
- return;
-
- (*p) = setting->next;
-
- kfree(setting->name);
- kfree(setting);
-}
-
-/**
- * auto_remove_settings - remove driver specific settings
- * @drive: drive
- *
- * Automatically remove all the driver specific settings for this
- * drive. This function may not be called from IRQ context. The
- * caller must hold ide_setting_mtx.
- */
-
-static void auto_remove_settings(ide_drive_t *drive)
-{
- ide_settings_t *setting;
-repeat:
- setting = drive->settings;
- while (setting) {
- if (setting->auto_remove) {
- __ide_remove_setting(drive, setting->name);
- goto repeat;
- }
- setting = setting->next;
- }
-}
-
-/**
- * ide_find_setting_by_name - find a drive specific setting
- * @drive: drive to scan
- * @name: setting name
- *
- * Scan's the device setting table for a matching entry and returns
+ * Scan's the setting table for a matching entry and returns
* this or NULL if no entry is found. The caller must hold the
* setting semaphore
*/
-static ide_settings_t *ide_find_setting_by_name(ide_drive_t *drive, char *name)
+static
+const struct ide_proc_devset *ide_find_setting(const struct ide_proc_devset *st,
+ char *name)
{
- ide_settings_t *setting = drive->settings;
-
- while (setting) {
- if (strcmp(setting->name, name) == 0)
+ while (st->name) {
+ if (strcmp(st->name, name) == 0)
break;
- setting = setting->next;
+ st++;
}
- return setting;
+ return st->name ? st : NULL;
}
/**
@@ -272,26 +149,20 @@
* be told apart
*/
-static int ide_read_setting(ide_drive_t *drive, ide_settings_t *setting)
+static int ide_read_setting(ide_drive_t *drive,
+ const struct ide_proc_devset *setting)
{
- int val = -EINVAL;
- unsigned long flags;
+ const struct ide_devset *ds = setting->setting;
+ int val = -EINVAL;
- if ((setting->rw & SETTING_READ)) {
+ if (ds->get) {
+ unsigned long flags;
+
spin_lock_irqsave(&ide_lock, flags);
- switch (setting->data_type) {
- case TYPE_BYTE:
- val = *((u8 *) setting->data);
- break;
- case TYPE_SHORT:
- val = *((u16 *) setting->data);
- break;
- case TYPE_INT:
- val = *((u32 *) setting->data);
- break;
- }
+ val = ds->get(drive);
spin_unlock_irqrestore(&ide_lock, flags);
}
+
return val;
}
@@ -313,33 +184,23 @@
* The current scheme of polling is kludgy, though safe enough.
*/
-static int ide_write_setting(ide_drive_t *drive, ide_settings_t *setting, int val)
+static int ide_write_setting(ide_drive_t *drive,
+ const struct ide_proc_devset *setting, int val)
{
+ const struct ide_devset *ds = setting->setting;
+
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
- if (setting->set)
- return setting->set(drive, val);
- if (!(setting->rw & SETTING_WRITE))
+ if (!ds->set)
return -EPERM;
- if (val < setting->min || val > setting->max)
+ if ((ds->flags & DS_SYNC)
+ && (val < setting->min || val > setting->max))
return -EINVAL;
- if (ide_spin_wait_hwgroup(drive))
- return -EBUSY;
- switch (setting->data_type) {
- case TYPE_BYTE:
- *((u8 *) setting->data) = val;
- break;
- case TYPE_SHORT:
- *((u16 *) setting->data) = val;
- break;
- case TYPE_INT:
- *((u32 *) setting->data) = val;
- break;
- }
- spin_unlock_irq(&ide_lock);
- return 0;
+ return ide_devset_execute(drive, ds, val);
}
+ide_devset_get(xfer_rate, current_speed);
+
static int set_xfer_rate (ide_drive_t *drive, int arg)
{
ide_task_t task;
@@ -349,7 +210,7 @@
return -EINVAL;
memset(&task, 0, sizeof(task));
- task.tf.command = WIN_SETFEATURES;
+ task.tf.command = ATA_CMD_SET_FEATURES;
task.tf.feature = SETFEATURES_XFER;
task.tf.nsect = (u8)arg;
task.tf_flags = IDE_TFLAG_OUT_FEATURE | IDE_TFLAG_OUT_NSECT |
@@ -364,29 +225,23 @@
return err;
}
-/**
- * ide_add_generic_settings - generic ide settings
- * @drive: drive being configured
- *
- * Add the generic parts of the system settings to the /proc files.
- * The caller must not be holding the ide_setting_mtx.
- */
+ide_devset_rw(current_speed, xfer_rate);
+ide_devset_rw_field(init_speed, init_speed);
+ide_devset_rw_flag(nice1, IDE_DFLAG_NICE1);
+ide_devset_rw_field(number, dn);
-void ide_add_generic_settings (ide_drive_t *drive)
-{
-/*
- * drive setting name read/write access data type min max mul_factor div_factor data pointer set function
- */
- __ide_add_setting(drive, "io_32bit", drive->no_io_32bit ? SETTING_READ : SETTING_RW, TYPE_BYTE, 0, 1 + (SUPPORT_VLB_SYNC << 1), 1, 1, &drive->io_32bit, set_io_32bit, 0);
- __ide_add_setting(drive, "keepsettings", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->keep_settings, NULL, 0);
- __ide_add_setting(drive, "nice1", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->nice1, NULL, 0);
- __ide_add_setting(drive, "pio_mode", SETTING_WRITE, TYPE_BYTE, 0, 255, 1, 1, NULL, set_pio_mode, 0);
- __ide_add_setting(drive, "unmaskirq", drive->no_unmask ? SETTING_READ : SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->unmask, NULL, 0);
- __ide_add_setting(drive, "using_dma", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->using_dma, set_using_dma, 0);
- __ide_add_setting(drive, "init_speed", SETTING_RW, TYPE_BYTE, 0, 70, 1, 1, &drive->init_speed, NULL, 0);
- __ide_add_setting(drive, "current_speed", SETTING_RW, TYPE_BYTE, 0, 70, 1, 1, &drive->current_speed, set_xfer_rate, 0);
- __ide_add_setting(drive, "number", SETTING_RW, TYPE_BYTE, 0, 3, 1, 1, &drive->dn, NULL, 0);
-}
+static const struct ide_proc_devset ide_generic_settings[] = {
+ IDE_PROC_DEVSET(current_speed, 0, 70),
+ IDE_PROC_DEVSET(init_speed, 0, 70),
+ IDE_PROC_DEVSET(io_32bit, 0, 1 + (SUPPORT_VLB_SYNC << 1)),
+ IDE_PROC_DEVSET(keepsettings, 0, 1),
+ IDE_PROC_DEVSET(nice1, 0, 1),
+ IDE_PROC_DEVSET(number, 0, 3),
+ IDE_PROC_DEVSET(pio_mode, 0, 255),
+ IDE_PROC_DEVSET(unmaskirq, 0, 1),
+ IDE_PROC_DEVSET(using_dma, 0, 1),
+ { 0 },
+};
static void proc_ide_settings_warn(void)
{
@@ -403,19 +258,32 @@
static int proc_ide_read_settings
(char *page, char **start, off_t off, int count, int *eof, void *data)
{
+ const struct ide_proc_devset *setting, *g, *d;
+ const struct ide_devset *ds;
ide_drive_t *drive = (ide_drive_t *) data;
- ide_settings_t *setting = (ide_settings_t *) drive->settings;
char *out = page;
int len, rc, mul_factor, div_factor;
proc_ide_settings_warn();
mutex_lock(&ide_setting_mtx);
+ g = ide_generic_settings;
+ d = drive->settings;
out += sprintf(out, "name\t\t\tvalue\t\tmin\t\tmax\t\tmode\n");
out += sprintf(out, "----\t\t\t-----\t\t---\t\t---\t\t----\n");
- while (setting) {
- mul_factor = setting->mul_factor;
- div_factor = setting->div_factor;
+ while (g->name || (d && d->name)) {
+ /* read settings in the alphabetical order */
+ if (g->name && d && d->name) {
+ if (strcmp(d->name, g->name) < 0)
+ setting = d++;
+ else
+ setting = g++;
+ } else if (d && d->name) {
+ setting = d++;
+ } else
+ setting = g++;
+ mul_factor = setting->mulf ? setting->mulf(drive) : 1;
+ div_factor = setting->divf ? setting->divf(drive) : 1;
out += sprintf(out, "%-24s", setting->name);
rc = ide_read_setting(drive, setting);
if (rc >= 0)
@@ -423,12 +291,12 @@
else
out += sprintf(out, "%-16s", "write-only");
out += sprintf(out, "%-16d%-16d", (setting->min * mul_factor + div_factor - 1) / div_factor, setting->max * mul_factor / div_factor);
- if (setting->rw & SETTING_READ)
+ ds = setting->setting;
+ if (ds->get)
out += sprintf(out, "r");
- if (setting->rw & SETTING_WRITE)
+ if (ds->set)
out += sprintf(out, "w");
out += sprintf(out, "\n");
- setting = setting->next;
}
len = out - page;
mutex_unlock(&ide_setting_mtx);
@@ -442,9 +310,10 @@
{
ide_drive_t *drive = (ide_drive_t *) data;
char name[MAX_LEN + 1];
- int for_real = 0;
+ int for_real = 0, mul_factor, div_factor;
unsigned long n;
- ide_settings_t *setting;
+
+ const struct ide_proc_devset *setting;
char *buf, *s;
if (!capable(CAP_SYS_ADMIN))
@@ -512,13 +381,21 @@
}
mutex_lock(&ide_setting_mtx);
- setting = ide_find_setting_by_name(drive, name);
+ /* generic settings first, then driver specific ones */
+ setting = ide_find_setting(ide_generic_settings, name);
if (!setting) {
- mutex_unlock(&ide_setting_mtx);
- goto parse_error;
+ if (drive->settings)
+ setting = ide_find_setting(drive->settings, name);
+ if (!setting) {
+ mutex_unlock(&ide_setting_mtx);
+ goto parse_error;
+ }
}
- if (for_real)
- ide_write_setting(drive, setting, val * setting->div_factor / setting->mul_factor);
+ if (for_real) {
+ mul_factor = setting->mulf ? setting->mulf(drive) : 1;
+ div_factor = setting->divf ? setting->divf(drive) : 1;
+ ide_write_setting(drive, setting, val * div_factor / mul_factor);
+ }
mutex_unlock(&ide_setting_mtx);
}
} while (!for_real++);
@@ -561,11 +438,10 @@
(char *page, char **start, off_t off, int count, int *eof, void *data)
{
ide_drive_t *drive = (ide_drive_t *) data;
- struct hd_driveid *id = drive->id;
+ char *m = (char *)&drive->id[ATA_ID_PROD];
int len;
- len = sprintf(page, "%.40s\n",
- (id && id->model[0]) ? (char *)id->model : "(none)");
+ len = sprintf(page, "%.40s\n", m[0] ? m : "(none)");
PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
}
@@ -690,6 +566,10 @@
void ide_proc_register_driver(ide_drive_t *drive, ide_driver_t *driver)
{
+ mutex_lock(&ide_setting_mtx);
+ drive->settings = driver->settings;
+ mutex_unlock(&ide_setting_mtx);
+
ide_add_proc_entries(drive->proc, driver->proc, drive);
}
@@ -726,7 +606,7 @@
* OTOH both ide_{read,write}_setting are only ever used under
* ide_setting_mtx.
*/
- auto_remove_settings(drive);
+ drive->settings = NULL;
spin_unlock_irqrestore(&ide_lock, flags);
mutex_unlock(&ide_setting_mtx);
}
@@ -742,9 +622,7 @@
for (d = 0; d < MAX_DRIVES; d++) {
ide_drive_t *drive = &hwif->drives[d];
- if (!drive->present)
- continue;
- if (drive->proc)
+ if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0 || drive->proc)
continue;
drive->proc = proc_mkdir(drive->name, parent);
diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c
index 3833189..25ac60f 100644
--- a/drivers/ide/ide-tape.c
+++ b/drivers/ide/ide-tape.c
@@ -15,6 +15,8 @@
* Documentation/ide/ChangeLog.ide-tape.1995-2002
*/
+#define DRV_NAME "ide-tape"
+
#define IDETAPE_VERSION "1.20"
#include <linux/module.h>
@@ -54,8 +56,6 @@
DBG_CHRDEV = (1 << 2),
/* all remaining procedures */
DBG_PROCS = (1 << 3),
- /* buffer alloc info (pc_stack & rq_stack) */
- DBG_PCRQ_STACK = (1 << 4),
};
/* define to see debug info */
@@ -81,26 +81,6 @@
#define IDETAPE_MAX_PC_RETRIES 3
/*
- * With each packet command, we allocate a buffer of IDETAPE_PC_BUFFER_SIZE
- * bytes. This is used for several packet commands (Not for READ/WRITE commands)
- */
-#define IDETAPE_PC_BUFFER_SIZE 256
-
-/*
- * In various places in the driver, we need to allocate storage
- * for packet commands and requests, which will remain valid while
- * we leave the driver to wait for an interrupt or a timeout event.
- */
-#define IDETAPE_PC_STACK (10 + IDETAPE_MAX_PC_RETRIES)
-
-/*
- * Some drives (for example, Seagate STT3401A Travan) require a very long
- * timeout, because they don't return an interrupt or clear their busy bit
- * until after the command completes (even retension commands).
- */
-#define IDETAPE_WAIT_CMD (900*HZ)
-
-/*
* The following parameter is used to select the point in the internal tape fifo
* in which we will start to refill the buffer. Decreasing the following
* parameter will improve the system's latency and interactive response, while
@@ -172,20 +152,6 @@
#define IDETAPE_LU_RETENSION_MASK 2
#define IDETAPE_LU_EOT_MASK 4
-/*
- * Special requests for our block device strategy routine.
- *
- * In order to service a character device command, we add special requests to
- * the tail of our block device request queue and wait for their completion.
- */
-
-enum {
- REQ_IDETAPE_PC1 = (1 << 0), /* packet command (first stage) */
- REQ_IDETAPE_PC2 = (1 << 1), /* packet command (second stage) */
- REQ_IDETAPE_READ = (1 << 2),
- REQ_IDETAPE_WRITE = (1 << 3),
-};
-
/* Error codes returned in rq->errors to the higher part of the driver. */
#define IDETAPE_ERROR_GENERAL 101
#define IDETAPE_ERROR_FILEMARK 102
@@ -206,31 +172,15 @@
struct kref kref;
/*
- * Since a typical character device operation requires more
- * than one packet command, we provide here enough memory
- * for the maximum of interconnected packet commands.
- * The packet commands are stored in the circular array pc_stack.
- * pc_stack_index points to the last used entry, and warps around
- * to the start when we get to the last array entry.
- *
- * pc points to the current processed packet command.
- *
* failed_pc points to the last failed packet command, or contains
* NULL if we do not need to retry any packet command. This is
* required since an additional packet command is needed before the
* retry, to get detailed information on what went wrong.
*/
- /* Current packet command */
- struct ide_atapi_pc *pc;
/* Last failed packet command */
struct ide_atapi_pc *failed_pc;
- /* Packet command stack */
- struct ide_atapi_pc pc_stack[IDETAPE_PC_STACK];
- /* Next free packet command storage space */
- int pc_stack_index;
- struct request rq_stack[IDETAPE_PC_STACK];
- /* We implement a circular array */
- int rq_stack_index;
+ /* used by REQ_IDETAPE_{READ,WRITE} requests */
+ struct ide_atapi_pc queued_pc;
/*
* DSC polling variables.
@@ -317,11 +267,6 @@
static struct class *idetape_sysfs_class;
-#define to_ide_tape(obj) container_of(obj, struct ide_tape_obj, kref)
-
-#define ide_tape_g(disk) \
- container_of((disk)->private_data, struct ide_tape_obj, driver)
-
static void ide_tape_release(struct kref *);
static struct ide_tape_obj *ide_tape_get(struct gendisk *disk)
@@ -329,7 +274,7 @@
struct ide_tape_obj *tape = NULL;
mutex_lock(&idetape_ref_mutex);
- tape = ide_tape_g(disk);
+ tape = ide_drv_g(disk, ide_tape_obj);
if (tape) {
if (ide_device_get(tape->drive))
tape = NULL;
@@ -356,8 +301,6 @@
*/
static struct ide_tape_obj *idetape_devs[MAX_HWIFS * MAX_DRIVES];
-#define ide_tape_f(file) ((file)->private_data)
-
static struct ide_tape_obj *ide_tape_chrdev_get(unsigned int i)
{
struct ide_tape_obj *tape = NULL;
@@ -451,47 +394,6 @@
}
/*
- * idetape_next_pc_storage returns a pointer to a place in which we can
- * safely store a packet command, even though we intend to leave the
- * driver. A storage space for a maximum of IDETAPE_PC_STACK packet
- * commands is allocated at initialization time.
- */
-static struct ide_atapi_pc *idetape_next_pc_storage(ide_drive_t *drive)
-{
- idetape_tape_t *tape = drive->driver_data;
-
- debug_log(DBG_PCRQ_STACK, "pc_stack_index=%d\n", tape->pc_stack_index);
-
- if (tape->pc_stack_index == IDETAPE_PC_STACK)
- tape->pc_stack_index = 0;
- return (&tape->pc_stack[tape->pc_stack_index++]);
-}
-
-/*
- * idetape_next_rq_storage is used along with idetape_next_pc_storage.
- * Since we queue packet commands in the request queue, we need to
- * allocate a request, along with the allocation of a packet command.
- */
-
-/**************************************************************
- * *
- * This should get fixed to use kmalloc(.., GFP_ATOMIC) *
- * followed later on by kfree(). -ml *
- * *
- **************************************************************/
-
-static struct request *idetape_next_rq_storage(ide_drive_t *drive)
-{
- idetape_tape_t *tape = drive->driver_data;
-
- debug_log(DBG_PCRQ_STACK, "rq_stack_index=%d\n", tape->rq_stack_index);
-
- if (tape->rq_stack_index == IDETAPE_PC_STACK)
- tape->rq_stack_index = 0;
- return (&tape->rq_stack[tape->rq_stack_index++]);
-}
-
-/*
* called on each failed packet command retry to analyze the request sense. We
* currently do not utilize this information.
*/
@@ -606,14 +508,19 @@
return 0;
}
-static void ide_tape_callback(ide_drive_t *drive)
+static void ide_tape_handle_dsc(ide_drive_t *);
+
+static void ide_tape_callback(ide_drive_t *drive, int dsc)
{
idetape_tape_t *tape = drive->driver_data;
- struct ide_atapi_pc *pc = tape->pc;
+ struct ide_atapi_pc *pc = drive->pc;
int uptodate = pc->error ? 0 : 1;
debug_log(DBG_PROCS, "Enter %s\n", __func__);
+ if (dsc)
+ ide_tape_handle_dsc(drive);
+
if (tape->failed_pc == pc)
tape->failed_pc = NULL;
@@ -642,7 +549,7 @@
if (pc->error)
uptodate = pc->error;
} else if (pc->c[0] == READ_POSITION && uptodate) {
- u8 *readpos = tape->pc->buf;
+ u8 *readpos = pc->buf;
debug_log(DBG_SENSE, "BOP - %s\n",
(readpos[0] & 0x80) ? "Yes" : "No");
@@ -667,79 +574,6 @@
idetape_end_request(drive, uptodate, 0);
}
-static void idetape_init_pc(struct ide_atapi_pc *pc)
-{
- memset(pc->c, 0, 12);
- pc->retries = 0;
- pc->flags = 0;
- pc->req_xfer = 0;
- pc->buf = pc->pc_buf;
- pc->buf_size = IDETAPE_PC_BUFFER_SIZE;
- pc->bh = NULL;
- pc->b_data = NULL;
-}
-
-static void idetape_create_request_sense_cmd(struct ide_atapi_pc *pc)
-{
- idetape_init_pc(pc);
- pc->c[0] = REQUEST_SENSE;
- pc->c[4] = 20;
- pc->req_xfer = 20;
-}
-
-static void idetape_init_rq(struct request *rq, u8 cmd)
-{
- blk_rq_init(NULL, rq);
- rq->cmd_type = REQ_TYPE_SPECIAL;
- rq->cmd[13] = cmd;
-}
-
-/*
- * Generate a new packet command request in front of the request queue, before
- * the current request, so that it will be processed immediately, on the next
- * pass through the driver. The function below is called from the request
- * handling part of the driver (the "bottom" part). Safe storage for the request
- * should be allocated with ide_tape_next_{pc,rq}_storage() prior to that.
- *
- * Memory for those requests is pre-allocated at initialization time, and is
- * limited to IDETAPE_PC_STACK requests. We assume that we have enough space for
- * the maximum possible number of inter-dependent packet commands.
- *
- * The higher level of the driver - The ioctl handler and the character device
- * handling functions should queue request to the lower level part and wait for
- * their completion using idetape_queue_pc_tail or idetape_queue_rw_tail.
- */
-static void idetape_queue_pc_head(ide_drive_t *drive, struct ide_atapi_pc *pc,
- struct request *rq)
-{
- struct ide_tape_obj *tape = drive->driver_data;
-
- idetape_init_rq(rq, REQ_IDETAPE_PC1);
- rq->cmd_flags |= REQ_PREEMPT;
- rq->buffer = (char *) pc;
- rq->rq_disk = tape->disk;
- memcpy(rq->cmd, pc->c, 12);
- ide_do_drive_cmd(drive, rq);
-}
-
-/*
- * idetape_retry_pc is called when an error was detected during the
- * last packet command. We queue a request sense packet command in
- * the head of the request list.
- */
-static void idetape_retry_pc(ide_drive_t *drive)
-{
- struct ide_atapi_pc *pc;
- struct request *rq;
-
- (void)ide_read_error(drive);
- pc = idetape_next_pc_storage(drive);
- rq = idetape_next_rq_storage(drive);
- idetape_create_request_sense_cmd(pc);
- set_bit(IDE_AFLAG_IGNORE_DSC, &drive->atapi_flags);
- idetape_queue_pc_head(drive, pc, rq);
-}
-
/*
* Postpone the current request so that ide.c will be able to service requests
* from another device on the same hwgroup while we are polling for DSC.
@@ -766,44 +600,30 @@
idetape_postpone_request(drive);
}
-static void ide_tape_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc,
+static int ide_tape_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc,
unsigned int bcount, int write)
{
if (write)
idetape_output_buffers(drive, pc, bcount);
else
idetape_input_buffers(drive, pc, bcount);
-}
-/*
- * This is the usual interrupt handler which will be called during a packet
- * command. We will transfer some of the data (as requested by the drive) and
- * will re-point interrupt handler to us. When data transfer is finished, we
- * will act according to the algorithm described before
- * idetape_issue_pc.
- */
-static ide_startstop_t idetape_pc_intr(ide_drive_t *drive)
-{
- idetape_tape_t *tape = drive->driver_data;
-
- return ide_pc_intr(drive, tape->pc, idetape_pc_intr, IDETAPE_WAIT_CMD,
- NULL, idetape_update_buffers, idetape_retry_pc,
- ide_tape_handle_dsc, ide_tape_io_buffers);
+ return bcount;
}
/*
* Packet Command Interface
*
- * The current Packet Command is available in tape->pc, and will not change
+ * The current Packet Command is available in drive->pc, and will not change
* until we finish handling it. Each packet command is associated with a
* callback function that will be called when the command is finished.
*
* The handling will be done in three stages:
*
* 1. idetape_issue_pc will send the packet command to the drive, and will set
- * the interrupt handler to idetape_pc_intr.
+ * the interrupt handler to ide_pc_intr.
*
- * 2. On each interrupt, idetape_pc_intr will be called. This step will be
+ * 2. On each interrupt, ide_pc_intr will be called. This step will be
* repeated until the device signals us that no more interrupts will be issued.
*
* 3. ATAPI Tape media access commands have immediate status with a delayed
@@ -827,20 +647,13 @@
* again, the callback function will be called and then we will handle the next
* request.
*/
-static ide_startstop_t idetape_transfer_pc(ide_drive_t *drive)
-{
- idetape_tape_t *tape = drive->driver_data;
-
- return ide_transfer_pc(drive, tape->pc, idetape_pc_intr,
- IDETAPE_WAIT_CMD, NULL);
-}
static ide_startstop_t idetape_issue_pc(ide_drive_t *drive,
struct ide_atapi_pc *pc)
{
idetape_tape_t *tape = drive->driver_data;
- if (tape->pc->c[0] == REQUEST_SENSE &&
+ if (drive->pc->c[0] == REQUEST_SENSE &&
pc->c[0] == REQUEST_SENSE) {
printk(KERN_ERR "ide-tape: possible ide-tape.c bug - "
"Two request sense in serial were issued\n");
@@ -848,8 +661,9 @@
if (tape->failed_pc == NULL && pc->c[0] != REQUEST_SENSE)
tape->failed_pc = pc;
+
/* Set the current packet command */
- tape->pc = pc;
+ drive->pc = pc;
if (pc->retries > IDETAPE_MAX_PC_RETRIES ||
(pc->flags & PC_FLAG_ABORT)) {
@@ -873,21 +687,20 @@
pc->error = IDETAPE_ERROR_GENERAL;
}
tape->failed_pc = NULL;
- drive->pc_callback(drive);
+ drive->pc_callback(drive, 0);
return ide_stopped;
}
debug_log(DBG_SENSE, "Retry #%d, cmd = %02X\n", pc->retries, pc->c[0]);
pc->retries++;
- return ide_issue_pc(drive, pc, idetape_transfer_pc,
- IDETAPE_WAIT_CMD, NULL);
+ return ide_issue_pc(drive, WAIT_TAPE_CMD, NULL);
}
/* A mode sense command is used to "sense" tape parameters. */
static void idetape_create_mode_sense_cmd(struct ide_atapi_pc *pc, u8 page_code)
{
- idetape_init_pc(pc);
+ ide_init_pc(pc);
pc->c[0] = MODE_SENSE;
if (page_code != IDETAPE_BLOCK_DESCRIPTOR)
/* DBD = 1 - Don't return block descriptors */
@@ -915,19 +728,19 @@
{
ide_hwif_t *hwif = drive->hwif;
idetape_tape_t *tape = drive->driver_data;
- struct ide_atapi_pc *pc = tape->pc;
+ struct ide_atapi_pc *pc = drive->pc;
u8 stat;
stat = hwif->tp_ops->read_status(hwif);
- if (stat & SEEK_STAT) {
- if (stat & ERR_STAT) {
+ if (stat & ATA_DSC) {
+ if (stat & ATA_ERR) {
/* Error detected */
if (pc->c[0] != TEST_UNIT_READY)
printk(KERN_ERR "ide-tape: %s: I/O error, ",
tape->name);
/* Retry operation */
- idetape_retry_pc(drive);
+ ide_retry_pc(drive, tape->disk);
return ide_stopped;
}
pc->error = 0;
@@ -935,7 +748,7 @@
pc->error = IDETAPE_ERROR_GENERAL;
tape->failed_pc = NULL;
}
- drive->pc_callback(drive);
+ drive->pc_callback(drive, 0);
return ide_stopped;
}
@@ -946,7 +759,7 @@
struct idetape_bh *bh = (struct idetape_bh *)rq->special;
unsigned int length = rq->current_nr_sectors;
- idetape_init_pc(pc);
+ ide_init_pc(pc);
put_unaligned(cpu_to_be32(length), (unsigned int *) &pc->c[1]);
pc->c[1] = 1;
pc->bh = bh;
@@ -978,9 +791,10 @@
struct request *postponed_rq = tape->postponed_rq;
u8 stat;
- debug_log(DBG_SENSE, "sector: %ld, nr_sectors: %ld,"
- " current_nr_sectors: %d\n",
- rq->sector, rq->nr_sectors, rq->current_nr_sectors);
+ debug_log(DBG_SENSE, "sector: %llu, nr_sectors: %lu,"
+ " current_nr_sectors: %u\n",
+ (unsigned long long)rq->sector, rq->nr_sectors,
+ rq->current_nr_sectors);
if (!blk_special_request(rq)) {
/* We do not support buffer cache originated requests. */
@@ -991,7 +805,7 @@
}
/* Retry a failed packet command */
- if (tape->failed_pc && tape->pc->c[0] == REQUEST_SENSE) {
+ if (tape->failed_pc && drive->pc->c[0] == REQUEST_SENSE) {
pc = tape->failed_pc;
goto out;
}
@@ -1012,16 +826,17 @@
*/
stat = hwif->tp_ops->read_status(hwif);
- if (!drive->dsc_overlap && !(rq->cmd[13] & REQ_IDETAPE_PC2))
+ if ((drive->dev_flags & IDE_DFLAG_DSC_OVERLAP) == 0 &&
+ (rq->cmd[13] & REQ_IDETAPE_PC2) == 0)
set_bit(IDE_AFLAG_IGNORE_DSC, &drive->atapi_flags);
- if (drive->post_reset == 1) {
+ if (drive->dev_flags & IDE_DFLAG_POST_RESET) {
set_bit(IDE_AFLAG_IGNORE_DSC, &drive->atapi_flags);
- drive->post_reset = 0;
+ drive->dev_flags &= ~IDE_DFLAG_POST_RESET;
}
if (!test_and_clear_bit(IDE_AFLAG_IGNORE_DSC, &drive->atapi_flags) &&
- (stat & SEEK_STAT) == 0) {
+ (stat & ATA_DSC) == 0) {
if (postponed_rq == NULL) {
tape->dsc_polling_start = jiffies;
tape->dsc_poll_freq = tape->best_dsc_rw_freq;
@@ -1043,12 +858,12 @@
return ide_stopped;
}
if (rq->cmd[13] & REQ_IDETAPE_READ) {
- pc = idetape_next_pc_storage(drive);
+ pc = &tape->queued_pc;
ide_tape_create_rw_cmd(tape, pc, rq, READ_6);
goto out;
}
if (rq->cmd[13] & REQ_IDETAPE_WRITE) {
- pc = idetape_next_pc_storage(drive);
+ pc = &tape->queued_pc;
ide_tape_create_rw_cmd(tape, pc, rq, WRITE_6);
goto out;
}
@@ -1235,77 +1050,30 @@
static void idetape_create_write_filemark_cmd(ide_drive_t *drive,
struct ide_atapi_pc *pc, int write_filemark)
{
- idetape_init_pc(pc);
+ ide_init_pc(pc);
pc->c[0] = WRITE_FILEMARKS;
pc->c[4] = write_filemark;
pc->flags |= PC_FLAG_WAIT_FOR_DSC;
}
-static void idetape_create_test_unit_ready_cmd(struct ide_atapi_pc *pc)
-{
- idetape_init_pc(pc);
- pc->c[0] = TEST_UNIT_READY;
-}
-
-/*
- * We add a special packet command request to the tail of the request queue, and
- * wait for it to be serviced. This is not to be called from within the request
- * handling part of the driver! We allocate here data on the stack and it is
- * valid until the request is finished. This is not the case for the bottom part
- * of the driver, where we are always leaving the functions to wait for an
- * interrupt or a timer event.
- *
- * From the bottom part of the driver, we should allocate safe memory using
- * idetape_next_pc_storage() and ide_tape_next_rq_storage(), and add the request
- * to the request list without waiting for it to be serviced! In that case, we
- * usually use idetape_queue_pc_head().
- */
-static int idetape_queue_pc_tail(ide_drive_t *drive, struct ide_atapi_pc *pc)
-{
- struct ide_tape_obj *tape = drive->driver_data;
- struct request *rq;
- int error;
-
- rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
- rq->cmd_type = REQ_TYPE_SPECIAL;
- rq->cmd[13] = REQ_IDETAPE_PC1;
- rq->buffer = (char *)pc;
- memcpy(rq->cmd, pc->c, 12);
- error = blk_execute_rq(drive->queue, tape->disk, rq, 0);
- blk_put_request(rq);
- return error;
-}
-
-static void idetape_create_load_unload_cmd(ide_drive_t *drive,
- struct ide_atapi_pc *pc, int cmd)
-{
- idetape_init_pc(pc);
- pc->c[0] = START_STOP;
- pc->c[4] = cmd;
- pc->flags |= PC_FLAG_WAIT_FOR_DSC;
-}
-
static int idetape_wait_ready(ide_drive_t *drive, unsigned long timeout)
{
idetape_tape_t *tape = drive->driver_data;
- struct ide_atapi_pc pc;
+ struct gendisk *disk = tape->disk;
int load_attempted = 0;
/* Wait for the tape to become ready */
set_bit(IDE_AFLAG_MEDIUM_PRESENT, &drive->atapi_flags);
timeout += jiffies;
while (time_before(jiffies, timeout)) {
- idetape_create_test_unit_ready_cmd(&pc);
- if (!idetape_queue_pc_tail(drive, &pc))
+ if (ide_do_test_unit_ready(drive, disk) == 0)
return 0;
if ((tape->sense_key == 2 && tape->asc == 4 && tape->ascq == 2)
|| (tape->asc == 0x3A)) {
/* no media */
if (load_attempted)
return -ENOMEDIUM;
- idetape_create_load_unload_cmd(drive, &pc,
- IDETAPE_LU_LOAD_MASK);
- idetape_queue_pc_tail(drive, &pc);
+ ide_do_start_stop(drive, disk, IDETAPE_LU_LOAD_MASK);
load_attempted = 1;
/* not about to be ready */
} else if (!(tape->sense_key == 2 && tape->asc == 4 &&
@@ -1318,11 +1086,12 @@
static int idetape_flush_tape_buffers(ide_drive_t *drive)
{
+ struct ide_tape_obj *tape = drive->driver_data;
struct ide_atapi_pc pc;
int rc;
idetape_create_write_filemark_cmd(drive, &pc, 0);
- rc = idetape_queue_pc_tail(drive, &pc);
+ rc = ide_queue_pc_tail(drive, tape->disk, &pc);
if (rc)
return rc;
idetape_wait_ready(drive, 60 * 5 * HZ);
@@ -1331,7 +1100,7 @@
static void idetape_create_read_position_cmd(struct ide_atapi_pc *pc)
{
- idetape_init_pc(pc);
+ ide_init_pc(pc);
pc->c[0] = READ_POSITION;
pc->req_xfer = 20;
}
@@ -1345,7 +1114,7 @@
debug_log(DBG_PROCS, "Enter %s\n", __func__);
idetape_create_read_position_cmd(&pc);
- if (idetape_queue_pc_tail(drive, &pc))
+ if (ide_queue_pc_tail(drive, tape->disk, &pc))
return -1;
position = tape->first_frame;
return position;
@@ -1355,7 +1124,7 @@
struct ide_atapi_pc *pc,
unsigned int block, u8 partition, int skip)
{
- idetape_init_pc(pc);
+ ide_init_pc(pc);
pc->c[0] = POSITION_TO_ELEMENT;
pc->c[1] = 2;
put_unaligned(cpu_to_be32(block), (unsigned int *) &pc->c[3]);
@@ -1363,21 +1132,6 @@
pc->flags |= PC_FLAG_WAIT_FOR_DSC;
}
-static int idetape_create_prevent_cmd(ide_drive_t *drive,
- struct ide_atapi_pc *pc, int prevent)
-{
- idetape_tape_t *tape = drive->driver_data;
-
- /* device supports locking according to capabilities page */
- if (!(tape->caps[6] & 0x01))
- return 0;
-
- idetape_init_pc(pc);
- pc->c[0] = ALLOW_MEDIUM_REMOVAL;
- pc->c[4] = prevent;
- return 1;
-}
-
static void __ide_tape_discard_merge_buffer(ide_drive_t *drive)
{
idetape_tape_t *tape = drive->driver_data;
@@ -1405,6 +1159,7 @@
u8 partition, int skip)
{
idetape_tape_t *tape = drive->driver_data;
+ struct gendisk *disk = tape->disk;
int retval;
struct ide_atapi_pc pc;
@@ -1412,12 +1167,12 @@
__ide_tape_discard_merge_buffer(drive);
idetape_wait_ready(drive, 60 * 5 * HZ);
idetape_create_locate_cmd(drive, &pc, block, partition, skip);
- retval = idetape_queue_pc_tail(drive, &pc);
+ retval = ide_queue_pc_tail(drive, disk, &pc);
if (retval)
return (retval);
idetape_create_read_position_cmd(&pc);
- return (idetape_queue_pc_tail(drive, &pc));
+ return ide_queue_pc_tail(drive, disk, &pc);
}
static void ide_tape_discard_merge_buffer(ide_drive_t *drive,
@@ -1477,7 +1232,7 @@
static void idetape_create_inquiry_cmd(struct ide_atapi_pc *pc)
{
- idetape_init_pc(pc);
+ ide_init_pc(pc);
pc->c[0] = INQUIRY;
pc->c[4] = 254;
pc->req_xfer = 254;
@@ -1486,14 +1241,14 @@
static void idetape_create_rewind_cmd(ide_drive_t *drive,
struct ide_atapi_pc *pc)
{
- idetape_init_pc(pc);
+ ide_init_pc(pc);
pc->c[0] = REZERO_UNIT;
pc->flags |= PC_FLAG_WAIT_FOR_DSC;
}
static void idetape_create_erase_cmd(struct ide_atapi_pc *pc)
{
- idetape_init_pc(pc);
+ ide_init_pc(pc);
pc->c[0] = ERASE;
pc->c[1] = 1;
pc->flags |= PC_FLAG_WAIT_FOR_DSC;
@@ -1501,7 +1256,7 @@
static void idetape_create_space_cmd(struct ide_atapi_pc *pc, int count, u8 cmd)
{
- idetape_init_pc(pc);
+ ide_init_pc(pc);
pc->c[0] = SPACE;
put_unaligned(cpu_to_be32(count), (unsigned int *) &pc->c[1]);
pc->c[1] = cmd;
@@ -1600,7 +1355,7 @@
* No point in issuing this if DSC overlap isn't supported, some
* drives (Seagate STT3401A) will return an error.
*/
- if (drive->dsc_overlap) {
+ if (drive->dev_flags & IDE_DFLAG_DSC_OVERLAP) {
bytes_read = idetape_queue_rw_tail(drive,
REQ_IDETAPE_READ, 0,
tape->merge_bh);
@@ -1664,20 +1419,20 @@
*/
static int idetape_rewind_tape(ide_drive_t *drive)
{
+ struct ide_tape_obj *tape = drive->driver_data;
+ struct gendisk *disk = tape->disk;
int retval;
struct ide_atapi_pc pc;
- idetape_tape_t *tape;
- tape = drive->driver_data;
debug_log(DBG_SENSE, "Enter %s\n", __func__);
idetape_create_rewind_cmd(drive, &pc);
- retval = idetape_queue_pc_tail(drive, &pc);
+ retval = ide_queue_pc_tail(drive, disk, &pc);
if (retval)
return retval;
idetape_create_read_position_cmd(&pc);
- retval = idetape_queue_pc_tail(drive, &pc);
+ retval = ide_queue_pc_tail(drive, disk, &pc);
if (retval)
return retval;
return 0;
@@ -1720,6 +1475,7 @@
int mt_count)
{
idetape_tape_t *tape = drive->driver_data;
+ struct gendisk *disk = tape->disk;
struct ide_atapi_pc pc;
int retval, count = 0;
int sprev = !!(tape->caps[4] & 0x20);
@@ -1744,7 +1500,7 @@
case MTBSF:
idetape_create_space_cmd(&pc, mt_count - count,
IDETAPE_SPACE_OVER_FILEMARK);
- return idetape_queue_pc_tail(drive, &pc);
+ return ide_queue_pc_tail(drive, disk, &pc);
case MTFSFM:
case MTBSFM:
if (!sprev)
@@ -1780,7 +1536,7 @@
static ssize_t idetape_chrdev_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
- struct ide_tape_obj *tape = ide_tape_f(file);
+ struct ide_tape_obj *tape = file->private_data;
ide_drive_t *drive = tape->drive;
ssize_t bytes_read, temp, actually_read = 0, rc;
ssize_t ret = 0;
@@ -1842,7 +1598,7 @@
static ssize_t idetape_chrdev_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- struct ide_tape_obj *tape = ide_tape_f(file);
+ struct ide_tape_obj *tape = file->private_data;
ide_drive_t *drive = tape->drive;
ssize_t actually_written = 0;
ssize_t ret = 0;
@@ -1875,7 +1631,7 @@
* point in issuing this if DSC overlap isn't supported, some
* drives (Seagate STT3401A) will return an error.
*/
- if (drive->dsc_overlap) {
+ if (drive->dev_flags & IDE_DFLAG_DSC_OVERLAP) {
ssize_t retval = idetape_queue_rw_tail(drive,
REQ_IDETAPE_WRITE, 0,
tape->merge_bh);
@@ -1933,11 +1689,12 @@
static int idetape_write_filemark(ide_drive_t *drive)
{
+ struct ide_tape_obj *tape = drive->driver_data;
struct ide_atapi_pc pc;
/* Write a filemark */
idetape_create_write_filemark_cmd(drive, &pc, 1);
- if (idetape_queue_pc_tail(drive, &pc)) {
+ if (ide_queue_pc_tail(drive, tape->disk, &pc)) {
printk(KERN_ERR "ide-tape: Couldn't write a filemark\n");
return -EIO;
}
@@ -1960,6 +1717,7 @@
static int idetape_mtioctop(ide_drive_t *drive, short mt_op, int mt_count)
{
idetape_tape_t *tape = drive->driver_data;
+ struct gendisk *disk = tape->disk;
struct ide_atapi_pc pc;
int i, retval;
@@ -1996,9 +1754,7 @@
return 0;
case MTLOAD:
ide_tape_discard_merge_buffer(drive, 0);
- idetape_create_load_unload_cmd(drive, &pc,
- IDETAPE_LU_LOAD_MASK);
- return idetape_queue_pc_tail(drive, &pc);
+ return ide_do_start_stop(drive, disk, IDETAPE_LU_LOAD_MASK);
case MTUNLOAD:
case MTOFFL:
/*
@@ -2006,14 +1762,11 @@
* attempting to eject.
*/
if (tape->door_locked) {
- if (idetape_create_prevent_cmd(drive, &pc, 0))
- if (!idetape_queue_pc_tail(drive, &pc))
- tape->door_locked = DOOR_UNLOCKED;
+ if (!ide_set_media_lock(drive, disk, 0))
+ tape->door_locked = DOOR_UNLOCKED;
}
ide_tape_discard_merge_buffer(drive, 0);
- idetape_create_load_unload_cmd(drive, &pc,
- !IDETAPE_LU_LOAD_MASK);
- retval = idetape_queue_pc_tail(drive, &pc);
+ retval = ide_do_start_stop(drive, disk, !IDETAPE_LU_LOAD_MASK);
if (!retval)
clear_bit(IDE_AFLAG_MEDIUM_PRESENT, &drive->atapi_flags);
return retval;
@@ -2022,16 +1775,15 @@
return idetape_flush_tape_buffers(drive);
case MTRETEN:
ide_tape_discard_merge_buffer(drive, 0);
- idetape_create_load_unload_cmd(drive, &pc,
+ return ide_do_start_stop(drive, disk,
IDETAPE_LU_RETENSION_MASK | IDETAPE_LU_LOAD_MASK);
- return idetape_queue_pc_tail(drive, &pc);
case MTEOM:
idetape_create_space_cmd(&pc, 0, IDETAPE_SPACE_TO_EOD);
- return idetape_queue_pc_tail(drive, &pc);
+ return ide_queue_pc_tail(drive, disk, &pc);
case MTERASE:
(void)idetape_rewind_tape(drive);
idetape_create_erase_cmd(&pc);
- return idetape_queue_pc_tail(drive, &pc);
+ return ide_queue_pc_tail(drive, disk, &pc);
case MTSETBLK:
if (mt_count) {
if (mt_count < tape->blk_size ||
@@ -2052,17 +1804,13 @@
case MTFSR:
case MTBSR:
case MTLOCK:
- if (!idetape_create_prevent_cmd(drive, &pc, 1))
- return 0;
- retval = idetape_queue_pc_tail(drive, &pc);
+ retval = ide_set_media_lock(drive, disk, 1);
if (retval)
return retval;
tape->door_locked = DOOR_EXPLICITLY_LOCKED;
return 0;
case MTUNLOCK:
- if (!idetape_create_prevent_cmd(drive, &pc, 0))
- return 0;
- retval = idetape_queue_pc_tail(drive, &pc);
+ retval = ide_set_media_lock(drive, disk, 0);
if (retval)
return retval;
tape->door_locked = DOOR_UNLOCKED;
@@ -2082,7 +1830,7 @@
static int idetape_chrdev_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- struct ide_tape_obj *tape = ide_tape_f(file);
+ struct ide_tape_obj *tape = file->private_data;
ide_drive_t *drive = tape->drive;
struct mtop mtop;
struct mtget mtget;
@@ -2144,7 +1892,7 @@
struct ide_atapi_pc pc;
idetape_create_mode_sense_cmd(&pc, IDETAPE_BLOCK_DESCRIPTOR);
- if (idetape_queue_pc_tail(drive, &pc)) {
+ if (ide_queue_pc_tail(drive, tape->disk, &pc)) {
printk(KERN_ERR "ide-tape: Can't get block descriptor\n");
if (tape->blk_size == 0) {
printk(KERN_WARNING "ide-tape: Cannot deal with zero "
@@ -2164,7 +1912,6 @@
unsigned int minor = iminor(inode), i = minor & ~0xc0;
ide_drive_t *drive;
idetape_tape_t *tape;
- struct ide_atapi_pc pc;
int retval;
if (i >= MAX_HWIFS * MAX_DRIVES)
@@ -2227,11 +1974,9 @@
/* Lock the tape drive door so user can't eject. */
if (tape->chrdev_dir == IDETAPE_DIR_NONE) {
- if (idetape_create_prevent_cmd(drive, &pc, 1)) {
- if (!idetape_queue_pc_tail(drive, &pc)) {
- if (tape->door_locked != DOOR_EXPLICITLY_LOCKED)
- tape->door_locked = DOOR_LOCKED;
- }
+ if (!ide_set_media_lock(drive, tape->disk, 1)) {
+ if (tape->door_locked != DOOR_EXPLICITLY_LOCKED)
+ tape->door_locked = DOOR_LOCKED;
}
}
unlock_kernel();
@@ -2262,9 +2007,8 @@
static int idetape_chrdev_release(struct inode *inode, struct file *filp)
{
- struct ide_tape_obj *tape = ide_tape_f(filp);
+ struct ide_tape_obj *tape = filp->private_data;
ide_drive_t *drive = tape->drive;
- struct ide_atapi_pc pc;
unsigned int minor = iminor(inode);
lock_kernel();
@@ -2283,10 +2027,8 @@
(void) idetape_rewind_tape(drive);
if (tape->chrdev_dir == IDETAPE_DIR_NONE) {
if (tape->door_locked == DOOR_LOCKED) {
- if (idetape_create_prevent_cmd(drive, &pc, 0)) {
- if (!idetape_queue_pc_tail(drive, &pc))
- tape->door_locked = DOOR_UNLOCKED;
- }
+ if (!ide_set_media_lock(drive, tape->disk, 0))
+ tape->door_locked = DOOR_UNLOCKED;
}
}
clear_bit(IDE_AFLAG_BUSY, &drive->atapi_flags);
@@ -2295,45 +2037,6 @@
return 0;
}
-/*
- * check the contents of the ATAPI IDENTIFY command results. We return:
- *
- * 1 - If the tape can be supported by us, based on the information we have so
- * far.
- *
- * 0 - If this tape driver is not currently supported by us.
- */
-static int idetape_identify_device(ide_drive_t *drive)
-{
- u8 gcw[2], protocol, device_type, removable, packet_size;
-
- if (drive->id_read == 0)
- return 1;
-
- *((unsigned short *) &gcw) = drive->id->config;
-
- protocol = (gcw[1] & 0xC0) >> 6;
- device_type = gcw[1] & 0x1F;
- removable = !!(gcw[0] & 0x80);
- packet_size = gcw[0] & 0x3;
-
- /* Check that we can support this device */
- if (protocol != 2)
- printk(KERN_ERR "ide-tape: Protocol (0x%02x) is not ATAPI\n",
- protocol);
- else if (device_type != 1)
- printk(KERN_ERR "ide-tape: Device type (0x%02x) is not set "
- "to tape\n", device_type);
- else if (!removable)
- printk(KERN_ERR "ide-tape: The removable flag is not set\n");
- else if (packet_size != 0) {
- printk(KERN_ERR "ide-tape: Packet size (0x%02x) is not 12"
- " bytes\n", packet_size);
- } else
- return 1;
- return 0;
-}
-
static void idetape_get_inquiry_results(ide_drive_t *drive)
{
idetape_tape_t *tape = drive->driver_data;
@@ -2341,7 +2044,7 @@
char fw_rev[4], vendor_id[8], product_id[16];
idetape_create_inquiry_cmd(&pc);
- if (idetape_queue_pc_tail(drive, &pc)) {
+ if (ide_queue_pc_tail(drive, tape->disk, &pc)) {
printk(KERN_ERR "ide-tape: %s: can't get INQUIRY results\n",
tape->name);
return;
@@ -2370,7 +2073,7 @@
u8 speed, max_speed;
idetape_create_mode_sense_cmd(&pc, IDETAPE_CAPABILITIES_PAGE);
- if (idetape_queue_pc_tail(drive, &pc)) {
+ if (ide_queue_pc_tail(drive, tape->disk, &pc)) {
printk(KERN_ERR "ide-tape: Can't get tape parameters - assuming"
" some default values\n");
tape->blk_size = 512;
@@ -2402,6 +2105,11 @@
}
memcpy(&tape->caps, caps, 20);
+
+ /* device lacks locking support according to capabilities page */
+ if ((caps[6] & 1) == 0)
+ drive->atapi_flags |= IDE_AFLAG_NO_DOORLOCK;
+
if (caps[7] & 0x02)
tape->blk_size = 512;
else if (caps[7] & 0x04)
@@ -2409,28 +2117,56 @@
}
#ifdef CONFIG_IDE_PROC_FS
-static void idetape_add_settings(ide_drive_t *drive)
-{
- idetape_tape_t *tape = drive->driver_data;
-
- ide_add_setting(drive, "buffer", SETTING_READ, TYPE_SHORT, 0, 0xffff,
- 1, 2, (u16 *)&tape->caps[16], NULL);
- ide_add_setting(drive, "speed", SETTING_READ, TYPE_SHORT, 0, 0xffff,
- 1, 1, (u16 *)&tape->caps[14], NULL);
- ide_add_setting(drive, "buffer_size", SETTING_READ, TYPE_INT, 0, 0xffff,
- 1, 1024, &tape->buffer_size, NULL);
- ide_add_setting(drive, "tdsc", SETTING_RW, TYPE_INT, IDETAPE_DSC_RW_MIN,
- IDETAPE_DSC_RW_MAX, 1000, HZ, &tape->best_dsc_rw_freq,
- NULL);
- ide_add_setting(drive, "dsc_overlap", SETTING_RW, TYPE_BYTE, 0, 1, 1,
- 1, &drive->dsc_overlap, NULL);
- ide_add_setting(drive, "avg_speed", SETTING_READ, TYPE_INT, 0, 0xffff,
- 1, 1, &tape->avg_speed, NULL);
- ide_add_setting(drive, "debug_mask", SETTING_RW, TYPE_INT, 0, 0xffff, 1,
- 1, &tape->debug_mask, NULL);
+#define ide_tape_devset_get(name, field) \
+static int get_##name(ide_drive_t *drive) \
+{ \
+ idetape_tape_t *tape = drive->driver_data; \
+ return tape->field; \
}
-#else
-static inline void idetape_add_settings(ide_drive_t *drive) { ; }
+
+#define ide_tape_devset_set(name, field) \
+static int set_##name(ide_drive_t *drive, int arg) \
+{ \
+ idetape_tape_t *tape = drive->driver_data; \
+ tape->field = arg; \
+ return 0; \
+}
+
+#define ide_tape_devset_rw_field(_name, _field) \
+ide_tape_devset_get(_name, _field) \
+ide_tape_devset_set(_name, _field) \
+IDE_DEVSET(_name, DS_SYNC, get_##_name, set_##_name)
+
+#define ide_tape_devset_r_field(_name, _field) \
+ide_tape_devset_get(_name, _field) \
+IDE_DEVSET(_name, 0, get_##_name, NULL)
+
+static int mulf_tdsc(ide_drive_t *drive) { return 1000; }
+static int divf_tdsc(ide_drive_t *drive) { return HZ; }
+static int divf_buffer(ide_drive_t *drive) { return 2; }
+static int divf_buffer_size(ide_drive_t *drive) { return 1024; }
+
+ide_devset_rw_flag(dsc_overlap, IDE_DFLAG_DSC_OVERLAP);
+
+ide_tape_devset_rw_field(debug_mask, debug_mask);
+ide_tape_devset_rw_field(tdsc, best_dsc_rw_freq);
+
+ide_tape_devset_r_field(avg_speed, avg_speed);
+ide_tape_devset_r_field(speed, caps[14]);
+ide_tape_devset_r_field(buffer, caps[16]);
+ide_tape_devset_r_field(buffer_size, buffer_size);
+
+static const struct ide_proc_devset idetape_settings[] = {
+ __IDE_PROC_DEVSET(avg_speed, 0, 0xffff, NULL, NULL),
+ __IDE_PROC_DEVSET(buffer, 0, 0xffff, NULL, divf_buffer),
+ __IDE_PROC_DEVSET(buffer_size, 0, 0xffff, NULL, divf_buffer_size),
+ __IDE_PROC_DEVSET(debug_mask, 0, 0xffff, NULL, NULL),
+ __IDE_PROC_DEVSET(dsc_overlap, 0, 1, NULL, NULL),
+ __IDE_PROC_DEVSET(speed, 0, 0xffff, NULL, NULL),
+ __IDE_PROC_DEVSET(tdsc, IDETAPE_DSC_RW_MIN, IDETAPE_DSC_RW_MAX,
+ mulf_tdsc, divf_tdsc),
+ { 0 },
+};
#endif
/*
@@ -2449,32 +2185,31 @@
unsigned long t;
int speed;
int buffer_size;
- u8 gcw[2];
u16 *ctl = (u16 *)&tape->caps[12];
- drive->pc_callback = ide_tape_callback;
+ drive->pc_callback = ide_tape_callback;
+ drive->pc_update_buffers = idetape_update_buffers;
+ drive->pc_io_buffers = ide_tape_io_buffers;
spin_lock_init(&tape->lock);
- drive->dsc_overlap = 1;
+
+ drive->dev_flags |= IDE_DFLAG_DSC_OVERLAP;
+
if (drive->hwif->host_flags & IDE_HFLAG_NO_DSC) {
printk(KERN_INFO "ide-tape: %s: disabling DSC overlap\n",
tape->name);
- drive->dsc_overlap = 0;
+ drive->dev_flags &= ~IDE_DFLAG_DSC_OVERLAP;
}
+
/* Seagate Travan drives do not support DSC overlap. */
- if (strstr(drive->id->model, "Seagate STT3401"))
- drive->dsc_overlap = 0;
+ if (strstr((char *)&drive->id[ATA_ID_PROD], "Seagate STT3401"))
+ drive->dev_flags &= ~IDE_DFLAG_DSC_OVERLAP;
+
tape->minor = minor;
tape->name[0] = 'h';
tape->name[1] = 't';
tape->name[2] = '0' + minor;
tape->chrdev_dir = IDETAPE_DIR_NONE;
- tape->pc = tape->pc_stack;
- *((unsigned short *) &gcw) = drive->id->config;
-
- /* Command packet DRQ type */
- if (((gcw[0] & 0x60) >> 5) == 1)
- set_bit(IDE_AFLAG_DRQ_INTERRUPT, &drive->atapi_flags);
idetape_get_inquiry_results(drive);
idetape_get_mode_sense_results(drive);
@@ -2510,9 +2245,9 @@
(*(u16 *)&tape->caps[16] * 512) / tape->buffer_size,
tape->buffer_size / 1024,
tape->best_dsc_rw_freq * 1000 / HZ,
- drive->using_dma ? ", DMA":"");
+ (drive->dev_flags & IDE_DFLAG_USING_DMA) ? ", DMA" : "");
- idetape_add_settings(drive);
+ ide_proc_register_driver(drive, tape->driver);
}
static void ide_tape_remove(ide_drive_t *drive)
@@ -2528,13 +2263,13 @@
static void ide_tape_release(struct kref *kref)
{
- struct ide_tape_obj *tape = to_ide_tape(kref);
+ struct ide_tape_obj *tape = to_ide_drv(kref, ide_tape_obj);
ide_drive_t *drive = tape->drive;
struct gendisk *g = tape->disk;
BUG_ON(tape->merge_bh_size);
- drive->dsc_overlap = 0;
+ drive->dev_flags &= ~IDE_DFLAG_DSC_OVERLAP;
drive->driver_data = NULL;
device_destroy(idetape_sysfs_class, MKDEV(IDETAPE_MAJOR, tape->minor));
device_destroy(idetape_sysfs_class,
@@ -2576,13 +2311,12 @@
.probe = ide_tape_probe,
.remove = ide_tape_remove,
.version = IDETAPE_VERSION,
- .media = ide_tape,
- .supports_dsc_overlap = 1,
.do_request = idetape_do_request,
.end_request = idetape_end_request,
.error = __ide_error,
#ifdef CONFIG_IDE_PROC_FS
.proc = idetape_proc,
+ .settings = idetape_settings,
#endif
};
@@ -2611,7 +2345,7 @@
static int idetape_release(struct inode *inode, struct file *filp)
{
struct gendisk *disk = inode->i_bdev->bd_disk;
- struct ide_tape_obj *tape = ide_tape_g(disk);
+ struct ide_tape_obj *tape = ide_drv_g(disk, ide_tape_obj);
ide_tape_put(tape);
@@ -2622,7 +2356,7 @@
unsigned int cmd, unsigned long arg)
{
struct block_device *bdev = inode->i_bdev;
- struct ide_tape_obj *tape = ide_tape_g(bdev->bd_disk);
+ struct ide_tape_obj *tape = ide_drv_g(bdev->bd_disk, ide_tape_obj);
ide_drive_t *drive = tape->drive;
int err = generic_ide_ioctl(drive, file, bdev, cmd, arg);
if (err == -EINVAL)
@@ -2645,11 +2379,12 @@
if (!strstr("ide-tape", drive->driver_req))
goto failed;
- if (!drive->present)
- goto failed;
+
if (drive->media != ide_tape)
goto failed;
- if (!idetape_identify_device(drive)) {
+
+ if ((drive->dev_flags & IDE_DFLAG_ID_READ) &&
+ ide_check_atapi_device(drive, DRV_NAME) == 0) {
printk(KERN_ERR "ide-tape: %s: not supported by this version of"
" the driver\n", drive->name);
goto failed;
@@ -2667,8 +2402,6 @@
ide_init_disk(g, drive);
- ide_proc_register_driver(drive, &idetape_driver);
-
kref_init(&tape->kref);
tape->drive = drive;
diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c
index 7fb6f1c..bf4fb9d 100644
--- a/drivers/ide/ide-taskfile.c
+++ b/drivers/ide/ide-taskfile.c
@@ -44,18 +44,15 @@
memset(&args, 0, sizeof(ide_task_t));
args.tf.nsect = 0x01;
if (drive->media == ide_disk)
- args.tf.command = WIN_IDENTIFY;
+ args.tf.command = ATA_CMD_ID_ATA;
else
- args.tf.command = WIN_PIDENTIFY;
+ args.tf.command = ATA_CMD_ID_ATAPI;
args.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
args.data_phase = TASKFILE_IN;
return ide_raw_taskfile(drive, &args, buf, 1);
}
static ide_startstop_t task_no_data_intr(ide_drive_t *);
-static ide_startstop_t set_geometry_intr(ide_drive_t *);
-static ide_startstop_t recal_intr(ide_drive_t *);
-static ide_startstop_t set_multmode_intr(ide_drive_t *);
static ide_startstop_t pre_task_out_intr(ide_drive_t *, struct request *);
static ide_startstop_t task_in_intr(ide_drive_t *);
@@ -79,6 +76,8 @@
if (task->tf_flags & IDE_TFLAG_FLAGGED)
task->tf_flags |= IDE_TFLAG_FLAGGED_SET_IN_FLAGS;
+ memcpy(&hwif->task, task, sizeof(*task));
+
if ((task->tf_flags & IDE_TFLAG_DMA_PIO_FALLBACK) == 0) {
ide_tf_dump(drive->name, tf);
tp_ops->set_irq(hwif, 1);
@@ -99,19 +98,12 @@
case TASKFILE_NO_DATA:
if (handler == NULL)
handler = task_no_data_intr;
- /* WIN_{SPECIFY,RESTORE,SETMULT} use custom handlers */
- if (task->tf_flags & IDE_TFLAG_CUSTOM_HANDLER) {
- switch (tf->command) {
- case WIN_SPECIFY: handler = set_geometry_intr; break;
- case WIN_RESTORE: handler = recal_intr; break;
- case WIN_SETMULT: handler = set_multmode_intr; break;
- }
- }
ide_execute_command(drive, tf->command, handler,
WAIT_WORSTCASE, NULL);
return ide_started;
default:
- if (drive->using_dma == 0 || dma_ops->dma_setup(drive))
+ if ((drive->dev_flags & IDE_DFLAG_USING_DMA) == 0 ||
+ dma_ops->dma_setup(drive))
return ide_stopped;
dma_ops->dma_exec_cmd(drive, tf->command);
dma_ops->dma_start(drive);
@@ -121,88 +113,56 @@
EXPORT_SYMBOL_GPL(do_rw_taskfile);
/*
- * set_multmode_intr() is invoked on completion of a WIN_SETMULT cmd.
+ * Handler for commands without a data phase
*/
-static ide_startstop_t set_multmode_intr(ide_drive_t *drive)
+static ide_startstop_t task_no_data_intr(ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
- u8 stat;
-
- local_irq_enable_in_hardirq();
- stat = hwif->tp_ops->read_status(hwif);
-
- if (OK_STAT(stat, READY_STAT, BAD_STAT))
- drive->mult_count = drive->mult_req;
- else {
- drive->mult_req = drive->mult_count = 0;
- drive->special.b.recalibrate = 1;
- (void) ide_dump_status(drive, "set_multmode", stat);
- }
- return ide_stopped;
-}
-
-/*
- * set_geometry_intr() is invoked on completion of a WIN_SPECIFY cmd.
- */
-static ide_startstop_t set_geometry_intr(ide_drive_t *drive)
-{
- ide_hwif_t *hwif = drive->hwif;
- int retries = 5;
+ ide_task_t *task = &hwif->task;
+ struct ide_taskfile *tf = &task->tf;
+ int custom = (task->tf_flags & IDE_TFLAG_CUSTOM_HANDLER) ? 1 : 0;
+ int retries = (custom && tf->command == ATA_CMD_INIT_DEV_PARAMS) ? 5 : 1;
u8 stat;
local_irq_enable_in_hardirq();
while (1) {
stat = hwif->tp_ops->read_status(hwif);
- if ((stat & BUSY_STAT) == 0 || retries-- == 0)
+ if ((stat & ATA_BUSY) == 0 || retries-- == 0)
break;
udelay(10);
};
- if (OK_STAT(stat, READY_STAT, BAD_STAT))
- return ide_stopped;
-
- if (stat & (ERR_STAT|DRQ_STAT))
- return ide_error(drive, "set_geometry_intr", stat);
-
- ide_set_handler(drive, &set_geometry_intr, WAIT_WORSTCASE, NULL);
- return ide_started;
-}
-
-/*
- * recal_intr() is invoked on completion of a WIN_RESTORE (recalibrate) cmd.
- */
-static ide_startstop_t recal_intr(ide_drive_t *drive)
-{
- ide_hwif_t *hwif = drive->hwif;
- u8 stat;
-
- local_irq_enable_in_hardirq();
- stat = hwif->tp_ops->read_status(hwif);
-
- if (!OK_STAT(stat, READY_STAT, BAD_STAT))
- return ide_error(drive, "recal_intr", stat);
- return ide_stopped;
-}
-
-/*
- * Handler for commands without a data phase
- */
-static ide_startstop_t task_no_data_intr(ide_drive_t *drive)
-{
- ide_hwif_t *hwif = drive->hwif;
- ide_task_t *args = hwif->hwgroup->rq->special;
- u8 stat;
-
- local_irq_enable_in_hardirq();
- stat = hwif->tp_ops->read_status(hwif);
-
- if (!OK_STAT(stat, READY_STAT, BAD_STAT))
+ if (!OK_STAT(stat, ATA_DRDY, BAD_STAT)) {
+ if (custom && tf->command == ATA_CMD_SET_MULTI) {
+ drive->mult_req = drive->mult_count = 0;
+ drive->special.b.recalibrate = 1;
+ (void)ide_dump_status(drive, __func__, stat);
+ return ide_stopped;
+ } else if (custom && tf->command == ATA_CMD_INIT_DEV_PARAMS) {
+ if ((stat & (ATA_ERR | ATA_DRQ)) == 0) {
+ ide_set_handler(drive, &task_no_data_intr,
+ WAIT_WORSTCASE, NULL);
+ return ide_started;
+ }
+ }
return ide_error(drive, "task_no_data_intr", stat);
/* calls ide_end_drive_cmd */
+ }
- if (args)
+ if (!custom)
ide_end_drive_cmd(drive, stat, ide_read_error(drive));
+ else if (tf->command == ATA_CMD_IDLEIMMEDIATE) {
+ hwif->tp_ops->tf_read(drive, task);
+ if (tf->lbal != 0xc4) {
+ printk(KERN_ERR "%s: head unload failed!\n",
+ drive->name);
+ ide_tf_dump(drive->name, tf);
+ } else
+ drive->dev_flags |= IDE_DFLAG_PARKED;
+ ide_end_drive_cmd(drive, stat, ide_read_error(drive));
+ } else if (tf->command == ATA_CMD_SET_MULTI)
+ drive->mult_count = drive->mult_req;
return ide_stopped;
}
@@ -220,13 +180,13 @@
for (retries = 0; retries < 1000; retries++) {
stat = hwif->tp_ops->read_status(hwif);
- if (stat & BUSY_STAT)
+ if (stat & ATA_BUSY)
udelay(10);
else
break;
}
- if (stat & BUSY_STAT)
+ if (stat & ATA_BUSY)
printk(KERN_ERR "%s: drive still BUSY!\n", drive->name);
return stat;
@@ -385,7 +345,7 @@
static ide_startstop_t task_in_unexpected(ide_drive_t *drive, struct request *rq, u8 stat)
{
/* Command all done? */
- if (OK_STAT(stat, READY_STAT, BUSY_STAT)) {
+ if (OK_STAT(stat, ATA_DRDY, ATA_BUSY)) {
task_end_request(drive, rq, stat);
return ide_stopped;
}
@@ -405,11 +365,11 @@
u8 stat = hwif->tp_ops->read_status(hwif);
/* Error? */
- if (stat & ERR_STAT)
+ if (stat & ATA_ERR)
return task_error(drive, rq, __func__, stat);
/* Didn't want any data? Odd. */
- if (!(stat & DRQ_STAT))
+ if ((stat & ATA_DRQ) == 0)
return task_in_unexpected(drive, rq, stat);
ide_pio_datablock(drive, rq, 0);
@@ -442,7 +402,7 @@
return task_error(drive, rq, __func__, stat);
/* Deal with unexpected ATA data phase. */
- if (((stat & DRQ_STAT) == 0) ^ !hwif->nleft)
+ if (((stat & ATA_DRQ) == 0) ^ !hwif->nleft)
return task_error(drive, rq, __func__, stat);
if (!hwif->nleft) {
@@ -461,16 +421,15 @@
{
ide_startstop_t startstop;
- if (ide_wait_stat(&startstop, drive, DRQ_STAT,
+ if (ide_wait_stat(&startstop, drive, ATA_DRQ,
drive->bad_wstat, WAIT_DRQ)) {
printk(KERN_ERR "%s: no DRQ after issuing %sWRITE%s\n",
- drive->name,
- drive->hwif->data_phase ? "MULT" : "",
- drive->addressing ? "_EXT" : "");
+ drive->name, drive->hwif->data_phase ? "MULT" : "",
+ (drive->dev_flags & IDE_DFLAG_LBA48) ? "_EXT" : "");
return startstop;
}
- if (!drive->unmask)
+ if ((drive->dev_flags & IDE_DFLAG_UNMASK) == 0)
local_irq_disable();
ide_set_handler(drive, &task_out_intr, WAIT_WORSTCASE, NULL);
@@ -586,7 +545,7 @@
args.tf_flags = IDE_TFLAG_IO_16BIT | IDE_TFLAG_DEVICE |
IDE_TFLAG_IN_TF;
- if (drive->addressing == 1)
+ if (drive->dev_flags & IDE_DFLAG_LBA48)
args.tf_flags |= (IDE_TFLAG_LBA48 | IDE_TFLAG_IN_HOB);
if (req_task->out_flags.all) {
@@ -689,7 +648,7 @@
if ((args.tf_flags & IDE_TFLAG_FLAGGED_SET_IN_FLAGS) &&
req_task->in_flags.all == 0) {
req_task->in_flags.all = IDE_TASKFILE_STD_IN_FLAGS;
- if (drive->addressing == 1)
+ if (drive->dev_flags & IDE_DFLAG_LBA48)
req_task->in_flags.all |= (IDE_HOB_STD_IN_FLAGS << 8);
}
@@ -721,110 +680,3 @@
return err;
}
#endif
-
-int ide_cmd_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg)
-{
- u8 *buf = NULL;
- int bufsize = 0, err = 0;
- u8 args[4], xfer_rate = 0;
- ide_task_t tfargs;
- struct ide_taskfile *tf = &tfargs.tf;
- struct hd_driveid *id = drive->id;
-
- if (NULL == (void *) arg) {
- struct request *rq;
-
- rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
- rq->cmd_type = REQ_TYPE_ATA_TASKFILE;
- err = blk_execute_rq(drive->queue, NULL, rq, 0);
- blk_put_request(rq);
-
- return err;
- }
-
- if (copy_from_user(args, (void __user *)arg, 4))
- return -EFAULT;
-
- memset(&tfargs, 0, sizeof(ide_task_t));
- tf->feature = args[2];
- if (args[0] == WIN_SMART) {
- tf->nsect = args[3];
- tf->lbal = args[1];
- tf->lbam = 0x4f;
- tf->lbah = 0xc2;
- tfargs.tf_flags = IDE_TFLAG_OUT_TF | IDE_TFLAG_IN_NSECT;
- } else {
- tf->nsect = args[1];
- tfargs.tf_flags = IDE_TFLAG_OUT_FEATURE |
- IDE_TFLAG_OUT_NSECT | IDE_TFLAG_IN_NSECT;
- }
- tf->command = args[0];
- tfargs.data_phase = args[3] ? TASKFILE_IN : TASKFILE_NO_DATA;
-
- if (args[3]) {
- tfargs.tf_flags |= IDE_TFLAG_IO_16BIT;
- bufsize = SECTOR_WORDS * 4 * args[3];
- buf = kzalloc(bufsize, GFP_KERNEL);
- if (buf == NULL)
- return -ENOMEM;
- }
-
- if (tf->command == WIN_SETFEATURES &&
- tf->feature == SETFEATURES_XFER &&
- tf->nsect >= XFER_SW_DMA_0 &&
- (id->dma_ultra || id->dma_mword || id->dma_1word)) {
- xfer_rate = args[1];
- if (tf->nsect > XFER_UDMA_2 && !eighty_ninty_three(drive)) {
- printk(KERN_WARNING "%s: UDMA speeds >UDMA33 cannot "
- "be set\n", drive->name);
- goto abort;
- }
- }
-
- err = ide_raw_taskfile(drive, &tfargs, buf, args[3]);
-
- args[0] = tf->status;
- args[1] = tf->error;
- args[2] = tf->nsect;
-
- if (!err && xfer_rate) {
- /* active-retuning-calls future */
- ide_set_xfer_rate(drive, xfer_rate);
- ide_driveid_update(drive);
- }
-abort:
- if (copy_to_user((void __user *)arg, &args, 4))
- err = -EFAULT;
- if (buf) {
- if (copy_to_user((void __user *)(arg + 4), buf, bufsize))
- err = -EFAULT;
- kfree(buf);
- }
- return err;
-}
-
-int ide_task_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg)
-{
- void __user *p = (void __user *)arg;
- int err = 0;
- u8 args[7];
- ide_task_t task;
-
- if (copy_from_user(args, p, 7))
- return -EFAULT;
-
- memset(&task, 0, sizeof(task));
- memcpy(&task.tf_array[7], &args[1], 6);
- task.tf.command = args[0];
- task.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
-
- err = ide_no_data_taskfile(drive, &task);
-
- args[0] = task.tf.command;
- memcpy(&args[1], &task.tf_array[7], 6);
-
- if (copy_to_user(p, args, 7))
- err = -EFAULT;
-
- return err;
-}
diff --git a/drivers/ide/ide-timings.c b/drivers/ide/ide-timings.c
index 8c2f832..81f527a 100644
--- a/drivers/ide/ide-timings.c
+++ b/drivers/ide/ide-timings.c
@@ -22,7 +22,6 @@
*/
#include <linux/kernel.h>
-#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/module.h>
@@ -78,15 +77,15 @@
u16 ide_pio_cycle_time(ide_drive_t *drive, u8 pio)
{
- struct hd_driveid *id = drive->id;
+ u16 *id = drive->id;
struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio);
u16 cycle = 0;
- if (id->field_valid & 2) {
- if (id->capability & 8)
- cycle = id->eide_pio_iordy;
+ if (id[ATA_ID_FIELD_VALID] & 2) {
+ if (ata_id_has_iordy(drive->id))
+ cycle = id[ATA_ID_EIDE_PIO_IORDY];
else
- cycle = id->eide_pio;
+ cycle = id[ATA_ID_EIDE_PIO];
/* conservative "downgrade" for all pre-ATA2 drives */
if (pio < 3 && cycle < t->cycle)
@@ -138,7 +137,7 @@
int ide_timing_compute(ide_drive_t *drive, u8 speed,
struct ide_timing *t, int T, int UT)
{
- struct hd_driveid *id = drive->id;
+ u16 *id = drive->id;
struct ide_timing *s, p;
/*
@@ -157,16 +156,15 @@
* If the drive is an EIDE drive, it can tell us it needs extended
* PIO/MWDMA cycle timing.
*/
- if (id && id->field_valid & 2) { /* EIDE drive */
-
+ if (id[ATA_ID_FIELD_VALID] & 2) { /* EIDE drive */
memset(&p, 0, sizeof(p));
if (speed <= XFER_PIO_2)
- p.cycle = p.cyc8b = id->eide_pio;
+ p.cycle = p.cyc8b = id[ATA_ID_EIDE_PIO];
else if (speed <= XFER_PIO_5)
- p.cycle = p.cyc8b = id->eide_pio_iordy;
+ p.cycle = p.cyc8b = id[ATA_ID_EIDE_PIO_IORDY];
else if (speed >= XFER_MW_DMA_0 && speed <= XFER_MW_DMA_2)
- p.cycle = id->eide_dma_min;
+ p.cycle = id[ATA_ID_EIDE_DMA_MIN];
ide_timing_merge(&p, t, t, IDE_TIMING_CYCLE | IDE_TIMING_CYC8B);
}
diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c
index 7724516..04f8f13 100644
--- a/drivers/ide/ide.c
+++ b/drivers/ide/ide.c
@@ -44,8 +44,6 @@
* inspiration from lots of linux users, esp. hamish@zot.apana.org.au
*/
-#define _IDE_C /* Tell ide.h it's really us */
-
#include <linux/module.h>
#include <linux/types.h>
#include <linux/string.h>
@@ -58,6 +56,7 @@
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/ide.h>
+#include <linux/hdreg.h>
#include <linux/completion.h>
#include <linux/device.h>
@@ -97,8 +96,6 @@
hwif->name[2] = 'e';
hwif->name[3] = '0' + index;
- hwif->bus_state = BUSSTATE_ON;
-
init_completion(&hwif->gendev_rel_comp);
hwif->tp_ops = &default_tp_ops;
@@ -117,9 +114,9 @@
memset(drive, 0, sizeof(*drive));
drive->media = ide_disk;
- drive->select.all = (unit<<4)|0xa0;
+ drive->select = (unit << 4) | ATA_DEVICE_OBS;
drive->hwif = hwif;
- drive->ready_stat = READY_STAT;
+ drive->ready_stat = ATA_DRDY;
drive->bad_wstat = BAD_W_STAT;
drive->special.b.recalibrate = 1;
drive->special.b.set_geometry = 1;
@@ -141,7 +138,7 @@
for (i = 0; i < MAX_DRIVES; i++) {
ide_drive_t *drive = &hwif->drives[i];
- if (drive->present) {
+ if (drive->dev_flags & IDE_DFLAG_PRESENT) {
spin_unlock_irq(&ide_lock);
device_unregister(&drive->gendev);
wait_for_completion(&drive->gendev_rel_comp);
@@ -230,8 +227,7 @@
kfree(hwif->sg_table);
unregister_blkdev(hwif->major, hwif->name);
- if (hwif->dma_base)
- ide_release_dma_engine(hwif);
+ ide_release_dma_engine(hwif);
mutex_unlock(&ide_cfg_mtx);
}
@@ -253,96 +249,52 @@
DEFINE_MUTEX(ide_setting_mtx);
-EXPORT_SYMBOL_GPL(ide_setting_mtx);
+ide_devset_get(io_32bit, io_32bit);
-/**
- * ide_spin_wait_hwgroup - wait for group
- * @drive: drive in the group
- *
- * Wait for an IDE device group to go non busy and then return
- * holding the ide_lock which guards the hwgroup->busy status
- * and right to use it.
- */
-
-int ide_spin_wait_hwgroup (ide_drive_t *drive)
+static int set_io_32bit(ide_drive_t *drive, int arg)
{
- ide_hwgroup_t *hwgroup = HWGROUP(drive);
- unsigned long timeout = jiffies + (3 * HZ);
-
- spin_lock_irq(&ide_lock);
-
- while (hwgroup->busy) {
- unsigned long lflags;
- spin_unlock_irq(&ide_lock);
- local_irq_set(lflags);
- if (time_after(jiffies, timeout)) {
- local_irq_restore(lflags);
- printk(KERN_ERR "%s: channel busy\n", drive->name);
- return -EBUSY;
- }
- local_irq_restore(lflags);
- spin_lock_irq(&ide_lock);
- }
- return 0;
-}
-
-EXPORT_SYMBOL(ide_spin_wait_hwgroup);
-
-int set_io_32bit(ide_drive_t *drive, int arg)
-{
- if (drive->no_io_32bit)
+ if (drive->dev_flags & IDE_DFLAG_NO_IO_32BIT)
return -EPERM;
if (arg < 0 || arg > 1 + (SUPPORT_VLB_SYNC << 1))
return -EINVAL;
- if (ide_spin_wait_hwgroup(drive))
- return -EBUSY;
-
drive->io_32bit = arg;
- spin_unlock_irq(&ide_lock);
-
return 0;
}
+ide_devset_get_flag(ksettings, IDE_DFLAG_KEEP_SETTINGS);
+
static int set_ksettings(ide_drive_t *drive, int arg)
{
if (arg < 0 || arg > 1)
return -EINVAL;
- if (ide_spin_wait_hwgroup(drive))
- return -EBUSY;
- drive->keep_settings = arg;
- spin_unlock_irq(&ide_lock);
+ if (arg)
+ drive->dev_flags |= IDE_DFLAG_KEEP_SETTINGS;
+ else
+ drive->dev_flags &= ~IDE_DFLAG_KEEP_SETTINGS;
return 0;
}
-int set_using_dma(ide_drive_t *drive, int arg)
+ide_devset_get_flag(using_dma, IDE_DFLAG_USING_DMA);
+
+static int set_using_dma(ide_drive_t *drive, int arg)
{
#ifdef CONFIG_BLK_DEV_IDEDMA
- ide_hwif_t *hwif = drive->hwif;
int err = -EPERM;
if (arg < 0 || arg > 1)
return -EINVAL;
- if (!drive->id || !(drive->id->capability & 1))
+ if (ata_id_has_dma(drive->id) == 0)
goto out;
- if (hwif->dma_ops == NULL)
+ if (drive->hwif->dma_ops == NULL)
goto out;
- err = -EBUSY;
- if (ide_spin_wait_hwgroup(drive))
- goto out;
- /*
- * set ->busy flag, unlock and let it ride
- */
- hwif->hwgroup->busy = 1;
- spin_unlock_irq(&ide_lock);
-
err = 0;
if (arg) {
@@ -351,12 +303,6 @@
} else
ide_dma_off(drive);
- /*
- * lock, clear ->busy flag and unlock before leaving
- */
- spin_lock_irq(&ide_lock);
- hwif->hwgroup->busy = 0;
- spin_unlock_irq(&ide_lock);
out:
return err;
#else
@@ -367,9 +313,32 @@
#endif
}
-int set_pio_mode(ide_drive_t *drive, int arg)
+/*
+ * handle HDIO_SET_PIO_MODE ioctl abusers here, eventually it will go away
+ */
+static int set_pio_mode_abuse(ide_hwif_t *hwif, u8 req_pio)
{
- struct request *rq;
+ switch (req_pio) {
+ case 202:
+ case 201:
+ case 200:
+ case 102:
+ case 101:
+ case 100:
+ return (hwif->host_flags & IDE_HFLAG_ABUSE_DMA_MODES) ? 1 : 0;
+ case 9:
+ case 8:
+ return (hwif->host_flags & IDE_HFLAG_ABUSE_PREFETCH) ? 1 : 0;
+ case 7:
+ case 6:
+ return (hwif->host_flags & IDE_HFLAG_ABUSE_FAST_DEVSEL) ? 1 : 0;
+ default:
+ return 0;
+ }
+}
+
+static int set_pio_mode(ide_drive_t *drive, int arg)
+{
ide_hwif_t *hwif = drive->hwif;
const struct ide_port_ops *port_ops = hwif->port_ops;
@@ -380,48 +349,65 @@
(hwif->host_flags & IDE_HFLAG_NO_SET_MODE))
return -ENOSYS;
- if (drive->special.b.set_tune)
- return -EBUSY;
+ if (set_pio_mode_abuse(drive->hwif, arg)) {
+ if (arg == 8 || arg == 9) {
+ unsigned long flags;
- rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
- rq->cmd_type = REQ_TYPE_ATA_TASKFILE;
+ /* take lock for IDE_DFLAG_[NO_]UNMASK/[NO_]IO_32BIT */
+ spin_lock_irqsave(&ide_lock, flags);
+ port_ops->set_pio_mode(drive, arg);
+ spin_unlock_irqrestore(&ide_lock, flags);
+ } else
+ port_ops->set_pio_mode(drive, arg);
+ } else {
+ int keep_dma = !!(drive->dev_flags & IDE_DFLAG_USING_DMA);
- drive->tune_req = (u8) arg;
- drive->special.b.set_tune = 1;
+ ide_set_pio(drive, arg);
- blk_execute_rq(drive->queue, NULL, rq, 0);
- blk_put_request(rq);
+ if (hwif->host_flags & IDE_HFLAG_SET_PIO_MODE_KEEP_DMA) {
+ if (keep_dma)
+ ide_dma_on(drive);
+ }
+ }
return 0;
}
+ide_devset_get_flag(unmaskirq, IDE_DFLAG_UNMASK);
+
static int set_unmaskirq(ide_drive_t *drive, int arg)
{
- if (drive->no_unmask)
+ if (drive->dev_flags & IDE_DFLAG_NO_UNMASK)
return -EPERM;
if (arg < 0 || arg > 1)
return -EINVAL;
- if (ide_spin_wait_hwgroup(drive))
- return -EBUSY;
- drive->unmask = arg;
- spin_unlock_irq(&ide_lock);
+ if (arg)
+ drive->dev_flags |= IDE_DFLAG_UNMASK;
+ else
+ drive->dev_flags &= ~IDE_DFLAG_UNMASK;
return 0;
}
+ide_ext_devset_rw_sync(io_32bit, io_32bit);
+ide_ext_devset_rw_sync(keepsettings, ksettings);
+ide_ext_devset_rw_sync(unmaskirq, unmaskirq);
+ide_ext_devset_rw_sync(using_dma, using_dma);
+__IDE_DEVSET(pio_mode, DS_SYNC, NULL, set_pio_mode);
+
static int generic_ide_suspend(struct device *dev, pm_message_t mesg)
{
- ide_drive_t *drive = dev->driver_data;
+ ide_drive_t *drive = dev->driver_data, *pair = ide_get_pair_dev(drive);
ide_hwif_t *hwif = HWIF(drive);
struct request *rq;
struct request_pm_state rqpm;
ide_task_t args;
int ret;
- /* Call ACPI _GTM only once */
- if (!(drive->dn % 2))
+ /* call ACPI _GTM only once */
+ if ((drive->dn & 1) == 0 || pair == NULL)
ide_acpi_get_timing(hwif);
memset(&rqpm, 0, sizeof(rqpm));
@@ -430,33 +416,32 @@
rq->cmd_type = REQ_TYPE_PM_SUSPEND;
rq->special = &args;
rq->data = &rqpm;
- rqpm.pm_step = ide_pm_state_start_suspend;
+ rqpm.pm_step = IDE_PM_START_SUSPEND;
if (mesg.event == PM_EVENT_PRETHAW)
mesg.event = PM_EVENT_FREEZE;
rqpm.pm_state = mesg.event;
ret = blk_execute_rq(drive->queue, NULL, rq, 0);
blk_put_request(rq);
- /* only call ACPI _PS3 after both drivers are suspended */
- if (!ret && (((drive->dn % 2) && hwif->drives[0].present
- && hwif->drives[1].present)
- || !hwif->drives[0].present
- || !hwif->drives[1].present))
+
+ /* call ACPI _PS3 only after both devices are suspended */
+ if (ret == 0 && ((drive->dn & 1) || pair == NULL))
ide_acpi_set_state(hwif, 0);
+
return ret;
}
static int generic_ide_resume(struct device *dev)
{
- ide_drive_t *drive = dev->driver_data;
+ ide_drive_t *drive = dev->driver_data, *pair = ide_get_pair_dev(drive);
ide_hwif_t *hwif = HWIF(drive);
struct request *rq;
struct request_pm_state rqpm;
ide_task_t args;
int err;
- /* Call ACPI _STM only once */
- if (!(drive->dn % 2)) {
+ /* call ACPI _PS0 / _STM only once */
+ if ((drive->dn & 1) == 0 || pair == NULL) {
ide_acpi_set_state(hwif, 1);
ide_acpi_push_timing(hwif);
}
@@ -470,7 +455,7 @@
rq->cmd_flags |= REQ_PREEMPT;
rq->special = &args;
rq->data = &rqpm;
- rqpm.pm_step = ide_pm_state_start_resume;
+ rqpm.pm_step = IDE_PM_START_RESUME;
rqpm.pm_state = PM_EVENT_ON;
err = blk_execute_rq(drive->queue, NULL, rq, 1);
@@ -486,138 +471,6 @@
return err;
}
-static int generic_drive_reset(ide_drive_t *drive)
-{
- struct request *rq;
- int ret = 0;
-
- rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
- rq->cmd_type = REQ_TYPE_SPECIAL;
- rq->cmd_len = 1;
- rq->cmd[0] = REQ_DRIVE_RESET;
- rq->cmd_flags |= REQ_SOFTBARRIER;
- if (blk_execute_rq(drive->queue, NULL, rq, 1))
- ret = rq->errors;
- blk_put_request(rq);
- return ret;
-}
-
-int generic_ide_ioctl(ide_drive_t *drive, struct file *file, struct block_device *bdev,
- unsigned int cmd, unsigned long arg)
-{
- unsigned long flags;
- ide_driver_t *drv;
- void __user *p = (void __user *)arg;
- int err = 0, (*setfunc)(ide_drive_t *, int);
- u8 *val;
-
- switch (cmd) {
- case HDIO_GET_32BIT: val = &drive->io_32bit; goto read_val;
- case HDIO_GET_KEEPSETTINGS: val = &drive->keep_settings; goto read_val;
- case HDIO_GET_UNMASKINTR: val = &drive->unmask; goto read_val;
- case HDIO_GET_DMA: val = &drive->using_dma; goto read_val;
- case HDIO_SET_32BIT: setfunc = set_io_32bit; goto set_val;
- case HDIO_SET_KEEPSETTINGS: setfunc = set_ksettings; goto set_val;
- case HDIO_SET_PIO_MODE: setfunc = set_pio_mode; goto set_val;
- case HDIO_SET_UNMASKINTR: setfunc = set_unmaskirq; goto set_val;
- case HDIO_SET_DMA: setfunc = set_using_dma; goto set_val;
- }
-
- switch (cmd) {
- case HDIO_OBSOLETE_IDENTITY:
- case HDIO_GET_IDENTITY:
- if (bdev != bdev->bd_contains)
- return -EINVAL;
- if (drive->id_read == 0)
- return -ENOMSG;
- if (copy_to_user(p, drive->id, (cmd == HDIO_GET_IDENTITY) ? sizeof(*drive->id) : 142))
- return -EFAULT;
- return 0;
-
- case HDIO_GET_NICE:
- return put_user(drive->dsc_overlap << IDE_NICE_DSC_OVERLAP |
- drive->atapi_overlap << IDE_NICE_ATAPI_OVERLAP |
- drive->nice1 << IDE_NICE_1,
- (long __user *) arg);
-#ifdef CONFIG_IDE_TASK_IOCTL
- case HDIO_DRIVE_TASKFILE:
- if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
- return -EACCES;
- switch(drive->media) {
- case ide_disk:
- return ide_taskfile_ioctl(drive, cmd, arg);
- default:
- return -ENOMSG;
- }
-#endif /* CONFIG_IDE_TASK_IOCTL */
-
- case HDIO_DRIVE_CMD:
- if (!capable(CAP_SYS_RAWIO))
- return -EACCES;
- return ide_cmd_ioctl(drive, cmd, arg);
-
- case HDIO_DRIVE_TASK:
- if (!capable(CAP_SYS_RAWIO))
- return -EACCES;
- return ide_task_ioctl(drive, cmd, arg);
- case HDIO_SET_NICE:
- if (!capable(CAP_SYS_ADMIN)) return -EACCES;
- if (arg != (arg & ((1 << IDE_NICE_DSC_OVERLAP) | (1 << IDE_NICE_1))))
- return -EPERM;
- drive->dsc_overlap = (arg >> IDE_NICE_DSC_OVERLAP) & 1;
- drv = *(ide_driver_t **)bdev->bd_disk->private_data;
- if (drive->dsc_overlap && !drv->supports_dsc_overlap) {
- drive->dsc_overlap = 0;
- return -EPERM;
- }
- drive->nice1 = (arg >> IDE_NICE_1) & 1;
- return 0;
- case HDIO_DRIVE_RESET:
- if (!capable(CAP_SYS_ADMIN))
- return -EACCES;
-
- return generic_drive_reset(drive);
-
- case HDIO_GET_BUSSTATE:
- if (!capable(CAP_SYS_ADMIN))
- return -EACCES;
- if (put_user(HWIF(drive)->bus_state, (long __user *)arg))
- return -EFAULT;
- return 0;
-
- case HDIO_SET_BUSSTATE:
- if (!capable(CAP_SYS_ADMIN))
- return -EACCES;
- return -EOPNOTSUPP;
- default:
- return -EINVAL;
- }
-
-read_val:
- mutex_lock(&ide_setting_mtx);
- spin_lock_irqsave(&ide_lock, flags);
- err = *val;
- spin_unlock_irqrestore(&ide_lock, flags);
- mutex_unlock(&ide_setting_mtx);
- return err >= 0 ? put_user(err, (long __user *)arg) : err;
-
-set_val:
- if (bdev != bdev->bd_contains)
- err = -EINVAL;
- else {
- if (!capable(CAP_SYS_ADMIN))
- err = -EACCES;
- else {
- mutex_lock(&ide_setting_mtx);
- err = setfunc(drive, arg);
- mutex_unlock(&ide_setting_mtx);
- }
- }
- return err;
-}
-
-EXPORT_SYMBOL(generic_ide_ioctl);
-
/**
* ide_device_get - get an additional reference to a ide_drive_t
* @drive: device to get a reference to
@@ -710,21 +563,21 @@
char *buf)
{
ide_drive_t *drive = to_ide_device(dev);
- return sprintf(buf, "%s\n", drive->id->model);
+ return sprintf(buf, "%s\n", (char *)&drive->id[ATA_ID_PROD]);
}
static ssize_t firmware_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
ide_drive_t *drive = to_ide_device(dev);
- return sprintf(buf, "%s\n", drive->id->fw_rev);
+ return sprintf(buf, "%s\n", (char *)&drive->id[ATA_ID_FW_REV]);
}
static ssize_t serial_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
ide_drive_t *drive = to_ide_device(dev);
- return sprintf(buf, "%s\n", drive->id->serial_no);
+ return sprintf(buf, "%s\n", (char *)&drive->id[ATA_ID_SERNO]);
}
static struct device_attribute ide_dev_attrs[] = {
@@ -734,6 +587,7 @@
__ATTR_RO(model),
__ATTR_RO(firmware),
__ATTR(serial, 0400, serial_show, NULL),
+ __ATTR(unload_heads, 0644, ide_park_show, ide_park_store),
__ATTR_NULL
};
@@ -841,7 +695,7 @@
static unsigned int ide_nowerr;
module_param_call(nowerr, ide_set_dev_param_mask, NULL, &ide_nowerr, 0);
-MODULE_PARM_DESC(nowerr, "ignore the WRERR_STAT bit for a device");
+MODULE_PARM_DESC(nowerr, "ignore the ATA_DF bit for a device");
static unsigned int ide_cdroms;
@@ -888,31 +742,31 @@
module_param_call(chs, ide_set_disk_chs, NULL, NULL, 0);
MODULE_PARM_DESC(chs, "force device as a disk (using CHS)");
-static void ide_dev_apply_params(ide_drive_t *drive)
+static void ide_dev_apply_params(ide_drive_t *drive, u8 unit)
{
- int i = drive->hwif->index * MAX_DRIVES + drive->select.b.unit;
+ int i = drive->hwif->index * MAX_DRIVES + unit;
if (ide_nodma & (1 << i)) {
printk(KERN_INFO "ide: disallowing DMA for %s\n", drive->name);
- drive->nodma = 1;
+ drive->dev_flags |= IDE_DFLAG_NODMA;
}
if (ide_noflush & (1 << i)) {
printk(KERN_INFO "ide: disabling flush requests for %s\n",
drive->name);
- drive->noflush = 1;
+ drive->dev_flags |= IDE_DFLAG_NOFLUSH;
}
if (ide_noprobe & (1 << i)) {
printk(KERN_INFO "ide: skipping probe for %s\n", drive->name);
- drive->noprobe = 1;
+ drive->dev_flags |= IDE_DFLAG_NOPROBE;
}
if (ide_nowerr & (1 << i)) {
- printk(KERN_INFO "ide: ignoring the WRERR_STAT bit for %s\n",
+ printk(KERN_INFO "ide: ignoring the ATA_DF bit for %s\n",
drive->name);
drive->bad_wstat = BAD_R_STAT;
}
if (ide_cdroms & (1 << i)) {
printk(KERN_INFO "ide: forcing %s as a CD-ROM\n", drive->name);
- drive->present = 1;
+ drive->dev_flags |= IDE_DFLAG_PRESENT;
drive->media = ide_cdrom;
/* an ATAPI device ignores DRDY */
drive->ready_stat = 0;
@@ -921,13 +775,14 @@
drive->cyl = drive->bios_cyl = ide_disks_chs[i].cyl;
drive->head = drive->bios_head = ide_disks_chs[i].head;
drive->sect = drive->bios_sect = ide_disks_chs[i].sect;
- drive->forced_geom = 1;
+
printk(KERN_INFO "ide: forcing %s as a disk (%d/%d/%d)\n",
drive->name,
drive->cyl, drive->head, drive->sect);
- drive->present = 1;
+
+ drive->dev_flags |= IDE_DFLAG_FORCED_GEOM | IDE_DFLAG_PRESENT;
drive->media = ide_disk;
- drive->ready_stat = READY_STAT;
+ drive->ready_stat = ATA_DRDY;
}
}
@@ -965,7 +820,7 @@
}
for (i = 0; i < MAX_DRIVES; i++)
- ide_dev_apply_params(&hwif->drives[i]);
+ ide_dev_apply_params(&hwif->drives[i], i);
}
/*
diff --git a/drivers/ide/legacy/ali14xx.c b/drivers/ide/legacy/ali14xx.c
index 4ec1973..90da1f9 100644
--- a/drivers/ide/legacy/ali14xx.c
+++ b/drivers/ide/legacy/ali14xx.c
@@ -43,7 +43,6 @@
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/blkdev.h>
-#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/init.h>
@@ -132,7 +131,7 @@
drive->name, pio, time1, time2, param1, param2, param3, param4);
/* stuff timing parameters into controller registers */
- driveNum = (HWIF(drive)->index << 1) + drive->select.b.unit;
+ driveNum = (drive->hwif->index << 1) + (drive->dn & 1);
spin_lock_irqsave(&ali14xx_lock, flags);
outb_p(regOn, basePort);
outReg(param1, regTab[driveNum].reg1);
diff --git a/drivers/ide/legacy/buddha.c b/drivers/ide/legacy/buddha.c
index 7c2afa9..c5a3c9e 100644
--- a/drivers/ide/legacy/buddha.c
+++ b/drivers/ide/legacy/buddha.c
@@ -20,7 +20,6 @@
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/blkdev.h>
-#include <linux/hdreg.h>
#include <linux/zorro.h>
#include <linux/ide.h>
#include <linux/init.h>
diff --git a/drivers/ide/legacy/dtc2278.c b/drivers/ide/legacy/dtc2278.c
index af791a0..689b2e4 100644
--- a/drivers/ide/legacy/dtc2278.c
+++ b/drivers/ide/legacy/dtc2278.c
@@ -10,7 +10,6 @@
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/blkdev.h>
-#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/init.h>
diff --git a/drivers/ide/legacy/falconide.c b/drivers/ide/legacy/falconide.c
index 724f950..39d500d 100644
--- a/drivers/ide/legacy/falconide.c
+++ b/drivers/ide/legacy/falconide.c
@@ -13,7 +13,6 @@
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/blkdev.h>
-#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/init.h>
diff --git a/drivers/ide/legacy/gayle.c b/drivers/ide/legacy/gayle.c
index 51ba085..6915068 100644
--- a/drivers/ide/legacy/gayle.c
+++ b/drivers/ide/legacy/gayle.c
@@ -12,7 +12,6 @@
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/blkdev.h>
-#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/zorro.h>
diff --git a/drivers/ide/legacy/ht6560b.c b/drivers/ide/legacy/ht6560b.c
index 98f7c95..c7e5c22 100644
--- a/drivers/ide/legacy/ht6560b.c
+++ b/drivers/ide/legacy/ht6560b.c
@@ -24,7 +24,6 @@
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/blkdev.h>
-#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/init.h>
@@ -121,7 +120,8 @@
* Need to enforce prefetch sometimes because otherwise
* it'll hang (hard).
*/
- if (drive->media != ide_disk || !drive->present)
+ if (drive->media != ide_disk ||
+ (drive->dev_flags & IDE_DFLAG_PRESENT) == 0)
select |= HT_PREFETCH_MODE;
if (select != current_select || timing != current_timing) {
@@ -250,11 +250,11 @@
*/
if (state) {
drive->drive_data |= t; /* enable prefetch mode */
- drive->no_unmask = 1;
- drive->unmask = 0;
+ drive->dev_flags |= IDE_DFLAG_NO_UNMASK;
+ drive->dev_flags &= ~IDE_DFLAG_UNMASK;
} else {
drive->drive_data &= ~t; /* disable prefetch mode */
- drive->no_unmask = 0;
+ drive->dev_flags &= ~IDE_DFLAG_NO_UNMASK;
}
spin_unlock_irqrestore(&ht6560b_lock, flags);
diff --git a/drivers/ide/legacy/ide-4drives.c b/drivers/ide/legacy/ide-4drives.c
index c76d55d..9e85b1e 100644
--- a/drivers/ide/legacy/ide-4drives.c
+++ b/drivers/ide/legacy/ide-4drives.c
@@ -14,7 +14,7 @@
static void ide_4drives_init_dev(ide_drive_t *drive)
{
if (drive->hwif->channel)
- drive->select.all ^= 0x20;
+ drive->select ^= 0x20;
}
static const struct ide_port_ops ide_4drives_port_ops = {
diff --git a/drivers/ide/legacy/ide-cs.c b/drivers/ide/legacy/ide-cs.c
index 21bfac1..cb199c8 100644
--- a/drivers/ide/legacy/ide-cs.c
+++ b/drivers/ide/legacy/ide-cs.c
@@ -38,7 +38,6 @@
#include <linux/timer.h>
#include <linux/ioport.h>
#include <linux/ide.h>
-#include <linux/hdreg.h>
#include <linux/major.h>
#include <linux/delay.h>
#include <asm/io.h>
@@ -220,103 +219,91 @@
#define CS_CHECK(fn, ret) \
do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+struct pcmcia_config_check {
+ unsigned long ctl_base;
+ int skip_vcc;
+ int is_kme;
+};
+
+static int pcmcia_check_one_config(struct pcmcia_device *pdev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ struct pcmcia_config_check *stk = priv_data;
+
+ /* Check for matching Vcc, unless we're desperate */
+ if (!stk->skip_vcc) {
+ if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
+ if (vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / 10000)
+ return -ENODEV;
+ } else if (dflt->vcc.present & (1 << CISTPL_POWER_VNOM)) {
+ if (vcc != dflt->vcc.param[CISTPL_POWER_VNOM] / 10000)
+ return -ENODEV;
+ }
+ }
+
+ if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ pdev->conf.Vpp = cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+ else if (dflt->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ pdev->conf.Vpp = dflt->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+
+ if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
+ pdev->conf.ConfigIndex = cfg->index;
+ pdev->io.BasePort1 = io->win[0].base;
+ pdev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+ if (!(io->flags & CISTPL_IO_16BIT))
+ pdev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ if (io->nwin == 2) {
+ pdev->io.NumPorts1 = 8;
+ pdev->io.BasePort2 = io->win[1].base;
+ pdev->io.NumPorts2 = (stk->is_kme) ? 2 : 1;
+ if (pcmcia_request_io(pdev, &pdev->io) != 0)
+ return -ENODEV;
+ stk->ctl_base = pdev->io.BasePort2;
+ } else if ((io->nwin == 1) && (io->win[0].len >= 16)) {
+ pdev->io.NumPorts1 = io->win[0].len;
+ pdev->io.NumPorts2 = 0;
+ if (pcmcia_request_io(pdev, &pdev->io) != 0)
+ return -ENODEV;
+ stk->ctl_base = pdev->io.BasePort1 + 0x0e;
+ } else
+ return -ENODEV;
+ /* If we've got this far, we're done */
+ return 0;
+ }
+ return -ENODEV;
+}
+
static int ide_config(struct pcmcia_device *link)
{
ide_info_t *info = link->priv;
- tuple_t tuple;
- struct {
- u_short buf[128];
- cisparse_t parse;
- config_info_t conf;
- cistpl_cftable_entry_t dflt;
- } *stk = NULL;
- cistpl_cftable_entry_t *cfg;
- int pass, last_ret = 0, last_fn = 0, is_kme = 0;
+ struct pcmcia_config_check *stk = NULL;
+ int last_ret = 0, last_fn = 0, is_kme = 0;
unsigned long io_base, ctl_base;
struct ide_host *host;
DEBUG(0, "ide_config(0x%p)\n", link);
- stk = kzalloc(sizeof(*stk), GFP_KERNEL);
- if (!stk) goto err_mem;
- cfg = &stk->parse.cftable_entry;
-
- tuple.TupleData = (cisdata_t *)&stk->buf;
- tuple.TupleOffset = 0;
- tuple.TupleDataMax = 255;
- tuple.Attributes = 0;
-
is_kme = ((link->manf_id == MANFID_KME) &&
((link->card_id == PRODID_KME_KXLC005_A) ||
(link->card_id == PRODID_KME_KXLC005_B)));
- /* Not sure if this is right... look up the current Vcc */
- CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(link, &stk->conf));
+ stk = kzalloc(sizeof(*stk), GFP_KERNEL);
+ if (!stk)
+ goto err_mem;
+ stk->is_kme = is_kme;
+ stk->skip_vcc = io_base = ctl_base = 0;
- pass = io_base = ctl_base = 0;
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- tuple.Attributes = 0;
- CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
- while (1) {
- if (pcmcia_get_tuple_data(link, &tuple) != 0) goto next_entry;
- if (pcmcia_parse_tuple(link, &tuple, &stk->parse) != 0) goto next_entry;
-
- /* Check for matching Vcc, unless we're desperate */
- if (!pass) {
- if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
- if (stk->conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / 10000)
- goto next_entry;
- } else if (stk->dflt.vcc.present & (1 << CISTPL_POWER_VNOM)) {
- if (stk->conf.Vcc != stk->dflt.vcc.param[CISTPL_POWER_VNOM] / 10000)
- goto next_entry;
- }
- }
-
- if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
- link->conf.Vpp =
- cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
- else if (stk->dflt.vpp1.present & (1 << CISTPL_POWER_VNOM))
- link->conf.Vpp =
- stk->dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000;
-
- if ((cfg->io.nwin > 0) || (stk->dflt.io.nwin > 0)) {
- cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &stk->dflt.io;
- link->conf.ConfigIndex = cfg->index;
- link->io.BasePort1 = io->win[0].base;
- link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
- if (!(io->flags & CISTPL_IO_16BIT))
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
- if (io->nwin == 2) {
- link->io.NumPorts1 = 8;
- link->io.BasePort2 = io->win[1].base;
- link->io.NumPorts2 = (is_kme) ? 2 : 1;
- if (pcmcia_request_io(link, &link->io) != 0)
- goto next_entry;
- io_base = link->io.BasePort1;
- ctl_base = link->io.BasePort2;
- } else if ((io->nwin == 1) && (io->win[0].len >= 16)) {
- link->io.NumPorts1 = io->win[0].len;
- link->io.NumPorts2 = 0;
- if (pcmcia_request_io(link, &link->io) != 0)
- goto next_entry;
- io_base = link->io.BasePort1;
- ctl_base = link->io.BasePort1 + 0x0e;
- } else goto next_entry;
- /* If we've got this far, we're done */
- break;
- }
-
- next_entry:
- if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
- memcpy(&stk->dflt, cfg, sizeof(stk->dflt));
- if (pass) {
- CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple));
- } else if (pcmcia_get_next_tuple(link, &tuple) != 0) {
- CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
- memset(&stk->dflt, 0, sizeof(stk->dflt));
- pass++;
- }
+ if (pcmcia_loop_config(link, pcmcia_check_one_config, stk)) {
+ stk->skip_vcc = 1;
+ if (pcmcia_loop_config(link, pcmcia_check_one_config, stk))
+ goto failed; /* No suitable config found */
}
+ io_base = link->io.BasePort1;
+ ctl_base = stk->ctl_base;
CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
@@ -404,8 +391,10 @@
PCMCIA_DEVICE_MANF_CARD(0x000a, 0x0000), /* I-O Data CFA */
PCMCIA_DEVICE_MANF_CARD(0x001c, 0x0001), /* Mitsubishi CFA */
PCMCIA_DEVICE_MANF_CARD(0x0032, 0x0704),
+ PCMCIA_DEVICE_MANF_CARD(0x0032, 0x2904),
PCMCIA_DEVICE_MANF_CARD(0x0045, 0x0401), /* SanDisk CFA */
PCMCIA_DEVICE_MANF_CARD(0x004f, 0x0000), /* Kingston */
+ PCMCIA_DEVICE_MANF_CARD(0x0097, 0x1620), /* TI emulated */
PCMCIA_DEVICE_MANF_CARD(0x0098, 0x0000), /* Toshiba */
PCMCIA_DEVICE_MANF_CARD(0x00a4, 0x002d),
PCMCIA_DEVICE_MANF_CARD(0x00ce, 0x0000), /* Samsung */
diff --git a/drivers/ide/legacy/macide.c b/drivers/ide/legacy/macide.c
index a0bb167..43f97cc 100644
--- a/drivers/ide/legacy/macide.c
+++ b/drivers/ide/legacy/macide.c
@@ -15,7 +15,6 @@
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/blkdev.h>
-#include <linux/hdreg.h>
#include <linux/delay.h>
#include <linux/ide.h>
diff --git a/drivers/ide/legacy/q40ide.c b/drivers/ide/legacy/q40ide.c
index 4abd8fc..4af4a8c 100644
--- a/drivers/ide/legacy/q40ide.c
+++ b/drivers/ide/legacy/q40ide.c
@@ -14,8 +14,6 @@
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/blkdev.h>
-#include <linux/hdreg.h>
-
#include <linux/ide.h>
/*
diff --git a/drivers/ide/legacy/qd65xx.c b/drivers/ide/legacy/qd65xx.c
index 2338f34..bc27c7a 100644
--- a/drivers/ide/legacy/qd65xx.c
+++ b/drivers/ide/legacy/qd65xx.c
@@ -27,7 +27,6 @@
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/blkdev.h>
-#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <asm/system.h>
@@ -151,12 +150,14 @@
int *active_time, int *recovery_time)
{
struct qd65xx_timing_s *p;
- char model[40];
+ char *m = (char *)&drive->id[ATA_ID_PROD];
+ char model[ATA_ID_PROD_LEN];
- if (!*drive->id->model) return 0;
+ if (*m == 0)
+ return 0;
- strncpy(model,drive->id->model,40);
- ide_fixstring(model,40,1); /* byte-swap */
+ strncpy(model, m, ATA_ID_PROD_LEN);
+ ide_fixstring(model, ATA_ID_PROD_LEN, 1); /* byte-swap */
for (p = qd65xx_timing ; p->offset != -1 ; p++) {
if (!strncmp(p->model, model+p->offset, 4)) {
@@ -185,20 +186,20 @@
static void qd6500_set_pio_mode(ide_drive_t *drive, const u8 pio)
{
+ u16 *id = drive->id;
int active_time = 175;
int recovery_time = 415; /* worst case values from the dos driver */
/*
* FIXME: use "pio" value
*/
- if (drive->id && !qd_find_disk_type(drive, &active_time, &recovery_time)
- && drive->id->tPIO && (drive->id->field_valid & 0x02)
- && drive->id->eide_pio >= 240) {
-
+ if (!qd_find_disk_type(drive, &active_time, &recovery_time) &&
+ (id[ATA_ID_OLD_PIO_MODES] & 0xff) && (id[ATA_ID_FIELD_VALID] & 2) &&
+ id[ATA_ID_EIDE_PIO] >= 240) {
printk(KERN_INFO "%s: PIO mode%d\n", drive->name,
- drive->id->tPIO);
+ id[ATA_ID_OLD_PIO_MODES] & 0xff);
active_time = 110;
- recovery_time = drive->id->eide_pio - 120;
+ recovery_time = drive->id[ATA_ID_EIDE_PIO] - 120;
}
qd_set_timing(drive, qd6500_compute_timing(HWIF(drive), active_time, recovery_time));
@@ -304,7 +305,7 @@
} else
t2 = t1 = hwif->channel ? QD6580_DEF_DATA2 : QD6580_DEF_DATA;
- drive->drive_data = drive->select.b.unit ? t2 : t1;
+ drive->drive_data = (drive->dn & 1) ? t2 : t1;
}
static const struct ide_port_ops qd6500_port_ops = {
diff --git a/drivers/ide/legacy/umc8672.c b/drivers/ide/legacy/umc8672.c
index b54a14a..1da076e 100644
--- a/drivers/ide/legacy/umc8672.c
+++ b/drivers/ide/legacy/umc8672.c
@@ -45,7 +45,6 @@
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/blkdev.h>
-#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/init.h>
diff --git a/drivers/ide/mips/au1xxx-ide.c b/drivers/ide/mips/au1xxx-ide.c
index 11b7f61..0ec8fd1 100644
--- a/drivers/ide/mips/au1xxx-ide.c
+++ b/drivers/ide/mips/au1xxx-ide.c
@@ -322,11 +322,7 @@
}
static int auide_dma_test_irq(ide_drive_t *drive)
-{
- if (drive->waiting_for_dma == 0)
- printk(KERN_WARNING "%s: ide_dma_test_irq \
- called while not waiting\n", drive->name);
-
+{
/* If dbdma didn't execute the STOP command yet, the
* active bit is still set
*/
@@ -344,11 +340,6 @@
{
}
-static void auide_dma_lost_irq(ide_drive_t *drive)
-{
- printk(KERN_ERR "%s: IRQ lost\n", drive->name);
-}
-
static void auide_ddma_tx_callback(int irq, void *param)
{
_auide_hwif *ahwif = (_auide_hwif*)param;
@@ -375,18 +366,6 @@
}
#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA
-static void auide_dma_timeout(ide_drive_t *drive)
-{
- ide_hwif_t *hwif = HWIF(drive);
-
- printk(KERN_ERR "%s: DMA timeout occurred: ", drive->name);
-
- if (auide_dma_test_irq(drive))
- return;
-
- auide_dma_end(drive);
-}
-
static const struct ide_dma_ops au1xxx_dma_ops = {
.dma_host_set = auide_dma_host_set,
.dma_setup = auide_dma_setup,
@@ -394,8 +373,8 @@
.dma_start = auide_dma_start,
.dma_end = auide_dma_end,
.dma_test_irq = auide_dma_test_irq,
- .dma_lost_irq = auide_dma_lost_irq,
- .dma_timeout = auide_dma_timeout,
+ .dma_lost_irq = ide_dma_lost_irq,
+ .dma_timeout = ide_dma_timeout,
};
static int auide_ddma_init(ide_hwif_t *hwif, const struct ide_port_info *d)
@@ -448,10 +427,9 @@
NUM_DESCRIPTORS);
auide->rx_desc_head = (void*)au1xxx_dbdma_ring_alloc(auide->rx_chan,
NUM_DESCRIPTORS);
-
- hwif->dmatable_cpu = dma_alloc_coherent(hwif->dev,
- PRD_ENTRIES * PRD_BYTES, /* 1 Page */
- &hwif->dmatable_dma, GFP_KERNEL);
+
+ /* FIXME: check return value */
+ (void)ide_allocate_dma_engine(hwif);
au1xxx_dbdma_start( auide->tx_chan );
au1xxx_dbdma_start( auide->rx_chan );
diff --git a/drivers/ide/pci/aec62xx.c b/drivers/ide/pci/aec62xx.c
index 3187215..4142c69 100644
--- a/drivers/ide/pci/aec62xx.c
+++ b/drivers/ide/pci/aec62xx.c
@@ -7,7 +7,6 @@
#include <linux/module.h>
#include <linux/types.h>
#include <linux/pci.h>
-#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/init.h>
@@ -116,7 +115,7 @@
struct pci_dev *dev = to_pci_dev(hwif->dev);
struct ide_host *host = pci_get_drvdata(dev);
struct chipset_bus_clock_list_entry *bus_clock = host->host_priv;
- u8 unit = (drive->select.b.unit & 0x01);
+ u8 unit = drive->dn & 1;
u8 tmp1 = 0, tmp2 = 0;
u8 ultra = 0, drive_conf = 0, ultra_conf = 0;
unsigned long flags;
@@ -140,7 +139,7 @@
drive->hwif->port_ops->set_dma_mode(drive, pio + XFER_PIO_0);
}
-static unsigned int __devinit init_chipset_aec62xx(struct pci_dev *dev)
+static unsigned int init_chipset_aec62xx(struct pci_dev *dev)
{
/* These are necessary to get AEC6280 Macintosh cards to work */
if ((dev->device == PCI_DEVICE_ID_ARTOP_ATP865) ||
@@ -303,21 +302,23 @@
};
MODULE_DEVICE_TABLE(pci, aec62xx_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver aec62xx_pci_driver = {
.name = "AEC62xx_IDE",
.id_table = aec62xx_pci_tbl,
.probe = aec62xx_init_one,
.remove = __devexit_p(aec62xx_remove),
+ .suspend = ide_pci_suspend,
+ .resume = ide_pci_resume,
};
static int __init aec62xx_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&aec62xx_pci_driver);
}
static void __exit aec62xx_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&aec62xx_pci_driver);
}
module_init(aec62xx_ide_init);
diff --git a/drivers/ide/pci/alim15x3.c b/drivers/ide/pci/alim15x3.c
index d647526..daf9dce 100644
--- a/drivers/ide/pci/alim15x3.c
+++ b/drivers/ide/pci/alim15x3.c
@@ -31,7 +31,6 @@
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/pci.h>
-#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/dmi.h>
@@ -78,8 +77,7 @@
int bus_speed = ide_pci_clk ? ide_pci_clk : 33;
int port = hwif->channel ? 0x5c : 0x58;
int portFIFO = hwif->channel ? 0x55 : 0x54;
- u8 cd_dma_fifo = 0;
- int unit = drive->select.b.unit & 1;
+ u8 cd_dma_fifo = 0, unit = drive->dn & 1;
if ((s_clc = (s_time * bus_speed + 999) / 1000) >= 8)
s_clc = 0;
@@ -113,7 +111,7 @@
}
pci_write_config_byte(dev, port, s_clc);
- pci_write_config_byte(dev, port+drive->select.b.unit+2, (a_clc << 4) | r_clc);
+ pci_write_config_byte(dev, port + unit + 2, (a_clc << 4) | r_clc);
local_irq_restore(flags);
}
@@ -134,8 +132,8 @@
if (m5229_revision > 0x20 && m5229_revision < 0xC2) {
if (drive->media != ide_disk)
return 0;
- if (chip_is_1543c_e && strstr(drive->id->model, "WDC ") &&
- wdc_udma == 0)
+ if (wdc_udma == 0 && chip_is_1543c_e &&
+ strstr((char *)&drive->id[ATA_ID_PROD], "WDC "))
return 0;
}
@@ -155,7 +153,7 @@
ide_hwif_t *hwif = HWIF(drive);
struct pci_dev *dev = to_pci_dev(hwif->dev);
u8 speed1 = speed;
- u8 unit = (drive->select.b.unit & 0x01);
+ u8 unit = drive->dn & 1;
u8 tmpbyte = 0x00;
int m5229_udma = (hwif->channel) ? 0x57 : 0x56;
@@ -214,7 +212,7 @@
* appropriate also sets up the 1533 southbridge.
*/
-static unsigned int __devinit init_chipset_ali15x3(struct pci_dev *dev)
+static unsigned int init_chipset_ali15x3(struct pci_dev *dev)
{
unsigned long flags;
u8 tmpbyte;
@@ -509,7 +507,7 @@
.dma_setup = ali15x3_dma_setup,
.dma_exec_cmd = ide_dma_exec_cmd,
.dma_start = ide_dma_start,
- .dma_end = __ide_dma_end,
+ .dma_end = ide_dma_end,
.dma_test_irq = ide_dma_test_irq,
.dma_lost_irq = ide_dma_lost_irq,
.dma_timeout = ide_dma_timeout,
@@ -577,21 +575,23 @@
};
MODULE_DEVICE_TABLE(pci, alim15x3_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver alim15x3_pci_driver = {
.name = "ALI15x3_IDE",
.id_table = alim15x3_pci_tbl,
.probe = alim15x3_init_one,
.remove = ide_pci_remove,
+ .suspend = ide_pci_suspend,
+ .resume = ide_pci_resume,
};
static int __init ali15x3_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&alim15x3_pci_driver);
}
static void __exit ali15x3_ide_exit(void)
{
- return pci_unregister_driver(&driver);
+ return pci_unregister_driver(&alim15x3_pci_driver);
}
module_init(ali15x3_ide_init);
diff --git a/drivers/ide/pci/amd74xx.c b/drivers/ide/pci/amd74xx.c
index 1e66a96..81ec731 100644
--- a/drivers/ide/pci/amd74xx.c
+++ b/drivers/ide/pci/amd74xx.c
@@ -92,7 +92,7 @@
ide_timing_compute(drive, speed, &t, T, UT);
- if (peer->present) {
+ if (peer->dev_flags & IDE_DFLAG_PRESENT) {
ide_timing_compute(peer, peer->current_speed, &p, T, UT);
ide_timing_merge(&p, &t, &t, IDE_TIMING_8BIT);
}
@@ -112,13 +112,13 @@
amd_set_drive(drive, XFER_PIO_0 + pio);
}
-static void __devinit amd7409_cable_detect(struct pci_dev *dev)
+static void amd7409_cable_detect(struct pci_dev *dev)
{
/* no host side cable detection */
amd_80w = 0x03;
}
-static void __devinit amd7411_cable_detect(struct pci_dev *dev)
+static void amd7411_cable_detect(struct pci_dev *dev)
{
int i;
u32 u = 0;
@@ -140,7 +140,7 @@
* The initialization callback. Initialize drive independent registers.
*/
-static unsigned int __devinit init_chipset_amd74xx(struct pci_dev *dev)
+static unsigned int init_chipset_amd74xx(struct pci_dev *dev)
{
u8 t = 0, offset = amd_offset(dev);
@@ -319,21 +319,23 @@
};
MODULE_DEVICE_TABLE(pci, amd74xx_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver amd74xx_pci_driver = {
.name = "AMD_IDE",
.id_table = amd74xx_pci_tbl,
.probe = amd74xx_probe,
.remove = ide_pci_remove,
+ .suspend = ide_pci_suspend,
+ .resume = ide_pci_resume,
};
static int __init amd74xx_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&amd74xx_pci_driver);
}
static void __exit amd74xx_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&amd74xx_pci_driver);
}
module_init(amd74xx_ide_init);
diff --git a/drivers/ide/pci/atiixp.c b/drivers/ide/pci/atiixp.c
index 41f6cb6..b2735d2 100644
--- a/drivers/ide/pci/atiixp.c
+++ b/drivers/ide/pci/atiixp.c
@@ -7,7 +7,6 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
-#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/init.h>
@@ -183,21 +182,23 @@
};
MODULE_DEVICE_TABLE(pci, atiixp_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver atiixp_pci_driver = {
.name = "ATIIXP_IDE",
.id_table = atiixp_pci_tbl,
.probe = atiixp_init_one,
.remove = ide_pci_remove,
+ .suspend = ide_pci_suspend,
+ .resume = ide_pci_resume,
};
static int __init atiixp_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&atiixp_pci_driver);
}
static void __exit atiixp_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&atiixp_pci_driver);
}
module_init(atiixp_ide_init);
diff --git a/drivers/ide/pci/cmd640.c b/drivers/ide/pci/cmd640.c
index e6c6200..e430664 100644
--- a/drivers/ide/pci/cmd640.c
+++ b/drivers/ide/pci/cmd640.c
@@ -103,7 +103,6 @@
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
-#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/init.h>
@@ -375,6 +374,21 @@
}
#endif
+static void __set_prefetch_mode(ide_drive_t *drive, int mode)
+{
+ if (mode) { /* want prefetch on? */
+#if CMD640_PREFETCH_MASKS
+ drive->dev_flags |= IDE_DFLAG_NO_UNMASK;
+ drive->dev_flags &= ~IDE_DFLAG_UNMASK;
+#endif
+ drive->dev_flags &= ~IDE_DFLAG_NO_IO_32BIT;
+ } else {
+ drive->dev_flags &= ~IDE_DFLAG_NO_UNMASK;
+ drive->dev_flags |= IDE_DFLAG_NO_IO_32BIT;
+ drive->io_32bit = 0;
+ }
+}
+
#ifndef CONFIG_BLK_DEV_CMD640_ENHANCED
/*
* Check whether prefetch is on for a drive,
@@ -384,19 +398,10 @@
{
u8 b = get_cmd640_reg(prefetch_regs[index]);
- if (b & prefetch_masks[index]) { /* is prefetch off? */
- drive->no_unmask = 0;
- drive->no_io_32bit = 1;
- drive->io_32bit = 0;
- } else {
-#if CMD640_PREFETCH_MASKS
- drive->no_unmask = 1;
- drive->unmask = 0;
-#endif
- drive->no_io_32bit = 0;
- }
+ __set_prefetch_mode(drive, (b & prefetch_masks[index]) ? 0 : 1);
}
#else
+
/*
* Sets prefetch mode for a drive.
*/
@@ -408,19 +413,11 @@
spin_lock_irqsave(&cmd640_lock, flags);
b = __get_cmd640_reg(reg);
- if (mode) { /* want prefetch on? */
-#if CMD640_PREFETCH_MASKS
- drive->no_unmask = 1;
- drive->unmask = 0;
-#endif
- drive->no_io_32bit = 0;
+ __set_prefetch_mode(drive, mode);
+ if (mode)
b &= ~prefetch_masks[index]; /* enable prefetch */
- } else {
- drive->no_unmask = 0;
- drive->no_io_32bit = 1;
- drive->io_32bit = 0;
+ else
b |= prefetch_masks[index]; /* disable prefetch */
- }
__put_cmd640_reg(reg, b);
spin_unlock_irqrestore(&cmd640_lock, flags);
}
@@ -471,10 +468,10 @@
*/
if (index > 1) {
ide_hwif_t *hwif = drive->hwif;
- ide_drive_t *peer = &hwif->drives[!drive->select.b.unit];
+ ide_drive_t *peer = &hwif->drives[!(drive->dn & 1)];
unsigned int mate = index ^ 1;
- if (peer->present) {
+ if (peer->dev_flags & IDE_DFLAG_PRESENT) {
if (setup_count < setup_counts[mate])
setup_count = setup_counts[mate];
if (active_count < active_counts[mate])
@@ -610,7 +607,7 @@
static void cmd640_init_dev(ide_drive_t *drive)
{
- unsigned int i = drive->hwif->channel * 2 + drive->select.b.unit;
+ unsigned int i = drive->hwif->channel * 2 + (drive->dn & 1);
#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
/*
@@ -629,7 +626,7 @@
*/
check_prefetch(drive, i);
printk(KERN_INFO DRV_NAME ": drive%d timings/prefetch(%s) preserved\n",
- i, drive->no_io_32bit ? "off" : "on");
+ i, (drive->dev_flags & IDE_DFLAG_NO_IO_32BIT) ? "off" : "on");
#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
}
diff --git a/drivers/ide/pci/cmd64x.c b/drivers/ide/pci/cmd64x.c
index e064398..935385c 100644
--- a/drivers/ide/pci/cmd64x.c
+++ b/drivers/ide/pci/cmd64x.c
@@ -13,7 +13,6 @@
#include <linux/module.h>
#include <linux/types.h>
#include <linux/pci.h>
-#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/init.h>
@@ -229,7 +228,7 @@
{
ide_hwif_t *hwif = HWIF(drive);
unsigned long base = hwif->dma_base - (hwif->channel * 8);
- int err = __ide_dma_end(drive);
+ int err = ide_dma_end(drive);
u8 irq_mask = hwif->channel ? MRDMODE_INTR_CH1 :
MRDMODE_INTR_CH0;
u8 mrdmode = inb(base + 1);
@@ -249,7 +248,7 @@
u8 irq_mask = hwif->channel ? ARTTIM23_INTR_CH1 :
CFR_INTR_CH0;
u8 irq_stat = 0;
- int err = __ide_dma_end(drive);
+ int err = ide_dma_end(drive);
(void) pci_read_config_byte(dev, irq_reg, &irq_stat);
/* clear the interrupt bit */
@@ -332,7 +331,7 @@
return (dma_stat & 7) != 4;
}
-static unsigned int __devinit init_chipset_cmd64x(struct pci_dev *dev)
+static unsigned int init_chipset_cmd64x(struct pci_dev *dev)
{
u8 mrdmode = 0;
@@ -506,21 +505,23 @@
};
MODULE_DEVICE_TABLE(pci, cmd64x_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver cmd64x_pci_driver = {
.name = "CMD64x_IDE",
.id_table = cmd64x_pci_tbl,
.probe = cmd64x_init_one,
.remove = ide_pci_remove,
+ .suspend = ide_pci_suspend,
+ .resume = ide_pci_resume,
};
static int __init cmd64x_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&cmd64x_pci_driver);
}
static void __exit cmd64x_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&cmd64x_pci_driver);
}
module_init(cmd64x_ide_init);
diff --git a/drivers/ide/pci/cs5520.c b/drivers/ide/pci/cs5520.c
index 151844f..5efb467 100644
--- a/drivers/ide/pci/cs5520.c
+++ b/drivers/ide/pci/cs5520.c
@@ -35,7 +35,6 @@
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
-#include <linux/hdreg.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/ide.h>
@@ -146,15 +145,17 @@
};
MODULE_DEVICE_TABLE(pci, cs5520_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver cs5520_pci_driver = {
.name = "Cyrix_IDE",
.id_table = cs5520_pci_tbl,
.probe = cs5520_init_one,
+ .suspend = ide_pci_suspend,
+ .resume = ide_pci_resume,
};
static int __init cs5520_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&cs5520_pci_driver);
}
module_init(cs5520_ide_init);
diff --git a/drivers/ide/pci/cs5530.c b/drivers/ide/pci/cs5530.c
index f235db8..53f079c 100644
--- a/drivers/ide/pci/cs5530.c
+++ b/drivers/ide/pci/cs5530.c
@@ -15,7 +15,6 @@
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
-#include <linux/hdreg.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/ide.h>
@@ -81,17 +80,19 @@
static u8 cs5530_udma_filter(ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
- ide_drive_t *mate = &hwif->drives[(drive->dn & 1) ^ 1];
- struct hd_driveid *mateid = mate->id;
+ ide_drive_t *mate = ide_get_pair_dev(drive);
+ u16 *mateid = mate->id;
u8 mask = hwif->ultra_mask;
- if (mate->present == 0)
+ if (mate == NULL)
goto out;
- if ((mateid->capability & 1) && __ide_dma_bad_drive(mate) == 0) {
- if ((mateid->field_valid & 4) && (mateid->dma_ultra & 7))
+ if (ata_id_has_dma(mateid) && __ide_dma_bad_drive(mate) == 0) {
+ if ((mateid[ATA_ID_FIELD_VALID] & 4) &&
+ (mateid[ATA_ID_UDMA_MODES] & 7))
goto out;
- if ((mateid->field_valid & 2) && (mateid->dma_mword & 7))
+ if ((mateid[ATA_ID_FIELD_VALID] & 2) &&
+ (mateid[ATA_ID_MWDMA_MODES] & 7))
mask = 0;
}
out:
@@ -133,7 +134,7 @@
* Initialize the cs5530 bridge for reliable IDE DMA operation.
*/
-static unsigned int __devinit init_chipset_cs5530(struct pci_dev *dev)
+static unsigned int init_chipset_cs5530(struct pci_dev *dev)
{
struct pci_dev *master_0 = NULL, *cs5530_0 = NULL;
@@ -266,21 +267,23 @@
};
MODULE_DEVICE_TABLE(pci, cs5530_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver cs5530_pci_driver = {
.name = "CS5530 IDE",
.id_table = cs5530_pci_tbl,
.probe = cs5530_init_one,
.remove = ide_pci_remove,
+ .suspend = ide_pci_suspend,
+ .resume = ide_pci_resume,
};
static int __init cs5530_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&cs5530_pci_driver);
}
static void __exit cs5530_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&cs5530_pci_driver);
}
module_init(cs5530_ide_init);
diff --git a/drivers/ide/pci/cs5535.c b/drivers/ide/pci/cs5535.c
index dd3dc23..983d957 100644
--- a/drivers/ide/pci/cs5535.c
+++ b/drivers/ide/pci/cs5535.c
@@ -76,16 +76,16 @@
static void cs5535_set_speed(ide_drive_t *drive, const u8 speed)
{
u32 reg = 0, dummy;
- int unit = drive->select.b.unit;
+ u8 unit = drive->dn & 1;
/* Set the PIO timings */
if (speed < XFER_SW_DMA_0) {
- ide_drive_t *pair = ide_get_paired_drive(drive);
+ ide_drive_t *pair = ide_get_pair_dev(drive);
u8 cmd, pioa;
cmd = pioa = speed - XFER_PIO_0;
- if (pair->present) {
+ if (pair) {
u8 piob = ide_get_best_pio_mode(pair, 255, 4);
if (piob < cmd)
@@ -192,21 +192,23 @@
MODULE_DEVICE_TABLE(pci, cs5535_pci_tbl);
-static struct pci_driver driver = {
- .name = "CS5535_IDE",
- .id_table = cs5535_pci_tbl,
- .probe = cs5535_init_one,
- .remove = ide_pci_remove,
+static struct pci_driver cs5535_pci_driver = {
+ .name = "CS5535_IDE",
+ .id_table = cs5535_pci_tbl,
+ .probe = cs5535_init_one,
+ .remove = ide_pci_remove,
+ .suspend = ide_pci_suspend,
+ .resume = ide_pci_resume,
};
static int __init cs5535_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&cs5535_pci_driver);
}
static void __exit cs5535_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&cs5535_pci_driver);
}
module_init(cs5535_ide_init);
diff --git a/drivers/ide/pci/cy82c693.c b/drivers/ide/pci/cy82c693.c
index e6d8ee8..5297f07 100644
--- a/drivers/ide/pci/cy82c693.c
+++ b/drivers/ide/pci/cy82c693.c
@@ -50,18 +50,11 @@
#define DRV_NAME "cy82c693"
-/* the current version */
-#define CY82_VERSION "CY82C693U driver v0.34 99-13-12 Andreas S. Krebs (akrebs@altavista.net)"
-
/*
* The following are used to debug the driver.
*/
-#define CY82C693_DEBUG_LOGS 0
#define CY82C693_DEBUG_INFO 0
-/* define CY82C693_SETDMA_CLOCK to set DMA Controller Clock Speed to ATCLK */
-#undef CY82C693_SETDMA_CLOCK
-
/*
* NOTE: the value for busmaster timeout is tricky and I got it by
* trial and error! By using a to low value will cause DMA timeouts
@@ -89,7 +82,6 @@
#define CY82_INDEX_PORT 0x22
#define CY82_DATA_PORT 0x23
-#define CY82_INDEX_CTRLREG1 0x01
#define CY82_INDEX_CHANNEL0 0x30
#define CY82_INDEX_CHANNEL1 0x31
#define CY82_INDEX_TIMEOUT 0x32
@@ -179,17 +171,6 @@
index = hwif->channel ? CY82_INDEX_CHANNEL1 : CY82_INDEX_CHANNEL0;
-#if CY82C693_DEBUG_LOGS
- /* for debug let's show the previous values */
-
- outb(index, CY82_INDEX_PORT);
- data = inb(CY82_DATA_PORT);
-
- printk(KERN_INFO "%s (ch=%d, dev=%d): DMA mode is %d (single=%d)\n",
- drive->name, HWIF(drive)->channel, drive->select.b.unit,
- (data&0x3), ((data>>2)&1));
-#endif /* CY82C693_DEBUG_LOGS */
-
data = (mode & 3) | (single << 2);
outb(index, CY82_INDEX_PORT);
@@ -197,8 +178,7 @@
#if CY82C693_DEBUG_INFO
printk(KERN_INFO "%s (ch=%d, dev=%d): set DMA mode to %d (single=%d)\n",
- drive->name, HWIF(drive)->channel, drive->select.b.unit,
- mode & 3, single);
+ drive->name, hwif->channel, drive->dn & 1, mode & 3, single);
#endif /* CY82C693_DEBUG_INFO */
/*
@@ -239,50 +219,11 @@
}
}
-#if CY82C693_DEBUG_LOGS
- /* for debug let's show the register values */
-
- if (drive->select.b.unit == 0) {
- /*
- * get master drive registers
- * address setup control register
- * is 32 bit !!!
- */
- pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);
- addrCtrl &= 0x0F;
-
- /* now let's get the remaining registers */
- pci_read_config_byte(dev, CY82_IDE_MASTER_IOR, &pclk.time_16r);
- pci_read_config_byte(dev, CY82_IDE_MASTER_IOW, &pclk.time_16w);
- pci_read_config_byte(dev, CY82_IDE_MASTER_8BIT, &pclk.time_8);
- } else {
- /*
- * set slave drive registers
- * address setup control register
- * is 32 bit !!!
- */
- pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);
-
- addrCtrl &= 0xF0;
- addrCtrl >>= 4;
-
- /* now let's get the remaining registers */
- pci_read_config_byte(dev, CY82_IDE_SLAVE_IOR, &pclk.time_16r);
- pci_read_config_byte(dev, CY82_IDE_SLAVE_IOW, &pclk.time_16w);
- pci_read_config_byte(dev, CY82_IDE_SLAVE_8BIT, &pclk.time_8);
- }
-
- printk(KERN_INFO "%s (ch=%d, dev=%d): PIO timing is "
- "(addr=0x%X, ior=0x%X, iow=0x%X, 8bit=0x%X)\n",
- drive->name, hwif->channel, drive->select.b.unit,
- addrCtrl, pclk.time_16r, pclk.time_16w, pclk.time_8);
-#endif /* CY82C693_DEBUG_LOGS */
-
/* let's calc the values for this PIO mode */
compute_clocks(pio, &pclk);
/* now let's write the clocks registers */
- if (drive->select.b.unit == 0) {
+ if ((drive->dn & 1) == 0) {
/*
* set master drive
* address setup control register
@@ -324,63 +265,11 @@
#if CY82C693_DEBUG_INFO
printk(KERN_INFO "%s (ch=%d, dev=%d): set PIO timing to "
"(addr=0x%X, ior=0x%X, iow=0x%X, 8bit=0x%X)\n",
- drive->name, hwif->channel, drive->select.b.unit,
+ drive->name, hwif->channel, drive->dn & 1,
addrCtrl, pclk.time_16r, pclk.time_16w, pclk.time_8);
#endif /* CY82C693_DEBUG_INFO */
}
-/*
- * this function is called during init and is used to setup the cy82c693 chip
- */
-static unsigned int __devinit init_chipset_cy82c693(struct pci_dev *dev)
-{
- if (PCI_FUNC(dev->devfn) != 1)
- return 0;
-
-#ifdef CY82C693_SETDMA_CLOCK
- u8 data = 0;
-#endif /* CY82C693_SETDMA_CLOCK */
-
- /* write info about this verion of the driver */
- printk(KERN_INFO CY82_VERSION "\n");
-
-#ifdef CY82C693_SETDMA_CLOCK
- /* okay let's set the DMA clock speed */
-
- outb(CY82_INDEX_CTRLREG1, CY82_INDEX_PORT);
- data = inb(CY82_DATA_PORT);
-
-#if CY82C693_DEBUG_INFO
- printk(KERN_INFO DRV_NAME ": Peripheral Configuration Register: 0x%X\n",
- data);
-#endif /* CY82C693_DEBUG_INFO */
-
- /*
- * for some reason sometimes the DMA controller
- * speed is set to ATCLK/2 ???? - we fix this here
- *
- * note: i don't know what causes this strange behaviour,
- * but even changing the dma speed doesn't solve it :-(
- * the ide performance is still only half the normal speed
- *
- * if anybody knows what goes wrong with my machine, please
- * let me know - ASK
- */
-
- data |= 0x03;
-
- outb(CY82_INDEX_CTRLREG1, CY82_INDEX_PORT);
- outb(data, CY82_DATA_PORT);
-
-#if CY82C693_DEBUG_INFO
- printk(KERN_INFO ": New Peripheral Configuration Register: 0x%X\n",
- data);
-#endif /* CY82C693_DEBUG_INFO */
-
-#endif /* CY82C693_SETDMA_CLOCK */
- return 0;
-}
-
static void __devinit init_iops_cy82c693(ide_hwif_t *hwif)
{
static ide_hwif_t *primary;
@@ -401,7 +290,6 @@
static const struct ide_port_info cy82c693_chipset __devinitdata = {
.name = DRV_NAME,
- .init_chipset = init_chipset_cy82c693,
.init_iops = init_iops_cy82c693,
.port_ops = &cy82c693_port_ops,
.chipset = ide_cy82c693,
@@ -443,21 +331,23 @@
};
MODULE_DEVICE_TABLE(pci, cy82c693_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver cy82c693_pci_driver = {
.name = "Cypress_IDE",
.id_table = cy82c693_pci_tbl,
.probe = cy82c693_init_one,
.remove = __devexit_p(cy82c693_remove),
+ .suspend = ide_pci_suspend,
+ .resume = ide_pci_resume,
};
static int __init cy82c693_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&cy82c693_pci_driver);
}
static void __exit cy82c693_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&cy82c693_pci_driver);
}
module_init(cy82c693_ide_init);
diff --git a/drivers/ide/pci/delkin_cb.c b/drivers/ide/pci/delkin_cb.c
index f84bfb4..8689a70 100644
--- a/drivers/ide/pci/delkin_cb.c
+++ b/drivers/ide/pci/delkin_cb.c
@@ -19,7 +19,6 @@
#include <linux/types.h>
#include <linux/module.h>
-#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/pci.h>
@@ -118,7 +117,7 @@
};
MODULE_DEVICE_TABLE(pci, delkin_cb_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver delkin_cb_pci_driver = {
.name = "Delkin-ASKA-Workbit Cardbus IDE",
.id_table = delkin_cb_pci_tbl,
.probe = delkin_cb_probe,
@@ -127,12 +126,12 @@
static int __init delkin_cb_init(void)
{
- return pci_register_driver(&driver);
+ return pci_register_driver(&delkin_cb_pci_driver);
}
static void __exit delkin_cb_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&delkin_cb_pci_driver);
}
module_init(delkin_cb_init);
diff --git a/drivers/ide/pci/generic.c b/drivers/ide/pci/generic.c
index b07d4f4..474f96a 100644
--- a/drivers/ide/pci/generic.c
+++ b/drivers/ide/pci/generic.c
@@ -22,7 +22,6 @@
#include <linux/types.h>
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/hdreg.h>
#include <linux/pci.h>
#include <linux/ide.h>
#include <linux/init.h>
@@ -167,21 +166,23 @@
};
MODULE_DEVICE_TABLE(pci, generic_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver generic_pci_driver = {
.name = "PCI_IDE",
.id_table = generic_pci_tbl,
.probe = generic_init_one,
.remove = ide_pci_remove,
+ .suspend = ide_pci_suspend,
+ .resume = ide_pci_resume,
};
static int __init generic_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&generic_pci_driver);
}
static void __exit generic_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&generic_pci_driver);
}
module_init(generic_ide_init);
diff --git a/drivers/ide/pci/hpt34x.c b/drivers/ide/pci/hpt34x.c
index 6009b0b..fb1a3aa 100644
--- a/drivers/ide/pci/hpt34x.c
+++ b/drivers/ide/pci/hpt34x.c
@@ -27,7 +27,6 @@
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
-#include <linux/hdreg.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/init.h>
@@ -79,7 +78,7 @@
*/
#define HPT34X_PCI_INIT_REG 0x80
-static unsigned int __devinit init_chipset_hpt34x(struct pci_dev *dev)
+static unsigned int init_chipset_hpt34x(struct pci_dev *dev)
{
int i = 0;
unsigned long hpt34xIoBase = pci_resource_start(dev, 4);
@@ -167,21 +166,23 @@
};
MODULE_DEVICE_TABLE(pci, hpt34x_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver hpt34x_pci_driver = {
.name = "HPT34x_IDE",
.id_table = hpt34x_pci_tbl,
.probe = hpt34x_init_one,
.remove = ide_pci_remove,
+ .suspend = ide_pci_suspend,
+ .resume = ide_pci_resume,
};
static int __init hpt34x_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&hpt34x_pci_driver);
}
static void __exit hpt34x_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&hpt34x_pci_driver);
}
module_init(hpt34x_ide_init);
diff --git a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c
index c37ab17..9cf171c 100644
--- a/drivers/ide/pci/hpt366.c
+++ b/drivers/ide/pci/hpt366.c
@@ -122,7 +122,6 @@
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/blkdev.h>
-#include <linux/hdreg.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/init.h>
@@ -605,10 +604,10 @@
static int check_in_drive_list(ide_drive_t *drive, const char **list)
{
- struct hd_driveid *id = drive->id;
+ char *m = (char *)&drive->id[ATA_ID_PROD];
while (*list)
- if (!strcmp(*list++,id->model))
+ if (!strcmp(*list++, m))
return 1;
return 0;
}
@@ -655,7 +654,7 @@
case HPT372A:
case HPT372N:
case HPT374 :
- if (ide_dev_is_sata(drive->id))
+ if (ata_id_is_sata(drive->id))
mask &= ~0x0e;
/* Fall thru */
default:
@@ -675,7 +674,7 @@
case HPT372A:
case HPT372N:
case HPT374 :
- if (ide_dev_is_sata(drive->id))
+ if (ata_id_is_sata(drive->id))
return 0x00;
/* Fall thru */
default:
@@ -731,11 +730,11 @@
static void hpt3xx_quirkproc(ide_drive_t *drive)
{
- struct hd_driveid *id = drive->id;
+ char *m = (char *)&drive->id[ATA_ID_PROD];
const char **list = quirk_drives;
while (*list)
- if (strstr(id->model, *list++)) {
+ if (strstr(m, *list++)) {
drive->quirk_list = 1;
return;
}
@@ -836,7 +835,7 @@
if (dma_stat & 0x01)
hpt370_irq_timeout(drive);
}
- return __ide_dma_end(drive);
+ return ide_dma_end(drive);
}
static void hpt370_dma_timeout(ide_drive_t *drive)
@@ -864,9 +863,6 @@
if (dma_stat & 4)
return 1;
- if (!drive->waiting_for_dma)
- printk(KERN_WARNING "%s: (%s) called while not waiting\n",
- drive->name, __func__);
return 0;
}
@@ -881,7 +877,7 @@
pci_read_config_byte(dev, mcr_addr, &mcr);
if (bwsr & mask)
pci_write_config_byte(dev, mcr_addr, mcr | 0x30);
- return __ide_dma_end(drive);
+ return ide_dma_end(drive);
}
/**
@@ -944,7 +940,7 @@
* Perform a calibration cycle on the DPLL.
* Returns 1 if this succeeds
*/
-static int __devinit hpt37x_calibrate_dpll(struct pci_dev *dev, u16 f_low, u16 f_high)
+static int hpt37x_calibrate_dpll(struct pci_dev *dev, u16 f_low, u16 f_high)
{
u32 dpll = (f_high << 16) | f_low | 0x100;
u8 scr2;
@@ -972,7 +968,37 @@
return 1;
}
-static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev)
+static void hpt3xx_disable_fast_irq(struct pci_dev *dev, u8 mcr_addr)
+{
+ struct ide_host *host = pci_get_drvdata(dev);
+ struct hpt_info *info = host->host_priv + (&dev->dev == host->dev[1]);
+ u8 chip_type = info->chip_type;
+ u8 new_mcr, old_mcr = 0;
+
+ /*
+ * Disable the "fast interrupt" prediction. Don't hold off
+ * on interrupts. (== 0x01 despite what the docs say)
+ */
+ pci_read_config_byte(dev, mcr_addr + 1, &old_mcr);
+
+ if (chip_type >= HPT374)
+ new_mcr = old_mcr & ~0x07;
+ else if (chip_type >= HPT370) {
+ new_mcr = old_mcr;
+ new_mcr &= ~0x02;
+#ifdef HPT_DELAY_INTERRUPT
+ new_mcr &= ~0x01;
+#else
+ new_mcr |= 0x01;
+#endif
+ } else /* HPT366 and HPT368 */
+ new_mcr = old_mcr & ~0x80;
+
+ if (new_mcr != old_mcr)
+ pci_write_config_byte(dev, mcr_addr + 1, new_mcr);
+}
+
+static unsigned int init_chipset_hpt366(struct pci_dev *dev)
{
unsigned long io_base = pci_resource_start(dev, 4);
struct hpt_info *info = hpt3xx_get_info(&dev->dev);
@@ -1209,9 +1235,11 @@
* NOTE: This register is only writeable via I/O space.
*/
if (chip_type == HPT371N && clock == ATA_CLOCK_66MHZ)
-
outb(inb(io_base + 0x9c) | 0x04, io_base + 0x9c);
+ hpt3xx_disable_fast_irq(dev, 0x50);
+ hpt3xx_disable_fast_irq(dev, 0x54);
+
return dev->irq;
}
@@ -1265,7 +1293,6 @@
struct hpt_info *info = hpt3xx_get_info(hwif->dev);
int serialize = HPT_SERIALIZE_IO;
u8 chip_type = info->chip_type;
- u8 new_mcr, old_mcr = 0;
/* Cache the channel's MISC. control registers' offset */
hwif->select_data = hwif->channel ? 0x54 : 0x50;
@@ -1288,29 +1315,6 @@
/* Serialize access to this device if needed */
if (serialize && hwif->mate)
hwif->serialized = hwif->mate->serialized = 1;
-
- /*
- * Disable the "fast interrupt" prediction. Don't hold off
- * on interrupts. (== 0x01 despite what the docs say)
- */
- pci_read_config_byte(dev, hwif->select_data + 1, &old_mcr);
-
- if (info->chip_type >= HPT374)
- new_mcr = old_mcr & ~0x07;
- else if (info->chip_type >= HPT370) {
- new_mcr = old_mcr;
- new_mcr &= ~0x02;
-
-#ifdef HPT_DELAY_INTERRUPT
- new_mcr &= ~0x01;
-#else
- new_mcr |= 0x01;
-#endif
- } else /* HPT366 and HPT368 */
- new_mcr = old_mcr & ~0x80;
-
- if (new_mcr != old_mcr)
- pci_write_config_byte(dev, hwif->select_data + 1, new_mcr);
}
static int __devinit init_dma_hpt366(ide_hwif_t *hwif,
@@ -1449,7 +1453,7 @@
.dma_setup = ide_dma_setup,
.dma_exec_cmd = ide_dma_exec_cmd,
.dma_start = ide_dma_start,
- .dma_end = __ide_dma_end,
+ .dma_end = ide_dma_end,
.dma_test_irq = ide_dma_test_irq,
.dma_lost_irq = hpt366_dma_lost_irq,
.dma_timeout = ide_dma_timeout,
@@ -1615,21 +1619,23 @@
};
MODULE_DEVICE_TABLE(pci, hpt366_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver hpt366_pci_driver = {
.name = "HPT366_IDE",
.id_table = hpt366_pci_tbl,
.probe = hpt366_init_one,
.remove = __devexit_p(hpt366_remove),
+ .suspend = ide_pci_suspend,
+ .resume = ide_pci_resume,
};
static int __init hpt366_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&hpt366_pci_driver);
}
static void __exit hpt366_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&hpt366_pci_driver);
}
module_init(hpt366_ide_init);
diff --git a/drivers/ide/pci/it8213.c b/drivers/ide/pci/it8213.c
index 652e47d..7c2feeb 100644
--- a/drivers/ide/pci/it8213.c
+++ b/drivers/ide/pci/it8213.c
@@ -10,7 +10,6 @@
#include <linux/types.h>
#include <linux/module.h>
#include <linux/pci.h>
-#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/init.h>
@@ -190,21 +189,23 @@
MODULE_DEVICE_TABLE(pci, it8213_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver it8213_pci_driver = {
.name = "ITE8213_IDE",
.id_table = it8213_pci_tbl,
.probe = it8213_init_one,
.remove = ide_pci_remove,
+ .suspend = ide_pci_suspend,
+ .resume = ide_pci_resume,
};
static int __init it8213_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&it8213_pci_driver);
}
static void __exit it8213_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&it8213_pci_driver);
}
module_init(it8213_ide_init);
diff --git a/drivers/ide/pci/it821x.c b/drivers/ide/pci/it821x.c
index 4a1508a..995e18b 100644
--- a/drivers/ide/pci/it821x.c
+++ b/drivers/ide/pci/it821x.c
@@ -63,7 +63,6 @@
#include <linux/types.h>
#include <linux/module.h>
#include <linux/pci.h>
-#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/init.h>
@@ -139,8 +138,7 @@
struct pci_dev *dev = to_pci_dev(hwif->dev);
struct it821x_dev *itdev = ide_get_hwifdata(hwif);
int channel = hwif->channel;
- int unit = drive->select.b.unit;
- u8 conf;
+ u8 unit = drive->dn & 1, conf;
/* Program UDMA timing bits */
if(itdev->clock_mode == ATA_66)
@@ -169,13 +167,11 @@
ide_hwif_t *hwif = drive->hwif;
struct pci_dev *dev = to_pci_dev(hwif->dev);
struct it821x_dev *itdev = ide_get_hwifdata(hwif);
+ ide_drive_t *pair;
+ int clock, altclock, sel = 0;
+ u8 unit = drive->dn & 1, v;
- u8 unit = drive->select.b.unit;
- ide_drive_t *pair = &hwif->drives[1-unit];
-
- int clock, altclock;
- u8 v;
- int sel = 0;
+ pair = &hwif->drives[1 - unit];
if(itdev->want[0][0] > itdev->want[1][0]) {
clock = itdev->want[0][1];
@@ -241,16 +237,17 @@
static void it821x_set_pio_mode(ide_drive_t *drive, const u8 pio)
{
- ide_hwif_t *hwif = drive->hwif;
+ ide_hwif_t *hwif = drive->hwif;
struct it821x_dev *itdev = ide_get_hwifdata(hwif);
- int unit = drive->select.b.unit;
- ide_drive_t *pair = &hwif->drives[1 - unit];
- u8 set_pio = pio;
+ ide_drive_t *pair;
+ u8 unit = drive->dn & 1, set_pio = pio;
/* Spec says 89 ref driver uses 88 */
static u16 pio_timings[]= { 0xAA88, 0xA382, 0xA181, 0x3332, 0x3121 };
static u8 pio_want[] = { ATA_66, ATA_66, ATA_66, ATA_66, ATA_ANY };
+ pair = &hwif->drives[1 - unit];
+
/*
* Compute the best PIO mode we can for a given device. We must
* pick a speed that does not cause problems with the other device
@@ -287,9 +284,7 @@
ide_hwif_t *hwif = drive->hwif;
struct pci_dev *dev = to_pci_dev(hwif->dev);
struct it821x_dev *itdev = (void *)ide_get_hwifdata(hwif);
- int unit = drive->select.b.unit;
- int channel = hwif->channel;
- u8 conf;
+ u8 unit = drive->dn & 1, channel = hwif->channel, conf;
static u16 dma[] = { 0x8866, 0x3222, 0x3121 };
static u8 mwdma_want[] = { ATA_ANY, ATA_66, ATA_ANY };
@@ -326,9 +321,7 @@
ide_hwif_t *hwif = drive->hwif;
struct pci_dev *dev = to_pci_dev(hwif->dev);
struct it821x_dev *itdev = ide_get_hwifdata(hwif);
- int unit = drive->select.b.unit;
- int channel = hwif->channel;
- u8 conf;
+ u8 unit = drive->dn & 1, channel = hwif->channel, conf;
static u16 udma[] = { 0x4433, 0x4231, 0x3121, 0x2121, 0x1111, 0x2211, 0x1111 };
static u8 udma_want[] = { ATA_ANY, ATA_50, ATA_ANY, ATA_66, ATA_66, ATA_50, ATA_66 };
@@ -370,7 +363,8 @@
{
ide_hwif_t *hwif = drive->hwif;
struct it821x_dev *itdev = ide_get_hwifdata(hwif);
- int unit = drive->select.b.unit;
+ u8 unit = drive->dn & 1;
+
if(itdev->mwdma[unit] != MWDMA_OFF)
it821x_program(drive, itdev->mwdma[unit]);
else if(itdev->udma[unit] != UDMA_OFF && itdev->timing10)
@@ -390,9 +384,10 @@
static int it821x_dma_end(ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
- int unit = drive->select.b.unit;
struct it821x_dev *itdev = ide_get_hwifdata(hwif);
- int ret = __ide_dma_end(drive);
+ int ret = ide_dma_end(drive);
+ u8 unit = drive->dn & 1;
+
if(itdev->mwdma[unit] != MWDMA_OFF)
it821x_program(drive, itdev->pio[unit]);
return ret;
@@ -446,8 +441,7 @@
static void it821x_quirkproc(ide_drive_t *drive)
{
struct it821x_dev *itdev = ide_get_hwifdata(drive->hwif);
- struct hd_driveid *id = drive->id;
- u16 *idbits = (u16 *)drive->id;
+ u16 *id = drive->id;
if (!itdev->smart) {
/*
@@ -456,7 +450,7 @@
* IRQ mask as we may well be in PIO (eg rev 0x10)
* for now and we know unmasking is safe on this chipset.
*/
- drive->unmask = 1;
+ drive->dev_flags |= IDE_DFLAG_UNMASK;
} else {
/*
* Perform fixups on smart mode. We need to "lose" some
@@ -466,36 +460,36 @@
*/
/* Check for RAID v native */
- if(strstr(id->model, "Integrated Technology Express")) {
+ if (strstr((char *)&id[ATA_ID_PROD],
+ "Integrated Technology Express")) {
/* In raid mode the ident block is slightly buggy
We need to set the bits so that the IDE layer knows
LBA28. LBA48 and DMA ar valid */
- id->capability |= 3; /* LBA28, DMA */
- id->command_set_2 |= 0x0400; /* LBA48 valid */
- id->cfs_enable_2 |= 0x0400; /* LBA48 on */
+ id[ATA_ID_CAPABILITY] |= (3 << 8); /* LBA28, DMA */
+ id[ATA_ID_COMMAND_SET_2] |= 0x0400; /* LBA48 valid */
+ id[ATA_ID_CFS_ENABLE_2] |= 0x0400; /* LBA48 on */
/* Reporting logic */
printk(KERN_INFO "%s: IT8212 %sRAID %d volume",
- drive->name,
- idbits[147] ? "Bootable ":"",
- idbits[129]);
- if(idbits[129] != 1)
- printk("(%dK stripe)", idbits[146]);
- printk(".\n");
+ drive->name, id[147] ? "Bootable " : "",
+ id[ATA_ID_CSFO]);
+ if (id[ATA_ID_CSFO] != 1)
+ printk(KERN_CONT "(%dK stripe)", id[146]);
+ printk(KERN_CONT ".\n");
} else {
/* Non RAID volume. Fixups to stop the core code
doing unsupported things */
- id->field_valid &= 3;
- id->queue_depth = 0;
- id->command_set_1 = 0;
- id->command_set_2 &= 0xC400;
- id->cfsse &= 0xC000;
- id->cfs_enable_1 = 0;
- id->cfs_enable_2 &= 0xC400;
- id->csf_default &= 0xC000;
- id->word127 = 0;
- id->dlf = 0;
- id->csfo = 0;
- id->cfa_power = 0;
+ id[ATA_ID_FIELD_VALID] &= 3;
+ id[ATA_ID_QUEUE_DEPTH] = 0;
+ id[ATA_ID_COMMAND_SET_1] = 0;
+ id[ATA_ID_COMMAND_SET_2] &= 0xC400;
+ id[ATA_ID_CFSSE] &= 0xC000;
+ id[ATA_ID_CFS_ENABLE_1] = 0;
+ id[ATA_ID_CFS_ENABLE_2] &= 0xC400;
+ id[ATA_ID_CSF_DEFAULT] &= 0xC000;
+ id[127] = 0;
+ id[ATA_ID_DLF] = 0;
+ id[ATA_ID_CSFO] = 0;
+ id[ATA_ID_CFA_POWER] = 0;
printk(KERN_INFO "%s: Performing identify fixups.\n",
drive->name);
}
@@ -505,8 +499,8 @@
* IDE core that DMA is supported (it821x hardware
* takes care of DMA mode programming).
*/
- if (id->capability & 1) {
- id->dma_mword |= 0x0101;
+ if (ata_id_has_dma(id)) {
+ id[ATA_ID_MWDMA_MODES] |= 0x0101;
drive->current_speed = XFER_MW_DMA_0;
}
}
@@ -588,7 +582,7 @@
hwif->mwdma_mask = ATA_MWDMA2;
}
-static void __devinit it8212_disable_raid(struct pci_dev *dev)
+static void it8212_disable_raid(struct pci_dev *dev)
{
/* Reset local CPU, and set BIOS not ready */
pci_write_config_byte(dev, 0x5E, 0x01);
@@ -605,7 +599,7 @@
pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x20);
}
-static unsigned int __devinit init_chipset_it821x(struct pci_dev *dev)
+static unsigned int init_chipset_it821x(struct pci_dev *dev)
{
u8 conf;
static char *mode[2] = { "pass through", "smart" };
@@ -682,21 +676,23 @@
MODULE_DEVICE_TABLE(pci, it821x_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver it821x_pci_driver = {
.name = "ITE821x IDE",
.id_table = it821x_pci_tbl,
.probe = it821x_init_one,
.remove = __devexit_p(it821x_remove),
+ .suspend = ide_pci_suspend,
+ .resume = ide_pci_resume,
};
static int __init it821x_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&it821x_pci_driver);
}
static void __exit it821x_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&it821x_pci_driver);
}
module_init(it821x_ide_init);
diff --git a/drivers/ide/pci/jmicron.c b/drivers/ide/pci/jmicron.c
index bb9d09d..9a68433 100644
--- a/drivers/ide/pci/jmicron.c
+++ b/drivers/ide/pci/jmicron.c
@@ -8,7 +8,6 @@
#include <linux/types.h>
#include <linux/module.h>
#include <linux/pci.h>
-#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/init.h>
@@ -150,21 +149,23 @@
MODULE_DEVICE_TABLE(pci, jmicron_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver jmicron_pci_driver = {
.name = "JMicron IDE",
.id_table = jmicron_pci_tbl,
.probe = jmicron_init_one,
.remove = ide_pci_remove,
+ .suspend = ide_pci_suspend,
+ .resume = ide_pci_resume,
};
static int __init jmicron_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&jmicron_pci_driver);
}
static void __exit jmicron_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&jmicron_pci_driver);
}
module_init(jmicron_ide_init);
diff --git a/drivers/ide/pci/ns87415.c b/drivers/ide/pci/ns87415.c
index ffefcd1..1378906 100644
--- a/drivers/ide/pci/ns87415.c
+++ b/drivers/ide/pci/ns87415.c
@@ -11,7 +11,6 @@
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
-#include <linux/hdreg.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/ide.h>
@@ -138,7 +137,7 @@
static unsigned int ns87415_count = 0, ns87415_control[MAX_HWIFS] = { 0 };
/*
- * This routine either enables/disables (according to drive->present)
+ * This routine either enables/disables (according to IDE_DFLAG_PRESENT)
* the IRQ associated with the port (HWIF(drive)),
* and selects either PIO or DMA handshaking for the next I/O operation.
*/
@@ -154,11 +153,15 @@
/* Adjust IRQ enable bit */
bit = 1 << (8 + hwif->channel);
- new = drive->present ? (new & ~bit) : (new | bit);
+
+ if (drive->dev_flags & IDE_DFLAG_PRESENT)
+ new &= ~bit;
+ else
+ new |= bit;
/* Select PIO or DMA, DMA may only be selected for one drive/channel. */
- bit = 1 << (20 + drive->select.b.unit + (hwif->channel << 1));
- other = 1 << (20 + (1 - drive->select.b.unit) + (hwif->channel << 1));
+ bit = 1 << (20 + (drive->dn & 1) + (hwif->channel << 1));
+ other = 1 << (20 + (1 - (drive->dn & 1)) + (hwif->channel << 1));
new = use_dma ? ((new & ~other) | bit) : (new & ~bit);
if (new != *old) {
@@ -188,7 +191,8 @@
static void ns87415_selectproc (ide_drive_t *drive)
{
- ns87415_prepare_drive (drive, drive->using_dma);
+ ns87415_prepare_drive(drive,
+ !!(drive->dev_flags & IDE_DFLAG_USING_DMA));
}
static int ns87415_dma_end(ide_drive_t *drive)
@@ -274,9 +278,9 @@
do {
udelay(50);
stat = hwif->tp_ops->read_status(hwif);
- if (stat == 0xff)
- break;
- } while ((stat & BUSY_STAT) && --timeout);
+ if (stat == 0xff)
+ break;
+ } while ((stat & ATA_BUSY) && --timeout);
#endif
}
@@ -335,21 +339,23 @@
};
MODULE_DEVICE_TABLE(pci, ns87415_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver ns87415_pci_driver = {
.name = "NS87415_IDE",
.id_table = ns87415_pci_tbl,
.probe = ns87415_init_one,
.remove = ide_pci_remove,
+ .suspend = ide_pci_suspend,
+ .resume = ide_pci_resume,
};
static int __init ns87415_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&ns87415_pci_driver);
}
static void __exit ns87415_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&ns87415_pci_driver);
}
module_init(ns87415_ide_init);
diff --git a/drivers/ide/pci/opti621.c b/drivers/ide/pci/opti621.c
index e28e672..6048eda 100644
--- a/drivers/ide/pci/opti621.c
+++ b/drivers/ide/pci/opti621.c
@@ -85,7 +85,6 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
-#include <linux/hdreg.h>
#include <linux/ide.h>
#include <asm/io.h>
@@ -137,7 +136,7 @@
static void opti621_set_pio_mode(ide_drive_t *drive, const u8 pio)
{
ide_hwif_t *hwif = drive->hwif;
- ide_drive_t *pair = ide_get_paired_drive(drive);
+ ide_drive_t *pair = ide_get_pair_dev(drive);
unsigned long flags;
u8 tim, misc, addr_pio = pio, clk;
@@ -153,7 +152,7 @@
drive->drive_data = XFER_PIO_0 + pio;
- if (pair->present) {
+ if (pair) {
if (pair->drive_data && pair->drive_data < drive->drive_data)
addr_pio = pair->drive_data - XFER_PIO_0;
}
@@ -180,7 +179,7 @@
misc = addr_timings[clk][addr_pio];
/* select Index-0/1 for Register-A/B */
- write_reg(drive->select.b.unit, MISC_REG);
+ write_reg(drive->dn & 1, MISC_REG);
/* set read cycle timings */
write_reg(tim, READ_REG);
/* set write cycle timings */
@@ -221,21 +220,23 @@
};
MODULE_DEVICE_TABLE(pci, opti621_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver opti621_pci_driver = {
.name = "Opti621_IDE",
.id_table = opti621_pci_tbl,
.probe = opti621_init_one,
.remove = ide_pci_remove,
+ .suspend = ide_pci_suspend,
+ .resume = ide_pci_resume,
};
static int __init opti621_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&opti621_pci_driver);
}
static void __exit opti621_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&opti621_pci_driver);
}
module_init(opti621_ide_init);
diff --git a/drivers/ide/pci/pdc202xx_new.c b/drivers/ide/pci/pdc202xx_new.c
index d477da6..211ae46 100644
--- a/drivers/ide/pci/pdc202xx_new.c
+++ b/drivers/ide/pci/pdc202xx_new.c
@@ -19,7 +19,6 @@
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
-#include <linux/hdreg.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/ide.h>
@@ -203,10 +202,10 @@
static void pdcnew_quirkproc(ide_drive_t *drive)
{
- const char **list, *model = drive->id->model;
+ const char **list, *m = (char *)&drive->id[ATA_ID_PROD];
for (list = pdc_quirk_drives; *list != NULL; list++)
- if (strstr(model, *list) != NULL) {
+ if (strstr(m, *list) != NULL) {
drive->quirk_list = 2;
return;
}
@@ -227,7 +226,7 @@
* read_counter - Read the byte count registers
* @dma_base: for the port address
*/
-static long __devinit read_counter(u32 dma_base)
+static long read_counter(u32 dma_base)
{
u32 pri_dma_base = dma_base, sec_dma_base = dma_base + 0x08;
u8 cnt0, cnt1, cnt2, cnt3;
@@ -267,7 +266,7 @@
* @dma_base: for the port address
* E.g. 16949000 on 33 MHz PCI bus, i.e. half of the PCI clock.
*/
-static long __devinit detect_pll_input_clock(unsigned long dma_base)
+static long detect_pll_input_clock(unsigned long dma_base)
{
struct timeval start_time, end_time;
long start_count, end_count;
@@ -310,7 +309,7 @@
}
#ifdef CONFIG_PPC_PMAC
-static void __devinit apple_kiwi_init(struct pci_dev *pdev)
+static void apple_kiwi_init(struct pci_dev *pdev)
{
struct device_node *np = pci_device_to_OF_node(pdev);
u8 conf;
@@ -326,7 +325,7 @@
}
#endif /* CONFIG_PPC_PMAC */
-static unsigned int __devinit init_chipset_pdcnew(struct pci_dev *dev)
+static unsigned int init_chipset_pdcnew(struct pci_dev *dev)
{
const char *name = DRV_NAME;
unsigned long dma_base = pci_resource_start(dev, 4);
@@ -562,21 +561,23 @@
};
MODULE_DEVICE_TABLE(pci, pdc202new_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver pdc202new_pci_driver = {
.name = "Promise_IDE",
.id_table = pdc202new_pci_tbl,
.probe = pdc202new_init_one,
.remove = __devexit_p(pdc202new_remove),
+ .suspend = ide_pci_suspend,
+ .resume = ide_pci_resume,
};
static int __init pdc202new_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&pdc202new_pci_driver);
}
static void __exit pdc202new_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&pdc202new_pci_driver);
}
module_init(pdc202new_ide_init);
diff --git a/drivers/ide/pci/pdc202xx_old.c b/drivers/ide/pci/pdc202xx_old.c
index de9a274..799557c 100644
--- a/drivers/ide/pci/pdc202xx_old.c
+++ b/drivers/ide/pci/pdc202xx_old.c
@@ -13,7 +13,6 @@
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/blkdev.h>
-#include <linux/hdreg.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/ide.h>
@@ -86,7 +85,7 @@
* Prefetch_EN / IORDY_EN / PA[3:0] bits of register A
*/
AP &= ~0x3f;
- if (drive->id->capability & 4)
+ if (ata_id_iordy_disable(drive->id))
AP |= 0x20; /* set IORDY_EN bit */
if (drive->media == ide_disk)
AP |= 0x10; /* set Prefetch_EN bit */
@@ -154,10 +153,10 @@
static void pdc202xx_quirkproc(ide_drive_t *drive)
{
- const char **list, *model = drive->id->model;
+ const char **list, *m = (char *)&drive->id[ATA_ID_PROD];
for (list = pdc_quirk_drives; *list != NULL; list++)
- if (strstr(model, *list) != NULL) {
+ if (strstr(m, *list) != NULL) {
drive->quirk_list = 2;
return;
}
@@ -169,7 +168,7 @@
{
if (drive->current_speed > XFER_UDMA_2)
pdc_old_enable_66MHz_clock(drive->hwif);
- if (drive->media != ide_disk || drive->addressing == 1) {
+ if (drive->media != ide_disk || (drive->dev_flags & IDE_DFLAG_LBA48)) {
struct request *rq = HWGROUP(drive)->rq;
ide_hwif_t *hwif = HWIF(drive);
unsigned long high_16 = hwif->extra_base - 16;
@@ -189,7 +188,7 @@
static int pdc202xx_dma_end(ide_drive_t *drive)
{
- if (drive->media != ide_disk || drive->addressing == 1) {
+ if (drive->media != ide_disk || (drive->dev_flags & IDE_DFLAG_LBA48)) {
ide_hwif_t *hwif = HWIF(drive);
unsigned long high_16 = hwif->extra_base - 16;
unsigned long atapi_reg = high_16 + (hwif->channel ? 0x24 : 0x20);
@@ -201,7 +200,7 @@
}
if (drive->current_speed > XFER_UDMA_2)
pdc_old_disable_66MHz_clock(drive->hwif);
- return __ide_dma_end(drive);
+ return ide_dma_end(drive);
}
static int pdc202xx_dma_test_irq(ide_drive_t *drive)
@@ -265,7 +264,7 @@
ide_dma_timeout(drive);
}
-static unsigned int __devinit init_chipset_pdc202xx(struct pci_dev *dev)
+static unsigned int init_chipset_pdc202xx(struct pci_dev *dev)
{
unsigned long dmabase = pci_resource_start(dev, 4);
u8 udma_speed_flag = 0, primary_mode = 0, secondary_mode = 0;
@@ -334,7 +333,7 @@
.dma_setup = ide_dma_setup,
.dma_exec_cmd = ide_dma_exec_cmd,
.dma_start = ide_dma_start,
- .dma_end = __ide_dma_end,
+ .dma_end = ide_dma_end,
.dma_test_irq = pdc202xx_dma_test_irq,
.dma_lost_irq = pdc202xx_dma_lost_irq,
.dma_timeout = pdc202xx_dma_timeout,
@@ -427,21 +426,23 @@
};
MODULE_DEVICE_TABLE(pci, pdc202xx_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver pdc202xx_pci_driver = {
.name = "Promise_Old_IDE",
.id_table = pdc202xx_pci_tbl,
.probe = pdc202xx_init_one,
.remove = ide_pci_remove,
+ .suspend = ide_pci_suspend,
+ .resume = ide_pci_resume,
};
static int __init pdc202xx_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&pdc202xx_pci_driver);
}
static void __exit pdc202xx_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&pdc202xx_pci_driver);
}
module_init(pdc202xx_ide_init);
diff --git a/drivers/ide/pci/piix.c b/drivers/ide/pci/piix.c
index 30cfc81..d63f9fd 100644
--- a/drivers/ide/pci/piix.c
+++ b/drivers/ide/pci/piix.c
@@ -48,7 +48,6 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
-#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/init.h>
@@ -205,7 +204,7 @@
* out to be nice and simple.
*/
-static unsigned int __devinit init_chipset_ich(struct pci_dev *dev)
+static unsigned int init_chipset_ich(struct pci_dev *dev)
{
u32 extra = 0;
@@ -216,17 +215,26 @@
}
/**
- * piix_dma_clear_irq - clear BMDMA status
- * @drive: IDE drive to clear
+ * ich_clear_irq - clear BMDMA status
+ * @drive: IDE drive
*
- * Called from ide_intr() for PIO interrupts
- * to clear BMDMA status as needed by ICHx
+ * ICHx contollers set DMA INTR no matter DMA or PIO.
+ * BMDMA status might need to be cleared even for
+ * PIO interrupts to prevent spurious/lost IRQ.
*/
-static void piix_dma_clear_irq(ide_drive_t *drive)
+static void ich_clear_irq(ide_drive_t *drive)
{
ide_hwif_t *hwif = HWIF(drive);
u8 dma_stat;
+ /*
+ * ide_dma_end() needs BMDMA status for error checking.
+ * So, skip clearing BMDMA status here and leave it
+ * to ide_dma_end() if this is DMA interrupt.
+ */
+ if (drive->waiting_for_dma || hwif->dma_base == 0)
+ return;
+
/* clear the INTR & ERROR bits */
dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS);
/* Should we force the bit as well ? */
@@ -250,6 +258,7 @@
{ 0x27DF, 0x1025, 0x0110 }, /* ICH7 on Acer 3682WLMi */
{ 0x27DF, 0x1043, 0x1267 }, /* ICH7 on Asus W5F */
{ 0x27DF, 0x103C, 0x30A1 }, /* ICH7 on HP Compaq nc2400 */
+ { 0x27DF, 0x1071, 0xD221 }, /* ICH7 on Hercules EC-900 */
{ 0x24CA, 0x1025, 0x0061 }, /* ICH4 on Acer Aspire 2023WLMi */
{ 0x2653, 0x1043, 0x82D8 }, /* ICH6M on Asus Eee 701 */
/* end marker */
@@ -294,21 +303,19 @@
hwif->ultra_mask = hwif->mwdma_mask = hwif->swdma_mask = 0;
}
-static void __devinit init_hwif_ich(ide_hwif_t *hwif)
-{
- init_hwif_piix(hwif);
-
- /* ICHx need to clear the BMDMA status for all interrupts */
- if (hwif->dma_base)
- hwif->ide_dma_clear_irq = &piix_dma_clear_irq;
-}
-
static const struct ide_port_ops piix_port_ops = {
.set_pio_mode = piix_set_pio_mode,
.set_dma_mode = piix_set_dma_mode,
.cable_detect = piix_cable_detect,
};
+static const struct ide_port_ops ich_port_ops = {
+ .set_pio_mode = piix_set_pio_mode,
+ .set_dma_mode = piix_set_dma_mode,
+ .clear_irq = ich_clear_irq,
+ .cable_detect = piix_cable_detect,
+};
+
#ifndef CONFIG_IA64
#define IDE_HFLAGS_PIIX IDE_HFLAG_LEGACY_IRQS
#else
@@ -332,9 +339,9 @@
{ \
.name = DRV_NAME, \
.init_chipset = init_chipset_ich, \
- .init_hwif = init_hwif_ich, \
+ .init_hwif = init_hwif_piix, \
.enablebits = {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, \
- .port_ops = &piix_port_ops, \
+ .port_ops = &ich_port_ops, \
.host_flags = IDE_HFLAGS_PIIX, \
.pio_mask = ATA_PIO4, \
.swdma_mask = ATA_SWDMA2_ONLY, \
@@ -445,22 +452,24 @@
};
MODULE_DEVICE_TABLE(pci, piix_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver piix_pci_driver = {
.name = "PIIX_IDE",
.id_table = piix_pci_tbl,
.probe = piix_init_one,
.remove = ide_pci_remove,
+ .suspend = ide_pci_suspend,
+ .resume = ide_pci_resume,
};
static int __init piix_ide_init(void)
{
piix_check_450nx();
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&piix_pci_driver);
}
static void __exit piix_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&piix_pci_driver);
}
module_init(piix_ide_init);
diff --git a/drivers/ide/pci/rz1000.c b/drivers/ide/pci/rz1000.c
index 8d11ee8..7daf013 100644
--- a/drivers/ide/pci/rz1000.c
+++ b/drivers/ide/pci/rz1000.c
@@ -16,7 +16,6 @@
#include <linux/types.h>
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/hdreg.h>
#include <linux/pci.h>
#include <linux/ide.h>
#include <linux/init.h>
@@ -60,7 +59,7 @@
};
MODULE_DEVICE_TABLE(pci, rz1000_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver rz1000_pci_driver = {
.name = "RZ1000_IDE",
.id_table = rz1000_pci_tbl,
.probe = rz1000_init_one,
@@ -69,12 +68,12 @@
static int __init rz1000_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&rz1000_pci_driver);
}
static void __exit rz1000_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&rz1000_pci_driver);
}
module_init(rz1000_ide_init);
diff --git a/drivers/ide/pci/sc1200.c b/drivers/ide/pci/sc1200.c
index 8efaed1..f1a8758 100644
--- a/drivers/ide/pci/sc1200.c
+++ b/drivers/ide/pci/sc1200.c
@@ -14,7 +14,6 @@
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
-#include <linux/hdreg.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/ide.h>
@@ -104,17 +103,19 @@
static u8 sc1200_udma_filter(ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
- ide_drive_t *mate = &hwif->drives[(drive->dn & 1) ^ 1];
- struct hd_driveid *mateid = mate->id;
+ ide_drive_t *mate = ide_get_pair_dev(drive);
+ u16 *mateid = mate->id;
u8 mask = hwif->ultra_mask;
- if (mate->present == 0)
+ if (mate == NULL)
goto out;
- if ((mateid->capability & 1) && __ide_dma_bad_drive(mate) == 0) {
- if ((mateid->field_valid & 4) && (mateid->dma_ultra & 7))
+ if (ata_id_has_dma(mateid) && __ide_dma_bad_drive(mate) == 0) {
+ if ((mateid[ATA_ID_FIELD_VALID] & 4) &&
+ (mateid[ATA_ID_UDMA_MODES] & 7))
goto out;
- if ((mateid->field_valid & 2) && (mateid->dma_mword & 7))
+ if ((mateid[ATA_ID_FIELD_VALID] & 2) &&
+ (mateid[ATA_ID_MWDMA_MODES] & 7))
mask = 0;
}
out:
@@ -125,7 +126,6 @@
{
ide_hwif_t *hwif = HWIF(drive);
struct pci_dev *dev = to_pci_dev(hwif->dev);
- int unit = drive->select.b.unit;
unsigned int reg, timings;
unsigned short pci_clock;
unsigned int basereg = hwif->channel ? 0x50 : 0x40;
@@ -154,7 +154,7 @@
else
timings = mwdma_timing[pci_clock][mode - XFER_MW_DMA_0];
- if (unit == 0) { /* are we configuring drive0? */
+ if ((drive->dn & 1) == 0) {
pci_read_config_dword(dev, basereg + 4, ®);
timings |= reg & 0x80000000; /* preserve PIO format bit */
pci_write_config_dword(dev, basereg + 4, timings);
@@ -215,7 +215,8 @@
if (mode != -1) {
printk("SC1200: %s: changing (U)DMA mode\n", drive->name);
ide_dma_off_quietly(drive);
- if (ide_set_dma_mode(drive, mode) == 0 && drive->using_dma)
+ if (ide_set_dma_mode(drive, mode) == 0 &&
+ (drive->dev_flags & IDE_DFLAG_USING_DMA))
hwif->dma_ops->dma_host_set(drive, 1);
return;
}
@@ -327,7 +328,7 @@
};
MODULE_DEVICE_TABLE(pci, sc1200_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver sc1200_pci_driver = {
.name = "SC1200_IDE",
.id_table = sc1200_pci_tbl,
.probe = sc1200_init_one,
@@ -340,12 +341,12 @@
static int __init sc1200_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&sc1200_pci_driver);
}
static void __exit sc1200_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&sc1200_pci_driver);
}
module_init(sc1200_ide_init);
diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c
index 44cccd1..9ce1d80 100644
--- a/drivers/ide/pci/scc_pata.c
+++ b/drivers/ide/pci/scc_pata.c
@@ -26,7 +26,6 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/delay.h>
-#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/init.h>
@@ -292,7 +291,7 @@
static void scc_dma_host_set(ide_drive_t *drive, int on)
{
ide_hwif_t *hwif = drive->hwif;
- u8 unit = (drive->select.b.unit & 0x01);
+ u8 unit = drive->dn & 1;
u8 dma_stat = scc_ide_inb(hwif->dma_base + 4);
if (on)
@@ -354,7 +353,6 @@
/* start DMA */
scc_ide_outb(dma_cmd | 1, hwif->dma_base);
- hwif->dma = 1;
wmb();
}
@@ -375,7 +373,6 @@
/* purge DMA mappings */
ide_destroy_dmatable(drive);
/* verify good DMA status */
- hwif->dma = 0;
wmb();
return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0;
}
@@ -400,7 +397,7 @@
/* errata A308 workaround: Step5 (check data loss) */
/* We don't check non ide_disk because it is limited to UDMA4 */
if (!(in_be32((void __iomem *)hwif->io_ports.ctl_addr)
- & ERR_STAT) &&
+ & ATA_ERR) &&
drive->media == ide_disk && drive->current_speed > XFER_UDMA_4) {
reg = in_be32((void __iomem *)intsts_port);
if (!(reg & INTSTS_ACTEINT)) {
@@ -504,7 +501,7 @@
/* SCC errata A252,A308 workaround: Step4 */
if ((in_be32((void __iomem *)hwif->io_ports.ctl_addr)
- & ERR_STAT) &&
+ & ATA_ERR) &&
(int_stat & INTSTS_INTRQ))
return 1;
@@ -512,9 +509,6 @@
if (int_stat & INTSTS_IOIRQS)
return 1;
- if (!drive->waiting_for_dma)
- printk(KERN_WARNING "%s: (%s) called while not waiting\n",
- drive->name, __func__);
return 0;
}
@@ -711,7 +705,7 @@
scc_ide_outb(tf->lbah, io_ports->lbah_addr);
if (task->tf_flags & IDE_TFLAG_OUT_DEVICE)
- scc_ide_outb((tf->device & HIHI) | drive->select.all,
+ scc_ide_outb((tf->device & HIHI) | drive->select,
io_ports->device_addr);
}
@@ -827,6 +821,12 @@
init_mmio_iops_scc(hwif);
}
+static int __devinit scc_init_dma(ide_hwif_t *hwif,
+ const struct ide_port_info *d)
+{
+ return ide_allocate_dma_engine(hwif);
+}
+
static u8 scc_cable_detect(ide_hwif_t *hwif)
{
return ATA_CBL_PATA80;
@@ -891,6 +891,7 @@
{ \
.name = name_str, \
.init_iops = init_iops_scc, \
+ .init_dma = scc_init_dma, \
.init_hwif = init_hwif_scc, \
.tp_ops = &scc_tp_ops, \
.port_ops = &scc_port_ops, \
@@ -928,13 +929,6 @@
{
struct scc_ports *ports = pci_get_drvdata(dev);
struct ide_host *host = ports->host;
- ide_hwif_t *hwif = host->ports[0];
-
- if (hwif->dmatable_cpu) {
- pci_free_consistent(dev, PRD_ENTRIES * PRD_BYTES,
- hwif->dmatable_cpu, hwif->dmatable_dma);
- hwif->dmatable_cpu = NULL;
- }
ide_host_remove(host);
@@ -950,7 +944,7 @@
};
MODULE_DEVICE_TABLE(pci, scc_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver scc_pci_driver = {
.name = "SCC IDE",
.id_table = scc_pci_tbl,
.probe = scc_init_one,
@@ -959,14 +953,14 @@
static int scc_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&scc_pci_driver);
}
module_init(scc_ide_init);
/* -- No exit code?
static void scc_ide_exit(void)
{
- ide_pci_unregister_driver(&driver);
+ ide_pci_unregister_driver(&scc_pci_driver);
}
module_exit(scc_ide_exit);
*/
diff --git a/drivers/ide/pci/serverworks.c b/drivers/ide/pci/serverworks.c
index c3bdc6e..437bc91 100644
--- a/drivers/ide/pci/serverworks.c
+++ b/drivers/ide/pci/serverworks.c
@@ -32,7 +32,6 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
-#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/init.h>
@@ -57,8 +56,10 @@
static int check_in_drive_lists (ide_drive_t *drive, const char **list)
{
+ char *m = (char *)&drive->id[ATA_ID_PROD];
+
while (*list)
- if (!strcmp(*list++, drive->id->model))
+ if (!strcmp(*list++, m))
return 1;
return 0;
}
@@ -152,7 +153,7 @@
ide_hwif_t *hwif = HWIF(drive);
struct pci_dev *dev = to_pci_dev(hwif->dev);
- u8 unit = (drive->select.b.unit & 0x01);
+ u8 unit = drive->dn & 1;
u8 ultra_enable = 0, ultra_timing = 0, dma_timing = 0;
@@ -174,7 +175,7 @@
pci_write_config_byte(dev, 0x54, ultra_enable);
}
-static unsigned int __devinit init_chipset_svwks(struct pci_dev *dev)
+static unsigned int init_chipset_svwks(struct pci_dev *dev)
{
unsigned int reg;
u8 btr;
@@ -442,21 +443,23 @@
};
MODULE_DEVICE_TABLE(pci, svwks_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver svwks_pci_driver = {
.name = "Serverworks_IDE",
.id_table = svwks_pci_tbl,
.probe = svwks_init_one,
.remove = ide_pci_remove,
+ .suspend = ide_pci_suspend,
+ .resume = ide_pci_resume,
};
static int __init svwks_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&svwks_pci_driver);
}
static void __exit svwks_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&svwks_pci_driver);
}
module_init(svwks_ide_init);
diff --git a/drivers/ide/pci/sgiioc4.c b/drivers/ide/pci/sgiioc4.c
index 681306c..dd63454 100644
--- a/drivers/ide/pci/sgiioc4.c
+++ b/drivers/ide/pci/sgiioc4.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2003-2006 Silicon Graphics, Inc. All Rights Reserved.
+ * Copyright (C) 2008 MontaVista Software, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
@@ -22,7 +23,6 @@
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/delay.h>
-#include <linux/hdreg.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
@@ -151,7 +151,7 @@
int count = 0;
stat = sgiioc4_read_status(hwif);
- while ((stat & 0x80) && (count++ < 100)) {
+ while ((stat & ATA_BUSY) && (count++ < 100)) {
udelay(1);
stat = sgiioc4_read_status(hwif);
}
@@ -311,7 +311,7 @@
u8 reg = (u8) readb((void __iomem *) port);
if ((port & 0xFFF) == 0x11C) { /* Status register of IOC4 */
- if (reg & 0x51) { /* Not busy...check for interrupt */
+ if (!(reg & ATA_BUSY)) { /* Not busy... check for interrupt */
unsigned long other_ir = port - 0x110;
unsigned int intr_reg = (u32) readl((void __iomem *) other_ir);
@@ -339,36 +339,32 @@
if (dma_base == 0)
return -1;
- printk(KERN_INFO "%s: BM-DMA at 0x%04lx-0x%04lx\n", hwif->name,
- dma_base, dma_base + num_ports - 1);
+ printk(KERN_INFO " %s: MMIO-DMA\n", hwif->name);
- if (!request_mem_region(dma_base, num_ports, hwif->name)) {
- printk(KERN_ERR
- "%s(%s) -- ERROR, Addresses 0x%p to 0x%p "
- "ALREADY in use\n",
- __func__, hwif->name, (void *) dma_base,
- (void *) dma_base + num_ports - 1);
+ if (request_mem_region(dma_base, num_ports, hwif->name) == NULL) {
+ printk(KERN_ERR "%s(%s) -- ERROR: addresses 0x%08lx to 0x%08lx "
+ "already in use\n", __func__, hwif->name,
+ dma_base, dma_base + num_ports - 1);
return -1;
}
virt_dma_base = ioremap(dma_base, num_ports);
if (virt_dma_base == NULL) {
- printk(KERN_ERR
- "%s(%s) -- ERROR, Unable to map addresses 0x%lx to 0x%lx\n",
- __func__, hwif->name, dma_base, dma_base + num_ports - 1);
+ printk(KERN_ERR "%s(%s) -- ERROR: unable to map addresses "
+ "0x%lx to 0x%lx\n", __func__, hwif->name,
+ dma_base, dma_base + num_ports - 1);
goto dma_remap_failure;
}
hwif->dma_base = (unsigned long) virt_dma_base;
- hwif->dmatable_cpu = pci_alloc_consistent(dev,
- IOC4_PRD_ENTRIES * IOC4_PRD_BYTES,
- &hwif->dmatable_dma);
-
- if (!hwif->dmatable_cpu)
- goto dma_pci_alloc_failure;
-
hwif->sg_max_nents = IOC4_PRD_ENTRIES;
+ hwif->prd_max_nents = IOC4_PRD_ENTRIES;
+ hwif->prd_ent_size = IOC4_PRD_BYTES;
+
+ if (ide_allocate_dma_engine(hwif))
+ goto dma_pci_alloc_failure;
+
pad = pci_alloc_consistent(dev, IOC4_IDE_CACHELINE_SIZE,
(dma_addr_t *)&hwif->extra_base);
if (pad) {
@@ -376,13 +372,11 @@
return 0;
}
- pci_free_consistent(dev, IOC4_PRD_ENTRIES * IOC4_PRD_BYTES,
- hwif->dmatable_cpu, hwif->dmatable_dma);
- printk(KERN_INFO
- "%s() -- Error! Unable to allocate DMA Maps for drive %s\n",
+ ide_release_dma_engine(hwif);
+
+ printk(KERN_ERR "%s(%s) -- ERROR: Unable to allocate DMA maps\n",
__func__, hwif->name);
- printk(KERN_INFO
- "Changing from DMA to PIO mode for Drive %s\n", hwif->name);
+ printk(KERN_INFO "%s: changing from DMA to PIO mode", hwif->name);
dma_pci_alloc_failure:
iounmap(virt_dma_base);
@@ -618,14 +612,12 @@
irqport = (unsigned long) virt_base + IOC4_INTR_OFFSET;
cmd_phys_base = bar0 + IOC4_CMD_OFFSET;
- if (!request_mem_region(cmd_phys_base, IOC4_CMD_CTL_BLK_SIZE,
- DRV_NAME)) {
- printk(KERN_ERR
- "%s %s: -- ERROR, Addresses "
- "0x%p to 0x%p ALREADY in use\n",
- DRV_NAME, pci_name(dev), (void *)cmd_phys_base,
- (void *) cmd_phys_base + IOC4_CMD_CTL_BLK_SIZE);
- return -ENOMEM;
+ if (request_mem_region(cmd_phys_base, IOC4_CMD_CTL_BLK_SIZE,
+ DRV_NAME) == NULL) {
+ printk(KERN_ERR "%s %s -- ERROR: addresses 0x%08lx to 0x%08lx "
+ "already in use\n", DRV_NAME, pci_name(dev),
+ cmd_phys_base, cmd_phys_base + IOC4_CMD_CTL_BLK_SIZE);
+ return -EBUSY;
}
/* Initialize the IO registers */
diff --git a/drivers/ide/pci/siimage.c b/drivers/ide/pci/siimage.c
index db2b88a..eb4faf9 100644
--- a/drivers/ide/pci/siimage.c
+++ b/drivers/ide/pci/siimage.c
@@ -39,7 +39,6 @@
#include <linux/types.h>
#include <linux/module.h>
#include <linux/pci.h>
-#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/io.h>
@@ -117,13 +116,14 @@
{
ide_hwif_t *hwif = HWIF(drive);
unsigned long base = (unsigned long)hwif->hwif_data;
+ u8 unit = drive->dn & 1;
base += 0xA0 + r;
if (hwif->host_flags & IDE_HFLAG_MMIO)
base += hwif->channel << 6;
else
base += hwif->channel << 4;
- base |= drive->select.b.unit << drive->select.b.unit;
+ base |= unit << unit;
return base;
}
@@ -223,7 +223,9 @@
static u8 sil_sata_udma_filter(ide_drive_t *drive)
{
- return strstr(drive->id->model, "Maxtor") ? ATA_UDMA5 : ATA_UDMA6;
+ char *m = (char *)&drive->id[ATA_ID_PROD];
+
+ return strstr(m, "Maxtor") ? ATA_UDMA5 : ATA_UDMA6;
}
/**
@@ -243,7 +245,7 @@
ide_hwif_t *hwif = HWIF(drive);
struct pci_dev *dev = to_pci_dev(hwif->dev);
- ide_drive_t *pair = ide_get_paired_drive(drive);
+ ide_drive_t *pair = ide_get_pair_dev(drive);
u32 speedt = 0;
u16 speedp = 0;
unsigned long addr = siimage_seldev(drive, 0x04);
@@ -254,10 +256,10 @@
u8 addr_mask = hwif->channel ? (mmio ? 0xF4 : 0x84)
: (mmio ? 0xB4 : 0x80);
u8 mode = 0;
- u8 unit = drive->select.b.unit;
+ u8 unit = drive->dn & 1;
/* trim *taskfile* PIO to the slowest of the master/slave */
- if (pair->present) {
+ if (pair) {
u8 pair_pio = ide_get_best_pio_mode(pair, 255, 4);
if (pair_pio < tf_pio)
@@ -300,9 +302,9 @@
ide_hwif_t *hwif = HWIF(drive);
struct pci_dev *dev = to_pci_dev(hwif->dev);
- u16 ultra = 0, multi = 0;
- u8 mode = 0, unit = drive->select.b.unit;
unsigned long base = (unsigned long)hwif->hwif_data;
+ u16 ultra = 0, multi = 0;
+ u8 mode = 0, unit = drive->dn & 1;
u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0;
u8 scsc = 0, addr_mask = hwif->channel ? (mmio ? 0xF4 : 0x84)
: (mmio ? 0xB4 : 0x80);
@@ -462,7 +464,7 @@
* to 133 MHz clocking if the system isn't already set up to do it.
*/
-static unsigned int __devinit init_chipset_siimage(struct pci_dev *dev)
+static unsigned int init_chipset_siimage(struct pci_dev *dev)
{
struct ide_host *host = pci_get_drvdata(dev);
void __iomem *ioaddr = host->host_priv;
@@ -616,8 +618,8 @@
static int is_dev_seagate_sata(ide_drive_t *drive)
{
- const char *s = &drive->id->model[0];
- unsigned len = strnlen(s, sizeof(drive->id->model));
+ const char *s = (const char *)&drive->id[ATA_ID_PROD];
+ unsigned len = strnlen(s, ATA_ID_PROD_LEN);
if ((len > 4) && (!memcmp(s, "ST", 2)))
if ((!memcmp(s + len - 2, "AS", 2)) ||
@@ -711,7 +713,7 @@
.dma_setup = ide_dma_setup,
.dma_exec_cmd = ide_dma_exec_cmd,
.dma_start = ide_dma_start,
- .dma_end = __ide_dma_end,
+ .dma_end = ide_dma_end,
.dma_test_irq = siimage_dma_test_irq,
.dma_timeout = ide_dma_timeout,
.dma_lost_irq = ide_dma_lost_irq,
@@ -828,21 +830,23 @@
};
MODULE_DEVICE_TABLE(pci, siimage_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver siimage_pci_driver = {
.name = "SiI_IDE",
.id_table = siimage_pci_tbl,
.probe = siimage_init_one,
.remove = __devexit_p(siimage_remove),
+ .suspend = ide_pci_suspend,
+ .resume = ide_pci_resume,
};
static int __init siimage_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&siimage_pci_driver);
}
static void __exit siimage_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&siimage_pci_driver);
}
module_init(siimage_ide_init);
diff --git a/drivers/ide/pci/sis5513.c b/drivers/ide/pci/sis5513.c
index 5efe21d..ad32e18 100644
--- a/drivers/ide/pci/sis5513.c
+++ b/drivers/ide/pci/sis5513.c
@@ -47,7 +47,6 @@
#include <linux/types.h>
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/hdreg.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/ide.h>
@@ -448,7 +447,7 @@
return chipset_family;
}
-static unsigned int __devinit init_chipset_sis5513(struct pci_dev *dev)
+static unsigned int init_chipset_sis5513(struct pci_dev *dev)
{
/* Make general config ops here
1/ tell IDE channels to operate in Compatibility mode only
@@ -606,21 +605,23 @@
};
MODULE_DEVICE_TABLE(pci, sis5513_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver sis5513_pci_driver = {
.name = "SIS_IDE",
.id_table = sis5513_pci_tbl,
.probe = sis5513_init_one,
.remove = __devexit_p(sis5513_remove),
+ .suspend = ide_pci_suspend,
+ .resume = ide_pci_resume,
};
static int __init sis5513_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&sis5513_pci_driver);
}
static void __exit sis5513_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&sis5513_pci_driver);
}
module_init(sis5513_ide_init);
diff --git a/drivers/ide/pci/sl82c105.c b/drivers/ide/pci/sl82c105.c
index 73905bc..84dc336 100644
--- a/drivers/ide/pci/sl82c105.c
+++ b/drivers/ide/pci/sl82c105.c
@@ -17,7 +17,6 @@
#include <linux/types.h>
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/hdreg.h>
#include <linux/pci.h>
#include <linux/ide.h>
@@ -62,7 +61,7 @@
if (cmd_off == 0)
cmd_off = 1;
- if (pio > 2 || ide_dev_has_iordy(drive->id))
+ if (pio > 2 || ata_id_has_iordy(drive->id))
iordy = 0x40;
return (cmd_on - 1) << 8 | (cmd_off - 1) | iordy;
@@ -208,7 +207,7 @@
DBG(("%s(drive:%s)\n", __func__, drive->name));
- ret = __ide_dma_end(drive);
+ ret = ide_dma_end(drive);
pci_write_config_word(dev, reg, drive->drive_data);
@@ -272,7 +271,7 @@
* channel 0 here at least, but channel 1 has to be enabled by
* firmware or arch code. We still set both to 16 bits mode.
*/
-static unsigned int __devinit init_chipset_sl82c105(struct pci_dev *dev)
+static unsigned int init_chipset_sl82c105(struct pci_dev *dev)
{
u32 val;
@@ -346,21 +345,23 @@
};
MODULE_DEVICE_TABLE(pci, sl82c105_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver sl82c105_pci_driver = {
.name = "W82C105_IDE",
.id_table = sl82c105_pci_tbl,
.probe = sl82c105_init_one,
.remove = ide_pci_remove,
+ .suspend = ide_pci_suspend,
+ .resume = ide_pci_resume,
};
static int __init sl82c105_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&sl82c105_pci_driver);
}
static void __exit sl82c105_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&sl82c105_pci_driver);
}
module_init(sl82c105_ide_init);
diff --git a/drivers/ide/pci/slc90e66.c b/drivers/ide/pci/slc90e66.c
index 866d6c6..0f759e4 100644
--- a/drivers/ide/pci/slc90e66.c
+++ b/drivers/ide/pci/slc90e66.c
@@ -11,7 +11,6 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
-#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/init.h>
@@ -155,21 +154,23 @@
};
MODULE_DEVICE_TABLE(pci, slc90e66_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver slc90e66_pci_driver = {
.name = "SLC90e66_IDE",
.id_table = slc90e66_pci_tbl,
.probe = slc90e66_init_one,
.remove = ide_pci_remove,
+ .suspend = ide_pci_suspend,
+ .resume = ide_pci_resume,
};
static int __init slc90e66_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&slc90e66_pci_driver);
}
static void __exit slc90e66_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&slc90e66_pci_driver);
}
module_init(slc90e66_ide_init);
diff --git a/drivers/ide/pci/tc86c001.c b/drivers/ide/pci/tc86c001.c
index 927277c..93e2cce 100644
--- a/drivers/ide/pci/tc86c001.c
+++ b/drivers/ide/pci/tc86c001.c
@@ -186,7 +186,7 @@
.dma_setup = ide_dma_setup,
.dma_exec_cmd = ide_dma_exec_cmd,
.dma_start = tc86c001_dma_start,
- .dma_end = __ide_dma_end,
+ .dma_end = ide_dma_end,
.dma_test_irq = ide_dma_test_irq,
.dma_lost_irq = ide_dma_lost_irq,
.dma_timeout = ide_dma_timeout,
@@ -245,7 +245,7 @@
};
MODULE_DEVICE_TABLE(pci, tc86c001_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver tc86c001_pci_driver = {
.name = "TC86C001",
.id_table = tc86c001_pci_tbl,
.probe = tc86c001_init_one,
@@ -254,12 +254,12 @@
static int __init tc86c001_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&tc86c001_pci_driver);
}
static void __exit tc86c001_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&tc86c001_pci_driver);
}
module_init(tc86c001_ide_init);
diff --git a/drivers/ide/pci/triflex.c b/drivers/ide/pci/triflex.c
index b77ec35..b6ff403 100644
--- a/drivers/ide/pci/triflex.c
+++ b/drivers/ide/pci/triflex.c
@@ -28,7 +28,6 @@
#include <linux/types.h>
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/hdreg.h>
#include <linux/pci.h>
#include <linux/ide.h>
#include <linux/init.h>
@@ -39,13 +38,12 @@
{
ide_hwif_t *hwif = HWIF(drive);
struct pci_dev *dev = to_pci_dev(hwif->dev);
- u8 channel_offset = hwif->channel ? 0x74 : 0x70;
- u16 timing = 0;
u32 triflex_timings = 0;
- u8 unit = (drive->select.b.unit & 0x01);
-
+ u16 timing = 0;
+ u8 channel_offset = hwif->channel ? 0x74 : 0x70, unit = drive->dn & 1;
+
pci_read_config_dword(dev, channel_offset, &triflex_timings);
-
+
switch(speed) {
case XFER_MW_DMA_2:
timing = 0x0103;
@@ -115,21 +113,23 @@
};
MODULE_DEVICE_TABLE(pci, triflex_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver triflex_pci_driver = {
.name = "TRIFLEX_IDE",
.id_table = triflex_pci_tbl,
.probe = triflex_init_one,
.remove = ide_pci_remove,
+ .suspend = ide_pci_suspend,
+ .resume = ide_pci_resume,
};
static int __init triflex_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&triflex_pci_driver);
}
static void __exit triflex_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&triflex_pci_driver);
}
module_init(triflex_ide_init);
diff --git a/drivers/ide/pci/trm290.c b/drivers/ide/pci/trm290.c
index fd28b49..75ea615 100644
--- a/drivers/ide/pci/trm290.c
+++ b/drivers/ide/pci/trm290.c
@@ -135,7 +135,6 @@
#include <linux/interrupt.h>
#include <linux/blkdev.h>
#include <linux/init.h>
-#include <linux/hdreg.h>
#include <linux/pci.h>
#include <linux/ide.h>
@@ -162,7 +161,7 @@
}
/* enable IRQ if not probing */
- if (drive->present) {
+ if (drive->dev_flags & IDE_DFLAG_PRESENT) {
reg = inw(hwif->config_data + 3);
reg &= 0x13;
reg &= ~(1 << hwif->channel);
@@ -174,7 +173,7 @@
static void trm290_selectproc (ide_drive_t *drive)
{
- trm290_prepare_drive(drive, drive->using_dma);
+ trm290_prepare_drive(drive, !!(drive->dev_flags & IDE_DFLAG_USING_DMA));
}
static void trm290_dma_exec_cmd(ide_drive_t *drive, u8 command)
@@ -351,7 +350,7 @@
};
MODULE_DEVICE_TABLE(pci, trm290_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver trm290_pci_driver = {
.name = "TRM290_IDE",
.id_table = trm290_pci_tbl,
.probe = trm290_init_one,
@@ -360,12 +359,12 @@
static int __init trm290_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&trm290_pci_driver);
}
static void __exit trm290_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&trm290_pci_driver);
}
module_init(trm290_ide_init);
diff --git a/drivers/ide/pci/via82cxxx.c b/drivers/ide/pci/via82cxxx.c
index 94fb9ab..2a812d3 100644
--- a/drivers/ide/pci/via82cxxx.c
+++ b/drivers/ide/pci/via82cxxx.c
@@ -154,7 +154,7 @@
static void via_set_drive(ide_drive_t *drive, const u8 speed)
{
ide_hwif_t *hwif = drive->hwif;
- ide_drive_t *peer = hwif->drives + (~drive->dn & 1);
+ ide_drive_t *peer = ide_get_pair_dev(drive);
struct pci_dev *dev = to_pci_dev(hwif->dev);
struct ide_host *host = pci_get_drvdata(dev);
struct via82cxxx_dev *vdev = host->host_priv;
@@ -173,7 +173,7 @@
ide_timing_compute(drive, speed, &t, T, UT);
- if (peer->present) {
+ if (peer) {
ide_timing_compute(peer, peer->current_speed, &p, T, UT);
ide_timing_merge(&p, &t, &t, IDE_TIMING_8BIT);
}
@@ -215,7 +215,7 @@
/*
* Check and handle 80-wire cable presence
*/
-static void __devinit via_cable_detect(struct via82cxxx_dev *vdev, u32 u)
+static void via_cable_detect(struct via82cxxx_dev *vdev, u32 u)
{
int i;
@@ -267,7 +267,7 @@
* and initialize its drive independent registers.
*/
-static unsigned int __devinit init_chipset_via82cxxx(struct pci_dev *dev)
+static unsigned int init_chipset_via82cxxx(struct pci_dev *dev)
{
struct ide_host *host = pci_get_drvdata(dev);
struct via82cxxx_dev *vdev = host->host_priv;
@@ -487,21 +487,23 @@
};
MODULE_DEVICE_TABLE(pci, via_pci_tbl);
-static struct pci_driver driver = {
+static struct pci_driver via_pci_driver = {
.name = "VIA_IDE",
.id_table = via_pci_tbl,
.probe = via_init_one,
.remove = __devexit_p(via_remove),
+ .suspend = ide_pci_suspend,
+ .resume = ide_pci_resume,
};
static int __init via_ide_init(void)
{
- return ide_pci_register_driver(&driver);
+ return ide_pci_register_driver(&via_pci_driver);
}
static void __exit via_ide_exit(void)
{
- pci_unregister_driver(&driver);
+ pci_unregister_driver(&via_pci_driver);
}
module_init(via_ide_init);
diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c
index fa2be26..2e19d62 100644
--- a/drivers/ide/ppc/pmac.c
+++ b/drivers/ide/ppc/pmac.c
@@ -430,10 +430,7 @@
pmac_ide_hwif_t *pmif =
(pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
- if (pmif == NULL)
- return;
-
- if (drive->select.b.unit & 0x01)
+ if (drive->dn & 1)
writel(pmif->timings[1], PMAC_IDE_REG(IDE_TIMING_CONFIG));
else
writel(pmif->timings[0], PMAC_IDE_REG(IDE_TIMING_CONFIG));
@@ -452,10 +449,7 @@
pmac_ide_hwif_t *pmif =
(pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
- if (pmif == NULL)
- return;
-
- if (drive->select.b.unit & 0x01) {
+ if (drive->dn & 1) {
writel(pmif->timings[1], PMAC_IDE_REG(IDE_KAUAI_PIO_CONFIG));
writel(pmif->timings[3], PMAC_IDE_REG(IDE_KAUAI_ULTRA_CONFIG));
} else {
@@ -475,9 +469,6 @@
pmac_ide_hwif_t *pmif =
(pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
- if (pmif == NULL)
- return;
-
if (pmif->kind == controller_sh_ata6 ||
pmif->kind == controller_un_ata6 ||
pmif->kind == controller_k2_ata6)
@@ -524,11 +515,8 @@
unsigned accessTime, recTime;
unsigned int cycle_time;
- if (pmif == NULL)
- return;
-
/* which drive is it ? */
- timings = &pmif->timings[drive->select.b.unit & 0x01];
+ timings = &pmif->timings[drive->dn & 1];
t = *timings;
cycle_time = ide_pio_cycle_time(drive, pio);
@@ -669,9 +657,9 @@
set_timings_mdma(ide_drive_t *drive, int intf_type, u32 *timings, u32 *timings2,
u8 speed)
{
+ u16 *id = drive->id;
int cycleTime, accessTime = 0, recTime = 0;
unsigned accessTicks, recTicks;
- struct hd_driveid *id = drive->id;
struct mdma_timings_t* tm = NULL;
int i;
@@ -686,8 +674,8 @@
}
/* Check if drive provides explicit DMA cycle time */
- if ((id->field_valid & 2) && id->eide_dma_time)
- cycleTime = max_t(int, id->eide_dma_time, cycleTime);
+ if ((id[ATA_ID_FIELD_VALID] & 2) && id[ATA_ID_EIDE_DMA_TIME])
+ cycleTime = max_t(int, id[ATA_ID_EIDE_DMA_TIME], cycleTime);
/* OHare limits according to some old Apple sources */
if ((intf_type == controller_ohare) && (cycleTime < 150))
@@ -805,9 +793,9 @@
ide_hwif_t *hwif = drive->hwif;
pmac_ide_hwif_t *pmif =
(pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
- int unit = (drive->select.b.unit & 0x01);
int ret = 0;
u32 *timings, *timings2, tl[2];
+ u8 unit = drive->dn & 1;
timings = &pmif->timings[unit];
timings2 = &pmif->timings[unit+2];
@@ -966,11 +954,11 @@
if (pmif->mediabay) {
#ifdef CONFIG_PMAC_MEDIABAY
if (check_media_bay_by_base(pmif->regbase, MB_CD) == 0) {
- drive->noprobe = 0;
+ drive->dev_flags &= ~IDE_DFLAG_NOPROBE;
return;
}
#endif
- drive->noprobe = 1;
+ drive->dev_flags |= IDE_DFLAG_NOPROBE;
}
}
@@ -1535,18 +1523,6 @@
return 0; /* revert to PIO for this request */
}
-/* Teardown mappings after DMA has completed. */
-static void
-pmac_ide_destroy_dmatable (ide_drive_t *drive)
-{
- ide_hwif_t *hwif = drive->hwif;
-
- if (hwif->sg_nents) {
- ide_destroy_dmatable(drive);
- hwif->sg_nents = 0;
- }
-}
-
/*
* Prepare a DMA transfer. We build the DMA table, adjust the timings for
* a read on KeyLargo ATA/66 and mark us as waiting for DMA completion
@@ -1558,12 +1534,7 @@
pmac_ide_hwif_t *pmif =
(pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
struct request *rq = HWGROUP(drive)->rq;
- u8 unit = (drive->select.b.unit & 0x01);
- u8 ata4;
-
- if (pmif == NULL)
- return 1;
- ata4 = (pmif->kind == controller_kl_ata4);
+ u8 unit = drive->dn & 1, ata4 = (pmif->kind == controller_kl_ata4);
if (!pmac_ide_build_dmatable(drive, rq)) {
ide_map_sg(drive, rq);
@@ -1617,17 +1588,15 @@
ide_hwif_t *hwif = drive->hwif;
pmac_ide_hwif_t *pmif =
(pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
- volatile struct dbdma_regs __iomem *dma;
+ volatile struct dbdma_regs __iomem *dma = pmif->dma_regs;
u32 dstat;
-
- if (pmif == NULL)
- return 0;
- dma = pmif->dma_regs;
drive->waiting_for_dma = 0;
dstat = readl(&dma->status);
writel(((RUN|WAKE|DEAD) << 16), &dma->control);
- pmac_ide_destroy_dmatable(drive);
+
+ ide_destroy_dmatable(drive);
+
/* verify good dma status. we don't check for ACTIVE beeing 0. We should...
* in theory, but with ATAPI decices doing buffer underruns, that would
* cause us to disable DMA, which isn't what we want
@@ -1647,13 +1616,9 @@
ide_hwif_t *hwif = drive->hwif;
pmac_ide_hwif_t *pmif =
(pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
- volatile struct dbdma_regs __iomem *dma;
+ volatile struct dbdma_regs __iomem *dma = pmif->dma_regs;
unsigned long status, timeout;
- if (pmif == NULL)
- return 0;
- dma = pmif->dma_regs;
-
/* We have to things to deal with here:
*
* - The dbdma won't stop if the command was started
@@ -1672,9 +1637,6 @@
status = readl(&dma->status);
if (!(status & ACTIVE))
return 1;
- if (!drive->waiting_for_dma)
- printk(KERN_WARNING "ide%d, ide_dma_test_irq \
- called while not waiting\n", HWIF(drive)->index);
/* If dbdma didn't execute the STOP command yet, the
* active bit is still set. We consider that we aren't
@@ -1709,14 +1671,9 @@
ide_hwif_t *hwif = drive->hwif;
pmac_ide_hwif_t *pmif =
(pmac_ide_hwif_t *)dev_get_drvdata(hwif->gendev.parent);
- volatile struct dbdma_regs __iomem *dma;
- unsigned long status;
+ volatile struct dbdma_regs __iomem *dma = pmif->dma_regs;
+ unsigned long status = readl(&dma->status);
- if (pmif == NULL)
- return;
- dma = pmif->dma_regs;
-
- status = readl(&dma->status);
printk(KERN_ERR "ide-pmac lost interrupt, dma status: %lx\n", status);
}
diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c
index a8e9e8a..9f1f916 100644
--- a/drivers/ide/setup-pci.c
+++ b/drivers/ide/setup-pci.c
@@ -659,3 +659,36 @@
pci_disable_device(dev);
}
EXPORT_SYMBOL_GPL(ide_pci_remove);
+
+#ifdef CONFIG_PM
+int ide_pci_suspend(struct pci_dev *dev, pm_message_t state)
+{
+ pci_save_state(dev);
+ pci_disable_device(dev);
+ pci_set_power_state(dev, pci_choose_state(dev, state));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ide_pci_suspend);
+
+int ide_pci_resume(struct pci_dev *dev)
+{
+ struct ide_host *host = pci_get_drvdata(dev);
+ int rc;
+
+ pci_set_power_state(dev, PCI_D0);
+
+ rc = pci_enable_device(dev);
+ if (rc)
+ return rc;
+
+ pci_restore_state(dev);
+ pci_set_master(dev);
+
+ if (host->init_chipset)
+ host->init_chipset(dev);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ide_pci_resume);
+#endif
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index ed7c5f7..5b8b533 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -1683,7 +1683,7 @@
SRP_OPT_SERVICE_ID),
};
-static match_table_t srp_opt_tokens = {
+static const match_table_t srp_opt_tokens = {
{ SRP_OPT_ID_EXT, "id_ext=%s" },
{ SRP_OPT_IOC_GUID, "ioc_guid=%s" },
{ SRP_OPT_DGID, "dgid=%s" },
diff --git a/drivers/input/misc/hp_sdc_rtc.c b/drivers/input/misc/hp_sdc_rtc.c
index daa9d42..82ec6b1 100644
--- a/drivers/input/misc/hp_sdc_rtc.c
+++ b/drivers/input/misc/hp_sdc_rtc.c
@@ -458,35 +458,35 @@
p += sprintf(p, "i8042 rtc\t: READ FAILED!\n");
} else {
p += sprintf(p, "i8042 rtc\t: %ld.%02d seconds\n",
- tv.tv_sec, tv.tv_usec/1000);
+ tv.tv_sec, (int)tv.tv_usec/1000);
}
if (hp_sdc_rtc_read_fhs(&tv)) {
p += sprintf(p, "handshake\t: READ FAILED!\n");
} else {
p += sprintf(p, "handshake\t: %ld.%02d seconds\n",
- tv.tv_sec, tv.tv_usec/1000);
+ tv.tv_sec, (int)tv.tv_usec/1000);
}
if (hp_sdc_rtc_read_mt(&tv)) {
p += sprintf(p, "alarm\t\t: READ FAILED!\n");
} else {
p += sprintf(p, "alarm\t\t: %ld.%02d seconds\n",
- tv.tv_sec, tv.tv_usec/1000);
+ tv.tv_sec, (int)tv.tv_usec/1000);
}
if (hp_sdc_rtc_read_dt(&tv)) {
p += sprintf(p, "delay\t\t: READ FAILED!\n");
} else {
p += sprintf(p, "delay\t\t: %ld.%02d seconds\n",
- tv.tv_sec, tv.tv_usec/1000);
+ tv.tv_sec, (int)tv.tv_usec/1000);
}
if (hp_sdc_rtc_read_ct(&tv)) {
p += sprintf(p, "periodic\t: READ FAILED!\n");
} else {
p += sprintf(p, "periodic\t: %ld.%02d seconds\n",
- tv.tv_sec, tv.tv_usec/1000);
+ tv.tv_sec, (int)tv.tv_usec/1000);
}
p += sprintf(p,
diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c
index d8765cc..c4f4231 100644
--- a/drivers/input/misc/sparcspkr.c
+++ b/drivers/input/misc/sparcspkr.c
@@ -249,7 +249,7 @@
return 0;
}
-static struct of_device_id bbc_beep_match[] = {
+static const struct of_device_id bbc_beep_match[] = {
{
.name = "beep",
.compatible = "SUNW,bbc-beep",
@@ -328,7 +328,7 @@
return 0;
}
-static struct of_device_id grover_beep_match[] = {
+static const struct of_device_id grover_beep_match[] = {
{
.name = "beep",
.compatible = "SUNW,smbus-beep",
diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c
index 0d39597..bfe4924 100644
--- a/drivers/input/serio/hp_sdc.c
+++ b/drivers/input/serio/hp_sdc.c
@@ -323,7 +323,7 @@
* it back to the application. and be less verbose.
*/
printk(KERN_WARNING PREFIX "read timeout (%ius)!\n",
- tv.tv_usec - hp_sdc.rtv.tv_usec);
+ (int)(tv.tv_usec - hp_sdc.rtv.tv_usec));
curr->idx += hp_sdc.rqty;
hp_sdc.rqty = 0;
tmp = curr->seq[curr->actidx];
diff --git a/drivers/input/serio/i8042-sparcio.h b/drivers/input/serio/i8042-sparcio.h
index 692a79e..5071af2 100644
--- a/drivers/input/serio/i8042-sparcio.h
+++ b/drivers/input/serio/i8042-sparcio.h
@@ -87,7 +87,7 @@
return 0;
}
-static struct of_device_id sparc_i8042_match[] = {
+static const struct of_device_id sparc_i8042_match[] = {
{
.name = "8042",
},
diff --git a/drivers/input/xen-kbdfront.c b/drivers/input/xen-kbdfront.c
index 9ce3b3b..3ab6362 100644
--- a/drivers/input/xen-kbdfront.c
+++ b/drivers/input/xen-kbdfront.c
@@ -335,11 +335,11 @@
static int __init xenkbd_init(void)
{
- if (!is_running_on_xen())
+ if (!xen_domain())
return -ENODEV;
/* Nothing to do if running in dom0. */
- if (is_initial_xendomain())
+ if (xen_initial_domain())
return -ENODEV;
return xenbus_register_frontend(&xenkbd);
diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c
index 871b0cbc..798d7f3 100644
--- a/drivers/isdn/capi/capi.c
+++ b/drivers/isdn/capi/capi.c
@@ -1231,7 +1231,7 @@
int error = 0;
switch (cmd) {
default:
- error = n_tty_ioctl (tty, file, cmd, arg);
+ error = n_tty_ioctl_helper(tty, file, cmd, arg);
break;
}
return error;
diff --git a/drivers/isdn/gigaset/ser-gigaset.c b/drivers/isdn/gigaset/ser-gigaset.c
index 5e89fa1..07052ed 100644
--- a/drivers/isdn/gigaset/ser-gigaset.c
+++ b/drivers/isdn/gigaset/ser-gigaset.c
@@ -571,6 +571,7 @@
}
/* prevent other callers from entering ldisc methods */
+ /* FIXME: should use the tty state flags */
tty->disc_data = NULL;
if (!cs->hw.ser)
@@ -642,10 +643,11 @@
return -ENXIO;
switch (cmd) {
- case TCGETS:
- case TCGETA:
- /* pass through to underlying serial device */
- rc = n_tty_ioctl(tty, file, cmd, arg);
+
+ case FIONREAD:
+ /* unused, always return zero */
+ val = 0;
+ rc = put_user(val, p);
break;
case TCFLSH:
@@ -659,20 +661,13 @@
flush_send_queue(cs);
break;
}
- /* flush the serial port's buffer */
- rc = n_tty_ioctl(tty, file, cmd, arg);
- break;
-
- case FIONREAD:
- /* unused, always return zero */
- val = 0;
- rc = put_user(val, p);
- break;
+ /* Pass through */
default:
- rc = -ENOIOCTLCMD;
+ /* pass through to underlying serial device */
+ rc = n_tty_ioctl_helper(tty, file, cmd, arg);
+ break;
}
-
cs_put(cs);
return rc;
}
@@ -680,6 +675,8 @@
/*
* Poll on the tty.
* Unused, always return zero.
+ *
+ * FIXME: should probably return an exception - especially on hangup
*/
static unsigned int
gigaset_tty_poll(struct tty_struct *tty, struct file *file, poll_table *wait)
diff --git a/drivers/isdn/hardware/avm/avm_cs.c b/drivers/isdn/hardware/avm/avm_cs.c
index a5b941c..c725655 100644
--- a/drivers/isdn/hardware/avm/avm_cs.c
+++ b/drivers/isdn/hardware/avm/avm_cs.c
@@ -154,83 +154,50 @@
======================================================================*/
-static int get_tuple(struct pcmcia_device *handle, tuple_t *tuple,
- cisparse_t *parse)
+static int avmcs_configcheck(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
{
- int i = pcmcia_get_tuple_data(handle, tuple);
- if (i != CS_SUCCESS) return i;
- return pcmcia_parse_tuple(handle, tuple, parse);
-}
+ if (cf->io.nwin <= 0)
+ return -ENODEV;
-static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple,
- cisparse_t *parse)
-{
- int i = pcmcia_get_first_tuple(handle, tuple);
- if (i != CS_SUCCESS) return i;
- return get_tuple(handle, tuple, parse);
-}
-
-static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple,
- cisparse_t *parse)
-{
- int i = pcmcia_get_next_tuple(handle, tuple);
- if (i != CS_SUCCESS) return i;
- return get_tuple(handle, tuple, parse);
+ p_dev->io.BasePort1 = cf->io.win[0].base;
+ p_dev->io.NumPorts1 = cf->io.win[0].len;
+ p_dev->io.NumPorts2 = 0;
+ printk(KERN_INFO "avm_cs: testing i/o %#x-%#x\n",
+ p_dev->io.BasePort1,
+ p_dev->io.BasePort1+p_dev->io.NumPorts1-1);
+ return pcmcia_request_io(p_dev, &p_dev->io);
}
static int avmcs_config(struct pcmcia_device *link)
{
- tuple_t tuple;
- cisparse_t parse;
- cistpl_cftable_entry_t *cf = &parse.cftable_entry;
local_info_t *dev;
int i;
- u_char buf[64];
char devname[128];
int cardtype;
int (*addcard)(unsigned int port, unsigned irq);
dev = link->priv;
+ devname[0] = 0;
+ if (link->prod_id[1])
+ strlcpy(devname, link->prod_id[1], sizeof(devname));
+
+ /*
+ * find IO port
+ */
+ if (pcmcia_loop_config(link, avmcs_configcheck, NULL))
+ return -ENODEV;
+
do {
- devname[0] = 0;
- if (link->prod_id[1])
- strlcpy(devname, link->prod_id[1], sizeof(devname));
-
- /*
- * find IO port
- */
- tuple.TupleData = (cisdata_t *)buf;
- tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
- tuple.Attributes = 0;
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- i = first_tuple(link, &tuple, &parse);
- while (i == CS_SUCCESS) {
- if (cf->io.nwin > 0) {
- link->conf.ConfigIndex = cf->index;
- link->io.BasePort1 = cf->io.win[0].base;
- link->io.NumPorts1 = cf->io.win[0].len;
- link->io.NumPorts2 = 0;
- printk(KERN_INFO "avm_cs: testing i/o %#x-%#x\n",
- link->io.BasePort1,
- link->io.BasePort1+link->io.NumPorts1-1);
- i = pcmcia_request_io(link, &link->io);
- if (i == CS_SUCCESS) goto found_port;
- }
- i = next_tuple(link, &tuple, &parse);
- }
-
-found_port:
- if (i != CS_SUCCESS) {
- cs_error(link, RequestIO, i);
- break;
- }
-
/*
* allocate an interrupt line
*/
i = pcmcia_request_irq(link, &link->irq);
- if (i != CS_SUCCESS) {
+ if (i != 0) {
cs_error(link, RequestIRQ, i);
/* undo */
pcmcia_disable_device(link);
@@ -241,7 +208,7 @@
* configure the PCMCIA socket
*/
i = pcmcia_request_configuration(link, &link->conf);
- if (i != CS_SUCCESS) {
+ if (i != 0) {
cs_error(link, RequestConfiguration, i);
pcmcia_disable_device(link);
break;
diff --git a/drivers/isdn/hisax/avma1_cs.c b/drivers/isdn/hisax/avma1_cs.c
index fc6cc2c..23560c8 100644
--- a/drivers/isdn/hisax/avma1_cs.c
+++ b/drivers/isdn/hisax/avma1_cs.c
@@ -174,38 +174,29 @@
======================================================================*/
-static int get_tuple(struct pcmcia_device *handle, tuple_t *tuple,
- cisparse_t *parse)
+static int avma1cs_configcheck(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
{
- int i = pcmcia_get_tuple_data(handle, tuple);
- if (i != CS_SUCCESS) return i;
- return pcmcia_parse_tuple(handle, tuple, parse);
+ if (cf->io.nwin <= 0)
+ return -ENODEV;
+
+ p_dev->io.BasePort1 = cf->io.win[0].base;
+ p_dev->io.NumPorts1 = cf->io.win[0].len;
+ p_dev->io.NumPorts2 = 0;
+ printk(KERN_INFO "avma1_cs: testing i/o %#x-%#x\n",
+ p_dev->io.BasePort1,
+ p_dev->io.BasePort1+p_dev->io.NumPorts1-1);
+ return pcmcia_request_io(p_dev, &p_dev->io);
}
-static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple,
- cisparse_t *parse)
-{
- int i = pcmcia_get_first_tuple(handle, tuple);
- if (i != CS_SUCCESS) return i;
- return get_tuple(handle, tuple, parse);
-}
-
-static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple,
- cisparse_t *parse)
-{
- int i = pcmcia_get_next_tuple(handle, tuple);
- if (i != CS_SUCCESS) return i;
- return get_tuple(handle, tuple, parse);
-}
static int avma1cs_config(struct pcmcia_device *link)
{
- tuple_t tuple;
- cisparse_t parse;
- cistpl_cftable_entry_t *cf = &parse.cftable_entry;
local_info_t *dev;
int i;
- u_char buf[64];
char devname[128];
IsdnCard_t icard;
int busy = 0;
@@ -214,45 +205,19 @@
DEBUG(0, "avma1cs_config(0x%p)\n", link);
+ devname[0] = 0;
+ if (link->prod_id[1])
+ strlcpy(devname, link->prod_id[1], sizeof(devname));
+
+ if (pcmcia_loop_config(link, avma1cs_configcheck, NULL))
+ return -ENODEV;
+
do {
- devname[0] = 0;
- if (link->prod_id[1])
- strlcpy(devname, link->prod_id[1], sizeof(devname));
-
- /*
- * find IO port
- */
- tuple.TupleData = (cisdata_t *)buf;
- tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
- tuple.Attributes = 0;
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- i = first_tuple(link, &tuple, &parse);
- while (i == CS_SUCCESS) {
- if (cf->io.nwin > 0) {
- link->conf.ConfigIndex = cf->index;
- link->io.BasePort1 = cf->io.win[0].base;
- link->io.NumPorts1 = cf->io.win[0].len;
- link->io.NumPorts2 = 0;
- printk(KERN_INFO "avma1_cs: testing i/o %#x-%#x\n",
- link->io.BasePort1,
- link->io.BasePort1+link->io.NumPorts1 - 1);
- i = pcmcia_request_io(link, &link->io);
- if (i == CS_SUCCESS) goto found_port;
- }
- i = next_tuple(link, &tuple, &parse);
- }
-
-found_port:
- if (i != CS_SUCCESS) {
- cs_error(link, RequestIO, i);
- break;
- }
-
/*
* allocate an interrupt line
*/
i = pcmcia_request_irq(link, &link->irq);
- if (i != CS_SUCCESS) {
+ if (i != 0) {
cs_error(link, RequestIRQ, i);
/* undo */
pcmcia_disable_device(link);
@@ -263,7 +228,7 @@
* configure the PCMCIA socket
*/
i = pcmcia_request_configuration(link, &link->conf);
- if (i != CS_SUCCESS) {
+ if (i != 0) {
cs_error(link, RequestConfiguration, i);
pcmcia_disable_device(link);
break;
diff --git a/drivers/isdn/hisax/elsa_cs.c b/drivers/isdn/hisax/elsa_cs.c
index db7e644..f4d0fe2 100644
--- a/drivers/isdn/hisax/elsa_cs.c
+++ b/drivers/isdn/hisax/elsa_cs.c
@@ -203,82 +203,55 @@
device available to the system.
======================================================================*/
-static int get_tuple(struct pcmcia_device *handle, tuple_t *tuple,
- cisparse_t *parse)
-{
- int i = pcmcia_get_tuple_data(handle, tuple);
- if (i != CS_SUCCESS) return i;
- return pcmcia_parse_tuple(handle, tuple, parse);
-}
-static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple,
- cisparse_t *parse)
+static int elsa_cs_configcheck(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
{
- int i = pcmcia_get_first_tuple(handle, tuple);
- if (i != CS_SUCCESS) return i;
- return get_tuple(handle, tuple, parse);
-}
+ int j;
-static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple,
- cisparse_t *parse)
-{
- int i = pcmcia_get_next_tuple(handle, tuple);
- if (i != CS_SUCCESS) return i;
- return get_tuple(handle, tuple, parse);
+ if ((cf->io.nwin > 0) && cf->io.win[0].base) {
+ printk(KERN_INFO "(elsa_cs: looks like the 96 model)\n");
+ p_dev->io.BasePort1 = cf->io.win[0].base;
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+ return 0;
+ } else {
+ printk(KERN_INFO "(elsa_cs: looks like the 97 model)\n");
+ for (j = 0x2f0; j > 0x100; j -= 0x10) {
+ p_dev->io.BasePort1 = j;
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+ return 0;
+ }
+ }
+ return -ENODEV;
}
static int elsa_cs_config(struct pcmcia_device *link)
{
- tuple_t tuple;
- cisparse_t parse;
local_info_t *dev;
- int i, j, last_fn;
- u_short buf[128];
- cistpl_cftable_entry_t *cf = &parse.cftable_entry;
+ int i, last_fn;
IsdnCard_t icard;
DEBUG(0, "elsa_config(0x%p)\n", link);
dev = link->priv;
- tuple.TupleData = (cisdata_t *)buf;
- tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
- tuple.Attributes = 0;
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- i = first_tuple(link, &tuple, &parse);
- while (i == CS_SUCCESS) {
- if ( (cf->io.nwin > 0) && cf->io.win[0].base) {
- printk(KERN_INFO "(elsa_cs: looks like the 96 model)\n");
- link->conf.ConfigIndex = cf->index;
- link->io.BasePort1 = cf->io.win[0].base;
- i = pcmcia_request_io(link, &link->io);
- if (i == CS_SUCCESS) break;
- } else {
- printk(KERN_INFO "(elsa_cs: looks like the 97 model)\n");
- link->conf.ConfigIndex = cf->index;
- for (i = 0, j = 0x2f0; j > 0x100; j -= 0x10) {
- link->io.BasePort1 = j;
- i = pcmcia_request_io(link, &link->io);
- if (i == CS_SUCCESS) break;
- }
- break;
- }
- i = next_tuple(link, &tuple, &parse);
- }
-
- if (i != CS_SUCCESS) {
+ i = pcmcia_loop_config(link, elsa_cs_configcheck, NULL);
+ if (i != 0) {
last_fn = RequestIO;
goto cs_failed;
}
i = pcmcia_request_irq(link, &link->irq);
- if (i != CS_SUCCESS) {
+ if (i != 0) {
link->irq.AssignedIRQ = 0;
last_fn = RequestIRQ;
goto cs_failed;
}
i = pcmcia_request_configuration(link, &link->conf);
- if (i != CS_SUCCESS) {
+ if (i != 0) {
last_fn = RequestConfiguration;
goto cs_failed;
}
diff --git a/drivers/isdn/hisax/sedlbauer_cs.c b/drivers/isdn/hisax/sedlbauer_cs.c
index 439cb53..9a3c9f5 100644
--- a/drivers/isdn/hisax/sedlbauer_cs.c
+++ b/drivers/isdn/hisax/sedlbauer_cs.c
@@ -217,101 +217,61 @@
#define CS_CHECK(fn, ret) \
do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-static int sedlbauer_config(struct pcmcia_device *link)
+static int sedlbauer_config_check(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
{
- local_info_t *dev = link->priv;
- tuple_t tuple;
- cisparse_t parse;
- int last_fn, last_ret;
- u8 buf[64];
- config_info_t conf;
- win_req_t req;
- memreq_t map;
- IsdnCard_t icard;
+ win_req_t *req = priv_data;
- DEBUG(0, "sedlbauer_config(0x%p)\n", link);
+ if (cfg->index == 0)
+ return -ENODEV;
- tuple.Attributes = 0;
- tuple.TupleData = buf;
- tuple.TupleDataMax = sizeof(buf);
- tuple.TupleOffset = 0;
-
- CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(link, &conf));
-
- /*
- In this loop, we scan the CIS for configuration table entries,
- each of which describes a valid card configuration, including
- voltage, IO window, memory window, and interrupt settings.
-
- We make no assumptions about the card to be configured: we use
- just the information available in the CIS. In an ideal world,
- this would work for any PCMCIA card, but it requires a complete
- and accurate CIS. In practice, a driver usually "knows" most of
- these things without consulting the CIS, and most client drivers
- will only use the CIS to fill in implementation-defined details.
- */
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
- while (1) {
- cistpl_cftable_entry_t dflt = { 0 };
- cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
- if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
- pcmcia_parse_tuple(link, &tuple, &parse) != 0)
- goto next_entry;
-
- if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg;
- if (cfg->index == 0) goto next_entry;
- link->conf.ConfigIndex = cfg->index;
-
/* Does this card need audio output? */
if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
- link->conf.Attributes |= CONF_ENABLE_SPKR;
- link->conf.Status = CCSR_AUDIO_ENA;
+ p_dev->conf.Attributes |= CONF_ENABLE_SPKR;
+ p_dev->conf.Status = CCSR_AUDIO_ENA;
}
-
+
/* Use power settings for Vcc and Vpp if present */
/* Note that the CIS values need to be rescaled */
if (cfg->vcc.present & (1<<CISTPL_POWER_VNOM)) {
- if (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM]/10000)
- goto next_entry;
- } else if (dflt.vcc.present & (1<<CISTPL_POWER_VNOM)) {
- if (conf.Vcc != dflt.vcc.param[CISTPL_POWER_VNOM]/10000)
- goto next_entry;
+ if (vcc != cfg->vcc.param[CISTPL_POWER_VNOM]/10000)
+ return -ENODEV;
+ } else if (dflt->vcc.present & (1<<CISTPL_POWER_VNOM)) {
+ if (vcc != dflt->vcc.param[CISTPL_POWER_VNOM]/10000)
+ return -ENODEV;
}
-
+
if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM))
- link->conf.Vpp =
- cfg->vpp1.param[CISTPL_POWER_VNOM]/10000;
- else if (dflt.vpp1.present & (1<<CISTPL_POWER_VNOM))
- link->conf.Vpp =
- dflt.vpp1.param[CISTPL_POWER_VNOM]/10000;
-
+ p_dev->conf.Vpp = cfg->vpp1.param[CISTPL_POWER_VNOM]/10000;
+ else if (dflt->vpp1.present & (1<<CISTPL_POWER_VNOM))
+ p_dev->conf.Vpp = dflt->vpp1.param[CISTPL_POWER_VNOM]/10000;
+
/* Do we need to allocate an interrupt? */
- if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
- link->conf.Attributes |= CONF_ENABLE_IRQ;
-
+ if (cfg->irq.IRQInfo1 || dflt->irq.IRQInfo1)
+ p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
+
/* IO window settings */
- link->io.NumPorts1 = link->io.NumPorts2 = 0;
- if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
- cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
- if (!(io->flags & CISTPL_IO_8BIT))
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
- if (!(io->flags & CISTPL_IO_16BIT))
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
-/* new in dummy.cs 2001/01/28 MN
- link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
-*/
- link->io.BasePort1 = io->win[0].base;
- link->io.NumPorts1 = io->win[0].len;
- if (io->nwin > 1) {
- link->io.Attributes2 = link->io.Attributes1;
- link->io.BasePort2 = io->win[1].base;
- link->io.NumPorts2 = io->win[1].len;
- }
- /* This reserves IO space but doesn't actually enable it */
- if (pcmcia_request_io(link, &link->io) != 0)
- goto next_entry;
+ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0;
+ if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(io->flags & CISTPL_IO_8BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(io->flags & CISTPL_IO_16BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ p_dev->io.BasePort1 = io->win[0].base;
+ p_dev->io.NumPorts1 = io->win[0].len;
+ if (io->nwin > 1) {
+ p_dev->io.Attributes2 = p_dev->io.Attributes1;
+ p_dev->io.BasePort2 = io->win[1].base;
+ p_dev->io.NumPorts2 = io->win[1].len;
+ }
+ /* This reserves IO space but doesn't actually enable it */
+ if (pcmcia_request_io(p_dev, &p_dev->io) != 0)
+ return -ENODEV;
}
/*
@@ -325,30 +285,54 @@
needs to be mapped to virtual space with ioremap() before it
is used.
*/
- if ((cfg->mem.nwin > 0) || (dflt.mem.nwin > 0)) {
- cistpl_mem_t *mem =
- (cfg->mem.nwin) ? &cfg->mem : &dflt.mem;
- req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM;
- req.Attributes |= WIN_ENABLE;
- req.Base = mem->win[0].host_addr;
- req.Size = mem->win[0].len;
-/* new in dummy.cs 2001/01/28 MN
- if (req.Size < 0x1000)
- req.Size = 0x1000;
-*/
- req.AccessSpeed = 0;
- if (pcmcia_request_window(&link, &req, &link->win) != 0)
- goto next_entry;
- map.Page = 0; map.CardOffset = mem->win[0].card_addr;
- if (pcmcia_map_mem_page(link->win, &map) != 0)
- goto next_entry;
+ if ((cfg->mem.nwin > 0) || (dflt->mem.nwin > 0)) {
+ cistpl_mem_t *mem = (cfg->mem.nwin) ? &cfg->mem : &dflt->mem;
+ memreq_t map;
+ req->Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM;
+ req->Attributes |= WIN_ENABLE;
+ req->Base = mem->win[0].host_addr;
+ req->Size = mem->win[0].len;
+ req->AccessSpeed = 0;
+ if (pcmcia_request_window(&p_dev, req, &p_dev->win) != 0)
+ return -ENODEV;
+ map.Page = 0;
+ map.CardOffset = mem->win[0].card_addr;
+ if (pcmcia_map_mem_page(p_dev->win, &map) != 0)
+ return -ENODEV;
}
- /* If we got this far, we're cool! */
- break;
+ return 0;
+}
- next_entry:
- CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple));
- }
+
+
+static int sedlbauer_config(struct pcmcia_device *link)
+{
+ local_info_t *dev = link->priv;
+ win_req_t *req;
+ int last_fn, last_ret;
+ IsdnCard_t icard;
+
+ DEBUG(0, "sedlbauer_config(0x%p)\n", link);
+
+ req = kzalloc(sizeof(win_req_t), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ /*
+ In this loop, we scan the CIS for configuration table entries,
+ each of which describes a valid card configuration, including
+ voltage, IO window, memory window, and interrupt settings.
+
+ We make no assumptions about the card to be configured: we use
+ just the information available in the CIS. In an ideal world,
+ this would work for any PCMCIA card, but it requires a complete
+ and accurate CIS. In practice, a driver usually "knows" most of
+ these things without consulting the CIS, and most client drivers
+ will only use the CIS to fill in implementation-defined details.
+ */
+ last_ret = pcmcia_loop_config(link, sedlbauer_config_check, req);
+ if (last_ret)
+ goto failed;
/*
Allocate an interrupt line. Note that this does not assign a
@@ -387,8 +371,8 @@
printk(" & 0x%04x-0x%04x", link->io.BasePort2,
link->io.BasePort2+link->io.NumPorts2-1);
if (link->win)
- printk(", mem 0x%06lx-0x%06lx", req.Base,
- req.Base+req.Size-1);
+ printk(", mem 0x%06lx-0x%06lx", req->Base,
+ req->Base+req->Size-1);
printk("\n");
icard.para[0] = link->irq.AssignedIRQ;
@@ -409,6 +393,7 @@
cs_failed:
cs_error(link, last_fn, last_ret);
+failed:
sedlbauer_release(link);
return -ENODEV;
diff --git a/drivers/isdn/hisax/teles_cs.c b/drivers/isdn/hisax/teles_cs.c
index ab4bd45..623d111 100644
--- a/drivers/isdn/hisax/teles_cs.c
+++ b/drivers/isdn/hisax/teles_cs.c
@@ -193,82 +193,55 @@
device available to the system.
======================================================================*/
-static int get_tuple(struct pcmcia_device *handle, tuple_t *tuple,
- cisparse_t *parse)
-{
- int i = pcmcia_get_tuple_data(handle, tuple);
- if (i != CS_SUCCESS) return i;
- return pcmcia_parse_tuple(handle, tuple, parse);
-}
-static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple,
- cisparse_t *parse)
+static int teles_cs_configcheck(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
{
- int i = pcmcia_get_first_tuple(handle, tuple);
- if (i != CS_SUCCESS) return i;
- return get_tuple(handle, tuple, parse);
-}
+ int j;
-static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple,
- cisparse_t *parse)
-{
- int i = pcmcia_get_next_tuple(handle, tuple);
- if (i != CS_SUCCESS) return i;
- return get_tuple(handle, tuple, parse);
+ if ((cf->io.nwin > 0) && cf->io.win[0].base) {
+ printk(KERN_INFO "(teles_cs: looks like the 96 model)\n");
+ p_dev->io.BasePort1 = cf->io.win[0].base;
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+ return 0;
+ } else {
+ printk(KERN_INFO "(teles_cs: looks like the 97 model)\n");
+ for (j = 0x2f0; j > 0x100; j -= 0x10) {
+ p_dev->io.BasePort1 = j;
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+ return 0;
+ }
+ }
+ return -ENODEV;
}
static int teles_cs_config(struct pcmcia_device *link)
{
- tuple_t tuple;
- cisparse_t parse;
local_info_t *dev;
- int i, j, last_fn;
- u_short buf[128];
- cistpl_cftable_entry_t *cf = &parse.cftable_entry;
+ int i, last_fn;
IsdnCard_t icard;
DEBUG(0, "teles_config(0x%p)\n", link);
dev = link->priv;
- tuple.TupleData = (cisdata_t *)buf;
- tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
- tuple.Attributes = 0;
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- i = first_tuple(link, &tuple, &parse);
- while (i == CS_SUCCESS) {
- if ( (cf->io.nwin > 0) && cf->io.win[0].base) {
- printk(KERN_INFO "(teles_cs: looks like the 96 model)\n");
- link->conf.ConfigIndex = cf->index;
- link->io.BasePort1 = cf->io.win[0].base;
- i = pcmcia_request_io(link, &link->io);
- if (i == CS_SUCCESS) break;
- } else {
- printk(KERN_INFO "(teles_cs: looks like the 97 model)\n");
- link->conf.ConfigIndex = cf->index;
- for (i = 0, j = 0x2f0; j > 0x100; j -= 0x10) {
- link->io.BasePort1 = j;
- i = pcmcia_request_io(link, &link->io);
- if (i == CS_SUCCESS) break;
- }
- break;
- }
- i = next_tuple(link, &tuple, &parse);
- }
-
- if (i != CS_SUCCESS) {
+ i = pcmcia_loop_config(link, teles_cs_configcheck, NULL);
+ if (i != 0) {
last_fn = RequestIO;
goto cs_failed;
}
i = pcmcia_request_irq(link, &link->irq);
- if (i != CS_SUCCESS) {
+ if (i != 0) {
link->irq.AssignedIRQ = 0;
last_fn = RequestIRQ;
goto cs_failed;
}
i = pcmcia_request_configuration(link, &link->conf);
- if (i != CS_SUCCESS) {
+ if (i != 0) {
last_fn = RequestConfiguration;
goto cs_failed;
}
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 5b14262..e3e4042 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -82,6 +82,14 @@
help
This option enables support for the Cobalt Raq series LEDs.
+config LEDS_SUNFIRE
+ tristate "LED support for SunFire servers."
+ depends on LEDS_CLASS && SPARC64
+ select LEDS_TRIGGERS
+ help
+ This option enables support for the Left, Middle, and Right
+ LEDs on the I/O and CPU boards of SunFire UltraSPARC servers.
+
config LEDS_HP6XX
tristate "LED Support for the HP Jornada 6xx"
depends on LEDS_CLASS && SH_HP6XX
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 3a8e6a0..eb186c3 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -14,6 +14,7 @@
obj-$(CONFIG_LEDS_H1940) += leds-h1940.o
obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o
obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o
+obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o
obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o
obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o
obj-$(CONFIG_LEDS_CM_X270) += leds-cm-x270.o
diff --git a/drivers/leds/leds-sunfire.c b/drivers/leds/leds-sunfire.c
new file mode 100644
index 0000000..6b008f0
--- /dev/null
+++ b/drivers/leds/leds-sunfire.c
@@ -0,0 +1,273 @@
+/* leds-sunfire.c: SUNW,Ultra-Enterprise LED driver.
+ *
+ * Copyright (C) 2008 David S. Miller <davem@davemloft.net>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+
+#include <asm/fhc.h>
+#include <asm/upa.h>
+
+#define DRIVER_NAME "leds-sunfire"
+#define PFX DRIVER_NAME ": "
+
+MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
+MODULE_DESCRIPTION("Sun Fire LED driver");
+MODULE_LICENSE("GPL");
+
+struct sunfire_led {
+ struct led_classdev led_cdev;
+ void __iomem *reg;
+};
+#define to_sunfire_led(d) container_of(d, struct sunfire_led, led_cdev)
+
+static void __clockboard_set(struct led_classdev *led_cdev,
+ enum led_brightness led_val, u8 bit)
+{
+ struct sunfire_led *p = to_sunfire_led(led_cdev);
+ u8 reg = upa_readb(p->reg);
+
+ switch (bit) {
+ case CLOCK_CTRL_LLED:
+ if (led_val)
+ reg &= ~bit;
+ else
+ reg |= bit;
+ break;
+
+ default:
+ if (led_val)
+ reg |= bit;
+ else
+ reg &= ~bit;
+ break;
+ }
+ upa_writeb(reg, p->reg);
+}
+
+static void clockboard_left_set(struct led_classdev *led_cdev,
+ enum led_brightness led_val)
+{
+ __clockboard_set(led_cdev, led_val, CLOCK_CTRL_LLED);
+}
+
+static void clockboard_middle_set(struct led_classdev *led_cdev,
+ enum led_brightness led_val)
+{
+ __clockboard_set(led_cdev, led_val, CLOCK_CTRL_MLED);
+}
+
+static void clockboard_right_set(struct led_classdev *led_cdev,
+ enum led_brightness led_val)
+{
+ __clockboard_set(led_cdev, led_val, CLOCK_CTRL_RLED);
+}
+
+static void __fhc_set(struct led_classdev *led_cdev,
+ enum led_brightness led_val, u32 bit)
+{
+ struct sunfire_led *p = to_sunfire_led(led_cdev);
+ u32 reg = upa_readl(p->reg);
+
+ switch (bit) {
+ case FHC_CONTROL_LLED:
+ if (led_val)
+ reg &= ~bit;
+ else
+ reg |= bit;
+ break;
+
+ default:
+ if (led_val)
+ reg |= bit;
+ else
+ reg &= ~bit;
+ break;
+ }
+ upa_writel(reg, p->reg);
+}
+
+static void fhc_left_set(struct led_classdev *led_cdev,
+ enum led_brightness led_val)
+{
+ __fhc_set(led_cdev, led_val, FHC_CONTROL_LLED);
+}
+
+static void fhc_middle_set(struct led_classdev *led_cdev,
+ enum led_brightness led_val)
+{
+ __fhc_set(led_cdev, led_val, FHC_CONTROL_MLED);
+}
+
+static void fhc_right_set(struct led_classdev *led_cdev,
+ enum led_brightness led_val)
+{
+ __fhc_set(led_cdev, led_val, FHC_CONTROL_RLED);
+}
+
+typedef void (*set_handler)(struct led_classdev *, enum led_brightness);
+struct led_type {
+ const char *name;
+ set_handler handler;
+ const char *default_trigger;
+};
+
+#define NUM_LEDS_PER_BOARD 3
+struct sunfire_drvdata {
+ struct sunfire_led leds[NUM_LEDS_PER_BOARD];
+};
+
+static int __devinit sunfire_led_generic_probe(struct platform_device *pdev,
+ struct led_type *types)
+{
+ struct sunfire_drvdata *p;
+ int i, err = -EINVAL;
+
+ if (pdev->num_resources != 1) {
+ printk(KERN_ERR PFX "Wrong number of resources %d, should be 1\n",
+ pdev->num_resources);
+ goto out;
+ }
+
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p) {
+ printk(KERN_ERR PFX "Could not allocate struct sunfire_drvdata\n");
+ goto out;
+ }
+
+ for (i = 0; i < NUM_LEDS_PER_BOARD; i++) {
+ struct led_classdev *lp = &p->leds[i].led_cdev;
+
+ p->leds[i].reg = (void __iomem *) pdev->resource[0].start;
+ lp->name = types[i].name;
+ lp->brightness = LED_FULL;
+ lp->brightness_set = types[i].handler;
+ lp->default_trigger = types[i].default_trigger;
+
+ err = led_classdev_register(&pdev->dev, lp);
+ if (err) {
+ printk(KERN_ERR PFX "Could not register %s LED\n",
+ lp->name);
+ goto out_unregister_led_cdevs;
+ }
+ }
+
+ dev_set_drvdata(&pdev->dev, p);
+
+ err = 0;
+out:
+ return err;
+
+out_unregister_led_cdevs:
+ for (i--; i >= 0; i--)
+ led_classdev_unregister(&p->leds[i].led_cdev);
+ goto out;
+}
+
+static int __devexit sunfire_led_generic_remove(struct platform_device *pdev)
+{
+ struct sunfire_drvdata *p = dev_get_drvdata(&pdev->dev);
+ int i;
+
+ for (i = 0; i < NUM_LEDS_PER_BOARD; i++)
+ led_classdev_unregister(&p->leds[i].led_cdev);
+
+ kfree(p);
+
+ return 0;
+}
+
+static struct led_type clockboard_led_types[NUM_LEDS_PER_BOARD] = {
+ {
+ .name = "clockboard-left",
+ .handler = clockboard_left_set,
+ },
+ {
+ .name = "clockboard-middle",
+ .handler = clockboard_middle_set,
+ },
+ {
+ .name = "clockboard-right",
+ .handler = clockboard_right_set,
+ .default_trigger= "heartbeat",
+ },
+};
+
+static int __devinit sunfire_clockboard_led_probe(struct platform_device *pdev)
+{
+ return sunfire_led_generic_probe(pdev, clockboard_led_types);
+}
+
+static struct led_type fhc_led_types[NUM_LEDS_PER_BOARD] = {
+ {
+ .name = "fhc-left",
+ .handler = fhc_left_set,
+ },
+ {
+ .name = "fhc-middle",
+ .handler = fhc_middle_set,
+ },
+ {
+ .name = "fhc-right",
+ .handler = fhc_right_set,
+ .default_trigger= "heartbeat",
+ },
+};
+
+static int __devinit sunfire_fhc_led_probe(struct platform_device *pdev)
+{
+ return sunfire_led_generic_probe(pdev, fhc_led_types);
+}
+
+MODULE_ALIAS("platform:sunfire-clockboard-leds");
+MODULE_ALIAS("platform:sunfire-fhc-leds");
+
+static struct platform_driver sunfire_clockboard_led_driver = {
+ .probe = sunfire_clockboard_led_probe,
+ .remove = __devexit_p(sunfire_led_generic_remove),
+ .driver = {
+ .name = "sunfire-clockboard-leds",
+ .owner = THIS_MODULE,
+ },
+};
+
+static struct platform_driver sunfire_fhc_led_driver = {
+ .probe = sunfire_fhc_led_probe,
+ .remove = __devexit_p(sunfire_led_generic_remove),
+ .driver = {
+ .name = "sunfire-fhc-leds",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init sunfire_leds_init(void)
+{
+ int err = platform_driver_register(&sunfire_clockboard_led_driver);
+
+ if (err) {
+ printk(KERN_ERR PFX "Could not register clock board LED driver\n");
+ return err;
+ }
+
+ err = platform_driver_register(&sunfire_fhc_led_driver);
+ if (err) {
+ printk(KERN_ERR PFX "Could not register FHC LED driver\n");
+ platform_driver_unregister(&sunfire_clockboard_led_driver);
+ }
+
+ return err;
+}
+
+static void __exit sunfire_leds_exit(void)
+{
+ platform_driver_unregister(&sunfire_clockboard_led_driver);
+ platform_driver_unregister(&sunfire_fhc_led_driver);
+}
+
+module_init(sunfire_leds_init);
+module_exit(sunfire_leds_exit);
diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c
index 8fa91f8..4952aeb 100644
--- a/drivers/media/common/ir-keymaps.c
+++ b/drivers/media/common/ir-keymaps.c
@@ -103,6 +103,56 @@
EXPORT_SYMBOL_GPL(ir_codes_avermedia_dvbt);
+/* Mauro Carvalho Chehab <mchehab@infradead.org> */
+IR_KEYTAB_TYPE ir_codes_avermedia_m135a[IR_KEYTAB_SIZE] = {
+ [0x00] = KEY_POWER2,
+ [0x2e] = KEY_DOT, /* '.' */
+ [0x01] = KEY_MODE, /* TV/FM */
+
+ [0x05] = KEY_1,
+ [0x06] = KEY_2,
+ [0x07] = KEY_3,
+ [0x09] = KEY_4,
+ [0x0a] = KEY_5,
+ [0x0b] = KEY_6,
+ [0x0d] = KEY_7,
+ [0x0e] = KEY_8,
+ [0x0f] = KEY_9,
+ [0x11] = KEY_0,
+
+ [0x13] = KEY_RIGHT, /* -> */
+ [0x12] = KEY_LEFT, /* <- */
+
+ [0x17] = KEY_SLEEP, /* Capturar Imagem */
+ [0x10] = KEY_SHUFFLE, /* Amostra */
+
+ /* FIXME: The keys bellow aren't ok */
+
+ [0x43] = KEY_CHANNELUP,
+ [0x42] = KEY_CHANNELDOWN,
+ [0x1f] = KEY_VOLUMEUP,
+ [0x1e] = KEY_VOLUMEDOWN,
+ [0x0c] = KEY_ENTER,
+
+ [0x14] = KEY_MUTE,
+ [0x08] = KEY_AUDIO,
+
+ [0x03] = KEY_TEXT,
+ [0x04] = KEY_EPG,
+ [0x2b] = KEY_TV2, /* TV2 */
+
+ [0x1d] = KEY_RED,
+ [0x1c] = KEY_YELLOW,
+ [0x41] = KEY_GREEN,
+ [0x40] = KEY_BLUE,
+
+ [0x1a] = KEY_PLAYPAUSE,
+ [0x19] = KEY_RECORD,
+ [0x18] = KEY_PLAY,
+ [0x1b] = KEY_STOP,
+};
+EXPORT_SYMBOL_GPL(ir_codes_avermedia_m135a);
+
/* Attila Kondoros <attila.kondoros@chello.hu> */
IR_KEYTAB_TYPE ir_codes_apac_viewcomp[IR_KEYTAB_SIZE] = {
@@ -467,7 +517,8 @@
/* ---------------------------------------------------------------------- */
-/* MSI TV@nywhere remote */
+/* MSI TV@nywhere MASTER remote */
+
IR_KEYTAB_TYPE ir_codes_msi_tvanywhere[IR_KEYTAB_SIZE] = {
/* Keys 0 to 9 */
[ 0x00 ] = KEY_0,
@@ -501,6 +552,95 @@
/* ---------------------------------------------------------------------- */
+/*
+ Keycodes for remote on the MSI TV@nywhere Plus. The controller IC on the card
+ is marked "KS003". The controller is I2C at address 0x30, but does not seem
+ to respond to probes until a read is performed from a valid device.
+ I don't know why...
+
+ Note: This remote may be of similar or identical design to the
+ Pixelview remote (?). The raw codes and duplicate button codes
+ appear to be the same.
+
+ Henry Wong <henry@stuffedcow.net>
+ Some changes to formatting and keycodes by Mark Schultz <n9xmj@yahoo.com>
+
+*/
+
+IR_KEYTAB_TYPE ir_codes_msi_tvanywhere_plus[IR_KEYTAB_SIZE] = {
+
+/* ---- Remote Button Layout ----
+
+ POWER SOURCE SCAN MUTE
+ TV/FM 1 2 3
+ |> 4 5 6
+ <| 7 8 9
+ ^^UP 0 + RECALL
+ vvDN RECORD STOP PLAY
+
+ MINIMIZE ZOOM
+
+ CH+
+ VOL- VOL+
+ CH-
+
+ SNAPSHOT MTS
+
+ << FUNC >> RESET
+*/
+
+ [0x01] = KEY_KP1, /* 1 */
+ [0x0b] = KEY_KP2, /* 2 */
+ [0x1b] = KEY_KP3, /* 3 */
+ [0x05] = KEY_KP4, /* 4 */
+ [0x09] = KEY_KP5, /* 5 */
+ [0x15] = KEY_KP6, /* 6 */
+ [0x06] = KEY_KP7, /* 7 */
+ [0x0a] = KEY_KP8, /* 8 */
+ [0x12] = KEY_KP9, /* 9 */
+ [0x02] = KEY_KP0, /* 0 */
+ [0x10] = KEY_KPPLUS, /* + */
+ [0x13] = KEY_AGAIN, /* Recall */
+
+ [0x1e] = KEY_POWER, /* Power */
+ [0x07] = KEY_TUNER, /* Source */
+ [0x1c] = KEY_SEARCH, /* Scan */
+ [0x18] = KEY_MUTE, /* Mute */
+
+ [0x03] = KEY_RADIO, /* TV/FM */
+ /* The next four keys are duplicates that appear to send the
+ same IR code as Ch+, Ch-, >>, and << . The raw code assigned
+ to them is the actual code + 0x20 - they will never be
+ detected as such unless some way is discovered to distinguish
+ these buttons from those that have the same code. */
+ [0x3f] = KEY_RIGHT, /* |> and Ch+ */
+ [0x37] = KEY_LEFT, /* <| and Ch- */
+ [0x2c] = KEY_UP, /* ^^Up and >> */
+ [0x24] = KEY_DOWN, /* vvDn and << */
+
+ [0x00] = KEY_RECORD, /* Record */
+ [0x08] = KEY_STOP, /* Stop */
+ [0x11] = KEY_PLAY, /* Play */
+
+ [0x0f] = KEY_CLOSE, /* Minimize */
+ [0x19] = KEY_ZOOM, /* Zoom */
+ [0x1a] = KEY_SHUFFLE, /* Snapshot */
+ [0x0d] = KEY_LANGUAGE, /* MTS */
+
+ [0x14] = KEY_VOLUMEDOWN, /* Vol- */
+ [0x16] = KEY_VOLUMEUP, /* Vol+ */
+ [0x17] = KEY_CHANNELDOWN, /* Ch- */
+ [0x1f] = KEY_CHANNELUP, /* Ch+ */
+
+ [0x04] = KEY_REWIND, /* << */
+ [0x0e] = KEY_MENU, /* Function */
+ [0x0c] = KEY_FASTFORWARD, /* >> */
+ [0x1d] = KEY_RESTART, /* Reset */
+};
+EXPORT_SYMBOL_GPL(ir_codes_msi_tvanywhere_plus);
+
+/* ---------------------------------------------------------------------- */
+
/* Cinergy 1400 DVB-T */
IR_KEYTAB_TYPE ir_codes_cinergy_1400[IR_KEYTAB_SIZE] = {
[ 0x01 ] = KEY_POWER,
@@ -1792,12 +1932,61 @@
[ 0x41 ] = KEY_GREEN, /* AP2 */
[ 0x47 ] = KEY_YELLOW, /* AP3 */
[ 0x57 ] = KEY_BLUE, /* AP4 */
-
-
};
-
EXPORT_SYMBOL_GPL(ir_codes_encore_enltv);
+/* Encore ENLTV2-FM - silver plastic - "Wand Media" written at the botton
+ Mauro Carvalho Chehab <mchehab@infradead.org> */
+IR_KEYTAB_TYPE ir_codes_encore_enltv2[IR_KEYTAB_SIZE] = {
+ [0x4c] = KEY_POWER2,
+ [0x4a] = KEY_TUNER,
+ [0x40] = KEY_1,
+ [0x60] = KEY_2,
+ [0x50] = KEY_3,
+ [0x70] = KEY_4,
+ [0x48] = KEY_5,
+ [0x68] = KEY_6,
+ [0x58] = KEY_7,
+ [0x78] = KEY_8,
+ [0x44] = KEY_9,
+ [0x54] = KEY_0,
+
+ [0x64] = KEY_LAST, /* +100 */
+ [0x4e] = KEY_AGAIN, /* Recall */
+
+ [0x6c] = KEY_SWITCHVIDEOMODE, /* Video Source */
+ [0x5e] = KEY_MENU,
+ [0x56] = KEY_SCREEN,
+ [0x7a] = KEY_SETUP,
+
+ [0x46] = KEY_MUTE,
+ [0x5c] = KEY_MODE, /* Stereo */
+ [0x74] = KEY_INFO,
+ [0x7c] = KEY_CLEAR,
+
+ [0x55] = KEY_UP,
+ [0x49] = KEY_DOWN,
+ [0x7e] = KEY_LEFT,
+ [0x59] = KEY_RIGHT,
+ [0x6a] = KEY_ENTER,
+
+ [0x42] = KEY_VOLUMEUP,
+ [0x62] = KEY_VOLUMEDOWN,
+ [0x52] = KEY_CHANNELUP,
+ [0x72] = KEY_CHANNELDOWN,
+
+ [0x41] = KEY_RECORD,
+ [0x51] = KEY_SHUFFLE, /* Snapshot */
+ [0x75] = KEY_TIME, /* Timeshift */
+ [0x71] = KEY_TV2, /* PIP */
+
+ [0x45] = KEY_REWIND,
+ [0x6f] = KEY_PAUSE,
+ [0x7d] = KEY_FORWARD,
+ [0x79] = KEY_STOP,
+};
+EXPORT_SYMBOL_GPL(ir_codes_encore_enltv2);
+
/* for the Technotrend 1500 bundled remotes (grey and black): */
IR_KEYTAB_TYPE ir_codes_tt_1500[IR_KEYTAB_SIZE] = {
[ 0x01 ] = KEY_POWER,
@@ -2239,3 +2428,86 @@
[0x2a] = KEY_MENU,
};
EXPORT_SYMBOL_GPL(ir_codes_avermedia_a16d);
+
+/* Encore ENLTV-FM v5.3
+ Mauro Carvalho Chehab <mchehab@infradead.org>
+ */
+IR_KEYTAB_TYPE ir_codes_encore_enltv_fm53[IR_KEYTAB_SIZE] = {
+ [0x10] = KEY_POWER2,
+ [0x06] = KEY_MUTE,
+
+ [0x09] = KEY_1,
+ [0x1d] = KEY_2,
+ [0x1f] = KEY_3,
+ [0x19] = KEY_4,
+ [0x1b] = KEY_5,
+ [0x11] = KEY_6,
+ [0x17] = KEY_7,
+ [0x12] = KEY_8,
+ [0x16] = KEY_9,
+ [0x48] = KEY_0,
+
+ [0x04] = KEY_LIST, /* -/-- */
+ [0x40] = KEY_LAST, /* recall */
+
+ [0x02] = KEY_MODE, /* TV/AV */
+ [0x05] = KEY_SHUFFLE, /* SNAPSHOT */
+
+ [0x4c] = KEY_CHANNELUP, /* UP */
+ [0x00] = KEY_CHANNELDOWN, /* DOWN */
+ [0x0d] = KEY_VOLUMEUP, /* RIGHT */
+ [0x15] = KEY_VOLUMEDOWN, /* LEFT */
+ [0x49] = KEY_ENTER, /* OK */
+
+ [0x54] = KEY_RECORD,
+ [0x4d] = KEY_PLAY, /* pause */
+
+ [0x1e] = KEY_UP, /* video setting */
+ [0x0e] = KEY_RIGHT, /* <- */
+ [0x1a] = KEY_LEFT, /* -> */
+
+ [0x0a] = KEY_DOWN, /* video default */
+ [0x0c] = KEY_ZOOM, /* hide pannel */
+ [0x47] = KEY_SLEEP, /* shutdown */
+};
+EXPORT_SYMBOL_GPL(ir_codes_encore_enltv_fm53);
+
+/* Zogis Real Audio 220 - 32 keys IR */
+IR_KEYTAB_TYPE ir_codes_real_audio_220_32_keys[IR_KEYTAB_SIZE] = {
+ [0x1c] = KEY_RADIO,
+ [0x12] = KEY_POWER2,
+
+ [0x01] = KEY_1,
+ [0x02] = KEY_2,
+ [0x03] = KEY_3,
+ [0x04] = KEY_4,
+ [0x05] = KEY_5,
+ [0x06] = KEY_6,
+ [0x07] = KEY_7,
+ [0x08] = KEY_8,
+ [0x09] = KEY_9,
+ [0x00] = KEY_0,
+
+ [0x0c] = KEY_VOLUMEUP,
+ [0x18] = KEY_VOLUMEDOWN,
+ [0x0b] = KEY_CHANNELUP,
+ [0x15] = KEY_CHANNELDOWN,
+ [0x16] = KEY_ENTER,
+
+ [0x11] = KEY_LIST, /* Source */
+ [0x0d] = KEY_AUDIO, /* stereo */
+
+ [0x0f] = KEY_PREVIOUS, /* Prev */
+ [0x1b] = KEY_PAUSE, /* Timeshift */
+ [0x1a] = KEY_NEXT, /* Next */
+
+ [0x0e] = KEY_STOP,
+ [0x1f] = KEY_PLAY,
+ [0x1e] = KEY_PLAYPAUSE, /* Pause */
+
+ [0x1d] = KEY_RECORD,
+ [0x13] = KEY_MUTE,
+ [0x19] = KEY_SHUFFLE, /* Snapshot */
+
+};
+EXPORT_SYMBOL_GPL(ir_codes_real_audio_220_32_keys);
diff --git a/drivers/media/common/saa7146_core.c b/drivers/media/common/saa7146_core.c
index d01965e..d599d36 100644
--- a/drivers/media/common/saa7146_core.c
+++ b/drivers/media/common/saa7146_core.c
@@ -234,7 +234,7 @@
int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt)
{
__le32 *cpu;
- dma_addr_t dma_addr;
+ dma_addr_t dma_addr = 0;
cpu = pci_alloc_consistent(pci, PAGE_SIZE, &dma_addr);
if (NULL == cpu) {
diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c
index cf6a817..5b34c13 100644
--- a/drivers/media/common/saa7146_fops.c
+++ b/drivers/media/common/saa7146_fops.c
@@ -533,7 +533,7 @@
memcpy(vfd, &device_template, sizeof(struct video_device));
strlcpy(vfd->name, name, sizeof(vfd->name));
vfd->release = video_device_release;
- vfd->priv = dev;
+ video_set_drvdata(vfd, dev);
// fixme: -1 should be an insmod parameter *for the extension* (like "video_nr");
if (video_register_device(vfd, type, -1) < 0) {
diff --git a/drivers/media/common/tuners/mt2060.c b/drivers/media/common/tuners/mt2060.c
index 1305b0e..12206d7 100644
--- a/drivers/media/common/tuners/mt2060.c
+++ b/drivers/media/common/tuners/mt2060.c
@@ -170,6 +170,9 @@
b[0] = REG_LO1B1;
b[1] = 0xFF;
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
+
mt2060_writeregs(priv,b,2);
freq = params->frequency / 1000; // Hz -> kHz
@@ -233,6 +236,9 @@
i++;
} while (i<10);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
+
return ret;
}
@@ -296,13 +302,35 @@
static int mt2060_init(struct dvb_frontend *fe)
{
struct mt2060_priv *priv = fe->tuner_priv;
- return mt2060_writereg(priv, REG_VGAG, (priv->cfg->clock_out << 6) | 0x33);
+ int ret;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
+
+ ret = mt2060_writereg(priv, REG_VGAG,
+ (priv->cfg->clock_out << 6) | 0x33);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
+
+ return ret;
}
static int mt2060_sleep(struct dvb_frontend *fe)
{
struct mt2060_priv *priv = fe->tuner_priv;
- return mt2060_writereg(priv, REG_VGAG, (priv->cfg->clock_out << 6) | 0x30);
+ int ret;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
+
+ ret = mt2060_writereg(priv, REG_VGAG,
+ (priv->cfg->clock_out << 6) | 0x30);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
+
+ return ret;
}
static int mt2060_release(struct dvb_frontend *fe)
@@ -344,6 +372,9 @@
priv->i2c = i2c;
priv->if1_freq = if1;
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
+
if (mt2060_readreg(priv,REG_PART_REV,&id) != 0) {
kfree(priv);
return NULL;
@@ -360,6 +391,9 @@
mt2060_calibrate(priv);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
+
return fe;
}
EXPORT_SYMBOL(mt2060_attach);
diff --git a/drivers/media/common/tuners/mxl5007t.c b/drivers/media/common/tuners/mxl5007t.c
index cb25e43..64379f2 100644
--- a/drivers/media/common/tuners/mxl5007t.c
+++ b/drivers/media/common/tuners/mxl5007t.c
@@ -979,7 +979,6 @@
switch (instance) {
case 0:
goto fail;
- break;
case 1:
/* new tuner instance */
state->config = cfg;
diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c
index 93063c6..1b48b5d 100644
--- a/drivers/media/common/tuners/tda18271-fe.c
+++ b/drivers/media/common/tuners/tda18271-fe.c
@@ -1155,7 +1155,6 @@
switch (instance) {
case 0:
goto fail;
- break;
case 1:
/* new tuner instance */
priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO;
diff --git a/drivers/media/common/tuners/tda827x.c b/drivers/media/common/tuners/tda827x.c
index 8555d9c..4a74f65 100644
--- a/drivers/media/common/tuners/tda827x.c
+++ b/drivers/media/common/tuners/tda827x.c
@@ -447,17 +447,19 @@
else
arg = 0;
}
- if (priv->cfg->tuner_callback)
- priv->cfg->tuner_callback(priv->i2c_adap->algo_data,
- gp_func, arg);
+ if (fe->callback)
+ fe->callback(priv->i2c_adap->algo_data,
+ DVB_FRONTEND_COMPONENT_TUNER,
+ gp_func, arg);
buf[1] = high ? 0 : 1;
if (priv->cfg->config == 2)
buf[1] = high ? 1 : 0;
i2c_transfer(priv->i2c_adap, &msg, 1);
break;
case 3: /* switch with GPIO of saa713x */
- if (priv->cfg->tuner_callback)
- priv->cfg->tuner_callback(priv->i2c_adap->algo_data, 0, high);
+ if (fe->callback)
+ fe->callback(priv->i2c_adap->algo_data,
+ DVB_FRONTEND_COMPONENT_TUNER, 0, high);
break;
}
}
diff --git a/drivers/media/common/tuners/tda827x.h b/drivers/media/common/tuners/tda827x.h
index 7850a9a..7d72ce0 100644
--- a/drivers/media/common/tuners/tda827x.h
+++ b/drivers/media/common/tuners/tda827x.h
@@ -36,7 +36,6 @@
/* interface to tda829x driver */
unsigned int config;
int switch_addr;
- int (*tuner_callback) (void *dev, int command, int arg);
void (*agcf)(struct dvb_frontend *fe);
};
diff --git a/drivers/media/common/tuners/tda8290.c b/drivers/media/common/tuners/tda8290.c
index 91204d3..c112bdd 100644
--- a/drivers/media/common/tuners/tda8290.c
+++ b/drivers/media/common/tuners/tda8290.c
@@ -672,10 +672,8 @@
priv->i2c_props.addr = i2c_addr;
priv->i2c_props.adap = i2c_adap;
priv->i2c_props.name = "tda829x";
- if (cfg) {
+ if (cfg)
priv->cfg.config = cfg->lna_cfg;
- priv->cfg.tuner_callback = cfg->tuner_callback;
- }
if (tda8290_probe(&priv->i2c_props) == 0) {
priv->ver = TDA8290;
diff --git a/drivers/media/common/tuners/tda8290.h b/drivers/media/common/tuners/tda8290.h
index aa074f3..7e288b2 100644
--- a/drivers/media/common/tuners/tda8290.h
+++ b/drivers/media/common/tuners/tda8290.h
@@ -22,7 +22,6 @@
struct tda829x_config {
unsigned int lna_cfg;
- int (*tuner_callback) (void *dev, int command, int arg);
unsigned int probe_tuner:1;
#define TDA829X_PROBE_TUNER 0
diff --git a/drivers/media/common/tuners/tda9887.c b/drivers/media/common/tuners/tda9887.c
index 72abf0b..ff1788c 100644
--- a/drivers/media/common/tuners/tda9887.c
+++ b/drivers/media/common/tuners/tda9887.c
@@ -686,7 +686,6 @@
case 0:
mutex_unlock(&tda9887_list_mutex);
return NULL;
- break;
case 1:
fe->analog_demod_priv = priv;
priv->mode = T_STANDBY;
diff --git a/drivers/media/common/tuners/tuner-simple.c b/drivers/media/common/tuners/tuner-simple.c
index aa773a6..2a1aac1 100644
--- a/drivers/media/common/tuners/tuner-simple.c
+++ b/drivers/media/common/tuners/tuner-simple.c
@@ -142,6 +142,7 @@
case TUNER_PHILIPS_FM1236_MK3:
case TUNER_PHILIPS_FM1256_IH3:
case TUNER_LG_NTSC_TAPE:
+ case TUNER_TCL_MF02GIP_5N:
return ((status & TUNER_SIGNAL) == TUNER_STEREO_MK3);
default:
return status & TUNER_STEREO;
@@ -494,6 +495,7 @@
case TUNER_PHILIPS_FMD1216ME_MK3:
case TUNER_LG_NTSC_TAPE:
case TUNER_PHILIPS_FM1256_IH3:
+ case TUNER_TCL_MF02GIP_5N:
buffer[3] = 0x19;
break;
case TUNER_TNF_5335MF:
@@ -1038,7 +1040,6 @@
case 0:
mutex_unlock(&tuner_simple_list_mutex);
return NULL;
- break;
case 1:
fe->tuner_priv = priv;
diff --git a/drivers/media/common/tuners/tuner-types.c b/drivers/media/common/tuners/tuner-types.c
index 10dddca..04961a1 100644
--- a/drivers/media/common/tuners/tuner-types.c
+++ b/drivers/media/common/tuners/tuner-types.c
@@ -1216,6 +1216,23 @@
},
};
+/* ------------ TUNER_TCL_MF02GIP-5N-E - TCL MF02GIP-5N ------------ */
+
+static struct tuner_range tuner_tcl_mf02gip_5n_ntsc_ranges[] = {
+ { 16 * 172.00 /*MHz*/, 0x8e, 0x01, },
+ { 16 * 448.00 /*MHz*/, 0x8e, 0x02, },
+ { 16 * 999.99 , 0x8e, 0x04, },
+};
+
+static struct tuner_params tuner_tcl_mf02gip_5n_params[] = {
+ {
+ .type = TUNER_PARAM_TYPE_NTSC,
+ .ranges = tuner_tcl_mf02gip_5n_ntsc_ranges,
+ .count = ARRAY_SIZE(tuner_tcl_mf02gip_5n_ntsc_ranges),
+ .cb_first_if_lower_freq = 1,
+ },
+};
+
/* --------------------------------------------------------------------- */
struct tunertype tuners[] = {
@@ -1641,6 +1658,11 @@
.name = "Xceive 5000 tuner",
/* see xc5000.c for details */
},
+ [TUNER_TCL_MF02GIP_5N] = { /* TCL tuner MF02GIP-5N-E */
+ .name = "TCL tuner MF02GIP-5N-E",
+ .params = tuner_tcl_mf02gip_5n_params,
+ .count = ARRAY_SIZE(tuner_tcl_mf02gip_5n_params),
+ },
};
EXPORT_SYMBOL(tuners);
diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c
index 4dd1d24..b65e680 100644
--- a/drivers/media/common/tuners/tuner-xc2028.c
+++ b/drivers/media/common/tuners/tuner-xc2028.c
@@ -71,9 +71,6 @@
struct xc2028_data {
struct list_head hybrid_tuner_instance_list;
struct tuner_i2c_props i2c_props;
- int (*tuner_callback) (void *dev,
- int command, int arg);
- void *video_dev;
__u32 frequency;
struct firmware_description *firm;
@@ -492,6 +489,23 @@
return i;
}
+static inline int do_tuner_callback(struct dvb_frontend *fe, int cmd, int arg)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+
+ /* analog side (tuner-core) uses i2c_adap->algo_data.
+ * digital side is not guaranteed to have algo_data defined.
+ *
+ * digital side will always have fe->dvb defined.
+ * analog side (tuner-core) doesn't (yet) define fe->dvb.
+ */
+
+ return (!fe->callback) ? -EINVAL :
+ fe->callback(((fe->dvb) && (fe->dvb->priv)) ?
+ fe->dvb->priv : priv->i2c_props.adap->algo_data,
+ DVB_FRONTEND_COMPONENT_TUNER, cmd, arg);
+}
+
static int load_firmware(struct dvb_frontend *fe, unsigned int type,
v4l2_std_id *id)
{
@@ -530,8 +544,7 @@
if (!size) {
/* Special callback command received */
- rc = priv->tuner_callback(priv->video_dev,
- XC2028_TUNER_RESET, 0);
+ rc = do_tuner_callback(fe, XC2028_TUNER_RESET, 0);
if (rc < 0) {
tuner_err("Error at RESET code %d\n",
(*p) & 0x7f);
@@ -542,8 +555,7 @@
if (size >= 0xff00) {
switch (size) {
case 0xff00:
- rc = priv->tuner_callback(priv->video_dev,
- XC2028_RESET_CLK, 0);
+ rc = do_tuner_callback(fe, XC2028_RESET_CLK, 0);
if (rc < 0) {
tuner_err("Error at RESET code %d\n",
(*p) & 0x7f);
@@ -715,8 +727,7 @@
memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
/* Reset is needed before loading firmware */
- rc = priv->tuner_callback(priv->video_dev,
- XC2028_TUNER_RESET, 0);
+ rc = do_tuner_callback(fe, XC2028_TUNER_RESET, 0);
if (rc < 0)
goto fail;
@@ -933,7 +944,7 @@
The reset CLK is needed only with tm6000.
Driver should work fine even if this fails.
*/
- priv->tuner_callback(priv->video_dev, XC2028_RESET_CLK, 1);
+ do_tuner_callback(fe, XC2028_RESET_CLK, 1);
msleep(10);
@@ -1002,11 +1013,6 @@
tuner_dbg("%s called\n", __func__);
- if (priv->ctrl.d2633)
- type |= D2633;
- else
- type |= D2620;
-
switch(fe->ops.info.type) {
case FE_OFDM:
bw = p->u.ofdm.bandwidth;
@@ -1021,10 +1027,8 @@
break;
case FE_ATSC:
bw = BANDWIDTH_6_MHZ;
- /* The only ATSC firmware (at least on v2.7) is D2633,
- so overrides ctrl->d2633 */
- type |= ATSC| D2633;
- type &= ~D2620;
+ /* The only ATSC firmware (at least on v2.7) is D2633 */
+ type |= ATSC | D2633;
break;
/* DVB-S is not supported */
default:
@@ -1057,6 +1061,28 @@
tuner_err("error: bandwidth not supported.\n");
};
+ /*
+ Selects between D2633 or D2620 firmware.
+ It doesn't make sense for ATSC, since it should be D2633 on all cases
+ */
+ if (fe->ops.info.type != FE_ATSC) {
+ switch (priv->ctrl.type) {
+ case XC2028_D2633:
+ type |= D2633;
+ break;
+ case XC2028_D2620:
+ type |= D2620;
+ break;
+ case XC2028_AUTO:
+ default:
+ /* Zarlink seems to need D2633 */
+ if (priv->ctrl.demod == XC3028_FE_ZARLINK456)
+ type |= D2633;
+ else
+ type |= D2620;
+ }
+ }
+
/* All S-code tables need a 200kHz shift */
if (priv->ctrl.demod)
demod = priv->ctrl.demod + 200;
@@ -1177,20 +1203,10 @@
break;
case 1:
/* new tuner instance */
- priv->tuner_callback = cfg->callback;
priv->ctrl.max_len = 13;
mutex_init(&priv->lock);
- /* analog side (tuner-core) uses i2c_adap->algo_data.
- * digital side is not guaranteed to have algo_data defined.
- *
- * digital side will always have fe->dvb defined.
- * analog side (tuner-core) doesn't (yet) define fe->dvb.
- */
- priv->video_dev = ((fe->dvb) && (fe->dvb->priv)) ?
- fe->dvb->priv : cfg->i2c_adap->algo_data;
-
fe->tuner_priv = priv;
break;
case 2:
diff --git a/drivers/media/common/tuners/tuner-xc2028.h b/drivers/media/common/tuners/tuner-xc2028.h
index 2c5b628..19de792 100644
--- a/drivers/media/common/tuners/tuner-xc2028.h
+++ b/drivers/media/common/tuners/tuner-xc2028.h
@@ -24,24 +24,28 @@
#define XC3028_FE_ZARLINK456 4560
#define XC3028_FE_CHINA 5200
+enum firmware_type {
+ XC2028_AUTO = 0, /* By default, auto-detects */
+ XC2028_D2633,
+ XC2028_D2620,
+};
+
struct xc2028_ctrl {
char *fname;
int max_len;
unsigned int scode_table;
unsigned int mts :1;
- unsigned int d2633 :1;
unsigned int input1:1;
unsigned int vhfbw7:1;
unsigned int uhfbw8:1;
unsigned int demod;
+ enum firmware_type type:2;
};
struct xc2028_config {
struct i2c_adapter *i2c_adap;
u8 i2c_addr;
- void *video_dev;
struct xc2028_ctrl *ctrl;
- int (*callback) (void *dev, int command, int arg);
};
/* xc2028 commands for callback */
diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c
index dcddfa8..f9c2bb9 100644
--- a/drivers/media/common/tuners/xc5000.c
+++ b/drivers/media/common/tuners/xc5000.c
@@ -30,7 +30,7 @@
#include "dvb_frontend.h"
#include "xc5000.h"
-#include "xc5000_priv.h"
+#include "tuner-i2c.h"
static int debug;
module_param(debug, int, 0644);
@@ -40,12 +40,26 @@
module_param_named(init_fw, xc5000_load_fw_on_attach, int, 0644);
MODULE_PARM_DESC(init_fw, "Load firmware during driver initialization.");
+static DEFINE_MUTEX(xc5000_list_mutex);
+static LIST_HEAD(hybrid_tuner_instance_list);
+
#define dprintk(level,fmt, arg...) if (debug >= level) \
printk(KERN_INFO "%s: " fmt, "xc5000", ## arg)
#define XC5000_DEFAULT_FIRMWARE "dvb-fe-xc5000-1.1.fw"
#define XC5000_DEFAULT_FIRMWARE_SIZE 12332
+struct xc5000_priv {
+ struct tuner_i2c_props i2c_props;
+ struct list_head hybrid_tuner_instance_list;
+
+ u32 if_khz;
+ u32 freq_hz;
+ u32 bandwidth;
+ u8 video_standard;
+ u8 rf_mode;
+};
+
/* Misc Defines */
#define MAX_TV_STANDARD 23
#define XC_MAX_I2C_WRITE_LENGTH 64
@@ -216,9 +230,12 @@
dprintk(1, "%s()\n", __func__);
- if (priv->cfg->tuner_callback) {
- ret = priv->cfg->tuner_callback(priv->devptr,
- XC5000_TUNER_RESET, 0);
+ if (fe->callback) {
+ ret = fe->callback(((fe->dvb) && (fe->dvb->priv)) ?
+ fe->dvb->priv :
+ priv->i2c_props.adap->algo_data,
+ DVB_FRONTEND_COMPONENT_TUNER,
+ XC5000_TUNER_RESET, 0);
if (ret)
printk(KERN_ERR "xc5000: reset failed\n");
} else
@@ -509,13 +526,13 @@
u8 buf[2] = { reg >> 8, reg & 0xff };
u8 bval[2] = { 0, 0 };
struct i2c_msg msg[2] = {
- { .addr = priv->cfg->i2c_address,
+ { .addr = priv->i2c_props.addr,
.flags = 0, .buf = &buf[0], .len = 2 },
- { .addr = priv->cfg->i2c_address,
+ { .addr = priv->i2c_props.addr,
.flags = I2C_M_RD, .buf = &bval[0], .len = 2 },
};
- if (i2c_transfer(priv->i2c, msg, 2) != 2) {
+ if (i2c_transfer(priv->i2c_props.adap, msg, 2) != 2) {
printk(KERN_WARNING "xc5000: I2C read failed\n");
return -EREMOTEIO;
}
@@ -526,10 +543,10 @@
static int xc5000_writeregs(struct xc5000_priv *priv, u8 *buf, u8 len)
{
- struct i2c_msg msg = { .addr = priv->cfg->i2c_address,
+ struct i2c_msg msg = { .addr = priv->i2c_props.addr,
.flags = 0, .buf = buf, .len = len };
- if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
+ if (i2c_transfer(priv->i2c_props.adap, &msg, 1) != 1) {
printk(KERN_ERR "xc5000: I2C write failed (len=%i)\n",
(int)len);
return -EREMOTEIO;
@@ -539,10 +556,10 @@
static int xc5000_readregs(struct xc5000_priv *priv, u8 *buf, u8 len)
{
- struct i2c_msg msg = { .addr = priv->cfg->i2c_address,
+ struct i2c_msg msg = { .addr = priv->i2c_props.addr,
.flags = I2C_M_RD, .buf = buf, .len = len };
- if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
+ if (i2c_transfer(priv->i2c_props.adap, &msg, 1) != 1) {
printk(KERN_ERR "xc5000 I2C read failed (len=%i)\n",(int)len);
return -EREMOTEIO;
}
@@ -559,7 +576,7 @@
printk(KERN_INFO "xc5000: waiting for firmware upload (%s)...\n",
XC5000_DEFAULT_FIRMWARE);
- ret = request_firmware(&fw, XC5000_DEFAULT_FIRMWARE, &priv->i2c->dev);
+ ret = request_firmware(&fw, XC5000_DEFAULT_FIRMWARE, &priv->i2c_props.adap->dev);
if (ret) {
printk(KERN_ERR "xc5000: Upload failed. (file not found?)\n");
ret = XC_RESULT_RESET_FAILURE;
@@ -675,10 +692,10 @@
return -EREMOTEIO;
}
- ret = xc_set_IF_frequency(priv, priv->cfg->if_khz);
+ ret = xc_set_IF_frequency(priv, priv->if_khz);
if (ret != XC_RESULT_SUCCESS) {
printk(KERN_ERR "xc5000: xc_Set_IF_frequency(%d) failed\n",
- priv->cfg->if_khz);
+ priv->if_khz);
return -EIO;
}
@@ -897,9 +914,19 @@
static int xc5000_release(struct dvb_frontend *fe)
{
+ struct xc5000_priv *priv = fe->tuner_priv;
+
dprintk(1, "%s()\n", __func__);
- kfree(fe->tuner_priv);
+
+ mutex_lock(&xc5000_list_mutex);
+
+ if (priv)
+ hybrid_tuner_release_state(priv);
+
+ mutex_unlock(&xc5000_list_mutex);
+
fe->tuner_priv = NULL;
+
return 0;
}
@@ -924,29 +951,43 @@
struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,
struct i2c_adapter *i2c,
- struct xc5000_config *cfg, void *devptr)
+ struct xc5000_config *cfg)
{
struct xc5000_priv *priv = NULL;
+ int instance;
u16 id = 0;
- dprintk(1, "%s()\n", __func__);
+ dprintk(1, "%s(%d-%04x)\n", __func__,
+ i2c ? i2c_adapter_id(i2c) : -1,
+ cfg ? cfg->i2c_address : -1);
- priv = kzalloc(sizeof(struct xc5000_priv), GFP_KERNEL);
- if (priv == NULL)
- return NULL;
+ mutex_lock(&xc5000_list_mutex);
- priv->cfg = cfg;
- priv->bandwidth = BANDWIDTH_6_MHZ;
- priv->i2c = i2c;
- priv->devptr = devptr;
+ instance = hybrid_tuner_request_state(struct xc5000_priv, priv,
+ hybrid_tuner_instance_list,
+ i2c, cfg->i2c_address, "xc5000");
+ switch (instance) {
+ case 0:
+ goto fail;
+ break;
+ case 1:
+ /* new tuner instance */
+ priv->bandwidth = BANDWIDTH_6_MHZ;
+ priv->if_khz = cfg->if_khz;
+
+ fe->tuner_priv = priv;
+ break;
+ default:
+ /* existing tuner instance */
+ fe->tuner_priv = priv;
+ break;
+ }
/* Check if firmware has been loaded. It is possible that another
instance of the driver has loaded the firmware.
*/
- if (xc5000_readreg(priv, XREG_PRODUCT_ID, &id) != 0) {
- kfree(priv);
- return NULL;
- }
+ if (xc5000_readreg(priv, XREG_PRODUCT_ID, &id) != 0)
+ goto fail;
switch(id) {
case XC_PRODUCT_ID_FW_LOADED:
@@ -967,19 +1008,23 @@
printk(KERN_ERR
"xc5000: Device not found at addr 0x%02x (0x%x)\n",
cfg->i2c_address, id);
- kfree(priv);
- return NULL;
+ goto fail;
}
+ mutex_unlock(&xc5000_list_mutex);
+
memcpy(&fe->ops.tuner_ops, &xc5000_tuner_ops,
sizeof(struct dvb_tuner_ops));
- fe->tuner_priv = priv;
-
if (xc5000_load_fw_on_attach)
xc5000_init(fe);
return fe;
+fail:
+ mutex_unlock(&xc5000_list_mutex);
+
+ xc5000_release(fe);
+ return NULL;
}
EXPORT_SYMBOL(xc5000_attach);
diff --git a/drivers/media/common/tuners/xc5000.h b/drivers/media/common/tuners/xc5000.h
index 5389f74..cf1a558 100644
--- a/drivers/media/common/tuners/xc5000.h
+++ b/drivers/media/common/tuners/xc5000.h
@@ -30,8 +30,6 @@
struct xc5000_config {
u8 i2c_address;
u32 if_khz;
-
- int (*tuner_callback) (void *priv, int command, int arg);
};
/* xc5000 callback command */
@@ -49,13 +47,11 @@
(defined(CONFIG_MEDIA_TUNER_XC5000_MODULE) && defined(MODULE))
extern struct dvb_frontend* xc5000_attach(struct dvb_frontend *fe,
struct i2c_adapter *i2c,
- struct xc5000_config *cfg,
- void *devptr);
+ struct xc5000_config *cfg);
#else
static inline struct dvb_frontend* xc5000_attach(struct dvb_frontend *fe,
struct i2c_adapter *i2c,
- struct xc5000_config *cfg,
- void *devptr)
+ struct xc5000_config *cfg)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL;
diff --git a/drivers/media/common/tuners/xc5000_priv.h b/drivers/media/common/tuners/xc5000_priv.h
deleted file mode 100644
index b2a0074..0000000
--- a/drivers/media/common/tuners/xc5000_priv.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Driver for Xceive XC5000 "QAM/8VSB single chip tuner"
- *
- * Copyright (c) 2007 Steven Toth <stoth@linuxtv.org>
- *
- * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef XC5000_PRIV_H
-#define XC5000_PRIV_H
-
-struct xc5000_priv {
- struct xc5000_config *cfg;
- struct i2c_adapter *i2c;
-
- u32 freq_hz;
- u32 bandwidth;
- u8 video_standard;
- u8 rf_mode;
-
- void *devptr;
-};
-
-#endif
diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig
index 8bc1445..0bcd852 100644
--- a/drivers/media/dvb/Kconfig
+++ b/drivers/media/dvb/Kconfig
@@ -20,7 +20,6 @@
source "drivers/media/dvb/dvb-usb/Kconfig"
source "drivers/media/dvb/ttusb-budget/Kconfig"
source "drivers/media/dvb/ttusb-dec/Kconfig"
-source "drivers/media/dvb/cinergyT2/Kconfig"
source "drivers/media/dvb/siano/Kconfig"
comment "Supported FlexCopII (B2C2) Adapters"
@@ -35,6 +34,10 @@
depends on DVB_CORE && PCI && I2C
source "drivers/media/dvb/pluto2/Kconfig"
+comment "Supported SDMC DM1105 Adapters"
+ depends on DVB_CORE && PCI && I2C
+source "drivers/media/dvb/dm1105/Kconfig"
+
comment "Supported DVB Frontends"
depends on DVB_CORE
source "drivers/media/dvb/frontends/Kconfig"
diff --git a/drivers/media/dvb/Makefile b/drivers/media/dvb/Makefile
index d6ba4d1..f91e9eb 100644
--- a/drivers/media/dvb/Makefile
+++ b/drivers/media/dvb/Makefile
@@ -2,4 +2,4 @@
# Makefile for the kernel multimedia device drivers.
#
-obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ cinergyT2/ dvb-usb/ pluto2/ siano/
+obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ dvb-usb/ pluto2/ siano/ dm1105/
diff --git a/drivers/media/dvb/b2c2/flexcop-dma.c b/drivers/media/dvb/b2c2/flexcop-dma.c
index a91ed28..26f0011 100644
--- a/drivers/media/dvb/b2c2/flexcop-dma.c
+++ b/drivers/media/dvb/b2c2/flexcop-dma.c
@@ -10,7 +10,7 @@
int flexcop_dma_allocate(struct pci_dev *pdev, struct flexcop_dma *dma, u32 size)
{
u8 *tcpu;
- dma_addr_t tdma;
+ dma_addr_t tdma = 0;
if (size % 2) {
err("dma buffersize has to be even.");
diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/drivers/media/dvb/bt8xx/dvb-bt8xx.c
index 6afbfbb..48762a2 100644
--- a/drivers/media/dvb/bt8xx/dvb-bt8xx.c
+++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.c
@@ -702,7 +702,7 @@
}
if (card->fe == NULL)
- printk("dvb-bt8xx: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n",
+ printk("dvb-bt8xx: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
card->bt->dev->vendor,
card->bt->dev->device,
card->bt->dev->subsystem_vendor,
diff --git a/drivers/media/dvb/cinergyT2/Kconfig b/drivers/media/dvb/cinergyT2/Kconfig
deleted file mode 100644
index c03513b..0000000
--- a/drivers/media/dvb/cinergyT2/Kconfig
+++ /dev/null
@@ -1,85 +0,0 @@
-config DVB_CINERGYT2
- tristate "Terratec CinergyT2/qanu USB2 DVB-T receiver"
- depends on DVB_CORE && USB && INPUT
- help
- Support for "TerraTec CinergyT2" USB2.0 Highspeed DVB Receivers
-
- Say Y if you own such a device and want to use it.
-
-
-config DVB_CINERGYT2_TUNING
- bool "sophisticated fine-tuning for CinergyT2 cards"
- depends on DVB_CINERGYT2
- help
- Here you can fine-tune some parameters of the CinergyT2 driver.
-
- Normally you don't need to touch this, but in exotic setups you
- may fine-tune your setup and adjust e.g. DMA buffer sizes for
- a particular application.
-
-
-config DVB_CINERGYT2_STREAM_URB_COUNT
- int "Number of queued USB Request Blocks for Highspeed Stream Transfers"
- depends on DVB_CINERGYT2_TUNING
- default "32"
- help
- USB Request Blocks for Highspeed Stream transfers are scheduled in
- a queue for the Host Controller.
-
- Usually the default value is a safe choice.
-
- You may increase this number if you are using this device in a
- Server Environment with many high-traffic USB Highspeed devices
- sharing the same USB bus.
-
-
-config DVB_CINERGYT2_STREAM_BUF_SIZE
- int "Size of URB Stream Buffers for Highspeed Transfers"
- depends on DVB_CINERGYT2_TUNING
- default "512"
- help
- Should be a multiple of native buffer size of 512 bytes.
- Default value is a safe choice.
-
- You may increase this number if you are using this device in a
- Server Environment with many high-traffic USB Highspeed devices
- sharing the same USB bus.
-
-
-config DVB_CINERGYT2_QUERY_INTERVAL
- int "Status update interval [milliseconds]"
- depends on DVB_CINERGYT2_TUNING
- default "250"
- help
- This is the interval for status readouts from the demodulator.
- You may try lower values if you need more responsive signal quality
- measurements.
-
- Please keep in mind that these updates cause traffic on the tuner
- control bus and thus may or may not affect reception sensitivity.
-
- The default value should be a safe choice for common applications.
-
-
-config DVB_CINERGYT2_ENABLE_RC_INPUT_DEVICE
- bool "Register the onboard IR Remote Control Receiver as Input Device"
- depends on DVB_CINERGYT2_TUNING
- default y
- help
- Enable this option if you want to use the onboard Infrared Remote
- Control Receiver as Linux-Input device.
-
- Right now only the keycode table for the default Remote Control
- delivered with the device is supported, please see the driver
- source code to find out how to add support for other controls.
-
-
-config DVB_CINERGYT2_RC_QUERY_INTERVAL
- int "Infrared Remote Controller update interval [milliseconds]"
- depends on DVB_CINERGYT2_TUNING && DVB_CINERGYT2_ENABLE_RC_INPUT_DEVICE
- default "50"
- help
- If you have a very fast-repeating remote control you can try lower
- values, for normal consumer receivers the default value should be
- a safe choice.
-
diff --git a/drivers/media/dvb/cinergyT2/Makefile b/drivers/media/dvb/cinergyT2/Makefile
deleted file mode 100644
index d762d8c..0000000
--- a/drivers/media/dvb/cinergyT2/Makefile
+++ /dev/null
@@ -1,3 +0,0 @@
-obj-$(CONFIG_DVB_CINERGYT2) += cinergyT2.o
-
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
diff --git a/drivers/media/dvb/cinergyT2/cinergyT2.c b/drivers/media/dvb/cinergyT2/cinergyT2.c
deleted file mode 100644
index a824f37..0000000
--- a/drivers/media/dvb/cinergyT2/cinergyT2.c
+++ /dev/null
@@ -1,1105 +0,0 @@
-/*
- * TerraTec Cinergy T²/qanu USB2 DVB-T adapter.
- *
- * Copyright (C) 2004 Daniel Mack <daniel@qanu.de> and
- * Holger Waechtler <holger@qanu.de>
- *
- * Protocol Spec published on http://qanu.de/specs/terratec_cinergyT2.pdf
- *
- * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/usb.h>
-#include <linux/input.h>
-#include <linux/dvb/frontend.h>
-#include <linux/mutex.h>
-#include <linux/mm.h>
-#include <asm/io.h>
-
-#include "dmxdev.h"
-#include "dvb_demux.h"
-#include "dvb_net.h"
-
-#ifdef CONFIG_DVB_CINERGYT2_TUNING
- #define STREAM_URB_COUNT (CONFIG_DVB_CINERGYT2_STREAM_URB_COUNT)
- #define STREAM_BUF_SIZE (CONFIG_DVB_CINERGYT2_STREAM_BUF_SIZE)
- #define QUERY_INTERVAL (CONFIG_DVB_CINERGYT2_QUERY_INTERVAL)
- #ifdef CONFIG_DVB_CINERGYT2_ENABLE_RC_INPUT_DEVICE
- #define RC_QUERY_INTERVAL (CONFIG_DVB_CINERGYT2_RC_QUERY_INTERVAL)
- #define ENABLE_RC (1)
- #endif
-#else
- #define STREAM_URB_COUNT (32)
- #define STREAM_BUF_SIZE (512) /* bytes */
- #define ENABLE_RC (1)
- #define RC_QUERY_INTERVAL (50) /* milliseconds */
- #define QUERY_INTERVAL (333) /* milliseconds */
-#endif
-
-#define DRIVER_NAME "TerraTec/qanu USB2.0 Highspeed DVB-T Receiver"
-
-static int debug;
-module_param_named(debug, debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
-
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-#define dprintk(level, args...) \
-do { \
- if ((debug & level)) { \
- printk("%s: %s(): ", KBUILD_MODNAME, \
- __func__); \
- printk(args); } \
-} while (0)
-
-enum cinergyt2_ep1_cmd {
- CINERGYT2_EP1_PID_TABLE_RESET = 0x01,
- CINERGYT2_EP1_PID_SETUP = 0x02,
- CINERGYT2_EP1_CONTROL_STREAM_TRANSFER = 0x03,
- CINERGYT2_EP1_SET_TUNER_PARAMETERS = 0x04,
- CINERGYT2_EP1_GET_TUNER_STATUS = 0x05,
- CINERGYT2_EP1_START_SCAN = 0x06,
- CINERGYT2_EP1_CONTINUE_SCAN = 0x07,
- CINERGYT2_EP1_GET_RC_EVENTS = 0x08,
- CINERGYT2_EP1_SLEEP_MODE = 0x09
-};
-
-struct dvbt_set_parameters_msg {
- uint8_t cmd;
- __le32 freq;
- uint8_t bandwidth;
- __le16 tps;
- uint8_t flags;
-} __attribute__((packed));
-
-struct dvbt_get_status_msg {
- __le32 freq;
- uint8_t bandwidth;
- __le16 tps;
- uint8_t flags;
- __le16 gain;
- uint8_t snr;
- __le32 viterbi_error_rate;
- __le32 rs_error_rate;
- __le32 uncorrected_block_count;
- uint8_t lock_bits;
- uint8_t prev_lock_bits;
-} __attribute__((packed));
-
-static struct dvb_frontend_info cinergyt2_fe_info = {
- .name = DRIVER_NAME,
- .type = FE_OFDM,
- .frequency_min = 174000000,
- .frequency_max = 862000000,
- .frequency_stepsize = 166667,
- .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 |
- FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
- FE_CAN_FEC_AUTO |
- FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
- FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
- FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER | FE_CAN_MUTE_TS
-};
-
-struct cinergyt2 {
- struct dvb_demux demux;
- struct usb_device *udev;
- struct mutex sem;
- struct mutex wq_sem;
- struct dvb_adapter adapter;
- struct dvb_device *fedev;
- struct dmxdev dmxdev;
- struct dvb_net dvbnet;
-
- int streaming;
- int sleeping;
-
- struct dvbt_set_parameters_msg param;
- struct dvbt_get_status_msg status;
- struct delayed_work query_work;
-
- wait_queue_head_t poll_wq;
- int pending_fe_events;
- int disconnect_pending;
- unsigned int uncorrected_block_count;
- atomic_t inuse;
-
- void *streambuf;
- dma_addr_t streambuf_dmahandle;
- struct urb *stream_urb [STREAM_URB_COUNT];
-
-#ifdef ENABLE_RC
- struct input_dev *rc_input_dev;
- char phys[64];
- struct delayed_work rc_query_work;
- int rc_input_event;
- __le32 rc_last_code;
- unsigned long last_event_jiffies;
-#endif
-};
-
-enum {
- CINERGYT2_RC_EVENT_TYPE_NONE = 0x00,
- CINERGYT2_RC_EVENT_TYPE_NEC = 0x01,
- CINERGYT2_RC_EVENT_TYPE_RC5 = 0x02
-};
-
-struct cinergyt2_rc_event {
- char type;
- __le32 value;
-} __attribute__((packed));
-
-static const uint32_t rc_keys[] = {
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xfe01eb04, KEY_POWER,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xfd02eb04, KEY_1,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xfc03eb04, KEY_2,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xfb04eb04, KEY_3,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xfa05eb04, KEY_4,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xf906eb04, KEY_5,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xf807eb04, KEY_6,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xf708eb04, KEY_7,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xf609eb04, KEY_8,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xf50aeb04, KEY_9,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xf30ceb04, KEY_0,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xf40beb04, KEY_VIDEO,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xf20deb04, KEY_REFRESH,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xf10eeb04, KEY_SELECT,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xf00feb04, KEY_EPG,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xef10eb04, KEY_UP,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xeb14eb04, KEY_DOWN,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xee11eb04, KEY_LEFT,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xec13eb04, KEY_RIGHT,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xed12eb04, KEY_OK,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xea15eb04, KEY_TEXT,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xe916eb04, KEY_INFO,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xe817eb04, KEY_RED,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xe718eb04, KEY_GREEN,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xe619eb04, KEY_YELLOW,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xe51aeb04, KEY_BLUE,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xe31ceb04, KEY_VOLUMEUP,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xe11eeb04, KEY_VOLUMEDOWN,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xe21deb04, KEY_MUTE,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xe41beb04, KEY_CHANNELUP,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xe01feb04, KEY_CHANNELDOWN,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xbf40eb04, KEY_PAUSE,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xb34ceb04, KEY_PLAY,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xa758eb04, KEY_RECORD,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xab54eb04, KEY_PREVIOUS,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xb748eb04, KEY_STOP,
- CINERGYT2_RC_EVENT_TYPE_NEC, 0xa35ceb04, KEY_NEXT
-};
-
-static int cinergyt2_command (struct cinergyt2 *cinergyt2,
- char *send_buf, int send_buf_len,
- char *recv_buf, int recv_buf_len)
-{
- int actual_len;
- char dummy;
- int ret;
-
- ret = usb_bulk_msg(cinergyt2->udev, usb_sndbulkpipe(cinergyt2->udev, 1),
- send_buf, send_buf_len, &actual_len, 1000);
-
- if (ret)
- dprintk(1, "usb_bulk_msg (send) failed, err %i\n", ret);
-
- if (!recv_buf)
- recv_buf = &dummy;
-
- ret = usb_bulk_msg(cinergyt2->udev, usb_rcvbulkpipe(cinergyt2->udev, 1),
- recv_buf, recv_buf_len, &actual_len, 1000);
-
- if (ret)
- dprintk(1, "usb_bulk_msg (read) failed, err %i\n", ret);
-
- return ret ? ret : actual_len;
-}
-
-static void cinergyt2_control_stream_transfer (struct cinergyt2 *cinergyt2, int enable)
-{
- char buf [] = { CINERGYT2_EP1_CONTROL_STREAM_TRANSFER, enable ? 1 : 0 };
- cinergyt2_command(cinergyt2, buf, sizeof(buf), NULL, 0);
-}
-
-static void cinergyt2_sleep (struct cinergyt2 *cinergyt2, int sleep)
-{
- char buf [] = { CINERGYT2_EP1_SLEEP_MODE, sleep ? 1 : 0 };
- cinergyt2_command(cinergyt2, buf, sizeof(buf), NULL, 0);
- cinergyt2->sleeping = sleep;
-}
-
-static void cinergyt2_stream_irq (struct urb *urb);
-
-static int cinergyt2_submit_stream_urb (struct cinergyt2 *cinergyt2, struct urb *urb)
-{
- int err;
-
- usb_fill_bulk_urb(urb,
- cinergyt2->udev,
- usb_rcvbulkpipe(cinergyt2->udev, 0x2),
- urb->transfer_buffer,
- STREAM_BUF_SIZE,
- cinergyt2_stream_irq,
- cinergyt2);
-
- if ((err = usb_submit_urb(urb, GFP_ATOMIC)))
- dprintk(1, "urb submission failed (err = %i)!\n", err);
-
- return err;
-}
-
-static void cinergyt2_stream_irq (struct urb *urb)
-{
- struct cinergyt2 *cinergyt2 = urb->context;
-
- if (urb->actual_length > 0)
- dvb_dmx_swfilter(&cinergyt2->demux,
- urb->transfer_buffer, urb->actual_length);
-
- if (cinergyt2->streaming)
- cinergyt2_submit_stream_urb(cinergyt2, urb);
-}
-
-static void cinergyt2_free_stream_urbs (struct cinergyt2 *cinergyt2)
-{
- int i;
-
- for (i=0; i<STREAM_URB_COUNT; i++)
- usb_free_urb(cinergyt2->stream_urb[i]);
-
- usb_buffer_free(cinergyt2->udev, STREAM_URB_COUNT*STREAM_BUF_SIZE,
- cinergyt2->streambuf, cinergyt2->streambuf_dmahandle);
-}
-
-static int cinergyt2_alloc_stream_urbs (struct cinergyt2 *cinergyt2)
-{
- int i;
-
- cinergyt2->streambuf = usb_buffer_alloc(cinergyt2->udev, STREAM_URB_COUNT*STREAM_BUF_SIZE,
- GFP_KERNEL, &cinergyt2->streambuf_dmahandle);
- if (!cinergyt2->streambuf) {
- dprintk(1, "failed to alloc consistent stream memory area, bailing out!\n");
- return -ENOMEM;
- }
-
- memset(cinergyt2->streambuf, 0, STREAM_URB_COUNT*STREAM_BUF_SIZE);
-
- for (i=0; i<STREAM_URB_COUNT; i++) {
- struct urb *urb;
-
- if (!(urb = usb_alloc_urb(0, GFP_ATOMIC))) {
- dprintk(1, "failed to alloc consistent stream urbs, bailing out!\n");
- cinergyt2_free_stream_urbs(cinergyt2);
- return -ENOMEM;
- }
-
- urb->transfer_buffer = cinergyt2->streambuf + i * STREAM_BUF_SIZE;
- urb->transfer_buffer_length = STREAM_BUF_SIZE;
-
- cinergyt2->stream_urb[i] = urb;
- }
-
- return 0;
-}
-
-static void cinergyt2_stop_stream_xfer (struct cinergyt2 *cinergyt2)
-{
- int i;
-
- cinergyt2_control_stream_transfer(cinergyt2, 0);
-
- for (i=0; i<STREAM_URB_COUNT; i++)
- usb_kill_urb(cinergyt2->stream_urb[i]);
-}
-
-static int cinergyt2_start_stream_xfer (struct cinergyt2 *cinergyt2)
-{
- int i, err;
-
- for (i=0; i<STREAM_URB_COUNT; i++) {
- if ((err = cinergyt2_submit_stream_urb(cinergyt2, cinergyt2->stream_urb[i]))) {
- cinergyt2_stop_stream_xfer(cinergyt2);
- dprintk(1, "failed urb submission (%i: err = %i)!\n", i, err);
- return err;
- }
- }
-
- cinergyt2_control_stream_transfer(cinergyt2, 1);
- return 0;
-}
-
-static int cinergyt2_start_feed(struct dvb_demux_feed *dvbdmxfeed)
-{
- struct dvb_demux *demux = dvbdmxfeed->demux;
- struct cinergyt2 *cinergyt2 = demux->priv;
-
- if (cinergyt2->disconnect_pending)
- return -EAGAIN;
- if (mutex_lock_interruptible(&cinergyt2->sem))
- return -ERESTARTSYS;
-
- if (cinergyt2->streaming == 0)
- cinergyt2_start_stream_xfer(cinergyt2);
-
- cinergyt2->streaming++;
- mutex_unlock(&cinergyt2->sem);
- return 0;
-}
-
-static int cinergyt2_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
-{
- struct dvb_demux *demux = dvbdmxfeed->demux;
- struct cinergyt2 *cinergyt2 = demux->priv;
-
- if (cinergyt2->disconnect_pending)
- return -EAGAIN;
- if (mutex_lock_interruptible(&cinergyt2->sem))
- return -ERESTARTSYS;
-
- if (--cinergyt2->streaming == 0)
- cinergyt2_stop_stream_xfer(cinergyt2);
-
- mutex_unlock(&cinergyt2->sem);
- return 0;
-}
-
-/**
- * convert linux-dvb frontend parameter set into TPS.
- * See ETSI ETS-300744, section 4.6.2, table 9 for details.
- *
- * This function is probably reusable and may better get placed in a support
- * library.
- *
- * We replace errornous fields by default TPS fields (the ones with value 0).
- */
-static uint16_t compute_tps (struct dvb_frontend_parameters *p)
-{
- struct dvb_ofdm_parameters *op = &p->u.ofdm;
- uint16_t tps = 0;
-
- switch (op->code_rate_HP) {
- case FEC_2_3:
- tps |= (1 << 7);
- break;
- case FEC_3_4:
- tps |= (2 << 7);
- break;
- case FEC_5_6:
- tps |= (3 << 7);
- break;
- case FEC_7_8:
- tps |= (4 << 7);
- break;
- case FEC_1_2:
- case FEC_AUTO:
- default:
- /* tps |= (0 << 7) */;
- }
-
- switch (op->code_rate_LP) {
- case FEC_2_3:
- tps |= (1 << 4);
- break;
- case FEC_3_4:
- tps |= (2 << 4);
- break;
- case FEC_5_6:
- tps |= (3 << 4);
- break;
- case FEC_7_8:
- tps |= (4 << 4);
- break;
- case FEC_1_2:
- case FEC_AUTO:
- default:
- /* tps |= (0 << 4) */;
- }
-
- switch (op->constellation) {
- case QAM_16:
- tps |= (1 << 13);
- break;
- case QAM_64:
- tps |= (2 << 13);
- break;
- case QPSK:
- default:
- /* tps |= (0 << 13) */;
- }
-
- switch (op->transmission_mode) {
- case TRANSMISSION_MODE_8K:
- tps |= (1 << 0);
- break;
- case TRANSMISSION_MODE_2K:
- default:
- /* tps |= (0 << 0) */;
- }
-
- switch (op->guard_interval) {
- case GUARD_INTERVAL_1_16:
- tps |= (1 << 2);
- break;
- case GUARD_INTERVAL_1_8:
- tps |= (2 << 2);
- break;
- case GUARD_INTERVAL_1_4:
- tps |= (3 << 2);
- break;
- case GUARD_INTERVAL_1_32:
- default:
- /* tps |= (0 << 2) */;
- }
-
- switch (op->hierarchy_information) {
- case HIERARCHY_1:
- tps |= (1 << 10);
- break;
- case HIERARCHY_2:
- tps |= (2 << 10);
- break;
- case HIERARCHY_4:
- tps |= (3 << 10);
- break;
- case HIERARCHY_NONE:
- default:
- /* tps |= (0 << 10) */;
- }
-
- return tps;
-}
-
-static int cinergyt2_open (struct inode *inode, struct file *file)
-{
- struct dvb_device *dvbdev = file->private_data;
- struct cinergyt2 *cinergyt2 = dvbdev->priv;
- int err = -EAGAIN;
-
- if (cinergyt2->disconnect_pending)
- goto out;
- err = mutex_lock_interruptible(&cinergyt2->wq_sem);
- if (err)
- goto out;
-
- err = mutex_lock_interruptible(&cinergyt2->sem);
- if (err)
- goto out_unlock1;
-
- if ((err = dvb_generic_open(inode, file)))
- goto out_unlock2;
-
- if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
- cinergyt2_sleep(cinergyt2, 0);
- schedule_delayed_work(&cinergyt2->query_work, HZ/2);
- }
-
- atomic_inc(&cinergyt2->inuse);
-
-out_unlock2:
- mutex_unlock(&cinergyt2->sem);
-out_unlock1:
- mutex_unlock(&cinergyt2->wq_sem);
-out:
- return err;
-}
-
-static void cinergyt2_unregister(struct cinergyt2 *cinergyt2)
-{
- dvb_net_release(&cinergyt2->dvbnet);
- dvb_dmxdev_release(&cinergyt2->dmxdev);
- dvb_dmx_release(&cinergyt2->demux);
- dvb_unregister_device(cinergyt2->fedev);
- dvb_unregister_adapter(&cinergyt2->adapter);
-
- cinergyt2_free_stream_urbs(cinergyt2);
- kfree(cinergyt2);
-}
-
-static int cinergyt2_release (struct inode *inode, struct file *file)
-{
- struct dvb_device *dvbdev = file->private_data;
- struct cinergyt2 *cinergyt2 = dvbdev->priv;
-
- mutex_lock(&cinergyt2->wq_sem);
-
- if (!cinergyt2->disconnect_pending && (file->f_flags & O_ACCMODE) != O_RDONLY) {
- cancel_rearming_delayed_work(&cinergyt2->query_work);
-
- mutex_lock(&cinergyt2->sem);
- cinergyt2_sleep(cinergyt2, 1);
- mutex_unlock(&cinergyt2->sem);
- }
-
- mutex_unlock(&cinergyt2->wq_sem);
-
- if (atomic_dec_and_test(&cinergyt2->inuse) && cinergyt2->disconnect_pending) {
- warn("delayed unregister in release");
- cinergyt2_unregister(cinergyt2);
- }
-
- return dvb_generic_release(inode, file);
-}
-
-static unsigned int cinergyt2_poll (struct file *file, struct poll_table_struct *wait)
-{
- struct dvb_device *dvbdev = file->private_data;
- struct cinergyt2 *cinergyt2 = dvbdev->priv;
- unsigned int mask = 0;
-
- if (cinergyt2->disconnect_pending)
- return -EAGAIN;
- if (mutex_lock_interruptible(&cinergyt2->sem))
- return -ERESTARTSYS;
-
- poll_wait(file, &cinergyt2->poll_wq, wait);
-
- if (cinergyt2->pending_fe_events != 0)
- mask |= (POLLIN | POLLRDNORM | POLLPRI);
-
- mutex_unlock(&cinergyt2->sem);
-
- return mask;
-}
-
-
-static int cinergyt2_ioctl (struct inode *inode, struct file *file,
- unsigned cmd, unsigned long arg)
-{
- struct dvb_device *dvbdev = file->private_data;
- struct cinergyt2 *cinergyt2 = dvbdev->priv;
- struct dvbt_get_status_msg *stat = &cinergyt2->status;
- fe_status_t status = 0;
-
- switch (cmd) {
- case FE_GET_INFO:
- return copy_to_user((void __user*) arg, &cinergyt2_fe_info,
- sizeof(struct dvb_frontend_info));
-
- case FE_READ_STATUS:
- if (0xffff - le16_to_cpu(stat->gain) > 30)
- status |= FE_HAS_SIGNAL;
- if (stat->lock_bits & (1 << 6))
- status |= FE_HAS_LOCK;
- if (stat->lock_bits & (1 << 5))
- status |= FE_HAS_SYNC;
- if (stat->lock_bits & (1 << 4))
- status |= FE_HAS_CARRIER;
- if (stat->lock_bits & (1 << 1))
- status |= FE_HAS_VITERBI;
-
- return copy_to_user((void __user*) arg, &status, sizeof(status));
-
- case FE_READ_BER:
- return put_user(le32_to_cpu(stat->viterbi_error_rate),
- (__u32 __user *) arg);
-
- case FE_READ_SIGNAL_STRENGTH:
- return put_user(0xffff - le16_to_cpu(stat->gain),
- (__u16 __user *) arg);
-
- case FE_READ_SNR:
- return put_user((stat->snr << 8) | stat->snr,
- (__u16 __user *) arg);
-
- case FE_READ_UNCORRECTED_BLOCKS:
- {
- uint32_t unc_count;
-
- if (mutex_lock_interruptible(&cinergyt2->sem))
- return -ERESTARTSYS;
- unc_count = cinergyt2->uncorrected_block_count;
- cinergyt2->uncorrected_block_count = 0;
- mutex_unlock(&cinergyt2->sem);
-
- /* UNC are already converted to host byte order... */
- return put_user(unc_count,(__u32 __user *) arg);
- }
- case FE_SET_FRONTEND:
- {
- struct dvbt_set_parameters_msg *param = &cinergyt2->param;
- struct dvb_frontend_parameters p;
- int err;
-
- if ((file->f_flags & O_ACCMODE) == O_RDONLY)
- return -EPERM;
-
- if (copy_from_user(&p, (void __user*) arg, sizeof(p)))
- return -EFAULT;
-
- if (cinergyt2->disconnect_pending)
- return -EAGAIN;
- if (mutex_lock_interruptible(&cinergyt2->sem))
- return -ERESTARTSYS;
-
- param->cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS;
- param->tps = cpu_to_le16(compute_tps(&p));
- param->freq = cpu_to_le32(p.frequency / 1000);
- param->bandwidth = 8 - p.u.ofdm.bandwidth - BANDWIDTH_8_MHZ;
-
- stat->lock_bits = 0;
- cinergyt2->pending_fe_events++;
- wake_up_interruptible(&cinergyt2->poll_wq);
-
- err = cinergyt2_command(cinergyt2,
- (char *) param, sizeof(*param),
- NULL, 0);
-
- mutex_unlock(&cinergyt2->sem);
-
- return (err < 0) ? err : 0;
- }
-
- case FE_GET_FRONTEND:
- /**
- * trivial to implement (see struct dvbt_get_status_msg).
- * equivalent to FE_READ ioctls, but needs
- * TPS -> linux-dvb parameter set conversion. Feel free
- * to implement this and send us a patch if you need this
- * functionality.
- */
- break;
-
- case FE_GET_EVENT:
- {
- /**
- * for now we only fill the status field. the parameters
- * are trivial to fill as soon FE_GET_FRONTEND is done.
- */
- struct dvb_frontend_event __user *e = (void __user *) arg;
- if (cinergyt2->pending_fe_events == 0) {
- if (file->f_flags & O_NONBLOCK)
- return -EWOULDBLOCK;
- wait_event_interruptible(cinergyt2->poll_wq,
- cinergyt2->pending_fe_events > 0);
- }
- cinergyt2->pending_fe_events = 0;
- return cinergyt2_ioctl(inode, file, FE_READ_STATUS,
- (unsigned long) &e->status);
- }
-
- default:
- ;
- }
-
- return -EINVAL;
-}
-
-static int cinergyt2_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct dvb_device *dvbdev = file->private_data;
- struct cinergyt2 *cinergyt2 = dvbdev->priv;
- int ret = 0;
-
- lock_kernel();
-
- if (vma->vm_flags & (VM_WRITE | VM_EXEC)) {
- ret = -EPERM;
- goto bailout;
- }
-
- if (vma->vm_end > vma->vm_start + STREAM_URB_COUNT * STREAM_BUF_SIZE) {
- ret = -EINVAL;
- goto bailout;
- }
-
- vma->vm_flags |= (VM_IO | VM_DONTCOPY);
- vma->vm_file = file;
-
- ret = remap_pfn_range(vma, vma->vm_start,
- virt_to_phys(cinergyt2->streambuf) >> PAGE_SHIFT,
- vma->vm_end - vma->vm_start,
- vma->vm_page_prot) ? -EAGAIN : 0;
-bailout:
- unlock_kernel();
- return ret;
-}
-
-static struct file_operations cinergyt2_fops = {
- .owner = THIS_MODULE,
- .ioctl = cinergyt2_ioctl,
- .poll = cinergyt2_poll,
- .open = cinergyt2_open,
- .release = cinergyt2_release,
- .mmap = cinergyt2_mmap
-};
-
-static struct dvb_device cinergyt2_fe_template = {
- .users = ~0,
- .writers = 1,
- .readers = (~0)-1,
- .fops = &cinergyt2_fops
-};
-
-#ifdef ENABLE_RC
-
-static void cinergyt2_query_rc (struct work_struct *work)
-{
- struct cinergyt2 *cinergyt2 =
- container_of(work, struct cinergyt2, rc_query_work.work);
- char buf[1] = { CINERGYT2_EP1_GET_RC_EVENTS };
- struct cinergyt2_rc_event rc_events[12];
- int n, len, i;
-
- if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->sem))
- return;
-
- len = cinergyt2_command(cinergyt2, buf, sizeof(buf),
- (char *) rc_events, sizeof(rc_events));
- if (len < 0)
- goto out;
- if (len == 0) {
- if (time_after(jiffies, cinergyt2->last_event_jiffies +
- msecs_to_jiffies(150))) {
- /* stop key repeat */
- if (cinergyt2->rc_input_event != KEY_MAX) {
- dprintk(1, "rc_input_event=%d Up\n", cinergyt2->rc_input_event);
- input_report_key(cinergyt2->rc_input_dev,
- cinergyt2->rc_input_event, 0);
- input_sync(cinergyt2->rc_input_dev);
- cinergyt2->rc_input_event = KEY_MAX;
- }
- cinergyt2->rc_last_code = cpu_to_le32(~0);
- }
- goto out;
- }
- cinergyt2->last_event_jiffies = jiffies;
-
- for (n = 0; n < (len / sizeof(rc_events[0])); n++) {
- dprintk(1, "rc_events[%d].value = %x, type=%x\n",
- n, le32_to_cpu(rc_events[n].value), rc_events[n].type);
-
- if (rc_events[n].type == CINERGYT2_RC_EVENT_TYPE_NEC &&
- rc_events[n].value == cpu_to_le32(~0)) {
- /* keyrepeat bit -> just repeat last rc_input_event */
- } else {
- cinergyt2->rc_input_event = KEY_MAX;
- for (i = 0; i < ARRAY_SIZE(rc_keys); i += 3) {
- if (rc_keys[i + 0] == rc_events[n].type &&
- rc_keys[i + 1] == le32_to_cpu(rc_events[n].value)) {
- cinergyt2->rc_input_event = rc_keys[i + 2];
- break;
- }
- }
- }
-
- if (cinergyt2->rc_input_event != KEY_MAX) {
- if (rc_events[n].value == cinergyt2->rc_last_code &&
- cinergyt2->rc_last_code != cpu_to_le32(~0)) {
- /* emit a key-up so the double event is recognized */
- dprintk(1, "rc_input_event=%d UP\n", cinergyt2->rc_input_event);
- input_report_key(cinergyt2->rc_input_dev,
- cinergyt2->rc_input_event, 0);
- }
- dprintk(1, "rc_input_event=%d\n", cinergyt2->rc_input_event);
- input_report_key(cinergyt2->rc_input_dev,
- cinergyt2->rc_input_event, 1);
- input_sync(cinergyt2->rc_input_dev);
- cinergyt2->rc_last_code = rc_events[n].value;
- }
- }
-
-out:
- schedule_delayed_work(&cinergyt2->rc_query_work,
- msecs_to_jiffies(RC_QUERY_INTERVAL));
-
- mutex_unlock(&cinergyt2->sem);
-}
-
-static int cinergyt2_register_rc(struct cinergyt2 *cinergyt2)
-{
- struct input_dev *input_dev;
- int i;
- int err;
-
- input_dev = input_allocate_device();
- if (!input_dev)
- return -ENOMEM;
-
- usb_make_path(cinergyt2->udev, cinergyt2->phys, sizeof(cinergyt2->phys));
- strlcat(cinergyt2->phys, "/input0", sizeof(cinergyt2->phys));
- cinergyt2->rc_input_event = KEY_MAX;
- cinergyt2->rc_last_code = cpu_to_le32(~0);
- INIT_DELAYED_WORK(&cinergyt2->rc_query_work, cinergyt2_query_rc);
-
- input_dev->name = DRIVER_NAME " remote control";
- input_dev->phys = cinergyt2->phys;
- input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
- for (i = 0; i < ARRAY_SIZE(rc_keys); i += 3)
- set_bit(rc_keys[i + 2], input_dev->keybit);
- input_dev->keycodesize = 0;
- input_dev->keycodemax = 0;
- input_dev->id.bustype = BUS_USB;
- input_dev->id.vendor = le16_to_cpu(cinergyt2->udev->descriptor.idVendor);
- input_dev->id.product = le16_to_cpu(cinergyt2->udev->descriptor.idProduct);
- input_dev->id.version = 1;
- input_dev->dev.parent = &cinergyt2->udev->dev;
-
- err = input_register_device(input_dev);
- if (err) {
- input_free_device(input_dev);
- return err;
- }
-
- cinergyt2->rc_input_dev = input_dev;
- schedule_delayed_work(&cinergyt2->rc_query_work, HZ/2);
-
- return 0;
-}
-
-static void cinergyt2_unregister_rc(struct cinergyt2 *cinergyt2)
-{
- cancel_rearming_delayed_work(&cinergyt2->rc_query_work);
- input_unregister_device(cinergyt2->rc_input_dev);
-}
-
-static inline void cinergyt2_suspend_rc(struct cinergyt2 *cinergyt2)
-{
- cancel_rearming_delayed_work(&cinergyt2->rc_query_work);
-}
-
-static inline void cinergyt2_resume_rc(struct cinergyt2 *cinergyt2)
-{
- schedule_delayed_work(&cinergyt2->rc_query_work, HZ/2);
-}
-
-#else
-
-static inline int cinergyt2_register_rc(struct cinergyt2 *cinergyt2) { return 0; }
-static inline void cinergyt2_unregister_rc(struct cinergyt2 *cinergyt2) { }
-static inline void cinergyt2_suspend_rc(struct cinergyt2 *cinergyt2) { }
-static inline void cinergyt2_resume_rc(struct cinergyt2 *cinergyt2) { }
-
-#endif /* ENABLE_RC */
-
-static void cinergyt2_query (struct work_struct *work)
-{
- struct cinergyt2 *cinergyt2 =
- container_of(work, struct cinergyt2, query_work.work);
- char cmd [] = { CINERGYT2_EP1_GET_TUNER_STATUS };
- struct dvbt_get_status_msg *s = &cinergyt2->status;
- uint8_t lock_bits;
-
- if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->sem))
- return;
-
- lock_bits = s->lock_bits;
-
- cinergyt2_command(cinergyt2, cmd, sizeof(cmd), (char *) s, sizeof(*s));
-
- cinergyt2->uncorrected_block_count +=
- le32_to_cpu(s->uncorrected_block_count);
-
- if (lock_bits != s->lock_bits) {
- wake_up_interruptible(&cinergyt2->poll_wq);
- cinergyt2->pending_fe_events++;
- }
-
- schedule_delayed_work(&cinergyt2->query_work,
- msecs_to_jiffies(QUERY_INTERVAL));
-
- mutex_unlock(&cinergyt2->sem);
-}
-
-static int cinergyt2_probe (struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- struct cinergyt2 *cinergyt2;
- int err;
-
- if (!(cinergyt2 = kzalloc (sizeof(struct cinergyt2), GFP_KERNEL))) {
- dprintk(1, "out of memory?!?\n");
- return -ENOMEM;
- }
-
- usb_set_intfdata (intf, (void *) cinergyt2);
-
- mutex_init(&cinergyt2->sem);
- mutex_init(&cinergyt2->wq_sem);
- init_waitqueue_head (&cinergyt2->poll_wq);
- INIT_DELAYED_WORK(&cinergyt2->query_work, cinergyt2_query);
-
- cinergyt2->udev = interface_to_usbdev(intf);
- cinergyt2->param.cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS;
-
- if (cinergyt2_alloc_stream_urbs (cinergyt2) < 0) {
- dprintk(1, "unable to allocate stream urbs\n");
- kfree(cinergyt2);
- return -ENOMEM;
- }
-
- err = dvb_register_adapter(&cinergyt2->adapter, DRIVER_NAME,
- THIS_MODULE, &cinergyt2->udev->dev,
- adapter_nr);
- if (err < 0) {
- kfree(cinergyt2);
- return err;
- }
-
- cinergyt2->demux.priv = cinergyt2;
- cinergyt2->demux.filternum = 256;
- cinergyt2->demux.feednum = 256;
- cinergyt2->demux.start_feed = cinergyt2_start_feed;
- cinergyt2->demux.stop_feed = cinergyt2_stop_feed;
- cinergyt2->demux.dmx.capabilities = DMX_TS_FILTERING |
- DMX_SECTION_FILTERING |
- DMX_MEMORY_BASED_FILTERING;
-
- if ((err = dvb_dmx_init(&cinergyt2->demux)) < 0) {
- dprintk(1, "dvb_dmx_init() failed (err = %d)\n", err);
- goto bailout;
- }
-
- cinergyt2->dmxdev.filternum = cinergyt2->demux.filternum;
- cinergyt2->dmxdev.demux = &cinergyt2->demux.dmx;
- cinergyt2->dmxdev.capabilities = 0;
-
- if ((err = dvb_dmxdev_init(&cinergyt2->dmxdev, &cinergyt2->adapter)) < 0) {
- dprintk(1, "dvb_dmxdev_init() failed (err = %d)\n", err);
- goto bailout;
- }
-
- if (dvb_net_init(&cinergyt2->adapter, &cinergyt2->dvbnet, &cinergyt2->demux.dmx))
- dprintk(1, "dvb_net_init() failed!\n");
-
- dvb_register_device(&cinergyt2->adapter, &cinergyt2->fedev,
- &cinergyt2_fe_template, cinergyt2,
- DVB_DEVICE_FRONTEND);
-
- err = cinergyt2_register_rc(cinergyt2);
- if (err)
- goto bailout;
-
- return 0;
-
-bailout:
- dvb_net_release(&cinergyt2->dvbnet);
- dvb_dmxdev_release(&cinergyt2->dmxdev);
- dvb_dmx_release(&cinergyt2->demux);
- dvb_unregister_adapter(&cinergyt2->adapter);
- cinergyt2_free_stream_urbs(cinergyt2);
- kfree(cinergyt2);
- return -ENOMEM;
-}
-
-static void cinergyt2_disconnect (struct usb_interface *intf)
-{
- struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
-
- cinergyt2_unregister_rc(cinergyt2);
- cancel_rearming_delayed_work(&cinergyt2->query_work);
- wake_up_interruptible(&cinergyt2->poll_wq);
-
- cinergyt2->demux.dmx.close(&cinergyt2->demux.dmx);
- cinergyt2->disconnect_pending = 1;
-
- if (!atomic_read(&cinergyt2->inuse))
- cinergyt2_unregister(cinergyt2);
-}
-
-static int cinergyt2_suspend (struct usb_interface *intf, pm_message_t state)
-{
- struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
-
- if (cinergyt2->disconnect_pending)
- return -EAGAIN;
- if (mutex_lock_interruptible(&cinergyt2->wq_sem))
- return -ERESTARTSYS;
-
- cinergyt2_suspend_rc(cinergyt2);
- cancel_rearming_delayed_work(&cinergyt2->query_work);
-
- mutex_lock(&cinergyt2->sem);
- if (cinergyt2->streaming)
- cinergyt2_stop_stream_xfer(cinergyt2);
- cinergyt2_sleep(cinergyt2, 1);
- mutex_unlock(&cinergyt2->sem);
-
- mutex_unlock(&cinergyt2->wq_sem);
-
- return 0;
-}
-
-static int cinergyt2_resume (struct usb_interface *intf)
-{
- struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
- struct dvbt_set_parameters_msg *param = &cinergyt2->param;
- int err = -EAGAIN;
-
- if (cinergyt2->disconnect_pending)
- goto out;
- err = mutex_lock_interruptible(&cinergyt2->wq_sem);
- if (err)
- goto out;
-
- err = mutex_lock_interruptible(&cinergyt2->sem);
- if (err)
- goto out_unlock1;
-
- if (!cinergyt2->sleeping) {
- cinergyt2_sleep(cinergyt2, 0);
- cinergyt2_command(cinergyt2, (char *) param, sizeof(*param), NULL, 0);
- if (cinergyt2->streaming)
- cinergyt2_start_stream_xfer(cinergyt2);
- schedule_delayed_work(&cinergyt2->query_work, HZ/2);
- }
-
- cinergyt2_resume_rc(cinergyt2);
-
- mutex_unlock(&cinergyt2->sem);
-out_unlock1:
- mutex_unlock(&cinergyt2->wq_sem);
-out:
- return err;
-}
-
-static const struct usb_device_id cinergyt2_table [] __devinitdata = {
- { USB_DEVICE(0x0ccd, 0x0038) },
- { 0 }
-};
-
-MODULE_DEVICE_TABLE(usb, cinergyt2_table);
-
-static struct usb_driver cinergyt2_driver = {
- .name = "cinergyT2",
- .probe = cinergyt2_probe,
- .disconnect = cinergyt2_disconnect,
- .suspend = cinergyt2_suspend,
- .resume = cinergyt2_resume,
- .id_table = cinergyt2_table
-};
-
-static int __init cinergyt2_init (void)
-{
- int err;
-
- if ((err = usb_register(&cinergyt2_driver)) < 0)
- dprintk(1, "usb_register() failed! (err %i)\n", err);
-
- return err;
-}
-
-static void __exit cinergyt2_exit (void)
-{
- usb_deregister(&cinergyt2_driver);
-}
-
-module_init (cinergyt2_init);
-module_exit (cinergyt2_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Holger Waechtler, Daniel Mack");
diff --git a/drivers/media/dvb/dm1105/Kconfig b/drivers/media/dvb/dm1105/Kconfig
new file mode 100644
index 0000000..1332301
--- /dev/null
+++ b/drivers/media/dvb/dm1105/Kconfig
@@ -0,0 +1,18 @@
+config DVB_DM1105
+ tristate "SDMC DM1105 based PCI cards"
+ depends on DVB_CORE && PCI && I2C
+ select DVB_PLL if !DVB_FE_CUSTOMISE
+ select DVB_STV0299 if !DVB_FE_CUSTOMISE
+ select DVB_STV0288 if !DVB_FE_CUSTOMISE
+ select DVB_STB6000 if !DVB_FE_CUSTOMISE
+ select DVB_CX24116 if !DVB_FE_CUSTOMISE
+ select DVB_SI21XX if !DVB_FE_CUSTOMISE
+ help
+ Support for cards based on the SDMC DM1105 PCI chip like
+ DvbWorld 2002
+
+ Since these cards have no MPEG decoder onboard, they transmit
+ only compressed MPEG data over the PCI bus, so you need
+ an external software decoder to watch TV on your computer.
+
+ Say Y or M if you own such a device and want to use it.
diff --git a/drivers/media/dvb/dm1105/Makefile b/drivers/media/dvb/dm1105/Makefile
new file mode 100644
index 0000000..8ac28b0
--- /dev/null
+++ b/drivers/media/dvb/dm1105/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_DVB_DM1105) += dm1105.o
+
+EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends
diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c
new file mode 100644
index 0000000..f732144
--- /dev/null
+++ b/drivers/media/dvb/dm1105/dm1105.c
@@ -0,0 +1,911 @@
+/*
+ * dm1105.c - driver for DVB cards based on SDMC DM1105 PCI chip
+ *
+ * Copyright (C) 2008 Igor M. Liplianin <liplianin@me.by>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/version.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/input.h>
+#include <media/ir-common.h>
+
+#include "demux.h"
+#include "dmxdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+#include "dvbdev.h"
+#include "dvb-pll.h"
+
+#include "stv0299.h"
+#include "stv0288.h"
+#include "stb6000.h"
+#include "si21xx.h"
+#include "cx24116.h"
+#include "z0194a.h"
+
+/* ----------------------------------------------- */
+/*
+ * PCI ID's
+ */
+#ifndef PCI_VENDOR_ID_TRIGEM
+#define PCI_VENDOR_ID_TRIGEM 0x109f
+#endif
+#ifndef PCI_DEVICE_ID_DM1105
+#define PCI_DEVICE_ID_DM1105 0x036f
+#endif
+#ifndef PCI_DEVICE_ID_DW2002
+#define PCI_DEVICE_ID_DW2002 0x2002
+#endif
+#ifndef PCI_DEVICE_ID_DW2004
+#define PCI_DEVICE_ID_DW2004 0x2004
+#endif
+/* ----------------------------------------------- */
+/* sdmc dm1105 registers */
+
+/* TS Control */
+#define DM1105_TSCTR 0x00
+#define DM1105_DTALENTH 0x04
+
+/* GPIO Interface */
+#define DM1105_GPIOVAL 0x08
+#define DM1105_GPIOCTR 0x0c
+
+/* PID serial number */
+#define DM1105_PIDN 0x10
+
+/* Odd-even secret key select */
+#define DM1105_CWSEL 0x14
+
+/* Host Command Interface */
+#define DM1105_HOST_CTR 0x18
+#define DM1105_HOST_AD 0x1c
+
+/* PCI Interface */
+#define DM1105_CR 0x30
+#define DM1105_RST 0x34
+#define DM1105_STADR 0x38
+#define DM1105_RLEN 0x3c
+#define DM1105_WRP 0x40
+#define DM1105_INTCNT 0x44
+#define DM1105_INTMAK 0x48
+#define DM1105_INTSTS 0x4c
+
+/* CW Value */
+#define DM1105_ODD 0x50
+#define DM1105_EVEN 0x58
+
+/* PID Value */
+#define DM1105_PID 0x60
+
+/* IR Control */
+#define DM1105_IRCTR 0x64
+#define DM1105_IRMODE 0x68
+#define DM1105_SYSTEMCODE 0x6c
+#define DM1105_IRCODE 0x70
+
+/* Unknown Values */
+#define DM1105_ENCRYPT 0x74
+#define DM1105_VER 0x7c
+
+/* I2C Interface */
+#define DM1105_I2CCTR 0x80
+#define DM1105_I2CSTS 0x81
+#define DM1105_I2CDAT 0x82
+#define DM1105_I2C_RA 0x83
+/* ----------------------------------------------- */
+/* Interrupt Mask Bits */
+
+#define INTMAK_TSIRQM 0x01
+#define INTMAK_HIRQM 0x04
+#define INTMAK_IRM 0x08
+#define INTMAK_ALLMASK (INTMAK_TSIRQM | \
+ INTMAK_HIRQM | \
+ INTMAK_IRM)
+#define INTMAK_NONEMASK 0x00
+
+/* Interrupt Status Bits */
+#define INTSTS_TSIRQ 0x01
+#define INTSTS_HIRQ 0x04
+#define INTSTS_IR 0x08
+
+/* IR Control Bits */
+#define DM1105_IR_EN 0x01
+#define DM1105_SYS_CHK 0x02
+#define DM1105_REP_FLG 0x08
+
+/* EEPROM addr */
+#define IIC_24C01_addr 0xa0
+/* Max board count */
+#define DM1105_MAX 0x04
+
+#define DRIVER_NAME "dm1105"
+
+#define DM1105_DMA_PACKETS 47
+#define DM1105_DMA_PACKET_LENGTH (128*4)
+#define DM1105_DMA_BYTES (128 * 4 * DM1105_DMA_PACKETS)
+
+/* GPIO's for LNB power control */
+#define DM1105_LNB_MASK 0x00000000
+#define DM1105_LNB_13V 0x00010100
+#define DM1105_LNB_18V 0x00000100
+
+static int ir_debug;
+module_param(ir_debug, int, 0644);
+MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static u16 ir_codes_dm1105_nec[128] = {
+ [0x0a] = KEY_Q, /*power*/
+ [0x0c] = KEY_M, /*mute*/
+ [0x11] = KEY_1,
+ [0x12] = KEY_2,
+ [0x13] = KEY_3,
+ [0x14] = KEY_4,
+ [0x15] = KEY_5,
+ [0x16] = KEY_6,
+ [0x17] = KEY_7,
+ [0x18] = KEY_8,
+ [0x19] = KEY_9,
+ [0x10] = KEY_0,
+ [0x1c] = KEY_PAGEUP, /*ch+*/
+ [0x0f] = KEY_PAGEDOWN, /*ch-*/
+ [0x1a] = KEY_O, /*vol+*/
+ [0x0e] = KEY_Z, /*vol-*/
+ [0x04] = KEY_R, /*rec*/
+ [0x09] = KEY_D, /*fav*/
+ [0x08] = KEY_BACKSPACE, /*rewind*/
+ [0x07] = KEY_A, /*fast*/
+ [0x0b] = KEY_P, /*pause*/
+ [0x02] = KEY_ESC, /*cancel*/
+ [0x03] = KEY_G, /*tab*/
+ [0x00] = KEY_UP, /*up*/
+ [0x1f] = KEY_ENTER, /*ok*/
+ [0x01] = KEY_DOWN, /*down*/
+ [0x05] = KEY_C, /*cap*/
+ [0x06] = KEY_S, /*stop*/
+ [0x40] = KEY_F, /*full*/
+ [0x1e] = KEY_W, /*tvmode*/
+ [0x1b] = KEY_B, /*recall*/
+};
+
+/* infrared remote control */
+struct infrared {
+ u16 key_map[128];
+ struct input_dev *input_dev;
+ char input_phys[32];
+ struct tasklet_struct ir_tasklet;
+ u32 ir_command;
+};
+
+struct dm1105dvb {
+ /* pci */
+ struct pci_dev *pdev;
+ u8 __iomem *io_mem;
+
+ /* ir */
+ struct infrared ir;
+
+ /* dvb */
+ struct dmx_frontend hw_frontend;
+ struct dmx_frontend mem_frontend;
+ struct dmxdev dmxdev;
+ struct dvb_adapter dvb_adapter;
+ struct dvb_demux demux;
+ struct dvb_frontend *fe;
+ struct dvb_net dvbnet;
+ unsigned int full_ts_users;
+
+ /* i2c */
+ struct i2c_adapter i2c_adap;
+
+ /* dma */
+ dma_addr_t dma_addr;
+ unsigned char *ts_buf;
+ u32 wrp;
+ u32 buffer_size;
+ unsigned int PacketErrorCount;
+ unsigned int dmarst;
+ spinlock_t lock;
+
+};
+
+#define dm_io_mem(reg) ((unsigned long)(&dm1105dvb->io_mem[reg]))
+
+static struct dm1105dvb *dm1105dvb_local;
+
+static int dm1105_i2c_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg *msgs, int num)
+{
+ struct dm1105dvb *dm1105dvb ;
+
+ int addr, rc, i, j, k, len, byte, data;
+ u8 status;
+
+ dm1105dvb = i2c_adap->algo_data;
+ for (i = 0; i < num; i++) {
+ outb(0x00, dm_io_mem(DM1105_I2CCTR));
+ if (msgs[i].flags & I2C_M_RD) {
+ /* read bytes */
+ addr = msgs[i].addr << 1;
+ addr |= 1;
+ outb(addr, dm_io_mem(DM1105_I2CDAT));
+ for (byte = 0; byte < msgs[i].len; byte++)
+ outb(0, dm_io_mem(DM1105_I2CDAT + byte + 1));
+
+ outb(0x81 + msgs[i].len, dm_io_mem(DM1105_I2CCTR));
+ for (j = 0; j < 55; j++) {
+ mdelay(10);
+ status = inb(dm_io_mem(DM1105_I2CSTS));
+ if ((status & 0xc0) == 0x40)
+ break;
+ }
+ if (j >= 55)
+ return -1;
+
+ for (byte = 0; byte < msgs[i].len; byte++) {
+ rc = inb(dm_io_mem(DM1105_I2CDAT + byte + 1));
+ if (rc < 0)
+ goto err;
+ msgs[i].buf[byte] = rc;
+ }
+ } else {
+ if ((msgs[i].buf[0] == 0xf7) && (msgs[i].addr == 0x55)) {
+ /* prepaired for cx24116 firmware */
+ /* Write in small blocks */
+ len = msgs[i].len - 1;
+ k = 1;
+ do {
+ outb(msgs[i].addr << 1, dm_io_mem(DM1105_I2CDAT));
+ outb(0xf7, dm_io_mem(DM1105_I2CDAT + 1));
+ for (byte = 0; byte < (len > 48 ? 48 : len); byte++) {
+ data = msgs[i].buf[k+byte];
+ outb(data, dm_io_mem(DM1105_I2CDAT + byte + 2));
+ }
+ outb(0x82 + (len > 48 ? 48 : len), dm_io_mem(DM1105_I2CCTR));
+ for (j = 0; j < 25; j++) {
+ mdelay(10);
+ status = inb(dm_io_mem(DM1105_I2CSTS));
+ if ((status & 0xc0) == 0x40)
+ break;
+ }
+
+ if (j >= 25)
+ return -1;
+
+ k += 48;
+ len -= 48;
+ } while (len > 0);
+ } else {
+ /* write bytes */
+ outb(msgs[i].addr<<1, dm_io_mem(DM1105_I2CDAT));
+ for (byte = 0; byte < msgs[i].len; byte++) {
+ data = msgs[i].buf[byte];
+ outb(data, dm_io_mem(DM1105_I2CDAT + byte + 1));
+ }
+ outb(0x81 + msgs[i].len, dm_io_mem(DM1105_I2CCTR));
+ for (j = 0; j < 25; j++) {
+ mdelay(10);
+ status = inb(dm_io_mem(DM1105_I2CSTS));
+ if ((status & 0xc0) == 0x40)
+ break;
+ }
+
+ if (j >= 25)
+ return -1;
+ }
+ }
+ }
+ return num;
+ err:
+ return rc;
+}
+
+static u32 functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm dm1105_algo = {
+ .master_xfer = dm1105_i2c_xfer,
+ .functionality = functionality,
+};
+
+static inline struct dm1105dvb *feed_to_dm1105dvb(struct dvb_demux_feed *feed)
+{
+ return container_of(feed->demux, struct dm1105dvb, demux);
+}
+
+static inline struct dm1105dvb *frontend_to_dm1105dvb(struct dvb_frontend *fe)
+{
+ return container_of(fe->dvb, struct dm1105dvb, dvb_adapter);
+}
+
+static int dm1105dvb_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+ struct dm1105dvb *dm1105dvb = frontend_to_dm1105dvb(fe);
+
+ if (voltage == SEC_VOLTAGE_18) {
+ outl(DM1105_LNB_MASK, dm_io_mem(DM1105_GPIOCTR));
+ outl(DM1105_LNB_18V, dm_io_mem(DM1105_GPIOVAL));
+ } else {
+ /*LNB ON-13V by default!*/
+ outl(DM1105_LNB_MASK, dm_io_mem(DM1105_GPIOCTR));
+ outl(DM1105_LNB_13V, dm_io_mem(DM1105_GPIOVAL));
+ }
+
+ return 0;
+}
+
+static void dm1105dvb_set_dma_addr(struct dm1105dvb *dm1105dvb)
+{
+ outl(cpu_to_le32(dm1105dvb->dma_addr), dm_io_mem(DM1105_STADR));
+}
+
+static int __devinit dm1105dvb_dma_map(struct dm1105dvb *dm1105dvb)
+{
+ dm1105dvb->ts_buf = pci_alloc_consistent(dm1105dvb->pdev, 6*DM1105_DMA_BYTES, &dm1105dvb->dma_addr);
+
+ return pci_dma_mapping_error(dm1105dvb->pdev, dm1105dvb->dma_addr);
+}
+
+static void dm1105dvb_dma_unmap(struct dm1105dvb *dm1105dvb)
+{
+ pci_free_consistent(dm1105dvb->pdev, 6*DM1105_DMA_BYTES, dm1105dvb->ts_buf, dm1105dvb->dma_addr);
+}
+
+static void __devinit dm1105dvb_enable_irqs(struct dm1105dvb *dm1105dvb)
+{
+ outb(INTMAK_ALLMASK, dm_io_mem(DM1105_INTMAK));
+ outb(1, dm_io_mem(DM1105_CR));
+}
+
+static void dm1105dvb_disable_irqs(struct dm1105dvb *dm1105dvb)
+{
+ outb(INTMAK_IRM, dm_io_mem(DM1105_INTMAK));
+ outb(0, dm_io_mem(DM1105_CR));
+}
+
+static int dm1105dvb_start_feed(struct dvb_demux_feed *f)
+{
+ struct dm1105dvb *dm1105dvb = feed_to_dm1105dvb(f);
+
+ if (dm1105dvb->full_ts_users++ == 0)
+ dm1105dvb_enable_irqs(dm1105dvb);
+
+ return 0;
+}
+
+static int dm1105dvb_stop_feed(struct dvb_demux_feed *f)
+{
+ struct dm1105dvb *dm1105dvb = feed_to_dm1105dvb(f);
+
+ if (--dm1105dvb->full_ts_users == 0)
+ dm1105dvb_disable_irqs(dm1105dvb);
+
+ return 0;
+}
+
+/* ir tasklet */
+static void dm1105_emit_key(unsigned long parm)
+{
+ struct infrared *ir = (struct infrared *) parm;
+ u32 ircom = ir->ir_command;
+ u8 data;
+ u16 keycode;
+
+ data = (ircom >> 8) & 0x7f;
+
+ input_event(ir->input_dev, EV_MSC, MSC_RAW, (0x0000f8 << 16) | data);
+ input_event(ir->input_dev, EV_MSC, MSC_SCAN, data);
+ keycode = ir->key_map[data];
+
+ if (!keycode)
+ return;
+
+ input_event(ir->input_dev, EV_KEY, keycode, 1);
+ input_sync(ir->input_dev);
+ input_event(ir->input_dev, EV_KEY, keycode, 0);
+ input_sync(ir->input_dev);
+
+}
+
+static irqreturn_t dm1105dvb_irq(int irq, void *dev_id)
+{
+ struct dm1105dvb *dm1105dvb = dev_id;
+ unsigned int piece;
+ unsigned int nbpackets;
+ u32 command;
+ u32 nextwrp;
+ u32 oldwrp;
+
+ /* Read-Write INSTS Ack's Interrupt for DM1105 chip 16.03.2008 */
+ unsigned int intsts = inb(dm_io_mem(DM1105_INTSTS));
+ outb(intsts, dm_io_mem(DM1105_INTSTS));
+
+ switch (intsts) {
+ case INTSTS_TSIRQ:
+ case (INTSTS_TSIRQ | INTSTS_IR):
+ nextwrp = inl(dm_io_mem(DM1105_WRP)) -
+ inl(dm_io_mem(DM1105_STADR)) ;
+ oldwrp = dm1105dvb->wrp;
+ spin_lock(&dm1105dvb->lock);
+ if (!((dm1105dvb->ts_buf[oldwrp] == 0x47) &&
+ (dm1105dvb->ts_buf[oldwrp + 188] == 0x47) &&
+ (dm1105dvb->ts_buf[oldwrp + 188 * 2] == 0x47))) {
+ dm1105dvb->PacketErrorCount++;
+ /* bad packet found */
+ if ((dm1105dvb->PacketErrorCount >= 2) &&
+ (dm1105dvb->dmarst == 0)) {
+ outb(1, dm_io_mem(DM1105_RST));
+ dm1105dvb->wrp = 0;
+ dm1105dvb->PacketErrorCount = 0;
+ dm1105dvb->dmarst = 0;
+ spin_unlock(&dm1105dvb->lock);
+ return IRQ_HANDLED;
+ }
+ }
+ if (nextwrp < oldwrp) {
+ piece = dm1105dvb->buffer_size - oldwrp;
+ memcpy(dm1105dvb->ts_buf + dm1105dvb->buffer_size, dm1105dvb->ts_buf, nextwrp);
+ nbpackets = (piece + nextwrp)/188;
+ } else {
+ nbpackets = (nextwrp - oldwrp)/188;
+ }
+ dvb_dmx_swfilter_packets(&dm1105dvb->demux, &dm1105dvb->ts_buf[oldwrp], nbpackets);
+ dm1105dvb->wrp = nextwrp;
+ spin_unlock(&dm1105dvb->lock);
+ break;
+ case INTSTS_IR:
+ command = inl(dm_io_mem(DM1105_IRCODE));
+ if (ir_debug)
+ printk("dm1105: received byte 0x%04x\n", command);
+
+ dm1105dvb->ir.ir_command = command;
+ tasklet_schedule(&dm1105dvb->ir.ir_tasklet);
+ break;
+ }
+ return IRQ_HANDLED;
+
+
+}
+
+/* register with input layer */
+static void input_register_keys(struct infrared *ir)
+{
+ int i;
+
+ memset(ir->input_dev->keybit, 0, sizeof(ir->input_dev->keybit));
+
+ for (i = 0; i < ARRAY_SIZE(ir->key_map); i++)
+ set_bit(ir->key_map[i], ir->input_dev->keybit);
+
+ ir->input_dev->keycode = ir->key_map;
+ ir->input_dev->keycodesize = sizeof(ir->key_map[0]);
+ ir->input_dev->keycodemax = ARRAY_SIZE(ir->key_map);
+}
+
+int __devinit dm1105_ir_init(struct dm1105dvb *dm1105)
+{
+ struct input_dev *input_dev;
+ int err;
+
+ dm1105dvb_local = dm1105;
+
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ return -ENOMEM;
+
+ dm1105->ir.input_dev = input_dev;
+ snprintf(dm1105->ir.input_phys, sizeof(dm1105->ir.input_phys),
+ "pci-%s/ir0", pci_name(dm1105->pdev));
+
+ input_dev->evbit[0] = BIT(EV_KEY);
+ input_dev->name = "DVB on-card IR receiver";
+
+ input_dev->phys = dm1105->ir.input_phys;
+ input_dev->id.bustype = BUS_PCI;
+ input_dev->id.version = 2;
+ if (dm1105->pdev->subsystem_vendor) {
+ input_dev->id.vendor = dm1105->pdev->subsystem_vendor;
+ input_dev->id.product = dm1105->pdev->subsystem_device;
+ } else {
+ input_dev->id.vendor = dm1105->pdev->vendor;
+ input_dev->id.product = dm1105->pdev->device;
+ }
+ input_dev->dev.parent = &dm1105->pdev->dev;
+ /* initial keymap */
+ memcpy(dm1105->ir.key_map, ir_codes_dm1105_nec, sizeof dm1105->ir.key_map);
+ input_register_keys(&dm1105->ir);
+ err = input_register_device(input_dev);
+ if (err) {
+ input_free_device(input_dev);
+ return err;
+ }
+
+ tasklet_init(&dm1105->ir.ir_tasklet, dm1105_emit_key, (unsigned long) &dm1105->ir);
+
+ return 0;
+}
+
+
+void __devexit dm1105_ir_exit(struct dm1105dvb *dm1105)
+{
+ tasklet_kill(&dm1105->ir.ir_tasklet);
+ input_unregister_device(dm1105->ir.input_dev);
+
+}
+
+static int __devinit dm1105dvb_hw_init(struct dm1105dvb *dm1105dvb)
+{
+ dm1105dvb_disable_irqs(dm1105dvb);
+
+ outb(0, dm_io_mem(DM1105_HOST_CTR));
+
+ /*DATALEN 188,*/
+ outb(188, dm_io_mem(DM1105_DTALENTH));
+ /*TS_STRT TS_VALP MSBFIRST TS_MODE ALPAS TSPES*/
+ outw(0xc10a, dm_io_mem(DM1105_TSCTR));
+
+ /* map DMA and set address */
+ dm1105dvb_dma_map(dm1105dvb);
+ dm1105dvb_set_dma_addr(dm1105dvb);
+ /* big buffer */
+ outl(5*DM1105_DMA_BYTES, dm_io_mem(DM1105_RLEN));
+ outb(47, dm_io_mem(DM1105_INTCNT));
+
+ /* IR NEC mode enable */
+ outb((DM1105_IR_EN | DM1105_SYS_CHK), dm_io_mem(DM1105_IRCTR));
+ outb(0, dm_io_mem(DM1105_IRMODE));
+ outw(0, dm_io_mem(DM1105_SYSTEMCODE));
+
+ return 0;
+}
+
+static void dm1105dvb_hw_exit(struct dm1105dvb *dm1105dvb)
+{
+ dm1105dvb_disable_irqs(dm1105dvb);
+
+ /* IR disable */
+ outb(0, dm_io_mem(DM1105_IRCTR));
+ outb(INTMAK_NONEMASK, dm_io_mem(DM1105_INTMAK));
+
+ dm1105dvb_dma_unmap(dm1105dvb);
+}
+
+static struct stv0288_config earda_config = {
+ .demod_address = 0x68,
+ .min_delay_ms = 100,
+};
+
+static struct si21xx_config serit_config = {
+ .demod_address = 0x68,
+ .min_delay_ms = 100,
+
+};
+
+static struct cx24116_config serit_sp2633_config = {
+ .demod_address = 0x55,
+};
+
+static int __devinit frontend_init(struct dm1105dvb *dm1105dvb)
+{
+ int ret;
+
+ switch (dm1105dvb->pdev->subsystem_device) {
+ case PCI_DEVICE_ID_DW2002:
+ dm1105dvb->fe = dvb_attach(
+ stv0299_attach, &sharp_z0194a_config,
+ &dm1105dvb->i2c_adap);
+
+ if (dm1105dvb->fe) {
+ dm1105dvb->fe->ops.set_voltage =
+ dm1105dvb_set_voltage;
+ dvb_attach(dvb_pll_attach, dm1105dvb->fe, 0x60,
+ &dm1105dvb->i2c_adap, DVB_PLL_OPERA1);
+ }
+
+ if (!dm1105dvb->fe) {
+ dm1105dvb->fe = dvb_attach(
+ stv0288_attach, &earda_config,
+ &dm1105dvb->i2c_adap);
+ if (dm1105dvb->fe) {
+ dm1105dvb->fe->ops.set_voltage =
+ dm1105dvb_set_voltage;
+ dvb_attach(stb6000_attach, dm1105dvb->fe, 0x61,
+ &dm1105dvb->i2c_adap);
+ }
+ }
+
+ if (!dm1105dvb->fe) {
+ dm1105dvb->fe = dvb_attach(
+ si21xx_attach, &serit_config,
+ &dm1105dvb->i2c_adap);
+ if (dm1105dvb->fe)
+ dm1105dvb->fe->ops.set_voltage =
+ dm1105dvb_set_voltage;
+ }
+ break;
+ case PCI_DEVICE_ID_DW2004:
+ dm1105dvb->fe = dvb_attach(
+ cx24116_attach, &serit_sp2633_config,
+ &dm1105dvb->i2c_adap);
+ if (dm1105dvb->fe)
+ dm1105dvb->fe->ops.set_voltage = dm1105dvb_set_voltage;
+ break;
+ }
+
+ if (!dm1105dvb->fe) {
+ dev_err(&dm1105dvb->pdev->dev, "could not attach frontend\n");
+ return -ENODEV;
+ }
+
+ ret = dvb_register_frontend(&dm1105dvb->dvb_adapter, dm1105dvb->fe);
+ if (ret < 0) {
+ if (dm1105dvb->fe->ops.release)
+ dm1105dvb->fe->ops.release(dm1105dvb->fe);
+ dm1105dvb->fe = NULL;
+ return ret;
+ }
+
+ return 0;
+}
+
+static void __devinit dm1105dvb_read_mac(struct dm1105dvb *dm1105dvb, u8 *mac)
+{
+ static u8 command[1] = { 0x28 };
+
+ struct i2c_msg msg[] = {
+ { .addr = IIC_24C01_addr >> 1, .flags = 0,
+ .buf = command, .len = 1 },
+ { .addr = IIC_24C01_addr >> 1, .flags = I2C_M_RD,
+ .buf = mac, .len = 6 },
+ };
+
+ dm1105_i2c_xfer(&dm1105dvb->i2c_adap, msg , 2);
+ dev_info(&dm1105dvb->pdev->dev, "MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+}
+
+static int __devinit dm1105_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct dm1105dvb *dm1105dvb;
+ struct dvb_adapter *dvb_adapter;
+ struct dvb_demux *dvbdemux;
+ struct dmx_demux *dmx;
+ int ret = -ENOMEM;
+
+ dm1105dvb = kzalloc(sizeof(struct dm1105dvb), GFP_KERNEL);
+ if (!dm1105dvb)
+ goto out;
+
+ dm1105dvb->pdev = pdev;
+ dm1105dvb->buffer_size = 5 * DM1105_DMA_BYTES;
+ dm1105dvb->PacketErrorCount = 0;
+ dm1105dvb->dmarst = 0;
+
+ ret = pci_enable_device(pdev);
+ if (ret < 0)
+ goto err_kfree;
+
+ ret = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+ if (ret < 0)
+ goto err_pci_disable_device;
+
+ pci_set_master(pdev);
+
+ ret = pci_request_regions(pdev, DRIVER_NAME);
+ if (ret < 0)
+ goto err_pci_disable_device;
+
+ dm1105dvb->io_mem = pci_iomap(pdev, 0, pci_resource_len(pdev, 0));
+ if (!dm1105dvb->io_mem) {
+ ret = -EIO;
+ goto err_pci_release_regions;
+ }
+
+ spin_lock_init(&dm1105dvb->lock);
+ pci_set_drvdata(pdev, dm1105dvb);
+
+ ret = request_irq(pdev->irq, dm1105dvb_irq, IRQF_SHARED, DRIVER_NAME, dm1105dvb);
+ if (ret < 0)
+ goto err_pci_iounmap;
+
+ ret = dm1105dvb_hw_init(dm1105dvb);
+ if (ret < 0)
+ goto err_free_irq;
+
+ /* i2c */
+ i2c_set_adapdata(&dm1105dvb->i2c_adap, dm1105dvb);
+ strcpy(dm1105dvb->i2c_adap.name, DRIVER_NAME);
+ dm1105dvb->i2c_adap.owner = THIS_MODULE;
+ dm1105dvb->i2c_adap.class = I2C_CLASS_TV_DIGITAL;
+ dm1105dvb->i2c_adap.dev.parent = &pdev->dev;
+ dm1105dvb->i2c_adap.algo = &dm1105_algo;
+ dm1105dvb->i2c_adap.algo_data = dm1105dvb;
+ ret = i2c_add_adapter(&dm1105dvb->i2c_adap);
+
+ if (ret < 0)
+ goto err_dm1105dvb_hw_exit;
+
+ /* dvb */
+ ret = dvb_register_adapter(&dm1105dvb->dvb_adapter, DRIVER_NAME,
+ THIS_MODULE, &pdev->dev, adapter_nr);
+ if (ret < 0)
+ goto err_i2c_del_adapter;
+
+ dvb_adapter = &dm1105dvb->dvb_adapter;
+
+ dm1105dvb_read_mac(dm1105dvb, dvb_adapter->proposed_mac);
+
+ dvbdemux = &dm1105dvb->demux;
+ dvbdemux->filternum = 256;
+ dvbdemux->feednum = 256;
+ dvbdemux->start_feed = dm1105dvb_start_feed;
+ dvbdemux->stop_feed = dm1105dvb_stop_feed;
+ dvbdemux->dmx.capabilities = (DMX_TS_FILTERING |
+ DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING);
+ ret = dvb_dmx_init(dvbdemux);
+ if (ret < 0)
+ goto err_dvb_unregister_adapter;
+
+ dmx = &dvbdemux->dmx;
+ dm1105dvb->dmxdev.filternum = 256;
+ dm1105dvb->dmxdev.demux = dmx;
+ dm1105dvb->dmxdev.capabilities = 0;
+
+ ret = dvb_dmxdev_init(&dm1105dvb->dmxdev, dvb_adapter);
+ if (ret < 0)
+ goto err_dvb_dmx_release;
+
+ dm1105dvb->hw_frontend.source = DMX_FRONTEND_0;
+
+ ret = dmx->add_frontend(dmx, &dm1105dvb->hw_frontend);
+ if (ret < 0)
+ goto err_dvb_dmxdev_release;
+
+ dm1105dvb->mem_frontend.source = DMX_MEMORY_FE;
+
+ ret = dmx->add_frontend(dmx, &dm1105dvb->mem_frontend);
+ if (ret < 0)
+ goto err_remove_hw_frontend;
+
+ ret = dmx->connect_frontend(dmx, &dm1105dvb->hw_frontend);
+ if (ret < 0)
+ goto err_remove_mem_frontend;
+
+ ret = frontend_init(dm1105dvb);
+ if (ret < 0)
+ goto err_disconnect_frontend;
+
+ dvb_net_init(dvb_adapter, &dm1105dvb->dvbnet, dmx);
+ dm1105_ir_init(dm1105dvb);
+out:
+ return ret;
+
+err_disconnect_frontend:
+ dmx->disconnect_frontend(dmx);
+err_remove_mem_frontend:
+ dmx->remove_frontend(dmx, &dm1105dvb->mem_frontend);
+err_remove_hw_frontend:
+ dmx->remove_frontend(dmx, &dm1105dvb->hw_frontend);
+err_dvb_dmxdev_release:
+ dvb_dmxdev_release(&dm1105dvb->dmxdev);
+err_dvb_dmx_release:
+ dvb_dmx_release(dvbdemux);
+err_dvb_unregister_adapter:
+ dvb_unregister_adapter(dvb_adapter);
+err_i2c_del_adapter:
+ i2c_del_adapter(&dm1105dvb->i2c_adap);
+err_dm1105dvb_hw_exit:
+ dm1105dvb_hw_exit(dm1105dvb);
+err_free_irq:
+ free_irq(pdev->irq, dm1105dvb);
+err_pci_iounmap:
+ pci_iounmap(pdev, dm1105dvb->io_mem);
+err_pci_release_regions:
+ pci_release_regions(pdev);
+err_pci_disable_device:
+ pci_disable_device(pdev);
+err_kfree:
+ pci_set_drvdata(pdev, NULL);
+ kfree(dm1105dvb);
+ goto out;
+}
+
+static void __devexit dm1105_remove(struct pci_dev *pdev)
+{
+ struct dm1105dvb *dm1105dvb = pci_get_drvdata(pdev);
+ struct dvb_adapter *dvb_adapter = &dm1105dvb->dvb_adapter;
+ struct dvb_demux *dvbdemux = &dm1105dvb->demux;
+ struct dmx_demux *dmx = &dvbdemux->dmx;
+
+ dm1105_ir_exit(dm1105dvb);
+ dmx->close(dmx);
+ dvb_net_release(&dm1105dvb->dvbnet);
+ if (dm1105dvb->fe)
+ dvb_unregister_frontend(dm1105dvb->fe);
+
+ dmx->disconnect_frontend(dmx);
+ dmx->remove_frontend(dmx, &dm1105dvb->mem_frontend);
+ dmx->remove_frontend(dmx, &dm1105dvb->hw_frontend);
+ dvb_dmxdev_release(&dm1105dvb->dmxdev);
+ dvb_dmx_release(dvbdemux);
+ dvb_unregister_adapter(dvb_adapter);
+ if (&dm1105dvb->i2c_adap)
+ i2c_del_adapter(&dm1105dvb->i2c_adap);
+
+ dm1105dvb_hw_exit(dm1105dvb);
+ synchronize_irq(pdev->irq);
+ free_irq(pdev->irq, dm1105dvb);
+ pci_iounmap(pdev, dm1105dvb->io_mem);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+ kfree(dm1105dvb);
+}
+
+static struct pci_device_id dm1105_id_table[] __devinitdata = {
+ {
+ .vendor = PCI_VENDOR_ID_TRIGEM,
+ .device = PCI_DEVICE_ID_DM1105,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_DEVICE_ID_DW2002,
+ }, {
+ .vendor = PCI_VENDOR_ID_TRIGEM,
+ .device = PCI_DEVICE_ID_DM1105,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_DEVICE_ID_DW2004,
+ }, {
+ /* empty */
+ },
+};
+
+MODULE_DEVICE_TABLE(pci, dm1105_id_table);
+
+static struct pci_driver dm1105_driver = {
+ .name = DRIVER_NAME,
+ .id_table = dm1105_id_table,
+ .probe = dm1105_probe,
+ .remove = __devexit_p(dm1105_remove),
+};
+
+static int __init dm1105_init(void)
+{
+ return pci_register_driver(&dm1105_driver);
+}
+
+static void __exit dm1105_exit(void)
+{
+ pci_unregister_driver(&dm1105_driver);
+}
+
+module_init(dm1105_init);
+module_exit(dm1105_exit);
+
+MODULE_AUTHOR("Igor M. Liplianin <liplianin@me.by>");
+MODULE_DESCRIPTION("SDMC DM1105 DVB driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c
index 3526e3e..f170e82 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.c
@@ -40,6 +40,7 @@
#include "dvb_frontend.h"
#include "dvbdev.h"
+#include <linux/dvb/version.h>
static int dvb_frontend_debug;
static int dvb_shutdown_timeout;
@@ -755,6 +756,539 @@
return 0;
}
+struct dtv_cmds_h dtv_cmds[] = {
+ [DTV_TUNE] = {
+ .name = "DTV_TUNE",
+ .cmd = DTV_TUNE,
+ .set = 1,
+ },
+ [DTV_CLEAR] = {
+ .name = "DTV_CLEAR",
+ .cmd = DTV_CLEAR,
+ .set = 1,
+ },
+
+ /* Set */
+ [DTV_FREQUENCY] = {
+ .name = "DTV_FREQUENCY",
+ .cmd = DTV_FREQUENCY,
+ .set = 1,
+ },
+ [DTV_BANDWIDTH_HZ] = {
+ .name = "DTV_BANDWIDTH_HZ",
+ .cmd = DTV_BANDWIDTH_HZ,
+ .set = 1,
+ },
+ [DTV_MODULATION] = {
+ .name = "DTV_MODULATION",
+ .cmd = DTV_MODULATION,
+ .set = 1,
+ },
+ [DTV_INVERSION] = {
+ .name = "DTV_INVERSION",
+ .cmd = DTV_INVERSION,
+ .set = 1,
+ },
+ [DTV_DISEQC_MASTER] = {
+ .name = "DTV_DISEQC_MASTER",
+ .cmd = DTV_DISEQC_MASTER,
+ .set = 1,
+ .buffer = 1,
+ },
+ [DTV_SYMBOL_RATE] = {
+ .name = "DTV_SYMBOL_RATE",
+ .cmd = DTV_SYMBOL_RATE,
+ .set = 1,
+ },
+ [DTV_INNER_FEC] = {
+ .name = "DTV_INNER_FEC",
+ .cmd = DTV_INNER_FEC,
+ .set = 1,
+ },
+ [DTV_VOLTAGE] = {
+ .name = "DTV_VOLTAGE",
+ .cmd = DTV_VOLTAGE,
+ .set = 1,
+ },
+ [DTV_TONE] = {
+ .name = "DTV_TONE",
+ .cmd = DTV_TONE,
+ .set = 1,
+ },
+ [DTV_PILOT] = {
+ .name = "DTV_PILOT",
+ .cmd = DTV_PILOT,
+ .set = 1,
+ },
+ [DTV_ROLLOFF] = {
+ .name = "DTV_ROLLOFF",
+ .cmd = DTV_ROLLOFF,
+ .set = 1,
+ },
+ [DTV_DELIVERY_SYSTEM] = {
+ .name = "DTV_DELIVERY_SYSTEM",
+ .cmd = DTV_DELIVERY_SYSTEM,
+ .set = 1,
+ },
+ [DTV_HIERARCHY] = {
+ .name = "DTV_HIERARCHY",
+ .cmd = DTV_HIERARCHY,
+ .set = 1,
+ },
+ [DTV_CODE_RATE_HP] = {
+ .name = "DTV_CODE_RATE_HP",
+ .cmd = DTV_CODE_RATE_HP,
+ .set = 1,
+ },
+ [DTV_CODE_RATE_LP] = {
+ .name = "DTV_CODE_RATE_LP",
+ .cmd = DTV_CODE_RATE_LP,
+ .set = 1,
+ },
+ [DTV_GUARD_INTERVAL] = {
+ .name = "DTV_GUARD_INTERVAL",
+ .cmd = DTV_GUARD_INTERVAL,
+ .set = 1,
+ },
+ [DTV_TRANSMISSION_MODE] = {
+ .name = "DTV_TRANSMISSION_MODE",
+ .cmd = DTV_TRANSMISSION_MODE,
+ .set = 1,
+ },
+ /* Get */
+ [DTV_DISEQC_SLAVE_REPLY] = {
+ .name = "DTV_DISEQC_SLAVE_REPLY",
+ .cmd = DTV_DISEQC_SLAVE_REPLY,
+ .set = 0,
+ .buffer = 1,
+ },
+ [DTV_API_VERSION] = {
+ .name = "DTV_API_VERSION",
+ .cmd = DTV_API_VERSION,
+ .set = 0,
+ },
+ [DTV_CODE_RATE_HP] = {
+ .name = "DTV_CODE_RATE_HP",
+ .cmd = DTV_CODE_RATE_HP,
+ .set = 0,
+ },
+ [DTV_CODE_RATE_LP] = {
+ .name = "DTV_CODE_RATE_LP",
+ .cmd = DTV_CODE_RATE_LP,
+ .set = 0,
+ },
+ [DTV_GUARD_INTERVAL] = {
+ .name = "DTV_GUARD_INTERVAL",
+ .cmd = DTV_GUARD_INTERVAL,
+ .set = 0,
+ },
+ [DTV_TRANSMISSION_MODE] = {
+ .name = "DTV_TRANSMISSION_MODE",
+ .cmd = DTV_TRANSMISSION_MODE,
+ .set = 0,
+ },
+ [DTV_HIERARCHY] = {
+ .name = "DTV_HIERARCHY",
+ .cmd = DTV_HIERARCHY,
+ .set = 0,
+ },
+};
+
+void dtv_property_dump(struct dtv_property *tvp)
+{
+ int i;
+
+ if (tvp->cmd <= 0 || tvp->cmd > DTV_MAX_COMMAND) {
+ printk("%s: tvp.cmd = 0x%08x (undefined/unknown/invalid)\n",
+ __func__, tvp->cmd);
+ return;
+ }
+
+ printk("%s() tvp.cmd = 0x%08x (%s)\n"
+ ,__FUNCTION__
+ ,tvp->cmd
+ ,dtv_cmds[ tvp->cmd ].name);
+
+ if(dtv_cmds[ tvp->cmd ].buffer) {
+
+ printk("%s() tvp.u.buffer.len = 0x%02x\n"
+ ,__FUNCTION__
+ ,tvp->u.buffer.len);
+
+ for(i = 0; i < tvp->u.buffer.len; i++)
+ printk("%s() tvp.u.buffer.data[0x%02x] = 0x%02x\n"
+ ,__FUNCTION__
+ ,i
+ ,tvp->u.buffer.data[i]);
+
+ } else
+ printk("%s() tvp.u.data = 0x%08x\n", __FUNCTION__, tvp->u.data);
+}
+
+int is_legacy_delivery_system(fe_delivery_system_t s)
+{
+ if((s == SYS_UNDEFINED) || (s == SYS_DVBC_ANNEX_AC) ||
+ (s == SYS_DVBC_ANNEX_B) || (s == SYS_DVBT) || (s == SYS_DVBS))
+ return 1;
+
+ return 0;
+}
+
+/* Synchronise the legacy tuning parameters into the cache, so that demodulator
+ * drivers can use a single set_frontend tuning function, regardless of whether
+ * it's being used for the legacy or new API, reducing code and complexity.
+ */
+void dtv_property_cache_sync(struct dvb_frontend *fe, struct dvb_frontend_parameters *p)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ printk("%s()\n", __FUNCTION__);
+
+ c->frequency = p->frequency;
+ c->inversion = p->inversion;
+
+ switch (fe->ops.info.type) {
+ case FE_QPSK:
+ c->modulation = QPSK; /* implied for DVB-S in legacy API */
+ c->rolloff = ROLLOFF_35;/* implied for DVB-S */
+ c->symbol_rate = p->u.qpsk.symbol_rate;
+ c->fec_inner = p->u.qpsk.fec_inner;
+ c->delivery_system = SYS_DVBS;
+ break;
+ case FE_QAM:
+ c->symbol_rate = p->u.qam.symbol_rate;
+ c->fec_inner = p->u.qam.fec_inner;
+ c->modulation = p->u.qam.modulation;
+ c->delivery_system = SYS_DVBC_ANNEX_AC;
+ break;
+ case FE_OFDM:
+ if (p->u.ofdm.bandwidth == BANDWIDTH_6_MHZ)
+ c->bandwidth_hz = 6000000;
+ else if (p->u.ofdm.bandwidth == BANDWIDTH_7_MHZ)
+ c->bandwidth_hz = 7000000;
+ else if (p->u.ofdm.bandwidth == BANDWIDTH_8_MHZ)
+ c->bandwidth_hz = 8000000;
+ else
+ /* Including BANDWIDTH_AUTO */
+ c->bandwidth_hz = 0;
+ c->code_rate_HP = p->u.ofdm.code_rate_HP;
+ c->code_rate_LP = p->u.ofdm.code_rate_LP;
+ c->modulation = p->u.ofdm.constellation;
+ c->transmission_mode = p->u.ofdm.transmission_mode;
+ c->guard_interval = p->u.ofdm.guard_interval;
+ c->hierarchy = p->u.ofdm.hierarchy_information;
+ c->delivery_system = SYS_DVBT;
+ break;
+ case FE_ATSC:
+ c->modulation = p->u.vsb.modulation;
+ if ((c->modulation == VSB_8) || (c->modulation == VSB_16))
+ c->delivery_system = SYS_ATSC;
+ else
+ c->delivery_system = SYS_DVBC_ANNEX_B;
+ break;
+ }
+}
+
+/* Ensure the cached values are set correctly in the frontend
+ * legacy tuning structures, for the advanced tuning API.
+ */
+void dtv_property_legacy_params_sync(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dvb_frontend_parameters *p = &fepriv->parameters;
+
+ printk("%s()\n", __FUNCTION__);
+
+ p->frequency = c->frequency;
+ p->inversion = c->inversion;
+
+ switch (fe->ops.info.type) {
+ case FE_QPSK:
+ printk("%s() Preparing QPSK req\n", __FUNCTION__);
+ p->u.qpsk.symbol_rate = c->symbol_rate;
+ p->u.qpsk.fec_inner = c->fec_inner;
+ c->delivery_system = SYS_DVBS;
+ break;
+ case FE_QAM:
+ printk("%s() Preparing QAM req\n", __FUNCTION__);
+ p->u.qam.symbol_rate = c->symbol_rate;
+ p->u.qam.fec_inner = c->fec_inner;
+ p->u.qam.modulation = c->modulation;
+ c->delivery_system = SYS_DVBC_ANNEX_AC;
+ break;
+ case FE_OFDM:
+ printk("%s() Preparing OFDM req\n", __FUNCTION__);
+ if (c->bandwidth_hz == 6000000)
+ p->u.ofdm.bandwidth = BANDWIDTH_6_MHZ;
+ else if (c->bandwidth_hz == 7000000)
+ p->u.ofdm.bandwidth = BANDWIDTH_7_MHZ;
+ else if (c->bandwidth_hz == 8000000)
+ p->u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
+ else
+ p->u.ofdm.bandwidth = BANDWIDTH_AUTO;
+ p->u.ofdm.code_rate_HP = c->code_rate_HP;
+ p->u.ofdm.code_rate_LP = c->code_rate_LP;
+ p->u.ofdm.constellation = c->modulation;
+ p->u.ofdm.transmission_mode = c->transmission_mode;
+ p->u.ofdm.guard_interval = c->guard_interval;
+ p->u.ofdm.hierarchy_information = c->hierarchy;
+ c->delivery_system = SYS_DVBT;
+ break;
+ case FE_ATSC:
+ printk("%s() Preparing VSB req\n", __FUNCTION__);
+ p->u.vsb.modulation = c->modulation;
+ if ((c->modulation == VSB_8) || (c->modulation == VSB_16))
+ c->delivery_system = SYS_ATSC;
+ else
+ c->delivery_system = SYS_DVBC_ANNEX_B;
+ break;
+ }
+}
+
+/* Ensure the cached values are set correctly in the frontend
+ * legacy tuning structures, for the legacy tuning API.
+ */
+void dtv_property_adv_params_sync(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dvb_frontend_parameters *p = &fepriv->parameters;
+
+ printk("%s()\n", __FUNCTION__);
+
+ p->frequency = c->frequency;
+ p->inversion = c->inversion;
+
+ switch(c->modulation) {
+ case PSK_8:
+ case APSK_16:
+ case QPSK:
+ p->u.qpsk.symbol_rate = c->symbol_rate;
+ p->u.qpsk.fec_inner = c->fec_inner;
+ break;
+ default:
+ break;
+ }
+
+ if(c->delivery_system == SYS_ISDBT) {
+ /* Fake out a generic DVB-T request so we pass validation in the ioctl */
+ p->frequency = c->frequency;
+ p->inversion = INVERSION_AUTO;
+ p->u.ofdm.constellation = QAM_AUTO;
+ p->u.ofdm.code_rate_HP = FEC_AUTO;
+ p->u.ofdm.code_rate_LP = FEC_AUTO;
+ p->u.ofdm.bandwidth = BANDWIDTH_AUTO;
+ p->u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO;
+ p->u.ofdm.guard_interval = GUARD_INTERVAL_AUTO;
+ p->u.ofdm.hierarchy_information = HIERARCHY_AUTO;
+ }
+}
+
+void dtv_property_cache_submit(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ printk("%s()\n", __FUNCTION__);
+
+ /* For legacy delivery systems we don't need the delivery_system to
+ * be specified, but we populate the older structures from the cache
+ * so we can call set_frontend on older drivers.
+ */
+ if(is_legacy_delivery_system(c->delivery_system)) {
+
+ printk("%s() legacy, modulation = %d\n", __FUNCTION__, c->modulation);
+ dtv_property_legacy_params_sync(fe);
+
+ } else {
+ printk("%s() adv, modulation = %d\n", __FUNCTION__, c->modulation);
+
+ /* For advanced delivery systems / modulation types ...
+ * we seed the lecacy dvb_frontend_parameters structure
+ * so that the sanity checking code later in the IOCTL processing
+ * can validate our basic frequency ranges, symbolrates, modulation
+ * etc.
+ */
+ dtv_property_adv_params_sync(fe);
+ }
+}
+
+static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file,
+ unsigned int cmd, void *parg);
+static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file,
+ unsigned int cmd, void *parg);
+
+int dtv_property_process_get(struct dvb_frontend *fe, struct dtv_property *tvp,
+ struct inode *inode, struct file *file)
+{
+ int r = 0;
+
+ printk("%s()\n", __FUNCTION__);
+
+ dtv_property_dump(tvp);
+
+ /* Allow the frontend to validate incoming properties */
+ if (fe->ops.get_property)
+ r = fe->ops.get_property(fe, tvp);
+
+ if (r < 0)
+ return r;
+
+ switch(tvp->cmd) {
+ case DTV_FREQUENCY:
+ tvp->u.data = fe->dtv_property_cache.frequency;
+ break;
+ case DTV_MODULATION:
+ tvp->u.data = fe->dtv_property_cache.modulation;
+ break;
+ case DTV_BANDWIDTH_HZ:
+ tvp->u.data = fe->dtv_property_cache.bandwidth_hz;
+ break;
+ case DTV_INVERSION:
+ tvp->u.data = fe->dtv_property_cache.inversion;
+ break;
+ case DTV_SYMBOL_RATE:
+ tvp->u.data = fe->dtv_property_cache.symbol_rate;
+ break;
+ case DTV_INNER_FEC:
+ tvp->u.data = fe->dtv_property_cache.fec_inner;
+ break;
+ case DTV_PILOT:
+ tvp->u.data = fe->dtv_property_cache.pilot;
+ break;
+ case DTV_ROLLOFF:
+ tvp->u.data = fe->dtv_property_cache.rolloff;
+ break;
+ case DTV_DELIVERY_SYSTEM:
+ tvp->u.data = fe->dtv_property_cache.delivery_system;
+ break;
+ case DTV_VOLTAGE:
+ tvp->u.data = fe->dtv_property_cache.voltage;
+ break;
+ case DTV_TONE:
+ tvp->u.data = fe->dtv_property_cache.sectone;
+ break;
+ case DTV_API_VERSION:
+ tvp->u.data = (DVB_API_VERSION << 8) | DVB_API_VERSION_MINOR;
+ break;
+ case DTV_CODE_RATE_HP:
+ tvp->u.data = fe->dtv_property_cache.code_rate_HP;
+ break;
+ case DTV_CODE_RATE_LP:
+ tvp->u.data = fe->dtv_property_cache.code_rate_LP;
+ break;
+ case DTV_GUARD_INTERVAL:
+ tvp->u.data = fe->dtv_property_cache.guard_interval;
+ break;
+ case DTV_TRANSMISSION_MODE:
+ tvp->u.data = fe->dtv_property_cache.transmission_mode;
+ break;
+ case DTV_HIERARCHY:
+ tvp->u.data = fe->dtv_property_cache.hierarchy;
+ break;
+ default:
+ r = -1;
+ }
+
+ return r;
+}
+
+int dtv_property_process_set(struct dvb_frontend *fe, struct dtv_property *tvp,
+ struct inode *inode, struct file *file)
+{
+ int r = 0;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ printk("%s()\n", __FUNCTION__);
+ dtv_property_dump(tvp);
+
+ /* Allow the frontend to validate incoming properties */
+ if (fe->ops.set_property)
+ r = fe->ops.set_property(fe, tvp);
+
+ if (r < 0)
+ return r;
+
+ switch(tvp->cmd) {
+ case DTV_CLEAR:
+ /* Reset a cache of data specific to the frontend here. This does
+ * not effect hardware.
+ */
+ printk("%s() Flushing property cache\n", __FUNCTION__);
+ memset(&fe->dtv_property_cache, 0, sizeof(struct dtv_frontend_properties));
+ fe->dtv_property_cache.state = tvp->cmd;
+ fe->dtv_property_cache.delivery_system = SYS_UNDEFINED;
+ break;
+ case DTV_TUNE:
+ /* interpret the cache of data, build either a traditional frontend
+ * tunerequest so we can pass validation in the FE_SET_FRONTEND
+ * ioctl.
+ */
+ fe->dtv_property_cache.state = tvp->cmd;
+ printk("%s() Finalised property cache\n", __FUNCTION__);
+ dtv_property_cache_submit(fe);
+
+ r |= dvb_frontend_ioctl_legacy(inode, file, FE_SET_FRONTEND,
+ &fepriv->parameters);
+ break;
+ case DTV_FREQUENCY:
+ fe->dtv_property_cache.frequency = tvp->u.data;
+ break;
+ case DTV_MODULATION:
+ fe->dtv_property_cache.modulation = tvp->u.data;
+ break;
+ case DTV_BANDWIDTH_HZ:
+ fe->dtv_property_cache.bandwidth_hz = tvp->u.data;
+ break;
+ case DTV_INVERSION:
+ fe->dtv_property_cache.inversion = tvp->u.data;
+ break;
+ case DTV_SYMBOL_RATE:
+ fe->dtv_property_cache.symbol_rate = tvp->u.data;
+ break;
+ case DTV_INNER_FEC:
+ fe->dtv_property_cache.fec_inner = tvp->u.data;
+ break;
+ case DTV_PILOT:
+ fe->dtv_property_cache.pilot = tvp->u.data;
+ break;
+ case DTV_ROLLOFF:
+ fe->dtv_property_cache.rolloff = tvp->u.data;
+ break;
+ case DTV_DELIVERY_SYSTEM:
+ fe->dtv_property_cache.delivery_system = tvp->u.data;
+ break;
+ case DTV_VOLTAGE:
+ fe->dtv_property_cache.voltage = tvp->u.data;
+ r = dvb_frontend_ioctl_legacy(inode, file, FE_SET_VOLTAGE,
+ (void *)fe->dtv_property_cache.voltage);
+ break;
+ case DTV_TONE:
+ fe->dtv_property_cache.sectone = tvp->u.data;
+ r = dvb_frontend_ioctl_legacy(inode, file, FE_SET_TONE,
+ (void *)fe->dtv_property_cache.sectone);
+ break;
+ case DTV_CODE_RATE_HP:
+ fe->dtv_property_cache.code_rate_HP = tvp->u.data;
+ break;
+ case DTV_CODE_RATE_LP:
+ fe->dtv_property_cache.code_rate_LP = tvp->u.data;
+ break;
+ case DTV_GUARD_INTERVAL:
+ fe->dtv_property_cache.guard_interval = tvp->u.data;
+ break;
+ case DTV_TRANSMISSION_MODE:
+ fe->dtv_property_cache.transmission_mode = tvp->u.data;
+ break;
+ case DTV_HIERARCHY:
+ fe->dtv_property_cache.hierarchy = tvp->u.data;
+ break;
+ default:
+ r = -1;
+ }
+
+ return r;
+}
+
static int dvb_frontend_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, void *parg)
{
@@ -776,6 +1310,116 @@
if (down_interruptible (&fepriv->sem))
return -ERESTARTSYS;
+ if ((cmd == FE_SET_PROPERTY) || (cmd == FE_GET_PROPERTY))
+ err = dvb_frontend_ioctl_properties(inode, file, cmd, parg);
+ else {
+ fe->dtv_property_cache.state = DTV_UNDEFINED;
+ err = dvb_frontend_ioctl_legacy(inode, file, cmd, parg);
+ }
+
+ up(&fepriv->sem);
+ return err;
+}
+
+static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file,
+ unsigned int cmd, void *parg)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_frontend *fe = dvbdev->priv;
+ int err = 0;
+
+ struct dtv_properties *tvps = NULL;
+ struct dtv_property *tvp = NULL;
+ int i;
+
+ dprintk("%s\n", __func__);
+
+ if(cmd == FE_SET_PROPERTY) {
+ printk("%s() FE_SET_PROPERTY\n", __FUNCTION__);
+
+ tvps = (struct dtv_properties __user *)parg;
+
+ printk("%s() properties.num = %d\n", __FUNCTION__, tvps->num);
+ printk("%s() properties.props = %p\n", __FUNCTION__, tvps->props);
+
+ /* Put an arbitrary limit on the number of messages that can
+ * be sent at once */
+ if ((tvps->num == 0) || (tvps->num > DTV_IOCTL_MAX_MSGS))
+ return -EINVAL;
+
+ tvp = (struct dtv_property *) kmalloc(tvps->num *
+ sizeof(struct dtv_property), GFP_KERNEL);
+ if (!tvp) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ if (copy_from_user(tvp, tvps->props, tvps->num * sizeof(struct dtv_property))) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ for (i = 0; i < tvps->num; i++) {
+ (tvp + i)->result = dtv_property_process_set(fe, tvp + i, inode, file);
+ err |= (tvp + i)->result;
+ }
+
+ if(fe->dtv_property_cache.state == DTV_TUNE) {
+ printk("%s() Property cache is full, tuning\n", __FUNCTION__);
+ }
+
+ } else
+ if(cmd == FE_GET_PROPERTY) {
+ printk("%s() FE_GET_PROPERTY\n", __FUNCTION__);
+
+ tvps = (struct dtv_properties __user *)parg;
+
+ printk("%s() properties.num = %d\n", __FUNCTION__, tvps->num);
+ printk("%s() properties.props = %p\n", __FUNCTION__, tvps->props);
+
+ /* Put an arbitrary limit on the number of messages that can
+ * be sent at once */
+ if ((tvps->num == 0) || (tvps->num > DTV_IOCTL_MAX_MSGS))
+ return -EINVAL;
+
+ tvp = (struct dtv_property *) kmalloc(tvps->num *
+ sizeof(struct dtv_property), GFP_KERNEL);
+ if (!tvp) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ if (copy_from_user(tvp, tvps->props, tvps->num * sizeof(struct dtv_property))) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ for (i = 0; i < tvps->num; i++) {
+ (tvp + i)->result = dtv_property_process_get(fe, tvp + i, inode, file);
+ err |= (tvp + i)->result;
+ }
+
+ if (copy_to_user(tvps->props, tvp, tvps->num * sizeof(struct dtv_property))) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ } else
+ err = -EOPNOTSUPP;
+
+out:
+ kfree(tvp);
+ return err;
+}
+
+static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file,
+ unsigned int cmd, void *parg)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_frontend *fe = dvbdev->priv;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ int err = -EOPNOTSUPP;
+
switch (cmd) {
case FE_GET_INFO: {
struct dvb_frontend_info* info = parg;
@@ -942,13 +1586,21 @@
case FE_SET_FRONTEND: {
struct dvb_frontend_tune_settings fetunesettings;
- if (dvb_frontend_check_parameters(fe, parg) < 0) {
- err = -EINVAL;
- break;
- }
+ if(fe->dtv_property_cache.state == DTV_TUNE) {
+ if (dvb_frontend_check_parameters(fe, &fepriv->parameters) < 0) {
+ err = -EINVAL;
+ break;
+ }
+ } else {
+ if (dvb_frontend_check_parameters(fe, parg) < 0) {
+ err = -EINVAL;
+ break;
+ }
- memcpy (&fepriv->parameters, parg,
- sizeof (struct dvb_frontend_parameters));
+ memcpy (&fepriv->parameters, parg,
+ sizeof (struct dvb_frontend_parameters));
+ dtv_property_cache_sync(fe, &fepriv->parameters);
+ }
memset(&fetunesettings, 0, sizeof(struct dvb_frontend_tune_settings));
memcpy(&fetunesettings.parameters, parg,
@@ -1027,10 +1679,10 @@
break;
};
- up (&fepriv->sem);
return err;
}
+
static unsigned int dvb_frontend_poll(struct file *file, struct poll_table_struct *wait)
{
struct dvb_device *dvbdev = file->private_data;
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h
index aa4133f..3055301 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.h
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.h
@@ -169,6 +169,9 @@
struct dvb_tuner_ops tuner_ops;
struct analog_demod_ops analog_ops;
+
+ int (*set_property)(struct dvb_frontend* fe, struct dtv_property* tvp);
+ int (*get_property)(struct dvb_frontend* fe, struct dtv_property* tvp);
};
#define MAX_EVENT 8
@@ -182,6 +185,32 @@
struct mutex mtx;
};
+struct dtv_frontend_properties {
+
+ /* Cache State */
+ u32 state;
+
+ u32 frequency;
+ fe_modulation_t modulation;
+
+ fe_sec_voltage_t voltage;
+ fe_sec_tone_mode_t sectone;
+ fe_spectral_inversion_t inversion;
+ fe_code_rate_t fec_inner;
+ fe_transmit_mode_t transmission_mode;
+ u32 bandwidth_hz; /* 0 = AUTO */
+ fe_guard_interval_t guard_interval;
+ fe_hierarchy_t hierarchy;
+ u32 symbol_rate;
+ fe_code_rate_t code_rate_HP;
+ fe_code_rate_t code_rate_LP;
+
+ fe_pilot_t pilot;
+ fe_rolloff_t rolloff;
+
+ fe_delivery_system_t delivery_system;
+};
+
struct dvb_frontend {
struct dvb_frontend_ops ops;
struct dvb_adapter *dvb;
@@ -190,6 +219,9 @@
void *frontend_priv;
void *sec_priv;
void *analog_demod_priv;
+ struct dtv_frontend_properties dtv_property_cache;
+#define DVB_FRONTEND_COMPONENT_TUNER 0
+ int (*callback)(void *adapter_priv, int component, int cmd, int arg);
};
extern int dvb_register_frontend(struct dvb_adapter *dvb,
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index e84152b..3c13bcf 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -72,9 +72,11 @@
select DVB_DIB7000P
select DVB_DIB7000M
select DVB_DIB3000MC
+ select DVB_S5H1411 if !DVB_FE_CUSTOMISE
select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
select MEDIA_TUNER_MT2266 if !DVB_FE_CUSTOMISE
select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMIZE
select DVB_TUNER_DIB0070
help
Support for USB2.0/1.1 DVB receivers based on the DiB0700 USB bridge. The
@@ -108,6 +110,8 @@
select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE
select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMISE
select MEDIA_TUNER_MXL5005S if !DVB_FE_CUSTOMISE
+ select DVB_DIB7000P if !DVB_FE_CUSTOMISE
+ select DVB_TUNER_DIB0070 if !DVB_FE_CUSTOMISE
help
Say Y here to support the Conexant USB2.0 hybrid reference design.
Currently, only DVB and ATSC modes are supported, analog mode
@@ -245,12 +249,25 @@
Afatech AF9005 based receiver.
config DVB_USB_DW2102
- tristate "DvbWorld 2102 DVB-S USB2.0 receiver"
+ tristate "DvbWorld DVB-S/S2 USB2.0 support"
depends on DVB_USB
- select DVB_STV0299 if !DVB_FE_CUSTOMISE
select DVB_PLL if !DVB_FE_CUSTOMISE
+ select DVB_STV0299 if !DVB_FE_CUSTOMISE
+ select DVB_STV0288 if !DVB_FE_CUSTOMISE
+ select DVB_STB6000 if !DVB_FE_CUSTOMISE
+ select DVB_CX24116 if !DVB_FE_CUSTOMISE
+ select DVB_SI21XX if !DVB_FE_CUSTOMISE
help
- Say Y here to support the DvbWorld 2102 DVB-S USB2.0 receiver.
+ Say Y here to support the DvbWorld DVB-S/S2 USB2.0 receivers
+ and the TeVii S650.
+
+config DVB_USB_CINERGY_T2
+ tristate "Terratec CinergyT2/qanu USB 2.0 DVB-T receiver"
+ depends on DVB_USB
+ help
+ Support for "TerraTec CinergyT2" USB2.0 Highspeed DVB Receivers
+
+ Say Y if you own such a device and want to use it.
config DVB_USB_ANYSEE
tristate "Anysee DVB-T/C USB2.0 support"
@@ -262,3 +279,22 @@
help
Say Y here to support the Anysee E30, Anysee E30 Plus or
Anysee E30 C Plus DVB USB2.0 receiver.
+
+config DVB_USB_DTV5100
+ tristate "AME DTV-5100 USB2.0 DVB-T support"
+ depends on DVB_USB
+ select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE
+ help
+ Say Y here to support the AME DTV-5100 USB2.0 DVB-T receiver.
+
+config DVB_USB_AF9015
+ tristate "Afatech AF9015 DVB-T USB2.0 support"
+ depends on DVB_USB && EXPERIMENTAL
+ select DVB_AF9013
+ select DVB_PLL if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_MXL5005S if !DVB_FE_CUSTOMISE
+ help
+ Say Y here to support the Afatech AF9015 based DVB-T USB2.0 receiver
diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile
index e206f1e..3122b7c 100644
--- a/drivers/media/dvb/dvb-usb/Makefile
+++ b/drivers/media/dvb/dvb-usb/Makefile
@@ -67,6 +67,16 @@
dvb-usb-dw2102-objs = dw2102.o
obj-$(CONFIG_DVB_USB_DW2102) += dvb-usb-dw2102.o
+dvb-usb-dtv5100-objs = dtv5100.o
+obj-$(CONFIG_DVB_USB_DTV5100) += dvb-usb-dtv5100.o
+
+dvb-usb-af9015-objs = af9015.o
+obj-$(CONFIG_DVB_USB_AF9015) += dvb-usb-af9015.o
+
+dvb-usb-cinergyT2-objs = cinergyT2-core.o cinergyT2-fe.o
+obj-$(CONFIG_DVB_USB_CINERGY_T2) += dvb-usb-cinergyT2.o
+
+
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
# due to tuner-xc3028
EXTRA_CFLAGS += -Idrivers/media/common/tuners
diff --git a/drivers/media/dvb/dvb-usb/af9005-remote.c b/drivers/media/dvb/dvb-usb/af9005-remote.c
index ff00c0e..7c596f9 100644
--- a/drivers/media/dvb/dvb-usb/af9005-remote.c
+++ b/drivers/media/dvb/dvb-usb/af9005-remote.c
@@ -25,7 +25,7 @@
*/
#include "af9005.h"
/* debug */
-int dvb_usb_af9005_remote_debug;
+static int dvb_usb_af9005_remote_debug;
module_param_named(debug, dvb_usb_af9005_remote_debug, int, 0644);
MODULE_PARM_DESC(debug,
"enable (1) or disable (0) debug messages."
diff --git a/drivers/media/dvb/dvb-usb/af9005-script.h b/drivers/media/dvb/dvb-usb/af9005-script.h
index 6eeaae5..4d69045 100644
--- a/drivers/media/dvb/dvb-usb/af9005-script.h
+++ b/drivers/media/dvb/dvb-usb/af9005-script.h
@@ -14,7 +14,7 @@
u8 val;
} RegDesc;
-RegDesc script[] = {
+static RegDesc script[] = {
{0xa180, 0x0, 0x8, 0xa},
{0xa181, 0x0, 0x8, 0xd7},
{0xa182, 0x0, 0x8, 0xa3},
diff --git a/drivers/media/dvb/dvb-usb/af9005.c b/drivers/media/dvb/dvb-usb/af9005.c
index cfe71fe..ca5a0a4 100644
--- a/drivers/media/dvb/dvb-usb/af9005.c
+++ b/drivers/media/dvb/dvb-usb/af9005.c
@@ -35,17 +35,17 @@
MODULE_PARM_DESC(led, "enable led (default: 1).");
/* eeprom dump */
-int dvb_usb_af9005_dump_eeprom = 0;
+static int dvb_usb_af9005_dump_eeprom;
module_param_named(dump_eeprom, dvb_usb_af9005_dump_eeprom, int, 0);
MODULE_PARM_DESC(dump_eeprom, "dump contents of the eeprom.");
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
/* remote control decoder */
-int (*rc_decode) (struct dvb_usb_device * d, u8 * data, int len, u32 * event,
- int *state);
-void *rc_keys;
-int *rc_keys_size;
+static int (*rc_decode) (struct dvb_usb_device *d, u8 *data, int len,
+ u32 *event, int *state);
+static void *rc_keys;
+static int *rc_keys_size;
u8 regmask[8] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
@@ -54,8 +54,8 @@
int led_state;
};
-int af9005_usb_generic_rw(struct dvb_usb_device *d, u8 * wbuf, u16 wlen,
- u8 * rbuf, u16 rlen, int delay_ms)
+static int af9005_usb_generic_rw(struct dvb_usb_device *d, u8 *wbuf, u16 wlen,
+ u8 *rbuf, u16 rlen, int delay_ms)
{
int actlen, ret = -ENOMEM;
@@ -98,12 +98,7 @@
return ret;
}
-int af9005_usb_generic_write(struct dvb_usb_device *d, u8 * buf, u16 len)
-{
- return af9005_usb_generic_rw(d, buf, len, NULL, 0, 0);
-}
-
-int af9005_generic_read_write(struct dvb_usb_device *d, u16 reg,
+static int af9005_generic_read_write(struct dvb_usb_device *d, u16 reg,
int readwrite, int type, u8 * values, int len)
{
struct af9005_device_state *st = d->priv;
@@ -765,7 +760,7 @@
return 0;
}
-int af9005_download_firmware(struct usb_device *udev, const struct firmware *fw)
+static int af9005_download_firmware(struct usb_device *udev, const struct firmware *fw)
{
int i, packets, ret, act_len;
diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c
new file mode 100644
index 0000000..cb0829c
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/af9015.c
@@ -0,0 +1,1474 @@
+/*
+ * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ *
+ * Thanks to Afatech who kindly provided information.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "af9015.h"
+#include "af9013.h"
+#include "mt2060.h"
+#include "qt1010.h"
+#include "tda18271.h"
+#include "mxl5005s.h"
+#if 0
+#include "mc44s80x.h"
+#endif
+
+int dvb_usb_af9015_debug;
+module_param_named(debug, dvb_usb_af9015_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
+int dvb_usb_af9015_remote;
+module_param_named(remote, dvb_usb_af9015_remote, int, 0644);
+MODULE_PARM_DESC(remote, "select remote");
+int dvb_usb_af9015_dual_mode;
+module_param_named(dual_mode, dvb_usb_af9015_dual_mode, int, 0644);
+MODULE_PARM_DESC(dual_mode, "enable dual mode");
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static DEFINE_MUTEX(af9015_usb_mutex);
+
+static struct af9015_config af9015_config;
+static struct dvb_usb_device_properties af9015_properties[2];
+int af9015_properties_count = ARRAY_SIZE(af9015_properties);
+
+static struct af9013_config af9015_af9013_config[] = {
+ {
+ .demod_address = AF9015_I2C_DEMOD,
+ .output_mode = AF9013_OUTPUT_MODE_USB,
+ .api_version = { 0, 1, 9, 0 },
+ .gpio[0] = AF9013_GPIO_HI,
+ .gpio[3] = AF9013_GPIO_TUNER_ON,
+
+ }, {
+ .output_mode = AF9013_OUTPUT_MODE_SERIAL,
+ .api_version = { 0, 1, 9, 0 },
+ .gpio[0] = AF9013_GPIO_TUNER_ON,
+ .gpio[1] = AF9013_GPIO_LO,
+ }
+};
+
+static int af9015_rw_udev(struct usb_device *udev, struct req_t *req)
+{
+ int act_len, ret;
+ u8 buf[64];
+ u8 write = 1;
+ u8 msg_len = 8;
+ static u8 seq; /* packet sequence number */
+
+ if (mutex_lock_interruptible(&af9015_usb_mutex) < 0)
+ return -EAGAIN;
+
+ buf[0] = req->cmd;
+ buf[1] = seq++;
+ buf[2] = req->i2c_addr;
+ buf[3] = req->addr >> 8;
+ buf[4] = req->addr & 0xff;
+ buf[5] = req->mbox;
+ buf[6] = req->addr_len;
+ buf[7] = req->data_len;
+
+ switch (req->cmd) {
+ case GET_CONFIG:
+ case BOOT:
+ case READ_MEMORY:
+ case RECONNECT_USB:
+ case GET_IR_CODE:
+ write = 0;
+ break;
+ case READ_I2C:
+ write = 0;
+ buf[2] |= 0x01; /* set I2C direction */
+ case WRITE_I2C:
+ buf[0] = READ_WRITE_I2C;
+ break;
+ case WRITE_MEMORY:
+ if (((req->addr & 0xff00) == 0xff00) ||
+ ((req->addr & 0xae00) == 0xae00))
+ buf[0] = WRITE_VIRTUAL_MEMORY;
+ case WRITE_VIRTUAL_MEMORY:
+ case COPY_FIRMWARE:
+ case DOWNLOAD_FIRMWARE:
+ break;
+ default:
+ err("unknown command:%d", req->cmd);
+ ret = -1;
+ goto error_unlock;
+ }
+
+ /* write requested */
+ if (write) {
+ memcpy(&buf[8], req->data, req->data_len);
+ msg_len += req->data_len;
+ }
+ deb_xfer(">>> ");
+ debug_dump(buf, msg_len, deb_xfer);
+
+ /* send req */
+ ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 0x02), buf, msg_len,
+ &act_len, AF9015_USB_TIMEOUT);
+ if (ret)
+ err("bulk message failed:%d (%d/%d)", ret, msg_len, act_len);
+ else
+ if (act_len != msg_len)
+ ret = -1; /* all data is not send */
+ if (ret)
+ goto error_unlock;
+
+ /* no ack for those packets */
+ if (req->cmd == DOWNLOAD_FIRMWARE || req->cmd == RECONNECT_USB)
+ goto exit_unlock;
+
+ /* receive ack and data if read req */
+ msg_len = 1 + 1 + req->data_len; /* seq + status + data len */
+ ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, 0x81), buf, msg_len,
+ &act_len, AF9015_USB_TIMEOUT);
+ if (ret) {
+ err("recv bulk message failed:%d", ret);
+ ret = -1;
+ goto error_unlock;
+ }
+
+ deb_xfer("<<< ");
+ debug_dump(buf, act_len, deb_xfer);
+
+ /* remote controller query status is 1 if remote code is not received */
+ if (req->cmd == GET_IR_CODE && buf[1] == 1) {
+ buf[1] = 0; /* clear command "error" status */
+ memset(&buf[2], 0, req->data_len);
+ buf[3] = 1; /* no remote code received mark */
+ }
+
+ /* check status */
+ if (buf[1]) {
+ err("command failed:%d", buf[1]);
+ ret = -1;
+ goto error_unlock;
+ }
+
+ /* read request, copy returned data to return buf */
+ if (!write)
+ memcpy(req->data, &buf[2], req->data_len);
+
+error_unlock:
+exit_unlock:
+ mutex_unlock(&af9015_usb_mutex);
+
+ return ret;
+}
+
+static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req)
+{
+ return af9015_rw_udev(d->udev, req);
+}
+
+static int af9015_write_regs(struct dvb_usb_device *d, u16 addr, u8 *val,
+ u8 len)
+{
+ struct req_t req = {WRITE_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, len,
+ val};
+ return af9015_ctrl_msg(d, &req);
+}
+
+static int af9015_write_reg(struct dvb_usb_device *d, u16 addr, u8 val)
+{
+ return af9015_write_regs(d, addr, &val, 1);
+}
+
+static int af9015_read_reg(struct dvb_usb_device *d, u16 addr, u8 *val)
+{
+ struct req_t req = {READ_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, 1, val};
+ return af9015_ctrl_msg(d, &req);
+}
+
+static int af9015_write_reg_i2c(struct dvb_usb_device *d, u8 addr, u16 reg,
+ u8 val)
+{
+ struct req_t req = {WRITE_I2C, addr, reg, 1, 1, 1, &val};
+
+ if (addr == af9015_af9013_config[0].demod_address ||
+ addr == af9015_af9013_config[1].demod_address)
+ req.addr_len = 3;
+
+ return af9015_ctrl_msg(d, &req);
+}
+
+static int af9015_read_reg_i2c(struct dvb_usb_device *d, u8 addr, u16 reg,
+ u8 *val)
+{
+ struct req_t req = {READ_I2C, addr, reg, 0, 1, 1, val};
+
+ if (addr == af9015_af9013_config[0].demod_address ||
+ addr == af9015_af9013_config[1].demod_address)
+ req.addr_len = 3;
+
+ return af9015_ctrl_msg(d, &req);
+}
+
+static int af9015_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+ int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ int ret = 0, i = 0;
+ u16 addr;
+ u8 mbox, addr_len;
+ struct req_t req;
+
+/* TODO: implement bus lock
+
+The bus lock is needed because there is two tuners both using same I2C-address.
+Due to that the only way to select correct tuner is use demodulator I2C-gate.
+
+................................................
+. AF9015 includes integrated AF9013 demodulator.
+. ____________ ____________ . ____________
+.| uC | | demod | . | tuner |
+.|------------| |------------| . |------------|
+.| AF9015 | | AF9013/5 | . | MXL5003 |
+.| |--+----I2C-------|-----/ -----|-.-----I2C-------| |
+.| | | | addr 0x38 | . | addr 0xc6 |
+.|____________| | |____________| . |____________|
+.................|..............................
+ | ____________ ____________
+ | | demod | | tuner |
+ | |------------| |------------|
+ | | AF9013 | | MXL5003 |
+ +----I2C-------|-----/ -----|-------I2C-------| |
+ | addr 0x3a | | addr 0xc6 |
+ |____________| |____________|
+*/
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ while (i < num) {
+ if (msg[i].addr == af9015_af9013_config[0].demod_address ||
+ msg[i].addr == af9015_af9013_config[1].demod_address) {
+ addr = msg[i].buf[0] << 8;
+ addr += msg[i].buf[1];
+ mbox = msg[i].buf[2];
+ addr_len = 3;
+ } else {
+ addr = msg[i].buf[0];
+ addr_len = 1;
+ mbox = 0;
+ }
+
+ if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) {
+ if (msg[i].addr ==
+ af9015_af9013_config[0].demod_address)
+ req.cmd = READ_MEMORY;
+ else
+ req.cmd = READ_I2C;
+ req.i2c_addr = msg[i].addr;
+ req.addr = addr;
+ req.mbox = mbox;
+ req.addr_len = addr_len;
+ req.data_len = msg[i+1].len;
+ req.data = &msg[i+1].buf[0];
+ ret = af9015_ctrl_msg(d, &req);
+ i += 2;
+ } else {
+ if (msg[i].addr ==
+ af9015_af9013_config[0].demod_address)
+ req.cmd = WRITE_MEMORY;
+ else
+ req.cmd = WRITE_I2C;
+ req.i2c_addr = msg[i].addr;
+ req.addr = addr;
+ req.mbox = mbox;
+ req.addr_len = addr_len;
+ req.data_len = msg[i].len-addr_len;
+ req.data = &msg[i].buf[addr_len];
+ ret = af9015_ctrl_msg(d, &req);
+ i += 1;
+ }
+ if (ret)
+ goto error;
+
+ }
+ ret = i;
+
+error:
+ mutex_unlock(&d->i2c_mutex);
+
+ return ret;
+}
+
+static u32 af9015_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm af9015_i2c_algo = {
+ .master_xfer = af9015_i2c_xfer,
+ .functionality = af9015_i2c_func,
+};
+
+static int af9015_do_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit, u8 op)
+{
+ int ret;
+ u8 val, mask = 0x01;
+
+ ret = af9015_read_reg(d, addr, &val);
+ if (ret)
+ return ret;
+
+ mask <<= bit;
+ if (op) {
+ /* set bit */
+ val |= mask;
+ } else {
+ /* clear bit */
+ mask ^= 0xff;
+ val &= mask;
+ }
+
+ return af9015_write_reg(d, addr, val);
+}
+
+static int af9015_set_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit)
+{
+ return af9015_do_reg_bit(d, addr, bit, 1);
+}
+
+static int af9015_clear_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit)
+{
+ return af9015_do_reg_bit(d, addr, bit, 0);
+}
+
+static int af9015_init_endpoint(struct dvb_usb_device *d)
+{
+ int ret;
+ u16 frame_size;
+ u8 packet_size;
+ deb_info("%s: USB speed:%d\n", __func__, d->udev->speed);
+
+#define TS_PACKET_SIZE 188
+
+#define TS_USB20_PACKET_COUNT 348
+#define TS_USB20_FRAME_SIZE (TS_PACKET_SIZE*TS_USB20_PACKET_COUNT)
+
+#define TS_USB11_PACKET_COUNT 21
+#define TS_USB11_FRAME_SIZE (TS_PACKET_SIZE*TS_USB11_PACKET_COUNT)
+
+#define TS_USB20_MAX_PACKET_SIZE 512
+#define TS_USB11_MAX_PACKET_SIZE 64
+
+ if (d->udev->speed == USB_SPEED_FULL) {
+ frame_size = TS_USB11_FRAME_SIZE/4;
+ packet_size = TS_USB11_MAX_PACKET_SIZE/4;
+ } else {
+ frame_size = TS_USB20_FRAME_SIZE/4;
+ packet_size = TS_USB20_MAX_PACKET_SIZE/4;
+ }
+
+ ret = af9015_set_reg_bit(d, 0xd507, 2); /* assert EP4 reset */
+ if (ret)
+ goto error;
+ ret = af9015_set_reg_bit(d, 0xd50b, 1); /* assert EP5 reset */
+ if (ret)
+ goto error;
+ ret = af9015_clear_reg_bit(d, 0xdd11, 5); /* disable EP4 */
+ if (ret)
+ goto error;
+ ret = af9015_clear_reg_bit(d, 0xdd11, 6); /* disable EP5 */
+ if (ret)
+ goto error;
+ ret = af9015_set_reg_bit(d, 0xdd11, 5); /* enable EP4 */
+ if (ret)
+ goto error;
+ if (af9015_config.dual_mode) {
+ ret = af9015_set_reg_bit(d, 0xdd11, 6); /* enable EP5 */
+ if (ret)
+ goto error;
+ }
+ ret = af9015_clear_reg_bit(d, 0xdd13, 5); /* disable EP4 NAK */
+ if (ret)
+ goto error;
+ if (af9015_config.dual_mode) {
+ ret = af9015_clear_reg_bit(d, 0xdd13, 6); /* disable EP5 NAK */
+ if (ret)
+ goto error;
+ }
+ /* EP4 xfer length */
+ ret = af9015_write_reg(d, 0xdd88, frame_size & 0xff);
+ if (ret)
+ goto error;
+ ret = af9015_write_reg(d, 0xdd89, frame_size >> 8);
+ if (ret)
+ goto error;
+ /* EP5 xfer length */
+ ret = af9015_write_reg(d, 0xdd8a, frame_size & 0xff);
+ if (ret)
+ goto error;
+ ret = af9015_write_reg(d, 0xdd8b, frame_size >> 8);
+ if (ret)
+ goto error;
+ ret = af9015_write_reg(d, 0xdd0c, packet_size); /* EP4 packet size */
+ if (ret)
+ goto error;
+ ret = af9015_write_reg(d, 0xdd0d, packet_size); /* EP5 packet size */
+ if (ret)
+ goto error;
+ ret = af9015_clear_reg_bit(d, 0xd507, 2); /* negate EP4 reset */
+ if (ret)
+ goto error;
+ if (af9015_config.dual_mode) {
+ ret = af9015_clear_reg_bit(d, 0xd50b, 1); /* negate EP5 reset */
+ if (ret)
+ goto error;
+ }
+
+ /* enable / disable mp2if2 */
+ if (af9015_config.dual_mode)
+ ret = af9015_set_reg_bit(d, 0xd50b, 0);
+ else
+ ret = af9015_clear_reg_bit(d, 0xd50b, 0);
+error:
+ if (ret)
+ err("endpoint init failed:%d", ret);
+ return ret;
+}
+
+static int af9015_copy_firmware(struct dvb_usb_device *d)
+{
+ int ret;
+ u8 fw_params[4];
+ u8 val, i;
+ struct req_t req = {COPY_FIRMWARE, 0, 0x5100, 0, 0, sizeof(fw_params),
+ fw_params };
+ deb_info("%s:\n", __func__);
+
+ fw_params[0] = af9015_config.firmware_size >> 8;
+ fw_params[1] = af9015_config.firmware_size & 0xff;
+ fw_params[2] = af9015_config.firmware_checksum >> 8;
+ fw_params[3] = af9015_config.firmware_checksum & 0xff;
+
+ /* wait 2nd demodulator ready */
+ msleep(100);
+
+ ret = af9015_read_reg_i2c(d, 0x3a, 0x98be, &val);
+ if (ret)
+ goto error;
+ else
+ deb_info("%s: firmware status:%02x\n", __func__, val);
+
+ if (val == 0x0c) /* fw is running, no need for download */
+ goto exit;
+
+ /* set I2C master clock to fast (to speed up firmware copy) */
+ ret = af9015_write_reg(d, 0xd416, 0x04); /* 0x04 * 400ns */
+ if (ret)
+ goto error;
+
+ msleep(50);
+
+ /* copy firmware */
+ ret = af9015_ctrl_msg(d, &req);
+ if (ret)
+ err("firmware copy cmd failed:%d", ret);
+ deb_info("%s: firmware copy done\n", __func__);
+
+ /* set I2C master clock back to normal */
+ ret = af9015_write_reg(d, 0xd416, 0x14); /* 0x14 * 400ns */
+ if (ret)
+ goto error;
+
+ /* request boot firmware */
+ ret = af9015_write_reg_i2c(d, af9015_af9013_config[1].demod_address,
+ 0xe205, 1);
+ deb_info("%s: firmware boot cmd status:%d\n", __func__, ret);
+ if (ret)
+ goto error;
+
+ for (i = 0; i < 15; i++) {
+ msleep(100);
+
+ /* check firmware status */
+ ret = af9015_read_reg_i2c(d,
+ af9015_af9013_config[1].demod_address, 0x98be, &val);
+ deb_info("%s: firmware status cmd status:%d fw status:%02x\n",
+ __func__, ret, val);
+ if (ret)
+ goto error;
+
+ if (val == 0x0c || val == 0x04) /* success or fail */
+ break;
+ }
+
+ if (val == 0x04) {
+ err("firmware did not run");
+ ret = -1;
+ } else if (val != 0x0c) {
+ err("firmware boot timeout");
+ ret = -1;
+ }
+
+error:
+exit:
+ return ret;
+}
+
+/* dump eeprom */
+static int af9015_eeprom_dump(struct dvb_usb_device *d)
+{
+ char buf[52], buf2[4];
+ u8 reg, val;
+
+ for (reg = 0; ; reg++) {
+ if (reg % 16 == 0) {
+ if (reg)
+ deb_info("%s\n", buf);
+ sprintf(buf, "%02x: ", reg);
+ }
+ if (af9015_read_reg_i2c(d, AF9015_I2C_EEPROM, reg, &val) == 0)
+ sprintf(buf2, "%02x ", val);
+ else
+ strcpy(buf2, "-- ");
+ strcat(buf, buf2);
+ if (reg == 0xff)
+ break;
+ }
+ deb_info("%s\n", buf);
+ return 0;
+}
+
+int af9015_download_ir_table(struct dvb_usb_device *d)
+{
+ int i, packets = 0, ret;
+ u16 addr = 0x9a56; /* ir-table start address */
+ struct req_t req = {WRITE_MEMORY, 0, 0, 0, 0, 1, NULL};
+ u8 *data = NULL;
+ deb_info("%s:\n", __func__);
+
+ data = af9015_config.ir_table;
+ packets = af9015_config.ir_table_size;
+
+ /* no remote */
+ if (!packets)
+ goto exit;
+
+ /* load remote ir-table */
+ for (i = 0; i < packets; i++) {
+ req.addr = addr + i;
+ req.data = &data[i];
+ ret = af9015_ctrl_msg(d, &req);
+ if (ret) {
+ err("ir-table download failed at packet %d with " \
+ "code %d", i, ret);
+ return ret;
+ }
+ }
+
+exit:
+ return 0;
+}
+
+static int af9015_init(struct dvb_usb_device *d)
+{
+ int ret;
+ deb_info("%s:\n", __func__);
+
+ ret = af9015_init_endpoint(d);
+ if (ret)
+ goto error;
+
+ ret = af9015_download_ir_table(d);
+ if (ret)
+ goto error;
+
+error:
+ return ret;
+}
+
+static int af9015_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+ int ret;
+ deb_info("%s: onoff:%d\n", __func__, onoff);
+
+ if (onoff)
+ ret = af9015_set_reg_bit(adap->dev, 0xd503, 0);
+ else
+ ret = af9015_clear_reg_bit(adap->dev, 0xd503, 0);
+
+ return ret;
+}
+
+static int af9015_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid,
+ int onoff)
+{
+ int ret;
+ u8 idx;
+
+ deb_info("%s: set pid filter, index %d, pid %x, onoff %d\n",
+ __func__, index, pid, onoff);
+
+ ret = af9015_write_reg(adap->dev, 0xd505, (pid & 0xff));
+ if (ret)
+ goto error;
+
+ ret = af9015_write_reg(adap->dev, 0xd506, (pid >> 8));
+ if (ret)
+ goto error;
+
+ idx = ((index & 0x1f) | (1 << 5));
+ ret = af9015_write_reg(adap->dev, 0xd504, idx);
+
+error:
+ return ret;
+}
+
+static int af9015_download_firmware(struct usb_device *udev,
+ const struct firmware *fw)
+{
+ int i, len, packets, remainder, ret;
+ struct req_t req = {DOWNLOAD_FIRMWARE, 0, 0, 0, 0, 0, NULL};
+ u16 addr = 0x5100; /* firmware start address */
+ u16 checksum = 0;
+
+ deb_info("%s:\n", __func__);
+
+ /* calc checksum */
+ for (i = 0; i < fw->size; i++)
+ checksum += fw->data[i];
+
+ af9015_config.firmware_size = fw->size;
+ af9015_config.firmware_checksum = checksum;
+
+ #define FW_PACKET_MAX_DATA 55
+
+ packets = fw->size / FW_PACKET_MAX_DATA;
+ remainder = fw->size % FW_PACKET_MAX_DATA;
+ len = FW_PACKET_MAX_DATA;
+ for (i = 0; i <= packets; i++) {
+ if (i == packets) /* set size of the last packet */
+ len = remainder;
+
+ req.data_len = len;
+ req.data = (u8 *)(fw->data + i * FW_PACKET_MAX_DATA);
+ req.addr = addr;
+ addr += FW_PACKET_MAX_DATA;
+
+ ret = af9015_rw_udev(udev, &req);
+ if (ret) {
+ err("firmware download failed at packet %d with " \
+ "code %d", i, ret);
+ goto error;
+ }
+ }
+
+ /* firmware loaded, request boot */
+ req.cmd = BOOT;
+ ret = af9015_rw_udev(udev, &req);
+ if (ret) {
+ err("firmware boot failed:%d", ret);
+ goto error;
+ }
+
+ /* firmware is running, reconnect device in the usb bus */
+ req.cmd = RECONNECT_USB;
+ ret = af9015_rw_udev(udev, &req);
+ if (ret)
+ err("reconnect failed: %d", ret);
+
+error:
+ return ret;
+}
+
+static int af9015_read_config(struct usb_device *udev)
+{
+ int ret;
+ u8 val, i, offset = 0;
+ struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, &val};
+ char manufacturer[10];
+
+ /* IR remote controller */
+ req.addr = AF9015_EEPROM_IR_MODE;
+ ret = af9015_rw_udev(udev, &req);
+ if (ret)
+ goto error;
+ deb_info("%s: IR mode:%d\n", __func__, val);
+ for (i = 0; i < af9015_properties_count; i++) {
+ if (val == AF9015_IR_MODE_DISABLED || val == 0x04) {
+ af9015_properties[i].rc_key_map = NULL;
+ af9015_properties[i].rc_key_map_size = 0;
+ } else if (dvb_usb_af9015_remote) {
+ /* load remote defined as module param */
+ switch (dvb_usb_af9015_remote) {
+ case AF9015_REMOTE_A_LINK_DTU_M:
+ af9015_properties[i].rc_key_map =
+ af9015_rc_keys_a_link;
+ af9015_properties[i].rc_key_map_size =
+ ARRAY_SIZE(af9015_rc_keys_a_link);
+ af9015_config.ir_table = af9015_ir_table_a_link;
+ af9015_config.ir_table_size =
+ ARRAY_SIZE(af9015_ir_table_a_link);
+ break;
+ case AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3:
+ af9015_properties[i].rc_key_map =
+ af9015_rc_keys_msi;
+ af9015_properties[i].rc_key_map_size =
+ ARRAY_SIZE(af9015_rc_keys_msi);
+ af9015_config.ir_table = af9015_ir_table_msi;
+ af9015_config.ir_table_size =
+ ARRAY_SIZE(af9015_ir_table_msi);
+ break;
+ case AF9015_REMOTE_MYGICTV_U718:
+ af9015_properties[i].rc_key_map =
+ af9015_rc_keys_mygictv;
+ af9015_properties[i].rc_key_map_size =
+ ARRAY_SIZE(af9015_rc_keys_mygictv);
+ af9015_config.ir_table =
+ af9015_ir_table_mygictv;
+ af9015_config.ir_table_size =
+ ARRAY_SIZE(af9015_ir_table_mygictv);
+ break;
+ }
+ } else {
+ switch (udev->descriptor.idVendor) {
+ case USB_VID_LEADTEK:
+ af9015_properties[i].rc_key_map =
+ af9015_rc_keys_leadtek;
+ af9015_properties[i].rc_key_map_size =
+ ARRAY_SIZE(af9015_rc_keys_leadtek);
+ af9015_config.ir_table =
+ af9015_ir_table_leadtek;
+ af9015_config.ir_table_size =
+ ARRAY_SIZE(af9015_ir_table_leadtek);
+ break;
+ case USB_VID_VISIONPLUS:
+ if (udev->descriptor.idProduct ==
+ USB_PID_AZUREWAVE_AD_TU700) {
+ af9015_properties[i].rc_key_map =
+ af9015_rc_keys_twinhan;
+ af9015_properties[i].rc_key_map_size =
+ ARRAY_SIZE(af9015_rc_keys_twinhan);
+ af9015_config.ir_table =
+ af9015_ir_table_twinhan;
+ af9015_config.ir_table_size =
+ ARRAY_SIZE(af9015_ir_table_twinhan);
+ }
+ break;
+ case USB_VID_KWORLD_2:
+ /* TODO: use correct rc keys */
+ af9015_properties[i].rc_key_map =
+ af9015_rc_keys_twinhan;
+ af9015_properties[i].rc_key_map_size =
+ ARRAY_SIZE(af9015_rc_keys_twinhan);
+ af9015_config.ir_table = af9015_ir_table_kworld;
+ af9015_config.ir_table_size =
+ ARRAY_SIZE(af9015_ir_table_kworld);
+ break;
+ /* Check USB manufacturer and product strings and try
+ to determine correct remote in case of chip vendor
+ reference IDs are used. */
+ case USB_VID_AFATECH:
+ memset(manufacturer, 0, sizeof(manufacturer));
+ usb_string(udev, udev->descriptor.iManufacturer,
+ manufacturer, sizeof(manufacturer));
+ if (!strcmp("Geniatech", manufacturer)) {
+ /* iManufacturer 1 Geniatech
+ iProduct 2 AF9015 */
+ af9015_properties[i].rc_key_map =
+ af9015_rc_keys_mygictv;
+ af9015_properties[i].rc_key_map_size =
+ ARRAY_SIZE(af9015_rc_keys_mygictv);
+ af9015_config.ir_table =
+ af9015_ir_table_mygictv;
+ af9015_config.ir_table_size =
+ ARRAY_SIZE(af9015_ir_table_mygictv);
+ } else if (!strcmp("MSI", manufacturer)) {
+ /* iManufacturer 1 MSI
+ iProduct 2 MSI K-VOX */
+ af9015_properties[i].rc_key_map =
+ af9015_rc_keys_msi;
+ af9015_properties[i].rc_key_map_size =
+ ARRAY_SIZE(af9015_rc_keys_msi);
+ af9015_config.ir_table =
+ af9015_ir_table_msi;
+ af9015_config.ir_table_size =
+ ARRAY_SIZE(af9015_ir_table_msi);
+ }
+ break;
+ }
+ }
+ }
+
+ /* TS mode - one or two receivers */
+ req.addr = AF9015_EEPROM_TS_MODE;
+ ret = af9015_rw_udev(udev, &req);
+ if (ret)
+ goto error;
+ af9015_config.dual_mode = val;
+ deb_info("%s: TS mode:%d\n", __func__, af9015_config.dual_mode);
+ /* disable dual mode by default because it is buggy */
+ if (!dvb_usb_af9015_dual_mode)
+ af9015_config.dual_mode = 0;
+
+ /* set buffer size according to USB port speed */
+ for (i = 0; i < af9015_properties_count; i++) {
+ /* USB1.1 set smaller buffersize and disable 2nd adapter */
+ if (udev->speed == USB_SPEED_FULL) {
+ af9015_properties[i].adapter->stream.u.bulk.buffersize =
+ TS_USB11_MAX_PACKET_SIZE;
+ /* disable 2nd adapter because we don't have
+ PID-filters */
+ af9015_config.dual_mode = 0;
+ } else {
+ af9015_properties[i].adapter->stream.u.bulk.buffersize =
+ TS_USB20_MAX_PACKET_SIZE;
+ }
+ }
+
+ if (af9015_config.dual_mode) {
+ /* read 2nd demodulator I2C address */
+ req.addr = AF9015_EEPROM_DEMOD2_I2C;
+ ret = af9015_rw_udev(udev, &req);
+ if (ret)
+ goto error;
+ af9015_af9013_config[1].demod_address = val;
+
+ /* enable 2nd adapter */
+ for (i = 0; i < af9015_properties_count; i++)
+ af9015_properties[i].num_adapters = 2;
+
+ } else {
+ /* disable 2nd adapter */
+ for (i = 0; i < af9015_properties_count; i++)
+ af9015_properties[i].num_adapters = 1;
+ }
+
+ for (i = 0; i < af9015_properties[0].num_adapters; i++) {
+ if (i == 1)
+ offset = AF9015_EEPROM_OFFSET;
+ /* xtal */
+ req.addr = AF9015_EEPROM_XTAL_TYPE1 + offset;
+ ret = af9015_rw_udev(udev, &req);
+ if (ret)
+ goto error;
+ switch (val) {
+ case 0:
+ af9015_af9013_config[i].adc_clock = 28800;
+ break;
+ case 1:
+ af9015_af9013_config[i].adc_clock = 20480;
+ break;
+ case 2:
+ af9015_af9013_config[i].adc_clock = 28000;
+ break;
+ case 3:
+ af9015_af9013_config[i].adc_clock = 25000;
+ break;
+ };
+ deb_info("%s: [%d] xtal:%d set adc_clock:%d\n", __func__, i,
+ val, af9015_af9013_config[i].adc_clock);
+
+ /* tuner IF */
+ req.addr = AF9015_EEPROM_IF1H + offset;
+ ret = af9015_rw_udev(udev, &req);
+ if (ret)
+ goto error;
+ af9015_af9013_config[i].tuner_if = val << 8;
+ req.addr = AF9015_EEPROM_IF1L + offset;
+ ret = af9015_rw_udev(udev, &req);
+ if (ret)
+ goto error;
+ af9015_af9013_config[i].tuner_if += val;
+ deb_info("%s: [%d] IF1:%d\n", __func__, i,
+ af9015_af9013_config[0].tuner_if);
+
+ /* MT2060 IF1 */
+ req.addr = AF9015_EEPROM_MT2060_IF1H + offset;
+ ret = af9015_rw_udev(udev, &req);
+ if (ret)
+ goto error;
+ af9015_config.mt2060_if1[i] = val << 8;
+ req.addr = AF9015_EEPROM_MT2060_IF1L + offset;
+ ret = af9015_rw_udev(udev, &req);
+ if (ret)
+ goto error;
+ af9015_config.mt2060_if1[i] += val;
+ deb_info("%s: [%d] MT2060 IF1:%d\n", __func__, i,
+ af9015_config.mt2060_if1[i]);
+
+ /* tuner */
+ req.addr = AF9015_EEPROM_TUNER_ID1 + offset;
+ ret = af9015_rw_udev(udev, &req);
+ if (ret)
+ goto error;
+ switch (val) {
+ case AF9013_TUNER_ENV77H11D5:
+ case AF9013_TUNER_MT2060:
+ case AF9013_TUNER_MC44S803:
+ case AF9013_TUNER_QT1010:
+ case AF9013_TUNER_UNKNOWN:
+ case AF9013_TUNER_MT2060_2:
+ case AF9013_TUNER_TDA18271:
+ case AF9013_TUNER_QT1010A:
+ af9015_af9013_config[i].rf_spec_inv = 1;
+ break;
+ case AF9013_TUNER_MXL5003D:
+ case AF9013_TUNER_MXL5005D:
+ case AF9013_TUNER_MXL5005R:
+ af9015_af9013_config[i].rf_spec_inv = 0;
+ break;
+ default:
+ warn("tuner id:%d not supported, please report!", val);
+ return -ENODEV;
+ };
+
+ af9015_af9013_config[i].tuner = val;
+ deb_info("%s: [%d] tuner id:%d\n", __func__, i, val);
+ }
+
+error:
+ if (ret)
+ err("eeprom read failed:%d", ret);
+
+ return ret;
+}
+
+static int af9015_identify_state(struct usb_device *udev,
+ struct dvb_usb_device_properties *props,
+ struct dvb_usb_device_description **desc,
+ int *cold)
+{
+ int ret;
+ u8 reply;
+ struct req_t req = {GET_CONFIG, 0, 0, 0, 0, 1, &reply};
+
+ ret = af9015_rw_udev(udev, &req);
+ if (ret)
+ return ret;
+
+ deb_info("%s: reply:%02x\n", __func__, reply);
+ if (reply == 0x02)
+ *cold = 0;
+ else
+ *cold = 1;
+
+ return ret;
+}
+
+static int af9015_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
+{
+ u8 buf[8];
+ struct req_t req = {GET_IR_CODE, 0, 0, 0, 0, sizeof(buf), buf};
+ struct dvb_usb_rc_key *keymap = d->props.rc_key_map;
+ int i, ret;
+
+ memset(buf, 0, sizeof(buf));
+
+ ret = af9015_ctrl_msg(d, &req);
+ if (ret)
+ return ret;
+
+ *event = 0;
+ *state = REMOTE_NO_KEY_PRESSED;
+
+ for (i = 0; i < d->props.rc_key_map_size; i++) {
+ if (!buf[1] && keymap[i].custom == buf[0] &&
+ keymap[i].data == buf[2]) {
+ *event = keymap[i].event;
+ *state = REMOTE_KEY_PRESSED;
+ break;
+ }
+ }
+ if (!buf[1])
+ deb_rc("%s: %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ __func__, buf[0], buf[1], buf[2], buf[3], buf[4],
+ buf[5], buf[6], buf[7]);
+
+ return 0;
+}
+
+/* init 2nd I2C adapter */
+int af9015_i2c_init(struct dvb_usb_device *d)
+{
+ int ret;
+ struct af9015_state *state = d->priv;
+ deb_info("%s:\n", __func__);
+
+ strncpy(state->i2c_adap.name, d->desc->name,
+ sizeof(state->i2c_adap.name));
+#ifdef I2C_ADAP_CLASS_TV_DIGITAL
+ state->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL,
+#else
+ state->i2c_adap.class = I2C_CLASS_TV_DIGITAL,
+#endif
+ state->i2c_adap.algo = d->props.i2c_algo;
+ state->i2c_adap.algo_data = NULL;
+ state->i2c_adap.dev.parent = &d->udev->dev;
+
+ i2c_set_adapdata(&state->i2c_adap, d);
+
+ ret = i2c_add_adapter(&state->i2c_adap);
+ if (ret < 0)
+ err("could not add i2c adapter");
+
+ return ret;
+}
+
+static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ int ret;
+ struct af9015_state *state = adap->dev->priv;
+ struct i2c_adapter *i2c_adap;
+
+ if (adap->id == 0) {
+ /* select I2C adapter */
+ i2c_adap = &adap->dev->i2c_adap;
+
+ deb_info("%s: init I2C\n", __func__);
+ ret = af9015_i2c_init(adap->dev);
+
+ /* dump eeprom (debug) */
+ ret = af9015_eeprom_dump(adap->dev);
+ if (ret)
+ return ret;
+ } else {
+ /* select I2C adapter */
+ i2c_adap = &state->i2c_adap;
+
+ /* copy firmware to 2nd demodulator */
+ if (af9015_config.dual_mode) {
+ ret = af9015_copy_firmware(adap->dev);
+ if (ret) {
+ err("firmware copy to 2nd frontend " \
+ "failed, will disable it");
+ af9015_config.dual_mode = 0;
+ return -ENODEV;
+ }
+ } else {
+ return -ENODEV;
+ }
+ }
+
+ /* attach demodulator */
+ adap->fe = dvb_attach(af9013_attach, &af9015_af9013_config[adap->id],
+ i2c_adap);
+
+ return adap->fe == NULL ? -ENODEV : 0;
+}
+
+static struct mt2060_config af9015_mt2060_config = {
+ .i2c_address = 0xc0,
+ .clock_out = 0,
+};
+
+static struct qt1010_config af9015_qt1010_config = {
+ .i2c_address = 0xc4,
+};
+
+static struct tda18271_config af9015_tda18271_config = {
+ .gate = TDA18271_GATE_DIGITAL,
+ .small_i2c = 1,
+};
+
+static struct mxl5005s_config af9015_mxl5003_config = {
+ .i2c_address = 0xc6,
+ .if_freq = IF_FREQ_4570000HZ,
+ .xtal_freq = CRYSTAL_FREQ_16000000HZ,
+ .agc_mode = MXL_SINGLE_AGC,
+ .tracking_filter = MXL_TF_DEFAULT,
+ .rssi_enable = MXL_RSSI_ENABLE,
+ .cap_select = MXL_CAP_SEL_ENABLE,
+ .div_out = MXL_DIV_OUT_4,
+ .clock_out = MXL_CLOCK_OUT_DISABLE,
+ .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
+ .top = MXL5005S_TOP_25P2,
+ .mod_mode = MXL_DIGITAL_MODE,
+ .if_mode = MXL_ZERO_IF,
+ .AgcMasterByte = 0x00,
+};
+
+static struct mxl5005s_config af9015_mxl5005_config = {
+ .i2c_address = 0xc6,
+ .if_freq = IF_FREQ_4570000HZ,
+ .xtal_freq = CRYSTAL_FREQ_16000000HZ,
+ .agc_mode = MXL_SINGLE_AGC,
+ .tracking_filter = MXL_TF_OFF,
+ .rssi_enable = MXL_RSSI_ENABLE,
+ .cap_select = MXL_CAP_SEL_ENABLE,
+ .div_out = MXL_DIV_OUT_4,
+ .clock_out = MXL_CLOCK_OUT_DISABLE,
+ .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
+ .top = MXL5005S_TOP_25P2,
+ .mod_mode = MXL_DIGITAL_MODE,
+ .if_mode = MXL_ZERO_IF,
+ .AgcMasterByte = 0x00,
+};
+
+static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ struct af9015_state *state = adap->dev->priv;
+ struct i2c_adapter *i2c_adap;
+ int ret;
+ deb_info("%s: \n", __func__);
+
+ /* select I2C adapter */
+ if (adap->id == 0)
+ i2c_adap = &adap->dev->i2c_adap;
+ else
+ i2c_adap = &state->i2c_adap;
+
+ switch (af9015_af9013_config[adap->id].tuner) {
+ case AF9013_TUNER_MT2060:
+ case AF9013_TUNER_MT2060_2:
+ ret = dvb_attach(mt2060_attach, adap->fe, i2c_adap,
+ &af9015_mt2060_config,
+ af9015_config.mt2060_if1[adap->id])
+ == NULL ? -ENODEV : 0;
+ break;
+ case AF9013_TUNER_QT1010:
+ case AF9013_TUNER_QT1010A:
+ ret = dvb_attach(qt1010_attach, adap->fe, i2c_adap,
+ &af9015_qt1010_config) == NULL ? -ENODEV : 0;
+ break;
+ case AF9013_TUNER_TDA18271:
+ ret = dvb_attach(tda18271_attach, adap->fe, 0xc0, i2c_adap,
+ &af9015_tda18271_config) == NULL ? -ENODEV : 0;
+ break;
+ case AF9013_TUNER_MXL5003D:
+ ret = dvb_attach(mxl5005s_attach, adap->fe, i2c_adap,
+ &af9015_mxl5003_config) == NULL ? -ENODEV : 0;
+ break;
+ case AF9013_TUNER_MXL5005D:
+ case AF9013_TUNER_MXL5005R:
+ ret = dvb_attach(mxl5005s_attach, adap->fe, i2c_adap,
+ &af9015_mxl5005_config) == NULL ? -ENODEV : 0;
+ break;
+ case AF9013_TUNER_ENV77H11D5:
+ ret = dvb_attach(dvb_pll_attach, adap->fe, 0xc0, i2c_adap,
+ DVB_PLL_TDA665X) == NULL ? -ENODEV : 0;
+ break;
+ case AF9013_TUNER_MC44S803:
+#if 0
+ ret = dvb_attach(mc44s80x_attach, adap->fe, i2c_adap)
+ == NULL ? -ENODEV : 0;
+#else
+ ret = -ENODEV;
+ info("Freescale MC44S803 tuner found but no driver for that" \
+ "tuner. Look at the Linuxtv.org for tuner driver" \
+ "status.");
+#endif
+ break;
+ case AF9013_TUNER_UNKNOWN:
+ default:
+ ret = -ENODEV;
+ err("Unknown tuner id:%d",
+ af9015_af9013_config[adap->id].tuner);
+ }
+ return ret;
+}
+
+static struct usb_device_id af9015_usb_table[] = {
+/* 0 */{USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9015_9015)},
+ {USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9015_9016)},
+ {USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_GOLD)},
+ {USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV71E)},
+ {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U)},
+/* 5 */{USB_DEVICE(USB_VID_VISIONPLUS,
+ USB_PID_TINYTWIN)},
+ {USB_DEVICE(USB_VID_VISIONPLUS,
+ USB_PID_AZUREWAVE_AD_TU700)},
+ {USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2)},
+ {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_PC160_2T)},
+ {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X)},
+/* 10 */{USB_DEVICE(USB_VID_XTENSIONS, USB_PID_XTENSIONS_XD_380)},
+ {USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGIVOX_DUO)},
+ {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X_2)},
+ {USB_DEVICE(USB_VID_TELESTAR, USB_PID_TELESTAR_STARSTICK_2)},
+ {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A309)},
+/* 15 */{USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGI_VOX_MINI_III)},
+ {0},
+};
+MODULE_DEVICE_TABLE(usb, af9015_usb_table);
+
+static struct dvb_usb_device_properties af9015_properties[] = {
+ {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = DEVICE_SPECIFIC,
+ .download_firmware = af9015_download_firmware,
+ .firmware = "dvb-usb-af9015.fw",
+
+ .size_of_priv = sizeof(struct af9015_state), \
+
+ .num_adapters = 2,
+ .adapter = {
+ {
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER |
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+
+ .pid_filter_count = 32,
+ .pid_filter = af9015_pid_filter,
+ .pid_filter_ctrl = af9015_pid_filter_ctrl,
+
+ .frontend_attach =
+ af9015_af9013_frontend_attach,
+ .tuner_attach = af9015_tuner_attach,
+ .stream = {
+ .type = USB_BULK,
+ .count = 6,
+ .endpoint = 0x84,
+ },
+ },
+ {
+ .frontend_attach =
+ af9015_af9013_frontend_attach,
+ .tuner_attach = af9015_tuner_attach,
+ .stream = {
+ .type = USB_BULK,
+ .count = 6,
+ .endpoint = 0x85,
+ },
+ }
+ },
+
+ .identify_state = af9015_identify_state,
+
+ .rc_query = af9015_rc_query,
+ .rc_interval = 150,
+
+ .i2c_algo = &af9015_i2c_algo,
+
+ .num_device_descs = 9,
+ .devices = {
+ {
+ .name = "Afatech AF9015 DVB-T USB2.0 stick",
+ .cold_ids = {&af9015_usb_table[0],
+ &af9015_usb_table[1], NULL},
+ .warm_ids = {NULL},
+ },
+ {
+ .name = "Leadtek WinFast DTV Dongle Gold",
+ .cold_ids = {&af9015_usb_table[2], NULL},
+ .warm_ids = {NULL},
+ },
+ {
+ .name = "Pinnacle PCTV 71e",
+ .cold_ids = {&af9015_usb_table[3], NULL},
+ .warm_ids = {NULL},
+ },
+ {
+ .name = "KWorld PlusTV Dual DVB-T Stick " \
+ "(DVB-T 399U)",
+ .cold_ids = {&af9015_usb_table[4], NULL},
+ .warm_ids = {NULL},
+ },
+ {
+ .name = "DigitalNow TinyTwin DVB-T Receiver",
+ .cold_ids = {&af9015_usb_table[5], NULL},
+ .warm_ids = {NULL},
+ },
+ {
+ .name = "TwinHan AzureWave AD-TU700(704J)",
+ .cold_ids = {&af9015_usb_table[6], NULL},
+ .warm_ids = {NULL},
+ },
+ {
+ .name = "TerraTec Cinergy T USB XE",
+ .cold_ids = {&af9015_usb_table[7], NULL},
+ .warm_ids = {NULL},
+ },
+ {
+ .name = "KWorld PlusTV Dual DVB-T PCI " \
+ "(DVB-T PC160-2T)",
+ .cold_ids = {&af9015_usb_table[8], NULL},
+ .warm_ids = {NULL},
+ },
+ {
+ .name = "AVerMedia AVerTV DVB-T Volar X",
+ .cold_ids = {&af9015_usb_table[9], NULL},
+ .warm_ids = {NULL},
+ },
+ }
+ }, {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = DEVICE_SPECIFIC,
+ .download_firmware = af9015_download_firmware,
+ .firmware = "dvb-usb-af9015.fw",
+
+ .size_of_priv = sizeof(struct af9015_state), \
+
+ .num_adapters = 2,
+ .adapter = {
+ {
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER |
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+
+ .pid_filter_count = 32,
+ .pid_filter = af9015_pid_filter,
+ .pid_filter_ctrl = af9015_pid_filter_ctrl,
+
+ .frontend_attach =
+ af9015_af9013_frontend_attach,
+ .tuner_attach = af9015_tuner_attach,
+ .stream = {
+ .type = USB_BULK,
+ .count = 6,
+ .endpoint = 0x84,
+ },
+ },
+ {
+ .frontend_attach =
+ af9015_af9013_frontend_attach,
+ .tuner_attach = af9015_tuner_attach,
+ .stream = {
+ .type = USB_BULK,
+ .count = 6,
+ .endpoint = 0x85,
+ },
+ }
+ },
+
+ .identify_state = af9015_identify_state,
+
+ .rc_query = af9015_rc_query,
+ .rc_interval = 150,
+
+ .i2c_algo = &af9015_i2c_algo,
+
+ .num_device_descs = 6,
+ .devices = {
+ {
+ .name = "Xtensions XD-380",
+ .cold_ids = {&af9015_usb_table[10], NULL},
+ .warm_ids = {NULL},
+ },
+ {
+ .name = "MSI DIGIVOX Duo",
+ .cold_ids = {&af9015_usb_table[11], NULL},
+ .warm_ids = {NULL},
+ },
+ {
+ .name = "Fujitsu-Siemens Slim Mobile USB DVB-T",
+ .cold_ids = {&af9015_usb_table[12], NULL},
+ .warm_ids = {NULL},
+ },
+ {
+ .name = "Telestar Starstick 2",
+ .cold_ids = {&af9015_usb_table[13], NULL},
+ .warm_ids = {NULL},
+ },
+ {
+ .name = "AVerMedia A309",
+ .cold_ids = {&af9015_usb_table[14], NULL},
+ .warm_ids = {NULL},
+ },
+ {
+ .name = "MSI Digi VOX mini III",
+ .cold_ids = {&af9015_usb_table[15], NULL},
+ .warm_ids = {NULL},
+ },
+ }
+ }
+};
+
+static int af9015_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ int ret = 0;
+ struct dvb_usb_device *d = NULL;
+ struct usb_device *udev = interface_to_usbdev(intf);
+ u8 i;
+
+ deb_info("%s: interface:%d\n", __func__,
+ intf->cur_altsetting->desc.bInterfaceNumber);
+
+ /* interface 0 is used by DVB-T receiver and
+ interface 1 is for remote controller (HID) */
+ if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
+ ret = af9015_read_config(udev);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < af9015_properties_count; i++) {
+ ret = dvb_usb_device_init(intf, &af9015_properties[i],
+ THIS_MODULE, &d, adapter_nr);
+ if (!ret)
+ break;
+ if (ret != -ENODEV)
+ return ret;
+ }
+ if (ret)
+ return ret;
+
+ if (d)
+ ret = af9015_init(d);
+ }
+
+ return ret;
+}
+
+void af9015_i2c_exit(struct dvb_usb_device *d)
+{
+ struct af9015_state *state = d->priv;
+ deb_info("%s: \n", __func__);
+
+ /* remove 2nd I2C adapter */
+ if (d->state & DVB_USB_STATE_I2C)
+ i2c_del_adapter(&state->i2c_adap);
+}
+
+static void af9015_usb_device_exit(struct usb_interface *intf)
+{
+ struct dvb_usb_device *d = usb_get_intfdata(intf);
+ deb_info("%s: \n", __func__);
+
+ /* remove 2nd I2C adapter */
+ if (d != NULL && d->desc != NULL)
+ af9015_i2c_exit(d);
+
+ dvb_usb_device_exit(intf);
+}
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver af9015_usb_driver = {
+ .name = "dvb_usb_af9015",
+ .probe = af9015_usb_probe,
+ .disconnect = af9015_usb_device_exit,
+ .id_table = af9015_usb_table,
+};
+
+/* module stuff */
+static int __init af9015_usb_module_init(void)
+{
+ int ret;
+ ret = usb_register(&af9015_usb_driver);
+ if (ret)
+ err("module init failed:%d", ret);
+
+ return ret;
+}
+
+static void __exit af9015_usb_module_exit(void)
+{
+ /* deregister this driver from the USB subsystem */
+ usb_deregister(&af9015_usb_driver);
+}
+
+module_init(af9015_usb_module_init);
+module_exit(af9015_usb_module_exit);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Driver for Afatech AF9015 DVB-T");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h
new file mode 100644
index 0000000..882e8a4
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/af9015.h
@@ -0,0 +1,524 @@
+/*
+ * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ *
+ * Thanks to Afatech who kindly provided information.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef _DVB_USB_AF9015_H_
+#define _DVB_USB_AF9015_H_
+
+#define DVB_USB_LOG_PREFIX "af9015"
+#include "dvb-usb.h"
+
+extern int dvb_usb_af9015_debug;
+#define deb_info(args...) dprintk(dvb_usb_af9015_debug, 0x01, args)
+#define deb_rc(args...) dprintk(dvb_usb_af9015_debug, 0x02, args)
+#define deb_xfer(args...) dprintk(dvb_usb_af9015_debug, 0x04, args)
+#define deb_reg(args...) dprintk(dvb_usb_af9015_debug, 0x08, args)
+#define deb_i2c(args...) dprintk(dvb_usb_af9015_debug, 0x10, args)
+#define deb_fw(args...) dprintk(dvb_usb_af9015_debug, 0x20, args)
+
+#define AF9015_I2C_EEPROM 0xa0
+#define AF9015_I2C_DEMOD 0x38
+#define AF9015_USB_TIMEOUT 2000
+
+/* EEPROM locations */
+#define AF9015_EEPROM_IR_MODE 0x18
+#define AF9015_EEPROM_IR_REMOTE_TYPE 0x34
+#define AF9015_EEPROM_TS_MODE 0x31
+#define AF9015_EEPROM_DEMOD2_I2C 0x32
+
+#define AF9015_EEPROM_SAW_BW1 0x35
+#define AF9015_EEPROM_XTAL_TYPE1 0x36
+#define AF9015_EEPROM_SPEC_INV1 0x37
+#define AF9015_EEPROM_IF1L 0x38
+#define AF9015_EEPROM_IF1H 0x39
+#define AF9015_EEPROM_MT2060_IF1L 0x3a
+#define AF9015_EEPROM_MT2060_IF1H 0x3b
+#define AF9015_EEPROM_TUNER_ID1 0x3c
+
+#define AF9015_EEPROM_SAW_BW2 0x45
+#define AF9015_EEPROM_XTAL_TYPE2 0x46
+#define AF9015_EEPROM_SPEC_INV2 0x47
+#define AF9015_EEPROM_IF2L 0x48
+#define AF9015_EEPROM_IF2H 0x49
+#define AF9015_EEPROM_MT2060_IF2L 0x4a
+#define AF9015_EEPROM_MT2060_IF2H 0x4b
+#define AF9015_EEPROM_TUNER_ID2 0x4c
+
+#define AF9015_EEPROM_OFFSET (AF9015_EEPROM_SAW_BW2 - AF9015_EEPROM_SAW_BW1)
+
+#define AF9015_GPIO_ON (1 << 0)
+#define AF9015_GPIO_EN (1 << 1)
+#define AF9015_GPIO_O (1 << 2)
+#define AF9015_GPIO_I (1 << 3)
+
+#define AF9015_GPIO_TUNER_ON (AF9015_GPIO_ON|AF9015_GPIO_EN)
+#define AF9015_GPIO_TUNER_OFF (AF9015_GPIO_ON|AF9015_GPIO_EN|AF9015_GPIO_O)
+
+struct req_t {
+ u8 cmd; /* [0] */
+ /* seq */ /* [1] */
+ u8 i2c_addr; /* [2] */
+ u16 addr; /* [3|4] */
+ u8 mbox; /* [5] */
+ u8 addr_len; /* [6] */
+ u8 data_len; /* [7] */
+ u8 *data;
+};
+
+enum af9015_cmd {
+ GET_CONFIG = 0x10,
+ DOWNLOAD_FIRMWARE = 0x11,
+ BOOT = 0x13,
+ READ_MEMORY = 0x20,
+ WRITE_MEMORY = 0x21,
+ READ_WRITE_I2C = 0x22,
+ COPY_FIRMWARE = 0x23,
+ RECONNECT_USB = 0x5a,
+ WRITE_VIRTUAL_MEMORY = 0x26,
+ GET_IR_CODE = 0x27,
+ READ_I2C,
+ WRITE_I2C,
+};
+
+enum af9015_ir_mode {
+ AF9015_IR_MODE_DISABLED = 0,
+ AF9015_IR_MODE_HID,
+ AF9015_IR_MODE_RLC,
+ AF9015_IR_MODE_RC6,
+};
+
+struct af9015_state {
+ struct i2c_adapter i2c_adap; /* I2C adapter for 2nd FE */
+};
+
+struct af9015_config {
+ u8 dual_mode:1;
+ u16 mt2060_if1[2];
+ u16 firmware_size;
+ u16 firmware_checksum;
+ u8 *ir_table;
+ u16 ir_table_size;
+};
+
+enum af9015_remote {
+ AF9015_REMOTE_NONE = 0,
+ AF9015_REMOTE_A_LINK_DTU_M,
+ AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3,
+ AF9015_REMOTE_MYGICTV_U718,
+};
+
+/* Leadtek WinFast DTV Dongle Gold */
+static struct dvb_usb_rc_key af9015_rc_keys_leadtek[] = {
+ { 0x00, 0x1e, KEY_1 },
+ { 0x00, 0x1f, KEY_2 },
+ { 0x00, 0x20, KEY_3 },
+ { 0x00, 0x21, KEY_4 },
+ { 0x00, 0x22, KEY_5 },
+ { 0x00, 0x23, KEY_6 },
+ { 0x00, 0x24, KEY_7 },
+ { 0x00, 0x25, KEY_8 },
+ { 0x00, 0x26, KEY_9 },
+ { 0x00, 0x27, KEY_0 },
+ { 0x00, 0x28, KEY_ENTER },
+ { 0x00, 0x4f, KEY_VOLUMEUP },
+ { 0x00, 0x50, KEY_VOLUMEDOWN },
+ { 0x00, 0x51, KEY_CHANNELDOWN },
+ { 0x00, 0x52, KEY_CHANNELUP },
+};
+
+static u8 af9015_ir_table_leadtek[] = {
+ 0x03, 0xfc, 0x00, 0xff, 0x1a, 0x01, 0x00,
+ 0x03, 0xfc, 0x56, 0xa9, 0x00, 0x00, 0x00,
+ 0x03, 0xfc, 0x4b, 0xb4, 0x00, 0x00, 0x00,
+ 0x03, 0xfc, 0x4c, 0xb3, 0xb2, 0x04, 0x00,
+ 0x03, 0xfc, 0x4d, 0xb2, 0x00, 0x00, 0x00,
+ 0x03, 0xfc, 0x4e, 0xb1, 0x00, 0x00, 0x00,
+ 0x03, 0xfc, 0x1f, 0xe0, 0x3d, 0x00, 0x00,
+ 0x03, 0xfc, 0x40, 0xbf, 0x13, 0x01, 0x00,
+ 0x03, 0xfc, 0x14, 0xeb, 0x10, 0x00, 0x00,
+ 0x03, 0xfc, 0x49, 0xb6, 0x05, 0x01, 0x00,
+ 0x03, 0xfc, 0x50, 0xaf, 0x29, 0x00, 0x00,
+ 0x03, 0xfc, 0x0c, 0xf3, 0x52, 0x00, 0x00,
+ 0x03, 0xfc, 0x03, 0xfc, 0x09, 0x00, 0x00,
+ 0x03, 0xfc, 0x08, 0xf7, 0x50, 0x00, 0x00,
+ 0x03, 0xfc, 0x13, 0xec, 0x28, 0x00, 0x00,
+ 0x03, 0xfc, 0x04, 0xfb, 0x4f, 0x00, 0x00,
+ 0x03, 0xfc, 0x4f, 0xb0, 0x0f, 0x01, 0x00,
+ 0x03, 0xfc, 0x10, 0xef, 0x51, 0x00, 0x00,
+ 0x03, 0xfc, 0x51, 0xae, 0x3f, 0x00, 0x00,
+ 0x03, 0xfc, 0x42, 0xbd, 0x13, 0x00, 0x00,
+ 0x03, 0xfc, 0x43, 0xbc, 0x00, 0x00, 0x00,
+ 0x03, 0xfc, 0x44, 0xbb, 0x11, 0x00, 0x00,
+ 0x03, 0xfc, 0x52, 0xad, 0x19, 0x00, 0x00,
+ 0x03, 0xfc, 0x54, 0xab, 0x05, 0x00, 0x00,
+ 0x03, 0xfc, 0x46, 0xb9, 0x29, 0x00, 0x00,
+ 0x03, 0xfc, 0x55, 0xaa, 0x2b, 0x00, 0x00,
+ 0x03, 0xfc, 0x53, 0xac, 0x41, 0x00, 0x00,
+ 0x03, 0xfc, 0x05, 0xfa, 0x1e, 0x00, 0x00,
+ 0x03, 0xfc, 0x06, 0xf9, 0x1f, 0x00, 0x00,
+ 0x03, 0xfc, 0x07, 0xf8, 0x20, 0x00, 0x00,
+ 0x03, 0xfc, 0x1e, 0xe1, 0x19, 0x00, 0x00,
+ 0x03, 0xfc, 0x09, 0xf6, 0x21, 0x00, 0x00,
+ 0x03, 0xfc, 0x0a, 0xf5, 0x22, 0x00, 0x00,
+ 0x03, 0xfc, 0x0b, 0xf4, 0x23, 0x00, 0x00,
+ 0x03, 0xfc, 0x1b, 0xe4, 0x16, 0x00, 0x00,
+ 0x03, 0xfc, 0x0d, 0xf2, 0x24, 0x00, 0x00,
+ 0x03, 0xfc, 0x0e, 0xf1, 0x25, 0x00, 0x00,
+ 0x03, 0xfc, 0x0f, 0xf0, 0x26, 0x00, 0x00,
+ 0x03, 0xfc, 0x16, 0xe9, 0x28, 0x00, 0x00,
+ 0x03, 0xfc, 0x41, 0xbe, 0x37, 0x00, 0x00,
+ 0x03, 0xfc, 0x12, 0xed, 0x27, 0x00, 0x00,
+ 0x03, 0xfc, 0x11, 0xee, 0x2a, 0x00, 0x00,
+ 0x03, 0xfc, 0x48, 0xb7, 0x2c, 0x00, 0x00,
+ 0x03, 0xfc, 0x4a, 0xb5, 0x3c, 0x00, 0x00,
+ 0x03, 0xfc, 0x47, 0xb8, 0x15, 0x01, 0x00,
+ 0x03, 0xfc, 0x45, 0xba, 0x0b, 0x01, 0x00,
+ 0x03, 0xfc, 0x5e, 0xa1, 0x43, 0x00, 0x00,
+ 0x03, 0xfc, 0x5a, 0xa5, 0x42, 0x00, 0x00,
+ 0x03, 0xfc, 0x5b, 0xa4, 0x4b, 0x00, 0x00,
+ 0x03, 0xfc, 0x5f, 0xa0, 0x4e, 0x00, 0x00,
+};
+
+/* TwinHan AzureWave AD-TU700(704J) */
+static struct dvb_usb_rc_key af9015_rc_keys_twinhan[] = {
+ { 0x05, 0x3f, KEY_POWER },
+ { 0x00, 0x19, KEY_FAVORITES }, /* Favorite List */
+ { 0x00, 0x04, KEY_TEXT }, /* Teletext */
+ { 0x00, 0x0e, KEY_POWER },
+ { 0x00, 0x0e, KEY_INFO }, /* Preview */
+ { 0x00, 0x08, KEY_EPG }, /* Info/EPG */
+ { 0x00, 0x0f, KEY_LIST }, /* Record List */
+ { 0x00, 0x1e, KEY_1 },
+ { 0x00, 0x1f, KEY_2 },
+ { 0x00, 0x20, KEY_3 },
+ { 0x00, 0x21, KEY_4 },
+ { 0x00, 0x22, KEY_5 },
+ { 0x00, 0x23, KEY_6 },
+ { 0x00, 0x24, KEY_7 },
+ { 0x00, 0x25, KEY_8 },
+ { 0x00, 0x26, KEY_9 },
+ { 0x00, 0x27, KEY_0 },
+ { 0x00, 0x29, KEY_CANCEL }, /* Cancel */
+ { 0x00, 0x4c, KEY_CLEAR }, /* Clear */
+ { 0x00, 0x2a, KEY_BACK }, /* Back */
+ { 0x00, 0x2b, KEY_TAB }, /* Tab */
+ { 0x00, 0x52, KEY_UP }, /* up arrow */
+ { 0x00, 0x51, KEY_DOWN }, /* down arrow */
+ { 0x00, 0x4f, KEY_RIGHT }, /* right arrow */
+ { 0x00, 0x50, KEY_LEFT }, /* left arrow */
+ { 0x00, 0x28, KEY_ENTER }, /* Enter / ok */
+ { 0x02, 0x52, KEY_VOLUMEUP },
+ { 0x02, 0x51, KEY_VOLUMEDOWN },
+ { 0x00, 0x4e, KEY_CHANNELDOWN },
+ { 0x00, 0x4b, KEY_CHANNELUP },
+ { 0x00, 0x4a, KEY_RECORD },
+ { 0x01, 0x11, KEY_PLAY },
+ { 0x00, 0x17, KEY_PAUSE },
+ { 0x00, 0x0c, KEY_REWIND }, /* FR << */
+ { 0x00, 0x11, KEY_FASTFORWARD }, /* FF >> */
+ { 0x01, 0x15, KEY_PREVIOUS }, /* Replay */
+ { 0x01, 0x0e, KEY_NEXT }, /* Skip */
+ { 0x00, 0x13, KEY_CAMERA }, /* Capture */
+ { 0x01, 0x0f, KEY_LANGUAGE }, /* SAP */
+ { 0x01, 0x13, KEY_TV2 }, /* PIP */
+ { 0x00, 0x1d, KEY_ZOOM }, /* Full Screen */
+ { 0x01, 0x17, KEY_SUBTITLE }, /* Subtitle / CC */
+ { 0x00, 0x10, KEY_MUTE },
+ { 0x01, 0x19, KEY_AUDIO }, /* L/R */ /* TODO better event */
+ { 0x01, 0x16, KEY_SLEEP }, /* Hibernate */
+ { 0x01, 0x16, KEY_SWITCHVIDEOMODE },
+ /* A/V */ /* TODO does not work */
+ { 0x00, 0x06, KEY_AGAIN }, /* Recall */
+ { 0x01, 0x16, KEY_KPPLUS }, /* Zoom+ */ /* TODO does not work */
+ { 0x01, 0x16, KEY_KPMINUS }, /* Zoom- */ /* TODO does not work */
+ { 0x02, 0x15, KEY_RED },
+ { 0x02, 0x0a, KEY_GREEN },
+ { 0x02, 0x1c, KEY_YELLOW },
+ { 0x02, 0x05, KEY_BLUE },
+};
+
+static u8 af9015_ir_table_twinhan[] = {
+ 0x00, 0xff, 0x16, 0xe9, 0x3f, 0x05, 0x00,
+ 0x00, 0xff, 0x07, 0xf8, 0x16, 0x01, 0x00,
+ 0x00, 0xff, 0x14, 0xeb, 0x11, 0x01, 0x00,
+ 0x00, 0xff, 0x1a, 0xe5, 0x4d, 0x00, 0x00,
+ 0x00, 0xff, 0x4c, 0xb3, 0x17, 0x00, 0x00,
+ 0x00, 0xff, 0x12, 0xed, 0x11, 0x00, 0x00,
+ 0x00, 0xff, 0x40, 0xbf, 0x0c, 0x00, 0x00,
+ 0x00, 0xff, 0x11, 0xee, 0x4a, 0x00, 0x00,
+ 0x00, 0xff, 0x54, 0xab, 0x13, 0x00, 0x00,
+ 0x00, 0xff, 0x41, 0xbe, 0x15, 0x01, 0x00,
+ 0x00, 0xff, 0x42, 0xbd, 0x0e, 0x01, 0x00,
+ 0x00, 0xff, 0x43, 0xbc, 0x17, 0x01, 0x00,
+ 0x00, 0xff, 0x50, 0xaf, 0x0f, 0x01, 0x00,
+ 0x00, 0xff, 0x4d, 0xb2, 0x1d, 0x00, 0x00,
+ 0x00, 0xff, 0x47, 0xb8, 0x13, 0x01, 0x00,
+ 0x00, 0xff, 0x05, 0xfa, 0x4b, 0x00, 0x00,
+ 0x00, 0xff, 0x02, 0xfd, 0x4e, 0x00, 0x00,
+ 0x00, 0xff, 0x0e, 0xf1, 0x06, 0x00, 0x00,
+ 0x00, 0xff, 0x1e, 0xe1, 0x52, 0x02, 0x00,
+ 0x00, 0xff, 0x0a, 0xf5, 0x51, 0x02, 0x00,
+ 0x00, 0xff, 0x10, 0xef, 0x10, 0x00, 0x00,
+ 0x00, 0xff, 0x49, 0xb6, 0x19, 0x01, 0x00,
+ 0x00, 0xff, 0x15, 0xea, 0x27, 0x00, 0x00,
+ 0x00, 0xff, 0x03, 0xfc, 0x1e, 0x00, 0x00,
+ 0x00, 0xff, 0x01, 0xfe, 0x1f, 0x00, 0x00,
+ 0x00, 0xff, 0x06, 0xf9, 0x20, 0x00, 0x00,
+ 0x00, 0xff, 0x09, 0xf6, 0x21, 0x00, 0x00,
+ 0x00, 0xff, 0x1d, 0xe2, 0x22, 0x00, 0x00,
+ 0x00, 0xff, 0x1f, 0xe0, 0x23, 0x00, 0x00,
+ 0x00, 0xff, 0x0d, 0xf2, 0x24, 0x00, 0x00,
+ 0x00, 0xff, 0x19, 0xe6, 0x25, 0x00, 0x00,
+ 0x00, 0xff, 0x1b, 0xe4, 0x26, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0xff, 0x2b, 0x00, 0x00,
+ 0x00, 0xff, 0x4a, 0xb5, 0x4c, 0x00, 0x00,
+ 0x00, 0xff, 0x4b, 0xb4, 0x52, 0x00, 0x00,
+ 0x00, 0xff, 0x51, 0xae, 0x51, 0x00, 0x00,
+ 0x00, 0xff, 0x52, 0xad, 0x4f, 0x00, 0x00,
+ 0x00, 0xff, 0x4e, 0xb1, 0x50, 0x00, 0x00,
+ 0x00, 0xff, 0x0c, 0xf3, 0x29, 0x00, 0x00,
+ 0x00, 0xff, 0x4f, 0xb0, 0x28, 0x00, 0x00,
+ 0x00, 0xff, 0x13, 0xec, 0x2a, 0x00, 0x00,
+ 0x00, 0xff, 0x17, 0xe8, 0x19, 0x00, 0x00,
+ 0x00, 0xff, 0x04, 0xfb, 0x0f, 0x00, 0x00,
+ 0x00, 0xff, 0x48, 0xb7, 0x0e, 0x00, 0x00,
+ 0x00, 0xff, 0x0f, 0xf0, 0x04, 0x00, 0x00,
+ 0x00, 0xff, 0x1c, 0xe3, 0x08, 0x00, 0x00,
+ 0x00, 0xff, 0x18, 0xe7, 0x15, 0x02, 0x00,
+ 0x00, 0xff, 0x53, 0xac, 0x0a, 0x02, 0x00,
+ 0x00, 0xff, 0x5e, 0xa1, 0x1c, 0x02, 0x00,
+ 0x00, 0xff, 0x5f, 0xa0, 0x05, 0x02, 0x00,
+};
+
+/* A-Link DTU(m) */
+static struct dvb_usb_rc_key af9015_rc_keys_a_link[] = {
+ { 0x00, 0x1e, KEY_1 },
+ { 0x00, 0x1f, KEY_2 },
+ { 0x00, 0x20, KEY_3 },
+ { 0x00, 0x21, KEY_4 },
+ { 0x00, 0x22, KEY_5 },
+ { 0x00, 0x23, KEY_6 },
+ { 0x00, 0x24, KEY_7 },
+ { 0x00, 0x25, KEY_8 },
+ { 0x00, 0x26, KEY_9 },
+ { 0x00, 0x27, KEY_0 },
+ { 0x00, 0x2e, KEY_CHANNELUP },
+ { 0x00, 0x2d, KEY_CHANNELDOWN },
+ { 0x04, 0x28, KEY_ZOOM },
+ { 0x00, 0x41, KEY_MUTE },
+ { 0x00, 0x42, KEY_VOLUMEDOWN },
+ { 0x00, 0x43, KEY_VOLUMEUP },
+ { 0x00, 0x44, KEY_GOTO }, /* jump */
+ { 0x05, 0x45, KEY_POWER },
+};
+
+static u8 af9015_ir_table_a_link[] = {
+ 0x08, 0xf7, 0x12, 0xed, 0x45, 0x05, 0x00, /* power */
+ 0x08, 0xf7, 0x1a, 0xe5, 0x41, 0x00, 0x00, /* mute */
+ 0x08, 0xf7, 0x01, 0xfe, 0x1e, 0x00, 0x00, /* 1 */
+ 0x08, 0xf7, 0x1c, 0xe3, 0x21, 0x00, 0x00, /* 4 */
+ 0x08, 0xf7, 0x03, 0xfc, 0x24, 0x00, 0x00, /* 7 */
+ 0x08, 0xf7, 0x05, 0xfa, 0x28, 0x04, 0x00, /* zoom */
+ 0x08, 0xf7, 0x00, 0xff, 0x43, 0x00, 0x00, /* volume up */
+ 0x08, 0xf7, 0x16, 0xe9, 0x42, 0x00, 0x00, /* volume down */
+ 0x08, 0xf7, 0x0f, 0xf0, 0x1f, 0x00, 0x00, /* 2 */
+ 0x08, 0xf7, 0x0d, 0xf2, 0x22, 0x00, 0x00, /* 5 */
+ 0x08, 0xf7, 0x1b, 0xe4, 0x25, 0x00, 0x00, /* 8 */
+ 0x08, 0xf7, 0x06, 0xf9, 0x27, 0x00, 0x00, /* 0 */
+ 0x08, 0xf7, 0x14, 0xeb, 0x2e, 0x00, 0x00, /* channel up */
+ 0x08, 0xf7, 0x1d, 0xe2, 0x2d, 0x00, 0x00, /* channel down */
+ 0x08, 0xf7, 0x02, 0xfd, 0x20, 0x00, 0x00, /* 3 */
+ 0x08, 0xf7, 0x18, 0xe7, 0x23, 0x00, 0x00, /* 6 */
+ 0x08, 0xf7, 0x04, 0xfb, 0x26, 0x00, 0x00, /* 9 */
+ 0x08, 0xf7, 0x07, 0xf8, 0x44, 0x00, 0x00, /* jump */
+};
+
+/* MSI DIGIVOX mini II V3.0 */
+static struct dvb_usb_rc_key af9015_rc_keys_msi[] = {
+ { 0x00, 0x1e, KEY_1 },
+ { 0x00, 0x1f, KEY_2 },
+ { 0x00, 0x20, KEY_3 },
+ { 0x00, 0x21, KEY_4 },
+ { 0x00, 0x22, KEY_5 },
+ { 0x00, 0x23, KEY_6 },
+ { 0x00, 0x24, KEY_7 },
+ { 0x00, 0x25, KEY_8 },
+ { 0x00, 0x26, KEY_9 },
+ { 0x00, 0x27, KEY_0 },
+ { 0x03, 0x0f, KEY_CHANNELUP },
+ { 0x03, 0x0e, KEY_CHANNELDOWN },
+ { 0x00, 0x42, KEY_VOLUMEDOWN },
+ { 0x00, 0x43, KEY_VOLUMEUP },
+ { 0x05, 0x45, KEY_POWER },
+ { 0x00, 0x52, KEY_UP }, /* up */
+ { 0x00, 0x51, KEY_DOWN }, /* down */
+ { 0x00, 0x28, KEY_ENTER },
+};
+
+static u8 af9015_ir_table_msi[] = {
+ 0x03, 0xfc, 0x17, 0xe8, 0x45, 0x05, 0x00, /* power */
+ 0x03, 0xfc, 0x0d, 0xf2, 0x51, 0x00, 0x00, /* down */
+ 0x03, 0xfc, 0x03, 0xfc, 0x52, 0x00, 0x00, /* up */
+ 0x03, 0xfc, 0x1a, 0xe5, 0x1e, 0x00, 0x00, /* 1 */
+ 0x03, 0xfc, 0x02, 0xfd, 0x1f, 0x00, 0x00, /* 2 */
+ 0x03, 0xfc, 0x04, 0xfb, 0x20, 0x00, 0x00, /* 3 */
+ 0x03, 0xfc, 0x1c, 0xe3, 0x21, 0x00, 0x00, /* 4 */
+ 0x03, 0xfc, 0x08, 0xf7, 0x22, 0x00, 0x00, /* 5 */
+ 0x03, 0xfc, 0x1d, 0xe2, 0x23, 0x00, 0x00, /* 6 */
+ 0x03, 0xfc, 0x11, 0xee, 0x24, 0x00, 0x00, /* 7 */
+ 0x03, 0xfc, 0x0b, 0xf4, 0x25, 0x00, 0x00, /* 8 */
+ 0x03, 0xfc, 0x10, 0xef, 0x26, 0x00, 0x00, /* 9 */
+ 0x03, 0xfc, 0x09, 0xf6, 0x27, 0x00, 0x00, /* 0 */
+ 0x03, 0xfc, 0x14, 0xeb, 0x43, 0x00, 0x00, /* volume up */
+ 0x03, 0xfc, 0x1f, 0xe0, 0x42, 0x00, 0x00, /* volume down */
+ 0x03, 0xfc, 0x15, 0xea, 0x0f, 0x03, 0x00, /* channel up */
+ 0x03, 0xfc, 0x05, 0xfa, 0x0e, 0x03, 0x00, /* channel down */
+ 0x03, 0xfc, 0x16, 0xe9, 0x28, 0x00, 0x00, /* enter */
+};
+
+/* MYGICTV U718 */
+static struct dvb_usb_rc_key af9015_rc_keys_mygictv[] = {
+ { 0x00, 0x3d, KEY_SWITCHVIDEOMODE },
+ /* TV / AV */
+ { 0x05, 0x45, KEY_POWER },
+ { 0x00, 0x1e, KEY_1 },
+ { 0x00, 0x1f, KEY_2 },
+ { 0x00, 0x20, KEY_3 },
+ { 0x00, 0x21, KEY_4 },
+ { 0x00, 0x22, KEY_5 },
+ { 0x00, 0x23, KEY_6 },
+ { 0x00, 0x24, KEY_7 },
+ { 0x00, 0x25, KEY_8 },
+ { 0x00, 0x26, KEY_9 },
+ { 0x00, 0x27, KEY_0 },
+ { 0x00, 0x41, KEY_MUTE },
+ { 0x00, 0x2a, KEY_ESC }, /* Esc */
+ { 0x00, 0x2e, KEY_CHANNELUP },
+ { 0x00, 0x2d, KEY_CHANNELDOWN },
+ { 0x00, 0x42, KEY_VOLUMEDOWN },
+ { 0x00, 0x43, KEY_VOLUMEUP },
+ { 0x00, 0x52, KEY_UP }, /* up arrow */
+ { 0x00, 0x51, KEY_DOWN }, /* down arrow */
+ { 0x00, 0x4f, KEY_RIGHT }, /* right arrow */
+ { 0x00, 0x50, KEY_LEFT }, /* left arrow */
+ { 0x00, 0x28, KEY_ENTER }, /* ok */
+ { 0x01, 0x15, KEY_RECORD },
+ { 0x03, 0x13, KEY_PLAY },
+ { 0x01, 0x13, KEY_PAUSE },
+ { 0x01, 0x16, KEY_STOP },
+ { 0x03, 0x07, KEY_REWIND }, /* FR << */
+ { 0x03, 0x09, KEY_FASTFORWARD }, /* FF >> */
+ { 0x00, 0x3b, KEY_TIME }, /* TimeShift */
+ { 0x00, 0x3e, KEY_CAMERA }, /* Snapshot */
+ { 0x03, 0x16, KEY_CYCLEWINDOWS }, /* yellow, min / max */
+ { 0x00, 0x00, KEY_ZOOM }, /* 'select' (?) */
+ { 0x03, 0x16, KEY_SHUFFLE }, /* Shuffle */
+ { 0x03, 0x45, KEY_POWER },
+};
+
+static u8 af9015_ir_table_mygictv[] = {
+ 0x02, 0xbd, 0x0c, 0xf3, 0x3d, 0x00, 0x00, /* TV / AV */
+ 0x02, 0xbd, 0x14, 0xeb, 0x45, 0x05, 0x00, /* power */
+ 0x02, 0xbd, 0x00, 0xff, 0x1e, 0x00, 0x00, /* 1 */
+ 0x02, 0xbd, 0x01, 0xfe, 0x1f, 0x00, 0x00, /* 2 */
+ 0x02, 0xbd, 0x02, 0xfd, 0x20, 0x00, 0x00, /* 3 */
+ 0x02, 0xbd, 0x03, 0xfc, 0x21, 0x00, 0x00, /* 4 */
+ 0x02, 0xbd, 0x04, 0xfb, 0x22, 0x00, 0x00, /* 5 */
+ 0x02, 0xbd, 0x05, 0xfa, 0x23, 0x00, 0x00, /* 6 */
+ 0x02, 0xbd, 0x06, 0xf9, 0x24, 0x00, 0x00, /* 7 */
+ 0x02, 0xbd, 0x07, 0xf8, 0x25, 0x00, 0x00, /* 8 */
+ 0x02, 0xbd, 0x08, 0xf7, 0x26, 0x00, 0x00, /* 9 */
+ 0x02, 0xbd, 0x09, 0xf6, 0x27, 0x00, 0x00, /* 0 */
+ 0x02, 0xbd, 0x0a, 0xf5, 0x41, 0x00, 0x00, /* mute */
+ 0x02, 0xbd, 0x1c, 0xe3, 0x2a, 0x00, 0x00, /* esc */
+ 0x02, 0xbd, 0x1f, 0xe0, 0x43, 0x00, 0x00, /* volume up */
+ 0x02, 0xbd, 0x12, 0xed, 0x52, 0x00, 0x00, /* up arrow */
+ 0x02, 0xbd, 0x11, 0xee, 0x50, 0x00, 0x00, /* left arrow */
+ 0x02, 0xbd, 0x15, 0xea, 0x28, 0x00, 0x00, /* ok */
+ 0x02, 0xbd, 0x10, 0xef, 0x4f, 0x00, 0x00, /* right arrow */
+ 0x02, 0xbd, 0x13, 0xec, 0x51, 0x00, 0x00, /* down arrow */
+ 0x02, 0xbd, 0x0e, 0xf1, 0x42, 0x00, 0x00, /* volume down */
+ 0x02, 0xbd, 0x19, 0xe6, 0x15, 0x01, 0x00, /* record */
+ 0x02, 0xbd, 0x1e, 0xe1, 0x13, 0x03, 0x00, /* play */
+ 0x02, 0xbd, 0x16, 0xe9, 0x16, 0x01, 0x00, /* stop */
+ 0x02, 0xbd, 0x0b, 0xf4, 0x28, 0x04, 0x00, /* yellow, min / max */
+ 0x02, 0xbd, 0x0f, 0xf0, 0x3b, 0x00, 0x00, /* time shift */
+ 0x02, 0xbd, 0x18, 0xe7, 0x2e, 0x00, 0x00, /* channel up */
+ 0x02, 0xbd, 0x1a, 0xe5, 0x2d, 0x00, 0x00, /* channel down */
+ 0x02, 0xbd, 0x17, 0xe8, 0x3e, 0x00, 0x00, /* snapshot */
+ 0x02, 0xbd, 0x40, 0xbf, 0x13, 0x01, 0x00, /* pause */
+ 0x02, 0xbd, 0x41, 0xbe, 0x09, 0x03, 0x00, /* FF >> */
+ 0x02, 0xbd, 0x42, 0xbd, 0x07, 0x03, 0x00, /* FR << */
+ 0x02, 0xbd, 0x43, 0xbc, 0x00, 0x00, 0x00, /* 'select' (?) */
+ 0x02, 0xbd, 0x44, 0xbb, 0x16, 0x03, 0x00, /* shuffle */
+ 0x02, 0xbd, 0x45, 0xba, 0x45, 0x03, 0x00, /* power */
+};
+
+/* KWorld PlusTV Dual DVB-T Stick (DVB-T 399U) */
+static u8 af9015_ir_table_kworld[] = {
+ 0x86, 0x6b, 0x0c, 0xf3, 0x2e, 0x07, 0x00,
+ 0x86, 0x6b, 0x16, 0xe9, 0x2d, 0x07, 0x00,
+ 0x86, 0x6b, 0x1d, 0xe2, 0x37, 0x07, 0x00,
+ 0x86, 0x6b, 0x00, 0xff, 0x1e, 0x07, 0x00,
+ 0x86, 0x6b, 0x01, 0xfe, 0x1f, 0x07, 0x00,
+ 0x86, 0x6b, 0x02, 0xfd, 0x20, 0x07, 0x00,
+ 0x86, 0x6b, 0x03, 0xfc, 0x21, 0x07, 0x00,
+ 0x86, 0x6b, 0x04, 0xfb, 0x22, 0x07, 0x00,
+ 0x86, 0x6b, 0x05, 0xfa, 0x23, 0x07, 0x00,
+ 0x86, 0x6b, 0x06, 0xf9, 0x24, 0x07, 0x00,
+ 0x86, 0x6b, 0x07, 0xf8, 0x25, 0x07, 0x00,
+ 0x86, 0x6b, 0x08, 0xf7, 0x26, 0x07, 0x00,
+ 0x86, 0x6b, 0x09, 0xf6, 0x4d, 0x07, 0x00,
+ 0x86, 0x6b, 0x0a, 0xf5, 0x4e, 0x07, 0x00,
+ 0x86, 0x6b, 0x14, 0xeb, 0x4f, 0x07, 0x00,
+ 0x86, 0x6b, 0x1e, 0xe1, 0x50, 0x07, 0x00,
+ 0x86, 0x6b, 0x17, 0xe8, 0x52, 0x07, 0x00,
+ 0x86, 0x6b, 0x1f, 0xe0, 0x51, 0x07, 0x00,
+ 0x86, 0x6b, 0x0e, 0xf1, 0x0b, 0x07, 0x00,
+ 0x86, 0x6b, 0x20, 0xdf, 0x0c, 0x07, 0x00,
+ 0x86, 0x6b, 0x42, 0xbd, 0x0d, 0x07, 0x00,
+ 0x86, 0x6b, 0x0b, 0xf4, 0x0e, 0x07, 0x00,
+ 0x86, 0x6b, 0x43, 0xbc, 0x0f, 0x07, 0x00,
+ 0x86, 0x6b, 0x10, 0xef, 0x10, 0x07, 0x00,
+ 0x86, 0x6b, 0x21, 0xde, 0x11, 0x07, 0x00,
+ 0x86, 0x6b, 0x13, 0xec, 0x12, 0x07, 0x00,
+ 0x86, 0x6b, 0x11, 0xee, 0x13, 0x07, 0x00,
+ 0x86, 0x6b, 0x12, 0xed, 0x14, 0x07, 0x00,
+ 0x86, 0x6b, 0x19, 0xe6, 0x15, 0x07, 0x00,
+ 0x86, 0x6b, 0x1a, 0xe5, 0x16, 0x07, 0x00,
+ 0x86, 0x6b, 0x1b, 0xe4, 0x17, 0x07, 0x00,
+ 0x86, 0x6b, 0x4b, 0xb4, 0x18, 0x07, 0x00,
+ 0x86, 0x6b, 0x40, 0xbf, 0x19, 0x07, 0x00,
+ 0x86, 0x6b, 0x44, 0xbb, 0x1a, 0x07, 0x00,
+ 0x86, 0x6b, 0x41, 0xbe, 0x1b, 0x07, 0x00,
+ 0x86, 0x6b, 0x22, 0xdd, 0x1c, 0x07, 0x00,
+ 0x86, 0x6b, 0x15, 0xea, 0x1d, 0x07, 0x00,
+ 0x86, 0x6b, 0x0f, 0xf0, 0x3f, 0x07, 0x00,
+ 0x86, 0x6b, 0x1c, 0xe3, 0x40, 0x07, 0x00,
+ 0x86, 0x6b, 0x4a, 0xb5, 0x41, 0x07, 0x00,
+ 0x86, 0x6b, 0x48, 0xb7, 0x42, 0x07, 0x00,
+ 0x86, 0x6b, 0x49, 0xb6, 0x43, 0x07, 0x00,
+ 0x86, 0x6b, 0x18, 0xe7, 0x44, 0x07, 0x00,
+ 0x86, 0x6b, 0x23, 0xdc, 0x45, 0x07, 0x00,
+};
+
+#endif
diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c
index 2f408d2..c786359 100644
--- a/drivers/media/dvb/dvb-usb/anysee.c
+++ b/drivers/media/dvb/dvb-usb/anysee.c
@@ -41,6 +41,9 @@
static int dvb_usb_anysee_debug;
module_param_named(debug, dvb_usb_anysee_debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
+int dvb_usb_anysee_delsys;
+module_param_named(delsys, dvb_usb_anysee_delsys, int, 0644);
+MODULE_PARM_DESC(delsys, "select delivery mode (0=DVB-C, 1=DVB-T)");
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
static struct mutex anysee_usb_mutex;
@@ -178,14 +181,14 @@
inc = 1;
}
if (ret)
- return ret;
+ break;
i += inc;
}
mutex_unlock(&d->i2c_mutex);
- return i;
+ return ret ? ret : i;
}
static u32 anysee_i2c_func(struct i2c_adapter *adapter)
@@ -272,9 +275,11 @@
model demod hw firmware
1. E30 MT352 02 0.2.1
2. E30 ZL10353 02 0.2.1
- 3. E30 Plus ZL10353 06 0.1.0
- 4. E30C Plus TDA10023 0a 0.1.0 rev 0.2
- 4. E30C Plus TDA10023 0f 0.1.2 rev 0.4
+ 3. E30 Combo ZL10353 0f 0.1.2 DVB-T/C combo
+ 4. E30 Plus ZL10353 06 0.1.0
+ 5. E30C Plus TDA10023 0a 0.1.0 rev 0.2
+ E30C Plus TDA10023 0f 0.1.2 rev 0.4
+ E30 Combo TDA10023 0f 0.1.2 DVB-T/C combo
*/
/* Zarlink MT352 DVB-T demod inside of Samsung DNOS404ZH102A NIM */
@@ -293,6 +298,21 @@
return 0;
}
+ /* for E30 Combo Plus DVB-T demodulator */
+ if (dvb_usb_anysee_delsys) {
+ ret = anysee_write_reg(adap->dev, 0xb0, 0x01);
+ if (ret)
+ return ret;
+
+ /* Zarlink ZL10353 DVB-T demod */
+ adap->fe = dvb_attach(zl10353_attach, &anysee_zl10353_config,
+ &adap->dev->i2c_adap);
+ if (adap->fe != NULL) {
+ state->tuner = DVB_PLL_SAMSUNG_DTOS403IH102A;
+ return 0;
+ }
+ }
+
/* connect demod on IO port D for TDA10023 & ZL10353 */
ret = anysee_write_reg(adap->dev, 0xb0, 0x25);
if (ret)
diff --git a/drivers/media/dvb/dvb-usb/cinergyT2-core.c b/drivers/media/dvb/dvb-usb/cinergyT2-core.c
new file mode 100644
index 0000000..3ac9f74
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/cinergyT2-core.c
@@ -0,0 +1,268 @@
+/*
+ * TerraTec Cinergy T2/qanu USB2 DVB-T adapter.
+ *
+ * Copyright (C) 2007 Tomi Orava (tomimo@ncircle.nullnet.fi)
+ *
+ * Based on the dvb-usb-framework code and the
+ * original Terratec Cinergy T2 driver by:
+ *
+ * Copyright (C) 2004 Daniel Mack <daniel@qanu.de> and
+ * Holger Waechtler <holger@qanu.de>
+ *
+ * Protocol Spec published on http://qanu.de/specs/terratec_cinergyT2.pdf
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "cinergyT2.h"
+
+
+/* debug */
+int dvb_usb_cinergyt2_debug;
+int disable_remote;
+
+module_param_named(debug, dvb_usb_cinergyt2_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info, xfer=2, rc=4 "
+ "(or-able)).");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+struct cinergyt2_state {
+ u8 rc_counter;
+};
+
+/* We are missing a release hook with usb_device data */
+struct dvb_usb_device *cinergyt2_usb_device;
+
+static struct dvb_usb_device_properties cinergyt2_properties;
+
+static int cinergyt2_streaming_ctrl(struct dvb_usb_adapter *adap, int enable)
+{
+ char buf[] = { CINERGYT2_EP1_CONTROL_STREAM_TRANSFER, enable ? 1 : 0 };
+ char result[64];
+ return dvb_usb_generic_rw(adap->dev, buf, sizeof(buf), result,
+ sizeof(result), 0);
+}
+
+static int cinergyt2_power_ctrl(struct dvb_usb_device *d, int enable)
+{
+ char buf[] = { CINERGYT2_EP1_SLEEP_MODE, enable ? 0 : 1 };
+ char state[3];
+ return dvb_usb_generic_rw(d, buf, sizeof(buf), state, sizeof(state), 0);
+}
+
+static int cinergyt2_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ char query[] = { CINERGYT2_EP1_GET_FIRMWARE_VERSION };
+ char state[3];
+ int ret;
+
+ adap->fe = cinergyt2_fe_attach(adap->dev);
+
+ ret = dvb_usb_generic_rw(adap->dev, query, sizeof(query), state,
+ sizeof(state), 0);
+ if (ret < 0) {
+ deb_rc("cinergyt2_power_ctrl() Failed to retrieve sleep "
+ "state info\n");
+ }
+
+ /* Copy this pointer as we are gonna need it in the release phase */
+ cinergyt2_usb_device = adap->dev;
+
+ return 0;
+}
+
+static struct dvb_usb_rc_key cinergyt2_rc_keys[] = {
+ { 0x04, 0x01, KEY_POWER },
+ { 0x04, 0x02, KEY_1 },
+ { 0x04, 0x03, KEY_2 },
+ { 0x04, 0x04, KEY_3 },
+ { 0x04, 0x05, KEY_4 },
+ { 0x04, 0x06, KEY_5 },
+ { 0x04, 0x07, KEY_6 },
+ { 0x04, 0x08, KEY_7 },
+ { 0x04, 0x09, KEY_8 },
+ { 0x04, 0x0a, KEY_9 },
+ { 0x04, 0x0c, KEY_0 },
+ { 0x04, 0x0b, KEY_VIDEO },
+ { 0x04, 0x0d, KEY_REFRESH },
+ { 0x04, 0x0e, KEY_SELECT },
+ { 0x04, 0x0f, KEY_EPG },
+ { 0x04, 0x10, KEY_UP },
+ { 0x04, 0x14, KEY_DOWN },
+ { 0x04, 0x11, KEY_LEFT },
+ { 0x04, 0x13, KEY_RIGHT },
+ { 0x04, 0x12, KEY_OK },
+ { 0x04, 0x15, KEY_TEXT },
+ { 0x04, 0x16, KEY_INFO },
+ { 0x04, 0x17, KEY_RED },
+ { 0x04, 0x18, KEY_GREEN },
+ { 0x04, 0x19, KEY_YELLOW },
+ { 0x04, 0x1a, KEY_BLUE },
+ { 0x04, 0x1c, KEY_VOLUMEUP },
+ { 0x04, 0x1e, KEY_VOLUMEDOWN },
+ { 0x04, 0x1d, KEY_MUTE },
+ { 0x04, 0x1b, KEY_CHANNELUP },
+ { 0x04, 0x1f, KEY_CHANNELDOWN },
+ { 0x04, 0x40, KEY_PAUSE },
+ { 0x04, 0x4c, KEY_PLAY },
+ { 0x04, 0x58, KEY_RECORD },
+ { 0x04, 0x54, KEY_PREVIOUS },
+ { 0x04, 0x48, KEY_STOP },
+ { 0x04, 0x5c, KEY_NEXT }
+};
+
+/* Number of keypresses to ignore before detect repeating */
+#define RC_REPEAT_DELAY 3
+
+static int repeatable_keys[] = {
+ KEY_UP,
+ KEY_DOWN,
+ KEY_LEFT,
+ KEY_RIGHT,
+ KEY_VOLUMEUP,
+ KEY_VOLUMEDOWN,
+ KEY_CHANNELUP,
+ KEY_CHANNELDOWN
+};
+
+static int cinergyt2_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
+{
+ struct cinergyt2_state *st = d->priv;
+ u8 key[5] = {0, 0, 0, 0, 0}, cmd = CINERGYT2_EP1_GET_RC_EVENTS;
+ int i;
+
+ *state = REMOTE_NO_KEY_PRESSED;
+
+ dvb_usb_generic_rw(d, &cmd, 1, key, sizeof(key), 0);
+ if (key[4] == 0xff) {
+ /* key repeat */
+ st->rc_counter++;
+ if (st->rc_counter > RC_REPEAT_DELAY) {
+ for (i = 0; i < ARRAY_SIZE(repeatable_keys); i++) {
+ if (d->last_event == repeatable_keys[i]) {
+ *state = REMOTE_KEY_REPEAT;
+ *event = d->last_event;
+ deb_rc("repeat key, event %x\n",
+ *event);
+ return 0;
+ }
+ }
+ deb_rc("repeated key (non repeatable)\n");
+ }
+ return 0;
+ }
+
+ /* hack to pass checksum on the custom field */
+ key[2] = ~key[1];
+ dvb_usb_nec_rc_key_to_event(d, key, event, state);
+ if (key[0] != 0) {
+ if (*event != d->last_event)
+ st->rc_counter = 0;
+
+ deb_rc("key: %x %x %x %x %x\n",
+ key[0], key[1], key[2], key[3], key[4]);
+ }
+ return 0;
+}
+
+static int cinergyt2_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return dvb_usb_device_init(intf, &cinergyt2_properties,
+ THIS_MODULE, NULL, adapter_nr);
+}
+
+
+static struct usb_device_id cinergyt2_usb_table[] = {
+ { USB_DEVICE(USB_VID_TERRATEC, 0x0038) },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(usb, cinergyt2_usb_table);
+
+static struct dvb_usb_device_properties cinergyt2_properties = {
+ .size_of_priv = sizeof(struct cinergyt2_state),
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .streaming_ctrl = cinergyt2_streaming_ctrl,
+ .frontend_attach = cinergyt2_frontend_attach,
+
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_BULK,
+ .count = 5,
+ .endpoint = 0x02,
+ .u = {
+ .bulk = {
+ .buffersize = 512,
+ }
+ }
+ },
+ }
+ },
+
+ .power_ctrl = cinergyt2_power_ctrl,
+
+ .rc_interval = 50,
+ .rc_key_map = cinergyt2_rc_keys,
+ .rc_key_map_size = ARRAY_SIZE(cinergyt2_rc_keys),
+ .rc_query = cinergyt2_rc_query,
+
+ .generic_bulk_ctrl_endpoint = 1,
+
+ .num_device_descs = 1,
+ .devices = {
+ { .name = "TerraTec/qanu USB2.0 Highspeed DVB-T Receiver",
+ .cold_ids = {NULL},
+ .warm_ids = { &cinergyt2_usb_table[0], NULL },
+ },
+ { NULL },
+ }
+};
+
+
+static struct usb_driver cinergyt2_driver = {
+ .name = "cinergyT2",
+ .probe = cinergyt2_usb_probe,
+ .disconnect = dvb_usb_device_exit,
+ .id_table = cinergyt2_usb_table
+};
+
+static int __init cinergyt2_usb_init(void)
+{
+ int err;
+
+ err = usb_register(&cinergyt2_driver);
+ if (err) {
+ err("usb_register() failed! (err %i)\n", err);
+ return err;
+ }
+ return 0;
+}
+
+static void __exit cinergyt2_usb_exit(void)
+{
+ usb_deregister(&cinergyt2_driver);
+}
+
+module_init(cinergyt2_usb_init);
+module_exit(cinergyt2_usb_exit);
+
+MODULE_DESCRIPTION("Terratec Cinergy T2 DVB-T driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Tomi Orava");
diff --git a/drivers/media/dvb/dvb-usb/cinergyT2-fe.c b/drivers/media/dvb/dvb-usb/cinergyT2-fe.c
new file mode 100644
index 0000000..649f25c
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/cinergyT2-fe.c
@@ -0,0 +1,351 @@
+/*
+ * TerraTec Cinergy T2/qanu USB2 DVB-T adapter.
+ *
+ * Copyright (C) 2007 Tomi Orava (tomimo@ncircle.nullnet.fi)
+ *
+ * Based on the dvb-usb-framework code and the
+ * original Terratec Cinergy T2 driver by:
+ *
+ * Copyright (C) 2004 Daniel Mack <daniel@qanu.de> and
+ * Holger Waechtler <holger@qanu.de>
+ *
+ * Protocol Spec published on http://qanu.de/specs/terratec_cinergyT2.pdf
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "cinergyT2.h"
+
+
+/**
+ * convert linux-dvb frontend parameter set into TPS.
+ * See ETSI ETS-300744, section 4.6.2, table 9 for details.
+ *
+ * This function is probably reusable and may better get placed in a support
+ * library.
+ *
+ * We replace errornous fields by default TPS fields (the ones with value 0).
+ */
+
+static uint16_t compute_tps(struct dvb_frontend_parameters *p)
+{
+ struct dvb_ofdm_parameters *op = &p->u.ofdm;
+ uint16_t tps = 0;
+
+ switch (op->code_rate_HP) {
+ case FEC_2_3:
+ tps |= (1 << 7);
+ break;
+ case FEC_3_4:
+ tps |= (2 << 7);
+ break;
+ case FEC_5_6:
+ tps |= (3 << 7);
+ break;
+ case FEC_7_8:
+ tps |= (4 << 7);
+ break;
+ case FEC_1_2:
+ case FEC_AUTO:
+ default:
+ /* tps |= (0 << 7) */;
+ }
+
+ switch (op->code_rate_LP) {
+ case FEC_2_3:
+ tps |= (1 << 4);
+ break;
+ case FEC_3_4:
+ tps |= (2 << 4);
+ break;
+ case FEC_5_6:
+ tps |= (3 << 4);
+ break;
+ case FEC_7_8:
+ tps |= (4 << 4);
+ break;
+ case FEC_1_2:
+ case FEC_AUTO:
+ default:
+ /* tps |= (0 << 4) */;
+ }
+
+ switch (op->constellation) {
+ case QAM_16:
+ tps |= (1 << 13);
+ break;
+ case QAM_64:
+ tps |= (2 << 13);
+ break;
+ case QPSK:
+ default:
+ /* tps |= (0 << 13) */;
+ }
+
+ switch (op->transmission_mode) {
+ case TRANSMISSION_MODE_8K:
+ tps |= (1 << 0);
+ break;
+ case TRANSMISSION_MODE_2K:
+ default:
+ /* tps |= (0 << 0) */;
+ }
+
+ switch (op->guard_interval) {
+ case GUARD_INTERVAL_1_16:
+ tps |= (1 << 2);
+ break;
+ case GUARD_INTERVAL_1_8:
+ tps |= (2 << 2);
+ break;
+ case GUARD_INTERVAL_1_4:
+ tps |= (3 << 2);
+ break;
+ case GUARD_INTERVAL_1_32:
+ default:
+ /* tps |= (0 << 2) */;
+ }
+
+ switch (op->hierarchy_information) {
+ case HIERARCHY_1:
+ tps |= (1 << 10);
+ break;
+ case HIERARCHY_2:
+ tps |= (2 << 10);
+ break;
+ case HIERARCHY_4:
+ tps |= (3 << 10);
+ break;
+ case HIERARCHY_NONE:
+ default:
+ /* tps |= (0 << 10) */;
+ }
+
+ return tps;
+}
+
+struct cinergyt2_fe_state {
+ struct dvb_frontend fe;
+ struct dvb_usb_device *d;
+};
+
+static int cinergyt2_fe_read_status(struct dvb_frontend *fe,
+ fe_status_t *status)
+{
+ struct cinergyt2_fe_state *state = fe->demodulator_priv;
+ struct dvbt_get_status_msg result;
+ u8 cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS };
+ int ret;
+
+ ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (u8 *)&result,
+ sizeof(result), 0);
+ if (ret < 0)
+ return ret;
+
+ *status = 0;
+
+ if (0xffff - le16_to_cpu(result.gain) > 30)
+ *status |= FE_HAS_SIGNAL;
+ if (result.lock_bits & (1 << 6))
+ *status |= FE_HAS_LOCK;
+ if (result.lock_bits & (1 << 5))
+ *status |= FE_HAS_SYNC;
+ if (result.lock_bits & (1 << 4))
+ *status |= FE_HAS_CARRIER;
+ if (result.lock_bits & (1 << 1))
+ *status |= FE_HAS_VITERBI;
+
+ if ((*status & (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) !=
+ (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC))
+ *status &= ~FE_HAS_LOCK;
+
+ return 0;
+}
+
+static int cinergyt2_fe_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct cinergyt2_fe_state *state = fe->demodulator_priv;
+ struct dvbt_get_status_msg status;
+ char cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS };
+ int ret;
+
+ ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (char *)&status,
+ sizeof(status), 0);
+ if (ret < 0)
+ return ret;
+
+ *ber = le32_to_cpu(status.viterbi_error_rate);
+ return 0;
+}
+
+static int cinergyt2_fe_read_unc_blocks(struct dvb_frontend *fe, u32 *unc)
+{
+ struct cinergyt2_fe_state *state = fe->demodulator_priv;
+ struct dvbt_get_status_msg status;
+ u8 cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS };
+ int ret;
+
+ ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (u8 *)&status,
+ sizeof(status), 0);
+ if (ret < 0) {
+ err("cinergyt2_fe_read_unc_blocks() Failed! (Error=%d)\n",
+ ret);
+ return ret;
+ }
+ *unc = le32_to_cpu(status.uncorrected_block_count);
+ return 0;
+}
+
+static int cinergyt2_fe_read_signal_strength(struct dvb_frontend *fe,
+ u16 *strength)
+{
+ struct cinergyt2_fe_state *state = fe->demodulator_priv;
+ struct dvbt_get_status_msg status;
+ char cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS };
+ int ret;
+
+ ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (char *)&status,
+ sizeof(status), 0);
+ if (ret < 0) {
+ err("cinergyt2_fe_read_signal_strength() Failed!"
+ " (Error=%d)\n", ret);
+ return ret;
+ }
+ *strength = (0xffff - le16_to_cpu(status.gain));
+ return 0;
+}
+
+static int cinergyt2_fe_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct cinergyt2_fe_state *state = fe->demodulator_priv;
+ struct dvbt_get_status_msg status;
+ char cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS };
+ int ret;
+
+ ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (char *)&status,
+ sizeof(status), 0);
+ if (ret < 0) {
+ err("cinergyt2_fe_read_snr() Failed! (Error=%d)\n", ret);
+ return ret;
+ }
+ *snr = (status.snr << 8) | status.snr;
+ return 0;
+}
+
+static int cinergyt2_fe_init(struct dvb_frontend *fe)
+{
+ return 0;
+}
+
+static int cinergyt2_fe_sleep(struct dvb_frontend *fe)
+{
+ deb_info("cinergyt2_fe_sleep() Called\n");
+ return 0;
+}
+
+static int cinergyt2_fe_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *tune)
+{
+ tune->min_delay_ms = 800;
+ return 0;
+}
+
+static int cinergyt2_fe_set_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *fep)
+{
+ struct cinergyt2_fe_state *state = fe->demodulator_priv;
+ struct dvbt_set_parameters_msg param;
+ char result[2];
+ int err;
+
+ param.cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS;
+ param.tps = cpu_to_le16(compute_tps(fep));
+ param.freq = cpu_to_le32(fep->frequency / 1000);
+ param.bandwidth = 8 - fep->u.ofdm.bandwidth - BANDWIDTH_8_MHZ;
+
+ err = dvb_usb_generic_rw(state->d,
+ (char *)¶m, sizeof(param),
+ result, sizeof(result), 0);
+ if (err < 0)
+ err("cinergyt2_fe_set_frontend() Failed! err=%d\n", err);
+
+ return (err < 0) ? err : 0;
+}
+
+static int cinergyt2_fe_get_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *fep)
+{
+ return 0;
+}
+
+static void cinergyt2_fe_release(struct dvb_frontend *fe)
+{
+ struct cinergyt2_fe_state *state = fe->demodulator_priv;
+ if (state != NULL)
+ kfree(state);
+}
+
+static struct dvb_frontend_ops cinergyt2_fe_ops;
+
+struct dvb_frontend *cinergyt2_fe_attach(struct dvb_usb_device *d)
+{
+ struct cinergyt2_fe_state *s = kzalloc(sizeof(
+ struct cinergyt2_fe_state), GFP_KERNEL);
+ if (s == NULL)
+ return NULL;
+
+ s->d = d;
+ memcpy(&s->fe.ops, &cinergyt2_fe_ops, sizeof(struct dvb_frontend_ops));
+ s->fe.demodulator_priv = s;
+ return &s->fe;
+}
+
+
+static struct dvb_frontend_ops cinergyt2_fe_ops = {
+ .info = {
+ .name = DRIVER_NAME,
+ .type = FE_OFDM,
+ .frequency_min = 174000000,
+ .frequency_max = 862000000,
+ .frequency_stepsize = 166667,
+ .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2
+ | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4
+ | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8
+ | FE_CAN_FEC_AUTO | FE_CAN_QPSK
+ | FE_CAN_QAM_16 | FE_CAN_QAM_64
+ | FE_CAN_QAM_AUTO
+ | FE_CAN_TRANSMISSION_MODE_AUTO
+ | FE_CAN_GUARD_INTERVAL_AUTO
+ | FE_CAN_HIERARCHY_AUTO
+ | FE_CAN_RECOVER
+ | FE_CAN_MUTE_TS
+ },
+
+ .release = cinergyt2_fe_release,
+
+ .init = cinergyt2_fe_init,
+ .sleep = cinergyt2_fe_sleep,
+
+ .set_frontend = cinergyt2_fe_set_frontend,
+ .get_frontend = cinergyt2_fe_get_frontend,
+ .get_tune_settings = cinergyt2_fe_get_tune_settings,
+
+ .read_status = cinergyt2_fe_read_status,
+ .read_ber = cinergyt2_fe_read_ber,
+ .read_signal_strength = cinergyt2_fe_read_signal_strength,
+ .read_snr = cinergyt2_fe_read_snr,
+ .read_ucblocks = cinergyt2_fe_read_unc_blocks,
+};
diff --git a/drivers/media/dvb/dvb-usb/cinergyT2.h b/drivers/media/dvb/dvb-usb/cinergyT2.h
new file mode 100644
index 0000000..11d79eb
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/cinergyT2.h
@@ -0,0 +1,95 @@
+/*
+ * TerraTec Cinergy T2/qanu USB2 DVB-T adapter.
+ *
+ * Copyright (C) 2007 Tomi Orava (tomimo@ncircle.nullnet.fi)
+ *
+ * Based on the dvb-usb-framework code and the
+ * original Terratec Cinergy T2 driver by:
+ *
+ * Copyright (C) 2004 Daniel Mack <daniel@qanu.de> and
+ * Holger Waechtler <holger@qanu.de>
+ *
+ * Protocol Spec published on http://qanu.de/specs/terratec_cinergyT2.pdf
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef _DVB_USB_CINERGYT2_H_
+#define _DVB_USB_CINERGYT2_H_
+
+#include <linux/usb/input.h>
+
+#define DVB_USB_LOG_PREFIX "cinergyT2"
+#include "dvb-usb.h"
+
+#define DRIVER_NAME "TerraTec/qanu USB2.0 Highspeed DVB-T Receiver"
+
+extern int dvb_usb_cinergyt2_debug;
+
+#define deb_info(args...) dprintk(dvb_usb_cinergyt2_debug, 0x001, args)
+#define deb_xfer(args...) dprintk(dvb_usb_cinergyt2_debug, 0x002, args)
+#define deb_pll(args...) dprintk(dvb_usb_cinergyt2_debug, 0x004, args)
+#define deb_ts(args...) dprintk(dvb_usb_cinergyt2_debug, 0x008, args)
+#define deb_err(args...) dprintk(dvb_usb_cinergyt2_debug, 0x010, args)
+#define deb_rc(args...) dprintk(dvb_usb_cinergyt2_debug, 0x020, args)
+#define deb_fw(args...) dprintk(dvb_usb_cinergyt2_debug, 0x040, args)
+#define deb_mem(args...) dprintk(dvb_usb_cinergyt2_debug, 0x080, args)
+#define deb_uxfer(args...) dprintk(dvb_usb_cinergyt2_debug, 0x100, args)
+
+
+
+enum cinergyt2_ep1_cmd {
+ CINERGYT2_EP1_PID_TABLE_RESET = 0x01,
+ CINERGYT2_EP1_PID_SETUP = 0x02,
+ CINERGYT2_EP1_CONTROL_STREAM_TRANSFER = 0x03,
+ CINERGYT2_EP1_SET_TUNER_PARAMETERS = 0x04,
+ CINERGYT2_EP1_GET_TUNER_STATUS = 0x05,
+ CINERGYT2_EP1_START_SCAN = 0x06,
+ CINERGYT2_EP1_CONTINUE_SCAN = 0x07,
+ CINERGYT2_EP1_GET_RC_EVENTS = 0x08,
+ CINERGYT2_EP1_SLEEP_MODE = 0x09,
+ CINERGYT2_EP1_GET_FIRMWARE_VERSION = 0x0A
+};
+
+
+struct dvbt_get_status_msg {
+ uint32_t freq;
+ uint8_t bandwidth;
+ uint16_t tps;
+ uint8_t flags;
+ uint16_t gain;
+ uint8_t snr;
+ uint32_t viterbi_error_rate;
+ uint32_t rs_error_rate;
+ uint32_t uncorrected_block_count;
+ uint8_t lock_bits;
+ uint8_t prev_lock_bits;
+} __attribute__((packed));
+
+
+struct dvbt_set_parameters_msg {
+ uint8_t cmd;
+ uint32_t freq;
+ uint8_t bandwidth;
+ uint16_t tps;
+ uint8_t flags;
+} __attribute__((packed));
+
+
+extern struct dvb_frontend *cinergyt2_fe_attach(struct dvb_usb_device *d);
+
+#endif /* _DVB_USB_CINERGYT2_H_ */
+
diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c
index 5634002..406d7fb 100644
--- a/drivers/media/dvb/dvb-usb/cxusb.c
+++ b/drivers/media/dvb/dvb-usb/cxusb.c
@@ -36,6 +36,9 @@
#include "tuner-xc2028.h"
#include "tuner-simple.h"
#include "mxl5005s.h"
+#include "dib7000p.h"
+#include "dib0070.h"
+#include "lgs8gl5.h"
/* debug */
static int dvb_usb_cxusb_debug;
@@ -109,6 +112,25 @@
cxusb_bluebird_gpio_rw(d, 0x40, onoff ? 0 : 0x40);
}
+static int cxusb_d680_dmb_gpio_tuner(struct dvb_usb_device *d,
+ u8 addr, int onoff)
+{
+ u8 o[2] = {addr, onoff};
+ u8 i;
+ int rc;
+
+ rc = cxusb_ctrl_msg(d, CMD_GPIO_WRITE, o, 2, &i, 1);
+
+ if (rc < 0)
+ return rc;
+ if (i == 0x01)
+ return 0;
+ else {
+ deb_info("gpio_write failed.\n");
+ return -EIO;
+ }
+}
+
/* I2C */
static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
int num)
@@ -262,6 +284,20 @@
return rc;
}
+static int cxusb_d680_dmb_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+ int ret;
+ u8 b;
+ ret = cxusb_power_ctrl(d, onoff);
+ if (!onoff)
+ return ret;
+
+ msleep(128);
+ cxusb_ctrl_msg(d, CMD_DIGITAL, NULL, 0, &b, 1);
+ msleep(100);
+ return ret;
+}
+
static int cxusb_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
u8 buf[2] = { 0x03, 0x00 };
@@ -283,6 +319,67 @@
return 0;
}
+static void cxusb_d680_dmb_drain_message(struct dvb_usb_device *d)
+{
+ int ep = d->props.generic_bulk_ctrl_endpoint;
+ const int timeout = 100;
+ const int junk_len = 32;
+ u8 *junk;
+ int rd_count;
+
+ /* Discard remaining data in video pipe */
+ junk = kmalloc(junk_len, GFP_KERNEL);
+ if (!junk)
+ return;
+ while (1) {
+ if (usb_bulk_msg(d->udev,
+ usb_rcvbulkpipe(d->udev, ep),
+ junk, junk_len, &rd_count, timeout) < 0)
+ break;
+ if (!rd_count)
+ break;
+ }
+ kfree(junk);
+}
+
+static void cxusb_d680_dmb_drain_video(struct dvb_usb_device *d)
+{
+ struct usb_data_stream_properties *p = &d->props.adapter[0].stream;
+ const int timeout = 100;
+ const int junk_len = p->u.bulk.buffersize;
+ u8 *junk;
+ int rd_count;
+
+ /* Discard remaining data in video pipe */
+ junk = kmalloc(junk_len, GFP_KERNEL);
+ if (!junk)
+ return;
+ while (1) {
+ if (usb_bulk_msg(d->udev,
+ usb_rcvbulkpipe(d->udev, p->endpoint),
+ junk, junk_len, &rd_count, timeout) < 0)
+ break;
+ if (!rd_count)
+ break;
+ }
+ kfree(junk);
+}
+
+static int cxusb_d680_dmb_streaming_ctrl(
+ struct dvb_usb_adapter *adap, int onoff)
+{
+ if (onoff) {
+ u8 buf[2] = { 0x03, 0x00 };
+ cxusb_d680_dmb_drain_video(adap->dev);
+ return cxusb_ctrl_msg(adap->dev, CMD_STREAMING_ON,
+ buf, sizeof(buf), NULL, 0);
+ } else {
+ int ret = cxusb_ctrl_msg(adap->dev,
+ CMD_STREAMING_OFF, NULL, 0, NULL, 0);
+ return ret;
+ }
+}
+
static int cxusb_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
{
struct dvb_usb_rc_key *keymap = d->props.rc_key_map;
@@ -335,6 +432,32 @@
return 0;
}
+static int cxusb_d680_dmb_rc_query(struct dvb_usb_device *d, u32 *event,
+ int *state)
+{
+ struct dvb_usb_rc_key *keymap = d->props.rc_key_map;
+ u8 ircode[2];
+ int i;
+
+ *event = 0;
+ *state = REMOTE_NO_KEY_PRESSED;
+
+ if (cxusb_ctrl_msg(d, 0x10, NULL, 0, ircode, 2) < 0)
+ return 0;
+
+ for (i = 0; i < d->props.rc_key_map_size; i++) {
+ if (keymap[i].custom == ircode[0] &&
+ keymap[i].data == ircode[1]) {
+ *event = keymap[i].event;
+ *state = REMOTE_KEY_PRESSED;
+
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
static struct dvb_usb_rc_key dvico_mce_rc_keys[] = {
{ 0xfe, 0x02, KEY_TV },
{ 0xfe, 0x0e, KEY_MP3 },
@@ -422,6 +545,44 @@
{ 0xfc, 0x00, KEY_UNKNOWN }, /* HD */
};
+static struct dvb_usb_rc_key d680_dmb_rc_keys[] = {
+ { 0x00, 0x38, KEY_UNKNOWN }, /* TV/AV */
+ { 0x08, 0x0c, KEY_ZOOM },
+ { 0x08, 0x00, KEY_0 },
+ { 0x00, 0x01, KEY_1 },
+ { 0x08, 0x02, KEY_2 },
+ { 0x00, 0x03, KEY_3 },
+ { 0x08, 0x04, KEY_4 },
+ { 0x00, 0x05, KEY_5 },
+ { 0x08, 0x06, KEY_6 },
+ { 0x00, 0x07, KEY_7 },
+ { 0x08, 0x08, KEY_8 },
+ { 0x00, 0x09, KEY_9 },
+ { 0x00, 0x0a, KEY_MUTE },
+ { 0x08, 0x29, KEY_BACK },
+ { 0x00, 0x12, KEY_CHANNELUP },
+ { 0x08, 0x13, KEY_CHANNELDOWN },
+ { 0x00, 0x2b, KEY_VOLUMEUP },
+ { 0x08, 0x2c, KEY_VOLUMEDOWN },
+ { 0x00, 0x20, KEY_UP },
+ { 0x08, 0x21, KEY_DOWN },
+ { 0x00, 0x11, KEY_LEFT },
+ { 0x08, 0x10, KEY_RIGHT },
+ { 0x00, 0x0d, KEY_OK },
+ { 0x08, 0x1f, KEY_RECORD },
+ { 0x00, 0x17, KEY_PLAYPAUSE },
+ { 0x08, 0x16, KEY_PLAYPAUSE },
+ { 0x00, 0x0b, KEY_STOP },
+ { 0x08, 0x27, KEY_FASTFORWARD },
+ { 0x00, 0x26, KEY_REWIND },
+ { 0x08, 0x1e, KEY_UNKNOWN }, /* Time Shift */
+ { 0x00, 0x0e, KEY_UNKNOWN }, /* Snapshot */
+ { 0x08, 0x2d, KEY_UNKNOWN }, /* Mouse Cursor */
+ { 0x00, 0x0f, KEY_UNKNOWN }, /* Minimize/Maximize */
+ { 0x08, 0x14, KEY_UNKNOWN }, /* Shuffle */
+ { 0x00, 0x25, KEY_POWER },
+};
+
static int cxusb_dee1601_demod_init(struct dvb_frontend* fe)
{
static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x28 };
@@ -527,6 +688,24 @@
.AgcMasterByte = 0x00,
};
+/* FIXME: needs tweaking */
+static struct mxl5005s_config d680_dmb_tuner = {
+ .i2c_address = 0x63,
+ .if_freq = 36125000UL,
+ .xtal_freq = CRYSTAL_FREQ_16000000HZ,
+ .agc_mode = MXL_SINGLE_AGC,
+ .tracking_filter = MXL_TF_C,
+ .rssi_enable = MXL_RSSI_ENABLE,
+ .cap_select = MXL_CAP_SEL_ENABLE,
+ .div_out = MXL_DIV_OUT_4,
+ .clock_out = MXL_CLOCK_OUT_DISABLE,
+ .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
+ .top = MXL5005S_TOP_25P2,
+ .mod_mode = MXL_DIGITAL_MODE,
+ .if_mode = MXL_ZERO_IF,
+ .AgcMasterByte = 0x00,
+};
+
/* Callbacks for DVB USB */
static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap)
{
@@ -563,7 +742,8 @@
return 0;
}
-static int dvico_bluebird_xc2028_callback(void *ptr, int command, int arg)
+static int dvico_bluebird_xc2028_callback(void *ptr, int component,
+ int command, int arg)
{
struct dvb_usb_adapter *adap = ptr;
struct dvb_usb_device *d = adap->dev;
@@ -591,14 +771,16 @@
struct xc2028_config cfg = {
.i2c_adap = &adap->dev->i2c_adap,
.i2c_addr = 0x61,
- .callback = dvico_bluebird_xc2028_callback,
};
static struct xc2028_ctrl ctl = {
- .fname = "xc3028-v27.fw",
+ .fname = XC2028_DEFAULT_FIRMWARE,
.max_len = 64,
.demod = XC3028_FE_ZARLINK456,
};
+ /* FIXME: generalize & move to common area */
+ adap->fe->callback = dvico_bluebird_xc2028_callback;
+
fe = dvb_attach(xc2028_attach, adap->fe, &cfg);
if (fe == NULL || fe->ops.tuner_ops.set_config == NULL)
return -EIO;
@@ -615,6 +797,14 @@
return 0;
}
+static int cxusb_d680_dmb_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ struct dvb_frontend *fe;
+ fe = dvb_attach(mxl5005s_attach, adap->fe,
+ &adap->dev->i2c_adap, &d680_dmb_tuner);
+ return (fe == NULL) ? -EIO : 0;
+}
+
static int cxusb_cx22702_frontend_attach(struct dvb_usb_adapter *adap)
{
u8 b;
@@ -726,6 +916,159 @@
return 0;
}
+static struct dibx000_agc_config dib7070_agc_config = {
+ .band_caps = BAND_UHF | BAND_VHF | BAND_LBAND | BAND_SBAND,
+
+ /*
+ * P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5,
+ * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0,
+ * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0
+ */
+ .setup = (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) |
+ (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0),
+ .inv_gain = 600,
+ .time_stabiliz = 10,
+ .alpha_level = 0,
+ .thlock = 118,
+ .wbd_inv = 0,
+ .wbd_ref = 3530,
+ .wbd_sel = 1,
+ .wbd_alpha = 5,
+ .agc1_max = 65535,
+ .agc1_min = 0,
+ .agc2_max = 65535,
+ .agc2_min = 0,
+ .agc1_pt1 = 0,
+ .agc1_pt2 = 40,
+ .agc1_pt3 = 183,
+ .agc1_slope1 = 206,
+ .agc1_slope2 = 255,
+ .agc2_pt1 = 72,
+ .agc2_pt2 = 152,
+ .agc2_slope1 = 88,
+ .agc2_slope2 = 90,
+ .alpha_mant = 17,
+ .alpha_exp = 27,
+ .beta_mant = 23,
+ .beta_exp = 51,
+ .perform_agc_softsplit = 0,
+};
+
+static struct dibx000_bandwidth_config dib7070_bw_config_12_mhz = {
+ .internal = 60000,
+ .sampling = 15000,
+ .pll_prediv = 1,
+ .pll_ratio = 20,
+ .pll_range = 3,
+ .pll_reset = 1,
+ .pll_bypass = 0,
+ .enable_refdiv = 0,
+ .bypclk_div = 0,
+ .IO_CLK_en_core = 1,
+ .ADClkSrc = 1,
+ .modulo = 2,
+ /* refsel, sel, freq_15k */
+ .sad_cfg = (3 << 14) | (1 << 12) | (524 << 0),
+ .ifreq = (0 << 25) | 0,
+ .timf = 20452225,
+ .xtal_hz = 12000000,
+};
+
+static struct dib7000p_config cxusb_dualdig4_rev2_config = {
+ .output_mode = OUTMODE_MPEG2_PAR_GATED_CLK,
+ .output_mpeg2_in_188_bytes = 1,
+
+ .agc_config_count = 1,
+ .agc = &dib7070_agc_config,
+ .bw = &dib7070_bw_config_12_mhz,
+ .tuner_is_baseband = 1,
+ .spur_protect = 1,
+
+ .gpio_dir = 0xfcef,
+ .gpio_val = 0x0110,
+
+ .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS,
+
+ .hostbus_diversity = 1,
+};
+
+static int cxusb_dualdig4_rev2_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ if (usb_set_interface(adap->dev->udev, 0, 1) < 0)
+ err("set interface failed");
+
+ cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0);
+
+ cxusb_bluebird_gpio_pulse(adap->dev, 0x02, 1);
+
+ dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 18,
+ &cxusb_dualdig4_rev2_config);
+
+ adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80,
+ &cxusb_dualdig4_rev2_config);
+ if (adap->fe == NULL)
+ return -EIO;
+
+ return 0;
+}
+
+static int dib7070_tuner_reset(struct dvb_frontend *fe, int onoff)
+{
+ return dib7000p_set_gpio(fe, 8, 0, !onoff);
+}
+
+static int dib7070_tuner_sleep(struct dvb_frontend *fe, int onoff)
+{
+ return 0;
+}
+
+static struct dib0070_config dib7070p_dib0070_config = {
+ .i2c_address = DEFAULT_DIB0070_I2C_ADDRESS,
+ .reset = dib7070_tuner_reset,
+ .sleep = dib7070_tuner_sleep,
+ .clock_khz = 12000,
+};
+
+struct dib0700_adapter_state {
+ int (*set_param_save) (struct dvb_frontend *,
+ struct dvb_frontend_parameters *);
+};
+
+static int dib7070_set_param_override(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *fep)
+{
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+ struct dib0700_adapter_state *state = adap->priv;
+
+ u16 offset;
+ u8 band = BAND_OF_FREQUENCY(fep->frequency/1000);
+ switch (band) {
+ case BAND_VHF: offset = 950; break;
+ default:
+ case BAND_UHF: offset = 550; break;
+ }
+
+ dib7000p_set_wbd_ref(fe, offset + dib0070_wbd_offset(fe));
+
+ return state->set_param_save(fe, fep);
+}
+
+static int cxusb_dualdig4_rev2_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ struct dib0700_adapter_state *st = adap->priv;
+ struct i2c_adapter *tun_i2c =
+ dib7000p_get_i2c_master(adap->fe,
+ DIBX000_I2C_INTERFACE_TUNER, 1);
+
+ if (dvb_attach(dib0070_attach, adap->fe, tun_i2c,
+ &dib7070p_dib0070_config) == NULL)
+ return -ENODEV;
+
+ st->set_param_save = adap->fe->ops.tuner_ops.set_params;
+ adap->fe->ops.tuner_ops.set_params = dib7070_set_param_override;
+ return 0;
+}
+
static int cxusb_nano2_frontend_attach(struct dvb_usb_adapter *adap)
{
if (usb_set_interface(adap->dev->udev, 0, 1) < 0)
@@ -751,6 +1094,54 @@
return -EIO;
}
+static struct lgs8gl5_config lgs8gl5_cfg = {
+ .demod_address = 0x19,
+};
+
+static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ struct dvb_usb_device *d = adap->dev;
+ int n;
+
+ /* Select required USB configuration */
+ if (usb_set_interface(d->udev, 0, 0) < 0)
+ err("set interface failed");
+
+ /* Unblock all USB pipes */
+ usb_clear_halt(d->udev,
+ usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint));
+ usb_clear_halt(d->udev,
+ usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint));
+ usb_clear_halt(d->udev,
+ usb_rcvbulkpipe(d->udev, d->props.adapter[0].stream.endpoint));
+
+ /* Drain USB pipes to avoid hang after reboot */
+ for (n = 0; n < 5; n++) {
+ cxusb_d680_dmb_drain_message(d);
+ cxusb_d680_dmb_drain_video(d);
+ msleep(200);
+ }
+
+ /* Reset the tuner */
+ if (cxusb_d680_dmb_gpio_tuner(d, 0x07, 0) < 0) {
+ err("clear tuner gpio failed");
+ return -EIO;
+ }
+ msleep(100);
+ if (cxusb_d680_dmb_gpio_tuner(d, 0x07, 1) < 0) {
+ err("set tuner gpio failed");
+ return -EIO;
+ }
+ msleep(100);
+
+ /* Attach frontend */
+ adap->fe = dvb_attach(lgs8gl5_attach, &lgs8gl5_cfg, &d->i2c_adap);
+ if (adap->fe == NULL)
+ return -EIO;
+
+ return 0;
+}
+
/*
* DViCO has shipped two devices with the same USB ID, but only one of them
* needs a firmware download. Check the device class details to see if they
@@ -826,9 +1217,11 @@
static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties;
static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties;
static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_properties;
+static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_rev2_properties;
static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties;
static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_properties;
static struct dvb_usb_device_properties cxusb_aver_a868r_properties;
+static struct dvb_usb_device_properties cxusb_d680_dmb_properties;
static int cxusb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
@@ -852,6 +1245,11 @@
THIS_MODULE, NULL, adapter_nr) ||
0 == dvb_usb_device_init(intf, &cxusb_aver_a868r_properties,
THIS_MODULE, NULL, adapter_nr) ||
+ 0 == dvb_usb_device_init(intf,
+ &cxusb_bluebird_dualdig4_rev2_properties,
+ THIS_MODULE, NULL, adapter_nr) ||
+ 0 == dvb_usb_device_init(intf, &cxusb_d680_dmb_properties,
+ THIS_MODULE, NULL, adapter_nr) ||
0)
return 0;
@@ -876,6 +1274,8 @@
{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2) },
{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM) },
{ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_A868R) },
+ { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_4_REV_2) },
+ { USB_DEVICE(USB_VID_CONEXANT, USB_PID_CONEXANT_D680_DMB) },
{} /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, cxusb_table);
@@ -1321,6 +1721,104 @@
}
};
+static
+struct dvb_usb_device_properties cxusb_bluebird_dualdig4_rev2_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = CYPRESS_FX2,
+
+ .size_of_priv = sizeof(struct cxusb_state),
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .streaming_ctrl = cxusb_streaming_ctrl,
+ .frontend_attach = cxusb_dualdig4_rev2_frontend_attach,
+ .tuner_attach = cxusb_dualdig4_rev2_tuner_attach,
+ .size_of_priv = sizeof(struct dib0700_adapter_state),
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_BULK,
+ .count = 7,
+ .endpoint = 0x02,
+ .u = {
+ .bulk = {
+ .buffersize = 4096,
+ }
+ }
+ },
+ },
+ },
+
+ .power_ctrl = cxusb_bluebird_power_ctrl,
+
+ .i2c_algo = &cxusb_i2c_algo,
+
+ .generic_bulk_ctrl_endpoint = 0x01,
+
+ .rc_interval = 100,
+ .rc_key_map = dvico_mce_rc_keys,
+ .rc_key_map_size = ARRAY_SIZE(dvico_mce_rc_keys),
+ .rc_query = cxusb_rc_query,
+
+ .num_device_descs = 1,
+ .devices = {
+ { "DViCO FusionHDTV DVB-T Dual Digital 4 (rev 2)",
+ { NULL },
+ { &cxusb_table[17], NULL },
+ },
+ }
+};
+
+static struct dvb_usb_device_properties cxusb_d680_dmb_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = CYPRESS_FX2,
+
+ .size_of_priv = sizeof(struct cxusb_state),
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .streaming_ctrl = cxusb_d680_dmb_streaming_ctrl,
+ .frontend_attach = cxusb_d680_dmb_frontend_attach,
+ .tuner_attach = cxusb_d680_dmb_tuner_attach,
+
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_BULK,
+ .count = 5,
+ .endpoint = 0x02,
+ .u = {
+ .bulk = {
+ .buffersize = 8192,
+ }
+ }
+ },
+ },
+ },
+
+ .power_ctrl = cxusb_d680_dmb_power_ctrl,
+
+ .i2c_algo = &cxusb_i2c_algo,
+
+ .generic_bulk_ctrl_endpoint = 0x01,
+
+ .rc_interval = 100,
+ .rc_key_map = d680_dmb_rc_keys,
+ .rc_key_map_size = ARRAY_SIZE(d680_dmb_rc_keys),
+ .rc_query = cxusb_d680_dmb_rc_query,
+
+ .num_device_descs = 1,
+ .devices = {
+ {
+ "Conexant DMB-TH Stick",
+ { NULL },
+ { &cxusb_table[18], NULL },
+ },
+ }
+};
+
static struct usb_driver cxusb_driver = {
.name = "dvb_usb_cxusb",
.probe = cxusb_probe,
diff --git a/drivers/media/dvb/dvb-usb/dib0700.h b/drivers/media/dvb/dvb-usb/dib0700.h
index 66d4dc6..7391939 100644
--- a/drivers/media/dvb/dvb-usb/dib0700.h
+++ b/drivers/media/dvb/dvb-usb/dib0700.h
@@ -31,6 +31,8 @@
// 2 Byte: MPEG2 mode: 4MSB(1 = Master Mode, 0 = Slave Mode) 4LSB(Channel 1 = bit0, Channel 2 = bit1)
// 2 Byte: Analog mode: 4MSB(0 = 625 lines, 1 = 525 lines) 4LSB( " " )
#define REQUEST_SET_RC 0x11
+#define REQUEST_NEW_I2C_READ 0x12
+#define REQUEST_NEW_I2C_WRITE 0x13
#define REQUEST_GET_VERSION 0x15
struct dib0700_state {
@@ -39,6 +41,8 @@
u8 rc_toggle;
u8 rc_counter;
u8 is_dib7000pc;
+ u8 fw_use_new_i2c_api;
+ u8 disable_streaming_master_mode;
};
extern int dib0700_set_gpio(struct dvb_usb_device *, enum dib07x0_gpios gpio, u8 gpio_dir, u8 gpio_val);
diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c
index 595a046..dd53cee 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_core.c
+++ b/drivers/media/dvb/dvb-usb/dib0700_core.c
@@ -82,9 +82,98 @@
}
/*
- * I2C master xfer function
+ * I2C master xfer function (supported in 1.20 firmware)
*/
-static int dib0700_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msg,int num)
+static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg,
+ int num)
+{
+ /* The new i2c firmware messages are more reliable and in particular
+ properly support i2c read calls not preceded by a write */
+
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ uint8_t bus_mode = 1; /* 0=eeprom bus, 1=frontend bus */
+ uint8_t gen_mode = 0; /* 0=master i2c, 1=gpio i2c */
+ uint8_t en_start = 0;
+ uint8_t en_stop = 0;
+ uint8_t buf[255]; /* TBV: malloc ? */
+ int result, i;
+
+ /* Ensure nobody else hits the i2c bus while we're sending our
+ sequence of messages, (such as the remote control thread) */
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ for (i = 0; i < num; i++) {
+ if (i == 0) {
+ /* First message in the transaction */
+ en_start = 1;
+ } else if (!(msg[i].flags & I2C_M_NOSTART)) {
+ /* Device supports repeated-start */
+ en_start = 1;
+ } else {
+ /* Not the first packet and device doesn't support
+ repeated start */
+ en_start = 0;
+ }
+ if (i == (num - 1)) {
+ /* Last message in the transaction */
+ en_stop = 1;
+ }
+
+ if (msg[i].flags & I2C_M_RD) {
+ /* Read request */
+ u16 index, value;
+ uint8_t i2c_dest;
+
+ i2c_dest = (msg[i].addr << 1);
+ value = ((en_start << 7) | (en_stop << 6) |
+ (msg[i].len & 0x3F)) << 8 | i2c_dest;
+ /* I2C ctrl + FE bus; */
+ index = ((gen_mode<<6)&0xC0) | ((bus_mode<<4)&0x30);
+
+ result = usb_control_msg(d->udev,
+ usb_rcvctrlpipe(d->udev, 0),
+ REQUEST_NEW_I2C_READ,
+ USB_TYPE_VENDOR | USB_DIR_IN,
+ value, index, msg[i].buf,
+ msg[i].len,
+ USB_CTRL_GET_TIMEOUT);
+ if (result < 0) {
+ err("i2c read error (status = %d)\n", result);
+ break;
+ }
+ } else {
+ /* Write request */
+ buf[0] = REQUEST_NEW_I2C_WRITE;
+ buf[1] = (msg[i].addr << 1);
+ buf[2] = (en_start << 7) | (en_stop << 6) |
+ (msg[i].len & 0x3F);
+ /* I2C ctrl + FE bus; */
+ buf[3] = ((gen_mode<<6)&0xC0) | ((bus_mode<<4)&0x30);
+ /* The Actual i2c payload */
+ memcpy(&buf[4], msg[i].buf, msg[i].len);
+
+ result = usb_control_msg(d->udev,
+ usb_sndctrlpipe(d->udev, 0),
+ REQUEST_NEW_I2C_WRITE,
+ USB_TYPE_VENDOR | USB_DIR_OUT,
+ 0, 0, buf, msg[i].len + 4,
+ USB_CTRL_GET_TIMEOUT);
+ if (result < 0) {
+ err("i2c write error (status = %d)\n", result);
+ break;
+ }
+ }
+ }
+ mutex_unlock(&d->i2c_mutex);
+ return i;
+}
+
+/*
+ * I2C master xfer function (pre-1.20 firmware)
+ */
+static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap,
+ struct i2c_msg *msg, int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
int i,len;
@@ -124,6 +213,21 @@
return i;
}
+static int dib0700_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
+ int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ struct dib0700_state *st = d->priv;
+
+ if (st->fw_use_new_i2c_api == 1) {
+ /* User running at least fw 1.20 */
+ return dib0700_i2c_xfer_new(adap, msg, num);
+ } else {
+ /* Use legacy calls */
+ return dib0700_i2c_xfer_legacy(adap, msg, num);
+ }
+}
+
static u32 dib0700_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
@@ -246,7 +350,12 @@
b[0] = REQUEST_ENABLE_VIDEO;
b[1] = (onoff << 4) | 0x00; /* this bit gives a kind of command, rather than enabling something or not */
- b[2] = (0x01 << 4); /* Master mode */
+
+ if (st->disable_streaming_master_mode == 1)
+ b[2] = 0x00;
+ else
+ b[2] = (0x01 << 4); /* Master mode */
+
b[3] = 0x00;
deb_info("modifying (%d) streaming state for %d\n", onoff, adap->id);
diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c
index 6c0e5c5..0cfccc24 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_devices.c
+++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c
@@ -14,6 +14,8 @@
#include "mt2060.h"
#include "mt2266.h"
#include "tuner-xc2028.h"
+#include "xc5000.h"
+#include "s5h1411.h"
#include "dib0070.h"
static int force_lna_activation;
@@ -366,7 +368,8 @@
.gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS,
};
-static int stk7700ph_xc3028_callback(void *ptr, int command, int arg)
+static int stk7700ph_xc3028_callback(void *ptr, int component,
+ int command, int arg)
{
struct dvb_usb_adapter *adap = ptr;
@@ -394,7 +397,6 @@
static struct xc2028_config stk7700ph_xc3028_config = {
.i2c_addr = 0x61,
- .callback = stk7700ph_xc3028_callback,
.ctrl = &stk7700ph_xc3028_ctrl,
};
@@ -435,7 +437,9 @@
DIBX000_I2C_INTERFACE_TUNER, 1);
stk7700ph_xc3028_config.i2c_adap = tun_i2c;
- stk7700ph_xc3028_config.video_dev = adap;
+
+ /* FIXME: generalize & move to common area */
+ adap->fe->callback = stk7700ph_xc3028_callback;
return dvb_attach(xc2028_attach, adap->fe, &stk7700ph_xc3028_config)
== NULL ? -ENODEV : 0;
@@ -677,6 +681,43 @@
{ 0x01, 0x7d, KEY_VOLUMEDOWN },
{ 0x02, 0x42, KEY_CHANNELUP },
{ 0x00, 0x7d, KEY_CHANNELDOWN },
+
+ /* Key codes for Nova-TD "credit card" remote control. */
+ { 0x1d, 0x00, KEY_0 },
+ { 0x1d, 0x01, KEY_1 },
+ { 0x1d, 0x02, KEY_2 },
+ { 0x1d, 0x03, KEY_3 },
+ { 0x1d, 0x04, KEY_4 },
+ { 0x1d, 0x05, KEY_5 },
+ { 0x1d, 0x06, KEY_6 },
+ { 0x1d, 0x07, KEY_7 },
+ { 0x1d, 0x08, KEY_8 },
+ { 0x1d, 0x09, KEY_9 },
+ { 0x1d, 0x0a, KEY_TEXT },
+ { 0x1d, 0x0d, KEY_MENU },
+ { 0x1d, 0x0f, KEY_MUTE },
+ { 0x1d, 0x10, KEY_VOLUMEUP },
+ { 0x1d, 0x11, KEY_VOLUMEDOWN },
+ { 0x1d, 0x12, KEY_CHANNEL },
+ { 0x1d, 0x14, KEY_UP },
+ { 0x1d, 0x15, KEY_DOWN },
+ { 0x1d, 0x16, KEY_LEFT },
+ { 0x1d, 0x17, KEY_RIGHT },
+ { 0x1d, 0x1c, KEY_TV },
+ { 0x1d, 0x1e, KEY_NEXT },
+ { 0x1d, 0x1f, KEY_BACK },
+ { 0x1d, 0x20, KEY_CHANNELUP },
+ { 0x1d, 0x21, KEY_CHANNELDOWN },
+ { 0x1d, 0x24, KEY_LAST },
+ { 0x1d, 0x25, KEY_OK },
+ { 0x1d, 0x30, KEY_PAUSE },
+ { 0x1d, 0x32, KEY_REWIND },
+ { 0x1d, 0x34, KEY_FASTFORWARD },
+ { 0x1d, 0x35, KEY_PLAY },
+ { 0x1d, 0x36, KEY_STOP },
+ { 0x1d, 0x37, KEY_RECORD },
+ { 0x1d, 0x3b, KEY_GOTO },
+ { 0x1d, 0x3d, KEY_POWER },
};
/* STK7700P: Hauppauge Nova-T Stick, AVerMedia Volar */
@@ -1078,6 +1119,97 @@
return adap->fe == NULL ? -ENODEV : 0;
}
+/* S5H1411 */
+static struct s5h1411_config pinnacle_801e_config = {
+ .output_mode = S5H1411_PARALLEL_OUTPUT,
+ .gpio = S5H1411_GPIO_OFF,
+ .mpeg_timing = S5H1411_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK,
+ .qam_if = S5H1411_IF_44000,
+ .vsb_if = S5H1411_IF_44000,
+ .inversion = S5H1411_INVERSION_OFF,
+ .status_mode = S5H1411_DEMODLOCKING
+};
+
+/* Pinnacle PCTV HD Pro 801e GPIOs map:
+ GPIO0 - currently unknown
+ GPIO1 - xc5000 tuner reset
+ GPIO2 - CX25843 sleep
+ GPIO3 - currently unknown
+ GPIO4 - currently unknown
+ GPIO6 - currently unknown
+ GPIO7 - currently unknown
+ GPIO9 - currently unknown
+ GPIO10 - CX25843 reset
+ */
+static int s5h1411_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ struct dib0700_state *st = adap->dev->priv;
+
+ /* Make use of the new i2c functions from FW 1.20 */
+ st->fw_use_new_i2c_api = 1;
+
+ /* The s5h1411 requires the dib0700 to not be in master mode */
+ st->disable_streaming_master_mode = 1;
+
+ /* All msleep values taken from Windows USB trace */
+ dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 0);
+ dib0700_set_gpio(adap->dev, GPIO3, GPIO_OUT, 0);
+ dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1);
+ msleep(400);
+ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0);
+ msleep(60);
+ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1);
+ msleep(30);
+ dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1);
+ dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1);
+ dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1);
+ dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1);
+ dib0700_set_gpio(adap->dev, GPIO2, GPIO_OUT, 0);
+ msleep(30);
+
+ /* Put the CX25843 to sleep for now since we're in digital mode */
+ dib0700_set_gpio(adap->dev, GPIO2, GPIO_OUT, 1);
+
+ /* GPIOs are initialized, do the attach */
+ adap->fe = dvb_attach(s5h1411_attach, &pinnacle_801e_config,
+ &adap->dev->i2c_adap);
+ return adap->fe == NULL ? -ENODEV : 0;
+}
+
+static int dib0700_xc5000_tuner_callback(void *priv, int component,
+ int command, int arg)
+{
+ struct dvb_usb_adapter *adap = priv;
+
+ if (command == XC5000_TUNER_RESET) {
+ /* Reset the tuner */
+ dib0700_set_gpio(adap->dev, GPIO1, GPIO_OUT, 0);
+ msleep(330); /* from Windows USB trace */
+ dib0700_set_gpio(adap->dev, GPIO1, GPIO_OUT, 1);
+ msleep(330); /* from Windows USB trace */
+ } else {
+ err("xc5000: unknown tuner callback command: %d\n", command);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct xc5000_config s5h1411_xc5000_tunerconfig = {
+ .i2c_address = 0x64,
+ .if_khz = 5380,
+};
+
+static int xc5000_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ /* FIXME: generalize & move to common area */
+ adap->fe->callback = dib0700_xc5000_tuner_callback;
+
+ return dvb_attach(xc5000_attach, adap->fe, &adap->dev->i2c_adap,
+ &s5h1411_xc5000_tunerconfig)
+ == NULL ? -ENODEV : 0;
+}
+
/* DVB-USB and USB stuff follows */
struct usb_device_id dib0700_usb_id_table[] = {
/* 0 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7700P) },
@@ -1119,6 +1251,11 @@
{ USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_STK7700P_2) },
/* 35 */{ USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_TD_STICK_52009) },
{ USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_500_3) },
+ { USB_DEVICE(USB_VID_GIGABYTE, USB_PID_GIGABYTE_U8000) },
+ { USB_DEVICE(USB_VID_YUAN, USB_PID_YUAN_STK7700PH) },
+ { USB_DEVICE(USB_VID_ASUS, USB_PID_ASUS_U3000H) },
+/* 40 */{ USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV801E) },
+ { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV801E_SE) },
{ 0 } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table);
@@ -1126,7 +1263,7 @@
#define DIB0700_DEFAULT_DEVICE_PROPERTIES \
.caps = DVB_USB_IS_AN_I2C_ADAPTER, \
.usb_ctrl = DEVICE_SPECIFIC, \
- .firmware = "dvb-usb-dib0700-1.10.fw", \
+ .firmware = "dvb-usb-dib0700-1.20.fw", \
.download_firmware = dib0700_download_firmware, \
.no_reconnect = 1, \
.size_of_priv = sizeof(struct dib0700_state), \
@@ -1293,7 +1430,12 @@
{ &dib0700_usb_id_table[31], NULL },
{ NULL },
}
- }
+ },
+
+ .rc_interval = DEFAULT_RC_INTERVAL,
+ .rc_key_map = dib0700_rc_keys,
+ .rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys),
+ .rc_query = dib0700_rc_query
}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
.num_adapters = 1,
@@ -1408,7 +1550,7 @@
},
},
- .num_device_descs = 3,
+ .num_device_descs = 5,
.devices = {
{ "Terratec Cinergy HT USB XE",
{ &dib0700_usb_id_table[27], NULL },
@@ -1422,6 +1564,47 @@
{ &dib0700_usb_id_table[32], NULL },
{ NULL },
},
+ { "Gigabyte U8000-RH",
+ { &dib0700_usb_id_table[37], NULL },
+ { NULL },
+ },
+ { "YUAN High-Tech STK7700PH",
+ { &dib0700_usb_id_table[38], NULL },
+ { NULL },
+ },
+ { "Asus My Cinema-U3000Hybrid",
+ { &dib0700_usb_id_table[39], NULL },
+ { NULL },
+ },
+ },
+ .rc_interval = DEFAULT_RC_INTERVAL,
+ .rc_key_map = dib0700_rc_keys,
+ .rc_key_map_size = ARRAY_SIZE(dib0700_rc_keys),
+ .rc_query = dib0700_rc_query
+ }, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .frontend_attach = s5h1411_frontend_attach,
+ .tuner_attach = xc5000_tuner_attach,
+
+ DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
+
+ .size_of_priv = sizeof(struct
+ dib0700_adapter_state),
+ },
+ },
+
+ .num_device_descs = 2,
+ .devices = {
+ { "Pinnacle PCTV HD Pro USB Stick",
+ { &dib0700_usb_id_table[40], NULL },
+ { NULL },
+ },
+ { "Pinnacle PCTV HD USB Stick",
+ { &dib0700_usb_id_table[41], NULL },
+ { NULL },
+ },
},
.rc_interval = DEFAULT_RC_INTERVAL,
.rc_key_map = dib0700_rc_keys,
diff --git a/drivers/media/dvb/dvb-usb/dtv5100.c b/drivers/media/dvb/dvb-usb/dtv5100.c
new file mode 100644
index 0000000..078ce92
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/dtv5100.c
@@ -0,0 +1,240 @@
+/*
+ * DVB USB Linux driver for AME DTV-5100 USB2.0 DVB-T
+ *
+ * Copyright (C) 2008 Antoine Jacquet <royale@zerezo.com>
+ * http://royale.zerezo.com/dtv5100/
+ *
+ * Inspired by gl861.c and au6610.c drivers
+ *
+ * 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
+ */
+
+#include "dtv5100.h"
+#include "zl10353.h"
+#include "qt1010.h"
+
+/* debug */
+static int dvb_usb_dtv5100_debug;
+module_param_named(debug, dvb_usb_dtv5100_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static int dtv5100_i2c_msg(struct dvb_usb_device *d, u8 addr,
+ u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
+{
+ u8 request;
+ u8 type;
+ u16 value;
+ u16 index;
+
+ switch (wlen) {
+ case 1:
+ /* write { reg }, read { value } */
+ request = (addr == DTV5100_DEMOD_ADDR ? DTV5100_DEMOD_READ :
+ DTV5100_TUNER_READ);
+ type = USB_TYPE_VENDOR | USB_DIR_IN;
+ value = 0;
+ break;
+ case 2:
+ /* write { reg, value } */
+ request = (addr == DTV5100_DEMOD_ADDR ? DTV5100_DEMOD_WRITE :
+ DTV5100_TUNER_WRITE);
+ type = USB_TYPE_VENDOR | USB_DIR_OUT;
+ value = wbuf[1];
+ break;
+ default:
+ warn("wlen = %x, aborting.", wlen);
+ return -EINVAL;
+ }
+ index = (addr << 8) + wbuf[0];
+
+ msleep(1); /* avoid I2C errors */
+ return usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), request,
+ type, value, index, rbuf, rlen,
+ DTV5100_USB_TIMEOUT);
+}
+
+/* I2C */
+static int dtv5100_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+ int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ int i;
+
+ if (num > 2)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ for (i = 0; i < num; i++) {
+ /* write/read request */
+ if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
+ if (dtv5100_i2c_msg(d, msg[i].addr, msg[i].buf,
+ msg[i].len, msg[i+1].buf,
+ msg[i+1].len) < 0)
+ break;
+ i++;
+ } else if (dtv5100_i2c_msg(d, msg[i].addr, msg[i].buf,
+ msg[i].len, NULL, 0) < 0)
+ break;
+ }
+
+ mutex_unlock(&d->i2c_mutex);
+ return i;
+}
+
+static u32 dtv5100_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm dtv5100_i2c_algo = {
+ .master_xfer = dtv5100_i2c_xfer,
+ .functionality = dtv5100_i2c_func,
+};
+
+/* Callbacks for DVB USB */
+static struct zl10353_config dtv5100_zl10353_config = {
+ .demod_address = DTV5100_DEMOD_ADDR,
+ .no_tuner = 1,
+ .parallel_ts = 1,
+};
+
+static int dtv5100_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ adap->fe = dvb_attach(zl10353_attach, &dtv5100_zl10353_config,
+ &adap->dev->i2c_adap);
+ if (adap->fe == NULL)
+ return -EIO;
+
+ /* disable i2c gate, or it won't work... is this safe? */
+ adap->fe->ops.i2c_gate_ctrl = NULL;
+
+ return 0;
+}
+
+static struct qt1010_config dtv5100_qt1010_config = {
+ .i2c_address = DTV5100_TUNER_ADDR
+};
+
+static int dtv5100_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ return dvb_attach(qt1010_attach,
+ adap->fe, &adap->dev->i2c_adap,
+ &dtv5100_qt1010_config) == NULL ? -ENODEV : 0;
+}
+
+/* DVB USB Driver stuff */
+static struct dvb_usb_device_properties dtv5100_properties;
+
+static int dtv5100_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ int i, ret;
+ struct usb_device *udev = interface_to_usbdev(intf);
+
+ /* initialize non qt1010/zl10353 part? */
+ for (i = 0; dtv5100_init[i].request; i++) {
+ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ dtv5100_init[i].request,
+ USB_TYPE_VENDOR | USB_DIR_OUT,
+ dtv5100_init[i].value,
+ dtv5100_init[i].index, NULL, 0,
+ DTV5100_USB_TIMEOUT);
+ if (ret)
+ return ret;
+ }
+
+ ret = dvb_usb_device_init(intf, &dtv5100_properties,
+ THIS_MODULE, NULL, adapter_nr);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static struct usb_device_id dtv5100_table[] = {
+ { USB_DEVICE(0x06be, 0xa232) },
+ { } /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, dtv5100_table);
+
+static struct dvb_usb_device_properties dtv5100_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+ .usb_ctrl = DEVICE_SPECIFIC,
+
+ .size_of_priv = 0,
+
+ .num_adapters = 1,
+ .adapter = {{
+ .frontend_attach = dtv5100_frontend_attach,
+ .tuner_attach = dtv5100_tuner_attach,
+
+ .stream = {
+ .type = USB_BULK,
+ .count = 8,
+ .endpoint = 0x82,
+ .u = {
+ .bulk = {
+ .buffersize = 4096,
+ }
+ }
+ },
+ } },
+
+ .i2c_algo = &dtv5100_i2c_algo,
+
+ .num_device_descs = 1,
+ .devices = {
+ {
+ .name = "AME DTV-5100 USB2.0 DVB-T",
+ .cold_ids = { NULL },
+ .warm_ids = { &dtv5100_table[0], NULL },
+ },
+ }
+};
+
+static struct usb_driver dtv5100_driver = {
+ .name = "dvb_usb_dtv5100",
+ .probe = dtv5100_probe,
+ .disconnect = dvb_usb_device_exit,
+ .id_table = dtv5100_table,
+};
+
+/* module stuff */
+static int __init dtv5100_module_init(void)
+{
+ int ret;
+
+ ret = usb_register(&dtv5100_driver);
+ if (ret)
+ err("usb_register failed. Error number %d", ret);
+
+ return ret;
+}
+
+static void __exit dtv5100_module_exit(void)
+{
+ /* deregister this driver from the USB subsystem */
+ usb_deregister(&dtv5100_driver);
+}
+
+module_init(dtv5100_module_init);
+module_exit(dtv5100_module_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/dtv5100.h b/drivers/media/dvb/dvb-usb/dtv5100.h
new file mode 100644
index 0000000..93e96e0
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/dtv5100.h
@@ -0,0 +1,51 @@
+/*
+ * DVB USB Linux driver for AME DTV-5100 USB2.0 DVB-T
+ *
+ * Copyright (C) 2008 Antoine Jacquet <royale@zerezo.com>
+ * http://royale.zerezo.com/dtv5100/
+ *
+ * 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 _DVB_USB_DTV5100_H_
+#define _DVB_USB_DTV5100_H_
+
+#define DVB_USB_LOG_PREFIX "dtv5100"
+#include "dvb-usb.h"
+
+#define DTV5100_USB_TIMEOUT 500
+
+#define DTV5100_DEMOD_ADDR 0x00
+#define DTV5100_DEMOD_WRITE 0xc0
+#define DTV5100_DEMOD_READ 0xc1
+
+#define DTV5100_TUNER_ADDR 0xc4
+#define DTV5100_TUNER_WRITE 0xc7
+#define DTV5100_TUNER_READ 0xc8
+
+#define DRIVER_AUTHOR "Antoine Jacquet, http://royale.zerezo.com/"
+#define DRIVER_DESC "AME DTV-5100 USB2.0 DVB-T"
+
+static struct {
+ u8 request;
+ u8 value;
+ u16 index;
+} dtv5100_init[] = {
+ { 0x000000c5, 0x00000000, 0x00000001 },
+ { 0x000000c5, 0x00000001, 0x00000001 },
+ { } /* Terminating entry */
+};
+
+#endif
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index 03dfb9f..7380b94 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -22,6 +22,7 @@
#define USB_VID_AVERMEDIA 0x07ca
#define USB_VID_COMPRO 0x185b
#define USB_VID_COMPRO_UNK 0x145f
+#define USB_VID_CONEXANT 0x0572
#define USB_VID_CYPRESS 0x04b4
#define USB_VID_DIBCOM 0x10b8
#define USB_VID_DPOSH 0x1498
@@ -33,16 +34,19 @@
#define USB_VID_HAUPPAUGE 0x2040
#define USB_VID_HYPER_PALTEK 0x1025
#define USB_VID_KWORLD 0xeb2a
+#define USB_VID_KWORLD_2 0x1b80
#define USB_VID_KYE 0x0458
#define USB_VID_LEADTEK 0x0413
#define USB_VID_LITEON 0x04ca
#define USB_VID_MEDION 0x1660
#define USB_VID_MIGLIA 0x18f3
#define USB_VID_MSI 0x0db0
+#define USB_VID_MSI_2 0x1462
#define USB_VID_OPERA1 0x695c
#define USB_VID_PINNACLE 0x2304
#define USB_VID_TECHNOTREND 0x0b48
#define USB_VID_TERRATEC 0x0ccd
+#define USB_VID_TELESTAR 0x10b9
#define USB_VID_VISIONPLUS 0x13d3
#define USB_VID_TWINHAN 0x1822
#define USB_VID_ULTIMA_ELECTRONIC 0x05d8
@@ -50,15 +54,18 @@
#define USB_VID_WIDEVIEW 0x14aa
#define USB_VID_GIGABYTE 0x1044
#define USB_VID_YUAN 0x1164
-
+#define USB_VID_XTENSIONS 0x1ae7
/* Product IDs */
#define USB_PID_ADSTECH_USB2_COLD 0xa333
#define USB_PID_ADSTECH_USB2_WARM 0xa334
#define USB_PID_AFATECH_AF9005 0x9020
+#define USB_PID_AFATECH_AF9015_9015 0x9015
+#define USB_PID_AFATECH_AF9015_9016 0x9016
#define USB_VID_ALINK_DTU 0xf170
#define USB_PID_ANSONIC_DVBT_USB 0x6000
#define USB_PID_ANYSEE 0x861f
+#define USB_PID_AZUREWAVE_AD_TU700 0x3237
#define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001
#define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002
#define USB_PID_AVERMEDIA_DVBT_USB2_COLD 0xa800
@@ -69,6 +76,7 @@
#define USB_PID_COMPRO_DVBU2000_UNK_WARM 0x010d
#define USB_PID_COMPRO_VIDEOMATE_U500 0x1e78
#define USB_PID_COMPRO_VIDEOMATE_U500_PC 0x1e80
+#define USB_PID_CONEXANT_D680_DMB 0x86d6
#define USB_PID_DIBCOM_HOOK_DEFAULT 0x0064
#define USB_PID_DIBCOM_HOOK_DEFAULT_REENUM 0x0065
#define USB_PID_DIBCOM_MOD3000_COLD 0x0bb8
@@ -87,9 +95,12 @@
#define USB_PID_UNIWILL_STK7700P 0x6003
#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0
#define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1
+#define USB_PID_KWORLD_399U 0xe399
+#define USB_PID_KWORLD_PC160_2T 0xc160
#define USB_PID_KWORLD_VSTREAM_COLD 0x17de
#define USB_PID_KWORLD_VSTREAM_WARM 0x17df
#define USB_PID_TERRATEC_CINERGY_T_USB_XE 0x0055
+#define USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2 0x0069
#define USB_PID_TWINHAN_VP7041_COLD 0x3201
#define USB_PID_TWINHAN_VP7041_WARM 0x3202
#define USB_PID_TWINHAN_VP7020_COLD 0x3203
@@ -98,6 +109,7 @@
#define USB_PID_TWINHAN_VP7045_WARM 0x3206
#define USB_PID_TWINHAN_VP7021_COLD 0x3207
#define USB_PID_TWINHAN_VP7021_WARM 0x3208
+#define USB_PID_TINYTWIN 0x3226
#define USB_PID_DNTV_TINYUSB2_COLD 0x3223
#define USB_PID_DNTV_TINYUSB2_WARM 0x3224
#define USB_PID_ULTIMA_TVBOX_COLD 0x8105
@@ -144,6 +156,9 @@
#define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R 0x0039
#define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_ATSC 0x1039
#define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_DVBT 0x2039
+#define USB_PID_AVERMEDIA_VOLAR_X 0xa815
+#define USB_PID_AVERMEDIA_VOLAR_X_2 0x8150
+#define USB_PID_AVERMEDIA_A309 0xa309
#define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006
#define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY 0x005a
#define USB_PID_TERRATEC_CINERGY_HT_USB_XE 0x0058
@@ -153,8 +168,11 @@
#define USB_PID_PINNACLE_PCTV2000E 0x022c
#define USB_PID_PINNACLE_PCTV_DVB_T_FLASH 0x0228
#define USB_PID_PINNACLE_PCTV_DUAL_DIVERSITY_DVB_T 0x0229
+#define USB_PID_PINNACLE_PCTV71E 0x022b
#define USB_PID_PINNACLE_PCTV72E 0x0236
#define USB_PID_PINNACLE_PCTV73E 0x0237
+#define USB_PID_PINNACLE_PCTV801E 0x023a
+#define USB_PID_PINNACLE_PCTV801E_SE 0x023b
#define USB_PID_PCTV_200E 0x020e
#define USB_PID_PCTV_400E 0x020f
#define USB_PID_PCTV_450E 0x0222
@@ -171,6 +189,7 @@
#define USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD 0xdb58
#define USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM 0xdb59
#define USB_PID_DVICO_BLUEBIRD_DUAL_4 0xdb78
+#define USB_PID_DVICO_BLUEBIRD_DUAL_4_REV_2 0xdb98
#define USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2 0xdb70
#define USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM 0xdb71
#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD 0xdb54
@@ -190,6 +209,7 @@
#define USB_PID_WINFAST_DTV_DONGLE_WARM 0x6026
#define USB_PID_WINFAST_DTV_DONGLE_STK7700P 0x6f00
#define USB_PID_WINFAST_DTV_DONGLE_STK7700P_2 0x6f01
+#define USB_PID_WINFAST_DTV_DONGLE_GOLD 0x6029
#define USB_PID_GENPIX_8PSK_REV_1_COLD 0x0200
#define USB_PID_GENPIX_8PSK_REV_1_WARM 0x0201
#define USB_PID_GENPIX_8PSK_REV_2 0x0202
@@ -197,14 +217,21 @@
#define USB_PID_GENPIX_SKYWALKER_CW3K 0x0204
#define USB_PID_SIGMATEK_DVB_110 0x6610
#define USB_PID_MSI_DIGI_VOX_MINI_II 0x1513
+#define USB_PID_MSI_DIGIVOX_DUO 0x8801
#define USB_PID_OPERA1_COLD 0x2830
#define USB_PID_OPERA1_WARM 0x3829
#define USB_PID_LIFEVIEW_TV_WALKER_TWIN_COLD 0x0514
#define USB_PID_LIFEVIEW_TV_WALKER_TWIN_WARM 0x0513
#define USB_PID_GIGABYTE_U7000 0x7001
+#define USB_PID_GIGABYTE_U8000 0x7002
#define USB_PID_ASUS_U3000 0x171f
+#define USB_PID_ASUS_U3000H 0x1736
#define USB_PID_ASUS_U3100 0x173f
#define USB_PID_YUAN_EC372S 0x1edc
+#define USB_PID_YUAN_STK7700PH 0x1f08
#define USB_PID_DW2102 0x2102
+#define USB_PID_XTENSIONS_XD_380 0x0381
+#define USB_PID_TELESTAR_STARSTICK_2 0x8000
+#define USB_PID_MSI_DIGI_VOX_MINI_III 0x8807
#endif
diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c
index a4d898b..ca53df6 100644
--- a/drivers/media/dvb/dvb-usb/dw2102.c
+++ b/drivers/media/dvb/dvb-usb/dw2102.c
@@ -1,4 +1,5 @@
-/* DVB USB framework compliant Linux driver for the DVBWorld DVB-S 2102 Card
+/* DVB USB framework compliant Linux driver for the
+* DVBWorld DVB-S 2101, 2102, DVB-S2 2104 Card
*
* Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by)
*
@@ -10,62 +11,74 @@
*/
#include <linux/version.h>
#include "dw2102.h"
+#include "si21xx.h"
#include "stv0299.h"
#include "z0194a.h"
+#include "stv0288.h"
+#include "stb6000.h"
+#include "eds1547.h"
+#include "cx24116.h"
#ifndef USB_PID_DW2102
#define USB_PID_DW2102 0x2102
#endif
-#define DW2102_READ_MSG 0
-#define DW2102_WRITE_MSG 1
+#ifndef USB_PID_DW2104
+#define USB_PID_DW2104 0x2104
+#endif
+
+#define DW210X_READ_MSG 0
+#define DW210X_WRITE_MSG 1
#define REG_1F_SYMBOLRATE_BYTE0 0x1f
#define REG_20_SYMBOLRATE_BYTE1 0x20
#define REG_21_SYMBOLRATE_BYTE2 0x21
-
+/* on my own*/
#define DW2102_VOLTAGE_CTRL (0x1800)
#define DW2102_RC_QUERY (0x1a00)
-struct dw2102_state {
+struct dw210x_state {
u32 last_key_pressed;
};
-struct dw2102_rc_keys {
+struct dw210x_rc_keys {
u32 keycode;
u32 event;
};
+/* debug */
+static int dvb_usb_dw2102_debug;
+module_param_named(debug, dvb_usb_dw2102_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info 2=xfer (or-able))." DVB_USB_DEBUG_STATUS);
+
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-static int dw2102_op_rw(struct usb_device *dev, u8 request, u16 value,
- u8 *data, u16 len, int flags)
+static int dw210x_op_rw(struct usb_device *dev, u8 request, u16 value,
+ u16 index, u8 * data, u16 len, int flags)
{
int ret;
u8 u8buf[len];
- unsigned int pipe = (flags == DW2102_READ_MSG) ?
- usb_rcvctrlpipe(dev, 0) : usb_sndctrlpipe(dev, 0);
- u8 request_type = (flags == DW2102_READ_MSG) ? USB_DIR_IN : USB_DIR_OUT;
+ unsigned int pipe = (flags == DW210X_READ_MSG) ?
+ usb_rcvctrlpipe(dev, 0) : usb_sndctrlpipe(dev, 0);
+ u8 request_type = (flags == DW210X_READ_MSG) ? USB_DIR_IN : USB_DIR_OUT;
- if (flags == DW2102_WRITE_MSG)
+ if (flags == DW210X_WRITE_MSG)
memcpy(u8buf, data, len);
- ret = usb_control_msg(dev, pipe, request,
- request_type | USB_TYPE_VENDOR, value, 0 , u8buf, len, 2000);
+ ret = usb_control_msg(dev, pipe, request, request_type | USB_TYPE_VENDOR,
+ value, index , u8buf, len, 2000);
- if (flags == DW2102_READ_MSG)
+ if (flags == DW210X_READ_MSG)
memcpy(data, u8buf, len);
return ret;
}
/* I2C */
-
static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
int i = 0, ret = 0;
u8 buf6[] = {0x2c, 0x05, 0xc0, 0, 0, 0, 0};
- u8 request;
u16 value;
if (!d)
@@ -76,14 +89,12 @@
switch (num) {
case 2:
/* read stv0299 register */
- request = 0xb5;
value = msg[0].buf[0];/* register */
for (i = 0; i < msg[1].len; i++) {
value = value + i;
- ret = dw2102_op_rw(d->udev, 0xb5,
- value, buf6, 2, DW2102_READ_MSG);
+ ret = dw210x_op_rw(d->udev, 0xb5, value, 0,
+ buf6, 2, DW210X_READ_MSG);
msg[1].buf[i] = buf6[0];
-
}
break;
case 1:
@@ -93,8 +104,8 @@
buf6[0] = 0x2a;
buf6[1] = msg[0].buf[0];
buf6[2] = msg[0].buf[1];
- ret = dw2102_op_rw(d->udev, 0xb2,
- 0, buf6, 3, DW2102_WRITE_MSG);
+ ret = dw210x_op_rw(d->udev, 0xb2, 0, 0,
+ buf6, 3, DW210X_WRITE_MSG);
break;
case 0x60:
if (msg[0].flags == 0) {
@@ -106,26 +117,26 @@
buf6[4] = msg[0].buf[1];
buf6[5] = msg[0].buf[2];
buf6[6] = msg[0].buf[3];
- ret = dw2102_op_rw(d->udev, 0xb2,
- 0, buf6, 7, DW2102_WRITE_MSG);
+ ret = dw210x_op_rw(d->udev, 0xb2, 0, 0,
+ buf6, 7, DW210X_WRITE_MSG);
} else {
- /* write to tuner pll */
- ret = dw2102_op_rw(d->udev, 0xb5,
- 0, buf6, 1, DW2102_READ_MSG);
+ /* read from tuner */
+ ret = dw210x_op_rw(d->udev, 0xb5, 0, 0,
+ buf6, 1, DW210X_READ_MSG);
msg[0].buf[0] = buf6[0];
}
break;
case (DW2102_RC_QUERY):
- ret = dw2102_op_rw(d->udev, 0xb8,
- 0, buf6, 2, DW2102_READ_MSG);
+ ret = dw210x_op_rw(d->udev, 0xb8, 0, 0,
+ buf6, 2, DW210X_READ_MSG);
msg[0].buf[0] = buf6[0];
msg[0].buf[1] = buf6[1];
break;
case (DW2102_VOLTAGE_CTRL):
buf6[0] = 0x30;
buf6[1] = msg[0].buf[0];
- ret = dw2102_op_rw(d->udev, 0xb2,
- 0, buf6, 2, DW2102_WRITE_MSG);
+ ret = dw210x_op_rw(d->udev, 0xb2, 0, 0,
+ buf6, 2, DW210X_WRITE_MSG);
break;
}
@@ -136,17 +147,265 @@
return num;
}
-static u32 dw2102_i2c_func(struct i2c_adapter *adapter)
+static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap,
+ struct i2c_msg msg[], int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ int ret = 0;
+ u8 buf6[] = {0, 0, 0, 0, 0, 0, 0};
+
+ if (!d)
+ return -ENODEV;
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ switch (num) {
+ case 2:
+ /* read si2109 register by number */
+ buf6[0] = 0xd0;
+ buf6[1] = msg[0].len;
+ buf6[2] = msg[0].buf[0];
+ ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
+ buf6, msg[0].len + 2, DW210X_WRITE_MSG);
+ /* read si2109 register */
+ ret = dw210x_op_rw(d->udev, 0xc3, 0xd0, 0,
+ buf6, msg[1].len + 2, DW210X_READ_MSG);
+ memcpy(msg[1].buf, buf6 + 2, msg[1].len);
+
+ break;
+ case 1:
+ switch (msg[0].addr) {
+ case 0x68:
+ /* write to si2109 register */
+ buf6[0] = 0xd0;
+ buf6[1] = msg[0].len;
+ memcpy(buf6 + 2, msg[0].buf, msg[0].len);
+ ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, buf6,
+ msg[0].len + 2, DW210X_WRITE_MSG);
+ break;
+ case(DW2102_RC_QUERY):
+ ret = dw210x_op_rw(d->udev, 0xb8, 0, 0,
+ buf6, 2, DW210X_READ_MSG);
+ msg[0].buf[0] = buf6[0];
+ msg[0].buf[1] = buf6[1];
+ break;
+ case(DW2102_VOLTAGE_CTRL):
+ buf6[0] = 0x30;
+ buf6[1] = msg[0].buf[0];
+ ret = dw210x_op_rw(d->udev, 0xb2, 0, 0,
+ buf6, 2, DW210X_WRITE_MSG);
+ break;
+ }
+ break;
+ }
+
+ mutex_unlock(&d->i2c_mutex);
+ return num;
+}
+static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ int ret = 0;
+
+ if (!d)
+ return -ENODEV;
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ switch (num) {
+ case 2: {
+ /* read */
+ /* first write first register number */
+ u8 ibuf [msg[1].len + 2], obuf[3];
+ obuf[0] = 0xd0;
+ obuf[1] = msg[0].len;
+ obuf[2] = msg[0].buf[0];
+ ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
+ obuf, msg[0].len + 2, DW210X_WRITE_MSG);
+ /* second read registers */
+ ret = dw210x_op_rw(d->udev, 0xc3, 0xd1 , 0,
+ ibuf, msg[1].len + 2, DW210X_READ_MSG);
+ memcpy(msg[1].buf, ibuf + 2, msg[1].len);
+
+ break;
+ }
+ case 1:
+ switch (msg[0].addr) {
+ case 0x68: {
+ /* write to register */
+ u8 obuf[msg[0].len + 2];
+ obuf[0] = 0xd0;
+ obuf[1] = msg[0].len;
+ memcpy(obuf + 2, msg[0].buf, msg[0].len);
+ ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
+ obuf, msg[0].len + 2, DW210X_WRITE_MSG);
+ break;
+ }
+ case 0x61: {
+ /* write to tuner */
+ u8 obuf[msg[0].len + 2];
+ obuf[0] = 0xc2;
+ obuf[1] = msg[0].len;
+ memcpy(obuf + 2, msg[0].buf, msg[0].len);
+ ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
+ obuf, msg[0].len + 2, DW210X_WRITE_MSG);
+ break;
+ }
+ case(DW2102_RC_QUERY): {
+ u8 ibuf[2];
+ ret = dw210x_op_rw(d->udev, 0xb8, 0, 0,
+ ibuf, 2, DW210X_READ_MSG);
+ memcpy(msg[0].buf, ibuf , 2);
+ break;
+ }
+ case(DW2102_VOLTAGE_CTRL): {
+ u8 obuf[2];
+ obuf[0] = 0x30;
+ obuf[1] = msg[0].buf[0];
+ ret = dw210x_op_rw(d->udev, 0xb2, 0, 0,
+ obuf, 2, DW210X_WRITE_MSG);
+ break;
+ }
+ }
+
+ break;
+ }
+
+ mutex_unlock(&d->i2c_mutex);
+ return num;
+}
+
+static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ int ret = 0;
+ int len, i;
+
+ if (!d)
+ return -ENODEV;
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ switch (num) {
+ case 2: {
+ /* read */
+ /* first write first register number */
+ u8 ibuf [msg[1].len + 2], obuf[3];
+ obuf[0] = 0xaa;
+ obuf[1] = msg[0].len;
+ obuf[2] = msg[0].buf[0];
+ ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
+ obuf, msg[0].len + 2, DW210X_WRITE_MSG);
+ /* second read registers */
+ ret = dw210x_op_rw(d->udev, 0xc3, 0xab , 0,
+ ibuf, msg[1].len + 2, DW210X_READ_MSG);
+ memcpy(msg[1].buf, ibuf + 2, msg[1].len);
+
+ break;
+ }
+ case 1:
+ switch (msg[0].addr) {
+ case 0x55: {
+ if (msg[0].buf[0] == 0xf7) {
+ /* firmware */
+ /* Write in small blocks */
+ u8 obuf[19];
+ obuf[0] = 0xaa;
+ obuf[1] = 0x11;
+ obuf[2] = 0xf7;
+ len = msg[0].len - 1;
+ i = 1;
+ do {
+ memcpy(obuf + 3, msg[0].buf + i, (len > 16 ? 16 : len));
+ ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
+ obuf, (len > 16 ? 16 : len) + 3, DW210X_WRITE_MSG);
+ i += 16;
+ len -= 16;
+ } while (len > 0);
+ } else {
+ /* write to register */
+ u8 obuf[msg[0].len + 2];
+ obuf[0] = 0xaa;
+ obuf[1] = msg[0].len;
+ memcpy(obuf + 2, msg[0].buf, msg[0].len);
+ ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
+ obuf, msg[0].len + 2, DW210X_WRITE_MSG);
+ }
+ break;
+ }
+ case(DW2102_RC_QUERY): {
+ u8 ibuf[2];
+ ret = dw210x_op_rw(d->udev, 0xb8, 0, 0,
+ ibuf, 2, DW210X_READ_MSG);
+ memcpy(msg[0].buf, ibuf , 2);
+ break;
+ }
+ case(DW2102_VOLTAGE_CTRL): {
+ u8 obuf[2];
+ obuf[0] = 0x30;
+ obuf[1] = msg[0].buf[0];
+ ret = dw210x_op_rw(d->udev, 0xb2, 0, 0,
+ obuf, 2, DW210X_WRITE_MSG);
+ break;
+ }
+ }
+
+ break;
+ }
+
+ mutex_unlock(&d->i2c_mutex);
+ return num;
+}
+
+static u32 dw210x_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
}
static struct i2c_algorithm dw2102_i2c_algo = {
.master_xfer = dw2102_i2c_transfer,
- .functionality = dw2102_i2c_func,
+ .functionality = dw210x_i2c_func,
};
-static int dw2102_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+static struct i2c_algorithm dw2102_serit_i2c_algo = {
+ .master_xfer = dw2102_serit_i2c_transfer,
+ .functionality = dw210x_i2c_func,
+};
+
+static struct i2c_algorithm dw2102_earda_i2c_algo = {
+ .master_xfer = dw2102_earda_i2c_transfer,
+ .functionality = dw210x_i2c_func,
+};
+
+static struct i2c_algorithm dw2104_i2c_algo = {
+ .master_xfer = dw2104_i2c_transfer,
+ .functionality = dw210x_i2c_func,
+};
+
+static int dw210x_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
+{
+ int i;
+ u8 ibuf[] = {0, 0};
+ u8 eeprom[256], eepromline[16];
+
+ for (i = 0; i < 256; i++) {
+ if (dw210x_op_rw(d->udev, 0xb6, 0xa0 , i, ibuf, 2, DW210X_READ_MSG) < 0) {
+ err("read eeprom failed.");
+ return -1;
+ } else {
+ eepromline[i%16] = ibuf[0];
+ eeprom[i] = ibuf[0];
+ }
+ if ((i % 16) == 15) {
+ deb_xfer("%02x: ", i - 15);
+ debug_dump(eepromline, 16, deb_xfer);
+ }
+ }
+ memcpy(mac, eeprom + 8, 6);
+ return 0;
+};
+
+static int dw210x_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
{
static u8 command_13v[1] = {0x00};
static u8 command_18v[1] = {0x01};
@@ -163,14 +422,62 @@
return 0;
}
+static struct cx24116_config dw2104_config = {
+ .demod_address = 0x55,
+ .mpg_clk_pos_pol = 0x01,
+};
+
+static struct si21xx_config serit_sp1511lhb_config = {
+ .demod_address = 0x68,
+ .min_delay_ms = 100,
+
+};
+
+static int dw2104_frontend_attach(struct dvb_usb_adapter *d)
+{
+ if ((d->fe = dvb_attach(cx24116_attach, &dw2104_config,
+ &d->dev->i2c_adap)) != NULL) {
+ d->fe->ops.set_voltage = dw210x_set_voltage;
+ info("Attached cx24116!\n");
+ return 0;
+ }
+ return -EIO;
+}
+
+static struct dvb_usb_device_properties dw2102_properties;
+
static int dw2102_frontend_attach(struct dvb_usb_adapter *d)
{
- d->fe = dvb_attach(stv0299_attach, &sharp_z0194a_config,
- &d->dev->i2c_adap);
- if (d->fe != NULL) {
- d->fe->ops.set_voltage = dw2102_set_voltage;
- info("Attached stv0299!\n");
- return 0;
+ if (dw2102_properties.i2c_algo == &dw2102_serit_i2c_algo) {
+ /*dw2102_properties.adapter->tuner_attach = NULL;*/
+ d->fe = dvb_attach(si21xx_attach, &serit_sp1511lhb_config,
+ &d->dev->i2c_adap);
+ if (d->fe != NULL) {
+ d->fe->ops.set_voltage = dw210x_set_voltage;
+ info("Attached si21xx!\n");
+ return 0;
+ }
+ }
+ if (dw2102_properties.i2c_algo == &dw2102_earda_i2c_algo) {
+ /*dw2102_properties.adapter->tuner_attach = dw2102_tuner_attach;*/
+ d->fe = dvb_attach(stv0288_attach, &earda_config,
+ &d->dev->i2c_adap);
+ if (d->fe != NULL) {
+ d->fe->ops.set_voltage = dw210x_set_voltage;
+ info("Attached stv0288!\n");
+ return 0;
+ }
+ }
+
+ if (dw2102_properties.i2c_algo == &dw2102_i2c_algo) {
+ /*dw2102_properties.adapter->tuner_attach = dw2102_tuner_attach;*/
+ d->fe = dvb_attach(stv0299_attach, &sharp_z0194a_config,
+ &d->dev->i2c_adap);
+ if (d->fe != NULL) {
+ d->fe->ops.set_voltage = dw210x_set_voltage;
+ info("Attached stv0299!\n");
+ return 0;
+ }
}
return -EIO;
}
@@ -182,7 +489,15 @@
return 0;
}
-static struct dvb_usb_rc_key dw2102_rc_keys[] = {
+static int dw2102_earda_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ dvb_attach(stb6000_attach, adap->fe, 0x61,
+ &adap->dev->i2c_adap);
+
+ return 0;
+}
+
+static struct dvb_usb_rc_key dw210x_rc_keys[] = {
{ 0xf8, 0x0a, KEY_Q }, /*power*/
{ 0xf8, 0x0c, KEY_M }, /*mute*/
{ 0xf8, 0x11, KEY_1 },
@@ -221,7 +536,7 @@
static int dw2102_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
{
- struct dw2102_state *st = d->priv;
+ struct dw210x_state *st = d->priv;
u8 key[2];
struct i2c_msg msg[] = {
{.addr = DW2102_RC_QUERY, .flags = I2C_M_RD, .buf = key,
@@ -231,12 +546,12 @@
*state = REMOTE_NO_KEY_PRESSED;
if (dw2102_i2c_transfer(&d->i2c_adap, msg, 1) == 1) {
- for (i = 0; i < ARRAY_SIZE(dw2102_rc_keys); i++) {
- if (dw2102_rc_keys[i].data == msg[0].buf[0]) {
+ for (i = 0; i < ARRAY_SIZE(dw210x_rc_keys); i++) {
+ if (dw210x_rc_keys[i].data == msg[0].buf[0]) {
*state = REMOTE_KEY_PRESSED;
- *event = dw2102_rc_keys[i].event;
+ *event = dw210x_rc_keys[i].event;
st->last_key_pressed =
- dw2102_rc_keys[i].event;
+ dw210x_rc_keys[i].event;
break;
}
st->last_key_pressed = 0;
@@ -249,6 +564,8 @@
static struct usb_device_id dw2102_table[] = {
{USB_DEVICE(USB_VID_CYPRESS, USB_PID_DW2102)},
{USB_DEVICE(USB_VID_CYPRESS, 0x2101)},
+ {USB_DEVICE(USB_VID_CYPRESS, 0x2104)},
+ {USB_DEVICE(0x9022, 0xd650)},
{ }
};
@@ -260,7 +577,7 @@
u8 *b, *p;
int ret = 0, i;
u8 reset;
- u8 reset16 [] = {0, 0, 0, 0, 0, 0, 0};
+ u8 reset16[] = {0, 0, 0, 0, 0, 0, 0};
const struct firmware *fw;
const char *filename = "dvb-usb-dw2101.fw";
switch (dev->descriptor.idProduct) {
@@ -273,25 +590,23 @@
return ret;
}
break;
- case USB_PID_DW2102:
+ default:
fw = frmwr;
break;
}
- info("start downloading DW2102 firmware");
+ info("start downloading DW210X firmware");
p = kmalloc(fw->size, GFP_KERNEL);
reset = 1;
/*stop the CPU*/
- dw2102_op_rw(dev, 0xa0, 0x7f92, &reset, 1, DW2102_WRITE_MSG);
- dw2102_op_rw(dev, 0xa0, 0xe600, &reset, 1, DW2102_WRITE_MSG);
+ dw210x_op_rw(dev, 0xa0, 0x7f92, 0, &reset, 1, DW210X_WRITE_MSG);
+ dw210x_op_rw(dev, 0xa0, 0xe600, 0, &reset, 1, DW210X_WRITE_MSG);
if (p != NULL) {
memcpy(p, fw->data, fw->size);
for (i = 0; i < fw->size; i += 0x40) {
b = (u8 *) p + i;
- if (dw2102_op_rw
- (dev, 0xa0, i, b , 0x40,
- DW2102_WRITE_MSG) != 0x40
- ) {
+ if (dw210x_op_rw(dev, 0xa0, i, 0, b , 0x40,
+ DW210X_WRITE_MSG) != 0x40) {
err("error while transferring firmware");
ret = -EINVAL;
break;
@@ -299,43 +614,66 @@
}
/* restart the CPU */
reset = 0;
- if (ret || dw2102_op_rw
- (dev, 0xa0, 0x7f92, &reset, 1,
- DW2102_WRITE_MSG) != 1) {
+ if (ret || dw210x_op_rw(dev, 0xa0, 0x7f92, 0, &reset, 1,
+ DW210X_WRITE_MSG) != 1) {
err("could not restart the USB controller CPU.");
ret = -EINVAL;
}
- if (ret || dw2102_op_rw
- (dev, 0xa0, 0xe600, &reset, 1,
- DW2102_WRITE_MSG) != 1) {
+ if (ret || dw210x_op_rw(dev, 0xa0, 0xe600, 0, &reset, 1,
+ DW210X_WRITE_MSG) != 1) {
err("could not restart the USB controller CPU.");
ret = -EINVAL;
}
/* init registers */
switch (dev->descriptor.idProduct) {
- case USB_PID_DW2102:
- dw2102_op_rw
- (dev, 0xbf, 0x0040, &reset, 0,
- DW2102_WRITE_MSG);
- dw2102_op_rw
- (dev, 0xb9, 0x0000, &reset16[0], 2,
- DW2102_READ_MSG);
+ case USB_PID_DW2104:
+ case 0xd650:
+ reset = 1;
+ dw210x_op_rw(dev, 0xc4, 0x0000, 0, &reset, 1,
+ DW210X_WRITE_MSG);
+ reset = 0;
+ dw210x_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0,
+ DW210X_WRITE_MSG);
break;
+ case USB_PID_DW2102:
+ dw210x_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0,
+ DW210X_WRITE_MSG);
+ dw210x_op_rw(dev, 0xb9, 0x0000, 0, &reset16[0], 2,
+ DW210X_READ_MSG);
+ /* check STV0299 frontend */
+ dw210x_op_rw(dev, 0xb5, 0, 0, &reset16[0], 2,
+ DW210X_READ_MSG);
+ if (reset16[0] == 0xa1) {
+ dw2102_properties.i2c_algo = &dw2102_i2c_algo;
+ dw2102_properties.adapter->tuner_attach = &dw2102_tuner_attach;
+ break;
+ } else {
+ /* check STV0288 frontend */
+ reset16[0] = 0xd0;
+ reset16[1] = 1;
+ reset16[2] = 0;
+ dw210x_op_rw(dev, 0xc2, 0, 0, &reset16[0], 3,
+ DW210X_WRITE_MSG);
+ dw210x_op_rw(dev, 0xc3, 0xd1, 0, &reset16[0], 3,
+ DW210X_READ_MSG);
+ if (reset16[2] == 0x11) {
+ dw2102_properties.i2c_algo = &dw2102_earda_i2c_algo;
+ dw2102_properties.adapter->tuner_attach = &dw2102_earda_tuner_attach;
+ break;
+ }
+ }
case 0x2101:
- dw2102_op_rw
- (dev, 0xbc, 0x0030, &reset16[0], 2,
- DW2102_READ_MSG);
- dw2102_op_rw
- (dev, 0xba, 0x0000, &reset16[0], 7,
- DW2102_READ_MSG);
- dw2102_op_rw
- (dev, 0xba, 0x0000, &reset16[0], 7,
- DW2102_READ_MSG);
- dw2102_op_rw
- (dev, 0xb9, 0x0000, &reset16[0], 2,
- DW2102_READ_MSG);
+ dw210x_op_rw(dev, 0xbc, 0x0030, 0, &reset16[0], 2,
+ DW210X_READ_MSG);
+ dw210x_op_rw(dev, 0xba, 0x0000, 0, &reset16[0], 7,
+ DW210X_READ_MSG);
+ dw210x_op_rw(dev, 0xba, 0x0000, 0, &reset16[0], 7,
+ DW210X_READ_MSG);
+ dw210x_op_rw(dev, 0xb9, 0x0000, 0, &reset16[0], 2,
+ DW210X_READ_MSG);
break;
}
+ msleep(100);
kfree(p);
}
return ret;
@@ -345,12 +683,12 @@
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = DEVICE_SPECIFIC,
.firmware = "dvb-usb-dw2102.fw",
- .size_of_priv = sizeof(struct dw2102_state),
+ .size_of_priv = sizeof(struct dw210x_state),
.no_reconnect = 1,
- .i2c_algo = &dw2102_i2c_algo,
- .rc_key_map = dw2102_rc_keys,
- .rc_key_map_size = ARRAY_SIZE(dw2102_rc_keys),
+ .i2c_algo = &dw2102_serit_i2c_algo,
+ .rc_key_map = dw210x_rc_keys,
+ .rc_key_map_size = ARRAY_SIZE(dw210x_rc_keys),
.rc_interval = 150,
.rc_query = dw2102_rc_query,
@@ -358,11 +696,12 @@
/* parameter for the MPEG2-data transfer */
.num_adapters = 1,
.download_firmware = dw2102_load_firmware,
- .adapter = {
+ .read_mac_address = dw210x_read_mac_address,
+ .adapter = {
{
.frontend_attach = dw2102_frontend_attach,
.streaming_ctrl = NULL,
- .tuner_attach = dw2102_tuner_attach,
+ .tuner_attach = NULL,
.stream = {
.type = USB_BULK,
.count = 8,
@@ -388,11 +727,64 @@
}
};
+static struct dvb_usb_device_properties dw2104_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+ .usb_ctrl = DEVICE_SPECIFIC,
+ .firmware = "dvb-usb-dw2104.fw",
+ .size_of_priv = sizeof(struct dw210x_state),
+ .no_reconnect = 1,
+
+ .i2c_algo = &dw2104_i2c_algo,
+ .rc_key_map = dw210x_rc_keys,
+ .rc_key_map_size = ARRAY_SIZE(dw210x_rc_keys),
+ .rc_interval = 150,
+ .rc_query = dw2102_rc_query,
+
+ .generic_bulk_ctrl_endpoint = 0x81,
+ /* parameter for the MPEG2-data transfer */
+ .num_adapters = 1,
+ .download_firmware = dw2102_load_firmware,
+ .read_mac_address = dw210x_read_mac_address,
+ .adapter = {
+ {
+ .frontend_attach = dw2104_frontend_attach,
+ .streaming_ctrl = NULL,
+ /*.tuner_attach = dw2104_tuner_attach,*/
+ .stream = {
+ .type = USB_BULK,
+ .count = 8,
+ .endpoint = 0x82,
+ .u = {
+ .bulk = {
+ .buffersize = 4096,
+ }
+ }
+ },
+ }
+ },
+ .num_device_descs = 2,
+ .devices = {
+ { "DVBWorld DW2104 USB2.0",
+ {&dw2102_table[2], NULL},
+ {NULL},
+ },
+ { "TeVii S650 USB2.0",
+ {&dw2102_table[3], NULL},
+ {NULL},
+ },
+ }
+};
+
static int dw2102_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
- return dvb_usb_device_init(intf, &dw2102_properties,
- THIS_MODULE, NULL, adapter_nr);
+ if (0 == dvb_usb_device_init(intf, &dw2102_properties,
+ THIS_MODULE, NULL, adapter_nr) ||
+ 0 == dvb_usb_device_init(intf, &dw2104_properties,
+ THIS_MODULE, NULL, adapter_nr)) {
+ return 0;
+ }
+ return -ENODEV;
}
static struct usb_driver dw2102_driver = {
@@ -420,6 +812,6 @@
module_exit(dw2102_module_exit);
MODULE_AUTHOR("Igor M. Liplianin (c) liplianin@me.by");
-MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101 2102 USB2.0 device");
+MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101, 2102, DVB-S2 2104 USB2.0 device");
MODULE_VERSION("0.1");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/dw2102.h b/drivers/media/dvb/dvb-usb/dw2102.h
index 7a310f9..e337073 100644
--- a/drivers/media/dvb/dvb-usb/dw2102.h
+++ b/drivers/media/dvb/dvb-usb/dw2102.h
@@ -4,6 +4,5 @@
#define DVB_USB_LOG_PREFIX "dw2102"
#include "dvb-usb.h"
-extern int dvb_usb_dw2102_debug;
#define deb_xfer(args...) dprintk(dvb_usb_dw2102_debug, 0x02, args)
#endif
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
index 7dbb4a2..96b93e2 100644
--- a/drivers/media/dvb/frontends/Kconfig
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -43,6 +43,20 @@
help
A DVB-S tuner module. Say Y when you want to support this frontend.
+config DVB_STV0288
+ tristate "ST STV0288 based"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_STB6000
+ tristate "ST STB6000 silicon tuner"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-S silicon tuner module. Say Y when you want to support this tuner.
+
config DVB_STV0299
tristate "ST STV0299 based"
depends on DVB_CORE && I2C
@@ -92,6 +106,20 @@
help
A DVB-S PLL chip.
+config DVB_CX24116
+ tristate "Conexant CX24116 based"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-S/S2 tuner module. Say Y when you want to support this frontend.
+
+config DVB_SI21XX
+ tristate "Silicon Labs SI21XX based"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
comment "DVB-T (terrestrial) frontends"
depends on DVB_CORE
@@ -385,4 +413,23 @@
help
An SEC control chip.
+config DVB_LGS8GL5
+ tristate "Silicon Legend LGS-8GL5 demodulator (OFDM)"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DMB-TH tuner module. Say Y when you want to support this frontend.
+
+comment "Tools to develop new frontends"
+
+config DVB_DUMMY_FE
+ tristate "Dummy frontend driver"
+ default n
+
+config DVB_AF9013
+ tristate "Afatech AF9013 demodulator"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ Say Y when you want to support this frontend.
endmenu
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
index 028da55..aba79f4 100644
--- a/drivers/media/dvb/frontends/Makefile
+++ b/drivers/media/dvb/frontends/Makefile
@@ -48,3 +48,10 @@
obj-$(CONFIG_DVB_AU8522) += au8522.o
obj-$(CONFIG_DVB_TDA10048) += tda10048.o
obj-$(CONFIG_DVB_S5H1411) += s5h1411.o
+obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o
+obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o
+obj-$(CONFIG_DVB_AF9013) += af9013.o
+obj-$(CONFIG_DVB_CX24116) += cx24116.o
+obj-$(CONFIG_DVB_SI21XX) += si21xx.o
+obj-$(CONFIG_DVB_STV0288) += stv0288.o
+obj-$(CONFIG_DVB_STB6000) += stb6000.o
diff --git a/drivers/media/dvb/frontends/af9013.c b/drivers/media/dvb/frontends/af9013.c
new file mode 100644
index 0000000..21c1060
--- /dev/null
+++ b/drivers/media/dvb/frontends/af9013.c
@@ -0,0 +1,1685 @@
+/*
+ * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ *
+ * Thanks to Afatech who kindly provided information.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+
+#include "dvb_frontend.h"
+#include "af9013_priv.h"
+#include "af9013.h"
+
+int af9013_debug;
+
+struct af9013_state {
+ struct i2c_adapter *i2c;
+ struct dvb_frontend frontend;
+
+ struct af9013_config config;
+
+ u16 signal_strength;
+ u32 ber;
+ u32 ucblocks;
+ u16 snr;
+ u32 frequency;
+ unsigned long next_statistics_check;
+};
+
+static u8 regmask[8] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
+
+static int af9013_write_regs(struct af9013_state *state, u8 mbox, u16 reg,
+ u8 *val, u8 len)
+{
+ u8 buf[3+len];
+ struct i2c_msg msg = {
+ .addr = state->config.demod_address,
+ .flags = 0,
+ .len = sizeof(buf),
+ .buf = buf };
+
+ buf[0] = reg >> 8;
+ buf[1] = reg & 0xff;
+ buf[2] = mbox;
+ memcpy(&buf[3], val, len);
+
+ if (i2c_transfer(state->i2c, &msg, 1) != 1) {
+ warn("I2C write failed reg:%04x len:%d", reg, len);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int af9013_write_ofdm_regs(struct af9013_state *state, u16 reg, u8 *val,
+ u8 len)
+{
+ u8 mbox = (1 << 0)|(1 << 1)|((len - 1) << 2)|(0 << 6)|(0 << 7);
+ return af9013_write_regs(state, mbox, reg, val, len);
+}
+
+static int af9013_write_ofsm_regs(struct af9013_state *state, u16 reg, u8 *val,
+ u8 len)
+{
+ u8 mbox = (1 << 0)|(1 << 1)|((len - 1) << 2)|(1 << 6)|(1 << 7);
+ return af9013_write_regs(state, mbox, reg, val, len);
+}
+
+/* write single register */
+static int af9013_write_reg(struct af9013_state *state, u16 reg, u8 val)
+{
+ return af9013_write_ofdm_regs(state, reg, &val, 1);
+}
+
+/* read single register */
+static int af9013_read_reg(struct af9013_state *state, u16 reg, u8 *val)
+{
+ u8 obuf[3] = { reg >> 8, reg & 0xff, 0 };
+ u8 ibuf[1];
+ struct i2c_msg msg[2] = {
+ {
+ .addr = state->config.demod_address,
+ .flags = 0,
+ .len = sizeof(obuf),
+ .buf = obuf
+ }, {
+ .addr = state->config.demod_address,
+ .flags = I2C_M_RD,
+ .len = sizeof(ibuf),
+ .buf = ibuf
+ }
+ };
+
+ if (i2c_transfer(state->i2c, msg, 2) != 2) {
+ warn("I2C read failed reg:%04x", reg);
+ return -EREMOTEIO;
+ }
+ *val = ibuf[0];
+ return 0;
+}
+
+static int af9013_write_reg_bits(struct af9013_state *state, u16 reg, u8 pos,
+ u8 len, u8 val)
+{
+ int ret;
+ u8 tmp, mask;
+
+ ret = af9013_read_reg(state, reg, &tmp);
+ if (ret)
+ return ret;
+
+ mask = regmask[len - 1] << pos;
+ tmp = (tmp & ~mask) | ((val << pos) & mask);
+
+ return af9013_write_reg(state, reg, tmp);
+}
+
+static int af9013_read_reg_bits(struct af9013_state *state, u16 reg, u8 pos,
+ u8 len, u8 *val)
+{
+ int ret;
+ u8 tmp;
+
+ ret = af9013_read_reg(state, reg, &tmp);
+ if (ret)
+ return ret;
+ *val = (tmp >> pos) & regmask[len - 1];
+ return 0;
+}
+
+static int af9013_set_gpio(struct af9013_state *state, u8 gpio, u8 gpioval)
+{
+ int ret;
+ u8 pos;
+ u16 addr;
+ deb_info("%s: gpio:%d gpioval:%02x\n", __func__, gpio, gpioval);
+
+/* GPIO0 & GPIO1 0xd735
+ GPIO2 & GPIO3 0xd736 */
+
+ switch (gpio) {
+ case 0:
+ case 1:
+ addr = 0xd735;
+ break;
+ case 2:
+ case 3:
+ addr = 0xd736;
+ break;
+
+ default:
+ err("invalid gpio:%d\n", gpio);
+ ret = -EINVAL;
+ goto error;
+ };
+
+ switch (gpio) {
+ case 0:
+ case 2:
+ pos = 0;
+ break;
+ case 1:
+ case 3:
+ default:
+ pos = 4;
+ break;
+ };
+
+ ret = af9013_write_reg_bits(state, addr, pos, 4, gpioval);
+
+error:
+ return ret;
+}
+
+static u32 af913_div(u32 a, u32 b, u32 x)
+{
+ u32 r = 0, c = 0, i;
+ deb_info("%s: a:%d b:%d x:%d\n", __func__, a, b, x);
+
+ if (a > b) {
+ c = a / b;
+ a = a - c * b;
+ }
+
+ for (i = 0; i < x; i++) {
+ if (a >= b) {
+ r += 1;
+ a -= b;
+ }
+ a <<= 1;
+ r <<= 1;
+ }
+ r = (c << (u32)x) + r;
+
+ deb_info("%s: a:%d b:%d x:%d r:%d r:%x\n", __func__, a, b, x, r, r);
+ return r;
+}
+
+static int af9013_set_coeff(struct af9013_state *state, fe_bandwidth_t bw)
+{
+ int ret = 0;
+ u8 i = 0;
+ u8 buf[24];
+ u32 ns_coeff1_2048nu;
+ u32 ns_coeff1_8191nu;
+ u32 ns_coeff1_8192nu;
+ u32 ns_coeff1_8193nu;
+ u32 ns_coeff2_2k;
+ u32 ns_coeff2_8k;
+
+ deb_info("%s: adc_clock:%d bw:%d\n", __func__,
+ state->config.adc_clock, bw);
+
+ switch (state->config.adc_clock) {
+ case 28800: /* 28.800 MHz */
+ switch (bw) {
+ case BANDWIDTH_6_MHZ:
+ ns_coeff1_2048nu = 0x01e79e7a;
+ ns_coeff1_8191nu = 0x0079eb6e;
+ ns_coeff1_8192nu = 0x0079e79e;
+ ns_coeff1_8193nu = 0x0079e3cf;
+ ns_coeff2_2k = 0x00f3cf3d;
+ ns_coeff2_8k = 0x003cf3cf;
+ break;
+ case BANDWIDTH_7_MHZ:
+ ns_coeff1_2048nu = 0x0238e38e;
+ ns_coeff1_8191nu = 0x008e3d55;
+ ns_coeff1_8192nu = 0x008e38e4;
+ ns_coeff1_8193nu = 0x008e3472;
+ ns_coeff2_2k = 0x011c71c7;
+ ns_coeff2_8k = 0x00471c72;
+ break;
+ case BANDWIDTH_8_MHZ:
+ ns_coeff1_2048nu = 0x028a28a3;
+ ns_coeff1_8191nu = 0x00a28f3d;
+ ns_coeff1_8192nu = 0x00a28a29;
+ ns_coeff1_8193nu = 0x00a28514;
+ ns_coeff2_2k = 0x01451451;
+ ns_coeff2_8k = 0x00514514;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ break;
+ case 20480: /* 20.480 MHz */
+ switch (bw) {
+ case BANDWIDTH_6_MHZ:
+ ns_coeff1_2048nu = 0x02adb6dc;
+ ns_coeff1_8191nu = 0x00ab7313;
+ ns_coeff1_8192nu = 0x00ab6db7;
+ ns_coeff1_8193nu = 0x00ab685c;
+ ns_coeff2_2k = 0x0156db6e;
+ ns_coeff2_8k = 0x0055b6dc;
+ break;
+ case BANDWIDTH_7_MHZ:
+ ns_coeff1_2048nu = 0x03200001;
+ ns_coeff1_8191nu = 0x00c80640;
+ ns_coeff1_8192nu = 0x00c80000;
+ ns_coeff1_8193nu = 0x00c7f9c0;
+ ns_coeff2_2k = 0x01900000;
+ ns_coeff2_8k = 0x00640000;
+ break;
+ case BANDWIDTH_8_MHZ:
+ ns_coeff1_2048nu = 0x03924926;
+ ns_coeff1_8191nu = 0x00e4996e;
+ ns_coeff1_8192nu = 0x00e49249;
+ ns_coeff1_8193nu = 0x00e48b25;
+ ns_coeff2_2k = 0x01c92493;
+ ns_coeff2_8k = 0x00724925;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ break;
+ case 28000: /* 28.000 MHz */
+ switch (bw) {
+ case BANDWIDTH_6_MHZ:
+ ns_coeff1_2048nu = 0x01f58d10;
+ ns_coeff1_8191nu = 0x007d672f;
+ ns_coeff1_8192nu = 0x007d6344;
+ ns_coeff1_8193nu = 0x007d5f59;
+ ns_coeff2_2k = 0x00fac688;
+ ns_coeff2_8k = 0x003eb1a2;
+ break;
+ case BANDWIDTH_7_MHZ:
+ ns_coeff1_2048nu = 0x02492492;
+ ns_coeff1_8191nu = 0x00924db7;
+ ns_coeff1_8192nu = 0x00924925;
+ ns_coeff1_8193nu = 0x00924492;
+ ns_coeff2_2k = 0x01249249;
+ ns_coeff2_8k = 0x00492492;
+ break;
+ case BANDWIDTH_8_MHZ:
+ ns_coeff1_2048nu = 0x029cbc15;
+ ns_coeff1_8191nu = 0x00a7343f;
+ ns_coeff1_8192nu = 0x00a72f05;
+ ns_coeff1_8193nu = 0x00a729cc;
+ ns_coeff2_2k = 0x014e5e0a;
+ ns_coeff2_8k = 0x00539783;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ break;
+ case 25000: /* 25.000 MHz */
+ switch (bw) {
+ case BANDWIDTH_6_MHZ:
+ ns_coeff1_2048nu = 0x0231bcb5;
+ ns_coeff1_8191nu = 0x008c7391;
+ ns_coeff1_8192nu = 0x008c6f2d;
+ ns_coeff1_8193nu = 0x008c6aca;
+ ns_coeff2_2k = 0x0118de5b;
+ ns_coeff2_8k = 0x00463797;
+ break;
+ case BANDWIDTH_7_MHZ:
+ ns_coeff1_2048nu = 0x028f5c29;
+ ns_coeff1_8191nu = 0x00a3dc29;
+ ns_coeff1_8192nu = 0x00a3d70a;
+ ns_coeff1_8193nu = 0x00a3d1ec;
+ ns_coeff2_2k = 0x0147ae14;
+ ns_coeff2_8k = 0x0051eb85;
+ break;
+ case BANDWIDTH_8_MHZ:
+ ns_coeff1_2048nu = 0x02ecfb9d;
+ ns_coeff1_8191nu = 0x00bb44c1;
+ ns_coeff1_8192nu = 0x00bb3ee7;
+ ns_coeff1_8193nu = 0x00bb390d;
+ ns_coeff2_2k = 0x01767dce;
+ ns_coeff2_8k = 0x005d9f74;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ break;
+ default:
+ err("invalid xtal");
+ return -EINVAL;
+ }
+ if (ret) {
+ err("invalid bandwidth");
+ return ret;
+ }
+
+ buf[i++] = (u8) ((ns_coeff1_2048nu & 0x03000000) >> 24);
+ buf[i++] = (u8) ((ns_coeff1_2048nu & 0x00ff0000) >> 16);
+ buf[i++] = (u8) ((ns_coeff1_2048nu & 0x0000ff00) >> 8);
+ buf[i++] = (u8) ((ns_coeff1_2048nu & 0x000000ff));
+ buf[i++] = (u8) ((ns_coeff2_2k & 0x01c00000) >> 22);
+ buf[i++] = (u8) ((ns_coeff2_2k & 0x003fc000) >> 14);
+ buf[i++] = (u8) ((ns_coeff2_2k & 0x00003fc0) >> 6);
+ buf[i++] = (u8) ((ns_coeff2_2k & 0x0000003f));
+ buf[i++] = (u8) ((ns_coeff1_8191nu & 0x03000000) >> 24);
+ buf[i++] = (u8) ((ns_coeff1_8191nu & 0x00ffc000) >> 16);
+ buf[i++] = (u8) ((ns_coeff1_8191nu & 0x0000ff00) >> 8);
+ buf[i++] = (u8) ((ns_coeff1_8191nu & 0x000000ff));
+ buf[i++] = (u8) ((ns_coeff1_8192nu & 0x03000000) >> 24);
+ buf[i++] = (u8) ((ns_coeff1_8192nu & 0x00ffc000) >> 16);
+ buf[i++] = (u8) ((ns_coeff1_8192nu & 0x0000ff00) >> 8);
+ buf[i++] = (u8) ((ns_coeff1_8192nu & 0x000000ff));
+ buf[i++] = (u8) ((ns_coeff1_8193nu & 0x03000000) >> 24);
+ buf[i++] = (u8) ((ns_coeff1_8193nu & 0x00ffc000) >> 16);
+ buf[i++] = (u8) ((ns_coeff1_8193nu & 0x0000ff00) >> 8);
+ buf[i++] = (u8) ((ns_coeff1_8193nu & 0x000000ff));
+ buf[i++] = (u8) ((ns_coeff2_8k & 0x01c00000) >> 22);
+ buf[i++] = (u8) ((ns_coeff2_8k & 0x003fc000) >> 14);
+ buf[i++] = (u8) ((ns_coeff2_8k & 0x00003fc0) >> 6);
+ buf[i++] = (u8) ((ns_coeff2_8k & 0x0000003f));
+
+ deb_info("%s: coeff:", __func__);
+ debug_dump(buf, sizeof(buf), deb_info);
+
+ /* program */
+ for (i = 0; i < sizeof(buf); i++) {
+ ret = af9013_write_reg(state, 0xae00 + i, buf[i]);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int af9013_set_adc_ctrl(struct af9013_state *state)
+{
+ int ret;
+ u8 buf[3], tmp, i;
+ u32 adc_cw;
+
+ deb_info("%s: adc_clock:%d\n", __func__, state->config.adc_clock);
+
+ /* adc frequency type */
+ switch (state->config.adc_clock) {
+ case 28800: /* 28.800 MHz */
+ tmp = 0;
+ break;
+ case 20480: /* 20.480 MHz */
+ tmp = 1;
+ break;
+ case 28000: /* 28.000 MHz */
+ tmp = 2;
+ break;
+ case 25000: /* 25.000 MHz */
+ tmp = 3;
+ break;
+ default:
+ err("invalid xtal");
+ return -EINVAL;
+ }
+
+ adc_cw = af913_div(state->config.adc_clock*1000, 1000000ul, 19ul);
+
+ buf[0] = (u8) ((adc_cw & 0x000000ff));
+ buf[1] = (u8) ((adc_cw & 0x0000ff00) >> 8);
+ buf[2] = (u8) ((adc_cw & 0x00ff0000) >> 16);
+
+ deb_info("%s: adc_cw:", __func__);
+ debug_dump(buf, sizeof(buf), deb_info);
+
+ /* program */
+ for (i = 0; i < sizeof(buf); i++) {
+ ret = af9013_write_reg(state, 0xd180 + i, buf[i]);
+ if (ret)
+ goto error;
+ }
+ ret = af9013_write_reg_bits(state, 0x9bd2, 0, 4, tmp);
+error:
+ return ret;
+}
+
+static int af9013_set_freq_ctrl(struct af9013_state *state, fe_bandwidth_t bw)
+{
+ int ret;
+ u16 addr;
+ u8 buf[3], i, j;
+ u32 adc_freq, freq_cw;
+ s8 bfs_spec_inv;
+ int if_sample_freq;
+
+ for (j = 0; j < 3; j++) {
+ if (j == 0) {
+ addr = 0xd140; /* fcw normal */
+ bfs_spec_inv = state->config.rf_spec_inv ? -1 : 1;
+ } else if (j == 1) {
+ addr = 0x9be7; /* fcw dummy ram */
+ bfs_spec_inv = state->config.rf_spec_inv ? -1 : 1;
+ } else {
+ addr = 0x9bea; /* fcw inverted */
+ bfs_spec_inv = state->config.rf_spec_inv ? 1 : -1;
+ }
+
+ adc_freq = state->config.adc_clock * 1000;
+ if_sample_freq = state->config.tuner_if * 1000;
+
+ /* TDA18271 uses different sampling freq for every bw */
+ if (state->config.tuner == AF9013_TUNER_TDA18271) {
+ switch (bw) {
+ case BANDWIDTH_6_MHZ:
+ if_sample_freq = 3300000; /* 3.3 MHz */
+ break;
+ case BANDWIDTH_7_MHZ:
+ if_sample_freq = 3800000; /* 3.8 MHz */
+ break;
+ case BANDWIDTH_8_MHZ:
+ default:
+ if_sample_freq = 4300000; /* 4.3 MHz */
+ break;
+ }
+ }
+
+ while (if_sample_freq > (adc_freq / 2))
+ if_sample_freq = if_sample_freq - adc_freq;
+
+ if (if_sample_freq >= 0)
+ bfs_spec_inv = bfs_spec_inv * (-1);
+ else
+ if_sample_freq = if_sample_freq * (-1);
+
+ freq_cw = af913_div(if_sample_freq, adc_freq, 23ul);
+
+ if (bfs_spec_inv == -1)
+ freq_cw = 0x00800000 - freq_cw;
+
+ buf[0] = (u8) ((freq_cw & 0x000000ff));
+ buf[1] = (u8) ((freq_cw & 0x0000ff00) >> 8);
+ buf[2] = (u8) ((freq_cw & 0x007f0000) >> 16);
+
+
+ deb_info("%s: freq_cw:", __func__);
+ debug_dump(buf, sizeof(buf), deb_info);
+
+ /* program */
+ for (i = 0; i < sizeof(buf); i++) {
+ ret = af9013_write_reg(state, addr++, buf[i]);
+ if (ret)
+ goto error;
+ }
+ }
+error:
+ return ret;
+}
+
+static int af9013_set_ofdm_params(struct af9013_state *state,
+ struct dvb_ofdm_parameters *params, u8 *auto_mode)
+{
+ int ret;
+ u8 i, buf[3] = {0, 0, 0};
+ *auto_mode = 0; /* set if parameters are requested to auto set */
+
+ switch (params->transmission_mode) {
+ case TRANSMISSION_MODE_AUTO:
+ *auto_mode = 1;
+ case TRANSMISSION_MODE_2K:
+ break;
+ case TRANSMISSION_MODE_8K:
+ buf[0] |= (1 << 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params->guard_interval) {
+ case GUARD_INTERVAL_AUTO:
+ *auto_mode = 1;
+ case GUARD_INTERVAL_1_32:
+ break;
+ case GUARD_INTERVAL_1_16:
+ buf[0] |= (1 << 2);
+ break;
+ case GUARD_INTERVAL_1_8:
+ buf[0] |= (2 << 2);
+ break;
+ case GUARD_INTERVAL_1_4:
+ buf[0] |= (3 << 2);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params->hierarchy_information) {
+ case HIERARCHY_AUTO:
+ *auto_mode = 1;
+ case HIERARCHY_NONE:
+ break;
+ case HIERARCHY_1:
+ buf[0] |= (1 << 4);
+ break;
+ case HIERARCHY_2:
+ buf[0] |= (2 << 4);
+ break;
+ case HIERARCHY_4:
+ buf[0] |= (3 << 4);
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ switch (params->constellation) {
+ case QAM_AUTO:
+ *auto_mode = 1;
+ case QPSK:
+ break;
+ case QAM_16:
+ buf[1] |= (1 << 6);
+ break;
+ case QAM_64:
+ buf[1] |= (2 << 6);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Use HP. How and which case we can switch to LP? */
+ buf[1] |= (1 << 4);
+
+ switch (params->code_rate_HP) {
+ case FEC_AUTO:
+ *auto_mode = 1;
+ case FEC_1_2:
+ break;
+ case FEC_2_3:
+ buf[2] |= (1 << 0);
+ break;
+ case FEC_3_4:
+ buf[2] |= (2 << 0);
+ break;
+ case FEC_5_6:
+ buf[2] |= (3 << 0);
+ break;
+ case FEC_7_8:
+ buf[2] |= (4 << 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params->code_rate_LP) {
+ case FEC_AUTO:
+ /* if HIERARCHY_NONE and FEC_NONE then LP FEC is set to FEC_AUTO
+ by dvb_frontend.c for compatibility */
+ if (params->hierarchy_information != HIERARCHY_NONE)
+ *auto_mode = 1;
+ case FEC_1_2:
+ break;
+ case FEC_2_3:
+ buf[2] |= (1 << 3);
+ break;
+ case FEC_3_4:
+ buf[2] |= (2 << 3);
+ break;
+ case FEC_5_6:
+ buf[2] |= (3 << 3);
+ break;
+ case FEC_7_8:
+ buf[2] |= (4 << 3);
+ break;
+ case FEC_NONE:
+ if (params->hierarchy_information == HIERARCHY_AUTO)
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params->bandwidth) {
+ case BANDWIDTH_6_MHZ:
+ break;
+ case BANDWIDTH_7_MHZ:
+ buf[1] |= (1 << 2);
+ break;
+ case BANDWIDTH_8_MHZ:
+ buf[1] |= (2 << 2);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* program */
+ for (i = 0; i < sizeof(buf); i++) {
+ ret = af9013_write_reg(state, 0xd3c0 + i, buf[i]);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int af9013_reset(struct af9013_state *state, u8 sleep)
+{
+ int ret;
+ u8 tmp, i;
+ deb_info("%s\n", __func__);
+
+ /* enable OFDM reset */
+ ret = af9013_write_reg_bits(state, 0xd417, 4, 1, 1);
+ if (ret)
+ goto error;
+
+ /* start reset mechanism */
+ ret = af9013_write_reg(state, 0xaeff, 1);
+ if (ret)
+ goto error;
+
+ /* reset is done when bit 1 is set */
+ for (i = 0; i < 150; i++) {
+ ret = af9013_read_reg_bits(state, 0xd417, 1, 1, &tmp);
+ if (ret)
+ goto error;
+ if (tmp)
+ break; /* reset done */
+ msleep(10);
+ }
+ if (!tmp)
+ return -ETIMEDOUT;
+
+ /* don't clear reset when going to sleep */
+ if (!sleep) {
+ /* clear OFDM reset */
+ ret = af9013_write_reg_bits(state, 0xd417, 1, 1, 0);
+ if (ret)
+ goto error;
+
+ /* disable OFDM reset */
+ ret = af9013_write_reg_bits(state, 0xd417, 4, 1, 0);
+ }
+error:
+ return ret;
+}
+
+static int af9013_power_ctrl(struct af9013_state *state, u8 onoff)
+{
+ int ret;
+ deb_info("%s: onoff:%d\n", __func__, onoff);
+
+ if (onoff) {
+ /* power on */
+ ret = af9013_write_reg_bits(state, 0xd73a, 3, 1, 0);
+ if (ret)
+ goto error;
+ ret = af9013_write_reg_bits(state, 0xd417, 1, 1, 0);
+ if (ret)
+ goto error;
+ ret = af9013_write_reg_bits(state, 0xd417, 4, 1, 0);
+ } else {
+ /* power off */
+ ret = af9013_reset(state, 1);
+ if (ret)
+ goto error;
+ ret = af9013_write_reg_bits(state, 0xd73a, 3, 1, 1);
+ }
+error:
+ return ret;
+}
+
+static int af9013_lock_led(struct af9013_state *state, u8 onoff)
+{
+ deb_info("%s: onoff:%d\n", __func__, onoff);
+
+ return af9013_write_reg_bits(state, 0xd730, 0, 1, onoff);
+}
+
+static int af9013_set_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ u8 auto_mode; /* auto set TPS */
+
+ deb_info("%s: freq:%d bw:%d\n", __func__, params->frequency,
+ params->u.ofdm.bandwidth);
+
+ state->frequency = params->frequency;
+
+ /* program CFOE coefficients */
+ ret = af9013_set_coeff(state, params->u.ofdm.bandwidth);
+ if (ret)
+ goto error;
+
+ /* program frequency control */
+ ret = af9013_set_freq_ctrl(state, params->u.ofdm.bandwidth);
+ if (ret)
+ goto error;
+
+ /* clear TPS lock flag (inverted flag) */
+ ret = af9013_write_reg_bits(state, 0xd330, 3, 1, 1);
+ if (ret)
+ goto error;
+
+ /* clear MPEG2 lock flag */
+ ret = af9013_write_reg_bits(state, 0xd507, 6, 1, 0);
+ if (ret)
+ goto error;
+
+ /* empty channel function */
+ ret = af9013_write_reg_bits(state, 0x9bfe, 0, 1, 0);
+ if (ret)
+ goto error;
+
+ /* empty DVB-T channel function */
+ ret = af9013_write_reg_bits(state, 0x9bc2, 0, 1, 0);
+ if (ret)
+ goto error;
+
+ /* program tuner */
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe, params);
+
+ /* program TPS and bandwidth, check if auto mode needed */
+ ret = af9013_set_ofdm_params(state, ¶ms->u.ofdm, &auto_mode);
+ if (ret)
+ goto error;
+
+ if (auto_mode) {
+ /* clear easy mode flag */
+ ret = af9013_write_reg(state, 0xaefd, 0);
+ deb_info("%s: auto TPS\n", __func__);
+ } else {
+ /* set easy mode flag */
+ ret = af9013_write_reg(state, 0xaefd, 1);
+ if (ret)
+ goto error;
+ ret = af9013_write_reg(state, 0xaefe, 0);
+ deb_info("%s: manual TPS\n", __func__);
+ }
+ if (ret)
+ goto error;
+
+ /* everything is set, lets try to receive channel - OFSM GO! */
+ ret = af9013_write_reg(state, 0xffff, 0);
+ if (ret)
+ goto error;
+
+error:
+ return ret;
+}
+
+static int af9013_get_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ u8 i, buf[3];
+ deb_info("%s\n", __func__);
+
+ /* read TPS registers */
+ for (i = 0; i < 3; i++) {
+ ret = af9013_read_reg(state, 0xd3c0 + i, &buf[i]);
+ if (ret)
+ goto error;
+ }
+
+ switch ((buf[1] >> 6) & 3) {
+ case 0:
+ p->u.ofdm.constellation = QPSK;
+ break;
+ case 1:
+ p->u.ofdm.constellation = QAM_16;
+ break;
+ case 2:
+ p->u.ofdm.constellation = QAM_64;
+ break;
+ }
+
+ switch ((buf[0] >> 0) & 3) {
+ case 0:
+ p->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case 1:
+ p->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K;
+ }
+
+ switch ((buf[0] >> 2) & 3) {
+ case 0:
+ p->u.ofdm.guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case 1:
+ p->u.ofdm.guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case 2:
+ p->u.ofdm.guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ p->u.ofdm.guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ }
+
+ switch ((buf[0] >> 4) & 7) {
+ case 0:
+ p->u.ofdm.hierarchy_information = HIERARCHY_NONE;
+ break;
+ case 1:
+ p->u.ofdm.hierarchy_information = HIERARCHY_1;
+ break;
+ case 2:
+ p->u.ofdm.hierarchy_information = HIERARCHY_2;
+ break;
+ case 3:
+ p->u.ofdm.hierarchy_information = HIERARCHY_4;
+ break;
+ }
+
+ switch ((buf[2] >> 0) & 7) {
+ case 0:
+ p->u.ofdm.code_rate_HP = FEC_1_2;
+ break;
+ case 1:
+ p->u.ofdm.code_rate_HP = FEC_2_3;
+ break;
+ case 2:
+ p->u.ofdm.code_rate_HP = FEC_3_4;
+ break;
+ case 3:
+ p->u.ofdm.code_rate_HP = FEC_5_6;
+ break;
+ case 4:
+ p->u.ofdm.code_rate_HP = FEC_7_8;
+ break;
+ }
+
+ switch ((buf[2] >> 3) & 7) {
+ case 0:
+ p->u.ofdm.code_rate_LP = FEC_1_2;
+ break;
+ case 1:
+ p->u.ofdm.code_rate_LP = FEC_2_3;
+ break;
+ case 2:
+ p->u.ofdm.code_rate_LP = FEC_3_4;
+ break;
+ case 3:
+ p->u.ofdm.code_rate_LP = FEC_5_6;
+ break;
+ case 4:
+ p->u.ofdm.code_rate_LP = FEC_7_8;
+ break;
+ }
+
+ switch ((buf[1] >> 2) & 3) {
+ case 0:
+ p->u.ofdm.bandwidth = BANDWIDTH_6_MHZ;
+ break;
+ case 1:
+ p->u.ofdm.bandwidth = BANDWIDTH_7_MHZ;
+ break;
+ case 2:
+ p->u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
+ break;
+ }
+
+ p->inversion = INVERSION_AUTO;
+ p->frequency = state->frequency;
+
+error:
+ return ret;
+}
+
+static int af9013_update_ber_unc(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ u8 buf[3], i;
+ u32 error_bit_count = 0;
+ u32 total_bit_count = 0;
+ u32 abort_packet_count = 0;
+
+ state->ber = 0;
+
+ /* check if error bit count is ready */
+ ret = af9013_read_reg_bits(state, 0xd391, 4, 1, &buf[0]);
+ if (ret)
+ goto error;
+ if (!buf[0])
+ goto exit;
+
+ /* get RSD packet abort count */
+ for (i = 0; i < 2; i++) {
+ ret = af9013_read_reg(state, 0xd38a + i, &buf[i]);
+ if (ret)
+ goto error;
+ }
+ abort_packet_count = (buf[1] << 8) + buf[0];
+
+ /* get error bit count */
+ for (i = 0; i < 3; i++) {
+ ret = af9013_read_reg(state, 0xd387 + i, &buf[i]);
+ if (ret)
+ goto error;
+ }
+ error_bit_count = (buf[2] << 16) + (buf[1] << 8) + buf[0];
+ error_bit_count = error_bit_count - abort_packet_count * 8 * 8;
+
+ /* get used RSD counting period (10000 RSD packets used) */
+ for (i = 0; i < 2; i++) {
+ ret = af9013_read_reg(state, 0xd385 + i, &buf[i]);
+ if (ret)
+ goto error;
+ }
+ total_bit_count = (buf[1] << 8) + buf[0];
+ total_bit_count = total_bit_count - abort_packet_count;
+ total_bit_count = total_bit_count * 204 * 8;
+
+ if (total_bit_count)
+ state->ber = error_bit_count * 1000000000 / total_bit_count;
+
+ state->ucblocks += abort_packet_count;
+
+ deb_info("%s: err bits:%d total bits:%d abort count:%d\n", __func__,
+ error_bit_count, total_bit_count, abort_packet_count);
+
+ /* set BER counting range */
+ ret = af9013_write_reg(state, 0xd385, 10000 & 0xff);
+ if (ret)
+ goto error;
+ ret = af9013_write_reg(state, 0xd386, 10000 >> 8);
+ if (ret)
+ goto error;
+ /* reset and start BER counter */
+ ret = af9013_write_reg_bits(state, 0xd391, 4, 1, 1);
+ if (ret)
+ goto error;
+
+exit:
+error:
+ return ret;
+}
+
+static int af9013_update_snr(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ u8 buf[3], i, len;
+ u32 quant = 0;
+ struct snr_table *snr_table;
+
+ /* check if quantizer ready (for snr) */
+ ret = af9013_read_reg_bits(state, 0xd2e1, 3, 1, &buf[0]);
+ if (ret)
+ goto error;
+ if (buf[0]) {
+ /* quantizer ready - read it */
+ for (i = 0; i < 3; i++) {
+ ret = af9013_read_reg(state, 0xd2e3 + i, &buf[i]);
+ if (ret)
+ goto error;
+ }
+ quant = (buf[2] << 16) + (buf[1] << 8) + buf[0];
+
+ /* read current constellation */
+ ret = af9013_read_reg(state, 0xd3c1, &buf[0]);
+ if (ret)
+ goto error;
+
+ switch ((buf[0] >> 6) & 3) {
+ case 0:
+ len = ARRAY_SIZE(qpsk_snr_table);
+ snr_table = qpsk_snr_table;
+ break;
+ case 1:
+ len = ARRAY_SIZE(qam16_snr_table);
+ snr_table = qam16_snr_table;
+ break;
+ case 2:
+ len = ARRAY_SIZE(qam64_snr_table);
+ snr_table = qam64_snr_table;
+ break;
+ default:
+ len = 0;
+ break;
+ }
+
+ if (len) {
+ for (i = 0; i < len; i++) {
+ if (quant < snr_table[i].val) {
+ state->snr = snr_table[i].snr * 10;
+ break;
+ }
+ }
+ }
+
+ /* set quantizer super frame count */
+ ret = af9013_write_reg(state, 0xd2e2, 1);
+ if (ret)
+ goto error;
+
+ /* check quantizer availability */
+ for (i = 0; i < 10; i++) {
+ msleep(10);
+ ret = af9013_read_reg_bits(state, 0xd2e6, 0, 1,
+ &buf[0]);
+ if (ret)
+ goto error;
+ if (!buf[0])
+ break;
+ }
+
+ /* reset quantizer */
+ ret = af9013_write_reg_bits(state, 0xd2e1, 3, 1, 1);
+ if (ret)
+ goto error;
+ }
+
+error:
+ return ret;
+}
+
+static int af9013_update_signal_strength(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ u8 tmp0;
+ u8 rf_gain, rf_50, rf_80, if_gain, if_50, if_80;
+ int signal_strength;
+
+ deb_info("%s\n", __func__);
+
+ state->signal_strength = 0;
+
+ ret = af9013_read_reg_bits(state, 0x9bee, 0, 1, &tmp0);
+ if (ret)
+ goto error;
+ if (tmp0) {
+ ret = af9013_read_reg(state, 0x9bbd, &rf_50);
+ if (ret)
+ goto error;
+ ret = af9013_read_reg(state, 0x9bd0, &rf_80);
+ if (ret)
+ goto error;
+ ret = af9013_read_reg(state, 0x9be2, &if_50);
+ if (ret)
+ goto error;
+ ret = af9013_read_reg(state, 0x9be4, &if_80);
+ if (ret)
+ goto error;
+ ret = af9013_read_reg(state, 0xd07c, &rf_gain);
+ if (ret)
+ goto error;
+ ret = af9013_read_reg(state, 0xd07d, &if_gain);
+ if (ret)
+ goto error;
+ signal_strength = (0xffff / (9 * (rf_50 + if_50) - \
+ 11 * (rf_80 + if_80))) * (10 * (rf_gain + if_gain) - \
+ 11 * (rf_80 + if_80));
+ if (signal_strength < 0)
+ signal_strength = 0;
+ else if (signal_strength > 0xffff)
+ signal_strength = 0xffff;
+
+ state->signal_strength = signal_strength;
+ }
+
+error:
+ return ret;
+}
+
+static int af9013_update_statistics(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+
+ if (time_before(jiffies, state->next_statistics_check))
+ return 0;
+
+ /* set minimum statistic update interval */
+ state->next_statistics_check = jiffies + msecs_to_jiffies(1200);
+
+ ret = af9013_update_signal_strength(fe);
+ if (ret)
+ goto error;
+ ret = af9013_update_snr(fe);
+ if (ret)
+ goto error;
+ ret = af9013_update_ber_unc(fe);
+ if (ret)
+ goto error;
+
+error:
+ return ret;
+}
+
+static int af9013_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *fesettings)
+{
+ fesettings->min_delay_ms = 800;
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
+
+ return 0;
+}
+
+static int af9013_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret = 0;
+ u8 tmp;
+ *status = 0;
+
+ /* TPS lock */
+ ret = af9013_read_reg_bits(state, 0xd330, 3, 1, &tmp);
+ if (ret)
+ goto error;
+ if (tmp)
+ *status |= FE_HAS_VITERBI | FE_HAS_CARRIER | FE_HAS_SIGNAL;
+
+ /* MPEG2 lock */
+ ret = af9013_read_reg_bits(state, 0xd507, 6, 1, &tmp);
+ if (ret)
+ goto error;
+ if (tmp)
+ *status |= FE_HAS_SYNC | FE_HAS_LOCK;
+
+ if (!*status & FE_HAS_SIGNAL) {
+ /* AGC lock */
+ ret = af9013_read_reg_bits(state, 0xd1a0, 6, 1, &tmp);
+ if (ret)
+ goto error;
+ if (tmp)
+ *status |= FE_HAS_SIGNAL;
+ }
+
+ if (!*status & FE_HAS_CARRIER) {
+ /* CFO lock */
+ ret = af9013_read_reg_bits(state, 0xd333, 7, 1, &tmp);
+ if (ret)
+ goto error;
+ if (tmp)
+ *status |= FE_HAS_CARRIER;
+ }
+
+ if (!*status & FE_HAS_CARRIER) {
+ /* SFOE lock */
+ ret = af9013_read_reg_bits(state, 0xd334, 6, 1, &tmp);
+ if (ret)
+ goto error;
+ if (tmp)
+ *status |= FE_HAS_CARRIER;
+ }
+
+ ret = af9013_update_statistics(fe);
+
+error:
+ return ret;
+}
+
+
+static int af9013_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ ret = af9013_update_statistics(fe);
+ *ber = state->ber;
+ return ret;
+}
+
+static int af9013_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ ret = af9013_update_statistics(fe);
+ *strength = state->signal_strength;
+ return ret;
+}
+
+static int af9013_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ ret = af9013_update_statistics(fe);
+ *snr = state->snr;
+ return ret;
+}
+
+static int af9013_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ ret = af9013_update_statistics(fe);
+ *ucblocks = state->ucblocks;
+ return ret;
+}
+
+static int af9013_sleep(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ deb_info("%s\n", __func__);
+
+ ret = af9013_lock_led(state, 0);
+ if (ret)
+ goto error;
+
+ ret = af9013_power_ctrl(state, 0);
+error:
+ return ret;
+}
+
+static int af9013_init(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret, i, len;
+ u8 tmp0, tmp1;
+ struct regdesc *init;
+ deb_info("%s\n", __func__);
+
+ /* reset OFDM */
+ ret = af9013_reset(state, 0);
+ if (ret)
+ goto error;
+
+ /* power on */
+ ret = af9013_power_ctrl(state, 1);
+ if (ret)
+ goto error;
+
+ /* enable ADC */
+ ret = af9013_write_reg(state, 0xd73a, 0xa4);
+ if (ret)
+ goto error;
+
+ /* write API version to firmware */
+ for (i = 0; i < sizeof(state->config.api_version); i++) {
+ ret = af9013_write_reg(state, 0x9bf2 + i,
+ state->config.api_version[i]);
+ if (ret)
+ goto error;
+ }
+
+ /* program ADC control */
+ ret = af9013_set_adc_ctrl(state);
+ if (ret)
+ goto error;
+
+ /* set I2C master clock */
+ ret = af9013_write_reg(state, 0xd416, 0x14);
+ if (ret)
+ goto error;
+
+ /* set 16 embx */
+ ret = af9013_write_reg_bits(state, 0xd700, 1, 1, 1);
+ if (ret)
+ goto error;
+
+ /* set no trigger */
+ ret = af9013_write_reg_bits(state, 0xd700, 2, 1, 0);
+ if (ret)
+ goto error;
+
+ /* set read-update bit for constellation */
+ ret = af9013_write_reg_bits(state, 0xd371, 1, 1, 1);
+ if (ret)
+ goto error;
+
+ /* enable FEC monitor */
+ ret = af9013_write_reg_bits(state, 0xd392, 1, 1, 1);
+ if (ret)
+ goto error;
+
+ /* load OFSM settings */
+ deb_info("%s: load ofsm settings\n", __func__);
+ len = ARRAY_SIZE(ofsm_init);
+ init = ofsm_init;
+ for (i = 0; i < len; i++) {
+ ret = af9013_write_reg_bits(state, init[i].addr, init[i].pos,
+ init[i].len, init[i].val);
+ if (ret)
+ goto error;
+ }
+
+ /* load tuner specific settings */
+ deb_info("%s: load tuner specific settings\n", __func__);
+ switch (state->config.tuner) {
+ case AF9013_TUNER_MXL5003D:
+ len = ARRAY_SIZE(tuner_init_mxl5003d);
+ init = tuner_init_mxl5003d;
+ break;
+ case AF9013_TUNER_MXL5005D:
+ case AF9013_TUNER_MXL5005R:
+ len = ARRAY_SIZE(tuner_init_mxl5005);
+ init = tuner_init_mxl5005;
+ break;
+ case AF9013_TUNER_ENV77H11D5:
+ len = ARRAY_SIZE(tuner_init_env77h11d5);
+ init = tuner_init_env77h11d5;
+ break;
+ case AF9013_TUNER_MT2060:
+ len = ARRAY_SIZE(tuner_init_mt2060);
+ init = tuner_init_mt2060;
+ break;
+ case AF9013_TUNER_MC44S803:
+ len = ARRAY_SIZE(tuner_init_mc44s803);
+ init = tuner_init_mc44s803;
+ break;
+ case AF9013_TUNER_QT1010:
+ case AF9013_TUNER_QT1010A:
+ len = ARRAY_SIZE(tuner_init_qt1010);
+ init = tuner_init_qt1010;
+ break;
+ case AF9013_TUNER_MT2060_2:
+ len = ARRAY_SIZE(tuner_init_mt2060_2);
+ init = tuner_init_mt2060_2;
+ break;
+ case AF9013_TUNER_TDA18271:
+ len = ARRAY_SIZE(tuner_init_tda18271);
+ init = tuner_init_tda18271;
+ break;
+ case AF9013_TUNER_UNKNOWN:
+ default:
+ len = ARRAY_SIZE(tuner_init_unknown);
+ init = tuner_init_unknown;
+ break;
+ }
+
+ for (i = 0; i < len; i++) {
+ ret = af9013_write_reg_bits(state, init[i].addr, init[i].pos,
+ init[i].len, init[i].val);
+ if (ret)
+ goto error;
+ }
+
+ /* set TS mode */
+ deb_info("%s: setting ts mode\n", __func__);
+ tmp0 = 0; /* parallel mode */
+ tmp1 = 0; /* serial mode */
+ switch (state->config.output_mode) {
+ case AF9013_OUTPUT_MODE_PARALLEL:
+ tmp0 = 1;
+ break;
+ case AF9013_OUTPUT_MODE_SERIAL:
+ tmp1 = 1;
+ break;
+ case AF9013_OUTPUT_MODE_USB:
+ /* usb mode for AF9015 */
+ default:
+ break;
+ }
+ ret = af9013_write_reg_bits(state, 0xd500, 1, 1, tmp0); /* parallel */
+ if (ret)
+ goto error;
+ ret = af9013_write_reg_bits(state, 0xd500, 2, 1, tmp1); /* serial */
+ if (ret)
+ goto error;
+
+ /* enable lock led */
+ ret = af9013_lock_led(state, 1);
+ if (ret)
+ goto error;
+
+error:
+ return ret;
+}
+
+static struct dvb_frontend_ops af9013_ops;
+
+static int af9013_download_firmware(struct af9013_state *state)
+{
+ int i, len, packets, remainder, ret;
+ const struct firmware *fw;
+ u16 addr = 0x5100; /* firmware start address */
+ u16 checksum = 0;
+ u8 val;
+ u8 fw_params[4];
+ u8 *data;
+ u8 *fw_file = AF9013_DEFAULT_FIRMWARE;
+
+ msleep(100);
+ /* check whether firmware is already running */
+ ret = af9013_read_reg(state, 0x98be, &val);
+ if (ret)
+ goto error;
+ else
+ deb_info("%s: firmware status:%02x\n", __func__, val);
+
+ if (val == 0x0c) /* fw is running, no need for download */
+ goto exit;
+
+ info("found a '%s' in cold state, will try to load a firmware",
+ af9013_ops.info.name);
+
+ /* request the firmware, this will block and timeout */
+ ret = request_firmware(&fw, fw_file, &state->i2c->dev);
+ if (ret) {
+ err("did not find the firmware file. (%s) "
+ "Please see linux/Documentation/dvb/ for more details" \
+ " on firmware-problems. (%d)",
+ fw_file, ret);
+ goto error;
+ }
+
+ info("downloading firmware from file '%s'", fw_file);
+
+ /* calc checksum */
+ for (i = 0; i < fw->size; i++)
+ checksum += fw->data[i];
+
+ fw_params[0] = checksum >> 8;
+ fw_params[1] = checksum & 0xff;
+ fw_params[2] = fw->size >> 8;
+ fw_params[3] = fw->size & 0xff;
+
+ /* write fw checksum & size */
+ ret = af9013_write_ofsm_regs(state, 0x50fc,
+ fw_params, sizeof(fw_params));
+ if (ret)
+ goto error_release;
+
+ #define FW_PACKET_MAX_DATA 16
+
+ packets = fw->size / FW_PACKET_MAX_DATA;
+ remainder = fw->size % FW_PACKET_MAX_DATA;
+ len = FW_PACKET_MAX_DATA;
+ for (i = 0; i <= packets; i++) {
+ if (i == packets) /* set size of the last packet */
+ len = remainder;
+
+ data = (u8 *)(fw->data + i * FW_PACKET_MAX_DATA);
+ ret = af9013_write_ofsm_regs(state, addr, data, len);
+ addr += FW_PACKET_MAX_DATA;
+
+ if (ret) {
+ err("firmware download failed at %d with %d", i, ret);
+ goto error_release;
+ }
+ }
+
+ /* request boot firmware */
+ ret = af9013_write_reg(state, 0xe205, 1);
+ if (ret)
+ goto error_release;
+
+ for (i = 0; i < 15; i++) {
+ msleep(100);
+
+ /* check firmware status */
+ ret = af9013_read_reg(state, 0x98be, &val);
+ if (ret)
+ goto error_release;
+
+ deb_info("%s: firmware status:%02x\n", __func__, val);
+
+ if (val == 0x0c || val == 0x04) /* success or fail */
+ break;
+ }
+
+ if (val == 0x04) {
+ err("firmware did not run");
+ ret = -1;
+ } else if (val != 0x0c) {
+ err("firmware boot timeout");
+ ret = -1;
+ }
+
+error_release:
+ release_firmware(fw);
+error:
+exit:
+ if (!ret)
+ info("found a '%s' in warm state.", af9013_ops.info.name);
+ return ret;
+}
+
+static int af9013_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ int ret;
+ struct af9013_state *state = fe->demodulator_priv;
+ deb_info("%s: enable:%d\n", __func__, enable);
+
+ if (state->config.output_mode == AF9013_OUTPUT_MODE_USB)
+ ret = af9013_write_reg_bits(state, 0xd417, 3, 1, enable);
+ else
+ ret = af9013_write_reg_bits(state, 0xd607, 2, 1, enable);
+
+ return ret;
+}
+
+static void af9013_release(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops af9013_ops;
+
+struct dvb_frontend *af9013_attach(const struct af9013_config *config,
+ struct i2c_adapter *i2c)
+{
+ int ret;
+ struct af9013_state *state = NULL;
+ u8 buf[3], i;
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct af9013_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ /* setup the state */
+ state->i2c = i2c;
+ memcpy(&state->config, config, sizeof(struct af9013_config));
+
+ /* chip version */
+ ret = af9013_read_reg_bits(state, 0xd733, 4, 4, &buf[2]);
+ if (ret)
+ goto error;
+
+ /* ROM version */
+ for (i = 0; i < 2; i++) {
+ ret = af9013_read_reg(state, 0x116b + i, &buf[i]);
+ if (ret)
+ goto error;
+ }
+ deb_info("%s: chip version:%d ROM version:%d.%d\n", __func__,
+ buf[2], buf[0], buf[1]);
+
+ /* download firmware */
+ if (state->config.output_mode != AF9013_OUTPUT_MODE_USB) {
+ ret = af9013_download_firmware(state);
+ if (ret)
+ goto error;
+ }
+
+ /* firmware version */
+ for (i = 0; i < 3; i++) {
+ ret = af9013_read_reg(state, 0x5103 + i, &buf[i]);
+ if (ret)
+ goto error;
+ }
+ info("firmware version:%d.%d.%d", buf[0], buf[1], buf[2]);
+
+ /* settings for mp2if */
+ if (state->config.output_mode == AF9013_OUTPUT_MODE_USB) {
+ /* AF9015 split PSB to 1.5k + 0.5k */
+ ret = af9013_write_reg_bits(state, 0xd50b, 2, 1, 1);
+ } else {
+ /* AF9013 change the output bit to data7 */
+ ret = af9013_write_reg_bits(state, 0xd500, 3, 1, 1);
+ if (ret)
+ goto error;
+ /* AF9013 set mpeg to full speed */
+ ret = af9013_write_reg_bits(state, 0xd502, 4, 1, 1);
+ }
+ if (ret)
+ goto error;
+ ret = af9013_write_reg_bits(state, 0xd520, 4, 1, 1);
+ if (ret)
+ goto error;
+
+ /* set GPIOs */
+ for (i = 0; i < sizeof(state->config.gpio); i++) {
+ ret = af9013_set_gpio(state, i, state->config.gpio[i]);
+ if (ret)
+ goto error;
+ }
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &af9013_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+
+ return &state->frontend;
+error:
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL(af9013_attach);
+
+static struct dvb_frontend_ops af9013_ops = {
+ .info = {
+ .name = "Afatech AF9013 DVB-T",
+ .type = FE_OFDM,
+ .frequency_min = 174000000,
+ .frequency_max = 862000000,
+ .frequency_stepsize = 250000,
+ .frequency_tolerance = 0,
+ .caps =
+ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK | FE_CAN_QAM_16 |
+ FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO |
+ FE_CAN_RECOVER |
+ FE_CAN_MUTE_TS
+ },
+
+ .release = af9013_release,
+ .init = af9013_init,
+ .sleep = af9013_sleep,
+ .i2c_gate_ctrl = af9013_i2c_gate_ctrl,
+
+ .set_frontend = af9013_set_frontend,
+ .get_frontend = af9013_get_frontend,
+
+ .get_tune_settings = af9013_get_tune_settings,
+
+ .read_status = af9013_read_status,
+ .read_ber = af9013_read_ber,
+ .read_signal_strength = af9013_read_signal_strength,
+ .read_snr = af9013_read_snr,
+ .read_ucblocks = af9013_read_ucblocks,
+};
+
+module_param_named(debug, af9013_debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Afatech AF9013 DVB-T demodulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/af9013.h b/drivers/media/dvb/frontends/af9013.h
new file mode 100644
index 0000000..28b90c9
--- /dev/null
+++ b/drivers/media/dvb/frontends/af9013.h
@@ -0,0 +1,107 @@
+/*
+ * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ *
+ * Thanks to Afatech who kindly provided information.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef _AF9013_H_
+#define _AF9013_H_
+
+#include <linux/dvb/frontend.h>
+
+enum af9013_ts_mode {
+ AF9013_OUTPUT_MODE_PARALLEL,
+ AF9013_OUTPUT_MODE_SERIAL,
+ AF9013_OUTPUT_MODE_USB, /* only for AF9015 */
+};
+
+enum af9013_tuner {
+ AF9013_TUNER_MXL5003D = 3, /* MaxLinear */
+ AF9013_TUNER_MXL5005D = 13, /* MaxLinear */
+ AF9013_TUNER_MXL5005R = 30, /* MaxLinear */
+ AF9013_TUNER_ENV77H11D5 = 129, /* Panasonic */
+ AF9013_TUNER_MT2060 = 130, /* Microtune */
+ AF9013_TUNER_MC44S803 = 133, /* Freescale */
+ AF9013_TUNER_QT1010 = 134, /* Quantek */
+ AF9013_TUNER_UNKNOWN = 140, /* for can tuners ? */
+ AF9013_TUNER_MT2060_2 = 147, /* Microtune */
+ AF9013_TUNER_TDA18271 = 156, /* NXP */
+ AF9013_TUNER_QT1010A = 162, /* Quantek */
+};
+
+/* AF9013/5 GPIOs (mostly guessed)
+ demod#1-gpio#0 - set demod#2 i2c-addr for dual devices
+ demod#1-gpio#1 - xtal setting (?)
+ demod#1-gpio#3 - tuner#1
+ demod#2-gpio#0 - tuner#2
+ demod#2-gpio#1 - xtal setting (?)
+*/
+#define AF9013_GPIO_ON (1 << 0)
+#define AF9013_GPIO_EN (1 << 1)
+#define AF9013_GPIO_O (1 << 2)
+#define AF9013_GPIO_I (1 << 3)
+
+#define AF9013_GPIO_LO (AF9013_GPIO_ON|AF9013_GPIO_EN)
+#define AF9013_GPIO_HI (AF9013_GPIO_ON|AF9013_GPIO_EN|AF9013_GPIO_O)
+
+#define AF9013_GPIO_TUNER_ON (AF9013_GPIO_ON|AF9013_GPIO_EN)
+#define AF9013_GPIO_TUNER_OFF (AF9013_GPIO_ON|AF9013_GPIO_EN|AF9013_GPIO_O)
+
+struct af9013_config {
+ /* demodulator's I2C address */
+ u8 demod_address;
+
+ /* frequencies in kHz */
+ u32 adc_clock;
+
+ /* tuner ID */
+ u8 tuner;
+
+ /* tuner IF */
+ u16 tuner_if;
+
+ /* TS data output mode */
+ u8 output_mode:2;
+
+ /* RF spectrum inversion */
+ u8 rf_spec_inv:1;
+
+ /* API version */
+ u8 api_version[4];
+
+ /* GPIOs */
+ u8 gpio[4];
+};
+
+
+#if defined(CONFIG_DVB_AF9013) || \
+ (defined(CONFIG_DVB_AF9013_MODULE) && defined(MODULE))
+extern struct dvb_frontend *af9013_attach(const struct af9013_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *af9013_attach(
+const struct af9013_config *config, struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_AF9013 */
+
+#endif /* _AF9013_H_ */
diff --git a/drivers/media/dvb/frontends/af9013_priv.h b/drivers/media/dvb/frontends/af9013_priv.h
new file mode 100644
index 0000000..163e251
--- /dev/null
+++ b/drivers/media/dvb/frontends/af9013_priv.h
@@ -0,0 +1,869 @@
+/*
+ * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ *
+ * Thanks to Afatech who kindly provided information.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef _AF9013_PRIV_
+#define _AF9013_PRIV_
+
+#define LOG_PREFIX "af9013"
+extern int af9013_debug;
+
+#define dprintk(var, level, args...) \
+ do { if ((var & level)) printk(args); } while (0)
+
+#define debug_dump(b, l, func) {\
+ int loop_; \
+ for (loop_ = 0; loop_ < l; loop_++) \
+ func("%02x ", b[loop_]); \
+ func("\n");\
+}
+
+#define deb_info(args...) dprintk(af9013_debug, 0x01, args)
+
+#undef err
+#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg)
+#undef info
+#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
+#undef warn
+#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
+
+#define AF9013_DEFAULT_FIRMWARE "dvb-fe-af9013.fw"
+
+struct regdesc {
+ u16 addr;
+ u8 pos:4;
+ u8 len:4;
+ u8 val;
+};
+
+struct snr_table {
+ u32 val;
+ u8 snr;
+};
+
+/* QPSK SNR lookup table */
+static struct snr_table qpsk_snr_table[] = {
+ { 0x0b4771, 0 },
+ { 0x0c1aed, 1 },
+ { 0x0d0d27, 2 },
+ { 0x0e4d19, 3 },
+ { 0x0e5da8, 4 },
+ { 0x107097, 5 },
+ { 0x116975, 6 },
+ { 0x1252d9, 7 },
+ { 0x131fa4, 8 },
+ { 0x13d5e1, 9 },
+ { 0x148e53, 10 },
+ { 0x15358b, 11 },
+ { 0x15dd29, 12 },
+ { 0x168112, 13 },
+ { 0x170b61, 14 },
+ { 0xffffff, 15 },
+};
+
+/* QAM16 SNR lookup table */
+static struct snr_table qam16_snr_table[] = {
+ { 0x05eb62, 5 },
+ { 0x05fecf, 6 },
+ { 0x060b80, 7 },
+ { 0x062501, 8 },
+ { 0x064865, 9 },
+ { 0x069604, 10 },
+ { 0x06f356, 11 },
+ { 0x07706a, 12 },
+ { 0x0804d3, 13 },
+ { 0x089d1a, 14 },
+ { 0x093e3d, 15 },
+ { 0x09e35d, 16 },
+ { 0x0a7c3c, 17 },
+ { 0x0afaf8, 18 },
+ { 0x0b719d, 19 },
+ { 0xffffff, 20 },
+};
+
+/* QAM64 SNR lookup table */
+static struct snr_table qam64_snr_table[] = {
+ { 0x03109b, 12 },
+ { 0x0310d4, 13 },
+ { 0x031920, 14 },
+ { 0x0322d0, 15 },
+ { 0x0339fc, 16 },
+ { 0x0364a1, 17 },
+ { 0x038bcc, 18 },
+ { 0x03c7d3, 19 },
+ { 0x0408cc, 20 },
+ { 0x043bed, 21 },
+ { 0x048061, 22 },
+ { 0x04be95, 23 },
+ { 0x04fa7d, 24 },
+ { 0x052405, 25 },
+ { 0x05570d, 26 },
+ { 0xffffff, 27 },
+};
+
+static struct regdesc ofsm_init[] = {
+ { 0xd73a, 0, 8, 0xa1 },
+ { 0xd73b, 0, 8, 0x1f },
+ { 0xd73c, 4, 4, 0x0a },
+ { 0xd732, 3, 1, 0x00 },
+ { 0xd731, 4, 2, 0x03 },
+ { 0xd73d, 7, 1, 0x01 },
+ { 0xd740, 0, 1, 0x00 },
+ { 0xd740, 1, 1, 0x00 },
+ { 0xd740, 2, 1, 0x00 },
+ { 0xd740, 3, 1, 0x01 },
+ { 0xd3c1, 4, 1, 0x01 },
+ { 0xd3a2, 0, 8, 0x00 },
+ { 0xd3a3, 0, 8, 0x04 },
+ { 0xd305, 0, 8, 0x32 },
+ { 0xd306, 0, 8, 0x10 },
+ { 0xd304, 0, 8, 0x04 },
+ { 0x9112, 0, 1, 0x01 },
+ { 0x911d, 0, 1, 0x01 },
+ { 0x911a, 0, 1, 0x01 },
+ { 0x911b, 0, 1, 0x01 },
+ { 0x9bce, 0, 4, 0x02 },
+ { 0x9116, 0, 1, 0x01 },
+ { 0x9bd1, 0, 1, 0x01 },
+ { 0xd2e0, 0, 8, 0xd0 },
+ { 0xd2e9, 0, 4, 0x0d },
+ { 0xd38c, 0, 8, 0xfc },
+ { 0xd38d, 0, 8, 0x00 },
+ { 0xd38e, 0, 8, 0x7e },
+ { 0xd38f, 0, 8, 0x00 },
+ { 0xd390, 0, 8, 0x2f },
+ { 0xd145, 4, 1, 0x01 },
+ { 0xd1a9, 4, 1, 0x01 },
+ { 0xd158, 5, 3, 0x01 },
+ { 0xd159, 0, 6, 0x06 },
+ { 0xd167, 0, 8, 0x00 },
+ { 0xd168, 0, 4, 0x07 },
+ { 0xd1c3, 5, 3, 0x00 },
+ { 0xd1c4, 0, 6, 0x00 },
+ { 0xd1c5, 0, 7, 0x10 },
+ { 0xd1c6, 0, 3, 0x02 },
+ { 0xd080, 2, 5, 0x03 },
+ { 0xd081, 4, 4, 0x09 },
+ { 0xd098, 4, 4, 0x0f },
+ { 0xd098, 0, 4, 0x03 },
+ { 0xdbc0, 3, 1, 0x01 },
+ { 0xdbc0, 4, 1, 0x01 },
+ { 0xdbc7, 0, 8, 0x08 },
+ { 0xdbc8, 4, 4, 0x00 },
+ { 0xdbc9, 0, 5, 0x01 },
+ { 0xd280, 0, 8, 0xe0 },
+ { 0xd281, 0, 8, 0xff },
+ { 0xd282, 0, 8, 0xff },
+ { 0xd283, 0, 8, 0xc3 },
+ { 0xd284, 0, 8, 0xff },
+ { 0xd285, 0, 4, 0x01 },
+ { 0xd0f0, 0, 7, 0x1a },
+ { 0xd0f1, 4, 1, 0x01 },
+ { 0xd0f2, 0, 8, 0x0c },
+ { 0xd103, 0, 4, 0x08 },
+ { 0xd0f8, 0, 7, 0x20 },
+ { 0xd111, 5, 1, 0x00 },
+ { 0xd111, 6, 1, 0x00 },
+ { 0x910b, 0, 8, 0x0a },
+ { 0x9115, 0, 8, 0x02 },
+ { 0x910c, 0, 8, 0x02 },
+ { 0x910d, 0, 8, 0x08 },
+ { 0x910e, 0, 8, 0x0a },
+ { 0x9bf6, 0, 8, 0x06 },
+ { 0x9bf8, 0, 8, 0x02 },
+ { 0x9bf7, 0, 8, 0x05 },
+ { 0x9bf9, 0, 8, 0x0f },
+ { 0x9bfc, 0, 8, 0x13 },
+ { 0x9bd3, 0, 8, 0xff },
+ { 0x9bbe, 0, 1, 0x01 },
+ { 0x9bcc, 0, 1, 0x01 },
+};
+
+/* Panasonic ENV77H11D5 tuner init
+ AF9013_TUNER_ENV77H11D5 = 129 */
+static struct regdesc tuner_init_env77h11d5[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x03 },
+ { 0x9bbe, 0, 8, 0x01 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x00 },
+ { 0x9be3, 0, 8, 0x00 },
+ { 0xd015, 0, 8, 0x50 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x46 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0xdf },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x44 },
+ { 0xd007, 0, 2, 0x01 },
+ { 0xd00c, 0, 8, 0xeb },
+ { 0xd00d, 0, 2, 0x02 },
+ { 0xd00a, 0, 8, 0xf4 },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bba, 0, 8, 0xf9 },
+ { 0x9bc3, 0, 8, 0xdf },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0xeb },
+ { 0x9bc6, 0, 8, 0x02 },
+ { 0x9bc9, 0, 8, 0x52 },
+ { 0xd011, 0, 8, 0x3c },
+ { 0xd012, 0, 2, 0x01 },
+ { 0xd013, 0, 8, 0xf7 },
+ { 0xd014, 0, 2, 0x02 },
+ { 0xd040, 0, 8, 0x0b },
+ { 0xd041, 0, 2, 0x02 },
+ { 0xd042, 0, 8, 0x4d },
+ { 0xd043, 0, 2, 0x00 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 1, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+};
+
+/* Microtune MT2060 tuner init
+ AF9013_TUNER_MT2060 = 130 */
+static struct regdesc tuner_init_mt2060[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x07 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x00 },
+ { 0x9be3, 0, 8, 0x00 },
+ { 0x9bbe, 0, 1, 0x00 },
+ { 0x9bcc, 0, 1, 0x00 },
+ { 0x9bb9, 0, 8, 0x75 },
+ { 0x9bcd, 0, 8, 0x24 },
+ { 0x9bff, 0, 8, 0x30 },
+ { 0xd015, 0, 8, 0x46 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x46 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0x0f },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x32 },
+ { 0xd007, 0, 2, 0x01 },
+ { 0xd00c, 0, 8, 0x36 },
+ { 0xd00d, 0, 2, 0x03 },
+ { 0xd00a, 0, 8, 0x35 },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bc7, 0, 8, 0x07 },
+ { 0x9bc8, 0, 8, 0x90 },
+ { 0x9bc3, 0, 8, 0x0f },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0x36 },
+ { 0x9bc6, 0, 8, 0x03 },
+ { 0x9bba, 0, 8, 0xc9 },
+ { 0x9bc9, 0, 8, 0x79 },
+ { 0xd011, 0, 8, 0x10 },
+ { 0xd012, 0, 2, 0x01 },
+ { 0xd013, 0, 8, 0x45 },
+ { 0xd014, 0, 2, 0x03 },
+ { 0xd040, 0, 8, 0x98 },
+ { 0xd041, 0, 2, 0x00 },
+ { 0xd042, 0, 8, 0xcf },
+ { 0xd043, 0, 2, 0x03 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 1, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+ { 0x9bd0, 0, 8, 0xcc },
+ { 0x9be4, 0, 8, 0xa0 },
+ { 0x9bbd, 0, 8, 0x8e },
+ { 0x9be2, 0, 8, 0x4d },
+ { 0x9bee, 0, 1, 0x01 },
+};
+
+/* Microtune MT2060 tuner init
+ AF9013_TUNER_MT2060_2 = 147 */
+static struct regdesc tuner_init_mt2060_2[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x06 },
+ { 0x9bbe, 0, 8, 0x01 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0xd015, 0, 8, 0x46 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x46 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0x0f },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x32 },
+ { 0xd007, 0, 2, 0x01 },
+ { 0xd00c, 0, 8, 0x36 },
+ { 0xd00d, 0, 2, 0x03 },
+ { 0xd00a, 0, 8, 0x35 },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bc7, 0, 8, 0x07 },
+ { 0x9bc8, 0, 8, 0x90 },
+ { 0x9bc3, 0, 8, 0x0f },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0x36 },
+ { 0x9bc6, 0, 8, 0x03 },
+ { 0x9bba, 0, 8, 0xc9 },
+ { 0x9bc9, 0, 8, 0x79 },
+ { 0xd011, 0, 8, 0x10 },
+ { 0xd012, 0, 2, 0x01 },
+ { 0xd013, 0, 8, 0x45 },
+ { 0xd014, 0, 2, 0x03 },
+ { 0xd040, 0, 8, 0x98 },
+ { 0xd041, 0, 2, 0x00 },
+ { 0xd042, 0, 8, 0xcf },
+ { 0xd043, 0, 2, 0x03 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 8, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x96 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0xd045, 7, 1, 0x00 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+};
+
+/* MaxLinear MXL5003 tuner init
+ AF9013_TUNER_MXL5003D = 3 */
+static struct regdesc tuner_init_mxl5003d[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x09 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x00 },
+ { 0x9be3, 0, 8, 0x00 },
+ { 0x9bfc, 0, 8, 0x0f },
+ { 0x9bf6, 0, 8, 0x01 },
+ { 0x9bbe, 0, 1, 0x01 },
+ { 0xd015, 0, 8, 0x33 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x40 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0x0f },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x6c },
+ { 0xd007, 0, 2, 0x00 },
+ { 0xd00c, 0, 8, 0x3d },
+ { 0xd00d, 0, 2, 0x00 },
+ { 0xd00a, 0, 8, 0x45 },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bc7, 0, 8, 0x07 },
+ { 0x9bc8, 0, 8, 0x52 },
+ { 0x9bc3, 0, 8, 0x0f },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0x3d },
+ { 0x9bc6, 0, 8, 0x00 },
+ { 0x9bba, 0, 8, 0xa2 },
+ { 0x9bc9, 0, 8, 0xa0 },
+ { 0xd011, 0, 8, 0x56 },
+ { 0xd012, 0, 2, 0x00 },
+ { 0xd013, 0, 8, 0x50 },
+ { 0xd014, 0, 2, 0x00 },
+ { 0xd040, 0, 8, 0x56 },
+ { 0xd041, 0, 2, 0x00 },
+ { 0xd042, 0, 8, 0x50 },
+ { 0xd043, 0, 2, 0x00 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 8, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+};
+
+/* MaxLinear MXL5005 tuner init
+ AF9013_TUNER_MXL5005D = 13
+ AF9013_TUNER_MXL5005R = 30 */
+static struct regdesc tuner_init_mxl5005[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x07 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x01 },
+ { 0x9be3, 0, 8, 0x01 },
+ { 0x9bbe, 0, 1, 0x01 },
+ { 0x9bcc, 0, 1, 0x01 },
+ { 0x9bb9, 0, 8, 0x00 },
+ { 0x9bcd, 0, 8, 0x28 },
+ { 0x9bff, 0, 8, 0x24 },
+ { 0xd015, 0, 8, 0x40 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x40 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0x0f },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x73 },
+ { 0xd007, 0, 2, 0x01 },
+ { 0xd00c, 0, 8, 0xfa },
+ { 0xd00d, 0, 2, 0x01 },
+ { 0xd00a, 0, 8, 0xff },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bc7, 0, 8, 0x23 },
+ { 0x9bc8, 0, 8, 0x55 },
+ { 0x9bc3, 0, 8, 0x01 },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0xfa },
+ { 0x9bc6, 0, 8, 0x01 },
+ { 0x9bba, 0, 8, 0xff },
+ { 0x9bc9, 0, 8, 0xff },
+ { 0x9bd3, 0, 8, 0x95 },
+ { 0xd011, 0, 8, 0x70 },
+ { 0xd012, 0, 2, 0x01 },
+ { 0xd013, 0, 8, 0xfb },
+ { 0xd014, 0, 2, 0x01 },
+ { 0xd040, 0, 8, 0x70 },
+ { 0xd041, 0, 2, 0x01 },
+ { 0xd042, 0, 8, 0xfb },
+ { 0xd043, 0, 2, 0x01 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 1, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+ { 0x9bd0, 0, 8, 0x93 },
+ { 0x9be4, 0, 8, 0xfe },
+ { 0x9bbd, 0, 8, 0x63 },
+ { 0x9be2, 0, 8, 0xfe },
+ { 0x9bee, 0, 1, 0x01 },
+};
+
+/* Quantek QT1010 tuner init
+ AF9013_TUNER_QT1010 = 134
+ AF9013_TUNER_QT1010A = 162 */
+static struct regdesc tuner_init_qt1010[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x09 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x01 },
+ { 0x9be3, 0, 8, 0x01 },
+ { 0xd015, 0, 8, 0x46 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x46 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0x9bbe, 0, 1, 0x01 },
+ { 0x9bcc, 0, 1, 0x01 },
+ { 0x9bb9, 0, 8, 0x00 },
+ { 0x9bcd, 0, 8, 0x28 },
+ { 0x9bff, 0, 8, 0x20 },
+ { 0xd008, 0, 8, 0x0f },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x99 },
+ { 0xd007, 0, 2, 0x01 },
+ { 0xd00c, 0, 8, 0x0f },
+ { 0xd00d, 0, 2, 0x02 },
+ { 0xd00a, 0, 8, 0x50 },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bc7, 0, 8, 0x00 },
+ { 0x9bc8, 0, 8, 0x00 },
+ { 0x9bc3, 0, 8, 0x0f },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0x0f },
+ { 0x9bc6, 0, 8, 0x02 },
+ { 0x9bba, 0, 8, 0xc5 },
+ { 0x9bc9, 0, 8, 0xff },
+ { 0xd011, 0, 8, 0x58 },
+ { 0xd012, 0, 2, 0x02 },
+ { 0xd013, 0, 8, 0x89 },
+ { 0xd014, 0, 2, 0x01 },
+ { 0xd040, 0, 8, 0x58 },
+ { 0xd041, 0, 2, 0x02 },
+ { 0xd042, 0, 8, 0x89 },
+ { 0xd043, 0, 2, 0x01 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 1, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+ { 0x9bd0, 0, 8, 0xcd },
+ { 0x9be4, 0, 8, 0xbb },
+ { 0x9bbd, 0, 8, 0x93 },
+ { 0x9be2, 0, 8, 0x80 },
+ { 0x9bee, 0, 1, 0x01 },
+};
+
+/* Freescale MC44S803 tuner init
+ AF9013_TUNER_MC44S803 = 133 */
+static struct regdesc tuner_init_mc44s803[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x06 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x00 },
+ { 0x9be3, 0, 8, 0x00 },
+ { 0x9bf6, 0, 8, 0x01 },
+ { 0x9bf8, 0, 8, 0x02 },
+ { 0x9bf9, 0, 8, 0x02 },
+ { 0x9bfc, 0, 8, 0x1f },
+ { 0x9bbe, 0, 1, 0x01 },
+ { 0x9bcc, 0, 1, 0x01 },
+ { 0x9bb9, 0, 8, 0x00 },
+ { 0x9bcd, 0, 8, 0x24 },
+ { 0x9bff, 0, 8, 0x24 },
+ { 0xd015, 0, 8, 0x46 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x46 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0x01 },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x7b },
+ { 0xd007, 0, 2, 0x00 },
+ { 0xd00c, 0, 8, 0x7c },
+ { 0xd00d, 0, 2, 0x02 },
+ { 0xd00a, 0, 8, 0xfe },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bc7, 0, 8, 0x08 },
+ { 0x9bc8, 0, 8, 0x9a },
+ { 0x9bc3, 0, 8, 0x01 },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0x7c },
+ { 0x9bc6, 0, 8, 0x02 },
+ { 0x9bba, 0, 8, 0xfc },
+ { 0x9bc9, 0, 8, 0xaa },
+ { 0xd011, 0, 8, 0x6b },
+ { 0xd012, 0, 2, 0x00 },
+ { 0xd013, 0, 8, 0x88 },
+ { 0xd014, 0, 2, 0x02 },
+ { 0xd040, 0, 8, 0x6b },
+ { 0xd041, 0, 2, 0x00 },
+ { 0xd042, 0, 8, 0x7c },
+ { 0xd043, 0, 2, 0x02 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 1, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+ { 0x9bd0, 0, 8, 0x9e },
+ { 0x9be4, 0, 8, 0xff },
+ { 0x9bbd, 0, 8, 0x9e },
+ { 0x9be2, 0, 8, 0x25 },
+ { 0x9bee, 0, 1, 0x01 },
+ { 0xd73b, 3, 1, 0x00 },
+};
+
+/* unknown, probably for tin can tuner, tuner init
+ AF9013_TUNER_UNKNOWN = 140 */
+static struct regdesc tuner_init_unknown[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x02 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x01 },
+ { 0x9be3, 0, 8, 0x01 },
+ { 0xd1a0, 1, 1, 0x00 },
+ { 0x9bbe, 0, 1, 0x01 },
+ { 0x9bcc, 0, 1, 0x01 },
+ { 0x9bb9, 0, 8, 0x00 },
+ { 0x9bcd, 0, 8, 0x18 },
+ { 0x9bff, 0, 8, 0x2c },
+ { 0xd015, 0, 8, 0x46 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x46 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0xdf },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x44 },
+ { 0xd007, 0, 2, 0x01 },
+ { 0xd00c, 0, 8, 0x00 },
+ { 0xd00d, 0, 2, 0x02 },
+ { 0xd00a, 0, 8, 0xf6 },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bba, 0, 8, 0xf9 },
+ { 0x9bc8, 0, 8, 0xaa },
+ { 0x9bc3, 0, 8, 0xdf },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0x00 },
+ { 0x9bc6, 0, 8, 0x02 },
+ { 0x9bc9, 0, 8, 0xf0 },
+ { 0xd011, 0, 8, 0x3c },
+ { 0xd012, 0, 2, 0x01 },
+ { 0xd013, 0, 8, 0xf7 },
+ { 0xd014, 0, 2, 0x02 },
+ { 0xd040, 0, 8, 0x0b },
+ { 0xd041, 0, 2, 0x02 },
+ { 0xd042, 0, 8, 0x4d },
+ { 0xd043, 0, 2, 0x00 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 1, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+};
+
+/* NXP TDA18271 tuner init
+ AF9013_TUNER_TDA18271 = 156 */
+static struct regdesc tuner_init_tda18271[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x04 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x01 },
+ { 0x9be3, 0, 8, 0x01 },
+ { 0xd1a0, 1, 1, 0x00 },
+ { 0x9bbe, 0, 1, 0x01 },
+ { 0x9bcc, 0, 1, 0x01 },
+ { 0x9bb9, 0, 8, 0x00 },
+ { 0x9bcd, 0, 8, 0x18 },
+ { 0x9bff, 0, 8, 0x2c },
+ { 0xd015, 0, 8, 0x46 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x46 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0xdf },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x44 },
+ { 0xd007, 0, 2, 0x01 },
+ { 0xd00c, 0, 8, 0x00 },
+ { 0xd00d, 0, 2, 0x02 },
+ { 0xd00a, 0, 8, 0xf6 },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bba, 0, 8, 0xf9 },
+ { 0x9bc8, 0, 8, 0xaa },
+ { 0x9bc3, 0, 8, 0xdf },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0x00 },
+ { 0x9bc6, 0, 8, 0x02 },
+ { 0x9bc9, 0, 8, 0xf0 },
+ { 0xd011, 0, 8, 0x3c },
+ { 0xd012, 0, 2, 0x01 },
+ { 0xd013, 0, 8, 0xf7 },
+ { 0xd014, 0, 2, 0x02 },
+ { 0xd040, 0, 8, 0x0b },
+ { 0xd041, 0, 2, 0x02 },
+ { 0xd042, 0, 8, 0x4d },
+ { 0xd043, 0, 2, 0x00 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 1, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+ { 0x9bd0, 0, 8, 0xa8 },
+ { 0x9be4, 0, 8, 0x7f },
+ { 0x9bbd, 0, 8, 0xa8 },
+ { 0x9be2, 0, 8, 0x20 },
+ { 0x9bee, 0, 1, 0x01 },
+};
+
+#endif /* _AF9013_PRIV_ */
diff --git a/drivers/media/dvb/frontends/au8522.c b/drivers/media/dvb/frontends/au8522.c
index 0b82cc2..eabf9a6 100644
--- a/drivers/media/dvb/frontends/au8522.c
+++ b/drivers/media/dvb/frontends/au8522.c
@@ -40,6 +40,8 @@
u32 current_frequency;
fe_modulation_t current_modulation;
+ u32 fe_status;
+ unsigned int led_state;
};
static int debug;
@@ -538,11 +540,98 @@
return 0;
}
+static int au8522_led_gpio_enable(struct au8522_state *state, int onoff)
+{
+ struct au8522_led_config *led_config = state->config->led_cfg;
+ u8 val;
+
+ /* bail out if we cant control an LED */
+ if (!led_config || !led_config->gpio_output ||
+ !led_config->gpio_output_enable || !led_config->gpio_output_disable)
+ return 0;
+
+ val = au8522_readreg(state, 0x4000 |
+ (led_config->gpio_output & ~0xc000));
+ if (onoff) {
+ /* enable GPIO output */
+ val &= ~((led_config->gpio_output_enable >> 8) & 0xff);
+ val |= (led_config->gpio_output_enable & 0xff);
+ } else {
+ /* disable GPIO output */
+ val &= ~((led_config->gpio_output_disable >> 8) & 0xff);
+ val |= (led_config->gpio_output_disable & 0xff);
+ }
+ return au8522_writereg(state, 0x8000 |
+ (led_config->gpio_output & ~0xc000), val);
+}
+
+/* led = 0 | off
+ * led = 1 | signal ok
+ * led = 2 | signal strong
+ * led < 0 | only light led if leds are currently off
+ */
+static int au8522_led_ctrl(struct au8522_state *state, int led)
+{
+ struct au8522_led_config *led_config = state->config->led_cfg;
+ int i, ret = 0;
+
+ /* bail out if we cant control an LED */
+ if (!led_config || !led_config->gpio_leds ||
+ !led_config->num_led_states || !led_config->led_states)
+ return 0;
+
+ if (led < 0) {
+ /* if LED is already lit, then leave it as-is */
+ if (state->led_state)
+ return 0;
+ else
+ led *= -1;
+ }
+
+ /* toggle LED if changing state */
+ if (state->led_state != led) {
+ u8 val;
+
+ dprintk("%s: %d\n", __func__, led);
+
+ au8522_led_gpio_enable(state, 1);
+
+ val = au8522_readreg(state, 0x4000 |
+ (led_config->gpio_leds & ~0xc000));
+
+ /* start with all leds off */
+ for (i = 0; i < led_config->num_led_states; i++)
+ val &= ~led_config->led_states[i];
+
+ /* set selected LED state */
+ if (led < led_config->num_led_states)
+ val |= led_config->led_states[led];
+ else if (led_config->num_led_states)
+ val |=
+ led_config->led_states[led_config->num_led_states - 1];
+
+ ret = au8522_writereg(state, 0x8000 |
+ (led_config->gpio_leds & ~0xc000), val);
+ if (ret < 0)
+ return ret;
+
+ state->led_state = led;
+
+ if (led == 0)
+ au8522_led_gpio_enable(state, 0);
+ }
+
+ return 0;
+}
+
static int au8522_sleep(struct dvb_frontend *fe)
{
struct au8522_state *state = fe->demodulator_priv;
dprintk("%s()\n", __func__);
+ /* turn off led */
+ au8522_led_ctrl(state, 0);
+
state->current_frequency = 0;
return 0;
@@ -592,12 +681,53 @@
*status |= FE_HAS_CARRIER | FE_HAS_SIGNAL;
break;
}
+ state->fe_status = *status;
+
+ if (*status & FE_HAS_LOCK)
+ /* turn on LED, if it isn't on already */
+ au8522_led_ctrl(state, -1);
+ else
+ /* turn off LED */
+ au8522_led_ctrl(state, 0);
dprintk("%s() status 0x%08x\n", __func__, *status);
return 0;
}
+static int au8522_led_status(struct au8522_state *state, const u16 *snr)
+{
+ struct au8522_led_config *led_config = state->config->led_cfg;
+ int led;
+ u16 strong;
+
+ /* bail out if we cant control an LED */
+ if (!led_config)
+ return 0;
+
+ if (0 == (state->fe_status & FE_HAS_LOCK))
+ return au8522_led_ctrl(state, 0);
+ else if (state->current_modulation == QAM_256)
+ strong = led_config->qam256_strong;
+ else if (state->current_modulation == QAM_64)
+ strong = led_config->qam64_strong;
+ else /* (state->current_modulation == VSB_8) */
+ strong = led_config->vsb8_strong;
+
+ if (*snr >= strong)
+ led = 2;
+ else
+ led = 1;
+
+ if ((state->led_state) &&
+ (((strong < *snr) ? (*snr - strong) : (strong - *snr)) <= 10))
+ /* snr didn't change enough to bother
+ * changing the color of the led */
+ return 0;
+
+ return au8522_led_ctrl(state, led);
+}
+
static int au8522_read_snr(struct dvb_frontend *fe, u16 *snr)
{
struct au8522_state *state = fe->demodulator_priv;
@@ -621,6 +751,9 @@
au8522_readreg(state, 0x4311),
snr);
+ if (state->config->led_cfg)
+ au8522_led_status(state, snr);
+
return ret;
}
diff --git a/drivers/media/dvb/frontends/au8522.h b/drivers/media/dvb/frontends/au8522.h
index 595915a..7b94f55 100644
--- a/drivers/media/dvb/frontends/au8522.h
+++ b/drivers/media/dvb/frontends/au8522.h
@@ -30,6 +30,21 @@
AU8522_IF_3_25MHZ,
};
+struct au8522_led_config {
+ u16 vsb8_strong;
+ u16 qam64_strong;
+ u16 qam256_strong;
+
+ u16 gpio_output;
+ /* unset hi bits, set low bits */
+ u16 gpio_output_enable;
+ u16 gpio_output_disable;
+
+ u16 gpio_leds;
+ u8 *led_states;
+ unsigned int num_led_states;
+};
+
struct au8522_config {
/* the demodulator's i2c address */
u8 demod_address;
@@ -39,6 +54,8 @@
#define AU8522_DEMODLOCKING 1
u8 status_mode;
+ struct au8522_led_config *led_cfg;
+
enum au8522_if_freq vsb_if;
enum au8522_if_freq qam_if;
};
diff --git a/drivers/media/dvb/frontends/cx24110.h b/drivers/media/dvb/frontends/cx24110.h
index 1792adb..fdcceee 100644
--- a/drivers/media/dvb/frontends/cx24110.h
+++ b/drivers/media/dvb/frontends/cx24110.h
@@ -33,12 +33,17 @@
u8 demod_address;
};
-static inline int cx24110_pll_write(struct dvb_frontend *fe, u32 val) {
- int r = 0;
- u8 buf[] = {(u8) (val>>24), (u8) (val>>16), (u8) (val>>8)};
+static inline int cx24110_pll_write(struct dvb_frontend *fe, u32 val)
+{
+ u8 buf[] = {
+ (u8)((val >> 24) & 0xff),
+ (u8)((val >> 16) & 0xff),
+ (u8)((val >> 8) & 0xff)
+ };
+
if (fe->ops.write)
- r = fe->ops.write(fe, buf, 3);
- return r;
+ return fe->ops.write(fe, buf, 3);
+ return 0;
}
#if defined(CONFIG_DVB_CX24110) || (defined(CONFIG_DVB_CX24110_MODULE) && defined(MODULE))
diff --git a/drivers/media/dvb/frontends/cx24116.c b/drivers/media/dvb/frontends/cx24116.c
new file mode 100644
index 0000000..deb36f4
--- /dev/null
+++ b/drivers/media/dvb/frontends/cx24116.c
@@ -0,0 +1,1423 @@
+/*
+ Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver
+
+ Copyright (C) 2006-2008 Steven Toth <stoth@hauppauge.com>
+ Copyright (C) 2006-2007 Georg Acher
+ Copyright (C) 2007-2008 Darron Broad
+ March 2007
+ Fixed some bugs.
+ Added diseqc support.
+ Added corrected signal strength support.
+ August 2007
+ Sync with legacy version.
+ Some clean ups.
+ Copyright (C) 2008 Igor Liplianin
+ September, 9th 2008
+ Fixed locking on high symbol rates (>30000).
+ Implement MPEG initialization parameter.
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+
+#include "dvb_frontend.h"
+#include "cx24116.h"
+
+static int debug = 0;
+#define dprintk(args...) \
+ do { \
+ if (debug) printk ("cx24116: " args); \
+ } while (0)
+
+#define CX24116_DEFAULT_FIRMWARE "dvb-fe-cx24116.fw"
+#define CX24116_SEARCH_RANGE_KHZ 5000
+
+/* known registers */
+#define CX24116_REG_COMMAND (0x00) /* command args 0x00..0x1e */
+#define CX24116_REG_EXECUTE (0x1f) /* execute command */
+#define CX24116_REG_MAILBOX (0x96) /* FW or multipurpose mailbox? */
+#define CX24116_REG_RESET (0x20) /* reset status > 0 */
+#define CX24116_REG_SIGNAL (0x9e) /* signal low */
+#define CX24116_REG_SSTATUS (0x9d) /* signal high / status */
+#define CX24116_REG_QUALITY8 (0xa3)
+#define CX24116_REG_QSTATUS (0xbc)
+#define CX24116_REG_QUALITY0 (0xd5)
+#define CX24116_REG_BER0 (0xc9)
+#define CX24116_REG_BER8 (0xc8)
+#define CX24116_REG_BER16 (0xc7)
+#define CX24116_REG_BER24 (0xc6)
+#define CX24116_REG_UCB0 (0xcb)
+#define CX24116_REG_UCB8 (0xca)
+#define CX24116_REG_CLKDIV (0xf3)
+#define CX24116_REG_RATEDIV (0xf9)
+#define CX24116_REG_FECSTATUS (0x9c) /* configured fec (not tuned) or actual FEC (tuned) 1=1/2 2=2/3 etc */
+
+/* FECSTATUS bits */
+#define CX24116_FEC_FECMASK (0x1f) /* mask to determine configured fec (not tuned) or actual fec (tuned) */
+#define CX24116_FEC_DVBS (0x20) /* Select DVB-S demodulator, else DVB-S2 */
+#define CX24116_FEC_UNKNOWN (0x40) /* Unknown/unused */
+#define CX24116_FEC_PILOT (0x80) /* Pilot mode requested when tuning else always reset when tuned */
+
+/* arg buffer size */
+#define CX24116_ARGLEN (0x1e)
+
+/* rolloff */
+#define CX24116_ROLLOFF_020 (0x00)
+#define CX24116_ROLLOFF_025 (0x01)
+#define CX24116_ROLLOFF_035 (0x02)
+
+/* pilot bit */
+#define CX24116_PILOT_OFF (0x00)
+#define CX24116_PILOT_ON (0x40)
+
+/* signal status */
+#define CX24116_HAS_SIGNAL (0x01)
+#define CX24116_HAS_CARRIER (0x02)
+#define CX24116_HAS_VITERBI (0x04)
+#define CX24116_HAS_SYNCLOCK (0x08)
+#define CX24116_HAS_UNKNOWN1 (0x10)
+#define CX24116_HAS_UNKNOWN2 (0x20)
+#define CX24116_STATUS_MASK (0x3f)
+#define CX24116_SIGNAL_MASK (0xc0)
+
+#define CX24116_DISEQC_TONEOFF (0) /* toneburst never sent */
+#define CX24116_DISEQC_TONECACHE (1) /* toneburst cached */
+#define CX24116_DISEQC_MESGCACHE (2) /* message cached */
+
+/* arg offset for DiSEqC */
+#define CX24116_DISEQC_BURST (1)
+#define CX24116_DISEQC_ARG2_2 (2) /* unknown value=2 */
+#define CX24116_DISEQC_ARG3_0 (3) /* unknown value=0 */
+#define CX24116_DISEQC_ARG4_0 (4) /* unknown value=0 */
+#define CX24116_DISEQC_MSGLEN (5)
+#define CX24116_DISEQC_MSGOFS (6)
+
+/* DiSEqC burst */
+#define CX24116_DISEQC_MINI_A (0)
+#define CX24116_DISEQC_MINI_B (1)
+
+/* DiSEqC tone burst */
+static int toneburst = 1;
+
+/* SNR measurements */
+static int esno_snr = 0;
+
+enum cmds
+{
+ CMD_SET_VCO = 0x10,
+ CMD_TUNEREQUEST = 0x11,
+ CMD_MPEGCONFIG = 0x13,
+ CMD_TUNERINIT = 0x14,
+ CMD_BANDWIDTH = 0x15,
+ CMD_GETAGC = 0x19,
+ CMD_LNBCONFIG = 0x20,
+ CMD_LNBSEND = 0x21, /* Formerly CMD_SEND_DISEQC */
+ CMD_SET_TONEPRE = 0x22,
+ CMD_SET_TONE = 0x23,
+ CMD_UPDFWVERS = 0x35,
+ CMD_TUNERSLEEP = 0x36,
+ CMD_AGCCONTROL = 0x3b, /* Unknown */
+};
+
+/* The Demod/Tuner can't easily provide these, we cache them */
+struct cx24116_tuning
+{
+ u32 frequency;
+ u32 symbol_rate;
+ fe_spectral_inversion_t inversion;
+ fe_code_rate_t fec;
+
+ fe_modulation_t modulation;
+ fe_pilot_t pilot;
+ fe_rolloff_t rolloff;
+
+ /* Demod values */
+ u8 fec_val;
+ u8 fec_mask;
+ u8 inversion_val;
+ u8 pilot_val;
+ u8 rolloff_val;
+};
+
+/* Basic commands that are sent to the firmware */
+struct cx24116_cmd
+{
+ u8 len;
+ u8 args[CX24116_ARGLEN];
+};
+
+struct cx24116_state
+{
+ struct i2c_adapter* i2c;
+ const struct cx24116_config* config;
+
+ struct dvb_frontend frontend;
+
+ struct cx24116_tuning dcur;
+ struct cx24116_tuning dnxt;
+
+ u8 skip_fw_load;
+ u8 burst;
+ struct cx24116_cmd dsec_cmd;
+};
+
+static int cx24116_writereg(struct cx24116_state* state, int reg, int data)
+{
+ u8 buf[] = { reg, data };
+ struct i2c_msg msg = { .addr = state->config->demod_address,
+ .flags = 0, .buf = buf, .len = 2 };
+ int err;
+
+ if (debug>1)
+ printk("cx24116: %s: write reg 0x%02x, value 0x%02x\n",
+ __func__,reg, data);
+
+ if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
+ printk("%s: writereg error(err == %i, reg == 0x%02x,"
+ " value == 0x%02x)\n", __func__, err, reg, data);
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+/* Bulk byte writes to a single I2C address, for 32k firmware load */
+static int cx24116_writeregN(struct cx24116_state* state, int reg, u8 *data, u16 len)
+{
+ int ret = -EREMOTEIO;
+ struct i2c_msg msg;
+ u8 *buf;
+
+ buf = kmalloc(len + 1, GFP_KERNEL);
+ if (buf == NULL) {
+ printk("Unable to kmalloc\n");
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ *(buf) = reg;
+ memcpy(buf + 1, data, len);
+
+ msg.addr = state->config->demod_address;
+ msg.flags = 0;
+ msg.buf = buf;
+ msg.len = len + 1;
+
+ if (debug>1)
+ printk("cx24116: %s: write regN 0x%02x, len = %d\n",
+ __func__,reg, len);
+
+ if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1) {
+ printk("%s: writereg error(err == %i, reg == 0x%02x\n",
+ __func__, ret, reg);
+ ret = -EREMOTEIO;
+ }
+
+error:
+ kfree(buf);
+
+ return ret;
+}
+
+static int cx24116_readreg(struct cx24116_state* state, u8 reg)
+{
+ int ret;
+ u8 b0[] = { reg };
+ u8 b1[] = { 0 };
+ struct i2c_msg msg[] = {
+ { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2) {
+ printk("%s: reg=0x%x (error=%d)\n", __func__, reg, ret);
+ return ret;
+ }
+
+ if (debug>1)
+ printk("cx24116: read reg 0x%02x, value 0x%02x\n",reg, b1[0]);
+
+ return b1[0];
+}
+
+static int cx24116_set_inversion(struct cx24116_state* state, fe_spectral_inversion_t inversion)
+{
+ dprintk("%s(%d)\n", __func__, inversion);
+
+ switch (inversion) {
+ case INVERSION_OFF:
+ state->dnxt.inversion_val = 0x00;
+ break;
+ case INVERSION_ON:
+ state->dnxt.inversion_val = 0x04;
+ break;
+ case INVERSION_AUTO:
+ state->dnxt.inversion_val = 0x0C;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ state->dnxt.inversion = inversion;
+
+ return 0;
+}
+
+/*
+ * modfec (modulation and FEC)
+ * ===========================
+ *
+ * MOD FEC mask/val standard
+ * ---- -------- ----------- --------
+ * QPSK FEC_1_2 0x02 0x02+X DVB-S
+ * QPSK FEC_2_3 0x04 0x02+X DVB-S
+ * QPSK FEC_3_4 0x08 0x02+X DVB-S
+ * QPSK FEC_4_5 0x10 0x02+X DVB-S (?)
+ * QPSK FEC_5_6 0x20 0x02+X DVB-S
+ * QPSK FEC_6_7 0x40 0x02+X DVB-S
+ * QPSK FEC_7_8 0x80 0x02+X DVB-S
+ * QPSK FEC_8_9 0x01 0x02+X DVB-S (?) (NOT SUPPORTED?)
+ * QPSK AUTO 0xff 0x02+X DVB-S
+ *
+ * For DVB-S high byte probably represents FEC
+ * and low byte selects the modulator. The high
+ * byte is search range mask. Bit 5 may turn
+ * on DVB-S and remaining bits represent some
+ * kind of calibration (how/what i do not know).
+ *
+ * Eg.(2/3) szap "Zone Horror"
+ *
+ * mask/val = 0x04, 0x20
+ * status 1f | signal c3c0 | snr a333 | ber 00000098 | unc 00000000 | FE_HAS_LOCK
+ *
+ * mask/val = 0x04, 0x30
+ * status 1f | signal c3c0 | snr a333 | ber 00000000 | unc 00000000 | FE_HAS_LOCK
+ *
+ * After tuning FECSTATUS contains actual FEC
+ * in use numbered 1 through to 8 for 1/2 .. 2/3 etc
+ *
+ * NBC=NOT/NON BACKWARD COMPATIBLE WITH DVB-S (DVB-S2 only)
+ *
+ * NBC-QPSK FEC_1_2 0x00, 0x04 DVB-S2
+ * NBC-QPSK FEC_3_5 0x00, 0x05 DVB-S2
+ * NBC-QPSK FEC_2_3 0x00, 0x06 DVB-S2
+ * NBC-QPSK FEC_3_4 0x00, 0x07 DVB-S2
+ * NBC-QPSK FEC_4_5 0x00, 0x08 DVB-S2
+ * NBC-QPSK FEC_5_6 0x00, 0x09 DVB-S2
+ * NBC-QPSK FEC_8_9 0x00, 0x0a DVB-S2
+ * NBC-QPSK FEC_9_10 0x00, 0x0b DVB-S2
+ *
+ * NBC-8PSK FEC_3_5 0x00, 0x0c DVB-S2
+ * NBC-8PSK FEC_2_3 0x00, 0x0d DVB-S2
+ * NBC-8PSK FEC_3_4 0x00, 0x0e DVB-S2
+ * NBC-8PSK FEC_5_6 0x00, 0x0f DVB-S2
+ * NBC-8PSK FEC_8_9 0x00, 0x10 DVB-S2
+ * NBC-8PSK FEC_9_10 0x00, 0x11 DVB-S2
+ *
+ * For DVB-S2 low bytes selects both modulator
+ * and FEC. High byte is meaningless here. To
+ * set pilot, bit 6 (0x40) is set. When inspecting
+ * FECSTATUS bit 7 (0x80) represents the pilot
+ * selection whilst not tuned. When tuned, actual FEC
+ * in use is found in FECSTATUS as per above. Pilot
+ * value is reset.
+ */
+
+/* A table of modulation, fec and configuration bytes for the demod.
+ * Not all S2 mmodulation schemes are support and not all rates with
+ * a scheme are support. Especially, no auto detect when in S2 mode.
+ */
+struct cx24116_modfec {
+ fe_delivery_system_t delivery_system;
+ fe_modulation_t modulation;
+ fe_code_rate_t fec;
+ u8 mask; /* In DVBS mode this is used to autodetect */
+ u8 val; /* Passed to the firmware to indicate mode selection */
+} CX24116_MODFEC_MODES[] = {
+ /* QPSK. For unknown rates we set hardware to auto detect 0xfe 0x30 */
+
+ /*mod fec mask val */
+ { SYS_DVBS, QPSK, FEC_NONE, 0xfe, 0x30 },
+ { SYS_DVBS, QPSK, FEC_1_2, 0x02, 0x2e }, /* 00000010 00101110 */
+ { SYS_DVBS, QPSK, FEC_2_3, 0x04, 0x2f }, /* 00000100 00101111 */
+ { SYS_DVBS, QPSK, FEC_3_4, 0x08, 0x30 }, /* 00001000 00110000 */
+ { SYS_DVBS, QPSK, FEC_4_5, 0xfe, 0x30 }, /* 000?0000 ? */
+ { SYS_DVBS, QPSK, FEC_5_6, 0x20, 0x31 }, /* 00100000 00110001 */
+ { SYS_DVBS, QPSK, FEC_6_7, 0xfe, 0x30 }, /* 0?000000 ? */
+ { SYS_DVBS, QPSK, FEC_7_8, 0x80, 0x32 }, /* 10000000 00110010 */
+ { SYS_DVBS, QPSK, FEC_8_9, 0xfe, 0x30 }, /* 0000000? ? */
+ { SYS_DVBS, QPSK, FEC_AUTO, 0xfe, 0x30 },
+ /* NBC-QPSK */
+ { SYS_DVBS2, QPSK, FEC_1_2, 0x00, 0x04 },
+ { SYS_DVBS2, QPSK, FEC_3_5, 0x00, 0x05 },
+ { SYS_DVBS2, QPSK, FEC_2_3, 0x00, 0x06 },
+ { SYS_DVBS2, QPSK, FEC_3_4, 0x00, 0x07 },
+ { SYS_DVBS2, QPSK, FEC_4_5, 0x00, 0x08 },
+ { SYS_DVBS2, QPSK, FEC_5_6, 0x00, 0x09 },
+ { SYS_DVBS2, QPSK, FEC_8_9, 0x00, 0x0a },
+ { SYS_DVBS2, QPSK, FEC_9_10, 0x00, 0x0b },
+ /* 8PSK */
+ { SYS_DVBS2, PSK_8, FEC_3_5, 0x00, 0x0c },
+ { SYS_DVBS2, PSK_8, FEC_2_3, 0x00, 0x0d },
+ { SYS_DVBS2, PSK_8, FEC_3_4, 0x00, 0x0e },
+ { SYS_DVBS2, PSK_8, FEC_5_6, 0x00, 0x0f },
+ { SYS_DVBS2, PSK_8, FEC_8_9, 0x00, 0x10 },
+ { SYS_DVBS2, PSK_8, FEC_9_10, 0x00, 0x11 },
+ /*
+ * `val' can be found in the FECSTATUS register when tuning.
+ * FECSTATUS will give the actual FEC in use if tuning was successful.
+ */
+};
+
+static int cx24116_lookup_fecmod(struct cx24116_state* state,
+ fe_modulation_t m, fe_code_rate_t f)
+{
+ int i, ret = -EOPNOTSUPP;
+
+ dprintk("%s(0x%02x,0x%02x)\n", __func__, m, f);
+
+ for(i=0 ; i < sizeof(CX24116_MODFEC_MODES) / sizeof(struct cx24116_modfec) ; i++)
+ {
+ if( (m == CX24116_MODFEC_MODES[i].modulation) &&
+ (f == CX24116_MODFEC_MODES[i].fec) )
+ {
+ ret = i;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int cx24116_set_fec(struct cx24116_state* state, fe_modulation_t mod, fe_code_rate_t fec)
+{
+ int ret = 0;
+
+ dprintk("%s(0x%02x,0x%02x)\n", __func__, mod, fec);
+
+ ret = cx24116_lookup_fecmod(state, mod, fec);
+
+ if(ret < 0)
+ return ret;
+
+ state->dnxt.fec = fec;
+ state->dnxt.fec_val = CX24116_MODFEC_MODES[ret].val;
+ state->dnxt.fec_mask = CX24116_MODFEC_MODES[ret].mask;
+ dprintk("%s() mask/val = 0x%02x/0x%02x\n", __func__,
+ state->dnxt.fec_mask, state->dnxt.fec_val);
+
+ return 0;
+}
+
+static int cx24116_set_symbolrate(struct cx24116_state* state, u32 rate)
+{
+ dprintk("%s(%d)\n", __func__, rate);
+
+ /* check if symbol rate is within limits */
+ if ((rate > state->frontend.ops.info.symbol_rate_max) ||
+ (rate < state->frontend.ops.info.symbol_rate_min)) {
+ dprintk("%s() unsupported symbol_rate = %d\n", __func__, rate);
+ return -EOPNOTSUPP;
+ }
+
+ state->dnxt.symbol_rate = rate;
+ dprintk("%s() symbol_rate = %d\n", __func__, rate);
+
+ return 0;
+}
+
+static int cx24116_load_firmware (struct dvb_frontend* fe, const struct firmware *fw);
+
+static int cx24116_firmware_ondemand(struct dvb_frontend* fe)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+ const struct firmware *fw;
+ int ret = 0;
+
+ dprintk("%s()\n",__func__);
+
+ if (cx24116_readreg(state, 0x20) > 0)
+ {
+
+ if (state->skip_fw_load)
+ return 0;
+
+ /* Load firmware */
+ /* request the firmware, this will block until someone uploads it */
+ printk("%s: Waiting for firmware upload (%s)...\n", __func__, CX24116_DEFAULT_FIRMWARE);
+ ret = request_firmware(&fw, CX24116_DEFAULT_FIRMWARE, &state->i2c->dev);
+ printk("%s: Waiting for firmware upload(2)...\n", __func__);
+ if (ret) {
+ printk("%s: No firmware uploaded (timeout or file not found?)\n", __func__);
+ return ret;
+ }
+
+ /* Make sure we don't recurse back through here during loading */
+ state->skip_fw_load = 1;
+
+ ret = cx24116_load_firmware(fe, fw);
+ if (ret)
+ printk("%s: Writing firmware to device failed\n", __func__);
+
+ release_firmware(fw);
+
+ printk("%s: Firmware upload %s\n", __func__, ret == 0 ? "complete" : "failed");
+
+ /* Ensure firmware is always loaded if required */
+ state->skip_fw_load = 0;
+ }
+
+ return ret;
+}
+
+/* Take a basic firmware command structure, format it and forward it for processing */
+static int cx24116_cmd_execute(struct dvb_frontend* fe, struct cx24116_cmd *cmd)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+ int i, ret;
+
+ dprintk("%s()\n", __func__);
+
+ /* Load the firmware if required */
+ if ( (ret = cx24116_firmware_ondemand(fe)) != 0)
+ {
+ printk("%s(): Unable initialise the firmware\n", __func__);
+ return ret;
+ }
+
+ /* Write the command */
+ for(i = 0; i < cmd->len ; i++)
+ {
+ dprintk("%s: 0x%02x == 0x%02x\n", __func__, i, cmd->args[i]);
+ cx24116_writereg(state, i, cmd->args[i]);
+ }
+
+ /* Start execution and wait for cmd to terminate */
+ cx24116_writereg(state, CX24116_REG_EXECUTE, 0x01);
+ while( cx24116_readreg(state, CX24116_REG_EXECUTE) )
+ {
+ msleep(10);
+ if(i++ > 64)
+ {
+ /* Avoid looping forever if the firmware does no respond */
+ printk("%s() Firmware not responding\n", __func__);
+ return -EREMOTEIO;
+ }
+ }
+ return 0;
+}
+
+static int cx24116_load_firmware (struct dvb_frontend* fe, const struct firmware *fw)
+{
+ struct cx24116_state* state = fe->demodulator_priv;
+ struct cx24116_cmd cmd;
+ int i, ret;
+ unsigned char vers[4];
+
+ dprintk("%s\n", __func__);
+ dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n"
+ ,fw->size
+ ,fw->data[0]
+ ,fw->data[1]
+ ,fw->data[ fw->size-2 ]
+ ,fw->data[ fw->size-1 ]
+ );
+
+ /* Toggle 88x SRST pin to reset demod */
+ if (state->config->reset_device)
+ state->config->reset_device(fe);
+
+ /* Begin the firmware load process */
+ /* Prepare the demod, load the firmware, cleanup after load */
+
+ /* Init PLL */
+ cx24116_writereg(state, 0xE5, 0x00);
+ cx24116_writereg(state, 0xF1, 0x08);
+ cx24116_writereg(state, 0xF2, 0x13);
+
+ /* Start PLL */
+ cx24116_writereg(state, 0xe0, 0x03);
+ cx24116_writereg(state, 0xe0, 0x00);
+
+ /* Unknown */
+ cx24116_writereg(state, CX24116_REG_CLKDIV, 0x46);
+ cx24116_writereg(state, CX24116_REG_RATEDIV, 0x00);
+
+ /* Unknown */
+ cx24116_writereg(state, 0xF0, 0x03);
+ cx24116_writereg(state, 0xF4, 0x81);
+ cx24116_writereg(state, 0xF5, 0x00);
+ cx24116_writereg(state, 0xF6, 0x00);
+
+ /* write the entire firmware as one transaction */
+ cx24116_writeregN(state, 0xF7, fw->data, fw->size);
+
+ cx24116_writereg(state, 0xF4, 0x10);
+ cx24116_writereg(state, 0xF0, 0x00);
+ cx24116_writereg(state, 0xF8, 0x06);
+
+ /* Firmware CMD 10: VCO config */
+ cmd.args[0x00] = CMD_SET_VCO;
+ cmd.args[0x01] = 0x05;
+ cmd.args[0x02] = 0xdc;
+ cmd.args[0x03] = 0xda;
+ cmd.args[0x04] = 0xae;
+ cmd.args[0x05] = 0xaa;
+ cmd.args[0x06] = 0x04;
+ cmd.args[0x07] = 0x9d;
+ cmd.args[0x08] = 0xfc;
+ cmd.args[0x09] = 0x06;
+ cmd.len= 0x0a;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if (ret != 0)
+ return ret;
+
+ cx24116_writereg(state, CX24116_REG_SSTATUS, 0x00);
+
+ /* Firmware CMD 14: Tuner config */
+ cmd.args[0x00] = CMD_TUNERINIT;
+ cmd.args[0x01] = 0x00;
+ cmd.args[0x02] = 0x00;
+ cmd.len= 0x03;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if (ret != 0)
+ return ret;
+
+ cx24116_writereg(state, 0xe5, 0x00);
+
+ /* Firmware CMD 13: MPEG config */
+ cmd.args[0x00] = CMD_MPEGCONFIG;
+ cmd.args[0x01] = 0x01;
+ cmd.args[0x02] = 0x75;
+ cmd.args[0x03] = 0x00;
+ if (state->config->mpg_clk_pos_pol)
+ cmd.args[0x04] = state->config->mpg_clk_pos_pol;
+ else
+ cmd.args[0x04] = 0x02;
+ cmd.args[0x05] = 0x00;
+ cmd.len= 0x06;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if (ret != 0)
+ return ret;
+
+ /* Firmware CMD 35: Get firmware version */
+ cmd.args[0x00] = CMD_UPDFWVERS;
+ cmd.len= 0x02;
+ for(i=0; i<4; i++) {
+ cmd.args[0x01] = i;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if (ret != 0)
+ return ret;
+ vers[i]= cx24116_readreg(state, CX24116_REG_MAILBOX);
+ }
+ printk("%s: FW version %i.%i.%i.%i\n", __func__,
+ vers[0], vers[1], vers[2], vers[3]);
+
+ return 0;
+}
+
+static int cx24116_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+ /* The isl6421 module will override this function in the fops. */
+ dprintk("%s() This should never appear if the isl6421 module is loaded correctly\n",__func__);
+
+ return -EOPNOTSUPP;
+}
+
+static int cx24116_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+
+ int lock = cx24116_readreg(state, CX24116_REG_SSTATUS);
+
+ dprintk("%s: status = 0x%02x\n", __func__, lock);
+
+ *status = 0;
+
+ if (lock & CX24116_HAS_SIGNAL)
+ *status |= FE_HAS_SIGNAL;
+ if (lock & CX24116_HAS_CARRIER)
+ *status |= FE_HAS_CARRIER;
+ if (lock & CX24116_HAS_VITERBI)
+ *status |= FE_HAS_VITERBI;
+ if (lock & CX24116_HAS_SYNCLOCK)
+ *status |= FE_HAS_SYNC | FE_HAS_LOCK;
+
+ return 0;
+}
+
+static int cx24116_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+
+ dprintk("%s()\n", __func__);
+
+ *ber = ( cx24116_readreg(state, CX24116_REG_BER24) << 24 ) |
+ ( cx24116_readreg(state, CX24116_REG_BER16) << 16 ) |
+ ( cx24116_readreg(state, CX24116_REG_BER8 ) << 8 ) |
+ cx24116_readreg(state, CX24116_REG_BER0 );
+
+ return 0;
+}
+
+/* TODO Determine function and scale appropriately */
+static int cx24116_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+ struct cx24116_cmd cmd;
+ int ret;
+ u16 sig_reading;
+
+ dprintk("%s()\n", __func__);
+
+ /* Firmware CMD 19: Get AGC */
+ cmd.args[0x00] = CMD_GETAGC;
+ cmd.len= 0x01;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if (ret != 0)
+ return ret;
+
+ sig_reading = ( cx24116_readreg(state, CX24116_REG_SSTATUS) & CX24116_SIGNAL_MASK ) |
+ ( cx24116_readreg(state, CX24116_REG_SIGNAL) << 6 );
+ *signal_strength= 0 - sig_reading;
+
+ dprintk("%s: raw / cooked = 0x%04x / 0x%04x\n", __func__, sig_reading, *signal_strength);
+
+ return 0;
+}
+
+/* SNR (0..100)% = (sig & 0xf0) * 10 + (sig & 0x0f) * 10 / 16 */
+static int cx24116_read_snr_pct(struct dvb_frontend* fe, u16* snr)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+ u8 snr_reading;
+ static const u32 snr_tab[] = { /* 10 x Table (rounded up) */
+ 0x00000,0x0199A,0x03333,0x04ccD,0x06667,
+ 0x08000,0x0999A,0x0b333,0x0cccD,0x0e667,
+ 0x10000,0x1199A,0x13333,0x14ccD,0x16667,0x18000 };
+
+ dprintk("%s()\n", __func__);
+
+ snr_reading = cx24116_readreg(state, CX24116_REG_QUALITY0);
+
+ if(snr_reading >= 0xa0 /* 100% */)
+ *snr = 0xffff;
+ else
+ *snr = snr_tab [ ( snr_reading & 0xf0 ) >> 4 ] +
+ ( snr_tab [ ( snr_reading & 0x0f ) ] >> 4 );
+
+ dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__,
+ snr_reading, *snr);
+
+ return 0;
+}
+
+/* The reelbox patches show the value in the registers represents
+ * ESNO, from 0->30db (values 0->300). We provide this value by
+ * default.
+ */
+static int cx24116_read_snr_esno(struct dvb_frontend* fe, u16* snr)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+
+ dprintk("%s()\n", __func__);
+
+ *snr = cx24116_readreg(state, CX24116_REG_QUALITY8) << 8 |
+ cx24116_readreg(state, CX24116_REG_QUALITY0);
+
+ dprintk("%s: raw 0x%04x\n", __func__, *snr);
+
+ return 0;
+}
+
+static int cx24116_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+ if (esno_snr == 1)
+ return cx24116_read_snr_esno(fe, snr);
+ else
+ return cx24116_read_snr_pct(fe, snr);
+}
+
+static int cx24116_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+
+ dprintk("%s()\n", __func__);
+
+ *ucblocks = ( cx24116_readreg(state, CX24116_REG_UCB8) << 8 ) |
+ cx24116_readreg(state, CX24116_REG_UCB0);
+
+ return 0;
+}
+
+/* Overwrite the current tuning params, we are about to tune */
+static void cx24116_clone_params(struct dvb_frontend* fe)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+ memcpy(&state->dcur, &state->dnxt, sizeof(state->dcur));
+}
+
+/* Wait for LNB */
+static int cx24116_wait_for_lnb(struct dvb_frontend* fe)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+ int i;
+
+ dprintk("%s() qstatus = 0x%02x\n", __func__,
+ cx24116_readreg(state, CX24116_REG_QSTATUS));
+
+ /* Wait for up to 300 ms */
+ for(i = 0; i < 30 ; i++) {
+ if (cx24116_readreg(state, CX24116_REG_QSTATUS) & 0x20)
+ return 0;
+ msleep(10);
+ }
+
+ dprintk("%s(): LNB not ready\n", __func__);
+
+ return -ETIMEDOUT; /* -EBUSY ? */
+}
+
+static int cx24116_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+ struct cx24116_cmd cmd;
+ int ret;
+
+ dprintk("%s(%d)\n", __func__, tone);
+ if ( (tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF) ) {
+ printk("%s: Invalid, tone=%d\n", __func__, tone);
+ return -EINVAL;
+ }
+
+ /* Wait for LNB ready */
+ ret = cx24116_wait_for_lnb(fe);
+ if(ret != 0)
+ return ret;
+
+ /* Min delay time after DiSEqC send */
+ msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */
+
+ /* This is always done before the tone is set */
+ cmd.args[0x00] = CMD_SET_TONEPRE;
+ cmd.args[0x01] = 0x00;
+ cmd.len= 0x02;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if (ret != 0)
+ return ret;
+
+ /* Now we set the tone */
+ cmd.args[0x00] = CMD_SET_TONE;
+ cmd.args[0x01] = 0x00;
+ cmd.args[0x02] = 0x00;
+
+ switch (tone) {
+ case SEC_TONE_ON:
+ dprintk("%s: setting tone on\n", __func__);
+ cmd.args[0x03] = 0x01;
+ break;
+ case SEC_TONE_OFF:
+ dprintk("%s: setting tone off\n",__func__);
+ cmd.args[0x03] = 0x00;
+ break;
+ }
+ cmd.len= 0x04;
+
+ /* Min delay time before DiSEqC send */
+ msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */
+
+ return cx24116_cmd_execute(fe, &cmd);
+}
+
+/* Initialise DiSEqC */
+static int cx24116_diseqc_init(struct dvb_frontend* fe)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+ struct cx24116_cmd cmd;
+ int ret;
+
+ /* Firmware CMD 20: LNB/DiSEqC config */
+ cmd.args[0x00] = CMD_LNBCONFIG;
+ cmd.args[0x01] = 0x00;
+ cmd.args[0x02] = 0x10;
+ cmd.args[0x03] = 0x00;
+ cmd.args[0x04] = 0x8f;
+ cmd.args[0x05] = 0x28;
+ cmd.args[0x06] = (toneburst == CX24116_DISEQC_TONEOFF) ? 0x00 : 0x01;
+ cmd.args[0x07] = 0x01;
+ cmd.len= 0x08;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if (ret != 0)
+ return ret;
+
+ /* Prepare a DiSEqC command */
+ state->dsec_cmd.args[0x00] = CMD_LNBSEND;
+
+ /* DiSEqC burst */
+ state->dsec_cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_A;
+
+ /* Unknown */
+ state->dsec_cmd.args[CX24116_DISEQC_ARG2_2] = 0x02;
+ state->dsec_cmd.args[CX24116_DISEQC_ARG3_0] = 0x00;
+ state->dsec_cmd.args[CX24116_DISEQC_ARG4_0] = 0x00; /* Continuation flag? */
+
+ /* DiSEqC message length */
+ state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = 0x00;
+
+ /* Command length */
+ state->dsec_cmd.len= CX24116_DISEQC_MSGOFS;
+
+ return 0;
+}
+
+/* Send DiSEqC message with derived burst (hack) || previous burst */
+static int cx24116_send_diseqc_msg(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *d)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+ int i, ret;
+
+ /* Dump DiSEqC message */
+ if (debug) {
+ printk("cx24116: %s(", __func__);
+ for(i = 0 ; i < d->msg_len ;) {
+ printk("0x%02x", d->msg[i]);
+ if(++i < d->msg_len)
+ printk(", ");
+ }
+ printk(") toneburst=%d\n", toneburst);
+ }
+
+ /* Validate length */
+ if(d->msg_len > (CX24116_ARGLEN - CX24116_DISEQC_MSGOFS))
+ return -EINVAL;
+
+ /* DiSEqC message */
+ for (i = 0; i < d->msg_len; i++)
+ state->dsec_cmd.args[CX24116_DISEQC_MSGOFS + i] = d->msg[i];
+
+ /* DiSEqC message length */
+ state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = d->msg_len;
+
+ /* Command length */
+ state->dsec_cmd.len= CX24116_DISEQC_MSGOFS + state->dsec_cmd.args[CX24116_DISEQC_MSGLEN];
+
+ /* DiSEqC toneburst */
+ if(toneburst == CX24116_DISEQC_MESGCACHE)
+ /* Message is cached */
+ return 0;
+
+ else if(toneburst == CX24116_DISEQC_TONEOFF)
+ /* Message is sent without burst */
+ state->dsec_cmd.args[CX24116_DISEQC_BURST] = 0;
+
+ else if(toneburst == CX24116_DISEQC_TONECACHE) {
+ /*
+ * Message is sent with derived else cached burst
+ *
+ * WRITE PORT GROUP COMMAND 38
+ *
+ * 0/A/A: E0 10 38 F0..F3
+ * 1/B/B: E0 10 38 F4..F7
+ * 2/C/A: E0 10 38 F8..FB
+ * 3/D/B: E0 10 38 FC..FF
+ *
+ * databyte[3]= 8421:8421
+ * ABCD:WXYZ
+ * CLR :SET
+ *
+ * WX= PORT SELECT 0..3 (X=TONEBURST)
+ * Y = VOLTAGE (0=13V, 1=18V)
+ * Z = BAND (0=LOW, 1=HIGH(22K))
+ */
+ if(d->msg_len >= 4 && d->msg[2] == 0x38)
+ state->dsec_cmd.args[CX24116_DISEQC_BURST] = ((d->msg[3] & 4) >> 2);
+ if(debug)
+ dprintk("%s burst=%d\n", __func__, state->dsec_cmd.args[CX24116_DISEQC_BURST]);
+ }
+
+ /* Wait for LNB ready */
+ ret = cx24116_wait_for_lnb(fe);
+ if(ret != 0)
+ return ret;
+
+ /* Wait for voltage/min repeat delay */
+ msleep(100);
+
+ /* Command */
+ ret = cx24116_cmd_execute(fe, &state->dsec_cmd);
+ if(ret != 0)
+ return ret;
+ /*
+ * Wait for send
+ *
+ * Eutelsat spec:
+ * >15ms delay + (XXX determine if FW does this, see set_tone)
+ * 13.5ms per byte +
+ * >15ms delay +
+ * 12.5ms burst +
+ * >15ms delay (XXX determine if FW does this, see set_tone)
+ */
+ msleep( (state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) + ((toneburst == CX24116_DISEQC_TONEOFF) ? 30 : 60) );
+
+ return 0;
+}
+
+/* Send DiSEqC burst */
+static int cx24116_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t burst)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+ int ret;
+
+ dprintk("%s(%d) toneburst=%d\n",__func__, burst, toneburst);
+
+ /* DiSEqC burst */
+ if (burst == SEC_MINI_A)
+ state->dsec_cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_A;
+ else if(burst == SEC_MINI_B)
+ state->dsec_cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_B;
+ else
+ return -EINVAL;
+
+ /* DiSEqC toneburst */
+ if(toneburst != CX24116_DISEQC_MESGCACHE)
+ /* Burst is cached */
+ return 0;
+
+ /* Burst is to be sent with cached message */
+
+ /* Wait for LNB ready */
+ ret = cx24116_wait_for_lnb(fe);
+ if(ret != 0)
+ return ret;
+
+ /* Wait for voltage/min repeat delay */
+ msleep(100);
+
+ /* Command */
+ ret = cx24116_cmd_execute(fe, &state->dsec_cmd);
+ if(ret != 0)
+ return ret;
+
+ /*
+ * Wait for send
+ *
+ * Eutelsat spec:
+ * >15ms delay + (XXX determine if FW does this, see set_tone)
+ * 13.5ms per byte +
+ * >15ms delay +
+ * 12.5ms burst +
+ * >15ms delay (XXX determine if FW does this, see set_tone)
+ */
+ msleep( (state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) + 60 );
+
+ return 0;
+}
+
+static void cx24116_release(struct dvb_frontend* fe)
+{
+ struct cx24116_state* state = fe->demodulator_priv;
+ dprintk("%s\n",__func__);
+ kfree(state);
+}
+
+static struct dvb_frontend_ops cx24116_ops;
+
+struct dvb_frontend* cx24116_attach(const struct cx24116_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct cx24116_state* state = NULL;
+ int ret;
+
+ dprintk("%s\n",__func__);
+
+ /* allocate memory for the internal state */
+ state = kmalloc(sizeof(struct cx24116_state), GFP_KERNEL);
+ if (state == NULL) {
+ printk("Unable to kmalloc\n");
+ goto error1;
+ }
+
+ /* setup the state */
+ memset(state, 0, sizeof(struct cx24116_state));
+
+ state->config = config;
+ state->i2c = i2c;
+
+ /* check if the demod is present */
+ ret = (cx24116_readreg(state, 0xFF) << 8) | cx24116_readreg(state, 0xFE);
+ if (ret != 0x0501) {
+ printk("Invalid probe, probably not a CX24116 device\n");
+ goto error2;
+ }
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &cx24116_ops, sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+
+error2: kfree(state);
+error1: return NULL;
+}
+/*
+ * Initialise or wake up device
+ *
+ * Power config will reset and load initial firmware if required
+ */
+static int cx24116_initfe(struct dvb_frontend* fe)
+{
+ struct cx24116_state* state = fe->demodulator_priv;
+ struct cx24116_cmd cmd;
+ int ret;
+
+ dprintk("%s()\n",__func__);
+
+ /* Power on */
+ cx24116_writereg(state, 0xe0, 0);
+ cx24116_writereg(state, 0xe1, 0);
+ cx24116_writereg(state, 0xea, 0);
+
+ /* Firmware CMD 36: Power config */
+ cmd.args[0x00] = CMD_TUNERSLEEP;
+ cmd.args[0x01] = 0;
+ cmd.len= 0x02;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if(ret != 0)
+ return ret;
+
+ return cx24116_diseqc_init(fe);
+}
+
+/*
+ * Put device to sleep
+ */
+static int cx24116_sleep(struct dvb_frontend* fe)
+{
+ struct cx24116_state* state = fe->demodulator_priv;
+ struct cx24116_cmd cmd;
+ int ret;
+
+ dprintk("%s()\n",__func__);
+
+ /* Firmware CMD 36: Power config */
+ cmd.args[0x00] = CMD_TUNERSLEEP;
+ cmd.args[0x01] = 1;
+ cmd.len= 0x02;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if(ret != 0)
+ return ret;
+
+ /* Power off (Shutdown clocks) */
+ cx24116_writereg(state, 0xea, 0xff);
+ cx24116_writereg(state, 0xe1, 1);
+ cx24116_writereg(state, 0xe0, 1);
+
+ return 0;
+}
+
+static int cx24116_set_property(struct dvb_frontend *fe, struct dtv_property* tvp)
+{
+ dprintk("%s(..)\n", __func__);
+ return 0;
+}
+
+static int cx24116_get_property(struct dvb_frontend *fe, struct dtv_property* tvp)
+{
+ dprintk("%s(..)\n", __func__);
+ return 0;
+}
+
+/* dvb-core told us to tune, the tv property cache will be complete,
+ * it's safe for is to pull values and use them for tuning purposes.
+ */
+static int cx24116_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+ struct cx24116_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct cx24116_cmd cmd;
+ fe_status_t tunerstat;
+ int i, status, ret, retune;
+
+ dprintk("%s()\n",__func__);
+
+ switch(c->delivery_system) {
+ case SYS_DVBS:
+ dprintk("%s: DVB-S delivery system selected\n",__func__);
+
+ /* Only QPSK is supported for DVB-S */
+ if(c->modulation != QPSK) {
+ dprintk("%s: unsupported modulation selected (%d)\n",
+ __func__, c->modulation);
+ return -EOPNOTSUPP;
+ }
+
+ /* Pilot doesn't exist in DVB-S, turn bit off */
+ state->dnxt.pilot_val = CX24116_PILOT_OFF;
+ retune = 1;
+
+ /* DVB-S only supports 0.35 */
+ if(c->rolloff != ROLLOFF_35) {
+ dprintk("%s: unsupported rolloff selected (%d)\n",
+ __func__, c->rolloff);
+ return -EOPNOTSUPP;
+ }
+ state->dnxt.rolloff_val = CX24116_ROLLOFF_035;
+ break;
+
+ case SYS_DVBS2:
+ dprintk("%s: DVB-S2 delivery system selected\n",__func__);
+
+ /*
+ * NBC 8PSK/QPSK with DVB-S is supported for DVB-S2,
+ * but not hardware auto detection
+ */
+ if(c->modulation != PSK_8 && c->modulation != QPSK) {
+ dprintk("%s: unsupported modulation selected (%d)\n",
+ __func__, c->modulation);
+ return -EOPNOTSUPP;
+ }
+
+ switch(c->pilot) {
+ case PILOT_AUTO: /* Not supported but emulated */
+ retune = 2; /* Fall-through */
+ case PILOT_OFF:
+ state->dnxt.pilot_val = CX24116_PILOT_OFF;
+ break;
+ case PILOT_ON:
+ state->dnxt.pilot_val = CX24116_PILOT_ON;
+ break;
+ default:
+ dprintk("%s: unsupported pilot mode selected (%d)\n",
+ __func__, c->pilot);
+ return -EOPNOTSUPP;
+ }
+
+ switch(c->rolloff) {
+ case ROLLOFF_20:
+ state->dnxt.rolloff_val= CX24116_ROLLOFF_020;
+ break;
+ case ROLLOFF_25:
+ state->dnxt.rolloff_val= CX24116_ROLLOFF_025;
+ break;
+ case ROLLOFF_35:
+ state->dnxt.rolloff_val= CX24116_ROLLOFF_035;
+ break;
+ case ROLLOFF_AUTO: /* Rolloff must be explicit */
+ default:
+ dprintk("%s: unsupported rolloff selected (%d)\n",
+ __func__, c->rolloff);
+ return -EOPNOTSUPP;
+ }
+ break;
+
+ default:
+ dprintk("%s: unsupported delivery system selected (%d)\n",
+ __func__, c->delivery_system);
+ return -EOPNOTSUPP;
+ }
+ state->dnxt.modulation = c->modulation;
+ state->dnxt.frequency = c->frequency;
+ state->dnxt.pilot = c->pilot;
+ state->dnxt.rolloff = c->rolloff;
+
+ if ((ret = cx24116_set_inversion(state, c->inversion)) != 0)
+ return ret;
+
+ /* FEC_NONE/AUTO for DVB-S2 is not supported and detected here */
+ if ((ret = cx24116_set_fec(state, c->modulation, c->fec_inner)) != 0)
+ return ret;
+
+ if ((ret = cx24116_set_symbolrate(state, c->symbol_rate)) != 0)
+ return ret;
+
+ /* discard the 'current' tuning parameters and prepare to tune */
+ cx24116_clone_params(fe);
+
+ dprintk("%s: modulation = %d\n", __func__, state->dcur.modulation);
+ dprintk("%s: frequency = %d\n", __func__, state->dcur.frequency);
+ dprintk("%s: pilot = %d (val = 0x%02x)\n", __func__,
+ state->dcur.pilot, state->dcur.pilot_val);
+ dprintk("%s: retune = %d\n", __func__, retune);
+ dprintk("%s: rolloff = %d (val = 0x%02x)\n", __func__,
+ state->dcur.rolloff, state->dcur.rolloff_val);
+ dprintk("%s: symbol_rate = %d\n", __func__, state->dcur.symbol_rate);
+ dprintk("%s: FEC = %d (mask/val = 0x%02x/0x%02x)\n", __func__,
+ state->dcur.fec, state->dcur.fec_mask, state->dcur.fec_val);
+ dprintk("%s: Inversion = %d (val = 0x%02x)\n", __func__,
+ state->dcur.inversion, state->dcur.inversion_val);
+
+ /* This is also done in advise/acquire on HVR4000 but not on LITE */
+ if (state->config->set_ts_params)
+ state->config->set_ts_params(fe, 0);
+
+ /* Set/Reset B/W */
+ cmd.args[0x00] = CMD_BANDWIDTH;
+ cmd.args[0x01] = 0x01;
+ cmd.len= 0x02;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if (ret != 0)
+ return ret;
+
+ /* Prepare a tune request */
+ cmd.args[0x00] = CMD_TUNEREQUEST;
+
+ /* Frequency */
+ cmd.args[0x01] = (state->dcur.frequency & 0xff0000) >> 16;
+ cmd.args[0x02] = (state->dcur.frequency & 0x00ff00) >> 8;
+ cmd.args[0x03] = (state->dcur.frequency & 0x0000ff);
+
+ /* Symbol Rate */
+ cmd.args[0x04] = ((state->dcur.symbol_rate / 1000) & 0xff00) >> 8;
+ cmd.args[0x05] = ((state->dcur.symbol_rate / 1000) & 0x00ff);
+
+ /* Automatic Inversion */
+ cmd.args[0x06] = state->dcur.inversion_val;
+
+ /* Modulation / FEC / Pilot */
+ cmd.args[0x07] = state->dcur.fec_val | state->dcur.pilot_val;
+
+ cmd.args[0x08] = CX24116_SEARCH_RANGE_KHZ >> 8;
+ cmd.args[0x09] = CX24116_SEARCH_RANGE_KHZ & 0xff;
+ cmd.args[0x0a] = 0x00;
+ cmd.args[0x0b] = 0x00;
+ cmd.args[0x0c] = state->dcur.rolloff_val;
+ cmd.args[0x0d] = state->dcur.fec_mask;
+
+ if (state->dcur.symbol_rate > 30000000) {
+ cmd.args[0x0e] = 0x04;
+ cmd.args[0x0f] = 0x00;
+ cmd.args[0x10] = 0x01;
+ cmd.args[0x11] = 0x77;
+ cmd.args[0x12] = 0x36;
+ cx24116_writereg(state, CX24116_REG_CLKDIV, 0x44);
+ cx24116_writereg(state, CX24116_REG_RATEDIV, 0x01);
+ } else {
+ cmd.args[0x0e] = 0x06;
+ cmd.args[0x0f] = 0x00;
+ cmd.args[0x10] = 0x00;
+ cmd.args[0x11] = 0xFA;
+ cmd.args[0x12] = 0x24;
+ cx24116_writereg(state, CX24116_REG_CLKDIV, 0x46);
+ cx24116_writereg(state, CX24116_REG_RATEDIV, 0x00);
+ }
+
+ cmd.len= 0x13;
+
+ /* We need to support pilot and non-pilot tuning in the
+ * driver automatically. This is a workaround for because
+ * the demod does not support autodetect.
+ */
+ do {
+ /* Reset status register */
+ status = cx24116_readreg(state, CX24116_REG_SSTATUS) & CX24116_SIGNAL_MASK;
+ cx24116_writereg(state, CX24116_REG_SSTATUS, status);
+
+ /* Tune */
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if( ret != 0 )
+ break;
+
+ /*
+ * Wait for up to 500 ms before retrying
+ *
+ * If we are able to tune then generally it occurs within 100ms.
+ * If it takes longer, try a different toneburst setting.
+ */
+ for(i = 0; i < 50 ; i++) {
+ cx24116_read_status(fe, &tunerstat);
+ status = tunerstat & (FE_HAS_SIGNAL | FE_HAS_SYNC);
+ if(status == (FE_HAS_SIGNAL | FE_HAS_SYNC)) {
+ dprintk("%s: Tuned\n",__func__);
+ goto tuned;
+ }
+ msleep(10);
+ }
+
+ dprintk("%s: Not tuned\n",__func__);
+
+ /* Toggle pilot bit when in auto-pilot */
+ if(state->dcur.pilot == PILOT_AUTO)
+ cmd.args[0x07] ^= CX24116_PILOT_ON;
+ }
+ while(--retune);
+
+tuned: /* Set/Reset B/W */
+ cmd.args[0x00] = CMD_BANDWIDTH;
+ cmd.args[0x01] = 0x00;
+ cmd.len= 0x02;
+ ret = cx24116_cmd_execute(fe, &cmd);
+ if (ret != 0)
+ return ret;
+
+ return ret;
+}
+
+static struct dvb_frontend_ops cx24116_ops = {
+
+ .info = {
+ .name = "Conexant CX24116/CX24118",
+ .type = FE_QPSK,
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_stepsize = 1011, /* kHz for QPSK frontends */
+ .frequency_tolerance = 5000,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+ FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK | FE_CAN_RECOVER
+ },
+
+ .release = cx24116_release,
+
+ .init = cx24116_initfe,
+ .sleep = cx24116_sleep,
+ .read_status = cx24116_read_status,
+ .read_ber = cx24116_read_ber,
+ .read_signal_strength = cx24116_read_signal_strength,
+ .read_snr = cx24116_read_snr,
+ .read_ucblocks = cx24116_read_ucblocks,
+ .set_tone = cx24116_set_tone,
+ .set_voltage = cx24116_set_voltage,
+ .diseqc_send_master_cmd = cx24116_send_diseqc_msg,
+ .diseqc_send_burst = cx24116_diseqc_send_burst,
+
+ .set_property = cx24116_set_property,
+ .get_property = cx24116_get_property,
+ .set_frontend = cx24116_set_frontend,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
+
+module_param(toneburst, int, 0644);
+MODULE_PARM_DESC(toneburst, "DiSEqC toneburst 0=OFF, 1=TONE CACHE, 2=MESSAGE CACHE (default:1)");
+
+module_param(esno_snr, int, 0644);
+MODULE_PARM_DESC(debug, "SNR return units, 0=PERCENTAGE 0-100, 1=ESNO(db * 10) (default:0)");
+
+MODULE_DESCRIPTION("DVB Frontend module for Conexant cx24116/cx24118 hardware");
+MODULE_AUTHOR("Steven Toth");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(cx24116_attach);
diff --git a/drivers/media/dvb/frontends/cx24116.h b/drivers/media/dvb/frontends/cx24116.h
new file mode 100644
index 0000000..8dbcec2
--- /dev/null
+++ b/drivers/media/dvb/frontends/cx24116.h
@@ -0,0 +1,53 @@
+/*
+ Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver
+
+ Copyright (C) 2006 Steven Toth <stoth@linuxtv.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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef CX24116_H
+#define CX24116_H
+
+#include <linux/dvb/frontend.h>
+
+struct cx24116_config
+{
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* Need to set device param for start_dma */
+ int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured);
+
+ /* Need to reset device during firmware loading */
+ int (*reset_device)(struct dvb_frontend* fe);
+
+ /* Need to set MPEG parameters */
+ u8 mpg_clk_pos_pol:0x02;
+};
+
+#if defined(CONFIG_DVB_CX24116) || defined(CONFIG_DVB_CX24116_MODULE)
+extern struct dvb_frontend* cx24116_attach(const struct cx24116_config* config,
+ struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* cx24116_attach(const struct cx24116_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_CX24116
+
+#endif /* CX24116_H */
diff --git a/drivers/media/dvb/frontends/dib0070.h b/drivers/media/dvb/frontends/dib0070.h
index 3eedfdf..21f2c51 100644
--- a/drivers/media/dvb/frontends/dib0070.h
+++ b/drivers/media/dvb/frontends/dib0070.h
@@ -41,6 +41,7 @@
extern struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe,
struct i2c_adapter *i2c,
struct dib0070_config *cfg);
+extern u16 dib0070_wbd_offset(struct dvb_frontend *);
#else
static inline struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe,
struct i2c_adapter *i2c,
@@ -49,9 +50,14 @@
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL;
}
+
+static inline u16 dib0070_wbd_offset(struct dvb_frontend *fe)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
#endif
extern void dib0070_ctrl_agc_filter(struct dvb_frontend *, uint8_t open);
-extern u16 dib0070_wbd_offset(struct dvb_frontend *);
#endif
diff --git a/drivers/media/dvb/frontends/dib7000m.c b/drivers/media/dvb/frontends/dib7000m.c
index 5f1375e..0109720 100644
--- a/drivers/media/dvb/frontends/dib7000m.c
+++ b/drivers/media/dvb/frontends/dib7000m.c
@@ -1284,7 +1284,10 @@
}
EXPORT_SYMBOL(dib7000m_get_i2c_master);
-int dib7000m_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000m_config cfg[])
+#if 0
+/* used with some prototype boards */
+int dib7000m_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods,
+ u8 default_addr, struct dib7000m_config cfg[])
{
struct dib7000m_state st = { .i2c_adap = i2c };
int k = 0;
@@ -1329,6 +1332,7 @@
return 0;
}
EXPORT_SYMBOL(dib7000m_i2c_enumeration);
+#endif
static struct dvb_frontend_ops dib7000m_ops;
struct dvb_frontend * dib7000m_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000m_config *cfg)
diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb/frontends/dib7000p.c
index 1a0142e..8217e5b 100644
--- a/drivers/media/dvb/frontends/dib7000p.c
+++ b/drivers/media/dvb/frontends/dib7000p.c
@@ -1333,7 +1333,8 @@
/* Ensure the output mode remains at the previous default if it's
* not specifically set by the caller.
*/
- if (st->cfg.output_mode != OUTMODE_MPEG2_SERIAL)
+ if ((st->cfg.output_mode != OUTMODE_MPEG2_SERIAL) &&
+ (st->cfg.output_mode != OUTMODE_MPEG2_PAR_GATED_CLK))
st->cfg.output_mode = OUTMODE_MPEG2_FIFO;
demod = &st->demod;
diff --git a/drivers/media/dvb/frontends/dib7000p.h b/drivers/media/dvb/frontends/dib7000p.h
index 07c4d12..3e81268 100644
--- a/drivers/media/dvb/frontends/dib7000p.h
+++ b/drivers/media/dvb/frontends/dib7000p.h
@@ -41,6 +41,14 @@
extern struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap,
u8 i2c_addr,
struct dib7000p_config *cfg);
+extern struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *,
+ enum dibx000_i2c_interface,
+ int);
+extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c,
+ int no_of_demods, u8 default_addr,
+ struct dib7000p_config cfg[]);
+extern int dib7000p_set_gpio(struct dvb_frontend *, u8 num, u8 dir, u8 val);
+extern int dib7000p_set_wbd_ref(struct dvb_frontend *, u16 value);
#else
static inline struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap,
u8 i2c_addr,
@@ -49,13 +57,36 @@
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL;
}
+
+static inline
+struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *fe,
+ enum dibx000_i2c_interface i, int x)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
+extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c,
+ int no_of_demods, u8 default_addr,
+ struct dib7000p_config cfg[])
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
+
+extern int dib7000p_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
+
+extern int dib7000p_set_wbd_ref(struct dvb_frontend *fe, u16 value)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+}
#endif
-extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]);
-
-extern struct i2c_adapter * dib7000p_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int);
extern int dib7000pc_detection(struct i2c_adapter *i2c_adap);
-extern int dib7000p_set_gpio(struct dvb_frontend *, u8 num, u8 dir, u8 val);
-extern int dib7000p_set_wbd_ref(struct dvb_frontend *, u16 value);
#endif
diff --git a/drivers/media/dvb/frontends/drx397xD.c b/drivers/media/dvb/frontends/drx397xD.c
index 3cbed87..b9ca5c8 100644
--- a/drivers/media/dvb/frontends/drx397xD.c
+++ b/drivers/media/dvb/frontends/drx397xD.c
@@ -38,35 +38,32 @@
#define F_SET_0D0h 1
#define F_SET_0D4h 2
-typedef enum fw_ix {
+enum fw_ix {
#define _FW_ENTRY(a, b) b
#include "drx397xD_fw.h"
-} fw_ix_t;
+};
/* chip specifics */
struct drx397xD_state {
struct i2c_adapter *i2c;
struct dvb_frontend frontend;
struct drx397xD_config config;
- fw_ix_t chip_rev;
+ enum fw_ix chip_rev;
int flags;
u32 bandwidth_parm; /* internal bandwidth conversions */
u32 f_osc; /* w90: actual osc frequency [Hz] */
};
-/*******************************************************************************
- * Firmware
- ******************************************************************************/
-
+/* Firmware */
static const char *blob_name[] = {
#define _BLOB_ENTRY(a, b) a
#include "drx397xD_fw.h"
};
-typedef enum blob_ix {
+enum blob_ix {
#define _BLOB_ENTRY(a, b) b
#include "drx397xD_fw.h"
-} blob_ix_t;
+};
static struct {
const char *name;
@@ -85,7 +82,7 @@
};
/* use only with writer lock aquired */
-static void _drx_release_fw(struct drx397xD_state *s, fw_ix_t ix)
+static void _drx_release_fw(struct drx397xD_state *s, enum fw_ix ix)
{
memset(&fw[ix].data[0], 0, sizeof(fw[0].data));
if (fw[ix].file)
@@ -94,9 +91,9 @@
static void drx_release_fw(struct drx397xD_state *s)
{
- fw_ix_t ix = s->chip_rev;
+ enum fw_ix ix = s->chip_rev;
- pr_debug("%s\n", __FUNCTION__);
+ pr_debug("%s\n", __func__);
write_lock(&fw[ix].lock);
if (fw[ix].refcnt) {
@@ -107,13 +104,13 @@
write_unlock(&fw[ix].lock);
}
-static int drx_load_fw(struct drx397xD_state *s, fw_ix_t ix)
+static int drx_load_fw(struct drx397xD_state *s, enum fw_ix ix)
{
const u8 *data;
size_t size, len;
int i = 0, j, rc = -EINVAL;
- pr_debug("%s\n", __FUNCTION__);
+ pr_debug("%s\n", __func__);
if (ix < 0 || ix >= ARRAY_SIZE(fw))
return -EINVAL;
@@ -175,32 +172,34 @@
goto exit_corrupt;
}
} while (i < size);
- exit_corrupt:
+
+exit_corrupt:
printk(KERN_ERR "%s: Firmware is corrupt\n", mod_name);
- exit_err:
+exit_err:
_drx_release_fw(s, ix);
fw[ix].refcnt--;
- exit_ok:
+exit_ok:
fw[ix].refcnt++;
write_unlock(&fw[ix].lock);
+
return rc;
}
-/*******************************************************************************
- * i2c bus IO
- ******************************************************************************/
-
-static int write_fw(struct drx397xD_state *s, blob_ix_t ix)
+/* i2c bus IO */
+static int write_fw(struct drx397xD_state *s, enum blob_ix ix)
{
- struct i2c_msg msg = {.addr = s->config.demod_address,.flags = 0 };
const u8 *data;
int len, rc = 0, i = 0;
+ struct i2c_msg msg = {
+ .addr = s->config.demod_address,
+ .flags = 0
+ };
if (ix < 0 || ix >= ARRAY_SIZE(blob_name)) {
- pr_debug("%s drx_fw_ix_t out of range\n", __FUNCTION__);
+ pr_debug("%s drx_fw_ix_t out of range\n", __func__);
return -EINVAL;
}
- pr_debug("%s %s\n", __FUNCTION__, blob_name[ix]);
+ pr_debug("%s %s\n", __func__, blob_name[ix]);
read_lock(&fw[s->chip_rev].lock);
data = fw[s->chip_rev].data[ix];
@@ -229,33 +228,33 @@
goto exit_rc;
}
}
- exit_rc:
+exit_rc:
read_unlock(&fw[s->chip_rev].lock);
+
return 0;
}
/* Function is not endian safe, use the RD16 wrapper below */
-static int _read16(struct drx397xD_state *s, u32 i2c_adr)
+static int _read16(struct drx397xD_state *s, __le32 i2c_adr)
{
int rc;
u8 a[4];
- u16 v;
+ __le16 v;
struct i2c_msg msg[2] = {
{
- .addr = s->config.demod_address,
- .flags = 0,
- .buf = a,
- .len = sizeof(a)
- }
- , {
- .addr = s->config.demod_address,
- .flags = I2C_M_RD,
- .buf = (u8 *) & v,
- .len = sizeof(v)
- }
+ .addr = s->config.demod_address,
+ .flags = 0,
+ .buf = a,
+ .len = sizeof(a)
+ }, {
+ .addr = s->config.demod_address,
+ .flags = I2C_M_RD,
+ .buf = (u8 *)&v,
+ .len = sizeof(v)
+ }
};
- *(u32 *) a = i2c_adr;
+ *(__le32 *) a = i2c_adr;
rc = i2c_transfer(s->i2c, msg, 2);
if (rc != 2)
@@ -265,7 +264,7 @@
}
/* Function is not endian safe, use the WR16.. wrappers below */
-static int _write16(struct drx397xD_state *s, u32 i2c_adr, u16 val)
+static int _write16(struct drx397xD_state *s, __le32 i2c_adr, __le16 val)
{
u8 a[6];
int rc;
@@ -276,28 +275,28 @@
.len = sizeof(a)
};
- *(u32 *) a = i2c_adr;
- *(u16 *) & a[4] = val;
+ *(__le32 *)a = i2c_adr;
+ *(__le16 *)&a[4] = val;
rc = i2c_transfer(s->i2c, &msg, 1);
if (rc != 1)
return -EIO;
+
return 0;
}
-#define WR16(ss,adr, val) \
+#define WR16(ss, adr, val) \
_write16(ss, I2C_ADR_C0(adr), cpu_to_le16(val))
-#define WR16_E0(ss,adr, val) \
+#define WR16_E0(ss, adr, val) \
_write16(ss, I2C_ADR_E0(adr), cpu_to_le16(val))
-#define RD16(ss,adr) \
+#define RD16(ss, adr) \
_read16(ss, I2C_ADR_C0(adr))
-#define EXIT_RC( cmd ) if ( (rc = (cmd)) < 0) goto exit_rc
+#define EXIT_RC(cmd) \
+ if ((rc = (cmd)) < 0) \
+ goto exit_rc
-/*******************************************************************************
- * Tuner callback
- ******************************************************************************/
-
+/* Tuner callback */
static int PLL_Set(struct drx397xD_state *s,
struct dvb_frontend_parameters *fep, int *df_tuner)
{
@@ -305,7 +304,7 @@
u32 f_tuner, f = fep->frequency;
int rc;
- pr_debug("%s\n", __FUNCTION__);
+ pr_debug("%s\n", __func__);
if ((f > s->frontend.ops.tuner_ops.info.frequency_max) ||
(f < s->frontend.ops.tuner_ops.info.frequency_min))
@@ -325,28 +324,26 @@
return rc;
*df_tuner = f_tuner - f;
- pr_debug("%s requested %d [Hz] tuner %d [Hz]\n", __FUNCTION__, f,
+ pr_debug("%s requested %d [Hz] tuner %d [Hz]\n", __func__, f,
f_tuner);
return 0;
}
-/*******************************************************************************
- * Demodulator helper functions
- ******************************************************************************/
-
+/* Demodulator helper functions */
static int SC_WaitForReady(struct drx397xD_state *s)
{
int cnt = 1000;
int rc;
- pr_debug("%s\n", __FUNCTION__);
+ pr_debug("%s\n", __func__);
while (cnt--) {
rc = RD16(s, 0x820043);
if (rc == 0)
return 0;
}
+
return -1;
}
@@ -354,13 +351,14 @@
{
int rc;
- pr_debug("%s\n", __FUNCTION__);
+ pr_debug("%s\n", __func__);
WR16(s, 0x820043, cmd);
SC_WaitForReady(s);
rc = RD16(s, 0x820042);
if ((rc & 0xffff) == 0xffff)
return -1;
+
return 0;
}
@@ -368,7 +366,7 @@
{
int rc, cnt = 1000;
- pr_debug("%s\n", __FUNCTION__);
+ pr_debug("%s\n", __func__);
rc = WR16(s, 0x420032, cmd);
if (rc < 0)
@@ -383,22 +381,24 @@
if (rc < 0)
return rc;
} while (--cnt);
+
return rc;
}
static int HI_CfgCommand(struct drx397xD_state *s)
{
- pr_debug("%s\n", __FUNCTION__);
+ pr_debug("%s\n", __func__);
WR16(s, 0x420033, 0x3973);
- WR16(s, 0x420034, s->config.w50); // code 4, log 4
- WR16(s, 0x420035, s->config.w52); // code 15, log 9
+ WR16(s, 0x420034, s->config.w50); /* code 4, log 4 */
+ WR16(s, 0x420035, s->config.w52); /* code 15, log 9 */
WR16(s, 0x420036, s->config.demod_address << 1);
- WR16(s, 0x420037, s->config.w56); // code (set_i2c ?? initX 1 ), log 1
-// WR16(s, 0x420033, 0x3973);
+ WR16(s, 0x420037, s->config.w56); /* code (set_i2c ?? initX 1 ), log 1 */
+ /* WR16(s, 0x420033, 0x3973); */
if ((s->config.w56 & 8) == 0)
return HI_Command(s, 3);
+
return WR16(s, 0x420032, 0x3);
}
@@ -419,7 +419,7 @@
u16 w0C = agc->w0C;
int quot, rem, i, rc = -EINVAL;
- pr_debug("%s\n", __FUNCTION__);
+ pr_debug("%s\n", __func__);
if (agc->w04 > 0x3ff)
goto exit_rc;
@@ -468,7 +468,7 @@
i = slowIncrDecLUT_15272[rem / 28];
EXIT_RC(WR16(s, 0x0c2002b, i));
rc = WR16(s, 0x0c2002c, i);
- exit_rc:
+exit_rc:
return rc;
}
@@ -478,7 +478,7 @@
u16 w06 = agc->w06;
int rc = -1;
- pr_debug("%s %d 0x%x 0x%x\n", __FUNCTION__, agc->d00, w04, w06);
+ pr_debug("%s %d 0x%x 0x%x\n", __func__, agc->d00, w04, w06);
if (w04 > 0x3ff)
goto exit_rc;
@@ -498,7 +498,7 @@
rc &= ~2;
break;
case 0:
- // loc_8000659
+ /* loc_8000659 */
s->config.w9C &= ~2;
EXIT_RC(WR16(s, 0x0c20015, s->config.w9C));
EXIT_RC(RD16(s, 0x0c20010));
@@ -522,7 +522,8 @@
rc |= 2;
}
rc = WR16(s, 0x0c20013, rc);
- exit_rc:
+
+exit_rc:
return rc;
}
@@ -554,7 +555,7 @@
int lockstat;
u32 clk, clk_limit;
- pr_debug("%s\n", __FUNCTION__);
+ pr_debug("%s\n", __func__);
if (s->config.d5C == 0) {
EXIT_RC(WR16(s, 0x08200e8, 0x010));
@@ -598,11 +599,12 @@
if (clk - s->config.f_osc * 1000 + clk_limit <= 2 * clk_limit) {
s->f_osc = clk;
- pr_debug("%s: osc %d %d [Hz]\n", __FUNCTION__,
+ pr_debug("%s: osc %d %d [Hz]\n", __func__,
s->config.f_osc * 1000, clk - s->config.f_osc * 1000);
}
rc = WR16(s, 0x08200e8, 0);
- exit_rc:
+
+exit_rc:
return rc;
}
@@ -610,7 +612,7 @@
{
int rc, si, bp;
- pr_debug("%s\n", __FUNCTION__);
+ pr_debug("%s\n", __func__);
si = s->config.wA0;
if (s->config.w98 == 0) {
@@ -620,17 +622,17 @@
si &= ~1;
bp = 0x200;
}
- if (s->config.w9A == 0) {
+ if (s->config.w9A == 0)
si |= 0x80;
- } else {
+ else
si &= ~0x80;
- }
EXIT_RC(WR16(s, 0x2150045, 0));
EXIT_RC(WR16(s, 0x2150010, si));
EXIT_RC(WR16(s, 0x2150011, bp));
rc = WR16(s, 0x2150012, (type == 0 ? 0xfff : 0));
- exit_rc:
+
+exit_rc:
return rc;
}
@@ -646,7 +648,7 @@
int rc, df_tuner;
int a, b, c, d;
- pr_debug("%s %d\n", __FUNCTION__, s->config.d60);
+ pr_debug("%s %d\n", __func__, s->config.d60);
if (s->config.d60 != 2)
goto set_tuner;
@@ -658,7 +660,7 @@
rc = ConfigureMPEGOutput(s, 0);
if (rc < 0)
goto set_tuner;
- set_tuner:
+set_tuner:
rc = PLL_Set(s, fep, &df_tuner);
if (rc < 0) {
@@ -835,16 +837,16 @@
rc = WR16(s, 0x2010012, 0);
if (rc < 0)
goto exit_rc;
- // QPSK QAM16 QAM64
- ebx = 0x19f; // 62
- ebp = 0x1fb; // 15
- v20 = 0x16a; // 62
- v1E = 0x195; // 62
- v16 = 0x1bb; // 15
- v14 = 0x1ef; // 15
- v12 = 5; // 16
- v10 = 5; // 16
- v0E = 5; // 16
+ /* QPSK QAM16 QAM64 */
+ ebx = 0x19f; /* 62 */
+ ebp = 0x1fb; /* 15 */
+ v20 = 0x16a; /* 62 */
+ v1E = 0x195; /* 62 */
+ v16 = 0x1bb; /* 15 */
+ v14 = 0x1ef; /* 15 */
+ v12 = 5; /* 16 */
+ v10 = 5; /* 16 */
+ v0E = 5; /* 16 */
}
switch (fep->u.ofdm.constellation) {
@@ -997,17 +999,17 @@
case BANDWIDTH_8_MHZ: /* 0 */
case BANDWIDTH_AUTO:
rc = WR16(s, 0x0c2003f, 0x32);
- s->bandwidth_parm = ebx = 0x8b8249; // 9142857
+ s->bandwidth_parm = ebx = 0x8b8249;
edx = 0;
break;
case BANDWIDTH_7_MHZ:
rc = WR16(s, 0x0c2003f, 0x3b);
- s->bandwidth_parm = ebx = 0x7a1200; // 8000000
+ s->bandwidth_parm = ebx = 0x7a1200;
edx = 0x4807;
break;
case BANDWIDTH_6_MHZ:
rc = WR16(s, 0x0c2003f, 0x47);
- s->bandwidth_parm = ebx = 0x68a1b6; // 6857142
+ s->bandwidth_parm = ebx = 0x68a1b6;
edx = 0x0f07;
break;
};
@@ -1060,8 +1062,6 @@
WR16(s, 0x0820040, 1);
SC_SendCommand(s, 1);
-// rc = WR16(s, 0x2150000, 1);
-// if (rc < 0) goto exit_rc;
rc = WR16(s, 0x2150000, 2);
rc = WR16(s, 0x2150016, a);
@@ -1069,7 +1069,8 @@
rc = WR16(s, 0x2150036, 0);
rc = WR16(s, 0x2150000, 1);
s->config.d60 = 2;
- exit_rc:
+
+exit_rc:
return rc;
}
@@ -1082,7 +1083,7 @@
struct drx397xD_state *s = fe->demodulator_priv;
int rc;
- pr_debug("%s\n", __FUNCTION__);
+ pr_debug("%s\n", __func__);
s->config.rfagc.d00 = 2; /* 0x7c */
s->config.rfagc.w04 = 0;
@@ -1102,18 +1103,18 @@
/* HI_CfgCommand */
s->config.w50 = 4;
- s->config.w52 = 9; // 0xf;
+ s->config.w52 = 9;
- s->config.f_if = 42800000; /* d14: intermediate frequency [Hz] */
- s->config.f_osc = 48000; /* s66 : oscillator frequency [kHz] */
- s->config.w92 = 12000; // 20000;
+ s->config.f_if = 42800000; /* d14: intermediate frequency [Hz] */
+ s->config.f_osc = 48000; /* s66 : oscillator frequency [kHz] */
+ s->config.w92 = 12000;
s->config.w9C = 0x000e;
s->config.w9E = 0x0000;
/* ConfigureMPEGOutput params */
s->config.wA0 = 4;
- s->config.w98 = 1; // 0;
+ s->config.w98 = 1;
s->config.w9A = 1;
/* get chip revision */
@@ -1248,7 +1249,7 @@
rc = WR16(s, 0x0c20012, 1);
}
- write_DRXD_InitFE_1:
+write_DRXD_InitFE_1:
rc = write_fw(s, DRXD_InitFE_1);
if (rc < 0)
@@ -1311,7 +1312,8 @@
s->config.d5C = 0;
s->config.d60 = 1;
s->config.d48 = 1;
- error:
+
+error:
return rc;
}
@@ -1326,7 +1328,8 @@
{
struct drx397xD_state *s = fe->demodulator_priv;
- s->config.s20d24 = 1; // 0;
+ s->config.s20d24 = 1;
+
return drx_tune(s, params);
}
@@ -1337,18 +1340,16 @@
fe_tune_settings->min_delay_ms = 10000;
fe_tune_settings->step_size = 0;
fe_tune_settings->max_drift = 0;
+
return 0;
}
-static int drx397x_read_status(struct dvb_frontend *fe, fe_status_t * status)
+static int drx397x_read_status(struct dvb_frontend *fe, fe_status_t *status)
{
struct drx397xD_state *s = fe->demodulator_priv;
int lockstat;
GetLockStatus(s, &lockstat);
- /* TODO */
-// if (lockstat & 1)
-// CorrectSysClockDeviation(s);
*status = 0;
if (lockstat & 2) {
@@ -1356,9 +1357,8 @@
ConfigureMPEGOutput(s, 1);
*status = FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI;
}
- if (lockstat & 4) {
+ if (lockstat & 4)
*status |= FE_HAS_CARRIER | FE_HAS_SIGNAL;
- }
return 0;
}
@@ -1366,16 +1366,18 @@
static int drx397x_read_ber(struct dvb_frontend *fe, unsigned int *ber)
{
*ber = 0;
+
return 0;
}
-static int drx397x_read_snr(struct dvb_frontend *fe, u16 * snr)
+static int drx397x_read_snr(struct dvb_frontend *fe, u16 *snr)
{
*snr = 0;
+
return 0;
}
-static int drx397x_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
+static int drx397x_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
{
struct drx397xD_state *s = fe->demodulator_priv;
int rc;
@@ -1401,6 +1403,7 @@
* The following does the same but with less rounding errors:
*/
*strength = ~(7720 + (rc * 30744 >> 10));
+
return 0;
}
@@ -1408,6 +1411,7 @@
unsigned int *ucblocks)
{
*ucblocks = 0;
+
return 0;
}
@@ -1436,22 +1440,22 @@
.frequency_max = 855250000,
.frequency_stepsize = 166667,
.frequency_tolerance = 0,
- .caps = /* 0x0C01B2EAE */
- FE_CAN_FEC_1_2 | // = 0x2,
- FE_CAN_FEC_2_3 | // = 0x4,
- FE_CAN_FEC_3_4 | // = 0x8,
- FE_CAN_FEC_5_6 | // = 0x20,
- FE_CAN_FEC_7_8 | // = 0x80,
- FE_CAN_FEC_AUTO | // = 0x200,
- FE_CAN_QPSK | // = 0x400,
- FE_CAN_QAM_16 | // = 0x800,
- FE_CAN_QAM_64 | // = 0x2000,
- FE_CAN_QAM_AUTO | // = 0x10000,
- FE_CAN_TRANSMISSION_MODE_AUTO | // = 0x20000,
- FE_CAN_GUARD_INTERVAL_AUTO | // = 0x80000,
- FE_CAN_HIERARCHY_AUTO | // = 0x100000,
- FE_CAN_RECOVER | // = 0x40000000,
- FE_CAN_MUTE_TS // = 0x80000000
+ .caps = /* 0x0C01B2EAE */
+ FE_CAN_FEC_1_2 | /* = 0x2, */
+ FE_CAN_FEC_2_3 | /* = 0x4, */
+ FE_CAN_FEC_3_4 | /* = 0x8, */
+ FE_CAN_FEC_5_6 | /* = 0x20, */
+ FE_CAN_FEC_7_8 | /* = 0x80, */
+ FE_CAN_FEC_AUTO | /* = 0x200, */
+ FE_CAN_QPSK | /* = 0x400, */
+ FE_CAN_QAM_16 | /* = 0x800, */
+ FE_CAN_QAM_64 | /* = 0x2000, */
+ FE_CAN_QAM_AUTO | /* = 0x10000, */
+ FE_CAN_TRANSMISSION_MODE_AUTO | /* = 0x20000, */
+ FE_CAN_GUARD_INTERVAL_AUTO | /* = 0x80000, */
+ FE_CAN_HIERARCHY_AUTO | /* = 0x100000, */
+ FE_CAN_RECOVER | /* = 0x40000000, */
+ FE_CAN_MUTE_TS /* = 0x80000000 */
},
.release = drx397x_release,
@@ -1472,33 +1476,35 @@
struct dvb_frontend *drx397xD_attach(const struct drx397xD_config *config,
struct i2c_adapter *i2c)
{
- struct drx397xD_state *s = NULL;
+ struct drx397xD_state *state;
/* allocate memory for the internal state */
- s = kzalloc(sizeof(struct drx397xD_state), GFP_KERNEL);
- if (s == NULL)
+ state = kzalloc(sizeof(struct drx397xD_state), GFP_KERNEL);
+ if (!state)
goto error;
/* setup the state */
- s->i2c = i2c;
- memcpy(&s->config, config, sizeof(struct drx397xD_config));
+ state->i2c = i2c;
+ memcpy(&state->config, config, sizeof(struct drx397xD_config));
/* check if the demod is there */
- if (RD16(s, 0x2410019) < 0)
+ if (RD16(state, 0x2410019) < 0)
goto error;
/* create dvb_frontend */
- memcpy(&s->frontend.ops, &drx397x_ops, sizeof(struct dvb_frontend_ops));
- s->frontend.demodulator_priv = s;
+ memcpy(&state->frontend.ops, &drx397x_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
- return &s->frontend;
- error:
- kfree(s);
+ return &state->frontend;
+error:
+ kfree(state);
+
return NULL;
}
+EXPORT_SYMBOL(drx397xD_attach);
MODULE_DESCRIPTION("Micronas DRX397xD DVB-T Frontend");
MODULE_AUTHOR("Henk Vergonet");
MODULE_LICENSE("GPL");
-EXPORT_SYMBOL(drx397xD_attach);
diff --git a/drivers/media/dvb/frontends/drx397xD.h b/drivers/media/dvb/frontends/drx397xD.h
index ddc7a07..ba05d17 100644
--- a/drivers/media/dvb/frontends/drx397xD.h
+++ b/drivers/media/dvb/frontends/drx397xD.h
@@ -28,7 +28,7 @@
#define DRX_F_OFFSET 36000000
#define I2C_ADR_C0(x) \
-( (u32)cpu_to_le32( \
+( cpu_to_le32( \
(u32)( \
(((u32)(x) & (u32)0x000000ffUL) ) | \
(((u32)(x) & (u32)0x0000ff00UL) << 16) | \
@@ -38,7 +38,7 @@
)
#define I2C_ADR_E0(x) \
-( (u32)cpu_to_le32( \
+( cpu_to_le32( \
(u32)( \
(((u32)(x) & (u32)0x000000ffUL) ) | \
(((u32)(x) & (u32)0x0000ff00UL) << 16) | \
@@ -122,7 +122,7 @@
static inline struct dvb_frontend* drx397xD_attach(const struct drx397xD_config *config,
struct i2c_adapter *i2c)
{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL;
}
#endif /* CONFIG_DVB_DRX397XD */
diff --git a/drivers/media/dvb/frontends/dvb_dummy_fe.c b/drivers/media/dvb/frontends/dvb_dummy_fe.c
index fed09df..db8a937 100644
--- a/drivers/media/dvb/frontends/dvb_dummy_fe.c
+++ b/drivers/media/dvb/frontends/dvb_dummy_fe.c
@@ -75,9 +75,10 @@
static int dvb_dummy_fe_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
{
- if (fe->ops->tuner_ops->set_params) {
- fe->ops->tuner_ops->set_params(fe, p);
- if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0);
+ if (fe->ops.tuner_ops.set_params) {
+ fe->ops.tuner_ops.set_params(fe, p);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
}
return 0;
@@ -131,7 +132,7 @@
static struct dvb_frontend_ops dvb_dummy_fe_qpsk_ops;
-struct dvb_frontend* dvb_dummy_fe_qpsk_attach()
+struct dvb_frontend *dvb_dummy_fe_qpsk_attach(void)
{
struct dvb_dummy_fe_state* state = NULL;
@@ -151,7 +152,7 @@
static struct dvb_frontend_ops dvb_dummy_fe_qam_ops;
-struct dvb_frontend* dvb_dummy_fe_qam_attach()
+struct dvb_frontend *dvb_dummy_fe_qam_attach(void)
{
struct dvb_dummy_fe_state* state = NULL;
diff --git a/drivers/media/dvb/frontends/eds1547.h b/drivers/media/dvb/frontends/eds1547.h
new file mode 100644
index 0000000..fa79b7c
--- /dev/null
+++ b/drivers/media/dvb/frontends/eds1547.h
@@ -0,0 +1,133 @@
+/* eds1547.h Earda EDS-1547 tuner support
+*
+* Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by)
+*
+* 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.
+*
+* see Documentation/dvb/README.dvb-usb for more information
+*/
+
+#ifndef EDS1547
+#define EDS1547
+
+static u8 stv0288_earda_inittab[] = {
+ 0x01, 0x57,
+ 0x02, 0x20,
+ 0x03, 0x8e,
+ 0x04, 0x8e,
+ 0x05, 0x12,
+ 0x06, 0x00,
+ 0x07, 0x00,
+ 0x09, 0x00,
+ 0x0a, 0x04,
+ 0x0b, 0x00,
+ 0x0c, 0x00,
+ 0x0d, 0x00,
+ 0x0e, 0xd4,
+ 0x0f, 0x30,
+ 0x11, 0x44,
+ 0x12, 0x03,
+ 0x13, 0x48,
+ 0x14, 0x84,
+ 0x15, 0x45,
+ 0x16, 0xb7,
+ 0x17, 0x9c,
+ 0x18, 0x00,
+ 0x19, 0xa6,
+ 0x1a, 0x88,
+ 0x1b, 0x8f,
+ 0x1c, 0xf0,
+ 0x20, 0x0b,
+ 0x21, 0x54,
+ 0x22, 0x00,
+ 0x23, 0x00,
+ 0x2b, 0xff,
+ 0x2c, 0xf7,
+ 0x30, 0x00,
+ 0x31, 0x1e,
+ 0x32, 0x14,
+ 0x33, 0x0f,
+ 0x34, 0x09,
+ 0x35, 0x0c,
+ 0x36, 0x05,
+ 0x37, 0x2f,
+ 0x38, 0x16,
+ 0x39, 0xbd,
+ 0x3a, 0x00,
+ 0x3b, 0x13,
+ 0x3c, 0x11,
+ 0x3d, 0x30,
+ 0x40, 0x63,
+ 0x41, 0x04,
+ 0x42, 0x60,
+ 0x43, 0x00,
+ 0x44, 0x00,
+ 0x45, 0x00,
+ 0x46, 0x00,
+ 0x47, 0x00,
+ 0x4a, 0x00,
+ 0x50, 0x10,
+ 0x51, 0x36,
+ 0x52, 0x09,
+ 0x53, 0x94,
+ 0x54, 0x62,
+ 0x55, 0x29,
+ 0x56, 0x64,
+ 0x57, 0x2b,
+ 0x58, 0x54,
+ 0x59, 0x86,
+ 0x5a, 0x00,
+ 0x5b, 0x9b,
+ 0x5c, 0x08,
+ 0x5d, 0x7f,
+ 0x5e, 0x00,
+ 0x5f, 0xff,
+ 0x70, 0x00,
+ 0x71, 0x00,
+ 0x72, 0x00,
+ 0x74, 0x00,
+ 0x75, 0x00,
+ 0x76, 0x00,
+ 0x81, 0x00,
+ 0x82, 0x3f,
+ 0x83, 0x3f,
+ 0x84, 0x00,
+ 0x85, 0x00,
+ 0x88, 0x00,
+ 0x89, 0x00,
+ 0x8a, 0x00,
+ 0x8b, 0x00,
+ 0x8c, 0x00,
+ 0x90, 0x00,
+ 0x91, 0x00,
+ 0x92, 0x00,
+ 0x93, 0x00,
+ 0x94, 0x1c,
+ 0x97, 0x00,
+ 0xa0, 0x48,
+ 0xa1, 0x00,
+ 0xb0, 0xb8,
+ 0xb1, 0x3a,
+ 0xb2, 0x10,
+ 0xb3, 0x82,
+ 0xb4, 0x80,
+ 0xb5, 0x82,
+ 0xb6, 0x82,
+ 0xb7, 0x82,
+ 0xb8, 0x20,
+ 0xb9, 0x00,
+ 0xf0, 0x00,
+ 0xf1, 0x00,
+ 0xf2, 0xc0,
+ 0xff,0xff,
+};
+
+static struct stv0288_config earda_config = {
+ .demod_address = 0x68,
+ .min_delay_ms = 100,
+ .inittab = stv0288_earda_inittab,
+};
+
+#endif
diff --git a/drivers/media/dvb/frontends/lgs8gl5.c b/drivers/media/dvb/frontends/lgs8gl5.c
new file mode 100644
index 0000000..855852f
--- /dev/null
+++ b/drivers/media/dvb/frontends/lgs8gl5.c
@@ -0,0 +1,454 @@
+/*
+ Legend Silicon LGS-8GL5 DMB-TH OFDM demodulator driver
+
+ Copyright (C) 2008 Sirius International (Hong Kong) Limited
+ Timothy Lee <timothy.lee@siriushk.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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "dvb_frontend.h"
+#include "lgs8gl5.h"
+
+
+#define REG_RESET 0x02
+#define REG_RESET_OFF 0x01
+#define REG_03 0x03
+#define REG_04 0x04
+#define REG_07 0x07
+#define REG_09 0x09
+#define REG_0A 0x0a
+#define REG_0B 0x0b
+#define REG_0C 0x0c
+#define REG_37 0x37
+#define REG_STRENGTH 0x4b
+#define REG_STRENGTH_MASK 0x7f
+#define REG_STRENGTH_CARRIER 0x80
+#define REG_INVERSION 0x7c
+#define REG_INVERSION_ON 0x80
+#define REG_7D 0x7d
+#define REG_7E 0x7e
+#define REG_A2 0xa2
+#define REG_STATUS 0xa4
+#define REG_STATUS_SYNC 0x04
+#define REG_STATUS_LOCK 0x01
+
+
+struct lgs8gl5_state {
+ struct i2c_adapter *i2c;
+ const struct lgs8gl5_config *config;
+ struct dvb_frontend frontend;
+};
+
+
+static int debug;
+#define dprintk(args...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG "lgs8gl5: " args); \
+ } while (0)
+
+
+/* Writes into demod's register */
+static int
+lgs8gl5_write_reg(struct lgs8gl5_state *state, u8 reg, u8 data)
+{
+ int ret;
+ u8 buf[] = {reg, data};
+ struct i2c_msg msg = {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = buf,
+ .len = 2
+ };
+
+ ret = i2c_transfer(state->i2c, &msg, 1);
+ if (ret != 1)
+ dprintk("%s: error (reg=0x%02x, val=0x%02x, ret=%i)\n",
+ __func__, reg, data, ret);
+ return (ret != 1) ? -1 : 0;
+}
+
+
+/* Reads from demod's register */
+static int
+lgs8gl5_read_reg(struct lgs8gl5_state *state, u8 reg)
+{
+ int ret;
+ u8 b0[] = {reg};
+ u8 b1[] = {0};
+ struct i2c_msg msg[2] = {
+ {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = b0,
+ .len = 1
+ },
+ {
+ .addr = state->config->demod_address,
+ .flags = I2C_M_RD,
+ .buf = b1,
+ .len = 1
+ }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+ if (ret != 2)
+ return -EIO;
+
+ return b1[0];
+}
+
+
+static int
+lgs8gl5_update_reg(struct lgs8gl5_state *state, u8 reg, u8 data)
+{
+ lgs8gl5_read_reg(state, reg);
+ lgs8gl5_write_reg(state, reg, data);
+ return 0;
+}
+
+
+/* Writes into alternate device's register */
+/* TODO: Find out what that device is for! */
+static int
+lgs8gl5_update_alt_reg(struct lgs8gl5_state *state, u8 reg, u8 data)
+{
+ int ret;
+ u8 b0[] = {reg};
+ u8 b1[] = {0};
+ u8 b2[] = {reg, data};
+ struct i2c_msg msg[3] = {
+ {
+ .addr = state->config->demod_address + 2,
+ .flags = 0,
+ .buf = b0,
+ .len = 1
+ },
+ {
+ .addr = state->config->demod_address + 2,
+ .flags = I2C_M_RD,
+ .buf = b1,
+ .len = 1
+ },
+ {
+ .addr = state->config->demod_address + 2,
+ .flags = 0,
+ .buf = b2,
+ .len = 2
+ },
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 3);
+ return (ret != 3) ? -1 : 0;
+}
+
+
+static void
+lgs8gl5_soft_reset(struct lgs8gl5_state *state)
+{
+ u8 val;
+
+ dprintk("%s\n", __func__);
+
+ val = lgs8gl5_read_reg(state, REG_RESET);
+ lgs8gl5_write_reg(state, REG_RESET, val & ~REG_RESET_OFF);
+ lgs8gl5_write_reg(state, REG_RESET, val | REG_RESET_OFF);
+ msleep(5);
+}
+
+
+/* Starts demodulation */
+static void
+lgs8gl5_start_demod(struct lgs8gl5_state *state)
+{
+ u8 val;
+ int n;
+
+ dprintk("%s\n", __func__);
+
+ lgs8gl5_update_alt_reg(state, 0xc2, 0x28);
+ lgs8gl5_soft_reset(state);
+ lgs8gl5_update_reg(state, REG_07, 0x10);
+ lgs8gl5_update_reg(state, REG_07, 0x10);
+ lgs8gl5_write_reg(state, REG_09, 0x0e);
+ lgs8gl5_write_reg(state, REG_0A, 0xe5);
+ lgs8gl5_write_reg(state, REG_0B, 0x35);
+ lgs8gl5_write_reg(state, REG_0C, 0x30);
+
+ lgs8gl5_update_reg(state, REG_03, 0x00);
+ lgs8gl5_update_reg(state, REG_7E, 0x01);
+ lgs8gl5_update_alt_reg(state, 0xc5, 0x00);
+ lgs8gl5_update_reg(state, REG_04, 0x02);
+ lgs8gl5_update_reg(state, REG_37, 0x01);
+ lgs8gl5_soft_reset(state);
+
+ /* Wait for carrier */
+ for (n = 0; n < 10; n++) {
+ val = lgs8gl5_read_reg(state, REG_STRENGTH);
+ dprintk("Wait for carrier[%d] 0x%02X\n", n, val);
+ if (val & REG_STRENGTH_CARRIER)
+ break;
+ msleep(4);
+ }
+ if (!(val & REG_STRENGTH_CARRIER))
+ return;
+
+ /* Wait for lock */
+ for (n = 0; n < 20; n++) {
+ val = lgs8gl5_read_reg(state, REG_STATUS);
+ dprintk("Wait for lock[%d] 0x%02X\n", n, val);
+ if (val & REG_STATUS_LOCK)
+ break;
+ msleep(12);
+ }
+ if (!(val & REG_STATUS_LOCK))
+ return;
+
+ lgs8gl5_write_reg(state, REG_7D, lgs8gl5_read_reg(state, REG_A2));
+ lgs8gl5_soft_reset(state);
+}
+
+
+static int
+lgs8gl5_init(struct dvb_frontend *fe)
+{
+ struct lgs8gl5_state *state = fe->demodulator_priv;
+
+ dprintk("%s\n", __func__);
+
+ lgs8gl5_update_alt_reg(state, 0xc2, 0x28);
+ lgs8gl5_soft_reset(state);
+ lgs8gl5_update_reg(state, REG_07, 0x10);
+ lgs8gl5_update_reg(state, REG_07, 0x10);
+ lgs8gl5_write_reg(state, REG_09, 0x0e);
+ lgs8gl5_write_reg(state, REG_0A, 0xe5);
+ lgs8gl5_write_reg(state, REG_0B, 0x35);
+ lgs8gl5_write_reg(state, REG_0C, 0x30);
+
+ return 0;
+}
+
+
+static int
+lgs8gl5_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct lgs8gl5_state *state = fe->demodulator_priv;
+ u8 level = lgs8gl5_read_reg(state, REG_STRENGTH);
+ u8 flags = lgs8gl5_read_reg(state, REG_STATUS);
+
+ *status = 0;
+
+ if ((level & REG_STRENGTH_MASK) > 0)
+ *status |= FE_HAS_SIGNAL;
+ if (level & REG_STRENGTH_CARRIER)
+ *status |= FE_HAS_CARRIER;
+ if (flags & REG_STATUS_SYNC)
+ *status |= FE_HAS_SYNC;
+ if (flags & REG_STATUS_LOCK)
+ *status |= FE_HAS_LOCK;
+
+ return 0;
+}
+
+
+static int
+lgs8gl5_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ *ber = 0;
+
+ return 0;
+}
+
+
+static int
+lgs8gl5_read_signal_strength(struct dvb_frontend *fe, u16 *signal_strength)
+{
+ struct lgs8gl5_state *state = fe->demodulator_priv;
+ u8 level = lgs8gl5_read_reg(state, REG_STRENGTH);
+ *signal_strength = (level & REG_STRENGTH_MASK) << 8;
+
+ return 0;
+}
+
+
+static int
+lgs8gl5_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct lgs8gl5_state *state = fe->demodulator_priv;
+ u8 level = lgs8gl5_read_reg(state, REG_STRENGTH);
+ *snr = (level & REG_STRENGTH_MASK) << 8;
+
+ return 0;
+}
+
+
+static int
+lgs8gl5_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ *ucblocks = 0;
+
+ return 0;
+}
+
+
+static int
+lgs8gl5_set_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p)
+{
+ struct lgs8gl5_state *state = fe->demodulator_priv;
+
+ dprintk("%s\n", __func__);
+
+ if (p->u.ofdm.bandwidth != BANDWIDTH_8_MHZ)
+ return -EINVAL;
+
+ if (fe->ops.tuner_ops.set_params) {
+ fe->ops.tuner_ops.set_params(fe, p);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+
+ /* lgs8gl5_set_inversion(state, p->inversion); */
+
+ lgs8gl5_start_demod(state);
+
+ return 0;
+}
+
+
+static int
+lgs8gl5_get_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p)
+{
+ struct lgs8gl5_state *state = fe->demodulator_priv;
+ u8 inv = lgs8gl5_read_reg(state, REG_INVERSION);
+ struct dvb_ofdm_parameters *o = &p->u.ofdm;
+
+ p->inversion = (inv & REG_INVERSION_ON) ? INVERSION_ON : INVERSION_OFF;
+
+ o->code_rate_HP = FEC_1_2;
+ o->code_rate_LP = FEC_7_8;
+ o->guard_interval = GUARD_INTERVAL_1_32;
+ o->transmission_mode = TRANSMISSION_MODE_2K;
+ o->constellation = QAM_64;
+ o->hierarchy_information = HIERARCHY_NONE;
+ o->bandwidth = BANDWIDTH_8_MHZ;
+
+ return 0;
+}
+
+
+static int
+lgs8gl5_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *fesettings)
+{
+ fesettings->min_delay_ms = 240;
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
+ return 0;
+}
+
+
+static void
+lgs8gl5_release(struct dvb_frontend *fe)
+{
+ struct lgs8gl5_state *state = fe->demodulator_priv;
+ kfree(state);
+}
+
+
+static struct dvb_frontend_ops lgs8gl5_ops;
+
+
+struct dvb_frontend*
+lgs8gl5_attach(const struct lgs8gl5_config *config, struct i2c_adapter *i2c)
+{
+ struct lgs8gl5_state *state = NULL;
+
+ dprintk("%s\n", __func__);
+
+ /* Allocate memory for the internal state */
+ state = kmalloc(sizeof(struct lgs8gl5_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ /* Setup the state */
+ state->config = config;
+ state->i2c = i2c;
+
+ /* Check if the demod is there */
+ if (lgs8gl5_read_reg(state, REG_RESET) < 0)
+ goto error;
+
+ /* Create dvb_frontend */
+ memcpy(&state->frontend.ops, &lgs8gl5_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+
+error:
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL(lgs8gl5_attach);
+
+
+static struct dvb_frontend_ops lgs8gl5_ops = {
+ .info = {
+ .name = "Legend Silicon LGS-8GL5 DMB-TH",
+ .type = FE_OFDM,
+ .frequency_min = 474000000,
+ .frequency_max = 858000000,
+ .frequency_stepsize = 10000,
+ .frequency_tolerance = 0,
+ .caps = FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_32 |
+ FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_BANDWIDTH_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO |
+ FE_CAN_RECOVER
+ },
+
+ .release = lgs8gl5_release,
+
+ .init = lgs8gl5_init,
+
+ .set_frontend = lgs8gl5_set_frontend,
+ .get_frontend = lgs8gl5_get_frontend,
+ .get_tune_settings = lgs8gl5_get_tune_settings,
+
+ .read_status = lgs8gl5_read_status,
+ .read_ber = lgs8gl5_read_ber,
+ .read_signal_strength = lgs8gl5_read_signal_strength,
+ .read_snr = lgs8gl5_read_snr,
+ .read_ucblocks = lgs8gl5_read_ucblocks,
+};
+
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Legend Silicon LGS-8GL5 DMB-TH Demodulator driver");
+MODULE_AUTHOR("Timothy Lee");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/lgs8gl5.h b/drivers/media/dvb/frontends/lgs8gl5.h
new file mode 100644
index 0000000..d1417678
--- /dev/null
+++ b/drivers/media/dvb/frontends/lgs8gl5.h
@@ -0,0 +1,45 @@
+/*
+ Legend Silicon LGS-8GL5 DMB-TH OFDM demodulator driver
+
+ Copyright (C) 2008 Sirius International (Hong Kong) Limited
+ Timothy Lee <timothy.lee@siriushk.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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef LGS8GL5_H
+#define LGS8GL5_H
+
+#include <linux/dvb/frontend.h>
+
+struct lgs8gl5_config {
+ /* the demodulator's i2c address */
+ u8 demod_address;
+};
+
+#if defined(CONFIG_DVB_LGS8GL5) || \
+ (defined(CONFIG_DVB_LGS8GL5_MODULE) && defined(MODULE))
+extern struct dvb_frontend *lgs8gl5_attach(
+ const struct lgs8gl5_config *config, struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *lgs8gl5_attach(
+ const struct lgs8gl5_config *config, struct i2c_adapter *i2c) {
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_LGS8GL5 */
+
+#endif /* LGS8GL5_H */
diff --git a/drivers/media/dvb/frontends/nxt200x.c b/drivers/media/dvb/frontends/nxt200x.c
index af29835..a8429eb 100644
--- a/drivers/media/dvb/frontends/nxt200x.c
+++ b/drivers/media/dvb/frontends/nxt200x.c
@@ -80,7 +80,7 @@
return 0;
}
-static u8 i2c_readbytes (struct nxt200x_state* state, u8 addr, u8* buf, u8 len)
+static int i2c_readbytes(struct nxt200x_state *state, u8 addr, u8 *buf, u8 len)
{
int err;
struct i2c_msg msg = { .addr = addr, .flags = I2C_M_RD, .buf = buf, .len = len };
@@ -111,7 +111,7 @@
return 0;
}
-static u8 nxt200x_readbytes (struct nxt200x_state* state, u8 reg, u8* buf, u8 len)
+static int nxt200x_readbytes(struct nxt200x_state *state, u8 reg, u8 *buf, u8 len)
{
u8 reg2 [] = { reg };
diff --git a/drivers/media/dvb/frontends/or51211.c b/drivers/media/dvb/frontends/or51211.c
index 6afe12a..16cf2fd 100644
--- a/drivers/media/dvb/frontends/or51211.c
+++ b/drivers/media/dvb/frontends/or51211.c
@@ -88,7 +88,7 @@
return 0;
}
-static u8 i2c_readbytes (struct or51211_state* state, u8 reg, u8* buf, int len)
+static int i2c_readbytes(struct or51211_state *state, u8 reg, u8 *buf, int len)
{
int err;
struct i2c_msg msg;
diff --git a/drivers/media/dvb/frontends/si21xx.c b/drivers/media/dvb/frontends/si21xx.c
new file mode 100644
index 0000000..3ddbe69
--- /dev/null
+++ b/drivers/media/dvb/frontends/si21xx.c
@@ -0,0 +1,974 @@
+/* DVB compliant Linux driver for the DVB-S si2109/2110 demodulator
+*
+* Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by)
+*
+* 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 <linux/version.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <asm/div64.h>
+
+#include "dvb_frontend.h"
+#include "si21xx.h"
+
+#define REVISION_REG 0x00
+#define SYSTEM_MODE_REG 0x01
+#define TS_CTRL_REG_1 0x02
+#define TS_CTRL_REG_2 0x03
+#define PIN_CTRL_REG_1 0x04
+#define PIN_CTRL_REG_2 0x05
+#define LOCK_STATUS_REG_1 0x0f
+#define LOCK_STATUS_REG_2 0x10
+#define ACQ_STATUS_REG 0x11
+#define ACQ_CTRL_REG_1 0x13
+#define ACQ_CTRL_REG_2 0x14
+#define PLL_DIVISOR_REG 0x15
+#define COARSE_TUNE_REG 0x16
+#define FINE_TUNE_REG_L 0x17
+#define FINE_TUNE_REG_H 0x18
+
+#define ANALOG_AGC_POWER_LEVEL_REG 0x28
+#define CFO_ESTIMATOR_CTRL_REG_1 0x29
+#define CFO_ESTIMATOR_CTRL_REG_2 0x2a
+#define CFO_ESTIMATOR_CTRL_REG_3 0x2b
+
+#define SYM_RATE_ESTIMATE_REG_L 0x31
+#define SYM_RATE_ESTIMATE_REG_M 0x32
+#define SYM_RATE_ESTIMATE_REG_H 0x33
+
+#define CFO_ESTIMATOR_OFFSET_REG_L 0x36
+#define CFO_ESTIMATOR_OFFSET_REG_H 0x37
+#define CFO_ERROR_REG_L 0x38
+#define CFO_ERROR_REG_H 0x39
+#define SYM_RATE_ESTIMATOR_CTRL_REG 0x3a
+
+#define SYM_RATE_REG_L 0x3f
+#define SYM_RATE_REG_M 0x40
+#define SYM_RATE_REG_H 0x41
+#define SYM_RATE_ESTIMATOR_MAXIMUM_REG 0x42
+#define SYM_RATE_ESTIMATOR_MINIMUM_REG 0x43
+
+#define C_N_ESTIMATOR_CTRL_REG 0x7c
+#define C_N_ESTIMATOR_THRSHLD_REG 0x7d
+#define C_N_ESTIMATOR_LEVEL_REG_L 0x7e
+#define C_N_ESTIMATOR_LEVEL_REG_H 0x7f
+
+#define BLIND_SCAN_CTRL_REG 0x80
+
+#define LSA_CTRL_REG_1 0x8D
+#define SPCTRM_TILT_CORR_THRSHLD_REG 0x8f
+#define ONE_DB_BNDWDTH_THRSHLD_REG 0x90
+#define TWO_DB_BNDWDTH_THRSHLD_REG 0x91
+#define THREE_DB_BNDWDTH_THRSHLD_REG 0x92
+#define INBAND_POWER_THRSHLD_REG 0x93
+#define REF_NOISE_LVL_MRGN_THRSHLD_REG 0x94
+
+#define VIT_SRCH_CTRL_REG_1 0xa0
+#define VIT_SRCH_CTRL_REG_2 0xa1
+#define VIT_SRCH_CTRL_REG_3 0xa2
+#define VIT_SRCH_STATUS_REG 0xa3
+#define VITERBI_BER_COUNT_REG_L 0xab
+#define REED_SOLOMON_CTRL_REG 0xb0
+#define REED_SOLOMON_ERROR_COUNT_REG_L 0xb1
+#define PRBS_CTRL_REG 0xb5
+
+#define LNB_CTRL_REG_1 0xc0
+#define LNB_CTRL_REG_2 0xc1
+#define LNB_CTRL_REG_3 0xc2
+#define LNB_CTRL_REG_4 0xc3
+#define LNB_CTRL_STATUS_REG 0xc4
+#define LNB_FIFO_REGS_0 0xc5
+#define LNB_FIFO_REGS_1 0xc6
+#define LNB_FIFO_REGS_2 0xc7
+#define LNB_FIFO_REGS_3 0xc8
+#define LNB_FIFO_REGS_4 0xc9
+#define LNB_FIFO_REGS_5 0xca
+#define LNB_SUPPLY_CTRL_REG_1 0xcb
+#define LNB_SUPPLY_CTRL_REG_2 0xcc
+#define LNB_SUPPLY_CTRL_REG_3 0xcd
+#define LNB_SUPPLY_CTRL_REG_4 0xce
+#define LNB_SUPPLY_STATUS_REG 0xcf
+
+#define FALSE 0
+#define TRUE 1
+#define FAIL -1
+#define PASS 0
+
+#define ALLOWABLE_FS_COUNT 10
+#define STATUS_BER 0
+#define STATUS_UCBLOCKS 1
+
+static int debug;
+#define dprintk(args...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG "si21xx: " args); \
+ } while (0)
+
+enum {
+ ACTIVE_HIGH,
+ ACTIVE_LOW
+};
+enum {
+ BYTE_WIDE,
+ BIT_WIDE
+};
+enum {
+ CLK_GAPPED_MODE,
+ CLK_CONTINUOUS_MODE
+};
+enum {
+ RISING_EDGE,
+ FALLING_EDGE
+};
+enum {
+ MSB_FIRST,
+ LSB_FIRST
+};
+enum {
+ SERIAL,
+ PARALLEL
+};
+
+struct si21xx_state {
+ struct i2c_adapter *i2c;
+ const struct si21xx_config *config;
+ struct dvb_frontend frontend;
+ u8 initialised:1;
+ int errmode;
+ int fs; /*Sampling rate of the ADC in MHz*/
+};
+
+/* register default initialization */
+static u8 serit_sp1511lhb_inittab[] = {
+ 0x01, 0x28, /* set i2c_inc_disable */
+ 0x20, 0x03,
+ 0x27, 0x20,
+ 0xe0, 0x45,
+ 0xe1, 0x08,
+ 0xfe, 0x01,
+ 0x01, 0x28,
+ 0x89, 0x09,
+ 0x04, 0x80,
+ 0x05, 0x01,
+ 0x06, 0x00,
+ 0x20, 0x03,
+ 0x24, 0x88,
+ 0x29, 0x09,
+ 0x2a, 0x0f,
+ 0x2c, 0x10,
+ 0x2d, 0x19,
+ 0x2e, 0x08,
+ 0x2f, 0x10,
+ 0x30, 0x19,
+ 0x34, 0x20,
+ 0x35, 0x03,
+ 0x45, 0x02,
+ 0x46, 0x45,
+ 0x47, 0xd0,
+ 0x48, 0x00,
+ 0x49, 0x40,
+ 0x4a, 0x03,
+ 0x4c, 0xfd,
+ 0x4f, 0x2e,
+ 0x50, 0x2e,
+ 0x51, 0x10,
+ 0x52, 0x10,
+ 0x56, 0x92,
+ 0x59, 0x00,
+ 0x5a, 0x2d,
+ 0x5b, 0x33,
+ 0x5c, 0x1f,
+ 0x5f, 0x76,
+ 0x62, 0xc0,
+ 0x63, 0xc0,
+ 0x64, 0xf3,
+ 0x65, 0xf3,
+ 0x79, 0x40,
+ 0x6a, 0x40,
+ 0x6b, 0x0a,
+ 0x6c, 0x80,
+ 0x6d, 0x27,
+ 0x71, 0x06,
+ 0x75, 0x60,
+ 0x78, 0x00,
+ 0x79, 0xb5,
+ 0x7c, 0x05,
+ 0x7d, 0x1a,
+ 0x87, 0x55,
+ 0x88, 0x72,
+ 0x8f, 0x08,
+ 0x90, 0xe0,
+ 0x94, 0x40,
+ 0xa0, 0x3f,
+ 0xa1, 0xc0,
+ 0xa4, 0xcc,
+ 0xa5, 0x66,
+ 0xa6, 0x66,
+ 0xa7, 0x7b,
+ 0xa8, 0x7b,
+ 0xa9, 0x7b,
+ 0xaa, 0x9a,
+ 0xed, 0x04,
+ 0xad, 0x00,
+ 0xae, 0x03,
+ 0xcc, 0xab,
+ 0x01, 0x08,
+ 0xff, 0xff
+};
+
+/* low level read/writes */
+static int si21_writeregs(struct si21xx_state *state, u8 reg1,
+ u8 *data, int len)
+{
+ int ret;
+ u8 buf[60];/* = { reg1, data };*/
+ struct i2c_msg msg = {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = buf,
+ .len = len + 1
+ };
+
+ msg.buf[0] = reg1;
+ memcpy(msg.buf + 1, data, len);
+
+ ret = i2c_transfer(state->i2c, &msg, 1);
+
+ if (ret != 1)
+ dprintk("%s: writereg error (reg1 == 0x%02x, data == 0x%02x, "
+ "ret == %i)\n", __func__, reg1, data[0], ret);
+
+ return (ret != 1) ? -EREMOTEIO : 0;
+}
+
+static int si21_writereg(struct si21xx_state *state, u8 reg, u8 data)
+{
+ int ret;
+ u8 buf[] = { reg, data };
+ struct i2c_msg msg = {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = buf,
+ .len = 2
+ };
+
+ ret = i2c_transfer(state->i2c, &msg, 1);
+
+ if (ret != 1)
+ dprintk("%s: writereg error (reg == 0x%02x, data == 0x%02x, "
+ "ret == %i)\n", __func__, reg, data, ret);
+
+ return (ret != 1) ? -EREMOTEIO : 0;
+}
+
+static int si21_write(struct dvb_frontend *fe, u8 *buf, int len)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+
+ if (len != 2)
+ return -EINVAL;
+
+ return si21_writereg(state, buf[0], buf[1]);
+}
+
+static u8 si21_readreg(struct si21xx_state *state, u8 reg)
+{
+ int ret;
+ u8 b0[] = { reg };
+ u8 b1[] = { 0 };
+ struct i2c_msg msg[] = {
+ {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = b0,
+ .len = 1
+ }, {
+ .addr = state->config->demod_address,
+ .flags = I2C_M_RD,
+ .buf = b1,
+ .len = 1
+ }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2)
+ dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n",
+ __func__, reg, ret);
+
+ return b1[0];
+}
+
+static int si21_readregs(struct si21xx_state *state, u8 reg1, u8 *b, u8 len)
+{
+ int ret;
+ struct i2c_msg msg[] = {
+ {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = ®1,
+ .len = 1
+ }, {
+ .addr = state->config->demod_address,
+ .flags = I2C_M_RD,
+ .buf = b,
+ .len = len
+ }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2)
+ dprintk("%s: readreg error (ret == %i)\n", __func__, ret);
+
+ return ret == 2 ? 0 : -1;
+}
+
+static int si21xx_wait_diseqc_idle(struct si21xx_state *state, int timeout)
+{
+ unsigned long start = jiffies;
+
+ dprintk("%s\n", __func__);
+
+ while ((si21_readreg(state, LNB_CTRL_REG_1) & 0x8) == 8) {
+ if (jiffies - start > timeout) {
+ dprintk("%s: timeout!!\n", __func__);
+ return -ETIMEDOUT;
+ }
+ msleep(10);
+ };
+
+ return 0;
+}
+
+static int si21xx_set_symbolrate(struct dvb_frontend *fe, u32 srate)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+ u32 sym_rate, data_rate;
+ int i;
+ u8 sym_rate_bytes[3];
+
+ dprintk("%s : srate = %i\n", __func__ , srate);
+
+ if ((srate < 1000000) || (srate > 45000000))
+ return -EINVAL;
+
+ data_rate = srate;
+ sym_rate = 0;
+
+ for (i = 0; i < 4; ++i) {
+ sym_rate /= 100;
+ sym_rate = sym_rate + ((data_rate % 100) * 0x800000) /
+ state->fs;
+ data_rate /= 100;
+ }
+ for (i = 0; i < 3; ++i)
+ sym_rate_bytes[i] = (u8)((sym_rate >> (i * 8)) & 0xff);
+
+ si21_writeregs(state, SYM_RATE_REG_L, sym_rate_bytes, 0x03);
+
+ return 0;
+}
+
+static int si21xx_send_diseqc_msg(struct dvb_frontend *fe,
+ struct dvb_diseqc_master_cmd *m)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+ u8 lnb_status;
+ u8 LNB_CTRL_1;
+ int status;
+
+ dprintk("%s\n", __func__);
+
+ status = PASS;
+ LNB_CTRL_1 = 0;
+
+ status |= si21_readregs(state, LNB_CTRL_STATUS_REG, &lnb_status, 0x01);
+ status |= si21_readregs(state, LNB_CTRL_REG_1, &lnb_status, 0x01);
+
+ /*fill the FIFO*/
+ status |= si21_writeregs(state, LNB_FIFO_REGS_0, m->msg, m->msg_len);
+
+ LNB_CTRL_1 = (lnb_status & 0x70);
+ LNB_CTRL_1 |= m->msg_len;
+
+ LNB_CTRL_1 |= 0x80; /* begin LNB signaling */
+
+ status |= si21_writeregs(state, LNB_CTRL_REG_1, &LNB_CTRL_1, 0x01);
+
+ return status;
+}
+
+static int si21xx_send_diseqc_burst(struct dvb_frontend *fe,
+ fe_sec_mini_cmd_t burst)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+ u8 val;
+
+ dprintk("%s\n", __func__);
+
+ if (si21xx_wait_diseqc_idle(state, 100) < 0)
+ return -ETIMEDOUT;
+
+ val = (0x80 | si21_readreg(state, 0xc1));
+ if (si21_writereg(state, LNB_CTRL_REG_1,
+ burst == SEC_MINI_A ? (val & ~0x10) : (val | 0x10)))
+ return -EREMOTEIO;
+
+ if (si21xx_wait_diseqc_idle(state, 100) < 0)
+ return -ETIMEDOUT;
+
+ if (si21_writereg(state, LNB_CTRL_REG_1, val))
+ return -EREMOTEIO;
+
+ return 0;
+}
+/* 30.06.2008 */
+static int si21xx_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+ u8 val;
+
+ dprintk("%s\n", __func__);
+ val = (0x80 | si21_readreg(state, LNB_CTRL_REG_1));
+
+ switch (tone) {
+ case SEC_TONE_ON:
+ return si21_writereg(state, LNB_CTRL_REG_1, val | 0x20);
+
+ case SEC_TONE_OFF:
+ return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x20));
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int si21xx_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+
+ u8 val;
+ dprintk("%s: %s\n", __func__,
+ volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" :
+ volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??");
+
+
+ val = (0x80 | si21_readreg(state, LNB_CTRL_REG_1));
+
+ switch (volt) {
+ case SEC_VOLTAGE_18:
+ return si21_writereg(state, LNB_CTRL_REG_1, val | 0x40);
+ break;
+ case SEC_VOLTAGE_13:
+ return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x40));
+ break;
+ default:
+ return -EINVAL;
+ };
+}
+
+static int si21xx_init(struct dvb_frontend *fe)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+ int i;
+ int status = 0;
+ u8 reg1;
+ u8 val;
+ u8 reg2[2];
+
+ dprintk("%s\n", __func__);
+
+ for (i = 0; ; i += 2) {
+ reg1 = serit_sp1511lhb_inittab[i];
+ val = serit_sp1511lhb_inittab[i+1];
+ if (reg1 == 0xff && val == 0xff)
+ break;
+ si21_writeregs(state, reg1, &val, 1);
+ }
+
+ /*DVB QPSK SYSTEM MODE REG*/
+ reg1 = 0x08;
+ si21_writeregs(state, SYSTEM_MODE_REG, ®1, 0x01);
+
+ /*transport stream config*/
+ /*
+ mode = PARALLEL;
+ sdata_form = LSB_FIRST;
+ clk_edge = FALLING_EDGE;
+ clk_mode = CLK_GAPPED_MODE;
+ strt_len = BYTE_WIDE;
+ sync_pol = ACTIVE_HIGH;
+ val_pol = ACTIVE_HIGH;
+ err_pol = ACTIVE_HIGH;
+ sclk_rate = 0x00;
+ parity = 0x00 ;
+ data_delay = 0x00;
+ clk_delay = 0x00;
+ pclk_smooth = 0x00;
+ */
+ reg2[0] =
+ PARALLEL + (LSB_FIRST << 1)
+ + (FALLING_EDGE << 2) + (CLK_GAPPED_MODE << 3)
+ + (BYTE_WIDE << 4) + (ACTIVE_HIGH << 5)
+ + (ACTIVE_HIGH << 6) + (ACTIVE_HIGH << 7);
+
+ reg2[1] = 0;
+ /* sclk_rate + (parity << 2)
+ + (data_delay << 3) + (clk_delay << 4)
+ + (pclk_smooth << 5);
+ */
+ status |= si21_writeregs(state, TS_CTRL_REG_1, reg2, 0x02);
+ if (status != 0)
+ dprintk(" %s : TS Set Error\n", __func__);
+
+ return 0;
+
+}
+
+static int si21_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+ u8 regs_read[2];
+ u8 reg_read;
+ u8 i;
+ u8 lock;
+ u8 signal = si21_readreg(state, ANALOG_AGC_POWER_LEVEL_REG);
+
+ si21_readregs(state, LOCK_STATUS_REG_1, regs_read, 0x02);
+ reg_read = 0;
+
+ for (i = 0; i < 7; ++i)
+ reg_read |= ((regs_read[0] >> i) & 0x01) << (6 - i);
+
+ lock = ((reg_read & 0x7f) | (regs_read[1] & 0x80));
+
+ dprintk("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __func__, lock);
+ *status = 0;
+
+ if (signal > 10)
+ *status |= FE_HAS_SIGNAL;
+
+ if (lock & 0x2)
+ *status |= FE_HAS_CARRIER;
+
+ if (lock & 0x20)
+ *status |= FE_HAS_VITERBI;
+
+ if (lock & 0x40)
+ *status |= FE_HAS_SYNC;
+
+ if ((lock & 0x7b) == 0x7b)
+ *status |= FE_HAS_LOCK;
+
+ return 0;
+}
+
+static int si21_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+
+ /*status = si21_readreg(state, ANALOG_AGC_POWER_LEVEL_REG,
+ (u8*)agclevel, 0x01);*/
+
+ u16 signal = (3 * si21_readreg(state, 0x27) *
+ si21_readreg(state, 0x28));
+
+ dprintk("%s : AGCPWR: 0x%02x%02x, signal=0x%04x\n", __func__,
+ si21_readreg(state, 0x27),
+ si21_readreg(state, 0x28), (int) signal);
+
+ signal <<= 4;
+ *strength = signal;
+
+ return 0;
+}
+
+static int si21_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+
+ dprintk("%s\n", __func__);
+
+ if (state->errmode != STATUS_BER)
+ return 0;
+
+ *ber = (si21_readreg(state, 0x1d) << 8) |
+ si21_readreg(state, 0x1e);
+
+ return 0;
+}
+
+static int si21_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+
+ s32 xsnr = 0xffff - ((si21_readreg(state, 0x24) << 8) |
+ si21_readreg(state, 0x25));
+ xsnr = 3 * (xsnr - 0xa100);
+ *snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr;
+
+ dprintk("%s\n", __func__);
+
+ return 0;
+}
+
+static int si21_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+
+ dprintk("%s\n", __func__);
+
+ if (state->errmode != STATUS_UCBLOCKS)
+ *ucblocks = 0;
+ else
+ *ucblocks = (si21_readreg(state, 0x1d) << 8) |
+ si21_readreg(state, 0x1e);
+
+ return 0;
+}
+
+/* initiates a channel acquisition sequence
+ using the specified symbol rate and code rate */
+static int si21xx_setacquire(struct dvb_frontend *fe, int symbrate,
+ fe_code_rate_t crate)
+{
+
+ struct si21xx_state *state = fe->demodulator_priv;
+ u8 coderates[] = {
+ 0x0, 0x01, 0x02, 0x04, 0x00,
+ 0x8, 0x10, 0x20, 0x00, 0x3f
+ };
+
+ u8 coderate_ptr;
+ int status;
+ u8 start_acq = 0x80;
+ u8 reg, regs[3];
+
+ dprintk("%s\n", __func__);
+
+ status = PASS;
+ coderate_ptr = coderates[crate];
+
+ si21xx_set_symbolrate(fe, symbrate);
+
+ /* write code rates to use in the Viterbi search */
+ status |= si21_writeregs(state,
+ VIT_SRCH_CTRL_REG_1,
+ &coderate_ptr, 0x01);
+
+ /* clear acq_start bit */
+ status |= si21_readregs(state, ACQ_CTRL_REG_2, ®, 0x01);
+ reg &= ~start_acq;
+ status |= si21_writeregs(state, ACQ_CTRL_REG_2, ®, 0x01);
+
+ /* use new Carrier Frequency Offset Estimator (QuickLock) */
+ regs[0] = 0xCB;
+ regs[1] = 0x40;
+ regs[2] = 0xCB;
+
+ status |= si21_writeregs(state,
+ TWO_DB_BNDWDTH_THRSHLD_REG,
+ ®s[0], 0x03);
+ reg = 0x56;
+ status |= si21_writeregs(state,
+ LSA_CTRL_REG_1, ®, 1);
+ reg = 0x05;
+ status |= si21_writeregs(state,
+ BLIND_SCAN_CTRL_REG, ®, 1);
+ /* start automatic acq */
+ status |= si21_writeregs(state,
+ ACQ_CTRL_REG_2, &start_acq, 0x01);
+
+ return status;
+}
+
+static int si21xx_set_property(struct dvb_frontend *fe, struct dtv_property *p)
+{
+ dprintk("%s(..)\n", __func__);
+ return 0;
+}
+
+static int si21xx_get_property(struct dvb_frontend *fe, struct dtv_property *p)
+{
+ dprintk("%s(..)\n", __func__);
+ return 0;
+}
+
+static int si21xx_set_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *dfp)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ /* freq Channel carrier frequency in KHz (i.e. 1550000 KHz)
+ datarate Channel symbol rate in Sps (i.e. 22500000 Sps)*/
+
+ /* in MHz */
+ unsigned char coarse_tune_freq;
+ int fine_tune_freq;
+ unsigned char sample_rate = 0;
+ /* boolean */
+ unsigned int inband_interferer_ind;
+
+ /* INTERMEDIATE VALUES */
+ int icoarse_tune_freq; /* MHz */
+ int ifine_tune_freq; /* MHz */
+ unsigned int band_high;
+ unsigned int band_low;
+ unsigned int x1;
+ unsigned int x2;
+ int i;
+ unsigned int inband_interferer_div2[ALLOWABLE_FS_COUNT] = {
+ FALSE, FALSE, FALSE, FALSE, FALSE,
+ FALSE, FALSE, FALSE, FALSE, FALSE
+ };
+ unsigned int inband_interferer_div4[ALLOWABLE_FS_COUNT] = {
+ FALSE, FALSE, FALSE, FALSE, FALSE,
+ FALSE, FALSE, FALSE, FALSE, FALSE
+ };
+
+ int status;
+
+ /* allowable sample rates for ADC in MHz */
+ int afs[ALLOWABLE_FS_COUNT] = { 200, 192, 193, 194, 195,
+ 196, 204, 205, 206, 207
+ };
+ /* in MHz */
+ int if_limit_high;
+ int if_limit_low;
+ int lnb_lo;
+ int lnb_uncertanity;
+
+ int rf_freq;
+ int data_rate;
+ unsigned char regs[4];
+
+ dprintk("%s : FE_SET_FRONTEND\n", __func__);
+
+ if (c->delivery_system != SYS_DVBS) {
+ dprintk("%s: unsupported delivery system selected (%d)\n",
+ __func__, c->delivery_system);
+ return -EOPNOTSUPP;
+ }
+
+ for (i = 0; i < ALLOWABLE_FS_COUNT; ++i)
+ inband_interferer_div2[i] = inband_interferer_div4[i] = FALSE;
+
+ if_limit_high = -700000;
+ if_limit_low = -100000;
+ /* in MHz */
+ lnb_lo = 0;
+ lnb_uncertanity = 0;
+
+ rf_freq = 10 * c->frequency ;
+ data_rate = c->symbol_rate / 100;
+
+ status = PASS;
+
+ band_low = (rf_freq - lnb_lo) - ((lnb_uncertanity * 200)
+ + (data_rate * 135)) / 200;
+
+ band_high = (rf_freq - lnb_lo) + ((lnb_uncertanity * 200)
+ + (data_rate * 135)) / 200;
+
+
+ icoarse_tune_freq = 100000 *
+ (((rf_freq - lnb_lo) -
+ (if_limit_low + if_limit_high) / 2)
+ / 100000);
+
+ ifine_tune_freq = (rf_freq - lnb_lo) - icoarse_tune_freq ;
+
+ for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) {
+ x1 = ((rf_freq - lnb_lo) / (afs[i] * 2500)) *
+ (afs[i] * 2500) + afs[i] * 2500;
+
+ x2 = ((rf_freq - lnb_lo) / (afs[i] * 2500)) *
+ (afs[i] * 2500);
+
+ if (((band_low < x1) && (x1 < band_high)) ||
+ ((band_low < x2) && (x2 < band_high)))
+ inband_interferer_div4[i] = TRUE;
+
+ }
+
+ for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) {
+ x1 = ((rf_freq - lnb_lo) / (afs[i] * 5000)) *
+ (afs[i] * 5000) + afs[i] * 5000;
+
+ x2 = ((rf_freq - lnb_lo) / (afs[i] * 5000)) *
+ (afs[i] * 5000);
+
+ if (((band_low < x1) && (x1 < band_high)) ||
+ ((band_low < x2) && (x2 < band_high)))
+ inband_interferer_div2[i] = TRUE;
+ }
+
+ inband_interferer_ind = TRUE;
+ for (i = 0; i < ALLOWABLE_FS_COUNT; ++i)
+ inband_interferer_ind &= inband_interferer_div2[i] |
+ inband_interferer_div4[i];
+
+ if (inband_interferer_ind) {
+ for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) {
+ if (inband_interferer_div2[i] == FALSE) {
+ sample_rate = (u8) afs[i];
+ break;
+ }
+ }
+ } else {
+ for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) {
+ if ((inband_interferer_div2[i] |
+ inband_interferer_div4[i]) == FALSE) {
+ sample_rate = (u8) afs[i];
+ break;
+ }
+ }
+
+ }
+
+ if (sample_rate > 207 || sample_rate < 192)
+ sample_rate = 200;
+
+ fine_tune_freq = ((0x4000 * (ifine_tune_freq / 10)) /
+ ((sample_rate) * 1000));
+
+ coarse_tune_freq = (u8)(icoarse_tune_freq / 100000);
+
+ regs[0] = sample_rate;
+ regs[1] = coarse_tune_freq;
+ regs[2] = fine_tune_freq & 0xFF;
+ regs[3] = fine_tune_freq >> 8 & 0xFF;
+
+ status |= si21_writeregs(state, PLL_DIVISOR_REG, ®s[0], 0x04);
+
+ state->fs = sample_rate;/*ADC MHz*/
+ si21xx_setacquire(fe, c->symbol_rate, c->fec_inner);
+
+ return 0;
+}
+
+static int si21xx_sleep(struct dvb_frontend *fe)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+ u8 regdata;
+
+ dprintk("%s\n", __func__);
+
+ si21_readregs(state, SYSTEM_MODE_REG, ®data, 0x01);
+ regdata |= 1 << 6;
+ si21_writeregs(state, SYSTEM_MODE_REG, ®data, 0x01);
+ state->initialised = 0;
+
+ return 0;
+}
+
+static void si21xx_release(struct dvb_frontend *fe)
+{
+ struct si21xx_state *state = fe->demodulator_priv;
+
+ dprintk("%s\n", __func__);
+
+ kfree(state);
+}
+
+static struct dvb_frontend_ops si21xx_ops = {
+
+ .info = {
+ .name = "SL SI21XX DVB-S",
+ .type = FE_QPSK,
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_stepsize = 125, /* kHz for QPSK frontends */
+ .frequency_tolerance = 0,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .symbol_rate_tolerance = 500, /* ppm */
+ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
+ FE_CAN_QPSK |
+ FE_CAN_FEC_AUTO
+ },
+
+ .release = si21xx_release,
+ .init = si21xx_init,
+ .sleep = si21xx_sleep,
+ .write = si21_write,
+ .read_status = si21_read_status,
+ .read_ber = si21_read_ber,
+ .read_signal_strength = si21_read_signal_strength,
+ .read_snr = si21_read_snr,
+ .read_ucblocks = si21_read_ucblocks,
+ .diseqc_send_master_cmd = si21xx_send_diseqc_msg,
+ .diseqc_send_burst = si21xx_send_diseqc_burst,
+ .set_tone = si21xx_set_tone,
+ .set_voltage = si21xx_set_voltage,
+
+ .set_property = si21xx_set_property,
+ .get_property = si21xx_get_property,
+ .set_frontend = si21xx_set_frontend,
+};
+
+struct dvb_frontend *si21xx_attach(const struct si21xx_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct si21xx_state *state = NULL;
+ int id;
+
+ dprintk("%s\n", __func__);
+
+ /* allocate memory for the internal state */
+ state = kmalloc(sizeof(struct si21xx_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ state->initialised = 0;
+ state->errmode = STATUS_BER;
+
+ /* check if the demod is there */
+ id = si21_readreg(state, SYSTEM_MODE_REG);
+ si21_writereg(state, SYSTEM_MODE_REG, id | 0x40); /* standby off */
+ msleep(200);
+ id = si21_readreg(state, 0x00);
+
+ /* register 0x00 contains:
+ 0x34 for SI2107
+ 0x24 for SI2108
+ 0x14 for SI2109
+ 0x04 for SI2110
+ */
+ if (id != 0x04 && id != 0x14)
+ goto error;
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &si21xx_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+
+error:
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL(si21xx_attach);
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("SL SI21XX DVB Demodulator driver");
+MODULE_AUTHOR("Igor M. Liplianin");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/si21xx.h b/drivers/media/dvb/frontends/si21xx.h
new file mode 100644
index 0000000..141b5b8
--- /dev/null
+++ b/drivers/media/dvb/frontends/si21xx.h
@@ -0,0 +1,37 @@
+#ifndef SI21XX_H
+#define SI21XX_H
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+struct si21xx_config {
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* minimum delay before retuning */
+ int min_delay_ms;
+};
+
+#if defined(CONFIG_DVB_SI21XX) || \
+ (defined(CONFIG_DVB_SI21XX_MODULE) && defined(MODULE))
+extern struct dvb_frontend *si21xx_attach(const struct si21xx_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *si21xx_attach(
+ const struct si21xx_config *config, struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+static inline int si21xx_writeregister(struct dvb_frontend *fe, u8 reg, u8 val)
+{
+ int r = 0;
+ u8 buf[] = {reg, val};
+ if (fe->ops.write)
+ r = fe->ops.write(fe, buf, 2);
+ return r;
+}
+
+#endif
diff --git a/drivers/media/dvb/frontends/sp887x.c b/drivers/media/dvb/frontends/sp887x.c
index 4543609..559509a 100644
--- a/drivers/media/dvb/frontends/sp887x.c
+++ b/drivers/media/dvb/frontends/sp887x.c
@@ -337,7 +337,8 @@
struct dvb_frontend_parameters *p)
{
struct sp887x_state* state = fe->demodulator_priv;
- int actual_freq, err;
+ unsigned actual_freq;
+ int err;
u16 val, reg0xc05;
if (p->u.ofdm.bandwidth != BANDWIDTH_8_MHZ &&
diff --git a/drivers/media/dvb/frontends/stb6000.c b/drivers/media/dvb/frontends/stb6000.c
new file mode 100644
index 0000000..0e2cb0d
--- /dev/null
+++ b/drivers/media/dvb/frontends/stb6000.c
@@ -0,0 +1,255 @@
+ /*
+ Driver for ST STB6000 DVBS Silicon tuner
+
+ Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by)
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+#include <linux/module.h>
+#include <linux/dvb/frontend.h>
+#include <asm/types.h>
+
+#include "stb6000.h"
+
+static int debug;
+#define dprintk(args...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG "stb6000: " args); \
+ } while (0)
+
+struct stb6000_priv {
+ /* i2c details */
+ int i2c_address;
+ struct i2c_adapter *i2c;
+ u32 frequency;
+};
+
+static int stb6000_release(struct dvb_frontend *fe)
+{
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+static int stb6000_sleep(struct dvb_frontend *fe)
+{
+ struct stb6000_priv *priv = fe->tuner_priv;
+ int ret;
+ u8 buf[] = { 10, 0 };
+ struct i2c_msg msg = {
+ .addr = priv->i2c_address,
+ .flags = 0,
+ .buf = buf,
+ .len = 2
+ };
+
+ dprintk("%s:\n", __func__);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ ret = i2c_transfer(priv->i2c, &msg, 1);
+ if (ret != 1)
+ dprintk("%s: i2c error\n", __func__);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ return (ret == 1) ? 0 : ret;
+}
+
+static int stb6000_set_params(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+{
+ struct stb6000_priv *priv = fe->tuner_priv;
+ unsigned int n, m;
+ int ret;
+ u32 freq_mhz;
+ int bandwidth;
+ u8 buf[12];
+ struct i2c_msg msg = {
+ .addr = priv->i2c_address,
+ .flags = 0,
+ .buf = buf,
+ .len = 12
+ };
+
+ dprintk("%s:\n", __func__);
+
+ freq_mhz = params->frequency / 1000;
+ bandwidth = params->u.qpsk.symbol_rate / 1000000;
+
+ if (bandwidth > 31)
+ bandwidth = 31;
+
+ if ((freq_mhz > 949) && (freq_mhz < 2151)) {
+ buf[0] = 0x01;
+ buf[1] = 0xac;
+ if (freq_mhz < 1950)
+ buf[1] = 0xaa;
+ if (freq_mhz < 1800)
+ buf[1] = 0xa8;
+ if (freq_mhz < 1650)
+ buf[1] = 0xa6;
+ if (freq_mhz < 1530)
+ buf[1] = 0xa5;
+ if (freq_mhz < 1470)
+ buf[1] = 0xa4;
+ if (freq_mhz < 1370)
+ buf[1] = 0xa2;
+ if (freq_mhz < 1300)
+ buf[1] = 0xa1;
+ if (freq_mhz < 1200)
+ buf[1] = 0xa0;
+ if (freq_mhz < 1075)
+ buf[1] = 0xbc;
+ if (freq_mhz < 1000)
+ buf[1] = 0xba;
+ if (freq_mhz < 1075) {
+ n = freq_mhz / 8; /* vco=lo*4 */
+ m = 2;
+ } else {
+ n = freq_mhz / 16; /* vco=lo*2 */
+ m = 1;
+ }
+ buf[2] = n >> 1;
+ buf[3] = (unsigned char)(((n & 1) << 7) |
+ (m * freq_mhz - n * 16) | 0x60);
+ buf[4] = 0x04;
+ buf[5] = 0x0e;
+
+ buf[6] = (unsigned char)(bandwidth);
+
+ buf[7] = 0xd8;
+ buf[8] = 0xd0;
+ buf[9] = 0x50;
+ buf[10] = 0xeb;
+ buf[11] = 0x4f;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ ret = i2c_transfer(priv->i2c, &msg, 1);
+ if (ret != 1)
+ dprintk("%s: i2c error\n", __func__);
+
+ udelay(10);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ buf[0] = 0x07;
+ buf[1] = 0xdf;
+ buf[2] = 0xd0;
+ buf[3] = 0x50;
+ buf[4] = 0xfb;
+ msg.len = 5;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ ret = i2c_transfer(priv->i2c, &msg, 1);
+ if (ret != 1)
+ dprintk("%s: i2c error\n", __func__);
+
+ udelay(10);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ priv->frequency = freq_mhz * 1000;
+
+ return (ret == 1) ? 0 : ret;
+ }
+ return -1;
+}
+
+static int stb6000_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct stb6000_priv *priv = fe->tuner_priv;
+ *frequency = priv->frequency;
+ return 0;
+}
+
+static struct dvb_tuner_ops stb6000_tuner_ops = {
+ .info = {
+ .name = "ST STB6000",
+ .frequency_min = 950000,
+ .frequency_max = 2150000
+ },
+ .release = stb6000_release,
+ .sleep = stb6000_sleep,
+ .set_params = stb6000_set_params,
+ .get_frequency = stb6000_get_frequency,
+};
+
+struct dvb_frontend *stb6000_attach(struct dvb_frontend *fe, int addr,
+ struct i2c_adapter *i2c)
+{
+ struct stb6000_priv *priv = NULL;
+ u8 b0[] = { 0 };
+ u8 b1[] = { 0, 0 };
+ struct i2c_msg msg[2] = {
+ {
+ .addr = addr,
+ .flags = 0,
+ .buf = b0,
+ .len = 0
+ }, {
+ .addr = addr,
+ .flags = I2C_M_RD,
+ .buf = b1,
+ .len = 2
+ }
+ };
+ int ret;
+
+ dprintk("%s:\n", __func__);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ /* is some i2c device here ? */
+ ret = i2c_transfer(i2c, msg, 2);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ if (ret != 2)
+ return NULL;
+
+ priv = kzalloc(sizeof(struct stb6000_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+
+ priv->i2c_address = addr;
+ priv->i2c = i2c;
+
+ memcpy(&fe->ops.tuner_ops, &stb6000_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ fe->tuner_priv = priv;
+
+ return fe;
+}
+EXPORT_SYMBOL(stb6000_attach);
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("DVB STB6000 driver");
+MODULE_AUTHOR("Igor M. Liplianin <liplianin@me.by>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/stb6000.h b/drivers/media/dvb/frontends/stb6000.h
new file mode 100644
index 0000000..7be479c
--- /dev/null
+++ b/drivers/media/dvb/frontends/stb6000.h
@@ -0,0 +1,51 @@
+ /*
+ Driver for ST stb6000 DVBS Silicon tuner
+
+ Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by)
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+#ifndef __DVB_STB6000_H__
+#define __DVB_STB6000_H__
+
+#include <linux/i2c.h>
+#include "dvb_frontend.h"
+
+/**
+ * Attach a stb6000 tuner to the supplied frontend structure.
+ *
+ * @param fe Frontend to attach to.
+ * @param addr i2c address of the tuner.
+ * @param i2c i2c adapter to use.
+ * @return FE pointer on success, NULL on failure.
+ */
+#if defined(CONFIG_DVB_STB6000) || (defined(CONFIG_DVB_STB6000_MODULE) \
+ && defined(MODULE))
+extern struct dvb_frontend *stb6000_attach(struct dvb_frontend *fe, int addr,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *stb6000_attach(struct dvb_frontend *fe,
+ int addr,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_STB6000 */
+
+#endif /* __DVB_STB6000_H__ */
diff --git a/drivers/media/dvb/frontends/stv0288.c b/drivers/media/dvb/frontends/stv0288.c
new file mode 100644
index 0000000..ff1194d
--- /dev/null
+++ b/drivers/media/dvb/frontends/stv0288.c
@@ -0,0 +1,618 @@
+/*
+ Driver for ST STV0288 demodulator
+ Copyright (C) 2006 Georg Acher, BayCom GmbH, acher (at) baycom (dot) de
+ for Reel Multimedia
+ Copyright (C) 2008 TurboSight.com, Bob Liu <bob@turbosight.com>
+ Copyright (C) 2008 Igor M. Liplianin <liplianin@me.by>
+ Removed stb6000 specific tuner code and revised some
+ procedures.
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <asm/div64.h>
+
+#include "dvb_frontend.h"
+#include "stv0288.h"
+
+struct stv0288_state {
+ struct i2c_adapter *i2c;
+ const struct stv0288_config *config;
+ struct dvb_frontend frontend;
+
+ u8 initialised:1;
+ u32 tuner_frequency;
+ u32 symbol_rate;
+ fe_code_rate_t fec_inner;
+ int errmode;
+};
+
+#define STATUS_BER 0
+#define STATUS_UCBLOCKS 1
+
+static int debug;
+static int debug_legacy_dish_switch;
+#define dprintk(args...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG "stv0288: " args); \
+ } while (0)
+
+
+static int stv0288_writeregI(struct stv0288_state *state, u8 reg, u8 data)
+{
+ int ret;
+ u8 buf[] = { reg, data };
+ struct i2c_msg msg = {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = buf,
+ .len = 2
+ };
+
+ ret = i2c_transfer(state->i2c, &msg, 1);
+
+ if (ret != 1)
+ dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, "
+ "ret == %i)\n", __func__, reg, data, ret);
+
+ return (ret != 1) ? -EREMOTEIO : 0;
+}
+
+static int stv0288_write(struct dvb_frontend *fe, u8 *buf, int len)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+
+ if (len != 2)
+ return -EINVAL;
+
+ return stv0288_writeregI(state, buf[0], buf[1]);
+}
+
+static u8 stv0288_readreg(struct stv0288_state *state, u8 reg)
+{
+ int ret;
+ u8 b0[] = { reg };
+ u8 b1[] = { 0 };
+ struct i2c_msg msg[] = {
+ {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = b0,
+ .len = 1
+ }, {
+ .addr = state->config->demod_address,
+ .flags = I2C_M_RD,
+ .buf = b1,
+ .len = 1
+ }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2)
+ dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n",
+ __func__, reg, ret);
+
+ return b1[0];
+}
+
+static int stv0288_set_symbolrate(struct dvb_frontend *fe, u32 srate)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+ unsigned int temp;
+ unsigned char b[3];
+
+ if ((srate < 1000000) || (srate > 45000000))
+ return -EINVAL;
+
+ temp = (unsigned int)srate / 1000;
+
+ temp = temp * 32768;
+ temp = temp / 25;
+ temp = temp / 125;
+ b[0] = (unsigned char)((temp >> 12) & 0xff);
+ b[1] = (unsigned char)((temp >> 4) & 0xff);
+ b[2] = (unsigned char)((temp << 4) & 0xf0);
+ stv0288_writeregI(state, 0x28, 0x80); /* SFRH */
+ stv0288_writeregI(state, 0x29, 0); /* SFRM */
+ stv0288_writeregI(state, 0x2a, 0); /* SFRL */
+
+ stv0288_writeregI(state, 0x28, b[0]);
+ stv0288_writeregI(state, 0x29, b[1]);
+ stv0288_writeregI(state, 0x2a, b[2]);
+ dprintk("stv0288: stv0288_set_symbolrate\n");
+
+ return 0;
+}
+
+static int stv0288_send_diseqc_msg(struct dvb_frontend *fe,
+ struct dvb_diseqc_master_cmd *m)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+
+ int i;
+
+ dprintk("%s\n", __func__);
+
+ stv0288_writeregI(state, 0x09, 0);
+ msleep(30);
+ stv0288_writeregI(state, 0x05, 0x16);
+
+ for (i = 0; i < m->msg_len; i++) {
+ if (stv0288_writeregI(state, 0x06, m->msg[i]))
+ return -EREMOTEIO;
+ msleep(12);
+ }
+
+ return 0;
+}
+
+static int stv0288_send_diseqc_burst(struct dvb_frontend *fe,
+ fe_sec_mini_cmd_t burst)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+
+ dprintk("%s\n", __func__);
+
+ if (stv0288_writeregI(state, 0x05, 0x16))/* burst mode */
+ return -EREMOTEIO;
+
+ if (stv0288_writeregI(state, 0x06, burst == SEC_MINI_A ? 0x00 : 0xff))
+ return -EREMOTEIO;
+
+ if (stv0288_writeregI(state, 0x06, 0x12))
+ return -EREMOTEIO;
+
+ return 0;
+}
+
+static int stv0288_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+
+ switch (tone) {
+ case SEC_TONE_ON:
+ if (stv0288_writeregI(state, 0x05, 0x10))/* burst mode */
+ return -EREMOTEIO;
+ return stv0288_writeregI(state, 0x06, 0xff);
+
+ case SEC_TONE_OFF:
+ if (stv0288_writeregI(state, 0x05, 0x13))/* burst mode */
+ return -EREMOTEIO;
+ return stv0288_writeregI(state, 0x06, 0x00);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static u8 stv0288_inittab[] = {
+ 0x01, 0x15,
+ 0x02, 0x20,
+ 0x09, 0x0,
+ 0x0a, 0x4,
+ 0x0b, 0x0,
+ 0x0c, 0x0,
+ 0x0d, 0x0,
+ 0x0e, 0xd4,
+ 0x0f, 0x30,
+ 0x11, 0x80,
+ 0x12, 0x03,
+ 0x13, 0x48,
+ 0x14, 0x84,
+ 0x15, 0x45,
+ 0x16, 0xb7,
+ 0x17, 0x9c,
+ 0x18, 0x0,
+ 0x19, 0xa6,
+ 0x1a, 0x88,
+ 0x1b, 0x8f,
+ 0x1c, 0xf0,
+ 0x20, 0x0b,
+ 0x21, 0x54,
+ 0x22, 0x0,
+ 0x23, 0x0,
+ 0x2b, 0xff,
+ 0x2c, 0xf7,
+ 0x30, 0x0,
+ 0x31, 0x1e,
+ 0x32, 0x14,
+ 0x33, 0x0f,
+ 0x34, 0x09,
+ 0x35, 0x0c,
+ 0x36, 0x05,
+ 0x37, 0x2f,
+ 0x38, 0x16,
+ 0x39, 0xbe,
+ 0x3a, 0x0,
+ 0x3b, 0x13,
+ 0x3c, 0x11,
+ 0x3d, 0x30,
+ 0x40, 0x63,
+ 0x41, 0x04,
+ 0x42, 0x60,
+ 0x43, 0x00,
+ 0x44, 0x00,
+ 0x45, 0x00,
+ 0x46, 0x00,
+ 0x47, 0x00,
+ 0x4a, 0x00,
+ 0x50, 0x10,
+ 0x51, 0x38,
+ 0x52, 0x21,
+ 0x58, 0x54,
+ 0x59, 0x86,
+ 0x5a, 0x0,
+ 0x5b, 0x9b,
+ 0x5c, 0x08,
+ 0x5d, 0x7f,
+ 0x5e, 0x0,
+ 0x5f, 0xff,
+ 0x70, 0x0,
+ 0x71, 0x0,
+ 0x72, 0x0,
+ 0x74, 0x0,
+ 0x75, 0x0,
+ 0x76, 0x0,
+ 0x81, 0x0,
+ 0x82, 0x3f,
+ 0x83, 0x3f,
+ 0x84, 0x0,
+ 0x85, 0x0,
+ 0x88, 0x0,
+ 0x89, 0x0,
+ 0x8a, 0x0,
+ 0x8b, 0x0,
+ 0x8c, 0x0,
+ 0x90, 0x0,
+ 0x91, 0x0,
+ 0x92, 0x0,
+ 0x93, 0x0,
+ 0x94, 0x1c,
+ 0x97, 0x0,
+ 0xa0, 0x48,
+ 0xa1, 0x0,
+ 0xb0, 0xb8,
+ 0xb1, 0x3a,
+ 0xb2, 0x10,
+ 0xb3, 0x82,
+ 0xb4, 0x80,
+ 0xb5, 0x82,
+ 0xb6, 0x82,
+ 0xb7, 0x82,
+ 0xb8, 0x20,
+ 0xb9, 0x0,
+ 0xf0, 0x0,
+ 0xf1, 0x0,
+ 0xf2, 0xc0,
+ 0x51, 0x36,
+ 0x52, 0x09,
+ 0x53, 0x94,
+ 0x54, 0x62,
+ 0x55, 0x29,
+ 0x56, 0x64,
+ 0x57, 0x2b,
+ 0xff, 0xff,
+};
+
+static int stv0288_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt)
+{
+ dprintk("%s: %s\n", __func__,
+ volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" :
+ volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??");
+
+ return 0;
+}
+
+static int stv0288_init(struct dvb_frontend *fe)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+ int i;
+ u8 reg;
+ u8 val;
+
+ dprintk("stv0288: init chip\n");
+ stv0288_writeregI(state, 0x41, 0x04);
+ msleep(50);
+
+ /* we have default inittab */
+ if (state->config->inittab == NULL) {
+ for (i = 0; !(stv0288_inittab[i] == 0xff &&
+ stv0288_inittab[i + 1] == 0xff); i += 2)
+ stv0288_writeregI(state, stv0288_inittab[i],
+ stv0288_inittab[i + 1]);
+ } else {
+ for (i = 0; ; i += 2) {
+ reg = state->config->inittab[i];
+ val = state->config->inittab[i+1];
+ if (reg == 0xff && val == 0xff)
+ break;
+ stv0288_writeregI(state, reg, val);
+ }
+ }
+ return 0;
+}
+
+static int stv0288_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+
+ u8 sync = stv0288_readreg(state, 0x24);
+ if (sync == 255)
+ sync = 0;
+
+ dprintk("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __func__, sync);
+
+ *status = 0;
+
+ if ((sync & 0x08) == 0x08) {
+ *status |= FE_HAS_LOCK;
+ dprintk("stv0288 has locked\n");
+ }
+
+ return 0;
+}
+
+static int stv0288_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+
+ if (state->errmode != STATUS_BER)
+ return 0;
+ *ber = (stv0288_readreg(state, 0x26) << 8) |
+ stv0288_readreg(state, 0x27);
+ dprintk("stv0288_read_ber %d\n", *ber);
+
+ return 0;
+}
+
+
+static int stv0288_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+
+ s32 signal = 0xffff - ((stv0288_readreg(state, 0x10) << 8));
+
+
+ signal = signal * 5 / 4;
+ *strength = (signal > 0xffff) ? 0xffff : (signal < 0) ? 0 : signal;
+ dprintk("stv0288_read_signal_strength %d\n", *strength);
+
+ return 0;
+}
+static int stv0288_sleep(struct dvb_frontend *fe)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+
+ stv0288_writeregI(state, 0x41, 0x84);
+ state->initialised = 0;
+
+ return 0;
+}
+static int stv0288_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+
+ s32 xsnr = 0xffff - ((stv0288_readreg(state, 0x2d) << 8)
+ | stv0288_readreg(state, 0x2e));
+ xsnr = 3 * (xsnr - 0xa100);
+ *snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr;
+ dprintk("stv0288_read_snr %d\n", *snr);
+
+ return 0;
+}
+
+static int stv0288_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+
+ if (state->errmode != STATUS_BER)
+ return 0;
+ *ucblocks = (stv0288_readreg(state, 0x26) << 8) |
+ stv0288_readreg(state, 0x27);
+ dprintk("stv0288_read_ber %d\n", *ucblocks);
+
+ return 0;
+}
+
+static int stv0288_set_property(struct dvb_frontend *fe, struct dtv_property *p)
+{
+ dprintk("%s(..)\n", __func__);
+ return 0;
+}
+
+static int stv0288_get_property(struct dvb_frontend *fe, struct dtv_property *p)
+{
+ dprintk("%s(..)\n", __func__);
+ return 0;
+}
+
+static int stv0288_set_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *dfp)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ char tm;
+ unsigned char tda[3];
+
+ dprintk("%s : FE_SET_FRONTEND\n", __func__);
+
+ if (c->delivery_system != SYS_DVBS) {
+ dprintk("%s: unsupported delivery "
+ "system selected (%d)\n",
+ __func__, c->delivery_system);
+ return -EOPNOTSUPP;
+ }
+
+ if (state->config->set_ts_params)
+ state->config->set_ts_params(fe, 0);
+
+ /* only frequency & symbol_rate are used for tuner*/
+ dfp->frequency = c->frequency;
+ dfp->u.qpsk.symbol_rate = c->symbol_rate;
+ if (fe->ops.tuner_ops.set_params) {
+ fe->ops.tuner_ops.set_params(fe, dfp);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+
+ udelay(10);
+ stv0288_set_symbolrate(fe, c->symbol_rate);
+ /* Carrier lock control register */
+ stv0288_writeregI(state, 0x15, 0xc5);
+
+ tda[0] = 0x2b; /* CFRM */
+ tda[2] = 0x0; /* CFRL */
+ for (tm = -6; tm < 7;) {
+ /* Viterbi status */
+ if (stv0288_readreg(state, 0x24) & 0x80)
+ break;
+
+ tda[2] += 40;
+ if (tda[2] < 40)
+ tm++;
+ tda[1] = (unsigned char)tm;
+ stv0288_writeregI(state, 0x2b, tda[1]);
+ stv0288_writeregI(state, 0x2c, tda[2]);
+ udelay(30);
+ }
+
+ state->tuner_frequency = c->frequency;
+ state->fec_inner = FEC_AUTO;
+ state->symbol_rate = c->symbol_rate;
+
+ return 0;
+}
+
+static int stv0288_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+
+ if (enable)
+ stv0288_writeregI(state, 0x01, 0xb5);
+ else
+ stv0288_writeregI(state, 0x01, 0x35);
+
+ udelay(1);
+
+ return 0;
+}
+
+static void stv0288_release(struct dvb_frontend *fe)
+{
+ struct stv0288_state *state = fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops stv0288_ops = {
+
+ .info = {
+ .name = "ST STV0288 DVB-S",
+ .type = FE_QPSK,
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_stepsize = 1000, /* kHz for QPSK frontends */
+ .frequency_tolerance = 0,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .symbol_rate_tolerance = 500, /* ppm */
+ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
+ FE_CAN_QPSK |
+ FE_CAN_FEC_AUTO
+ },
+
+ .release = stv0288_release,
+ .init = stv0288_init,
+ .sleep = stv0288_sleep,
+ .write = stv0288_write,
+ .i2c_gate_ctrl = stv0288_i2c_gate_ctrl,
+ .read_status = stv0288_read_status,
+ .read_ber = stv0288_read_ber,
+ .read_signal_strength = stv0288_read_signal_strength,
+ .read_snr = stv0288_read_snr,
+ .read_ucblocks = stv0288_read_ucblocks,
+ .diseqc_send_master_cmd = stv0288_send_diseqc_msg,
+ .diseqc_send_burst = stv0288_send_diseqc_burst,
+ .set_tone = stv0288_set_tone,
+ .set_voltage = stv0288_set_voltage,
+
+ .set_property = stv0288_set_property,
+ .get_property = stv0288_get_property,
+ .set_frontend = stv0288_set_frontend,
+};
+
+struct dvb_frontend *stv0288_attach(const struct stv0288_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct stv0288_state *state = NULL;
+ int id;
+
+ /* allocate memory for the internal state */
+ state = kmalloc(sizeof(struct stv0288_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ state->initialised = 0;
+ state->tuner_frequency = 0;
+ state->symbol_rate = 0;
+ state->fec_inner = 0;
+ state->errmode = STATUS_BER;
+
+ stv0288_writeregI(state, 0x41, 0x04);
+ msleep(200);
+ id = stv0288_readreg(state, 0x00);
+ dprintk("stv0288 id %x\n", id);
+
+ /* register 0x00 contains 0x11 for STV0288 */
+ if (id != 0x11)
+ goto error;
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &stv0288_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+
+error:
+ kfree(state);
+
+ return NULL;
+}
+EXPORT_SYMBOL(stv0288_attach);
+
+module_param(debug_legacy_dish_switch, int, 0444);
+MODULE_PARM_DESC(debug_legacy_dish_switch,
+ "Enable timing analysis for Dish Network legacy switches");
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("ST STV0288 DVB Demodulator driver");
+MODULE_AUTHOR("Georg Acher, Bob Liu, Igor liplianin");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/media/dvb/frontends/stv0288.h b/drivers/media/dvb/frontends/stv0288.h
new file mode 100644
index 0000000..f2b53db
--- /dev/null
+++ b/drivers/media/dvb/frontends/stv0288.h
@@ -0,0 +1,67 @@
+/*
+ Driver for ST STV0288 demodulator
+
+ Copyright (C) 2006 Georg Acher, BayCom GmbH, acher (at) baycom (dot) de
+ for Reel Multimedia
+ Copyright (C) 2008 TurboSight.com, <bob@turbosight.com>
+ Copyright (C) 2008 Igor M. Liplianin <liplianin@me.by>
+ Removed stb6000 specific tuner code and revised some
+ procedures.
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef STV0288_H
+#define STV0288_H
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+struct stv0288_config {
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ u8* inittab;
+
+ /* minimum delay before retuning */
+ int min_delay_ms;
+
+ int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured);
+};
+
+#if defined(CONFIG_DVB_STV0288) || (defined(CONFIG_DVB_STV0288_MODULE) && \
+ defined(MODULE))
+extern struct dvb_frontend *stv0288_attach(const struct stv0288_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *stv0288_attach(const struct stv0288_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_STV0288 */
+
+static inline int stv0288_writereg(struct dvb_frontend *fe, u8 reg, u8 val)
+{
+ int r = 0;
+ u8 buf[] = { reg, val };
+ if (fe->ops.write)
+ r = fe->ops.write(fe, buf, 2);
+ return r;
+}
+
+#endif /* STV0288_H */
diff --git a/drivers/media/dvb/frontends/stv0299.c b/drivers/media/dvb/frontends/stv0299.c
index 35435be..6c1cb19 100644
--- a/drivers/media/dvb/frontends/stv0299.c
+++ b/drivers/media/dvb/frontends/stv0299.c
@@ -559,6 +559,8 @@
int invval = 0;
dprintk ("%s : FE_SET_FRONTEND\n", __func__);
+ if (state->config->set_ts_params)
+ state->config->set_ts_params(fe, 0);
// set the inversion
if (p->inversion == INVERSION_OFF) invval = 0;
diff --git a/drivers/media/dvb/frontends/stv0299.h b/drivers/media/dvb/frontends/stv0299.h
index 3282f43..0fd96e2 100644
--- a/drivers/media/dvb/frontends/stv0299.h
+++ b/drivers/media/dvb/frontends/stv0299.h
@@ -89,15 +89,18 @@
int min_delay_ms;
/* Set the symbol rate */
- int (*set_symbol_rate)(struct dvb_frontend* fe, u32 srate, u32 ratio);
+ int (*set_symbol_rate)(struct dvb_frontend *fe, u32 srate, u32 ratio);
+
+ /* Set device param to start dma */
+ int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured);
};
#if defined(CONFIG_DVB_STV0299) || (defined(CONFIG_DVB_STV0299_MODULE) && defined(MODULE))
-extern struct dvb_frontend* stv0299_attach(const struct stv0299_config* config,
- struct i2c_adapter* i2c);
+extern struct dvb_frontend *stv0299_attach(const struct stv0299_config *config,
+ struct i2c_adapter *i2c);
#else
-static inline struct dvb_frontend* stv0299_attach(const struct stv0299_config* config,
- struct i2c_adapter* i2c)
+static inline struct dvb_frontend *stv0299_attach(const struct stv0299_config *config,
+ struct i2c_adapter *i2c)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL;
diff --git a/drivers/media/dvb/frontends/tdhd1.h b/drivers/media/dvb/frontends/tdhd1.h
new file mode 100644
index 0000000..51f1706
--- /dev/null
+++ b/drivers/media/dvb/frontends/tdhd1.h
@@ -0,0 +1,73 @@
+/*
+ * tdhd1.h - ALPS TDHD1-204A tuner support
+ *
+ * Copyright (C) 2008 Oliver Endriss <o.endriss@gmx.de>
+ *
+ *
+ * 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.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ *
+ * The project's page is at http://www.linuxtv.org
+ */
+
+#ifndef TDHD1_H
+#define TDHD1_H
+
+#include "tda1004x.h"
+
+static int alps_tdhd1_204_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char *name);
+
+static struct tda1004x_config alps_tdhd1_204a_config = {
+ .demod_address = 0x8,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_4M,
+ .agc_config = TDA10046_AGC_DEFAULT,
+ .if_freq = TDA10046_FREQ_3617,
+ .request_firmware = alps_tdhd1_204_request_firmware
+};
+
+static int alps_tdhd1_204a_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+{
+ struct i2c_adapter *i2c = fe->tuner_priv;
+ u8 data[4];
+ struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) };
+ u32 div;
+
+ div = (params->frequency + 36166666) / 166666;
+
+ data[0] = (div >> 8) & 0x7f;
+ data[1] = div & 0xff;
+ data[2] = 0x85;
+
+ if (params->frequency >= 174000000 && params->frequency <= 230000000)
+ data[3] = 0x02;
+ else if (params->frequency >= 470000000 && params->frequency <= 823000000)
+ data[3] = 0x0C;
+ else if (params->frequency > 823000000 && params->frequency <= 862000000)
+ data[3] = 0x8C;
+ else
+ return -EINVAL;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(i2c, &msg, 1) != 1)
+ return -EIO;
+
+ return 0;
+}
+
+#endif /* TDHD1_H */
diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig
index 41b5a98..867027c 100644
--- a/drivers/media/dvb/ttpci/Kconfig
+++ b/drivers/media/dvb/ttpci/Kconfig
@@ -86,6 +86,7 @@
select DVB_TDA10086 if !DVB_FE_CUSTOMISE
select DVB_TDA826X if !DVB_FE_CUSTOMISE
select DVB_LNBP21 if !DVB_FE_CUSTOMISE
+ select DVB_TDA1004X if !DVB_FE_CUSTOMISE
help
Support for simple SAA7146 based DVB cards (so called Budget-
or Nova-PCI cards) without onboard MPEG2 decoder, and without
diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c
index 0777e8f..c7c770c 100644
--- a/drivers/media/dvb/ttpci/av7110.c
+++ b/drivers/media/dvb/ttpci/av7110.c
@@ -88,6 +88,7 @@
static int wss_cfg_4_3 = 0x4008;
static int wss_cfg_16_9 = 0x0007;
static int tv_standard;
+static int full_ts;
module_param_named(debug, av7110_debug, int, 0644);
MODULE_PARM_DESC(debug, "debug level (bitmask, default 0)");
@@ -106,6 +107,8 @@
MODULE_PARM_DESC(volume, "initial volume: default 255 (range 0-255)");
module_param(budgetpatch, int, 0444);
MODULE_PARM_DESC(budgetpatch, "use budget-patch hardware modification: default 0 (0 no, 1 autodetect, 2 always)");
+module_param(full_ts, int, 0444);
+MODULE_PARM_DESC(full_ts, "enable code for full-ts hardware modification: 0 disable (default), 1 enable");
module_param(wss_cfg_4_3, int, 0444);
MODULE_PARM_DESC(wss_cfg_4_3, "WSS 4:3 - default 0x4008 - bit 15: disable, 14: burst mode, 13..0: wss data");
module_param(wss_cfg_16_9, int, 0444);
@@ -116,6 +119,8 @@
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
static void restart_feeds(struct av7110 *av7110);
+static int budget_start_feed(struct dvb_demux_feed *feed);
+static int budget_stop_feed(struct dvb_demux_feed *feed);
static int av7110_num;
@@ -376,9 +381,9 @@
irdebi(av7110, DEBISWAB, addr, 0, len);
}
-static void debiirq(unsigned long data)
+static void debiirq(unsigned long cookie)
{
- struct av7110 *av7110 = (struct av7110 *) data;
+ struct av7110 *av7110 = (struct av7110 *)cookie;
int type = av7110->debitype;
int handle = (type >> 8) & 0x1f;
unsigned int xfer = 0;
@@ -487,9 +492,9 @@
}
/* irq from av7110 firmware writing the mailbox register in the DPRAM */
-static void gpioirq(unsigned long data)
+static void gpioirq(unsigned long cookie)
{
- struct av7110 *av7110 = (struct av7110 *) data;
+ struct av7110 *av7110 = (struct av7110 *)cookie;
u32 rxbuf, txbuf;
int len;
@@ -806,6 +811,9 @@
dprintk(4, "%p\n", av7110);
+ if (av7110->full_ts)
+ return 0;
+
if (dvbdmxfilter->type == DMX_TYPE_SEC) {
if (hw_sections) {
buf[4] = (dvbdmxfilter->filter.filter_value[0] << 8) |
@@ -854,6 +862,9 @@
dprintk(4, "%p\n", av7110);
+ if (av7110->full_ts)
+ return 0;
+
handle = dvbdmxfilter->hw_handle;
if (handle >= 32) {
printk("%s tried to stop invalid filter %04x, filter type = %x\n",
@@ -913,7 +924,7 @@
return ret;
}
- if ((dvbdmxfeed->ts_type & TS_PACKET)) {
+ if ((dvbdmxfeed->ts_type & TS_PACKET) && !av7110->full_ts) {
if (dvbdmxfeed->pes_type == 0 && !(dvbdmx->pids[0] & 0x8000))
ret = av7110_av_start_record(av7110, RP_AUDIO, dvbdmxfeed);
if (dvbdmxfeed->pes_type == 1 && !(dvbdmx->pids[1] & 0x8000))
@@ -974,7 +985,7 @@
if (!demux->dmx.frontend)
return -EINVAL;
- if (feed->pid > 0x1fff)
+ if (!av7110->full_ts && feed->pid > 0x1fff)
return -EINVAL;
if (feed->type == DMX_TYPE_TS) {
@@ -1003,7 +1014,12 @@
}
}
- else if (feed->type == DMX_TYPE_SEC) {
+ if (av7110->full_ts) {
+ budget_start_feed(feed);
+ return ret;
+ }
+
+ if (feed->type == DMX_TYPE_SEC) {
int i;
for (i = 0; i < demux->filternum; i++) {
@@ -1050,7 +1066,12 @@
ret = StopHWFilter(feed->filter);
}
- if (!ret && feed->type == DMX_TYPE_SEC) {
+ if (av7110->full_ts) {
+ budget_stop_feed(feed);
+ return ret;
+ }
+
+ if (feed->type == DMX_TYPE_SEC) {
for (i = 0; i<demux->filternum; i++) {
if (demux->filter[i].state == DMX_STATE_GO &&
demux->filter[i].filter.parent == &feed->feed.sec) {
@@ -1074,6 +1095,7 @@
struct dvb_demux *dvbdmx = &av7110->demux;
struct dvb_demux_feed *feed;
int mode;
+ int feeding;
int i, j;
dprintk(4, "%p\n", av7110);
@@ -1082,6 +1104,8 @@
av7110->playing = 0;
av7110->rec_mode = 0;
+ feeding = av7110->feeding1; /* full_ts mod */
+
for (i = 0; i < dvbdmx->feednum; i++) {
feed = &dvbdmx->feed[i];
if (feed->state == DMX_STATE_GO) {
@@ -1099,6 +1123,8 @@
}
}
+ av7110->feeding1 = feeding; /* full_ts mod */
+
if (mode)
av7110_av_start_play(av7110, mode);
}
@@ -1197,8 +1223,9 @@
if (budget->feeding1)
return ++budget->feeding1;
- memset(budget->grabbing, 0x00, TS_HEIGHT * TS_WIDTH);
+ memset(budget->grabbing, 0x00, TS_BUFLEN);
budget->ttbp = 0;
+ SAA7146_ISR_CLEAR(budget->dev, MASK_10); /* VPE */
SAA7146_IER_ENABLE(budget->dev, MASK_10); /* VPE */
saa7146_write(budget->dev, MC1, (MASK_04 | MASK_20)); /* DMA3 on */
return ++budget->feeding1;
@@ -1233,18 +1260,14 @@
return status;
}
-static void vpeirq(unsigned long data)
+static void vpeirq(unsigned long cookie)
{
- struct av7110 *budget = (struct av7110 *) data;
+ struct av7110 *budget = (struct av7110 *)cookie;
u8 *mem = (u8 *) (budget->grabbing);
u32 olddma = budget->ttbp;
u32 newdma = saa7146_read(budget->dev, PCI_VDP3);
+ struct dvb_demux *demux = budget->full_ts ? &budget->demux : &budget->demux1;
- if (!budgetpatch) {
- printk("av7110.c: vpeirq() called while budgetpatch disabled!"
- " check saa7146 IER register\n");
- BUG();
- }
/* nearest lower position divisible by 188 */
newdma -= newdma % 188;
@@ -1268,11 +1291,11 @@
if (newdma > olddma)
/* no wraparound, dump olddma..newdma */
- dvb_dmx_swfilter_packets(&budget->demux1, mem + olddma, (newdma - olddma) / 188);
+ dvb_dmx_swfilter_packets(demux, mem + olddma, (newdma - olddma) / 188);
else {
/* wraparound, dump olddma..buflen and 0..newdma */
- dvb_dmx_swfilter_packets(&budget->demux1, mem + olddma, (TS_BUFLEN - olddma) / 188);
- dvb_dmx_swfilter_packets(&budget->demux1, mem, newdma / 188);
+ dvb_dmx_swfilter_packets(demux, mem + olddma, (TS_BUFLEN - olddma) / 188);
+ dvb_dmx_swfilter_packets(demux, mem, newdma / 188);
}
}
@@ -1294,8 +1317,8 @@
for (i = 0; i < 32; i++)
av7110->handle2filter[i] = NULL;
- dvbdemux->filternum = 32;
- dvbdemux->feednum = 32;
+ dvbdemux->filternum = (av7110->full_ts) ? 256 : 32;
+ dvbdemux->feednum = (av7110->full_ts) ? 256 : 32;
dvbdemux->start_feed = av7110_start_feed;
dvbdemux->stop_feed = av7110_stop_feed;
dvbdemux->write_to_decoder = av7110_write_to_decoder;
@@ -1305,7 +1328,7 @@
dvb_dmx_init(&av7110->demux);
av7110->demux.dmx.get_stc = dvb_get_stc;
- av7110->dmxdev.filternum = 32;
+ av7110->dmxdev.filternum = (av7110->full_ts) ? 256 : 32;
av7110->dmxdev.demux = &dvbdemux->dmx;
av7110->dmxdev.capabilities = 0;
@@ -1422,7 +1445,6 @@
return i2c_transfer(&av7110->i2c_adap, &msgs, 1);
}
-#if 0
u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg)
{
u8 mm1[] = {0x00};
@@ -1439,7 +1461,6 @@
return mm2[0];
}
-#endif
/****************************************************************************
* INITIALIZATION
@@ -2256,7 +2277,7 @@
if (!av7110->fe) {
/* FIXME: propagate the failure code from the lower layers */
ret = -ENOMEM;
- printk("dvb-ttpci: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n",
+ printk("dvb-ttpci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
av7110->dev->pci->vendor,
av7110->dev->pci->device,
av7110->dev->pci->subsystem_vendor,
@@ -2484,7 +2505,47 @@
av7110->dvb_adapter.proposed_mac);
ret = -ENOMEM;
- if (budgetpatch) {
+ /* full-ts mod? */
+ if (full_ts)
+ av7110->full_ts = true;
+
+ /* check for full-ts flag in eeprom */
+ if (i2c_readreg(av7110, 0xaa, 0) == 0x4f && i2c_readreg(av7110, 0xaa, 1) == 0x45) {
+ u8 flags = i2c_readreg(av7110, 0xaa, 2);
+ if (flags != 0xff && (flags & 0x01))
+ av7110->full_ts = true;
+ }
+
+ if (av7110->full_ts) {
+ printk(KERN_INFO "dvb-ttpci: full-ts mode enabled for saa7146 port B\n");
+ spin_lock_init(&av7110->feedlock1);
+ av7110->grabbing = saa7146_vmalloc_build_pgtable(pdev, length,
+ &av7110->pt);
+ if (!av7110->grabbing)
+ goto err_i2c_del_3;
+
+ saa7146_write(dev, DD1_STREAM_B, 0x00000000);
+ saa7146_write(dev, MC2, (MASK_10 | MASK_26));
+
+ saa7146_write(dev, DD1_INIT, 0x00000600);
+ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+
+ saa7146_write(dev, BRS_CTRL, 0x60000000);
+ saa7146_write(dev, MC2, MASK_08 | MASK_24);
+
+ /* dma3 */
+ saa7146_write(dev, PCI_BT_V1, 0x001c0000 | (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000));
+ saa7146_write(dev, BASE_ODD3, 0);
+ saa7146_write(dev, BASE_EVEN3, 0);
+ saa7146_write(dev, PROT_ADDR3, TS_WIDTH * TS_HEIGHT);
+ saa7146_write(dev, PITCH3, TS_WIDTH);
+ saa7146_write(dev, BASE_PAGE3, av7110->pt.dma | ME1 | 0x90);
+ saa7146_write(dev, NUM_LINE_BYTE3, (TS_HEIGHT << 16) | TS_WIDTH);
+ saa7146_write(dev, MC2, MASK_04 | MASK_20);
+
+ tasklet_init(&av7110->vpe_tasklet, vpeirq, (unsigned long) av7110);
+
+ } else if (budgetpatch) {
spin_lock_init(&av7110->feedlock1);
av7110->grabbing = saa7146_vmalloc_build_pgtable(pdev, length,
&av7110->pt);
@@ -2710,11 +2771,13 @@
#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE)
av7110_ir_exit(av7110);
#endif
- if (budgetpatch) {
- /* Disable RPS1 */
- saa7146_write(saa, MC1, MASK_29);
- /* VSYNC LOW (inactive) */
- saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO);
+ if (budgetpatch || av7110->full_ts) {
+ if (budgetpatch) {
+ /* Disable RPS1 */
+ saa7146_write(saa, MC1, MASK_29);
+ /* VSYNC LOW (inactive) */
+ saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO);
+ }
saa7146_write(saa, MC1, MASK_20); /* DMA3 off */
SAA7146_IER_DISABLE(saa, MASK_10);
SAA7146_ISR_CLEAR(saa, MASK_10);
@@ -2794,7 +2857,7 @@
tasklet_schedule(&av7110->gpio_tasklet);
}
- if ((*isr & MASK_10) && budgetpatch)
+ if (*isr & MASK_10)
tasklet_schedule(&av7110->vpe_tasklet);
}
diff --git a/drivers/media/dvb/ttpci/av7110.h b/drivers/media/dvb/ttpci/av7110.h
index 55f23dd..d85b851 100644
--- a/drivers/media/dvb/ttpci/av7110.h
+++ b/drivers/media/dvb/ttpci/av7110.h
@@ -192,6 +192,7 @@
unsigned char *grabbing;
struct saa7146_pgtable pt;
struct tasklet_struct vpe_tasklet;
+ bool full_ts;
int fe_synced;
struct mutex pid_mutex;
diff --git a/drivers/media/dvb/ttpci/av7110_av.c b/drivers/media/dvb/ttpci/av7110_av.c
index 184647a..bdc62ac 100644
--- a/drivers/media/dvb/ttpci/av7110_av.c
+++ b/drivers/media/dvb/ttpci/av7110_av.c
@@ -788,6 +788,9 @@
dprintk(2, "av7110:%p, \n", av7110);
+ if (av7110->full_ts && demux->dmx.frontend->source != DMX_MEMORY_FE)
+ return 0;
+
switch (feed->pes_type) {
case 0:
if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY)
diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c
index b7d1f2f..1032ea7 100644
--- a/drivers/media/dvb/ttpci/budget-av.c
+++ b/drivers/media/dvb/ttpci/budget-av.c
@@ -57,6 +57,8 @@
#define SLOTSTATUS_READY 8
#define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY)
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
struct budget_av {
struct budget budget;
struct video_device *vd;
@@ -1049,7 +1051,7 @@
if (fe == NULL) {
printk(KERN_ERR "budget-av: A frontend driver was not found "
- "for device %04x/%04x subsystem %04x/%04x\n",
+ "for device [%04x:%04x] subsystem [%04x:%04x]\n",
saa->pci->vendor,
saa->pci->device,
saa->pci->subsystem_vendor,
@@ -1127,7 +1129,9 @@
dev->ext_priv = budget_av;
- if ((err = ttpci_budget_init(&budget_av->budget, dev, info, THIS_MODULE))) {
+ err = ttpci_budget_init(&budget_av->budget, dev, info, THIS_MODULE,
+ adapter_nr);
+ if (err) {
kfree(budget_av);
return err;
}
diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c
index 060e7c7..0a5aad4 100644
--- a/drivers/media/dvb/ttpci/budget-ci.c
+++ b/drivers/media/dvb/ttpci/budget-ci.c
@@ -92,6 +92,8 @@
module_param(ir_debug, int, 0644);
MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding");
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
struct budget_ci_ir {
struct input_dev *dev;
struct tasklet_struct msp430_irq_tasklet;
@@ -1153,7 +1155,7 @@
}
if (budget_ci->budget.dvb_frontend == NULL) {
- printk("budget-ci: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n",
+ printk("budget-ci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
budget_ci->budget.dev->pci->vendor,
budget_ci->budget.dev->pci->device,
budget_ci->budget.dev->pci->subsystem_vendor,
@@ -1183,7 +1185,8 @@
dev->ext_priv = budget_ci;
- err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE);
+ err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE,
+ adapter_nr);
if (err)
goto out2;
diff --git a/drivers/media/dvb/ttpci/budget-core.c b/drivers/media/dvb/ttpci/budget-core.c
index 6f4ddb6..ba18e56 100644
--- a/drivers/media/dvb/ttpci/budget-core.c
+++ b/drivers/media/dvb/ttpci/budget-core.c
@@ -57,8 +57,6 @@
MODULE_PARM_DESC(debug, "Turn on/off budget debugging (default:off).");
MODULE_PARM_DESC(bufsize, "DMA buffer size in KB, default: 188, min: 188, max: 1410 (Activy: 564)");
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
/****************************************************************************
* TT budget / WinTV Nova
****************************************************************************/
@@ -411,7 +409,7 @@
int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
struct saa7146_pci_extension_data *info,
- struct module *owner)
+ struct module *owner, short *adapter_nums)
{
int ret = 0;
struct budget_info *bi = info->ext_priv;
@@ -474,7 +472,7 @@
printk("%s: dma buffer size %u\n", budget->dev->name, budget->buffer_size);
ret = dvb_register_adapter(&budget->dvb_adapter, budget->card->name,
- owner, &budget->dev->pci->dev, adapter_nr);
+ owner, &budget->dev->pci->dev, adapter_nums);
if (ret < 0)
return ret;
diff --git a/drivers/media/dvb/ttpci/budget-patch.c b/drivers/media/dvb/ttpci/budget-patch.c
index aa5ed4e..60136688 100644
--- a/drivers/media/dvb/ttpci/budget-patch.c
+++ b/drivers/media/dvb/ttpci/budget-patch.c
@@ -39,6 +39,8 @@
#include "bsru6.h"
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
#define budget_patch budget
static struct saa7146_extension budget_extension;
@@ -360,7 +362,7 @@
}
if (budget->dvb_frontend == NULL) {
- printk("dvb-ttpci: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n",
+ printk("dvb-ttpci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
budget->dev->pci->vendor,
budget->dev->pci->device,
budget->dev->pci->subsystem_vendor,
@@ -592,8 +594,9 @@
dprintk(2, "budget: %p\n", budget);
- if ((err = ttpci_budget_init (budget, dev, info, THIS_MODULE))) {
- kfree (budget);
+ err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr);
+ if (err) {
+ kfree(budget);
return err;
}
diff --git a/drivers/media/dvb/ttpci/budget.c b/drivers/media/dvb/ttpci/budget.c
index f006899..1638e1d 100644
--- a/drivers/media/dvb/ttpci/budget.c
+++ b/drivers/media/dvb/ttpci/budget.c
@@ -46,11 +46,14 @@
#include "lnbp21.h"
#include "bsru6.h"
#include "bsbe1.h"
+#include "tdhd1.h"
static int diseqc_method;
module_param(diseqc_method, int, 0444);
MODULE_PARM_DESC(diseqc_method, "Select DiSEqC method for subsystem id 13c2:1003, 0: default, 1: more reliable (for newer revisions only)");
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
static void Set22K (struct budget *budget, int state)
{
struct saa7146_dev *dev=budget->dev;
@@ -390,6 +393,13 @@
.set_symbol_rate = alps_bsbe1_set_symbol_rate,
};
+static int alps_tdhd1_204_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char *name)
+{
+ struct budget *budget = (struct budget *)fe->dvb->priv;
+
+ return request_firmware(fw, name, &budget->dev->pci->dev);
+}
+
static int i2c_readreg(struct i2c_adapter *i2c, u8 adr, u8 reg)
{
@@ -511,6 +521,14 @@
}
break;
+ case 0x5f60: /* Fujitsu Siemens Activy Budget-T PCI rev AL (tda10046/ALPS TDHD1-204A) */
+ budget->dvb_frontend = dvb_attach(tda10046_attach, &alps_tdhd1_204a_config, &budget->i2c_adap);
+ if (budget->dvb_frontend) {
+ budget->dvb_frontend->ops.tuner_ops.set_params = alps_tdhd1_204a_tuner_set_params;
+ budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
+ }
+ break;
+
case 0x5f61: /* Fujitsu Siemens Activy Budget-T PCI rev GR (L64781/Grundig 29504-401(tsa5060)) */
budget->dvb_frontend = dvb_attach(l64781_attach, &grundig_29504_401_config_activy, &budget->i2c_adap);
if (budget->dvb_frontend) {
@@ -550,7 +568,7 @@
}
if (budget->dvb_frontend == NULL) {
- printk("budget: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n",
+ printk("budget: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
budget->dev->pci->vendor,
budget->dev->pci->device,
budget->dev->pci->subsystem_vendor,
@@ -582,7 +600,8 @@
dev->ext_priv = budget;
- if ((err = ttpci_budget_init (budget, dev, info, THIS_MODULE))) {
+ err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr);
+ if (err) {
printk("==> failed\n");
kfree (budget);
return err;
@@ -624,6 +643,7 @@
MAKE_BUDGET_INFO(fsacs0, "Fujitsu Siemens Activy Budget-S PCI (rev GR/grundig frontend)", BUDGET_FS_ACTIVY);
MAKE_BUDGET_INFO(fsacs1, "Fujitsu Siemens Activy Budget-S PCI (rev AL/alps frontend)", BUDGET_FS_ACTIVY);
MAKE_BUDGET_INFO(fsact, "Fujitsu Siemens Activy Budget-T PCI (rev GR/Grundig frontend)", BUDGET_FS_ACTIVY);
+MAKE_BUDGET_INFO(fsact1, "Fujitsu Siemens Activy Budget-T PCI (rev AL/ALPS TDHD1-204A)", BUDGET_FS_ACTIVY);
static struct pci_device_id pci_tbl[] = {
MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1003),
@@ -634,6 +654,7 @@
MAKE_EXTENSION_PCI(ttbs1401, 0x13c2, 0x1018),
MAKE_EXTENSION_PCI(fsacs1,0x1131, 0x4f60),
MAKE_EXTENSION_PCI(fsacs0,0x1131, 0x4f61),
+ MAKE_EXTENSION_PCI(fsact1, 0x1131, 0x5f60),
MAKE_EXTENSION_PCI(fsact, 0x1131, 0x5f61),
{
.vendor = 0,
diff --git a/drivers/media/dvb/ttpci/budget.h b/drivers/media/dvb/ttpci/budget.h
index dd450b7..86435bf 100644
--- a/drivers/media/dvb/ttpci/budget.h
+++ b/drivers/media/dvb/ttpci/budget.h
@@ -109,7 +109,7 @@
extern int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
struct saa7146_pci_extension_data *info,
- struct module *owner);
+ struct module *owner, short *adapter_nums);
extern void ttpci_budget_init_hooks(struct budget *budget);
extern int ttpci_budget_deinit(struct budget *budget);
extern void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr);
diff --git a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c
index e6c9cd2..66ab0c6 100644
--- a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c
+++ b/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c
@@ -1614,7 +1614,7 @@
}
if (ttusb->fe == NULL) {
- printk("dvb-ttusb-budget: A frontend driver was not found for device %04x/%04x\n",
+ printk("dvb-ttusb-budget: A frontend driver was not found for device [%04x:%04x]\n",
le16_to_cpu(ttusb->dev->descriptor.idVendor),
le16_to_cpu(ttusb->dev->descriptor.idProduct));
} else {
diff --git a/drivers/media/dvb/ttusb-dec/ttusb_dec.c b/drivers/media/dvb/ttusb-dec/ttusb_dec.c
index de5829b..ab33fec 100644
--- a/drivers/media/dvb/ttusb-dec/ttusb_dec.c
+++ b/drivers/media/dvb/ttusb-dec/ttusb_dec.c
@@ -1665,7 +1665,7 @@
}
if (dec->fe == NULL) {
- printk("dvb-ttusb-dec: A frontend driver was not found for device %04x/%04x\n",
+ printk("dvb-ttusb-dec: A frontend driver was not found for device [%04x:%04x]\n",
le16_to_cpu(dec->udev->descriptor.idVendor),
le16_to_cpu(dec->udev->descriptor.idProduct));
} else {
diff --git a/drivers/media/dvb/ttusb-dec/ttusbdecfe.c b/drivers/media/dvb/ttusb-dec/ttusbdecfe.c
index 443af24..21260aa 100644
--- a/drivers/media/dvb/ttusb-dec/ttusbdecfe.c
+++ b/drivers/media/dvb/ttusb-dec/ttusbdecfe.c
@@ -38,7 +38,17 @@
};
-static int ttusbdecfe_read_status(struct dvb_frontend* fe, fe_status_t* status)
+static int ttusbdecfe_dvbs_read_status(struct dvb_frontend *fe,
+ fe_status_t *status)
+{
+ *status = FE_HAS_SIGNAL | FE_HAS_VITERBI |
+ FE_HAS_SYNC | FE_HAS_CARRIER | FE_HAS_LOCK;
+ return 0;
+}
+
+
+static int ttusbdecfe_dvbt_read_status(struct dvb_frontend *fe,
+ fe_status_t *status)
{
struct ttusbdecfe_state* state = fe->demodulator_priv;
u8 b[] = { 0x00, 0x00, 0x00, 0x00,
@@ -251,7 +261,7 @@
.get_tune_settings = ttusbdecfe_dvbt_get_tune_settings,
- .read_status = ttusbdecfe_read_status,
+ .read_status = ttusbdecfe_dvbt_read_status,
};
static struct dvb_frontend_ops ttusbdecfe_dvbs_ops = {
@@ -273,7 +283,7 @@
.set_frontend = ttusbdecfe_dvbs_set_frontend,
- .read_status = ttusbdecfe_read_status,
+ .read_status = ttusbdecfe_dvbs_read_status,
.diseqc_send_master_cmd = ttusbdecfe_dvbs_diseqc_send_master_cmd,
.set_voltage = ttusbdecfe_dvbs_set_voltage,
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index 1b41b3f..e51d707 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -361,4 +361,16 @@
To compile this driver as a module, choose M here: the
module will be called radio-silabs.
+config USB_MR800
+ tristate "AverMedia MR 800 USB FM radio support"
+ depends on USB && VIDEO_V4L2
+ ---help---
+ Say Y here if you want to connect this type of radio to your
+ computer's USB port. Note that the audio is not digital, and
+ you must connect the line out connector to a sound card or a
+ set of speakers.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-mr800.
+
endif # RADIO_ADAPTERS
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile
index 7ca71ab..240ec63c 100644
--- a/drivers/media/radio/Makefile
+++ b/drivers/media/radio/Makefile
@@ -18,5 +18,6 @@
obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o
obj-$(CONFIG_USB_DSBR) += dsbr100.o
obj-$(CONFIG_USB_SI470X) += radio-si470x.o
+obj-$(CONFIG_USB_MR800) += radio-mr800.o
EXTRA_CFLAGS += -Isound
diff --git a/drivers/media/radio/dsbr100.c b/drivers/media/radio/dsbr100.c
index 70c65a7..66783ff 100644
--- a/drivers/media/radio/dsbr100.c
+++ b/drivers/media/radio/dsbr100.c
@@ -274,7 +274,7 @@
static int vidioc_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
- struct dsbr100_device *radio = video_get_drvdata(video_devdata(file));
+ struct dsbr100_device *radio = video_drvdata(file);
if (v->index > 0)
return -EINVAL;
@@ -306,7 +306,7 @@
static int vidioc_s_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct dsbr100_device *radio = video_get_drvdata(video_devdata(file));
+ struct dsbr100_device *radio = video_drvdata(file);
radio->curfreq = f->frequency;
if (dsbr100_setfreq(radio, radio->curfreq)==-1)
@@ -317,7 +317,7 @@
static int vidioc_g_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct dsbr100_device *radio = video_get_drvdata(video_devdata(file));
+ struct dsbr100_device *radio = video_drvdata(file);
f->type = V4L2_TUNER_RADIO;
f->frequency = radio->curfreq;
@@ -342,7 +342,7 @@
static int vidioc_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct dsbr100_device *radio = video_get_drvdata(video_devdata(file));
+ struct dsbr100_device *radio = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -355,16 +355,20 @@
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct dsbr100_device *radio = video_get_drvdata(video_devdata(file));
+ struct dsbr100_device *radio = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
if (ctrl->value) {
- if (dsbr100_stop(radio)==-1)
+ if (dsbr100_stop(radio) == -1) {
warn("Radio did not respond properly");
+ return -EBUSY;
+ }
} else {
- if (dsbr100_start(radio)==-1)
+ if (dsbr100_start(radio) == -1) {
warn("Radio did not respond properly");
+ return -EBUSY;
+ }
}
return 0;
}
@@ -405,23 +409,26 @@
static int usb_dsbr100_open(struct inode *inode, struct file *file)
{
- struct dsbr100_device *radio=video_get_drvdata(video_devdata(file));
+ struct dsbr100_device *radio = video_drvdata(file);
+ lock_kernel();
radio->users = 1;
radio->muted = 1;
if (dsbr100_start(radio)<0) {
warn("Radio did not start up properly");
radio->users = 0;
+ unlock_kernel();
return -EIO;
}
dsbr100_setfreq(radio, radio->curfreq);
+ unlock_kernel();
return 0;
}
static int usb_dsbr100_close(struct inode *inode, struct file *file)
{
- struct dsbr100_device *radio=video_get_drvdata(video_devdata(file));
+ struct dsbr100_device *radio = video_drvdata(file);
if (!radio)
return -ENODEV;
@@ -507,7 +514,8 @@
static int __init dsbr100_init(void)
{
int retval = usb_register(&usb_dsbr100_driver);
- info(DRIVER_VERSION ":" DRIVER_DESC);
+ printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
+ DRIVER_DESC "\n");
return retval;
}
diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c
index 1f064f4..9305e95 100644
--- a/drivers/media/radio/radio-aimslab.c
+++ b/drivers/media/radio/radio-aimslab.c
@@ -51,6 +51,7 @@
struct rt_device
{
+ unsigned long in_use;
int port;
int curvol;
unsigned long curfreq;
@@ -245,8 +246,7 @@
static int vidioc_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
- struct video_device *dev = video_devdata(file);
- struct rt_device *rt = dev->priv;
+ struct rt_device *rt = video_drvdata(file);
if (v->index > 0)
return -EINVAL;
@@ -273,8 +273,7 @@
static int vidioc_s_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct video_device *dev = video_devdata(file);
- struct rt_device *rt = dev->priv;
+ struct rt_device *rt = video_drvdata(file);
rt->curfreq = f->frequency;
rt_setfreq(rt, rt->curfreq);
@@ -284,8 +283,7 @@
static int vidioc_g_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct video_device *dev = video_devdata(file);
- struct rt_device *rt = dev->priv;
+ struct rt_device *rt = video_drvdata(file);
f->type = V4L2_TUNER_RADIO;
f->frequency = rt->curfreq;
@@ -310,8 +308,7 @@
static int vidioc_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct video_device *dev = video_devdata(file);
- struct rt_device *rt = dev->priv;
+ struct rt_device *rt = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -327,8 +324,7 @@
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct video_device *dev = video_devdata(file);
- struct rt_device *rt = dev->priv;
+ struct rt_device *rt = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -378,10 +374,21 @@
static struct rt_device rtrack_unit;
+static int rtrack_exclusive_open(struct inode *inode, struct file *file)
+{
+ return test_and_set_bit(0, &rtrack_unit.in_use) ? -EBUSY : 0;
+}
+
+static int rtrack_exclusive_release(struct inode *inode, struct file *file)
+{
+ clear_bit(0, &rtrack_unit.in_use);
+ return 0;
+}
+
static const struct file_operations rtrack_fops = {
.owner = THIS_MODULE,
- .open = video_exclusive_open,
- .release = video_exclusive_release,
+ .open = rtrack_exclusive_open,
+ .release = rtrack_exclusive_release,
.ioctl = video_ioctl2,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l_compat_ioctl32,
@@ -408,6 +415,7 @@
.name = "RadioTrack radio",
.fops = &rtrack_fops,
.ioctl_ops = &rtrack_ioctl_ops,
+ .release = video_device_release_empty,
};
static int __init rtrack_init(void)
@@ -424,7 +432,7 @@
return -EBUSY;
}
- rtrack_radio.priv=&rtrack_unit;
+ video_set_drvdata(&rtrack_radio, &rtrack_unit);
if (video_register_device(&rtrack_radio, VFL_TYPE_RADIO, radio_nr) < 0) {
release_region(io, 2);
diff --git a/drivers/media/radio/radio-aztech.c b/drivers/media/radio/radio-aztech.c
index 628c689..d784895 100644
--- a/drivers/media/radio/radio-aztech.c
+++ b/drivers/media/radio/radio-aztech.c
@@ -70,6 +70,7 @@
struct az_device
{
+ unsigned long in_use;
int curvol;
unsigned long curfreq;
int stereo;
@@ -195,8 +196,7 @@
static int vidioc_g_tuner (struct file *file, void *priv,
struct v4l2_tuner *v)
{
- struct video_device *dev = video_devdata(file);
- struct az_device *az = dev->priv;
+ struct az_device *az = video_drvdata(file);
if (v->index > 0)
return -EINVAL;
@@ -264,8 +264,7 @@
static int vidioc_s_frequency (struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct video_device *dev = video_devdata(file);
- struct az_device *az = dev->priv;
+ struct az_device *az = video_drvdata(file);
az->curfreq = f->frequency;
az_setfreq(az, az->curfreq);
@@ -275,8 +274,7 @@
static int vidioc_g_frequency (struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct video_device *dev = video_devdata(file);
- struct az_device *az = dev->priv;
+ struct az_device *az = video_drvdata(file);
f->type = V4L2_TUNER_RADIO;
f->frequency = az->curfreq;
@@ -302,8 +300,7 @@
static int vidioc_g_ctrl (struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct video_device *dev = video_devdata(file);
- struct az_device *az = dev->priv;
+ struct az_device *az = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -322,8 +319,7 @@
static int vidioc_s_ctrl (struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct video_device *dev = video_devdata(file);
- struct az_device *az = dev->priv;
+ struct az_device *az = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -342,10 +338,21 @@
static struct az_device aztech_unit;
+static int aztech_exclusive_open(struct inode *inode, struct file *file)
+{
+ return test_and_set_bit(0, &aztech_unit.in_use) ? -EBUSY : 0;
+}
+
+static int aztech_exclusive_release(struct inode *inode, struct file *file)
+{
+ clear_bit(0, &aztech_unit.in_use);
+ return 0;
+}
+
static const struct file_operations aztech_fops = {
.owner = THIS_MODULE,
- .open = video_exclusive_open,
- .release = video_exclusive_release,
+ .open = aztech_exclusive_open,
+ .release = aztech_exclusive_release,
.ioctl = video_ioctl2,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l_compat_ioctl32,
@@ -369,9 +376,10 @@
};
static struct video_device aztech_radio = {
- .name = "Aztech radio",
- .fops = &aztech_fops,
- .ioctl_ops = &aztech_ioctl_ops,
+ .name = "Aztech radio",
+ .fops = &aztech_fops,
+ .ioctl_ops = &aztech_ioctl_ops,
+ .release = video_device_release_empty,
};
module_param_named(debug,aztech_radio.debug, int, 0644);
@@ -392,7 +400,7 @@
}
mutex_init(&lock);
- aztech_radio.priv=&aztech_unit;
+ video_set_drvdata(&aztech_radio, &aztech_unit);
if (video_register_device(&aztech_radio, VFL_TYPE_RADIO, radio_nr) < 0) {
release_region(io,2);
diff --git a/drivers/media/radio/radio-cadet.c b/drivers/media/radio/radio-cadet.c
index 04c3698..0490a1f 100644
--- a/drivers/media/radio/radio-cadet.c
+++ b/drivers/media/radio/radio-cadet.c
@@ -589,6 +589,7 @@
.name = "Cadet radio",
.fops = &cadet_fops,
.ioctl_ops = &cadet_ioctl_ops,
+ .release = video_device_release_empty,
};
#ifdef CONFIG_PNP
diff --git a/drivers/media/radio/radio-gemtek-pci.c b/drivers/media/radio/radio-gemtek-pci.c
index 5cd7f03..e15bee6 100644
--- a/drivers/media/radio/radio-gemtek-pci.c
+++ b/drivers/media/radio/radio-gemtek-pci.c
@@ -100,9 +100,8 @@
u8 mute;
};
-static const char rcsid[] = "$Id: radio-gemtek-pci.c,v 1.1 2001/07/23 08:08:16 ted Exp ted $";
-
static int nr_radio = -1;
+static unsigned long in_use;
static inline u8 gemtek_pci_out( u16 value, u32 port )
{
@@ -205,8 +204,7 @@
static int vidioc_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
- struct video_device *dev = video_devdata(file);
- struct gemtek_pci_card *card = dev->priv;
+ struct gemtek_pci_card *card = video_drvdata(file);
if (v->index > 0)
return -EINVAL;
@@ -233,8 +231,7 @@
static int vidioc_s_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct video_device *dev = video_devdata(file);
- struct gemtek_pci_card *card = dev->priv;
+ struct gemtek_pci_card *card = video_drvdata(file);
if ( (f->frequency < GEMTEK_PCI_RANGE_LOW) ||
(f->frequency > GEMTEK_PCI_RANGE_HIGH) )
@@ -248,8 +245,7 @@
static int vidioc_g_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct video_device *dev = video_devdata(file);
- struct gemtek_pci_card *card = dev->priv;
+ struct gemtek_pci_card *card = video_drvdata(file);
f->type = V4L2_TUNER_RADIO;
f->frequency = card->current_frequency;
@@ -273,8 +269,7 @@
static int vidioc_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct video_device *dev = video_devdata(file);
- struct gemtek_pci_card *card = dev->priv;
+ struct gemtek_pci_card *card = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -293,8 +288,7 @@
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct video_device *dev = video_devdata(file);
- struct gemtek_pci_card *card = dev->priv;
+ struct gemtek_pci_card *card = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -364,10 +358,21 @@
static int mx = 1;
+static int gemtek_pci_exclusive_open(struct inode *inode, struct file *file)
+{
+ return test_and_set_bit(0, &in_use) ? -EBUSY : 0;
+}
+
+static int gemtek_pci_exclusive_release(struct inode *inode, struct file *file)
+{
+ clear_bit(0, &in_use);
+ return 0;
+}
+
static const struct file_operations gemtek_pci_fops = {
.owner = THIS_MODULE,
- .open = video_exclusive_open,
- .release = video_exclusive_release,
+ .open = gemtek_pci_exclusive_open,
+ .release = gemtek_pci_exclusive_release,
.ioctl = video_ioctl2,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l_compat_ioctl32,
@@ -391,9 +396,10 @@
};
static struct video_device vdev_template = {
- .name = "Gemtek PCI Radio",
- .fops = &gemtek_pci_fops,
- .ioctl_ops = &gemtek_pci_ioctl_ops,
+ .name = "Gemtek PCI Radio",
+ .fops = &gemtek_pci_fops,
+ .ioctl_ops = &gemtek_pci_ioctl_ops,
+ .release = video_device_release_empty,
};
static int __devinit gemtek_pci_probe( struct pci_dev *pci_dev, const struct pci_device_id *pci_id )
@@ -431,7 +437,7 @@
}
card->videodev = devradio;
- devradio->priv = card;
+ video_set_drvdata(devradio, card);
gemtek_pci_mute( card );
printk( KERN_INFO "Gemtek PCI Radio (rev. %d) found at 0x%04x-0x%04x.\n",
diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c
index 0a0f956..d131a5d 100644
--- a/drivers/media/radio/radio-gemtek.c
+++ b/drivers/media/radio/radio-gemtek.c
@@ -57,6 +57,7 @@
static int keepmuted = 1;
static int initmute = 1;
static int radio_nr = -1;
+static unsigned long in_use;
module_param(io, int, 0444);
MODULE_PARM_DESC(io, "Force I/O port for the GemTek Radio card if automatic "
@@ -393,10 +394,21 @@
}
};
+static int gemtek_exclusive_open(struct inode *inode, struct file *file)
+{
+ return test_and_set_bit(0, &in_use) ? -EBUSY : 0;
+}
+
+static int gemtek_exclusive_release(struct inode *inode, struct file *file)
+{
+ clear_bit(0, &in_use);
+ return 0;
+}
+
static const struct file_operations gemtek_fops = {
.owner = THIS_MODULE,
- .open = video_exclusive_open,
- .release = video_exclusive_release,
+ .open = gemtek_exclusive_open,
+ .release = gemtek_exclusive_release,
.ioctl = video_ioctl2,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l_compat_ioctl32,
@@ -447,8 +459,7 @@
static int vidioc_s_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct video_device *dev = video_devdata(file);
- struct gemtek_device *rt = dev->priv;
+ struct gemtek_device *rt = video_drvdata(file);
gemtek_setfreq(rt, f->frequency);
@@ -458,8 +469,7 @@
static int vidioc_g_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct video_device *dev = video_devdata(file);
- struct gemtek_device *rt = dev->priv;
+ struct gemtek_device *rt = video_drvdata(file);
f->type = V4L2_TUNER_RADIO;
f->frequency = rt->lastfreq;
@@ -483,8 +493,7 @@
static int vidioc_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct video_device *dev = video_devdata(file);
- struct gemtek_device *rt = dev->priv;
+ struct gemtek_device *rt = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -503,8 +512,7 @@
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct video_device *dev = video_devdata(file);
- struct gemtek_device *rt = dev->priv;
+ struct gemtek_device *rt = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -569,9 +577,10 @@
};
static struct video_device gemtek_radio = {
- .name = "GemTek Radio card",
- .fops = &gemtek_fops,
- .ioctl_ops = &gemtek_ioctl_ops,
+ .name = "GemTek Radio card",
+ .fops = &gemtek_fops,
+ .ioctl_ops = &gemtek_ioctl_ops,
+ .release = video_device_release_empty,
};
/*
@@ -610,7 +619,7 @@
return -EINVAL;
}
- gemtek_radio.priv = &gemtek_unit;
+ video_set_drvdata(&gemtek_radio, &gemtek_unit);
if (video_register_device(&gemtek_radio, VFL_TYPE_RADIO, radio_nr) < 0) {
release_region(io, 1);
diff --git a/drivers/media/radio/radio-maestro.c b/drivers/media/radio/radio-maestro.c
index 9ef0a76..4bf4d00 100644
--- a/drivers/media/radio/radio-maestro.c
+++ b/drivers/media/radio/radio-maestro.c
@@ -75,7 +75,21 @@
static int radio_nr = -1;
module_param(radio_nr, int, 0);
+static unsigned long in_use;
+
static int maestro_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
+
+static int maestro_exclusive_open(struct inode *inode, struct file *file)
+{
+ return test_and_set_bit(0, &in_use) ? -EBUSY : 0;
+}
+
+static int maestro_exclusive_release(struct inode *inode, struct file *file)
+{
+ clear_bit(0, &in_use);
+ return 0;
+}
+
static void maestro_remove(struct pci_dev *pdev);
static struct pci_device_id maestro_r_pci_tbl[] = {
@@ -98,8 +112,8 @@
static const struct file_operations maestro_fops = {
.owner = THIS_MODULE,
- .open = video_exclusive_open,
- .release = video_exclusive_release,
+ .open = maestro_exclusive_open,
+ .release = maestro_exclusive_release,
.ioctl = video_ioctl2,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l_compat_ioctl32,
@@ -196,8 +210,7 @@
static int vidioc_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
- struct video_device *dev = video_devdata(file);
- struct radio_device *card = video_get_drvdata(dev);
+ struct radio_device *card = video_drvdata(file);
if (v->index > 0)
return -EINVAL;
@@ -229,8 +242,7 @@
static int vidioc_s_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct video_device *dev = video_devdata(file);
- struct radio_device *card = video_get_drvdata(dev);
+ struct radio_device *card = video_drvdata(file);
if (f->frequency < FREQ_LO || f->frequency > FREQ_HI)
return -EINVAL;
@@ -241,8 +253,7 @@
static int vidioc_g_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct video_device *dev = video_devdata(file);
- struct radio_device *card = video_get_drvdata(dev);
+ struct radio_device *card = video_drvdata(file);
f->type = V4L2_TUNER_RADIO;
f->frequency = BITS2FREQ(radio_bits_get(card));
@@ -267,8 +278,7 @@
static int vidioc_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct video_device *dev = video_devdata(file);
- struct radio_device *card = video_get_drvdata(dev);
+ struct radio_device *card = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -281,8 +291,7 @@
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct video_device *dev = video_devdata(file);
- struct radio_device *card = video_get_drvdata(dev);
+ struct radio_device *card = video_drvdata(file);
register u16 io = card->io;
register u16 omask = inw(io + IO_MASK);
@@ -374,6 +383,7 @@
.name = "Maestro radio",
.fops = &maestro_fops,
.ioctl_ops = &maestro_ioctl_ops,
+ .release = video_device_release,
};
static int __devinit maestro_probe(struct pci_dev *pdev,
diff --git a/drivers/media/radio/radio-maxiradio.c b/drivers/media/radio/radio-maxiradio.c
index 0cc6fcb..c777a17 100644
--- a/drivers/media/radio/radio-maxiradio.c
+++ b/drivers/media/radio/radio-maxiradio.c
@@ -85,6 +85,7 @@
static int radio_nr = -1;
module_param(radio_nr, int, 0);
+static unsigned long in_use;
#define FREQ_LO 50*16000
#define FREQ_HI 150*16000
@@ -99,10 +100,21 @@
#define BITS2FREQ(x) ((x) * FREQ_STEP - FREQ_IF)
+static int maxiradio_exclusive_open(struct inode *inode, struct file *file)
+{
+ return test_and_set_bit(0, &in_use) ? -EBUSY : 0;
+}
+
+static int maxiradio_exclusive_release(struct inode *inode, struct file *file)
+{
+ clear_bit(0, &in_use);
+ return 0;
+}
+
static const struct file_operations maxiradio_fops = {
.owner = THIS_MODULE,
- .open = video_exclusive_open,
- .release = video_exclusive_release,
+ .open = maxiradio_exclusive_open,
+ .release = maxiradio_exclusive_release,
.ioctl = video_ioctl2,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l_compat_ioctl32,
@@ -219,8 +231,7 @@
static int vidioc_g_tuner (struct file *file, void *priv,
struct v4l2_tuner *v)
{
- struct video_device *dev = video_devdata(file);
- struct radio_device *card=dev->priv;
+ struct radio_device *card = video_drvdata(file);
if (v->index > 0)
return -EINVAL;
@@ -290,8 +301,7 @@
static int vidioc_s_frequency (struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct video_device *dev = video_devdata(file);
- struct radio_device *card=dev->priv;
+ struct radio_device *card = video_drvdata(file);
if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) {
dprintk(1, "radio freq (%d.%02d MHz) out of range (%d-%d)\n",
@@ -312,8 +322,7 @@
static int vidioc_g_frequency (struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct video_device *dev = video_devdata(file);
- struct radio_device *card=dev->priv;
+ struct radio_device *card = video_drvdata(file);
f->type = V4L2_TUNER_RADIO;
f->frequency = card->freq;
@@ -343,8 +352,7 @@
static int vidioc_g_ctrl (struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct video_device *dev = video_devdata(file);
- struct radio_device *card=dev->priv;
+ struct radio_device *card = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -358,8 +366,7 @@
static int vidioc_s_ctrl (struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct video_device *dev = video_devdata(file);
- struct radio_device *card=dev->priv;
+ struct radio_device *card = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -390,9 +397,10 @@
};
static struct video_device maxiradio_radio = {
- .name = "Maxi Radio FM2000 radio",
- .fops = &maxiradio_fops,
- .ioctl_ops = &maxiradio_ioctl_ops,
+ .name = "Maxi Radio FM2000 radio",
+ .fops = &maxiradio_fops,
+ .ioctl_ops = &maxiradio_ioctl_ops,
+ .release = video_device_release_empty,
};
static int __devinit maxiradio_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
@@ -408,7 +416,7 @@
radio_unit.io = pci_resource_start(pdev, 0);
mutex_init(&radio_unit.lock);
- maxiradio_radio.priv = &radio_unit;
+ video_set_drvdata(&maxiradio_radio, &radio_unit);
if (video_register_device(&maxiradio_radio, VFL_TYPE_RADIO, radio_nr) < 0) {
printk("radio-maxiradio: can't register device!");
diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c
new file mode 100644
index 0000000..a33717c
--- /dev/null
+++ b/drivers/media/radio/radio-mr800.c
@@ -0,0 +1,628 @@
+/*
+ * A driver for the AverMedia MR 800 USB FM radio. This device plugs
+ * into both the USB and an analog audio input, so this thing
+ * only deals with initialization and frequency setting, the
+ * audio data has to be handled by a sound driver.
+ *
+ * Copyright (c) 2008 Alexey Klimov <klimov.linux@gmail.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.
+ *
+ * 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
+ */
+
+/*
+ * Big thanks to authors of dsbr100.c and radio-si470x.c
+ *
+ * When work was looked pretty good, i discover this:
+ * http://av-usbradio.sourceforge.net/index.php
+ * http://sourceforge.net/projects/av-usbradio/
+ * Latest release of theirs project was in 2005.
+ * Probably, this driver could be improved trough using their
+ * achievements (specifications given).
+ * So, we have smth to begin with.
+ *
+ * History:
+ * Version 0.01: First working version.
+ * It's required to blacklist AverMedia USB Radio
+ * in usbhid/hid-quirks.c
+ *
+ * Many things to do:
+ * - Correct power managment of device (suspend & resume)
+ * - Make x86 independance (little-endian and big-endian stuff)
+ * - Add code for scanning and smooth tuning
+ * - Checked and add stereo&mono stuff
+ * - Add code for sensitivity value
+ * - Correct mistakes
+ * - In Japan another FREQ_MIN and FREQ_MAX
+ */
+
+/* kernel includes */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <linux/usb.h>
+#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+
+/* driver and module definitions */
+#define DRIVER_AUTHOR "Alexey Klimov <klimov.linux@gmail.com>"
+#define DRIVER_DESC "AverMedia MR 800 USB FM radio driver"
+#define DRIVER_VERSION "0.01"
+#define RADIO_VERSION KERNEL_VERSION(0, 0, 1)
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define USB_AMRADIO_VENDOR 0x07ca
+#define USB_AMRADIO_PRODUCT 0xb800
+
+/* Probably USB_TIMEOUT should be modified in module parameter */
+#define BUFFER_LENGTH 8
+#define USB_TIMEOUT 500
+
+/* Frequency limits in MHz -- these are European values. For Japanese
+devices, that would be 76 and 91. */
+#define FREQ_MIN 87.5
+#define FREQ_MAX 108.0
+#define FREQ_MUL 16000
+
+/* module parameter */
+static int radio_nr = -1;
+module_param(radio_nr, int, 0);
+MODULE_PARM_DESC(radio_nr, "Radio Nr");
+
+static struct v4l2_queryctrl radio_qctrl[] = {
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },
+/* HINT: the disabled controls are only here to satify kradio and such apps */
+ { .id = V4L2_CID_AUDIO_VOLUME,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_BALANCE,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_BASS,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_TREBLE,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_LOUDNESS,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+};
+
+static int usb_amradio_probe(struct usb_interface *intf,
+ const struct usb_device_id *id);
+static void usb_amradio_disconnect(struct usb_interface *intf);
+static int usb_amradio_open(struct inode *inode, struct file *file);
+static int usb_amradio_close(struct inode *inode, struct file *file);
+static int usb_amradio_suspend(struct usb_interface *intf,
+ pm_message_t message);
+static int usb_amradio_resume(struct usb_interface *intf);
+
+/* Data for one (physical) device */
+struct amradio_device {
+ /* reference to USB and video device */
+ struct usb_device *usbdev;
+ struct video_device *videodev;
+
+ unsigned char *buffer;
+ struct mutex lock; /* buffer locking */
+ int curfreq;
+ int stereo;
+ int users;
+ int removed;
+ int muted;
+};
+
+/* USB Device ID List */
+static struct usb_device_id usb_amradio_device_table[] = {
+ {USB_DEVICE_AND_INTERFACE_INFO(USB_AMRADIO_VENDOR, USB_AMRADIO_PRODUCT,
+ USB_CLASS_HID, 0, 0) },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, usb_amradio_device_table);
+
+/* USB subsystem interface */
+static struct usb_driver usb_amradio_driver = {
+ .name = "radio-mr800",
+ .probe = usb_amradio_probe,
+ .disconnect = usb_amradio_disconnect,
+ .suspend = usb_amradio_suspend,
+ .resume = usb_amradio_resume,
+ .reset_resume = usb_amradio_resume,
+ .id_table = usb_amradio_device_table,
+ .supports_autosuspend = 1,
+};
+
+/* switch on radio. Send 8 bytes to device. */
+static int amradio_start(struct amradio_device *radio)
+{
+ int retval;
+ int size;
+
+ mutex_lock(&radio->lock);
+
+ radio->buffer[0] = 0x00;
+ radio->buffer[1] = 0x55;
+ radio->buffer[2] = 0xaa;
+ radio->buffer[3] = 0x00;
+ radio->buffer[4] = 0xab;
+ radio->buffer[5] = 0x00;
+ radio->buffer[6] = 0x00;
+ radio->buffer[7] = 0x00;
+
+ retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
+ (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
+
+ if (retval) {
+ mutex_unlock(&radio->lock);
+ return retval;
+ }
+
+ mutex_unlock(&radio->lock);
+
+ radio->muted = 0;
+
+ return retval;
+}
+
+/* switch off radio */
+static int amradio_stop(struct amradio_device *radio)
+{
+ int retval;
+ int size;
+
+ mutex_lock(&radio->lock);
+
+ radio->buffer[0] = 0x00;
+ radio->buffer[1] = 0x55;
+ radio->buffer[2] = 0xaa;
+ radio->buffer[3] = 0x00;
+ radio->buffer[4] = 0xab;
+ radio->buffer[5] = 0x01;
+ radio->buffer[6] = 0x00;
+ radio->buffer[7] = 0x00;
+
+ retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
+ (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
+
+ if (retval) {
+ mutex_unlock(&radio->lock);
+ return retval;
+ }
+
+ mutex_unlock(&radio->lock);
+
+ radio->muted = 1;
+
+ return retval;
+}
+
+/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */
+static int amradio_setfreq(struct amradio_device *radio, int freq)
+{
+ int retval;
+ int size;
+ unsigned short freq_send = 0x13 + (freq >> 3) / 25;
+
+ mutex_lock(&radio->lock);
+
+ radio->buffer[0] = 0x00;
+ radio->buffer[1] = 0x55;
+ radio->buffer[2] = 0xaa;
+ radio->buffer[3] = 0x03;
+ radio->buffer[4] = 0xa4;
+ radio->buffer[5] = 0x00;
+ radio->buffer[6] = 0x00;
+ radio->buffer[7] = 0x08;
+
+ retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
+ (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
+
+ if (retval) {
+ mutex_unlock(&radio->lock);
+ return retval;
+ }
+
+ /* frequency is calculated from freq_send and placed in first 2 bytes */
+ radio->buffer[0] = (freq_send >> 8) & 0xff;
+ radio->buffer[1] = freq_send & 0xff;
+ radio->buffer[2] = 0x01;
+ radio->buffer[3] = 0x00;
+ radio->buffer[4] = 0x00;
+ /* 5 and 6 bytes of buffer already = 0x00 */
+ radio->buffer[7] = 0x00;
+
+ retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
+ (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
+
+ if (retval) {
+ mutex_unlock(&radio->lock);
+ return retval;
+ }
+
+ mutex_unlock(&radio->lock);
+
+ radio->stereo = 0;
+
+ return retval;
+}
+
+/* USB subsystem interface begins here */
+
+/* handle unplugging of the device, release data structures
+if nothing keeps us from doing it. If something is still
+keeping us busy, the release callback of v4l will take care
+of releasing it. */
+static void usb_amradio_disconnect(struct usb_interface *intf)
+{
+ struct amradio_device *radio = usb_get_intfdata(intf);
+
+ usb_set_intfdata(intf, NULL);
+
+ if (radio) {
+ video_unregister_device(radio->videodev);
+ radio->videodev = NULL;
+ if (radio->users) {
+ kfree(radio->buffer);
+ kfree(radio);
+ } else {
+ radio->removed = 1;
+ }
+ }
+}
+
+/* vidioc_querycap - query device capabilities */
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+{
+ strlcpy(v->driver, "radio-mr800", sizeof(v->driver));
+ strlcpy(v->card, "AverMedia MR 800 USB FM Radio", sizeof(v->card));
+ sprintf(v->bus_info, "USB");
+ v->version = RADIO_VERSION;
+ v->capabilities = V4L2_CAP_TUNER;
+ return 0;
+}
+
+/* vidioc_g_tuner - get tuner attributes */
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+
+ if (v->index > 0)
+ return -EINVAL;
+
+/* TODO: Add function which look is signal stereo or not
+ * amradio_getstat(radio);
+ */
+ radio->stereo = -1;
+ strcpy(v->name, "FM");
+ v->type = V4L2_TUNER_RADIO;
+ v->rangelow = FREQ_MIN * FREQ_MUL;
+ v->rangehigh = FREQ_MAX * FREQ_MUL;
+ v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ v->capability = V4L2_TUNER_CAP_LOW;
+ if (radio->stereo)
+ v->audmode = V4L2_TUNER_MODE_STEREO;
+ else
+ v->audmode = V4L2_TUNER_MODE_MONO;
+ v->signal = 0xffff; /* Can't get the signal strength, sad.. */
+ v->afc = 0; /* Don't know what is this */
+ return 0;
+}
+
+/* vidioc_s_tuner - set tuner attributes */
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ if (v->index > 0)
+ return -EINVAL;
+ return 0;
+}
+
+/* vidioc_s_frequency - set tuner radio frequency */
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+
+ radio->curfreq = f->frequency;
+ if (amradio_setfreq(radio, radio->curfreq) < 0)
+ warn("Set frequency failed");
+ return 0;
+}
+
+/* vidioc_g_frequency - get tuner radio frequency */
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = radio->curfreq;
+ return 0;
+}
+
+/* vidioc_queryctrl - enumerate control items */
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+ if (qc->id && qc->id == radio_qctrl[i].id) {
+ memcpy(qc, &(radio_qctrl[i]),
+ sizeof(*qc));
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+/* vidioc_g_ctrl - get the value of a control */
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value = radio->muted;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/* vidioc_s_ctrl - set the value of a control */
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (ctrl->value) {
+ if (amradio_stop(radio) < 0) {
+ warn("amradio_stop() failed");
+ return -1;
+ }
+ } else {
+ if (amradio_start(radio) < 0) {
+ warn("amradio_start() failed");
+ return -1;
+ }
+ }
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/* vidioc_g_audio - get audio attributes */
+static int vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ if (a->index > 1)
+ return -EINVAL;
+
+ strcpy(a->name, "Radio");
+ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+}
+
+/* vidioc_s_audio - set audio attributes */
+static int vidioc_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ if (a->index != 0)
+ return -EINVAL;
+ return 0;
+}
+
+/* vidioc_g_input - get input */
+static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+/* vidioc_s_input - set input */
+static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ if (i != 0)
+ return -EINVAL;
+ return 0;
+}
+
+/* open device - amradio_start() and amradio_setfreq() */
+static int usb_amradio_open(struct inode *inode, struct file *file)
+{
+ struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+
+ radio->users = 1;
+ radio->muted = 1;
+
+ if (amradio_start(radio) < 0) {
+ warn("Radio did not start up properly");
+ radio->users = 0;
+ return -EIO;
+ }
+ if (amradio_setfreq(radio, radio->curfreq) < 0)
+ warn("Set frequency failed");
+ return 0;
+}
+
+/*close device - free driver structures */
+static int usb_amradio_close(struct inode *inode, struct file *file)
+{
+ struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+
+ if (!radio)
+ return -ENODEV;
+ radio->users = 0;
+ if (radio->removed) {
+ kfree(radio->buffer);
+ kfree(radio);
+ }
+ return 0;
+}
+
+/* Suspend device - stop device. Need to be checked and fixed */
+static int usb_amradio_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct amradio_device *radio = usb_get_intfdata(intf);
+
+ if (amradio_stop(radio) < 0)
+ warn("amradio_stop() failed");
+
+ info("radio-mr800: Going into suspend..");
+
+ return 0;
+}
+
+/* Resume device - start device. Need to be checked and fixed */
+static int usb_amradio_resume(struct usb_interface *intf)
+{
+ struct amradio_device *radio = usb_get_intfdata(intf);
+
+ if (amradio_start(radio) < 0)
+ warn("amradio_start() failed");
+
+ info("radio-mr800: Coming out of suspend..");
+
+ return 0;
+}
+
+/* File system interface */
+static const struct file_operations usb_amradio_fops = {
+ .owner = THIS_MODULE,
+ .open = usb_amradio_open,
+ .release = usb_amradio_close,
+ .ioctl = video_ioctl2,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = v4l_compat_ioctl32,
+#endif
+ .llseek = no_llseek,
+};
+
+static const struct v4l2_ioctl_ops usb_amradio_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+};
+
+/* V4L2 interface */
+static struct video_device amradio_videodev_template = {
+ .name = "AverMedia MR 800 USB FM Radio",
+ .fops = &usb_amradio_fops,
+ .ioctl_ops = &usb_amradio_ioctl_ops,
+ .release = video_device_release,
+};
+
+/* check if the device is present and register with v4l and
+usb if it is */
+static int usb_amradio_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct amradio_device *radio;
+
+ radio = kmalloc(sizeof(struct amradio_device), GFP_KERNEL);
+
+ if (!(radio))
+ return -ENOMEM;
+
+ radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL);
+
+ if (!(radio->buffer)) {
+ kfree(radio);
+ return -ENOMEM;
+ }
+
+ radio->videodev = video_device_alloc();
+
+ if (!(radio->videodev)) {
+ kfree(radio->buffer);
+ kfree(radio);
+ return -ENOMEM;
+ }
+
+ memcpy(radio->videodev, &amradio_videodev_template,
+ sizeof(amradio_videodev_template));
+
+ radio->removed = 0;
+ radio->users = 0;
+ radio->usbdev = interface_to_usbdev(intf);
+ radio->curfreq = 95.16 * FREQ_MUL;
+
+ mutex_init(&radio->lock);
+
+ video_set_drvdata(radio->videodev, radio);
+ if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr)) {
+ warn("Could not register video device");
+ video_device_release(radio->videodev);
+ kfree(radio->buffer);
+ kfree(radio);
+ return -EIO;
+ }
+
+ usb_set_intfdata(intf, radio);
+ return 0;
+}
+
+static int __init amradio_init(void)
+{
+ int retval = usb_register(&usb_amradio_driver);
+
+ info(DRIVER_VERSION " " DRIVER_DESC);
+ if (retval)
+ err("usb_register failed. Error number %d", retval);
+ return retval;
+}
+
+static void __exit amradio_exit(void)
+{
+ usb_deregister(&usb_amradio_driver);
+}
+
+module_init(amradio_init);
+module_exit(amradio_exit);
+
diff --git a/drivers/media/radio/radio-rtrack2.c b/drivers/media/radio/radio-rtrack2.c
index 6d820e2..a670797 100644
--- a/drivers/media/radio/radio-rtrack2.c
+++ b/drivers/media/radio/radio-rtrack2.c
@@ -52,6 +52,7 @@
struct rt_device
{
+ unsigned long in_use;
int port;
unsigned long curfreq;
int muted;
@@ -153,8 +154,7 @@
static int vidioc_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
- struct video_device *dev = video_devdata(file);
- struct rt_device *rt = dev->priv;
+ struct rt_device *rt = video_drvdata(file);
if (v->index > 0)
return -EINVAL;
@@ -173,8 +173,7 @@
static int vidioc_s_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct video_device *dev = video_devdata(file);
- struct rt_device *rt = dev->priv;
+ struct rt_device *rt = video_drvdata(file);
rt->curfreq = f->frequency;
rt_setfreq(rt, rt->curfreq);
@@ -184,8 +183,7 @@
static int vidioc_g_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct video_device *dev = video_devdata(file);
- struct rt_device *rt = dev->priv;
+ struct rt_device *rt = video_drvdata(file);
f->type = V4L2_TUNER_RADIO;
f->frequency = rt->curfreq;
@@ -210,8 +208,7 @@
static int vidioc_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct video_device *dev = video_devdata(file);
- struct rt_device *rt = dev->priv;
+ struct rt_device *rt = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -230,8 +227,7 @@
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct video_device *dev = video_devdata(file);
- struct rt_device *rt = dev->priv;
+ struct rt_device *rt = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -284,10 +280,21 @@
static struct rt_device rtrack2_unit;
+static int rtrack2_exclusive_open(struct inode *inode, struct file *file)
+{
+ return test_and_set_bit(0, &rtrack2_unit.in_use) ? -EBUSY : 0;
+}
+
+static int rtrack2_exclusive_release(struct inode *inode, struct file *file)
+{
+ clear_bit(0, &rtrack2_unit.in_use);
+ return 0;
+}
+
static const struct file_operations rtrack2_fops = {
.owner = THIS_MODULE,
- .open = video_exclusive_open,
- .release = video_exclusive_release,
+ .open = rtrack2_exclusive_open,
+ .release = rtrack2_exclusive_release,
.ioctl = video_ioctl2,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l_compat_ioctl32,
@@ -314,6 +321,7 @@
.name = "RadioTrack II radio",
.fops = &rtrack2_fops,
.ioctl_ops = &rtrack2_ioctl_ops,
+ .release = video_device_release_empty,
};
static int __init rtrack2_init(void)
@@ -329,7 +337,7 @@
return -EBUSY;
}
- rtrack2_radio.priv=&rtrack2_unit;
+ video_set_drvdata(&rtrack2_radio, &rtrack2_unit);
spin_lock_init(&lock);
if (video_register_device(&rtrack2_radio, VFL_TYPE_RADIO, radio_nr) < 0) {
diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c
index 0d478f5..329c90b 100644
--- a/drivers/media/radio/radio-sf16fmi.c
+++ b/drivers/media/radio/radio-sf16fmi.c
@@ -45,6 +45,7 @@
struct fmi_device
{
+ unsigned long in_use;
int port;
int curvol; /* 1 or 0 */
unsigned long curfreq; /* freq in kHz */
@@ -146,8 +147,7 @@
struct v4l2_tuner *v)
{
int mult;
- struct video_device *dev = video_devdata(file);
- struct fmi_device *fmi = dev->priv;
+ struct fmi_device *fmi = video_drvdata(file);
if (v->index > 0)
return -EINVAL;
@@ -175,8 +175,7 @@
static int vidioc_s_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct video_device *dev = video_devdata(file);
- struct fmi_device *fmi = dev->priv;
+ struct fmi_device *fmi = video_drvdata(file);
if (!(fmi->flags & V4L2_TUNER_CAP_LOW))
f->frequency *= 1000;
@@ -193,8 +192,7 @@
static int vidioc_g_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct video_device *dev = video_devdata(file);
- struct fmi_device *fmi = dev->priv;
+ struct fmi_device *fmi = video_drvdata(file);
f->type = V4L2_TUNER_RADIO;
f->frequency = fmi->curfreq;
@@ -221,8 +219,7 @@
static int vidioc_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct video_device *dev = video_devdata(file);
- struct fmi_device *fmi = dev->priv;
+ struct fmi_device *fmi = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -235,8 +232,7 @@
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct video_device *dev = video_devdata(file);
- struct fmi_device *fmi = dev->priv;
+ struct fmi_device *fmi = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -284,10 +280,21 @@
static struct fmi_device fmi_unit;
+static int fmi_exclusive_open(struct inode *inode, struct file *file)
+{
+ return test_and_set_bit(0, &fmi_unit.in_use) ? -EBUSY : 0;
+}
+
+static int fmi_exclusive_release(struct inode *inode, struct file *file)
+{
+ clear_bit(0, &fmi_unit.in_use);
+ return 0;
+}
+
static const struct file_operations fmi_fops = {
.owner = THIS_MODULE,
- .open = video_exclusive_open,
- .release = video_exclusive_release,
+ .open = fmi_exclusive_open,
+ .release = fmi_exclusive_release,
.ioctl = video_ioctl2,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l_compat_ioctl32,
@@ -314,6 +321,7 @@
.name = "SF16FMx radio",
.fops = &fmi_fops,
.ioctl_ops = &fmi_ioctl_ops,
+ .release = video_device_release_empty,
};
/* ladis: this is my card. does any other types exist? */
@@ -373,7 +381,7 @@
fmi_unit.curvol = 0;
fmi_unit.curfreq = 0;
fmi_unit.flags = V4L2_TUNER_CAP_LOW;
- fmi_radio.priv = &fmi_unit;
+ video_set_drvdata(&fmi_radio, &fmi_unit);
mutex_init(&lock);
diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c
index 6290553..b1f47c3 100644
--- a/drivers/media/radio/radio-sf16fmr2.c
+++ b/drivers/media/radio/radio-sf16fmr2.c
@@ -64,6 +64,7 @@
/* this should be static vars for module size */
struct fmr2_device
{
+ unsigned long in_use;
int port;
int curvol; /* 0-15 */
int mute;
@@ -229,8 +230,7 @@
struct v4l2_tuner *v)
{
int mult;
- struct video_device *dev = video_devdata(file);
- struct fmr2_device *fmr2 = dev->priv;
+ struct fmr2_device *fmr2 = video_drvdata(file);
if (v->index > 0)
return -EINVAL;
@@ -262,8 +262,7 @@
static int vidioc_s_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct video_device *dev = video_devdata(file);
- struct fmr2_device *fmr2 = dev->priv;
+ struct fmr2_device *fmr2 = video_drvdata(file);
if (!(fmr2->flags & V4L2_TUNER_CAP_LOW))
f->frequency *= 1000;
@@ -286,8 +285,7 @@
static int vidioc_g_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct video_device *dev = video_devdata(file);
- struct fmr2_device *fmr2 = dev->priv;
+ struct fmr2_device *fmr2 = video_drvdata(file);
f->type = V4L2_TUNER_RADIO;
f->frequency = fmr2->curfreq;
@@ -313,8 +311,7 @@
static int vidioc_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct video_device *dev = video_devdata(file);
- struct fmr2_device *fmr2 = dev->priv;
+ struct fmr2_device *fmr2 = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -330,8 +327,7 @@
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct video_device *dev = video_devdata(file);
- struct fmr2_device *fmr2 = dev->priv;
+ struct fmr2_device *fmr2 = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -400,10 +396,21 @@
static struct fmr2_device fmr2_unit;
+static int fmr2_exclusive_open(struct inode *inode, struct file *file)
+{
+ return test_and_set_bit(0, &fmr2_unit.in_use) ? -EBUSY : 0;
+}
+
+static int fmr2_exclusive_release(struct inode *inode, struct file *file)
+{
+ clear_bit(0, &fmr2_unit.in_use);
+ return 0;
+}
+
static const struct file_operations fmr2_fops = {
.owner = THIS_MODULE,
- .open = video_exclusive_open,
- .release = video_exclusive_release,
+ .open = fmr2_exclusive_open,
+ .release = fmr2_exclusive_release,
.ioctl = video_ioctl2,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l_compat_ioctl32,
@@ -430,6 +437,7 @@
.name = "SF16FMR2 radio",
.fops = &fmr2_fops,
.ioctl_ops = &fmr2_ioctl_ops,
+ .release = video_device_release_empty,
};
static int __init fmr2_init(void)
@@ -441,7 +449,7 @@
fmr2_unit.stereo = 1;
fmr2_unit.flags = V4L2_TUNER_CAP_LOW;
fmr2_unit.card_type = 0;
- fmr2_radio.priv = &fmr2_unit;
+ video_set_drvdata(&fmr2_radio, &fmr2_unit);
mutex_init(&lock);
diff --git a/drivers/media/radio/radio-si470x.c b/drivers/media/radio/radio-si470x.c
index 16c7ef2..f6cedcd 100644
--- a/drivers/media/radio/radio-si470x.c
+++ b/drivers/media/radio/radio-si470x.c
@@ -986,7 +986,7 @@
static ssize_t si470x_fops_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
- struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+ struct si470x_device *radio = video_drvdata(file);
int retval = 0;
unsigned int block_count = 0;
@@ -1047,7 +1047,7 @@
static unsigned int si470x_fops_poll(struct file *file,
struct poll_table_struct *pts)
{
- struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+ struct si470x_device *radio = video_drvdata(file);
int retval = 0;
/* switch on rds reception */
@@ -1071,9 +1071,10 @@
*/
static int si470x_fops_open(struct inode *inode, struct file *file)
{
- struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+ struct si470x_device *radio = video_drvdata(file);
int retval;
+ lock_kernel();
radio->users++;
retval = usb_autopm_get_interface(radio->intf);
@@ -1090,6 +1091,7 @@
}
done:
+ unlock_kernel();
return retval;
}
@@ -1099,7 +1101,7 @@
*/
static int si470x_fops_release(struct inode *inode, struct file *file)
{
- struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+ struct si470x_device *radio = video_drvdata(file);
int retval = 0;
/* safety check */
@@ -1282,7 +1284,7 @@
static int si470x_vidioc_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+ struct si470x_device *radio = video_drvdata(file);
int retval = 0;
/* safety checks */
@@ -1318,7 +1320,7 @@
static int si470x_vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+ struct si470x_device *radio = video_drvdata(file);
int retval = 0;
/* safety checks */
@@ -1405,7 +1407,7 @@
static int si470x_vidioc_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *tuner)
{
- struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+ struct si470x_device *radio = video_drvdata(file);
int retval = 0;
/* safety checks */
@@ -1471,7 +1473,7 @@
static int si470x_vidioc_s_tuner(struct file *file, void *priv,
struct v4l2_tuner *tuner)
{
- struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+ struct si470x_device *radio = video_drvdata(file);
int retval = 0;
/* safety checks */
@@ -1505,7 +1507,7 @@
static int si470x_vidioc_g_frequency(struct file *file, void *priv,
struct v4l2_frequency *freq)
{
- struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+ struct si470x_device *radio = video_drvdata(file);
int retval = 0;
/* safety checks */
@@ -1534,7 +1536,7 @@
static int si470x_vidioc_s_frequency(struct file *file, void *priv,
struct v4l2_frequency *freq)
{
- struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+ struct si470x_device *radio = video_drvdata(file);
int retval = 0;
/* safety checks */
@@ -1563,7 +1565,7 @@
static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv,
struct v4l2_hw_freq_seek *seek)
{
- struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+ struct si470x_device *radio = video_drvdata(file);
int retval = 0;
/* safety checks */
diff --git a/drivers/media/radio/radio-terratec.c b/drivers/media/radio/radio-terratec.c
index 0876fec..0abb186 100644
--- a/drivers/media/radio/radio-terratec.c
+++ b/drivers/media/radio/radio-terratec.c
@@ -79,6 +79,7 @@
struct tt_device
{
+ unsigned long in_use;
int port;
int curvol;
unsigned long curfreq;
@@ -220,8 +221,7 @@
static int vidioc_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
- struct video_device *dev = video_devdata(file);
- struct tt_device *tt = dev->priv;
+ struct tt_device *tt = video_drvdata(file);
if (v->index > 0)
return -EINVAL;
@@ -248,8 +248,7 @@
static int vidioc_s_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct video_device *dev = video_devdata(file);
- struct tt_device *tt = dev->priv;
+ struct tt_device *tt = video_drvdata(file);
tt->curfreq = f->frequency;
tt_setfreq(tt, tt->curfreq);
@@ -259,8 +258,7 @@
static int vidioc_g_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct video_device *dev = video_devdata(file);
- struct tt_device *tt = dev->priv;
+ struct tt_device *tt = video_drvdata(file);
f->type = V4L2_TUNER_RADIO;
f->frequency = tt->curfreq;
@@ -285,8 +283,7 @@
static int vidioc_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct video_device *dev = video_devdata(file);
- struct tt_device *tt = dev->priv;
+ struct tt_device *tt = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -305,8 +302,7 @@
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct video_device *dev = video_devdata(file);
- struct tt_device *tt = dev->priv;
+ struct tt_device *tt = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -356,10 +352,21 @@
static struct tt_device terratec_unit;
+static int terratec_exclusive_open(struct inode *inode, struct file *file)
+{
+ return test_and_set_bit(0, &terratec_unit.in_use) ? -EBUSY : 0;
+}
+
+static int terratec_exclusive_release(struct inode *inode, struct file *file)
+{
+ clear_bit(0, &terratec_unit.in_use);
+ return 0;
+}
+
static const struct file_operations terratec_fops = {
.owner = THIS_MODULE,
- .open = video_exclusive_open,
- .release = video_exclusive_release,
+ .open = terratec_exclusive_open,
+ .release = terratec_exclusive_release,
.ioctl = video_ioctl2,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l_compat_ioctl32,
@@ -386,6 +393,7 @@
.name = "TerraTec ActiveRadio",
.fops = &terratec_fops,
.ioctl_ops = &terratec_ioctl_ops,
+ .release = video_device_release_empty,
};
static int __init terratec_init(void)
@@ -401,7 +409,7 @@
return -EBUSY;
}
- terratec_radio.priv=&terratec_unit;
+ video_set_drvdata(&terratec_radio, &terratec_unit);
spin_lock_init(&lock);
diff --git a/drivers/media/radio/radio-trust.c b/drivers/media/radio/radio-trust.c
index 1931619..e7b111f 100644
--- a/drivers/media/radio/radio-trust.c
+++ b/drivers/media/radio/radio-trust.c
@@ -78,6 +78,7 @@
static unsigned long curfreq;
static int curstereo;
static int curmute;
+static unsigned long in_use;
/* i2c addresses */
#define TDA7318_ADDR 0x88
@@ -336,10 +337,21 @@
return 0;
}
+static int trust_exclusive_open(struct inode *inode, struct file *file)
+{
+ return test_and_set_bit(0, &in_use) ? -EBUSY : 0;
+}
+
+static int trust_exclusive_release(struct inode *inode, struct file *file)
+{
+ clear_bit(0, &in_use);
+ return 0;
+}
+
static const struct file_operations trust_fops = {
.owner = THIS_MODULE,
- .open = video_exclusive_open,
- .release = video_exclusive_release,
+ .open = trust_exclusive_open,
+ .release = trust_exclusive_release,
.ioctl = video_ioctl2,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l_compat_ioctl32,
@@ -366,6 +378,7 @@
.name = "Trust FM Radio",
.fops = &trust_fops,
.ioctl_ops = &trust_ioctl_ops,
+ .release = video_device_release_empty,
};
static int __init trust_init(void)
diff --git a/drivers/media/radio/radio-typhoon.c b/drivers/media/radio/radio-typhoon.c
index f8d62cf..952ec35 100644
--- a/drivers/media/radio/radio-typhoon.c
+++ b/drivers/media/radio/radio-typhoon.c
@@ -79,7 +79,7 @@
#endif
struct typhoon_device {
- int users;
+ unsigned long in_use;
int iobase;
int curvol;
int muted;
@@ -223,8 +223,7 @@
static int vidioc_s_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct video_device *dev = video_devdata(file);
- struct typhoon_device *typhoon = dev->priv;
+ struct typhoon_device *typhoon = video_drvdata(file);
typhoon->curfreq = f->frequency;
typhoon_setfreq(typhoon, typhoon->curfreq);
@@ -234,8 +233,7 @@
static int vidioc_g_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct video_device *dev = video_devdata(file);
- struct typhoon_device *typhoon = dev->priv;
+ struct typhoon_device *typhoon = video_drvdata(file);
f->type = V4L2_TUNER_RADIO;
f->frequency = typhoon->curfreq;
@@ -261,8 +259,7 @@
static int vidioc_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct video_device *dev = video_devdata(file);
- struct typhoon_device *typhoon = dev->priv;
+ struct typhoon_device *typhoon = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -278,8 +275,7 @@
static int vidioc_s_ctrl (struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct video_device *dev = video_devdata(file);
- struct typhoon_device *typhoon = dev->priv;
+ struct typhoon_device *typhoon = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -334,10 +330,21 @@
.mutefreq = CONFIG_RADIO_TYPHOON_MUTEFREQ,
};
+static int typhoon_exclusive_open(struct inode *inode, struct file *file)
+{
+ return test_and_set_bit(0, &typhoon_unit.in_use) ? -EBUSY : 0;
+}
+
+static int typhoon_exclusive_release(struct inode *inode, struct file *file)
+{
+ clear_bit(0, &typhoon_unit.in_use);
+ return 0;
+}
+
static const struct file_operations typhoon_fops = {
.owner = THIS_MODULE,
- .open = video_exclusive_open,
- .release = video_exclusive_release,
+ .open = typhoon_exclusive_open,
+ .release = typhoon_exclusive_release,
.ioctl = video_ioctl2,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l_compat_ioctl32,
@@ -364,6 +371,7 @@
.name = "Typhoon Radio",
.fops = &typhoon_fops,
.ioctl_ops = &typhoon_ioctl_ops,
+ .release = video_device_release_empty,
};
#ifdef CONFIG_RADIO_TYPHOON_PROC_FS
@@ -446,9 +454,8 @@
return -EBUSY;
}
- typhoon_radio.priv = &typhoon_unit;
- if (video_register_device(&typhoon_radio, VFL_TYPE_RADIO, radio_nr) == -1)
- {
+ video_set_drvdata(&typhoon_radio, &typhoon_unit);
+ if (video_register_device(&typhoon_radio, VFL_TYPE_RADIO, radio_nr) < 0) {
release_region(io, 8);
return -EINVAL;
}
diff --git a/drivers/media/radio/radio-zoltrix.c b/drivers/media/radio/radio-zoltrix.c
index 51d57ed..15b10ba 100644
--- a/drivers/media/radio/radio-zoltrix.c
+++ b/drivers/media/radio/radio-zoltrix.c
@@ -69,6 +69,7 @@
static int radio_nr = -1;
struct zol_device {
+ unsigned long in_use;
int port;
int curvol;
unsigned long curfreq;
@@ -122,8 +123,11 @@
unsigned int stereo = dev->stereo;
int i;
- if (freq == 0)
- return 1;
+ if (freq == 0) {
+ printk(KERN_WARNING "zoltrix: received zero freq. Failed to set.\n");
+ return -EINVAL;
+ }
+
m = (freq / 160 - 8800) * 2;
f = (unsigned long long) m + 0x4d1c;
@@ -245,8 +249,7 @@
static int vidioc_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
- struct video_device *dev = video_devdata(file);
- struct zol_device *zol = dev->priv;
+ struct zol_device *zol = video_drvdata(file);
if (v->index > 0)
return -EINVAL;
@@ -276,19 +279,20 @@
static int vidioc_s_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct video_device *dev = video_devdata(file);
- struct zol_device *zol = dev->priv;
+ struct zol_device *zol = video_drvdata(file);
zol->curfreq = f->frequency;
- zol_setfreq(zol, zol->curfreq);
+ if (zol_setfreq(zol, zol->curfreq) != 0) {
+ printk(KERN_WARNING "zoltrix: Set frequency failed.\n");
+ return -EINVAL;
+ }
return 0;
}
static int vidioc_g_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct video_device *dev = video_devdata(file);
- struct zol_device *zol = dev->priv;
+ struct zol_device *zol = video_drvdata(file);
f->type = V4L2_TUNER_RADIO;
f->frequency = zol->curfreq;
@@ -313,8 +317,7 @@
static int vidioc_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct video_device *dev = video_devdata(file);
- struct zol_device *zol = dev->priv;
+ struct zol_device *zol = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -330,8 +333,7 @@
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct video_device *dev = video_devdata(file);
- struct zol_device *zol = dev->priv;
+ struct zol_device *zol = video_drvdata(file);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -347,7 +349,10 @@
return 0;
}
zol->stereo = 1;
- zol_setfreq(zol, zol->curfreq);
+ if (zol_setfreq(zol, zol->curfreq) != 0) {
+ printk(KERN_WARNING "zoltrix: Set frequency failed.\n");
+ return -EINVAL;
+ }
#if 0
/* FIXME: Implement stereo/mono switch on V4L2 */
if (v->mode & VIDEO_SOUND_STEREO) {
@@ -396,11 +401,22 @@
static struct zol_device zoltrix_unit;
+static int zoltrix_exclusive_open(struct inode *inode, struct file *file)
+{
+ return test_and_set_bit(0, &zoltrix_unit.in_use) ? -EBUSY : 0;
+}
+
+static int zoltrix_exclusive_release(struct inode *inode, struct file *file)
+{
+ clear_bit(0, &zoltrix_unit.in_use);
+ return 0;
+}
+
static const struct file_operations zoltrix_fops =
{
.owner = THIS_MODULE,
- .open = video_exclusive_open,
- .release = video_exclusive_release,
+ .open = zoltrix_exclusive_open,
+ .release = zoltrix_exclusive_release,
.ioctl = video_ioctl2,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l_compat_ioctl32,
@@ -427,6 +443,7 @@
.name = "Zoltrix Radio Plus",
.fops = &zoltrix_fops,
.ioctl_ops = &zoltrix_ioctl_ops,
+ .release = video_device_release_empty,
};
static int __init zoltrix_init(void)
@@ -440,7 +457,7 @@
return -ENXIO;
}
- zoltrix_radio.priv = &zoltrix_unit;
+ video_set_drvdata(&zoltrix_radio, &zoltrix_unit);
if (!request_region(io, 2, "zoltrix")) {
printk(KERN_ERR "zoltrix: port 0x%x already in use\n", io);
return -EBUSY;
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 3e9e0dc..47102c2 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -34,6 +34,7 @@
select VIDEOBUF_GEN
config VIDEO_BTCX
+ depends on PCI
tristate
config VIDEO_IR
@@ -71,6 +72,15 @@
V4L devices.
In doubt, say N.
+config VIDEO_FIXED_MINOR_RANGES
+ bool "Enable old-style fixed minor ranges for video devices"
+ default n
+ ---help---
+ Say Y here to enable the old-style fixed-range minor assignments.
+ Only useful if you rely on the old behavior and use mknod instead of udev.
+
+ When in doubt, say N.
+
config VIDEO_HELPER_CHIPS_AUTO
bool "Autoselect pertinent encoders/decoders and other helper chips"
default y
@@ -578,13 +588,6 @@
To compile this driver as a module, choose M here: the
module will be called saa5249.
-config TUNER_3036
- tristate "SAB3036 tuner"
- depends on I2C && VIDEO_V4L1
- help
- Say Y here to include support for Philips SAB3036 compatible tuners.
- If in doubt, say N.
-
config VIDEO_VINO
tristate "SGI Vino Video For Linux (EXPERIMENTAL)"
depends on I2C && SGI_IP22 && EXPERIMENTAL && VIDEO_V4L2
@@ -602,79 +605,7 @@
driver for PCI. There is a product page at
<http://www.stradis.com/>.
-config VIDEO_ZORAN
- tristate "Zoran ZR36057/36067 Video For Linux"
- depends on PCI && I2C_ALGOBIT && VIDEO_V4L1 && VIRT_TO_BUS
- help
- Say Y for support for MJPEG capture cards based on the Zoran
- 36057/36067 PCI controller chipset. This includes the Iomega
- Buz, Pinnacle DC10+ and the Linux Media Labs LML33. There is
- a driver homepage at <http://mjpeg.sf.net/driver-zoran/>. For
- more information, check <file:Documentation/video4linux/Zoran>.
-
- To compile this driver as a module, choose M here: the
- module will be called zr36067.
-
-config VIDEO_ZORAN_DC30
- tristate "Pinnacle/Miro DC30(+) support"
- depends on VIDEO_ZORAN
- select VIDEO_ADV7175 if VIDEO_HELPER_CHIPS_AUTO
- select VIDEO_VPX3220 if VIDEO_HELPER_CHIPS_AUTO
- help
- Support for the Pinnacle/Miro DC30(+) MJPEG capture/playback
- card. This also supports really old DC10 cards based on the
- zr36050 MJPEG codec and zr36016 VFE.
-
-config VIDEO_ZORAN_ZR36060
- tristate "Zoran ZR36060"
- depends on VIDEO_ZORAN
- help
- Say Y to support Zoran boards based on 36060 chips.
- This includes Iomega Buz, Pinnacle DC10, Linux media Labs 33
- and 33 R10 and AverMedia 6 boards.
-
-config VIDEO_ZORAN_BUZ
- tristate "Iomega Buz support"
- depends on VIDEO_ZORAN_ZR36060
- select VIDEO_SAA7111 if VIDEO_HELPER_CHIPS_AUTO
- select VIDEO_SAA7185 if VIDEO_HELPER_CHIPS_AUTO
- help
- Support for the Iomega Buz MJPEG capture/playback card.
-
-config VIDEO_ZORAN_DC10
- tristate "Pinnacle/Miro DC10(+) support"
- depends on VIDEO_ZORAN_ZR36060
- select VIDEO_SAA7110 if VIDEO_HELPER_CHIPS_AUTO
- select VIDEO_ADV7175 if VIDEO_HELPER_CHIPS_AUTO
- help
- Support for the Pinnacle/Miro DC10(+) MJPEG capture/playback
- card.
-
-config VIDEO_ZORAN_LML33
- tristate "Linux Media Labs LML33 support"
- depends on VIDEO_ZORAN_ZR36060
- select VIDEO_BT819 if VIDEO_HELPER_CHIPS_AUTO
- select VIDEO_BT856 if VIDEO_HELPER_CHIPS_AUTO
- help
- Support for the Linux Media Labs LML33 MJPEG capture/playback
- card.
-
-config VIDEO_ZORAN_LML33R10
- tristate "Linux Media Labs LML33R10 support"
- depends on VIDEO_ZORAN_ZR36060
- select VIDEO_SAA7114 if VIDEO_HELPER_CHIPS_AUTO
- select VIDEO_ADV7170 if VIDEO_HELPER_CHIPS_AUTO
- help
- support for the Linux Media Labs LML33R10 MJPEG capture/playback
- card.
-
-config VIDEO_ZORAN_AVS6EYES
- tristate "AverMedia 6 Eyes support (EXPERIMENTAL)"
- depends on VIDEO_ZORAN_ZR36060 && EXPERIMENTAL && VIDEO_V4L1
- select VIDEO_BT856 if VIDEO_HELPER_CHIPS_AUTO
- select VIDEO_KS0127 if VIDEO_HELPER_CHIPS_AUTO
- help
- Support for the AverMedia 6 Eyes video surveillance card.
+source "drivers/media/video/zoran/Kconfig"
config VIDEO_MEYE
tristate "Sony Vaio Picturebook Motion Eye Video For Linux"
@@ -697,7 +628,7 @@
depends on PCI && VIDEO_V4L1 && I2C
select VIDEO_SAA7146_VV
select VIDEO_TUNER
- select VIDEO_SAA7111 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_SAA7115 if VIDEO_HELPER_CHIPS_AUTO
select VIDEO_TDA9840 if VIDEO_HELPER_CHIPS_AUTO
select VIDEO_TEA6415C if VIDEO_HELPER_CHIPS_AUTO
select VIDEO_TEA6420 if VIDEO_HELPER_CHIPS_AUTO
@@ -708,21 +639,6 @@
To compile this driver as a module, choose M here: the
module will be called mxb.
-config VIDEO_DPC
- tristate "Philips-Semiconductors 'dpc7146 demonstration board'"
- depends on PCI && VIDEO_V4L1 && I2C
- select VIDEO_SAA7146_VV
- select VIDEO_SAA7111 if VIDEO_HELPER_CHIPS_AUTO
- ---help---
- This is a video4linux driver for the 'dpc7146 demonstration
- board' by Philips-Semiconductors. It's the reference design
- for SAA7146 bases boards, so if you have some unsupported
- saa7146 based, analog video card, chances are good that it
- will work with this skeleton driver.
-
- To compile this driver as a module, choose M here: the
- module will be called dpc7146.
-
config VIDEO_HEXIUM_ORION
tristate "Hexium HV-PCI6 and Orion frame grabber"
depends on PCI && VIDEO_V4L2 && I2C
@@ -784,6 +700,70 @@
CMOS camera controller. This is the controller found on first-
generation OLPC systems.
+config SOC_CAMERA
+ tristate "SoC camera support"
+ depends on VIDEO_V4L2 && HAS_DMA
+ select VIDEOBUF_GEN
+ help
+ SoC Camera is a common API to several cameras, not connecting
+ over a bus like PCI or USB. For example some i2c camera connected
+ directly to the data bus of an SoC.
+
+config SOC_CAMERA_MT9M001
+ tristate "mt9m001 support"
+ depends on SOC_CAMERA && I2C
+ select GPIO_PCA953X if MT9M001_PCA9536_SWITCH
+ help
+ This driver supports MT9M001 cameras from Micron, monochrome
+ and colour models.
+
+config MT9M001_PCA9536_SWITCH
+ bool "pca9536 datawidth switch for mt9m001"
+ depends on SOC_CAMERA_MT9M001 && GENERIC_GPIO
+ help
+ Select this if your MT9M001 camera uses a PCA9536 I2C GPIO
+ extender to switch between 8 and 10 bit datawidth modes
+
+config SOC_CAMERA_MT9M111
+ tristate "mt9m111 support"
+ depends on SOC_CAMERA && I2C
+ help
+ This driver supports MT9M111 cameras from Micron
+
+config SOC_CAMERA_MT9V022
+ tristate "mt9v022 support"
+ depends on SOC_CAMERA && I2C
+ select GPIO_PCA953X if MT9V022_PCA9536_SWITCH
+ help
+ This driver supports MT9V022 cameras from Micron
+
+config MT9V022_PCA9536_SWITCH
+ bool "pca9536 datawidth switch for mt9v022"
+ depends on SOC_CAMERA_MT9V022 && GENERIC_GPIO
+ help
+ Select this if your MT9V022 camera uses a PCA9536 I2C GPIO
+ extender to switch between 8 and 10 bit datawidth modes
+
+config SOC_CAMERA_PLATFORM
+ tristate "platform camera support"
+ depends on SOC_CAMERA
+ help
+ This is a generic SoC camera platform driver, useful for testing
+
+config VIDEO_PXA27x
+ tristate "PXA27x Quick Capture Interface driver"
+ depends on VIDEO_DEV && PXA27x && SOC_CAMERA
+ select VIDEOBUF_DMA_SG
+ ---help---
+ This is a v4l2 driver for the PXA27x Quick Capture Interface
+
+config VIDEO_SH_MOBILE_CEU
+ tristate "SuperH Mobile CEU Interface driver"
+ depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA
+ select VIDEOBUF_DMA_CONTIG
+ ---help---
+ This is a v4l2 driver for the SuperH Mobile CEU Interface
+
#
# USB Multimedia device configuration
#
@@ -822,8 +802,7 @@
config USB_W9968CF
tristate "USB W996[87]CF JPEG Dual Mode Camera support"
- depends on VIDEO_V4L1 && I2C
- select VIDEO_OVCAMCHIP
+ depends on VIDEO_V4L1 && I2C && VIDEO_OVCAMCHIP
---help---
Say Y here if you want support for cameras based on OV681 or
Winbond W9967CF/W9968CF JPEG USB Dual Mode Camera Chips.
@@ -914,64 +893,4 @@
endif # V4L_USB_DRIVERS
-config SOC_CAMERA
- tristate "SoC camera support"
- depends on VIDEO_V4L2 && HAS_DMA
- select VIDEOBUF_GEN
- help
- SoC Camera is a common API to several cameras, not connecting
- over a bus like PCI or USB. For example some i2c camera connected
- directly to the data bus of an SoC.
-
-config SOC_CAMERA_MT9M001
- tristate "mt9m001 support"
- depends on SOC_CAMERA && I2C
- select GPIO_PCA953X if MT9M001_PCA9536_SWITCH
- help
- This driver supports MT9M001 cameras from Micron, monochrome
- and colour models.
-
-config MT9M001_PCA9536_SWITCH
- bool "pca9536 datawidth switch for mt9m001"
- depends on SOC_CAMERA_MT9M001 && GENERIC_GPIO
- help
- Select this if your MT9M001 camera uses a PCA9536 I2C GPIO
- extender to switch between 8 and 10 bit datawidth modes
-
-config SOC_CAMERA_MT9V022
- tristate "mt9v022 support"
- depends on SOC_CAMERA && I2C
- select GPIO_PCA953X if MT9V022_PCA9536_SWITCH
- help
- This driver supports MT9V022 cameras from Micron
-
-config MT9V022_PCA9536_SWITCH
- bool "pca9536 datawidth switch for mt9v022"
- depends on SOC_CAMERA_MT9V022 && GENERIC_GPIO
- help
- Select this if your MT9V022 camera uses a PCA9536 I2C GPIO
- extender to switch between 8 and 10 bit datawidth modes
-
-config SOC_CAMERA_PLATFORM
- tristate "platform camera support"
- depends on SOC_CAMERA
- help
- This is a generic SoC camera platform driver, useful for testing
-
-config VIDEO_PXA27x
- tristate "PXA27x Quick Capture Interface driver"
- depends on VIDEO_DEV && PXA27x
- select SOC_CAMERA
- select VIDEOBUF_DMA_SG
- ---help---
- This is a v4l2 driver for the PXA27x Quick Capture Interface
-
-config VIDEO_SH_MOBILE_CEU
- tristate "SuperH Mobile CEU Interface driver"
- depends on VIDEO_DEV && HAS_DMA
- select SOC_CAMERA
- select VIDEOBUF_DMA_CONTIG
- ---help---
- This is a v4l2 driver for the SuperH Mobile CEU Interface
-
endif # VIDEO_CAPTURE_DRIVERS
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index ef7c8d3..16962f3 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -2,8 +2,6 @@
# Makefile for the video capture/playback device drivers.
#
-zr36067-objs := zoran_procfs.o zoran_device.o \
- zoran_driver.o zoran_card.o
tuner-objs := tuner-core.o
msp3400-objs := msp3400-driver.o msp3400-kthreads.o
@@ -54,9 +52,7 @@
obj-$(CONFIG_VIDEO_BT866) += bt866.o
obj-$(CONFIG_VIDEO_KS0127) += ks0127.o
-obj-$(CONFIG_VIDEO_ZORAN) += zr36067.o videocodec.o
-obj-$(CONFIG_VIDEO_ZORAN_DC30) += zr36050.o zr36016.o
-obj-$(CONFIG_VIDEO_ZORAN_ZR36060) += zr36060.o
+obj-$(CONFIG_VIDEO_ZORAN) += zoran/
obj-$(CONFIG_VIDEO_PMS) += pms.o
obj-$(CONFIG_VIDEO_VINO) += vino.o indycam.o
@@ -84,8 +80,6 @@
obj-$(CONFIG_VIDEO_MXB) += mxb.o
obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o
obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o
-obj-$(CONFIG_VIDEO_DPC) += dpc7146.o
-obj-$(CONFIG_TUNER_3036) += tuner-3036.o
obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o
@@ -137,6 +131,7 @@
obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o
obj-$(CONFIG_SOC_CAMERA) += soc_camera.o
obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o
+obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o
obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o
obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o
diff --git a/drivers/media/video/arv.c b/drivers/media/video/arv.c
index 9e436ad..218754b 100644
--- a/drivers/media/video/arv.c
+++ b/drivers/media/video/arv.c
@@ -116,6 +116,7 @@
int width, height;
int frame_bytes, line_bytes;
wait_queue_head_t wait;
+ unsigned long in_use;
struct mutex lock;
};
@@ -269,7 +270,7 @@
static ssize_t ar_read(struct file *file, char *buf, size_t count, loff_t *ppos)
{
struct video_device *v = video_devdata(file);
- struct ar_device *ar = v->priv;
+ struct ar_device *ar = video_get_drvdata(v);
long ret = ar->frame_bytes; /* return read bytes */
unsigned long arvcr1 = 0;
unsigned long flags;
@@ -399,7 +400,7 @@
unsigned int cmd, void *arg)
{
struct video_device *dev = video_devdata(file);
- struct ar_device *ar = dev->priv;
+ struct ar_device *ar = video_get_drvdata(dev);
DEBUG(1, "ar_ioctl()\n");
switch(cmd) {
@@ -625,7 +626,7 @@
*/
static int ar_initialize(struct video_device *dev)
{
- struct ar_device *ar = dev->priv;
+ struct ar_device *ar = video_get_drvdata(dev);
unsigned long cr = 0;
int i,found=0;
@@ -732,7 +733,7 @@
void ar_release(struct video_device *vfd)
{
- struct ar_device *ar = vfd->priv;
+ struct ar_device *ar = video_get_drvdata(vfd);
mutex_lock(&ar->lock);
video_device_release(vfd);
}
@@ -742,10 +743,23 @@
* Video4Linux Module functions
*
****************************************************************************/
+static struct ar_device ardev;
+
+static int ar_exclusive_open(struct inode *inode, struct file *file)
+{
+ return test_and_set_bit(0, &ardev.in_use) ? -EBUSY : 0;
+}
+
+static int ar_exclusive_release(struct inode *inode, struct file *file)
+{
+ clear_bit(0, &ardev.in_use);
+ return 0;
+}
+
static const struct file_operations ar_fops = {
.owner = THIS_MODULE,
- .open = video_exclusive_open,
- .release = video_exclusive_release,
+ .open = ar_exclusive_open,
+ .release = ar_exclusive_release,
.read = ar_read,
.ioctl = ar_ioctl,
#ifdef CONFIG_COMPAT
@@ -762,7 +776,6 @@
};
#define ALIGN4(x) ((((int)(x)) & 0x3) == 0)
-static struct ar_device ardev;
static int __init ar_init(void)
{
@@ -802,7 +815,7 @@
return -ENOMEM;
}
memcpy(ar->vdev, &ar_template, sizeof(ar_template));
- ar->vdev->priv = ar;
+ video_set_drvdata(ar->vdev, ar);
if (vga) {
ar->width = AR_WIDTH_VGA;
diff --git a/drivers/media/video/au0828/au0828-cards.c b/drivers/media/video/au0828/au0828-cards.c
index ed48908..5f07a8a 100644
--- a/drivers/media/video/au0828/au0828-cards.c
+++ b/drivers/media/video/au0828/au0828-cards.c
@@ -46,7 +46,7 @@
/* Tuner callback function for au0828 boards. Currently only needed
* for HVR1500Q, which has an xc5000 tuner.
*/
-int au0828_tuner_callback(void *priv, int command, int arg)
+int au0828_tuner_callback(void *priv, int component, int command, int arg)
{
struct au0828_dev *dev = priv;
diff --git a/drivers/media/video/au0828/au0828-dvb.c b/drivers/media/video/au0828/au0828-dvb.c
index ba94be7..f0fcdb4 100644
--- a/drivers/media/video/au0828/au0828-dvb.c
+++ b/drivers/media/video/au0828/au0828-dvb.c
@@ -36,11 +36,39 @@
#define _AU0828_BULKPIPE 0x83
#define _BULKPIPESIZE 0xe522
+static u8 hauppauge_hvr950q_led_states[] = {
+ 0x00, /* off */
+ 0x02, /* yellow */
+ 0x04, /* green */
+};
+
+static struct au8522_led_config hauppauge_hvr950q_led_cfg = {
+ .gpio_output = 0x00e0,
+ .gpio_output_enable = 0x6006,
+ .gpio_output_disable = 0x0660,
+
+ .gpio_leds = 0x00e2,
+ .led_states = hauppauge_hvr950q_led_states,
+ .num_led_states = sizeof(hauppauge_hvr950q_led_states),
+
+ .vsb8_strong = 20 /* dB */ * 10,
+ .qam64_strong = 25 /* dB */ * 10,
+ .qam256_strong = 32 /* dB */ * 10,
+};
+
static struct au8522_config hauppauge_hvr950q_config = {
.demod_address = 0x8e >> 1,
.status_mode = AU8522_DEMODLOCKING,
.qam_if = AU8522_IF_6MHZ,
.vsb_if = AU8522_IF_6MHZ,
+ .led_cfg = &hauppauge_hvr950q_led_cfg,
+};
+
+static struct au8522_config fusionhdtv7usb_config = {
+ .demod_address = 0x8e >> 1,
+ .status_mode = AU8522_DEMODLOCKING,
+ .qam_if = AU8522_IF_6MHZ,
+ .vsb_if = AU8522_IF_6MHZ,
};
static struct au8522_config hauppauge_woodbury_config = {
@@ -53,7 +81,6 @@
static struct xc5000_config hauppauge_hvr950q_tunerconfig = {
.i2c_address = 0x61,
.if_khz = 6000,
- .tuner_callback = au0828_tuner_callback
};
static struct mxl5007t_config mxl5007t_hvr950q_config = {
@@ -353,14 +380,12 @@
switch (dev->board) {
case AU0828_BOARD_HAUPPAUGE_HVR850:
case AU0828_BOARD_HAUPPAUGE_HVR950Q:
- case AU0828_BOARD_DVICO_FUSIONHDTV7:
dvb->frontend = dvb_attach(au8522_attach,
&hauppauge_hvr950q_config,
&dev->i2c_adap);
if (dvb->frontend != NULL)
- dvb_attach(xc5000_attach, dvb->frontend,
- &dev->i2c_adap,
- &hauppauge_hvr950q_tunerconfig, dev);
+ dvb_attach(xc5000_attach, dvb->frontend, &dev->i2c_adap,
+ &hauppauge_hvr950q_tunerconfig);
break;
case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
dvb->frontend = dvb_attach(au8522_attach,
@@ -380,6 +405,16 @@
0x60, &dev->i2c_adap,
&hauppauge_woodbury_tunerconfig);
break;
+ case AU0828_BOARD_DVICO_FUSIONHDTV7:
+ dvb->frontend = dvb_attach(au8522_attach,
+ &fusionhdtv7usb_config,
+ &dev->i2c_adap);
+ if (dvb->frontend != NULL) {
+ dvb_attach(xc5000_attach, dvb->frontend,
+ &dev->i2c_adap,
+ &hauppauge_hvr950q_tunerconfig);
+ }
+ break;
default:
printk(KERN_WARNING "The frontend of your DVB/ATSC card "
"isn't supported yet\n");
@@ -390,6 +425,8 @@
__func__);
return -1;
}
+ /* define general-purpose callback pointer */
+ dvb->frontend->callback = au0828_tuner_callback;
/* register everything */
ret = dvb_register(dev);
diff --git a/drivers/media/video/au0828/au0828.h b/drivers/media/video/au0828/au0828.h
index 4f10ff3..9d6a116 100644
--- a/drivers/media/video/au0828/au0828.h
+++ b/drivers/media/video/au0828/au0828.h
@@ -103,7 +103,8 @@
extern struct au0828_board au0828_boards[];
extern struct usb_device_id au0828_usb_id_table[];
extern void au0828_gpio_setup(struct au0828_dev *dev);
-extern int au0828_tuner_callback(void *priv, int command, int arg);
+extern int au0828_tuner_callback(void *priv, int component,
+ int command, int arg);
extern void au0828_card_setup(struct au0828_dev *dev);
/* ----------------------------------------------------------- */
diff --git a/drivers/media/video/bt856.c b/drivers/media/video/bt856.c
index 98ee2d8..ab2ce4d 100644
--- a/drivers/media/video/bt856.c
+++ b/drivers/media/video/bt856.c
@@ -68,8 +68,8 @@
/* ----------------------------------------------------------------------- */
-#define REG_OFFSET 0xDA
-#define BT856_NR_REG 6
+#define BT856_REG_OFFSET 0xDA
+#define BT856_NR_REG 6
struct bt856 {
unsigned char reg[BT856_NR_REG];
@@ -89,7 +89,7 @@
{
struct bt856 *encoder = i2c_get_clientdata(client);
- encoder->reg[reg - REG_OFFSET] = value;
+ encoder->reg[reg - BT856_REG_OFFSET] = value;
return i2c_smbus_write_byte_data(client, reg, value);
}
@@ -103,7 +103,7 @@
return bt856_write(client, reg,
(encoder->
- reg[reg - REG_OFFSET] & ~(1 << bit)) |
+ reg[reg - BT856_REG_OFFSET] & ~(1 << bit)) |
(value ? (1 << bit) : 0));
}
diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c
index 6081edc..13742b0 100644
--- a/drivers/media/video/bt8xx/bttv-cards.c
+++ b/drivers/media/video/bt8xx/bttv-cards.c
@@ -305,7 +305,7 @@
{ 0x00261822, BTTV_BOARD_TWINHAN_DST, "DNTV Live! Mini "},
{ 0xd200dbc0, BTTV_BOARD_DVICO_FUSIONHDTV_2, "DViCO FusionHDTV 2" },
{ 0x763c008a, BTTV_BOARD_GEOVISION_GV600, "GeoVision GV-600" },
-
+ { 0x18011000, BTTV_BOARD_ENLTV_FM_2, "Encore ENL TV-FM-2" },
{ 0, -1, NULL }
};
@@ -3037,6 +3037,31 @@
.has_radio = 1,
.has_remote = 1,
},
+ [BTTV_BOARD_ENLTV_FM_2] = {
+ /* Encore TV Tuner Pro ENL TV-FM-2
+ Mauro Carvalho Chehab <mchehab@infradead.org */
+ .name = "Encore ENL TV-FM-2",
+ .video_inputs = 3,
+ .audio_inputs = 1,
+ .tuner = 0,
+ .svhs = 2,
+ /* bit 6 -> IR disabled
+ bit 18/17 = 00 -> mute
+ 01 -> enable external audio input
+ 10 -> internal audio input (mono?)
+ 11 -> internal audio input
+ */
+ .gpiomask = 0x060040,
+ .muxsel = { 2, 3, 3 },
+ .gpiomux = { 0x60000, 0x60000, 0x20000, 0x20000 },
+ .gpiomute = 0,
+ .tuner_type = TUNER_TCL_MF02GIP_5N,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .has_radio = 1,
+ .has_remote = 1,
+ }
};
static const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards);
diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c
index 933eaef..5858bf5 100644
--- a/drivers/media/video/bt8xx/bttv-driver.c
+++ b/drivers/media/video/bt8xx/bttv-driver.c
@@ -76,9 +76,9 @@
static unsigned int gbufsize = 0x208000;
static unsigned int reset_crop = 1;
-static int video_nr = -1;
-static int radio_nr = -1;
-static int vbi_nr = -1;
+static int video_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
+static int radio_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
+static int vbi_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
static int debug_latency;
static unsigned int fdsr;
@@ -108,9 +108,6 @@
module_param(debug_latency, int, 0644);
module_param(fdsr, int, 0444);
-module_param(video_nr, int, 0444);
-module_param(radio_nr, int, 0444);
-module_param(vbi_nr, int, 0444);
module_param(gbuffers, int, 0444);
module_param(gbufsize, int, 0444);
module_param(reset_crop, int, 0444);
@@ -130,7 +127,10 @@
module_param(full_luma_range, int, 0444);
module_param(coring, int, 0444);
-module_param_array(radio, int, NULL, 0444);
+module_param_array(radio, int, NULL, 0444);
+module_param_array(video_nr, int, NULL, 0444);
+module_param_array(radio_nr, int, NULL, 0444);
+module_param_array(vbi_nr, int, NULL, 0444);
MODULE_PARM_DESC(radio,"The TV card supports radio, default is 0 (no)");
MODULE_PARM_DESC(bigendian,"byte order of the framebuffer, default is native endian");
@@ -152,6 +152,9 @@
MODULE_PARM_DESC(uv_ratio,"ratio between u and v gains, default is 50");
MODULE_PARM_DESC(full_luma_range,"use the full luma range, default is 0 (no)");
MODULE_PARM_DESC(coring,"set the luma coring level, default is 0 (no)");
+MODULE_PARM_DESC(video_nr, "video device numbers");
+MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
+MODULE_PARM_DESC(radio_nr, "radio device numbers");
MODULE_DESCRIPTION("bttv - v4l/v4l2 driver module for bt848/878 based cards");
MODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr");
@@ -1367,7 +1370,7 @@
(btv->gpioirq ? BT848_INT_GPINT : 0) |
BT848_INT_SCERR |
(fdsr ? BT848_INT_FDSR : 0) |
- BT848_INT_RISCI|BT848_INT_OCERR|BT848_INT_VPRES|
+ BT848_INT_RISCI | BT848_INT_OCERR |
BT848_INT_FMTCHG|BT848_INT_HLOCK|
BT848_INT_I2CDONE,
BT848_INT_MASK);
@@ -2661,18 +2664,6 @@
return 0;
}
-static int bttv_enum_fmt_vbi_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- if (0 != f->index)
- return -EINVAL;
-
- f->pixelformat = V4L2_PIX_FMT_GREY;
- strcpy(f->description, "vbi data");
-
- return 0;
-}
-
static int bttv_enum_fmt_cap_ovr(struct v4l2_fmtdesc *f)
{
int index = -1, i;
@@ -3227,6 +3218,7 @@
dprintk(KERN_DEBUG "bttv: open minor=%d\n",minor);
+ lock_kernel();
for (i = 0; i < bttv_num; i++) {
if (bttvs[i].video_dev &&
bttvs[i].video_dev->minor == minor) {
@@ -3241,16 +3233,20 @@
break;
}
}
- if (NULL == btv)
+ if (NULL == btv) {
+ unlock_kernel();
return -ENODEV;
+ }
dprintk(KERN_DEBUG "bttv%d: open called (type=%s)\n",
btv->c.nr,v4l2_type_names[type]);
/* allocate per filehandle data */
fh = kmalloc(sizeof(*fh),GFP_KERNEL);
- if (NULL == fh)
+ if (NULL == fh) {
+ unlock_kernel();
return -ENOMEM;
+ }
file->private_data = fh;
*fh = btv->init;
fh->type = type;
@@ -3270,6 +3266,7 @@
sizeof(struct bttv_buffer),
fh);
set_tvnorm(btv,btv->tvnorm);
+ set_input(btv, btv->input, btv->tvnorm);
btv->users++;
@@ -3290,6 +3287,7 @@
bttv_vbi_fmt_reset(&fh->vbi_fmt, btv->tvnorm);
bttv_field_count(btv);
+ unlock_kernel();
return 0;
}
@@ -3330,6 +3328,10 @@
btv->users--;
bttv_field_count(btv);
+
+ if (!btv->users)
+ audio_mute(btv, 1);
+
return 0;
}
@@ -3367,7 +3369,6 @@
.vidioc_g_fmt_vid_overlay = bttv_g_fmt_vid_overlay,
.vidioc_try_fmt_vid_overlay = bttv_try_fmt_vid_overlay,
.vidioc_s_fmt_vid_overlay = bttv_s_fmt_vid_overlay,
- .vidioc_enum_fmt_vbi_cap = bttv_enum_fmt_vbi_cap,
.vidioc_g_fmt_vbi_cap = bttv_g_fmt_vbi_cap,
.vidioc_try_fmt_vbi_cap = bttv_try_fmt_vbi_cap,
.vidioc_s_fmt_vbi_cap = bttv_s_fmt_vbi_cap,
@@ -3430,21 +3431,26 @@
dprintk("bttv: open minor=%d\n",minor);
+ lock_kernel();
for (i = 0; i < bttv_num; i++) {
if (bttvs[i].radio_dev && bttvs[i].radio_dev->minor == minor) {
btv = &bttvs[i];
break;
}
}
- if (NULL == btv)
+ if (NULL == btv) {
+ unlock_kernel();
return -ENODEV;
+ }
dprintk("bttv%d: open called (radio)\n",btv->c.nr);
/* allocate per filehandle data */
fh = kmalloc(sizeof(*fh), GFP_KERNEL);
- if (NULL == fh)
+ if (NULL == fh) {
+ unlock_kernel();
return -ENOMEM;
+ }
file->private_data = fh;
*fh = btv->init;
v4l2_prio_open(&btv->prio, &fh->prio);
@@ -3457,6 +3463,7 @@
audio_input(btv,TVAUDIO_INPUT_RADIO);
mutex_unlock(&btv->lock);
+ unlock_kernel();
return 0;
}
@@ -4235,7 +4242,8 @@
if (NULL == btv->video_dev)
goto err;
- if (video_register_device(btv->video_dev,VFL_TYPE_GRABBER,video_nr)<0)
+ if (video_register_device(btv->video_dev, VFL_TYPE_GRABBER,
+ video_nr[btv->c.nr]) < 0)
goto err;
printk(KERN_INFO "bttv%d: registered device video%d\n",
btv->c.nr,btv->video_dev->minor & 0x1f);
@@ -4251,7 +4259,8 @@
if (NULL == btv->vbi_dev)
goto err;
- if (video_register_device(btv->vbi_dev,VFL_TYPE_VBI,vbi_nr)<0)
+ if (video_register_device(btv->vbi_dev, VFL_TYPE_VBI,
+ vbi_nr[btv->c.nr]) < 0)
goto err;
printk(KERN_INFO "bttv%d: registered device vbi%d\n",
btv->c.nr,btv->vbi_dev->minor & 0x1f);
@@ -4262,7 +4271,8 @@
btv->radio_dev = vdev_init(btv, &radio_template, "radio");
if (NULL == btv->radio_dev)
goto err;
- if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO,radio_nr)<0)
+ if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO,
+ radio_nr[btv->c.nr]) < 0)
goto err;
printk(KERN_INFO "bttv%d: registered device radio%d\n",
btv->c.nr,btv->radio_dev->minor & 0x1f);
diff --git a/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c
index a38af98..2f289d9 100644
--- a/drivers/media/video/bt8xx/bttv-input.c
+++ b/drivers/media/video/bt8xx/bttv-input.c
@@ -28,8 +28,8 @@
#include "bttvp.h"
-static int debug;
-module_param(debug, int, 0644); /* debug level (0,1,2) */
+static int ir_debug;
+module_param(ir_debug, int, 0644);
static int repeat_delay = 500;
module_param(repeat_delay, int, 0644);
static int repeat_period = 33;
@@ -40,6 +40,12 @@
static int ir_rc5_key_timeout = 200;
module_param(ir_rc5_key_timeout, int, 0644);
+#undef dprintk
+#define dprintk(arg...) do { \
+ if (ir_debug >= 1) \
+ printk(arg); \
+} while (0)
+
#define DEVNAME "bttv-input"
/* ---------------------------------------------------------------------- */
@@ -79,6 +85,45 @@
}
+static void ir_enltv_handle_key(struct bttv *btv)
+{
+ struct card_ir *ir = btv->remote;
+ u32 gpio, data, keyup;
+
+ /* read gpio value */
+ gpio = bttv_gpio_read(&btv->c);
+
+ /* extract data */
+ data = ir_extract_bits(gpio, ir->mask_keycode);
+
+ /* Check if it is keyup */
+ keyup = (gpio & ir->mask_keyup) ? 1 << 31 : 0;
+
+ if ((ir->last_gpio & 0x7f) != data) {
+ dprintk(KERN_INFO DEVNAME ": gpio=0x%x code=%d | %s\n",
+ gpio, data,
+ (gpio & ir->mask_keyup) ? " up" : "up/down");
+
+ ir_input_keydown(ir->dev, &ir->ir, data, data);
+ if (keyup)
+ ir_input_nokey(ir->dev, &ir->ir);
+ } else {
+ if ((ir->last_gpio & 1 << 31) == keyup)
+ return;
+
+ dprintk(KERN_INFO DEVNAME ":(cnt) gpio=0x%x code=%d | %s\n",
+ gpio, data,
+ (gpio & ir->mask_keyup) ? " up" : "down");
+
+ if (keyup)
+ ir_input_nokey(ir->dev, &ir->ir);
+ else
+ ir_input_keydown(ir->dev, &ir->ir, data, data);
+ }
+
+ ir->last_gpio = data | keyup;
+}
+
void bttv_input_irq(struct bttv *btv)
{
struct card_ir *ir = btv->remote;
@@ -92,7 +137,10 @@
struct bttv *btv = (struct bttv*)data;
struct card_ir *ir = btv->remote;
- ir_handle_key(btv);
+ if (btv->c.type == BTTV_BOARD_ENLTV_FM_2)
+ ir_enltv_handle_key(btv);
+ else
+ ir_handle_key(btv);
mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling));
}
@@ -284,6 +332,14 @@
ir->mask_keyup = 0x006000;
ir->polling = 50; /* ms */
break;
+ case BTTV_BOARD_ENLTV_FM_2:
+ ir_codes = ir_codes_encore_enltv2;
+ ir->mask_keycode = 0x00fd00;
+ ir->mask_keyup = 0x000080;
+ ir->polling = 1; /* ms */
+ ir->last_gpio = ir_extract_bits(bttv_gpio_read(&btv->c),
+ ir->mask_keycode);
+ break;
}
if (NULL == ir_codes) {
dprintk(KERN_INFO "Ooops: IR config error [card=%d]\n", btv->c.type);
diff --git a/drivers/media/video/bt8xx/bttv.h b/drivers/media/video/bt8xx/bttv.h
index 6d93d16c..46cb90e 100644
--- a/drivers/media/video/bt8xx/bttv.h
+++ b/drivers/media/video/bt8xx/bttv.h
@@ -176,7 +176,7 @@
#define BTTV_BOARD_TYPHOON_TVTUNERPCI 0x95
#define BTTV_BOARD_GEOVISION_GV600 0x96
#define BTTV_BOARD_KOZUMI_KTV_01C 0x97
-
+#define BTTV_BOARD_ENLTV_FM_2 0x98
/* more card-specific defines */
#define PT2254_L_CHANNEL 0x10
diff --git a/drivers/media/video/btcx-risc.c b/drivers/media/video/btcx-risc.c
index 3324ab3..ac1b268 100644
--- a/drivers/media/video/btcx-risc.c
+++ b/drivers/media/video/btcx-risc.c
@@ -64,7 +64,7 @@
unsigned int size)
{
__le32 *cpu;
- dma_addr_t dma;
+ dma_addr_t dma = 0;
if (NULL != risc->cpu && risc->size < size)
btcx_riscmem_free(pci,risc);
diff --git a/drivers/media/video/bw-qcam.c b/drivers/media/video/bw-qcam.c
index 6e39e25..ace4ff9 100644
--- a/drivers/media/video/bw-qcam.c
+++ b/drivers/media/video/bw-qcam.c
@@ -495,7 +495,7 @@
val2 = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) *
q->transfer_scale;
}
- val = (val + val2 - 1) / val2;
+ val = DIV_ROUND_UP(val, val2);
qc_command(q, 0x13);
qc_command(q, val);
@@ -651,7 +651,7 @@
transperline = q->width * q->bpp;
divisor = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) *
q->transfer_scale;
- transperline = (transperline + divisor - 1) / divisor;
+ transperline = DIV_ROUND_UP(transperline, divisor);
for (i = 0, yield = yieldlines; i < linestotrans; i++)
{
@@ -894,10 +894,27 @@
return len;
}
+static int qcam_exclusive_open(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ struct qcam_device *qcam = (struct qcam_device *)dev;
+
+ return test_and_set_bit(0, &qcam->in_use) ? -EBUSY : 0;
+}
+
+static int qcam_exclusive_release(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ struct qcam_device *qcam = (struct qcam_device *)dev;
+
+ clear_bit(0, &qcam->in_use);
+ return 0;
+}
+
static const struct file_operations qcam_fops = {
.owner = THIS_MODULE,
- .open = video_exclusive_open,
- .release = video_exclusive_release,
+ .open = qcam_exclusive_open,
+ .release = qcam_exclusive_release,
.ioctl = qcam_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l_compat_ioctl32,
@@ -909,6 +926,7 @@
{
.name = "Connectix Quickcam",
.fops = &qcam_fops,
+ .release = video_device_release_empty,
};
#define MAX_CAMS 4
diff --git a/drivers/media/video/bw-qcam.h b/drivers/media/video/bw-qcam.h
index 6701daf..8a60c5d 100644
--- a/drivers/media/video/bw-qcam.h
+++ b/drivers/media/video/bw-qcam.h
@@ -65,4 +65,5 @@
int top, left;
int status;
unsigned int saved_bits;
+ unsigned long in_use;
};
diff --git a/drivers/media/video/c-qcam.c b/drivers/media/video/c-qcam.c
index 7f6c6b4..17aa0ad 100644
--- a/drivers/media/video/c-qcam.c
+++ b/drivers/media/video/c-qcam.c
@@ -51,6 +51,7 @@
int contrast, brightness, whitebal;
int top, left;
unsigned int bidirectional;
+ unsigned long in_use;
struct mutex lock;
};
@@ -687,11 +688,28 @@
return len;
}
+static int qcam_exclusive_open(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ struct qcam_device *qcam = (struct qcam_device *)dev;
+
+ return test_and_set_bit(0, &qcam->in_use) ? -EBUSY : 0;
+}
+
+static int qcam_exclusive_release(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ struct qcam_device *qcam = (struct qcam_device *)dev;
+
+ clear_bit(0, &qcam->in_use);
+ return 0;
+}
+
/* video device template */
static const struct file_operations qcam_fops = {
.owner = THIS_MODULE,
- .open = video_exclusive_open,
- .release = video_exclusive_release,
+ .open = qcam_exclusive_open,
+ .release = qcam_exclusive_release,
.ioctl = qcam_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l_compat_ioctl32,
@@ -704,6 +722,7 @@
{
.name = "Colour QuickCam",
.fops = &qcam_fops,
+ .release = video_device_release_empty,
};
/* Initialize the QuickCam driver control structure. */
diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c
index 5405c30..fc9497b 100644
--- a/drivers/media/video/cafe_ccic.c
+++ b/drivers/media/video/cafe_ccic.c
@@ -1476,9 +1476,12 @@
{
struct cafe_camera *cam;
+ lock_kernel();
cam = cafe_find_dev(iminor(inode));
- if (cam == NULL)
+ if (cam == NULL) {
+ unlock_kernel();
return -ENODEV;
+ }
filp->private_data = cam;
mutex_lock(&cam->s_mutex);
@@ -1490,6 +1493,7 @@
}
(cam->users)++;
mutex_unlock(&cam->s_mutex);
+ unlock_kernel();
return 0;
}
@@ -2092,15 +2096,8 @@
const struct pci_device_id *id)
{
int ret;
- u16 classword;
struct cafe_camera *cam;
- /*
- * Make sure we have a camera here - we'll get calls for
- * the other cafe devices as well.
- */
- pci_read_config_word(pdev, PCI_CLASS_DEVICE, &classword);
- if (classword != PCI_CLASS_MULTIMEDIA_VIDEO)
- return -ENODEV;
+
/*
* Start putting together one of our big camera structures.
*/
@@ -2288,8 +2285,8 @@
static struct pci_device_id cafe_ids[] = {
- { PCI_DEVICE(0x11ab, 0x4100) }, /* Eventual real ID */
- { PCI_DEVICE(0x11ab, 0x4102) }, /* Really eventual real ID */
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL,
+ PCI_DEVICE_ID_MARVELL_88ALP01_CCIC) },
{ 0, }
};
diff --git a/drivers/media/video/cpia.c b/drivers/media/video/cpia.c
index a661800..c325e92 100644
--- a/drivers/media/video/cpia.c
+++ b/drivers/media/video/cpia.c
@@ -3155,7 +3155,7 @@
static int cpia_open(struct inode *inode, struct file *file)
{
struct video_device *dev = video_devdata(file);
- struct cam_data *cam = dev->priv;
+ struct cam_data *cam = video_get_drvdata(dev);
int err;
if (!cam) {
@@ -3202,7 +3202,7 @@
/* Set ownership of /proc/cpia/videoX to current user */
if(cam->proc_entry)
- cam->proc_entry->uid = current->uid;
+ cam->proc_entry->uid = current_uid();
/* set mark for loading first frame uncompressed */
cam->first_frame = 1;
@@ -3232,7 +3232,7 @@
static int cpia_close(struct inode *inode, struct file *file)
{
struct video_device *dev = file->private_data;
- struct cam_data *cam = dev->priv;
+ struct cam_data *cam = video_get_drvdata(dev);
if (cam->ops) {
/* Return ownership of /proc/cpia/videoX to root */
@@ -3284,7 +3284,7 @@
size_t count, loff_t *ppos)
{
struct video_device *dev = file->private_data;
- struct cam_data *cam = dev->priv;
+ struct cam_data *cam = video_get_drvdata(dev);
int err;
/* make this _really_ smp and multithread-safe */
@@ -3341,7 +3341,7 @@
unsigned int ioctlnr, void *arg)
{
struct video_device *dev = file->private_data;
- struct cam_data *cam = dev->priv;
+ struct cam_data *cam = video_get_drvdata(dev);
int retval = 0;
if (!cam || !cam->ops)
@@ -3739,7 +3739,7 @@
unsigned long start = vma->vm_start;
unsigned long size = vma->vm_end - vma->vm_start;
unsigned long page, pos;
- struct cam_data *cam = dev->priv;
+ struct cam_data *cam = video_get_drvdata(dev);
int retval;
if (!cam || !cam->ops)
@@ -3801,6 +3801,7 @@
static struct video_device cpia_template = {
.name = "CPiA Camera",
.fops = &cpia_fops,
+ .release = video_device_release_empty,
};
/* initialise cam_data structure */
@@ -3928,7 +3929,7 @@
cam->proc_entry = NULL;
memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template));
- cam->vdev.priv = cam;
+ video_set_drvdata(&cam->vdev, cam);
cam->curframe = 0;
for (i = 0; i < FRAME_NUM; i++) {
diff --git a/drivers/media/video/cpia2/cpia2_core.c b/drivers/media/video/cpia2/cpia2_core.c
index af8b9ec..7e791b6 100644
--- a/drivers/media/video/cpia2/cpia2_core.c
+++ b/drivers/media/video/cpia2/cpia2_core.c
@@ -1537,7 +1537,7 @@
*
* This sets all user changeable properties to the values in cam->params.
*****************************************************************************/
-int set_all_properties(struct camera_data *cam)
+static int set_all_properties(struct camera_data *cam)
{
/**
* Don't set target_kb here, it will be set later.
@@ -1588,7 +1588,7 @@
* get_color_params
*
*****************************************************************************/
-void get_color_params(struct camera_data *cam)
+static void get_color_params(struct camera_data *cam)
{
cpia2_do_command(cam, CPIA2_CMD_GET_VP_BRIGHTNESS, TRANSFER_READ, 0);
cpia2_do_command(cam, CPIA2_CMD_GET_VP_SATURATION, TRANSFER_READ, 0);
@@ -1881,7 +1881,7 @@
* wake_system
*
*****************************************************************************/
-void wake_system(struct camera_data *cam)
+static void wake_system(struct camera_data *cam)
{
cpia2_do_command(cam, CPIA2_CMD_SET_WAKEUP, TRANSFER_WRITE, 0);
}
@@ -1892,7 +1892,7 @@
*
* Valid for STV500 sensor only
*****************************************************************************/
-void set_lowlight_boost(struct camera_data *cam)
+static void set_lowlight_boost(struct camera_data *cam)
{
struct cpia2_command cmd;
@@ -2169,7 +2169,7 @@
*
* Sets all values to the defaults
*****************************************************************************/
-void reset_camera_struct(struct camera_data *cam)
+static void reset_camera_struct(struct camera_data *cam)
{
/***
* The following parameter values are the defaults from the register map.
diff --git a/drivers/media/video/cpia2/cpia2_usb.c b/drivers/media/video/cpia2/cpia2_usb.c
index a8a1990..73511a5 100644
--- a/drivers/media/video/cpia2/cpia2_usb.c
+++ b/drivers/media/video/cpia2/cpia2_usb.c
@@ -478,7 +478,7 @@
* set_alternate
*
*****************************************************************************/
-int set_alternate(struct camera_data *cam, unsigned int alt)
+static int set_alternate(struct camera_data *cam, unsigned int alt)
{
int ret = 0;
diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c
index eb9f15c..897e8d1 100644
--- a/drivers/media/video/cpia2/cpia2_v4l.c
+++ b/drivers/media/video/cpia2/cpia2_v4l.c
@@ -241,8 +241,7 @@
*****************************************************************************/
static int cpia2_open(struct inode *inode, struct file *file)
{
- struct video_device *dev = video_devdata(file);
- struct camera_data *cam = video_get_drvdata(dev);
+ struct camera_data *cam = video_drvdata(file);
int retval = 0;
if (!cam) {
@@ -357,8 +356,7 @@
static ssize_t cpia2_v4l_read(struct file *file, char __user *buf, size_t count,
loff_t *off)
{
- struct video_device *dev = video_devdata(file);
- struct camera_data *cam = video_get_drvdata(dev);
+ struct camera_data *cam = video_drvdata(file);
int noblock = file->f_flags&O_NONBLOCK;
struct cpia2_fh *fh = file->private_data;
@@ -382,9 +380,7 @@
*****************************************************************************/
static unsigned int cpia2_v4l_poll(struct file *filp, struct poll_table_struct *wait)
{
- struct video_device *dev = video_devdata(filp);
- struct camera_data *cam = video_get_drvdata(dev);
-
+ struct camera_data *cam = video_drvdata(filp);
struct cpia2_fh *fh = filp->private_data;
if(!cam)
@@ -1579,8 +1575,7 @@
static int cpia2_do_ioctl(struct inode *inode, struct file *file,
unsigned int ioctl_nr, void *arg)
{
- struct video_device *dev = video_devdata(file);
- struct camera_data *cam = video_get_drvdata(dev);
+ struct camera_data *cam = video_drvdata(file);
int retval = 0;
if (!cam)
@@ -1860,9 +1855,8 @@
*****************************************************************************/
static int cpia2_mmap(struct file *file, struct vm_area_struct *area)
{
+ struct camera_data *cam = video_drvdata(file);
int retval;
- struct video_device *dev = video_devdata(file);
- struct camera_data *cam = video_get_drvdata(dev);
/* Priority check */
struct cpia2_fh *fh = file->private_data;
diff --git a/drivers/media/video/cx18/Makefile b/drivers/media/video/cx18/Makefile
index b23d2e2..f7bf0ed 100644
--- a/drivers/media/video/cx18/Makefile
+++ b/drivers/media/video/cx18/Makefile
@@ -2,7 +2,7 @@
cx18-queue.o cx18-streams.o cx18-fileops.o cx18-ioctl.o cx18-controls.o \
cx18-mailbox.o cx18-vbi.o cx18-audio.o cx18-video.o cx18-irq.o \
cx18-av-core.o cx18-av-audio.o cx18-av-firmware.o cx18-av-vbi.o cx18-scb.o \
- cx18-dvb.o
+ cx18-dvb.o cx18-io.o
obj-$(CONFIG_VIDEO_CX18) += cx18.o
diff --git a/drivers/media/video/cx18/cx18-audio.c b/drivers/media/video/cx18/cx18-audio.c
index 6d5b94f..57beddf 100644
--- a/drivers/media/video/cx18/cx18-audio.c
+++ b/drivers/media/video/cx18/cx18-audio.c
@@ -22,6 +22,7 @@
*/
#include "cx18-driver.h"
+#include "cx18-io.h"
#include "cx18-i2c.h"
#include "cx18-cards.h"
#include "cx18-audio.h"
@@ -60,10 +61,10 @@
if (err)
return err;
- val = read_reg(CX18_AUDIO_ENABLE) & ~0x30;
+ val = cx18_read_reg(cx, CX18_AUDIO_ENABLE) & ~0x30;
val |= (audio_input > CX18_AV_AUDIO_SERIAL2) ? 0x20 :
(audio_input << 4);
- write_reg(val | 0xb00, CX18_AUDIO_ENABLE);
+ cx18_write_reg(cx, val | 0xb00, CX18_AUDIO_ENABLE);
cx18_vapi(cx, CX18_APU_RESETAI, 1, 0);
return 0;
}
diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c
index 3b0a2c4..73f5141 100644
--- a/drivers/media/video/cx18/cx18-av-core.c
+++ b/drivers/media/video/cx18/cx18-av-core.c
@@ -22,27 +22,35 @@
*/
#include "cx18-driver.h"
+#include "cx18-io.h"
int cx18_av_write(struct cx18 *cx, u16 addr, u8 value)
{
- u32 x = readl(cx->reg_mem + 0xc40000 + (addr & ~3));
+ u32 reg = 0xc40000 + (addr & ~3);
u32 mask = 0xff;
int shift = (addr & 3) * 8;
+ u32 x = cx18_read_reg(cx, reg);
x = (x & ~(mask << shift)) | ((u32)value << shift);
- writel(x, cx->reg_mem + 0xc40000 + (addr & ~3));
+ cx18_write_reg(cx, x, reg);
return 0;
}
int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value)
{
- writel(value, cx->reg_mem + 0xc40000 + addr);
+ cx18_write_reg(cx, value, 0xc40000 + addr);
+ return 0;
+}
+
+int cx18_av_write4_noretry(struct cx18 *cx, u16 addr, u32 value)
+{
+ cx18_write_reg_noretry(cx, value, 0xc40000 + addr);
return 0;
}
u8 cx18_av_read(struct cx18 *cx, u16 addr)
{
- u32 x = readl(cx->reg_mem + 0xc40000 + (addr & ~3));
+ u32 x = cx18_read_reg(cx, 0xc40000 + (addr & ~3));
int shift = (addr & 3) * 8;
return (x >> shift) & 0xff;
@@ -50,7 +58,12 @@
u32 cx18_av_read4(struct cx18 *cx, u16 addr)
{
- return readl(cx->reg_mem + 0xc40000 + addr);
+ return cx18_read_reg(cx, 0xc40000 + addr);
+}
+
+u32 cx18_av_read4_noretry(struct cx18 *cx, u16 addr)
+{
+ return cx18_read_reg_noretry(cx, 0xc40000 + addr);
}
int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned and_mask,
diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h
index eb61fa1..b67d8df 100644
--- a/drivers/media/video/cx18/cx18-av-core.h
+++ b/drivers/media/video/cx18/cx18-av-core.h
@@ -301,8 +301,10 @@
/* cx18_av-core.c */
int cx18_av_write(struct cx18 *cx, u16 addr, u8 value);
int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value);
+int cx18_av_write4_noretry(struct cx18 *cx, u16 addr, u32 value);
u8 cx18_av_read(struct cx18 *cx, u16 addr);
u32 cx18_av_read4(struct cx18 *cx, u16 addr);
+u32 cx18_av_read4_noretry(struct cx18 *cx, u16 addr);
int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned mask, u8 value);
int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 mask, u32 value);
int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg);
diff --git a/drivers/media/video/cx18/cx18-av-firmware.c b/drivers/media/video/cx18/cx18-av-firmware.c
index e996a4e..522a035 100644
--- a/drivers/media/video/cx18/cx18-av-firmware.c
+++ b/drivers/media/video/cx18/cx18-av-firmware.c
@@ -20,6 +20,7 @@
*/
#include "cx18-driver.h"
+#include "cx18-io.h"
#include <linux/firmware.h>
#define CX18_AUDIO_ENABLE 0xc72014
@@ -49,7 +50,7 @@
cx18_av_write4(cx, 0x8100, 0x00010000);
/* Put the 8051 in reset and enable firmware upload */
- cx18_av_write4(cx, CXADEC_DL_CTL, 0x0F000000);
+ cx18_av_write4_noretry(cx, CXADEC_DL_CTL, 0x0F000000);
ptr = fw->data;
size = fw->size;
@@ -58,22 +59,28 @@
u32 dl_control = 0x0F000000 | i | ((u32)ptr[i] << 16);
u32 value = 0;
int retries2;
+ int unrec_err = 0;
- for (retries2 = 0; retries2 < 5; retries2++) {
- cx18_av_write4(cx, CXADEC_DL_CTL, dl_control);
+ for (retries2 = 0; retries2 < CX18_MAX_MMIO_RETRIES;
+ retries2++) {
+ cx18_av_write4_noretry(cx, CXADEC_DL_CTL,
+ dl_control);
udelay(10);
- value = cx18_av_read4(cx, CXADEC_DL_CTL);
+ value = cx18_av_read4_noretry(cx,
+ CXADEC_DL_CTL);
if (value == dl_control)
break;
/* Check if we can correct the byte by changing
the address. We can only write the lower
address byte of the address. */
if ((value & 0x3F00) != (dl_control & 0x3F00)) {
- retries2 = 5;
+ unrec_err = 1;
break;
}
}
- if (retries2 >= 5)
+ cx18_log_write_retries(cx, retries2,
+ cx->reg_mem + 0xc40000 + CXADEC_DL_CTL);
+ if (unrec_err || retries2 >= CX18_MAX_MMIO_RETRIES)
break;
}
if (i == size)
@@ -119,10 +126,10 @@
have a name in the spec. */
cx18_av_write4(cx, 0x09CC, 1);
- v = read_reg(CX18_AUDIO_ENABLE);
- /* If bit 11 is 1 */
+ v = cx18_read_reg(cx, CX18_AUDIO_ENABLE);
+ /* If bit 11 is 1, clear bit 10 */
if (v & 0x800)
- write_reg(v & 0xFFFFFBFF, CX18_AUDIO_ENABLE); /* Clear bit 10 */
+ cx18_write_reg(cx, v & 0xFFFFFBFF, CX18_AUDIO_ENABLE);
/* Enable WW auto audio standard detection */
v = cx18_av_read4(cx, CXADEC_STD_DET_CTL);
diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c
index 3cb9734..5efe01e 100644
--- a/drivers/media/video/cx18/cx18-cards.c
+++ b/drivers/media/video/cx18/cx18-cards.c
@@ -292,12 +292,111 @@
/* ------------------------------------------------------------------------- */
+/* Toshiba Qosmio laptop internal DVB-T/Analog Hybrid Tuner */
+
+static const struct cx18_card_pci_info cx18_pci_toshiba_qosmio_dvbt[] = {
+ { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_TOSHIBA, 0x0110 },
+ { 0, 0, 0 }
+};
+
+static const struct cx18_card cx18_card_toshiba_qosmio_dvbt = {
+ .type = CX18_CARD_TOSHIBA_QOSMIO_DVBT,
+ .name = "Toshiba Qosmio DVB-T/Analog",
+ .comment = "Experimenters and photos needed for device to work well.\n"
+ "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n",
+ .v4l2_capabilities = CX18_CAP_ENCODER,
+ .hw_audio_ctrl = CX18_HW_CX23418,
+ .hw_all = CX18_HW_TUNER,
+ .video_inputs = {
+ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE6 },
+ { CX18_CARD_INPUT_SVIDEO1, 1,
+ CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 },
+ { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 },
+ },
+ .audio_inputs = {
+ { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 },
+ { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 1 },
+ },
+ .tuners = {
+ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+ },
+ .ddr = {
+ .chip_config = 0x202,
+ .refresh = 0x3bb,
+ .timing1 = 0x33320a63,
+ .timing2 = 0x0a,
+ .tune_lane = 0,
+ .initial_emrs = 0x42,
+ },
+ .xceive_pin = 15,
+ .pci_list = cx18_pci_toshiba_qosmio_dvbt,
+ .i2c = &cx18_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Leadtek WinFast PVR2100 */
+
+static const struct cx18_card_pci_info cx18_pci_leadtek_pvr2100[] = {
+ { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_LEADTEK, 0x6f27 },
+ { 0, 0, 0 }
+};
+
+static const struct cx18_card cx18_card_leadtek_pvr2100 = {
+ .type = CX18_CARD_LEADTEK_PVR2100,
+ .name = "Leadtek WinFast PVR2100",
+ .comment = "Experimenters and photos needed for device to work well.\n"
+ "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n",
+ .v4l2_capabilities = CX18_CAP_ENCODER,
+ .hw_audio_ctrl = CX18_HW_CX23418,
+ .hw_muxer = CX18_HW_GPIO,
+ .hw_all = CX18_HW_TUNER | CX18_HW_GPIO,
+ .video_inputs = {
+ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 },
+ { CX18_CARD_INPUT_SVIDEO1, 1,
+ CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 },
+ { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE7 },
+ },
+ .audio_inputs = {
+ { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 },
+ { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 1 },
+ },
+ .tuners = {
+ /* XC3028 tuner */
+ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+ },
+ .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 2 },
+ .ddr = {
+ /*
+ * Pointer to proper DDR config values provided by
+ * Terry Wu <terrywu at leadtek.com.tw>
+ */
+ .chip_config = 0x303,
+ .refresh = 0x3bb,
+ .timing1 = 0x24220e83,
+ .timing2 = 0x1f,
+ .tune_lane = 0,
+ .initial_emrs = 0x2,
+ },
+ .gpio_init.initial_value = 0x6,
+ .gpio_init.direction = 0x7,
+ .gpio_audio_input = { .mask = 0x7,
+ .tuner = 0x6, .linein = 0x2, .radio = 0x2 },
+ .xceive_pin = 15,
+ .pci_list = cx18_pci_leadtek_pvr2100,
+ .i2c = &cx18_i2c_std,
+};
+
+/* ------------------------------------------------------------------------- */
+
static const struct cx18_card *cx18_card_list[] = {
&cx18_card_hvr1600_esmt,
&cx18_card_hvr1600_samsung,
&cx18_card_h900,
&cx18_card_mpc718,
&cx18_card_cnxt_raptor_pal,
+ &cx18_card_toshiba_qosmio_dvbt,
+ &cx18_card_leadtek_pvr2100,
};
const struct cx18_card *cx18_get_card(u16 index)
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c
index bd18afe..085121c 100644
--- a/drivers/media/video/cx18/cx18-driver.c
+++ b/drivers/media/video/cx18/cx18-driver.c
@@ -4,6 +4,7 @@
* Derived from ivtv-driver.c
*
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
*
* 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
@@ -22,6 +23,7 @@
*/
#include "cx18-driver.h"
+#include "cx18-io.h"
#include "cx18-version.h"
#include "cx18-cards.h"
#include "cx18-i2c.h"
@@ -73,10 +75,14 @@
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1 };
-
+static int mmio_ndelay[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 };
static unsigned cardtype_c = 1;
static unsigned tuner_c = 1;
static unsigned radio_c = 1;
+static unsigned mmio_ndelay_c = 1;
static char pal[] = "--";
static char secam[] = "--";
static char ntsc[] = "-";
@@ -90,15 +96,18 @@
static int cx18_pci_latency = 1;
+int cx18_retry_mmio = 1;
int cx18_debug;
module_param_array(tuner, int, &tuner_c, 0644);
module_param_array(radio, bool, &radio_c, 0644);
module_param_array(cardtype, int, &cardtype_c, 0644);
+module_param_array(mmio_ndelay, int, &mmio_ndelay_c, 0644);
module_param_string(pal, pal, sizeof(pal), 0644);
module_param_string(secam, secam, sizeof(secam), 0644);
module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
module_param_named(debug, cx18_debug, int, 0644);
+module_param_named(retry_mmio, cx18_retry_mmio, int, 0644);
module_param(cx18_pci_latency, int, 0644);
module_param(cx18_first_minor, int, 0644);
@@ -121,6 +130,8 @@
"\t\t\t 3 = Compro VideoMate H900\n"
"\t\t\t 4 = Yuan MPC718\n"
"\t\t\t 5 = Conexant Raptor PAL/SECAM\n"
+ "\t\t\t 6 = Toshiba Qosmio DVB-T/Analog\n"
+ "\t\t\t 7 = Leadtek WinFast PVR2100\n"
"\t\t\t 0 = Autodetect (default)\n"
"\t\t\t-1 = Ignore this card\n\t\t");
MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60");
@@ -140,6 +151,14 @@
MODULE_PARM_DESC(cx18_pci_latency,
"Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n"
"\t\t\tDefault: Yes");
+MODULE_PARM_DESC(retry_mmio,
+ "Check and retry memory mapped IO accesses\n"
+ "\t\t\tDefault: 1 [Yes]");
+MODULE_PARM_DESC(mmio_ndelay,
+ "Delay (ns) for each CX23418 memory mapped IO access.\n"
+ "\t\t\tTry larger values that are close to a multiple of the\n"
+ "\t\t\tPCI clock period, 30.3 ns, if your card doesn't work.\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_MMIO_NDELAY));
MODULE_PARM_DESC(enc_mpg_buffers,
"Encoder MPG Buffers (in MB)\n"
"\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS));
@@ -156,7 +175,7 @@
"Encoder PCM buffers (in MB)\n"
"\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFFERS));
-MODULE_PARM_DESC(cx18_first_minor, "Set minor assigned to first card");
+MODULE_PARM_DESC(cx18_first_minor, "Set kernel number assigned to first card");
MODULE_AUTHOR("Hans Verkuil");
MODULE_DESCRIPTION("CX23418 driver");
@@ -356,6 +375,11 @@
cx->options.tuner = tuner[cx->num];
cx->options.radio = radio[cx->num];
+ if (mmio_ndelay[cx->num] < 0)
+ cx->options.mmio_ndelay = CX18_DEFAULT_MMIO_NDELAY;
+ else
+ cx->options.mmio_ndelay = mmio_ndelay[cx->num];
+
cx->std = cx18_parse_std(cx);
if (cx->options.cardtype == -1) {
CX18_INFO("Ignore card\n");
@@ -395,9 +419,9 @@
if (cx->card == NULL) {
cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
- CX18_ERR("Unknown card: vendor/device: %04x/%04x\n",
+ CX18_ERR("Unknown card: vendor/device: [%04x:%04x]\n",
cx->dev->vendor, cx->dev->device);
- CX18_ERR(" subsystem vendor/device: %04x/%04x\n",
+ CX18_ERR(" subsystem vendor/device: [%04x:%04x]\n",
cx->dev->subsystem_vendor, cx->dev->subsystem_device);
CX18_ERR("Defaulting to %s card\n", cx->card->name);
CX18_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n");
@@ -511,9 +535,9 @@
return -EIO;
}
- /* Check for bus mastering */
+ /* Enable bus mastering and memory mapped IO for the CX23418 */
pci_read_config_word(dev, PCI_COMMAND, &cmd);
- cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
+ cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
pci_write_config_word(dev, PCI_COMMAND, cmd);
pci_read_config_byte(dev, PCI_CLASS_REVISION, &cx->card_rev);
@@ -525,11 +549,6 @@
pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency);
}
- /* This config space value relates to DMA latencies. The
- default value 0x8080 is too low however and will lead
- to DMA errors. 0xffff is the max value which solves
- these problems. */
- pci_write_config_dword(dev, 0x40, 0xffff);
CX18_DEBUG_INFO("cx%d (rev %d) at %02x:%02x.%x, "
"irq: %d, latency: %d, memory: 0x%lx\n",
@@ -656,7 +675,7 @@
goto free_mem;
}
cx->reg_mem = cx->enc_mem + CX18_REG_OFFSET;
- devtype = read_reg(0xC72028);
+ devtype = cx18_read_reg(cx, 0xC72028);
switch (devtype & 0xff000000) {
case 0xff000000:
CX18_INFO("cx23418 revision %08x (A)\n", devtype);
@@ -815,6 +834,7 @@
if (retval == 0)
retval = -ENODEV;
CX18_ERR("Error %d on initialization\n", retval);
+ cx18_log_statistics(cx);
kfree(cx18_cards[cx18_cards_active]);
cx18_cards[cx18_cards_active] = NULL;
@@ -902,8 +922,8 @@
cx18_stop_all_captures(cx);
/* Interrupts */
- sw1_irq_disable(IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
- sw2_irq_disable(IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
+ cx18_sw1_irq_disable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
+ cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
cx18_halt_firmware(cx);
@@ -919,6 +939,7 @@
pci_disable_device(cx->dev);
+ cx18_log_statistics(cx);
CX18_INFO("Removed %s, card #%d\n", cx->card_name, cx->num);
}
@@ -938,7 +959,7 @@
/* Validate parameters */
if (cx18_first_minor < 0 || cx18_first_minor >= CX18_MAX_CARDS) {
- printk(KERN_ERR "cx18: Exiting, ivtv_first_minor must be between 0 and %d\n",
+ printk(KERN_ERR "cx18: Exiting, cx18_first_minor must be between 0 and %d\n",
CX18_MAX_CARDS - 1);
return -1;
}
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h
index 4801bc7..fa8be07 100644
--- a/drivers/media/video/cx18/cx18-driver.h
+++ b/drivers/media/video/cx18/cx18-driver.h
@@ -38,7 +38,6 @@
#include <linux/i2c-algo-bit.h>
#include <linux/list.h>
#include <linux/unistd.h>
-#include <linux/byteorder/swab.h>
#include <linux/pagemap.h>
#include <linux/workqueue.h>
#include <linux/mutex.h>
@@ -64,6 +63,9 @@
# error "This driver requires kernel PCI support."
#endif
+/* Default delay to throttle mmio access to the CX23418 */
+#define CX18_DEFAULT_MMIO_NDELAY 0 /* 0 ns = 0 PCI clock(s) / 33 MHz */
+
#define CX18_MEM_OFFSET 0x00000000
#define CX18_MEM_SIZE 0x04000000
#define CX18_REG_OFFSET 0x02000000
@@ -77,7 +79,9 @@
#define CX18_CARD_COMPRO_H900 2 /* Compro VideoMate H900 */
#define CX18_CARD_YUAN_MPC718 3 /* Yuan MPC718 */
#define CX18_CARD_CNXT_RAPTOR_PAL 4 /* Conexant Raptor PAL */
-#define CX18_CARD_LAST 4
+#define CX18_CARD_TOSHIBA_QOSMIO_DVBT 5 /* Toshiba Qosmio Interal DVB-T/Analog*/
+#define CX18_CARD_LEADTEK_PVR2100 6 /* Leadtek WinFast PVR2100 */
+#define CX18_CARD_LAST 6
#define CX18_ENC_STREAM_TYPE_MPG 0
#define CX18_ENC_STREAM_TYPE_TS 1
@@ -97,6 +101,8 @@
#define CX18_PCI_ID_COMPRO 0x185b
#define CX18_PCI_ID_YUAN 0x12ab
#define CX18_PCI_ID_CONEXANT 0x14f1
+#define CX18_PCI_ID_TOSHIBA 0x1179
+#define CX18_PCI_ID_LEADTEK 0x107D
/* ======================================================================== */
/* ========================== START USER SETTABLE DMA VARIABLES =========== */
@@ -169,6 +175,7 @@
#define CX18_MAX_PGM_INDEX (400)
+extern int cx18_retry_mmio; /* enable check & retry of mmio accesses */
extern int cx18_debug;
@@ -177,6 +184,7 @@
int cardtype; /* force card type on load */
int tuner; /* set tuner on load */
int radio; /* enable/disable radio */
+ unsigned long mmio_ndelay; /* delay in ns after every PCI mmio access */
};
/* per-buffer bit flags */
@@ -216,8 +224,7 @@
struct cx18_queue {
struct list_head list;
- u32 buffers;
- u32 length;
+ atomic_t buffers;
u32 bytesused;
};
@@ -237,6 +244,8 @@
struct cx18; /* forward reference */
struct cx18_scb; /* forward reference */
+#define CX18_INVALID_TASK_HANDLE 0xffffffff
+
struct cx18_stream {
/* These first four fields are always set, even if the stream
is not actually created. */
@@ -259,7 +268,6 @@
/* Buffer Stats */
u32 buffers;
u32 buf_size;
- u32 buffers_stolen;
/* Buffer Queues */
struct cx18_queue q_free; /* free buffers */
@@ -341,6 +349,13 @@
int bus_index; /* 0 or 1 for the cx23418's 1st or 2nd I2C bus */
};
+#define CX18_MAX_MMIO_RETRIES 10
+
+struct cx18_mmio_stats {
+ atomic_t retried_write[CX18_MAX_MMIO_RETRIES+1];
+ atomic_t retried_read[CX18_MAX_MMIO_RETRIES+1];
+};
+
/* Struct to hold info about cx18 cards */
struct cx18 {
int num; /* board number, -1 during init! */
@@ -430,6 +445,9 @@
u32 gpio_val;
struct mutex gpio_lock;
+ /* Statistics */
+ struct cx18_mmio_stats mmio_stats;
+
/* v4l2 and User settings */
/* codec settings */
@@ -458,47 +476,4 @@
/* First-open initialization: load firmware, etc. */
int cx18_init_on_first_open(struct cx18 *cx);
-/* This is a PCI post thing, where if the pci register is not read, then
- the write doesn't always take effect right away. By reading back the
- register any pending PCI writes will be performed (in order), and so
- you can be sure that the writes are guaranteed to be done.
-
- Rarely needed, only in some timing sensitive cases.
- Apparently if this is not done some motherboards seem
- to kill the firmware and get into the broken state until computer is
- rebooted. */
-#define write_sync(val, reg) \
- do { writel(val, reg); readl(reg); } while (0)
-
-#define read_reg(reg) readl(cx->reg_mem + (reg))
-#define write_reg(val, reg) writel(val, cx->reg_mem + (reg))
-#define write_reg_sync(val, reg) \
- do { write_reg(val, reg); read_reg(reg); } while (0)
-
-#define read_enc(addr) readl(cx->enc_mem + (u32)(addr))
-#define write_enc(val, addr) writel(val, cx->enc_mem + (u32)(addr))
-#define write_enc_sync(val, addr) \
- do { write_enc(val, addr); read_enc(addr); } while (0)
-
-#define sw1_irq_enable(val) do { \
- write_reg(val, SW1_INT_STATUS); \
- write_reg(read_reg(SW1_INT_ENABLE_PCI) | (val), SW1_INT_ENABLE_PCI); \
-} while (0)
-
-#define sw1_irq_disable(val) \
- write_reg(read_reg(SW1_INT_ENABLE_PCI) & ~(val), SW1_INT_ENABLE_PCI);
-
-#define sw2_irq_enable(val) do { \
- write_reg(val, SW2_INT_STATUS); \
- write_reg(read_reg(SW2_INT_ENABLE_PCI) | (val), SW2_INT_ENABLE_PCI); \
-} while (0)
-
-#define sw2_irq_disable(val) \
- write_reg(read_reg(SW2_INT_ENABLE_PCI) & ~(val), SW2_INT_ENABLE_PCI);
-
-#define setup_page(addr) do { \
- u32 val = read_reg(0xD000F8) & ~0x1f00; \
- write_reg(val | (((addr) >> 17) & 0x1f00), 0xD000F8); \
-} while (0)
-
#endif /* CX18_DRIVER_H */
diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c
index 1e420a8..afc694e 100644
--- a/drivers/media/video/cx18/cx18-dvb.c
+++ b/drivers/media/video/cx18/cx18-dvb.c
@@ -21,6 +21,7 @@
#include "cx18-version.h"
#include "cx18-dvb.h"
+#include "cx18-io.h"
#include "cx18-streams.h"
#include "cx18-cards.h"
#include "s5h1409.h"
@@ -87,13 +88,13 @@
switch (cx->card->type) {
case CX18_CARD_HVR_1600_ESMT:
case CX18_CARD_HVR_1600_SAMSUNG:
- v = read_reg(CX18_REG_DMUX_NUM_PORT_0_CONTROL);
+ v = cx18_read_reg(cx, CX18_REG_DMUX_NUM_PORT_0_CONTROL);
v |= 0x00400000; /* Serial Mode */
v |= 0x00002000; /* Data Length - Byte */
v |= 0x00010000; /* Error - Polarity */
v |= 0x00020000; /* Error - Passthru */
v |= 0x000c0000; /* Error - Ignore */
- write_reg(v, CX18_REG_DMUX_NUM_PORT_0_CONTROL);
+ cx18_write_reg(cx, v, CX18_REG_DMUX_NUM_PORT_0_CONTROL);
break;
default:
diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c
index 1e537fe..5f90899 100644
--- a/drivers/media/video/cx18/cx18-fileops.c
+++ b/drivers/media/video/cx18/cx18-fileops.c
@@ -132,6 +132,7 @@
u16 new_stereo_mode;
const u16 stereo_mask = 0x0300;
const u16 dual = 0x0200;
+ u32 h;
new_stereo_mode = cx->params.audio_properties & stereo_mask;
memset(&vt, 0, sizeof(vt));
@@ -143,13 +144,21 @@
if (new_stereo_mode == cx->dualwatch_stereo_mode)
return;
- new_bitmap = new_stereo_mode | (cx->params.audio_properties & ~stereo_mask);
+ new_bitmap = new_stereo_mode
+ | (cx->params.audio_properties & ~stereo_mask);
- CX18_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. new audio_bitmask=0x%ux\n",
- cx->dualwatch_stereo_mode, new_stereo_mode, new_bitmap);
+ CX18_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. "
+ "new audio_bitmask=0x%ux\n",
+ cx->dualwatch_stereo_mode, new_stereo_mode, new_bitmap);
- if (cx18_vapi(cx, CX18_CPU_SET_AUDIO_PARAMETERS, 2,
- cx18_find_handle(cx), new_bitmap) == 0) {
+ h = cx18_find_handle(cx);
+ if (h == CX18_INVALID_TASK_HANDLE) {
+ CX18_DEBUG_INFO("dualwatch: can't find valid task handle\n");
+ return;
+ }
+
+ if (cx18_vapi(cx,
+ CX18_CPU_SET_AUDIO_PARAMETERS, 2, h, new_bitmap) == 0) {
cx->dualwatch_stereo_mode = new_stereo_mode;
return;
}
@@ -223,7 +232,7 @@
prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE);
/* New buffers might have become available before we were added
to the waitqueue */
- if (!s->q_full.buffers)
+ if (!atomic_read(&s->q_full.buffers))
schedule();
finish_wait(&s->waitq, &wait);
if (signal_pending(current)) {
@@ -509,7 +518,7 @@
CX18_DEBUG_HI_FILE("Encoder poll\n");
poll_wait(filp, &s->waitq, wait);
- if (s->q_full.length || s->q_io.length)
+ if (atomic_read(&s->q_full.buffers) || atomic_read(&s->q_io.buffers))
return POLLIN | POLLRDNORM;
if (eof)
return POLLHUP;
@@ -695,20 +704,28 @@
void cx18_mute(struct cx18 *cx)
{
- if (atomic_read(&cx->ana_capturing))
- cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2,
- cx18_find_handle(cx), 1);
+ u32 h;
+ if (atomic_read(&cx->ana_capturing)) {
+ h = cx18_find_handle(cx);
+ if (h != CX18_INVALID_TASK_HANDLE)
+ cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2, h, 1);
+ else
+ CX18_ERR("Can't find valid task handle for mute\n");
+ }
CX18_DEBUG_INFO("Mute\n");
}
void cx18_unmute(struct cx18 *cx)
{
+ u32 h;
if (atomic_read(&cx->ana_capturing)) {
- cx18_msleep_timeout(100, 0);
- cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2,
- cx18_find_handle(cx), 12);
- cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2,
- cx18_find_handle(cx), 0);
+ h = cx18_find_handle(cx);
+ if (h != CX18_INVALID_TASK_HANDLE) {
+ cx18_msleep_timeout(100, 0);
+ cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, h, 12);
+ cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2, h, 0);
+ } else
+ CX18_ERR("Can't find valid task handle for unmute\n");
}
CX18_DEBUG_INFO("Unmute\n");
}
diff --git a/drivers/media/video/cx18/cx18-firmware.c b/drivers/media/video/cx18/cx18-firmware.c
index 78fadd2..5153442 100644
--- a/drivers/media/video/cx18/cx18-firmware.c
+++ b/drivers/media/video/cx18/cx18-firmware.c
@@ -20,6 +20,7 @@
*/
#include "cx18-driver.h"
+#include "cx18-io.h"
#include "cx18-scb.h"
#include "cx18-irq.h"
#include "cx18-firmware.h"
@@ -113,11 +114,11 @@
src = (const u32 *)fw->data;
for (i = 0; i < fw->size; i += 4096) {
- setup_page(i);
+ cx18_setup_page(cx, i);
for (j = i; j < fw->size && j < i + 4096; j += 4) {
/* no need for endianness conversion on the ppc */
- __raw_writel(*src, dst);
- if (__raw_readl(dst) != *src) {
+ cx18_raw_writel(cx, *src, dst);
+ if (cx18_raw_readl(cx, dst) != *src) {
CX18_ERR("Mismatch at offset %x\n", i);
release_firmware(fw);
return -EIO;
@@ -170,12 +171,15 @@
if (offset + seghdr.size > sz)
break;
for (i = 0; i < seghdr.size; i += 4096) {
- setup_page(offset + i);
+ cx18_setup_page(cx, offset + i);
for (j = i; j < seghdr.size && j < i + 4096; j += 4) {
/* no need for endianness conversion on the ppc */
- __raw_writel(src[(offset + j) / 4], dst + seghdr.addr + j);
- if (__raw_readl(dst + seghdr.addr + j) != src[(offset + j) / 4]) {
- CX18_ERR("Mismatch at offset %x\n", offset + j);
+ cx18_raw_writel(cx, src[(offset + j) / 4],
+ dst + seghdr.addr + j);
+ if (cx18_raw_readl(cx, dst + seghdr.addr + j)
+ != src[(offset + j) / 4]) {
+ CX18_ERR("Mismatch at offset %x\n",
+ offset + j);
release_firmware(fw);
return -EIO;
}
@@ -189,43 +193,45 @@
size = fw->size;
release_firmware(fw);
/* Clear bit0 for APU to start from 0 */
- write_reg(read_reg(0xc72030) & ~1, 0xc72030);
+ cx18_write_reg(cx, cx18_read_reg(cx, 0xc72030) & ~1, 0xc72030);
return size;
}
void cx18_halt_firmware(struct cx18 *cx)
{
CX18_DEBUG_INFO("Preparing for firmware halt.\n");
- write_reg(0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */
- write_reg(0x00020002, CX18_ADEC_CONTROL);
+ cx18_write_reg(cx, 0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */
+ cx18_write_reg(cx, 0x00020002, CX18_ADEC_CONTROL);
}
void cx18_init_power(struct cx18 *cx, int lowpwr)
{
/* power-down Spare and AOM PLLs */
/* power-up fast, slow and mpeg PLLs */
- write_reg(0x00000008, CX18_PLL_POWER_DOWN);
+ cx18_write_reg(cx, 0x00000008, CX18_PLL_POWER_DOWN);
/* ADEC out of sleep */
- write_reg(0x00020000, CX18_ADEC_CONTROL);
+ cx18_write_reg(cx, 0x00020000, CX18_ADEC_CONTROL);
/* The fast clock is at 200/245 MHz */
- write_reg(lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT);
- write_reg(lowpwr ? 0x1EFBF37 : 0x038E3D7, CX18_FAST_CLOCK_PLL_FRAC);
+ cx18_write_reg(cx, lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT);
+ cx18_write_reg(cx, lowpwr ? 0x1EFBF37 : 0x038E3D7,
+ CX18_FAST_CLOCK_PLL_FRAC);
- write_reg(2, CX18_FAST_CLOCK_PLL_POST);
- write_reg(1, CX18_FAST_CLOCK_PLL_PRESCALE);
- write_reg(4, CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH);
+ cx18_write_reg(cx, 2, CX18_FAST_CLOCK_PLL_POST);
+ cx18_write_reg(cx, 1, CX18_FAST_CLOCK_PLL_PRESCALE);
+ cx18_write_reg(cx, 4, CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH);
/* set slow clock to 125/120 MHz */
- write_reg(lowpwr ? 0x11 : 0x10, CX18_SLOW_CLOCK_PLL_INT);
- write_reg(lowpwr ? 0xEBAF05 : 0x18618A8, CX18_SLOW_CLOCK_PLL_FRAC);
- write_reg(4, CX18_SLOW_CLOCK_PLL_POST);
+ cx18_write_reg(cx, lowpwr ? 0x11 : 0x10, CX18_SLOW_CLOCK_PLL_INT);
+ cx18_write_reg(cx, lowpwr ? 0xEBAF05 : 0x18618A8,
+ CX18_SLOW_CLOCK_PLL_FRAC);
+ cx18_write_reg(cx, 4, CX18_SLOW_CLOCK_PLL_POST);
/* mpeg clock pll 54MHz */
- write_reg(0xF, CX18_MPEG_CLOCK_PLL_INT);
- write_reg(0x2BCFEF, CX18_MPEG_CLOCK_PLL_FRAC);
- write_reg(8, CX18_MPEG_CLOCK_PLL_POST);
+ cx18_write_reg(cx, 0xF, CX18_MPEG_CLOCK_PLL_INT);
+ cx18_write_reg(cx, 0x2BCFEF, CX18_MPEG_CLOCK_PLL_FRAC);
+ cx18_write_reg(cx, 8, CX18_MPEG_CLOCK_PLL_POST);
/* Defaults */
/* APU = SC or SC/2 = 125/62.5 */
@@ -242,81 +248,84 @@
/* VFC = disabled */
/* USB = disabled */
- write_reg(lowpwr ? 0xFFFF0020 : 0x00060004, CX18_CLOCK_SELECT1);
- write_reg(lowpwr ? 0xFFFF0004 : 0x00060006, CX18_CLOCK_SELECT2);
+ cx18_write_reg(cx, lowpwr ? 0xFFFF0020 : 0x00060004,
+ CX18_CLOCK_SELECT1);
+ cx18_write_reg(cx, lowpwr ? 0xFFFF0004 : 0x00060006,
+ CX18_CLOCK_SELECT2);
- write_reg(0xFFFF0002, CX18_HALF_CLOCK_SELECT1);
- write_reg(0xFFFF0104, CX18_HALF_CLOCK_SELECT2);
+ cx18_write_reg(cx, 0xFFFF0002, CX18_HALF_CLOCK_SELECT1);
+ cx18_write_reg(cx, 0xFFFF0104, CX18_HALF_CLOCK_SELECT2);
- write_reg(0xFFFF9026, CX18_CLOCK_ENABLE1);
- write_reg(0xFFFF3105, CX18_CLOCK_ENABLE2);
+ cx18_write_reg(cx, 0xFFFF9026, CX18_CLOCK_ENABLE1);
+ cx18_write_reg(cx, 0xFFFF3105, CX18_CLOCK_ENABLE2);
}
void cx18_init_memory(struct cx18 *cx)
{
cx18_msleep_timeout(10, 0);
- write_reg(0x10000, CX18_DDR_SOFT_RESET);
+ cx18_write_reg(cx, 0x10000, CX18_DDR_SOFT_RESET);
cx18_msleep_timeout(10, 0);
- write_reg(cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG);
+ cx18_write_reg(cx, cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG);
cx18_msleep_timeout(10, 0);
- write_reg(cx->card->ddr.refresh, CX18_DDR_REFRESH);
- write_reg(cx->card->ddr.timing1, CX18_DDR_TIMING1);
- write_reg(cx->card->ddr.timing2, CX18_DDR_TIMING2);
+ cx18_write_reg(cx, cx->card->ddr.refresh, CX18_DDR_REFRESH);
+ cx18_write_reg(cx, cx->card->ddr.timing1, CX18_DDR_TIMING1);
+ cx18_write_reg(cx, cx->card->ddr.timing2, CX18_DDR_TIMING2);
cx18_msleep_timeout(10, 0);
/* Initialize DQS pad time */
- write_reg(cx->card->ddr.tune_lane, CX18_DDR_TUNE_LANE);
- write_reg(cx->card->ddr.initial_emrs, CX18_DDR_INITIAL_EMRS);
+ cx18_write_reg(cx, cx->card->ddr.tune_lane, CX18_DDR_TUNE_LANE);
+ cx18_write_reg(cx, cx->card->ddr.initial_emrs, CX18_DDR_INITIAL_EMRS);
cx18_msleep_timeout(10, 0);
- write_reg(0x20000, CX18_DDR_SOFT_RESET);
+ cx18_write_reg(cx, 0x20000, CX18_DDR_SOFT_RESET);
cx18_msleep_timeout(10, 0);
/* use power-down mode when idle */
- write_reg(0x00000010, CX18_DDR_POWER_REG);
+ cx18_write_reg(cx, 0x00000010, CX18_DDR_POWER_REG);
- write_reg(0x10001, CX18_REG_BUS_TIMEOUT_EN);
+ cx18_write_reg(cx, 0x10001, CX18_REG_BUS_TIMEOUT_EN);
- write_reg(0x48, CX18_DDR_MB_PER_ROW_7);
- write_reg(0xE0000, CX18_DDR_BASE_63_ADDR);
+ cx18_write_reg(cx, 0x48, CX18_DDR_MB_PER_ROW_7);
+ cx18_write_reg(cx, 0xE0000, CX18_DDR_BASE_63_ADDR);
- write_reg(0x00000101, CX18_WMB_CLIENT02); /* AO */
- write_reg(0x00000101, CX18_WMB_CLIENT09); /* AI2 */
- write_reg(0x00000101, CX18_WMB_CLIENT05); /* VIM1 */
- write_reg(0x00000101, CX18_WMB_CLIENT06); /* AI1 */
- write_reg(0x00000101, CX18_WMB_CLIENT07); /* 3D comb */
- write_reg(0x00000101, CX18_WMB_CLIENT10); /* ME */
- write_reg(0x00000101, CX18_WMB_CLIENT12); /* ENC */
- write_reg(0x00000101, CX18_WMB_CLIENT13); /* PK */
- write_reg(0x00000101, CX18_WMB_CLIENT11); /* RC */
- write_reg(0x00000101, CX18_WMB_CLIENT14); /* AVO */
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT02); /* AO */
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT09); /* AI2 */
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT05); /* VIM1 */
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT06); /* AI1 */
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT07); /* 3D comb */
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT10); /* ME */
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT12); /* ENC */
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT13); /* PK */
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT11); /* RC */
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT14); /* AVO */
}
int cx18_firmware_init(struct cx18 *cx)
{
/* Allow chip to control CLKRUN */
- write_reg(0x5, CX18_DSP0_INTERRUPT_MASK);
+ cx18_write_reg(cx, 0x5, CX18_DSP0_INTERRUPT_MASK);
- write_reg(0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */
+ cx18_write_reg(cx, 0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */
cx18_msleep_timeout(1, 0);
- sw1_irq_enable(IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
- sw2_irq_enable(IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
+ cx18_sw1_irq_enable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
+ cx18_sw2_irq_enable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
/* Only if the processor is not running */
- if (read_reg(CX18_PROC_SOFT_RESET) & 8) {
+ if (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) {
int sz = load_apu_fw_direct("v4l-cx23418-apu.fw",
cx->enc_mem, cx);
- write_enc(0xE51FF004, 0);
- write_enc(0xa00000, 4); /* todo: not hardcoded */
- write_reg(0x00010000, CX18_PROC_SOFT_RESET); /* Start APU */
+ cx18_write_enc(cx, 0xE51FF004, 0);
+ cx18_write_enc(cx, 0xa00000, 4); /* todo: not hardcoded */
+ /* Start APU */
+ cx18_write_reg(cx, 0x00010000, CX18_PROC_SOFT_RESET);
cx18_msleep_timeout(500, 0);
sz = sz <= 0 ? sz : load_cpu_fw_direct("v4l-cx23418-cpu.fw",
@@ -326,9 +335,10 @@
int retries = 0;
/* start the CPU */
- write_reg(0x00080000, CX18_PROC_SOFT_RESET);
+ cx18_write_reg(cx, 0x00080000, CX18_PROC_SOFT_RESET);
while (retries++ < 50) { /* Loop for max 500mS */
- if ((read_reg(CX18_PROC_SOFT_RESET) & 1) == 0)
+ if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET)
+ & 1) == 0)
break;
cx18_msleep_timeout(10, 0);
}
@@ -342,6 +352,6 @@
return -EIO;
}
/* initialize GPIO */
- write_reg(0x14001400, 0xC78110);
+ cx18_write_reg(cx, 0x14001400, 0xC78110);
return 0;
}
diff --git a/drivers/media/video/cx18/cx18-gpio.c b/drivers/media/video/cx18/cx18-gpio.c
index 3d495db..0e56042 100644
--- a/drivers/media/video/cx18/cx18-gpio.c
+++ b/drivers/media/video/cx18/cx18-gpio.c
@@ -22,6 +22,7 @@
*/
#include "cx18-driver.h"
+#include "cx18-io.h"
#include "cx18-cards.h"
#include "cx18-gpio.h"
#include "tuner-xc2028.h"
@@ -49,11 +50,11 @@
u32 dir = cx->gpio_dir;
u32 val = cx->gpio_val;
- write_reg((dir & 0xffff) << 16, CX18_REG_GPIO_DIR1);
- write_reg(((dir & 0xffff) << 16) | (val & 0xffff),
+ cx18_write_reg(cx, (dir & 0xffff) << 16, CX18_REG_GPIO_DIR1);
+ cx18_write_reg(cx, ((dir & 0xffff) << 16) | (val & 0xffff),
CX18_REG_GPIO_OUT1);
- write_reg(dir & 0xffff0000, CX18_REG_GPIO_DIR2);
- write_reg_sync((dir & 0xffff0000) | ((val & 0xffff0000) >> 16),
+ cx18_write_reg(cx, dir & 0xffff0000, CX18_REG_GPIO_DIR2);
+ cx18_write_reg_sync(cx, (dir & 0xffff0000) | ((val & 0xffff0000) >> 16),
CX18_REG_GPIO_OUT2);
}
@@ -141,15 +142,17 @@
}
CX18_DEBUG_INFO("GPIO initial dir: %08x/%08x out: %08x/%08x\n",
- read_reg(CX18_REG_GPIO_DIR1), read_reg(CX18_REG_GPIO_DIR2),
- read_reg(CX18_REG_GPIO_OUT1), read_reg(CX18_REG_GPIO_OUT2));
+ cx18_read_reg(cx, CX18_REG_GPIO_DIR1),
+ cx18_read_reg(cx, CX18_REG_GPIO_DIR2),
+ cx18_read_reg(cx, CX18_REG_GPIO_OUT1),
+ cx18_read_reg(cx, CX18_REG_GPIO_OUT2));
gpio_write(cx);
mutex_unlock(&cx->gpio_lock);
}
/* Xceive tuner reset function */
-int cx18_reset_tuner_gpio(void *dev, int cmd, int value)
+int cx18_reset_tuner_gpio(void *dev, int component, int cmd, int value)
{
struct i2c_algo_bit_data *algo = dev;
struct cx18_i2c_algo_callback_data *cb_data = algo->data;
diff --git a/drivers/media/video/cx18/cx18-gpio.h b/drivers/media/video/cx18/cx18-gpio.h
index 22cd7dd..beb7424 100644
--- a/drivers/media/video/cx18/cx18-gpio.h
+++ b/drivers/media/video/cx18/cx18-gpio.h
@@ -23,5 +23,5 @@
void cx18_gpio_init(struct cx18 *cx);
void cx18_reset_i2c_slaves_gpio(struct cx18 *cx);
void cx18_reset_ir_gpio(void *data);
-int cx18_reset_tuner_gpio(void *dev, int cmd, int value);
+int cx18_reset_tuner_gpio(void *dev, int component, int cmd, int value);
int cx18_gpio(struct cx18 *cx, unsigned int command, void *arg);
diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c
index 6023ba3..aa09e55 100644
--- a/drivers/media/video/cx18/cx18-i2c.c
+++ b/drivers/media/video/cx18/cx18-i2c.c
@@ -22,13 +22,12 @@
*/
#include "cx18-driver.h"
+#include "cx18-io.h"
#include "cx18-cards.h"
#include "cx18-gpio.h"
#include "cx18-av-core.h"
#include "cx18-i2c.h"
-#include <media/ir-kbd-i2c.h>
-
#define CX18_REG_I2C_1_WR 0xf15000
#define CX18_REG_I2C_1_RD 0xf15008
#define CX18_REG_I2C_2_WR 0xf25100
@@ -158,12 +157,12 @@
struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;
u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR;
- u32 r = read_reg(addr);
+ u32 r = cx18_read_reg(cx, addr);
if (state)
- write_reg_sync(r | SETSCL_BIT, addr);
+ cx18_write_reg_sync(cx, r | SETSCL_BIT, addr);
else
- write_reg_sync(r & ~SETSCL_BIT, addr);
+ cx18_write_reg_sync(cx, r & ~SETSCL_BIT, addr);
}
static void cx18_setsda(void *data, int state)
@@ -171,12 +170,12 @@
struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;
u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR;
- u32 r = read_reg(addr);
+ u32 r = cx18_read_reg(cx, addr);
if (state)
- write_reg_sync(r | SETSDL_BIT, addr);
+ cx18_write_reg_sync(cx, r | SETSDL_BIT, addr);
else
- write_reg_sync(r & ~SETSDL_BIT, addr);
+ cx18_write_reg_sync(cx, r & ~SETSDL_BIT, addr);
}
static int cx18_getscl(void *data)
@@ -185,7 +184,7 @@
int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;
u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD;
- return read_reg(addr) & GETSCL_BIT;
+ return cx18_read_reg(cx, addr) & GETSCL_BIT;
}
static int cx18_getsda(void *data)
@@ -194,7 +193,7 @@
int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;
u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD;
- return read_reg(addr) & GETSDL_BIT;
+ return cx18_read_reg(cx, addr) & GETSDL_BIT;
}
/* template for i2c-bit-algo */
@@ -394,29 +393,33 @@
cx->i2c_adap[i].dev.parent = &cx->dev->dev;
}
- if (read_reg(CX18_REG_I2C_2_WR) != 0x0003c02f) {
+ if (cx18_read_reg(cx, CX18_REG_I2C_2_WR) != 0x0003c02f) {
/* Reset/Unreset I2C hardware block */
- write_reg(0x10000000, 0xc71004); /* Clock select 220MHz */
- write_reg_sync(0x10001000, 0xc71024); /* Clock Enable */
+ /* Clock select 220MHz */
+ cx18_write_reg(cx, 0x10000000, 0xc71004);
+ /* Clock Enable */
+ cx18_write_reg_sync(cx, 0x10001000, 0xc71024);
}
/* courtesy of Steven Toth <stoth@hauppauge.com> */
- write_reg_sync(0x00c00000, 0xc7001c);
+ cx18_write_reg_sync(cx, 0x00c00000, 0xc7001c);
mdelay(10);
- write_reg_sync(0x00c000c0, 0xc7001c);
+ cx18_write_reg_sync(cx, 0x00c000c0, 0xc7001c);
mdelay(10);
- write_reg_sync(0x00c00000, 0xc7001c);
+ cx18_write_reg_sync(cx, 0x00c00000, 0xc7001c);
mdelay(10);
- write_reg_sync(0x00c00000, 0xc730c8); /* Set to edge-triggered intrs. */
- write_reg_sync(0x00c00000, 0xc730c4); /* Clear any stale intrs */
+ /* Set to edge-triggered intrs. */
+ cx18_write_reg_sync(cx, 0x00c00000, 0xc730c8);
+ /* Clear any stale intrs */
+ cx18_write_reg_sync(cx, 0x00c00000, 0xc730c4);
/* Hw I2C1 Clock Freq ~100kHz */
- write_reg_sync(0x00021c0f & ~4, CX18_REG_I2C_1_WR);
+ cx18_write_reg_sync(cx, 0x00021c0f & ~4, CX18_REG_I2C_1_WR);
cx18_setscl(&cx->i2c_algo_cb_data[0], 1);
cx18_setsda(&cx->i2c_algo_cb_data[0], 1);
/* Hw I2C2 Clock Freq ~100kHz */
- write_reg_sync(0x00021c0f & ~4, CX18_REG_I2C_2_WR);
+ cx18_write_reg_sync(cx, 0x00021c0f & ~4, CX18_REG_I2C_2_WR);
cx18_setscl(&cx->i2c_algo_cb_data[1], 1);
cx18_setsda(&cx->i2c_algo_cb_data[1], 1);
@@ -430,8 +433,10 @@
{
int i;
CX18_DEBUG_I2C("i2c exit\n");
- write_reg(read_reg(CX18_REG_I2C_1_WR) | 4, CX18_REG_I2C_1_WR);
- write_reg(read_reg(CX18_REG_I2C_2_WR) | 4, CX18_REG_I2C_2_WR);
+ cx18_write_reg(cx, cx18_read_reg(cx, CX18_REG_I2C_1_WR) | 4,
+ CX18_REG_I2C_1_WR);
+ cx18_write_reg(cx, cx18_read_reg(cx, CX18_REG_I2C_2_WR) | 4,
+ CX18_REG_I2C_2_WR);
for (i = 0; i < 2; i++) {
i2c_del_adapter(&cx->i2c_adap[i]);
diff --git a/drivers/media/video/cx18/cx18-io.c b/drivers/media/video/cx18/cx18-io.c
new file mode 100644
index 0000000..700ab94
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-io.c
@@ -0,0 +1,254 @@
+/*
+ * cx18 driver PCI memory mapped IO access routines
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
+ *
+ * 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
+ */
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-irq.h"
+
+void cx18_log_statistics(struct cx18 *cx)
+{
+ int i;
+
+ if (!(cx18_debug & CX18_DBGFLG_INFO))
+ return;
+
+ for (i = 0; i <= CX18_MAX_MMIO_RETRIES; i++)
+ CX18_DEBUG_INFO("retried_write[%d] = %d\n", i,
+ atomic_read(&cx->mmio_stats.retried_write[i]));
+ for (i = 0; i <= CX18_MAX_MMIO_RETRIES; i++)
+ CX18_DEBUG_INFO("retried_read[%d] = %d\n", i,
+ atomic_read(&cx->mmio_stats.retried_read[i]));
+ return;
+}
+
+void cx18_raw_writel_retry(struct cx18 *cx, u32 val, void __iomem *addr)
+{
+ int i;
+ for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
+ cx18_raw_writel_noretry(cx, val, addr);
+ if (val == cx18_raw_readl_noretry(cx, addr))
+ break;
+ }
+ cx18_log_write_retries(cx, i, addr);
+}
+
+u32 cx18_raw_readl_retry(struct cx18 *cx, const void __iomem *addr)
+{
+ int i;
+ u32 val;
+ for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
+ val = cx18_raw_readl_noretry(cx, addr);
+ if (val != 0xffffffff) /* PCI bus read error */
+ break;
+ }
+ cx18_log_read_retries(cx, i, addr);
+ return val;
+}
+
+u16 cx18_raw_readw_retry(struct cx18 *cx, const void __iomem *addr)
+{
+ int i;
+ u16 val;
+ for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
+ val = cx18_raw_readw_noretry(cx, addr);
+ if (val != 0xffff) /* PCI bus read error */
+ break;
+ }
+ cx18_log_read_retries(cx, i, addr);
+ return val;
+}
+
+void cx18_writel_retry(struct cx18 *cx, u32 val, void __iomem *addr)
+{
+ int i;
+ for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
+ cx18_writel_noretry(cx, val, addr);
+ if (val == cx18_readl_noretry(cx, addr))
+ break;
+ }
+ cx18_log_write_retries(cx, i, addr);
+}
+
+void cx18_writew_retry(struct cx18 *cx, u16 val, void __iomem *addr)
+{
+ int i;
+ for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
+ cx18_writew_noretry(cx, val, addr);
+ if (val == cx18_readw_noretry(cx, addr))
+ break;
+ }
+ cx18_log_write_retries(cx, i, addr);
+}
+
+void cx18_writeb_retry(struct cx18 *cx, u8 val, void __iomem *addr)
+{
+ int i;
+ for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
+ cx18_writeb_noretry(cx, val, addr);
+ if (val == cx18_readb_noretry(cx, addr))
+ break;
+ }
+ cx18_log_write_retries(cx, i, addr);
+}
+
+u32 cx18_readl_retry(struct cx18 *cx, const void __iomem *addr)
+{
+ int i;
+ u32 val;
+ for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
+ val = cx18_readl_noretry(cx, addr);
+ if (val != 0xffffffff) /* PCI bus read error */
+ break;
+ }
+ cx18_log_read_retries(cx, i, addr);
+ return val;
+}
+
+u16 cx18_readw_retry(struct cx18 *cx, const void __iomem *addr)
+{
+ int i;
+ u16 val;
+ for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
+ val = cx18_readw_noretry(cx, addr);
+ if (val != 0xffff) /* PCI bus read error */
+ break;
+ }
+ cx18_log_read_retries(cx, i, addr);
+ return val;
+}
+
+u8 cx18_readb_retry(struct cx18 *cx, const void __iomem *addr)
+{
+ int i;
+ u8 val;
+ for (i = 0; i < CX18_MAX_MMIO_RETRIES; i++) {
+ val = cx18_readb_noretry(cx, addr);
+ if (val != 0xff) /* PCI bus read error */
+ break;
+ }
+ cx18_log_read_retries(cx, i, addr);
+ return val;
+}
+
+void cx18_memcpy_fromio(struct cx18 *cx, void *to,
+ const void __iomem *from, unsigned int len)
+{
+ const u8 __iomem *src = from;
+ u8 *dst = to;
+
+ /* Align reads on the CX23418's addresses */
+ if ((len > 0) && ((unsigned long) src & 1)) {
+ *dst = cx18_readb(cx, src);
+ len--;
+ dst++;
+ src++;
+ }
+ if ((len > 1) && ((unsigned long) src & 2)) {
+ *((u16 *)dst) = cx18_raw_readw(cx, src);
+ len -= 2;
+ dst += 2;
+ src += 2;
+ }
+ while (len > 3) {
+ *((u32 *)dst) = cx18_raw_readl(cx, src);
+ len -= 4;
+ dst += 4;
+ src += 4;
+ }
+ if (len > 1) {
+ *((u16 *)dst) = cx18_raw_readw(cx, src);
+ len -= 2;
+ dst += 2;
+ src += 2;
+ }
+ if (len > 0)
+ *dst = cx18_readb(cx, src);
+}
+
+void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count)
+{
+ u8 __iomem *dst = addr;
+ u16 val2 = val | (val << 8);
+ u32 val4 = val2 | (val2 << 16);
+
+ /* Align writes on the CX23418's addresses */
+ if ((count > 0) && ((unsigned long)dst & 1)) {
+ cx18_writeb(cx, (u8) val, dst);
+ count--;
+ dst++;
+ }
+ if ((count > 1) && ((unsigned long)dst & 2)) {
+ cx18_writew(cx, val2, dst);
+ count -= 2;
+ dst += 2;
+ }
+ while (count > 3) {
+ cx18_writel(cx, val4, dst);
+ count -= 4;
+ dst += 4;
+ }
+ if (count > 1) {
+ cx18_writew(cx, val2, dst);
+ count -= 2;
+ dst += 2;
+ }
+ if (count > 0)
+ cx18_writeb(cx, (u8) val, dst);
+}
+
+void cx18_sw1_irq_enable(struct cx18 *cx, u32 val)
+{
+ u32 r;
+ cx18_write_reg(cx, val, SW1_INT_STATUS);
+ r = cx18_read_reg(cx, SW1_INT_ENABLE_PCI);
+ cx18_write_reg(cx, r | val, SW1_INT_ENABLE_PCI);
+}
+
+void cx18_sw1_irq_disable(struct cx18 *cx, u32 val)
+{
+ u32 r;
+ r = cx18_read_reg(cx, SW1_INT_ENABLE_PCI);
+ cx18_write_reg(cx, r & ~val, SW1_INT_ENABLE_PCI);
+}
+
+void cx18_sw2_irq_enable(struct cx18 *cx, u32 val)
+{
+ u32 r;
+ cx18_write_reg(cx, val, SW2_INT_STATUS);
+ r = cx18_read_reg(cx, SW2_INT_ENABLE_PCI);
+ cx18_write_reg(cx, r | val, SW2_INT_ENABLE_PCI);
+}
+
+void cx18_sw2_irq_disable(struct cx18 *cx, u32 val)
+{
+ u32 r;
+ r = cx18_read_reg(cx, SW2_INT_ENABLE_PCI);
+ cx18_write_reg(cx, r & ~val, SW2_INT_ENABLE_PCI);
+}
+
+void cx18_setup_page(struct cx18 *cx, u32 addr)
+{
+ u32 val;
+ val = cx18_read_reg(cx, 0xD000F8);
+ val = (val & ~0x1f00) | ((addr >> 17) & 0x1f00);
+ cx18_write_reg(cx, val, 0xD000F8);
+}
diff --git a/drivers/media/video/cx18/cx18-io.h b/drivers/media/video/cx18/cx18-io.h
new file mode 100644
index 0000000..197d4fb
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-io.h
@@ -0,0 +1,378 @@
+/*
+ * cx18 driver PCI memory mapped IO access routines
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@radix.net>
+ *
+ * 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 CX18_IO_H
+#define CX18_IO_H
+
+#include "cx18-driver.h"
+
+static inline void cx18_io_delay(struct cx18 *cx)
+{
+ if (cx->options.mmio_ndelay)
+ ndelay(cx->options.mmio_ndelay);
+}
+
+/*
+ * Readback and retry of MMIO access for reliability:
+ * The concept was suggested by Steve Toth <stoth@linuxtv.org>.
+ * The implmentation is the fault of Andy Walls <awalls@radix.net>.
+ */
+
+/* Statistics gathering */
+static inline
+void cx18_log_write_retries(struct cx18 *cx, int i, const void *addr)
+{
+ if (i > CX18_MAX_MMIO_RETRIES)
+ i = CX18_MAX_MMIO_RETRIES;
+ atomic_inc(&cx->mmio_stats.retried_write[i]);
+ return;
+}
+
+static inline
+void cx18_log_read_retries(struct cx18 *cx, int i, const void *addr)
+{
+ if (i > CX18_MAX_MMIO_RETRIES)
+ i = CX18_MAX_MMIO_RETRIES;
+ atomic_inc(&cx->mmio_stats.retried_read[i]);
+ return;
+}
+
+void cx18_log_statistics(struct cx18 *cx);
+
+/* Non byteswapping memory mapped IO */
+static inline
+void cx18_raw_writel_noretry(struct cx18 *cx, u32 val, void __iomem *addr)
+{
+ __raw_writel(val, addr);
+ cx18_io_delay(cx);
+}
+
+void cx18_raw_writel_retry(struct cx18 *cx, u32 val, void __iomem *addr);
+
+static inline void cx18_raw_writel(struct cx18 *cx, u32 val, void __iomem *addr)
+{
+ if (cx18_retry_mmio)
+ cx18_raw_writel_retry(cx, val, addr);
+ else
+ cx18_raw_writel_noretry(cx, val, addr);
+}
+
+
+static inline
+u32 cx18_raw_readl_noretry(struct cx18 *cx, const void __iomem *addr)
+{
+ u32 ret = __raw_readl(addr);
+ cx18_io_delay(cx);
+ return ret;
+}
+
+u32 cx18_raw_readl_retry(struct cx18 *cx, const void __iomem *addr);
+
+static inline u32 cx18_raw_readl(struct cx18 *cx, const void __iomem *addr)
+{
+ if (cx18_retry_mmio)
+ return cx18_raw_readl_retry(cx, addr);
+
+ return cx18_raw_readl_noretry(cx, addr);
+}
+
+
+static inline
+u16 cx18_raw_readw_noretry(struct cx18 *cx, const void __iomem *addr)
+{
+ u16 ret = __raw_readw(addr);
+ cx18_io_delay(cx);
+ return ret;
+}
+
+u16 cx18_raw_readw_retry(struct cx18 *cx, const void __iomem *addr);
+
+static inline u16 cx18_raw_readw(struct cx18 *cx, const void __iomem *addr)
+{
+ if (cx18_retry_mmio)
+ return cx18_raw_readw_retry(cx, addr);
+
+ return cx18_raw_readw_noretry(cx, addr);
+}
+
+
+/* Normal memory mapped IO */
+static inline
+void cx18_writel_noretry(struct cx18 *cx, u32 val, void __iomem *addr)
+{
+ writel(val, addr);
+ cx18_io_delay(cx);
+}
+
+void cx18_writel_retry(struct cx18 *cx, u32 val, void __iomem *addr);
+
+static inline void cx18_writel(struct cx18 *cx, u32 val, void __iomem *addr)
+{
+ if (cx18_retry_mmio)
+ cx18_writel_retry(cx, val, addr);
+ else
+ cx18_writel_noretry(cx, val, addr);
+}
+
+
+static inline
+void cx18_writew_noretry(struct cx18 *cx, u16 val, void __iomem *addr)
+{
+ writew(val, addr);
+ cx18_io_delay(cx);
+}
+
+void cx18_writew_retry(struct cx18 *cx, u16 val, void __iomem *addr);
+
+static inline void cx18_writew(struct cx18 *cx, u16 val, void __iomem *addr)
+{
+ if (cx18_retry_mmio)
+ cx18_writew_retry(cx, val, addr);
+ else
+ cx18_writew_noretry(cx, val, addr);
+}
+
+
+static inline
+void cx18_writeb_noretry(struct cx18 *cx, u8 val, void __iomem *addr)
+{
+ writeb(val, addr);
+ cx18_io_delay(cx);
+}
+
+void cx18_writeb_retry(struct cx18 *cx, u8 val, void __iomem *addr);
+
+static inline void cx18_writeb(struct cx18 *cx, u8 val, void __iomem *addr)
+{
+ if (cx18_retry_mmio)
+ cx18_writeb_retry(cx, val, addr);
+ else
+ cx18_writeb_noretry(cx, val, addr);
+}
+
+
+static inline u32 cx18_readl_noretry(struct cx18 *cx, const void __iomem *addr)
+{
+ u32 ret = readl(addr);
+ cx18_io_delay(cx);
+ return ret;
+}
+
+u32 cx18_readl_retry(struct cx18 *cx, const void __iomem *addr);
+
+static inline u32 cx18_readl(struct cx18 *cx, const void __iomem *addr)
+{
+ if (cx18_retry_mmio)
+ return cx18_readl_retry(cx, addr);
+
+ return cx18_readl_noretry(cx, addr);
+}
+
+
+static inline u16 cx18_readw_noretry(struct cx18 *cx, const void __iomem *addr)
+{
+ u16 ret = readw(addr);
+ cx18_io_delay(cx);
+ return ret;
+}
+
+u16 cx18_readw_retry(struct cx18 *cx, const void __iomem *addr);
+
+static inline u16 cx18_readw(struct cx18 *cx, const void __iomem *addr)
+{
+ if (cx18_retry_mmio)
+ return cx18_readw_retry(cx, addr);
+
+ return cx18_readw_noretry(cx, addr);
+}
+
+
+static inline u8 cx18_readb_noretry(struct cx18 *cx, const void __iomem *addr)
+{
+ u8 ret = readb(addr);
+ cx18_io_delay(cx);
+ return ret;
+}
+
+u8 cx18_readb_retry(struct cx18 *cx, const void __iomem *addr);
+
+static inline u8 cx18_readb(struct cx18 *cx, const void __iomem *addr)
+{
+ if (cx18_retry_mmio)
+ return cx18_readb_retry(cx, addr);
+
+ return cx18_readb_noretry(cx, addr);
+}
+
+
+static inline
+u32 cx18_write_sync_noretry(struct cx18 *cx, u32 val, void __iomem *addr)
+{
+ cx18_writel_noretry(cx, val, addr);
+ return cx18_readl_noretry(cx, addr);
+}
+
+static inline
+u32 cx18_write_sync_retry(struct cx18 *cx, u32 val, void __iomem *addr)
+{
+ cx18_writel_retry(cx, val, addr);
+ return cx18_readl_retry(cx, addr);
+}
+
+static inline u32 cx18_write_sync(struct cx18 *cx, u32 val, void __iomem *addr)
+{
+ if (cx18_retry_mmio)
+ return cx18_write_sync_retry(cx, val, addr);
+
+ return cx18_write_sync_noretry(cx, val, addr);
+}
+
+
+void cx18_memcpy_fromio(struct cx18 *cx, void *to,
+ const void __iomem *from, unsigned int len);
+void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count);
+
+
+/* Access "register" region of CX23418 memory mapped I/O */
+static inline void cx18_write_reg_noretry(struct cx18 *cx, u32 val, u32 reg)
+{
+ cx18_writel_noretry(cx, val, cx->reg_mem + reg);
+}
+
+static inline void cx18_write_reg_retry(struct cx18 *cx, u32 val, u32 reg)
+{
+ cx18_writel_retry(cx, val, cx->reg_mem + reg);
+}
+
+static inline void cx18_write_reg(struct cx18 *cx, u32 val, u32 reg)
+{
+ if (cx18_retry_mmio)
+ cx18_write_reg_retry(cx, val, reg);
+ else
+ cx18_write_reg_noretry(cx, val, reg);
+}
+
+
+static inline u32 cx18_read_reg_noretry(struct cx18 *cx, u32 reg)
+{
+ return cx18_readl_noretry(cx, cx->reg_mem + reg);
+}
+
+static inline u32 cx18_read_reg_retry(struct cx18 *cx, u32 reg)
+{
+ return cx18_readl_retry(cx, cx->reg_mem + reg);
+}
+
+static inline u32 cx18_read_reg(struct cx18 *cx, u32 reg)
+{
+ if (cx18_retry_mmio)
+ return cx18_read_reg_retry(cx, reg);
+
+ return cx18_read_reg_noretry(cx, reg);
+}
+
+
+static inline u32 cx18_write_reg_sync_noretry(struct cx18 *cx, u32 val, u32 reg)
+{
+ return cx18_write_sync_noretry(cx, val, cx->reg_mem + reg);
+}
+
+static inline u32 cx18_write_reg_sync_retry(struct cx18 *cx, u32 val, u32 reg)
+{
+ return cx18_write_sync_retry(cx, val, cx->reg_mem + reg);
+}
+
+static inline u32 cx18_write_reg_sync(struct cx18 *cx, u32 val, u32 reg)
+{
+ if (cx18_retry_mmio)
+ return cx18_write_reg_sync_retry(cx, val, reg);
+
+ return cx18_write_reg_sync_noretry(cx, val, reg);
+}
+
+
+/* Access "encoder memory" region of CX23418 memory mapped I/O */
+static inline void cx18_write_enc_noretry(struct cx18 *cx, u32 val, u32 addr)
+{
+ cx18_writel_noretry(cx, val, cx->enc_mem + addr);
+}
+
+static inline void cx18_write_enc_retry(struct cx18 *cx, u32 val, u32 addr)
+{
+ cx18_writel_retry(cx, val, cx->enc_mem + addr);
+}
+
+static inline void cx18_write_enc(struct cx18 *cx, u32 val, u32 addr)
+{
+ if (cx18_retry_mmio)
+ cx18_write_enc_retry(cx, val, addr);
+ else
+ cx18_write_enc_noretry(cx, val, addr);
+}
+
+
+static inline u32 cx18_read_enc_noretry(struct cx18 *cx, u32 addr)
+{
+ return cx18_readl_noretry(cx, cx->enc_mem + addr);
+}
+
+static inline u32 cx18_read_enc_retry(struct cx18 *cx, u32 addr)
+{
+ return cx18_readl_retry(cx, cx->enc_mem + addr);
+}
+
+static inline u32 cx18_read_enc(struct cx18 *cx, u32 addr)
+{
+ if (cx18_retry_mmio)
+ return cx18_read_enc_retry(cx, addr);
+
+ return cx18_read_enc_noretry(cx, addr);
+}
+
+static inline
+u32 cx18_write_enc_sync_noretry(struct cx18 *cx, u32 val, u32 addr)
+{
+ return cx18_write_sync_noretry(cx, val, cx->enc_mem + addr);
+}
+
+static inline
+u32 cx18_write_enc_sync_retry(struct cx18 *cx, u32 val, u32 addr)
+{
+ return cx18_write_sync_retry(cx, val, cx->enc_mem + addr);
+}
+
+static inline
+u32 cx18_write_enc_sync(struct cx18 *cx, u32 val, u32 addr)
+{
+ if (cx18_retry_mmio)
+ return cx18_write_enc_sync_retry(cx, val, addr);
+
+ return cx18_write_enc_sync_noretry(cx, val, addr);
+}
+
+void cx18_sw1_irq_enable(struct cx18 *cx, u32 val);
+void cx18_sw1_irq_disable(struct cx18 *cx, u32 val);
+void cx18_sw2_irq_enable(struct cx18 *cx, u32 val);
+void cx18_sw2_irq_disable(struct cx18 *cx, u32 val);
+void cx18_setup_page(struct cx18 *cx, u32 addr);
+
+#endif /* CX18_IO_H */
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c
index a7f8396..f0ca50f 100644
--- a/drivers/media/video/cx18/cx18-ioctl.c
+++ b/drivers/media/video/cx18/cx18-ioctl.c
@@ -22,6 +22,7 @@
*/
#include "cx18-driver.h"
+#include "cx18-io.h"
#include "cx18-version.h"
#include "cx18-mailbox.h"
#include "cx18-i2c.h"
@@ -170,7 +171,6 @@
{
struct cx18_open_id *id = fh;
struct cx18 *cx = id->cx;
-
int w = fmt->fmt.pix.width;
int h = fmt->fmt.pix.height;
@@ -202,8 +202,7 @@
struct cx18_open_id *id = fh;
struct cx18 *cx = id->cx;
int ret;
- int w = fmt->fmt.pix.width;
- int h = fmt->fmt.pix.height;
+ int w, h;
ret = v4l2_prio_check(&cx->prio, &id->prio);
if (ret)
@@ -212,6 +211,8 @@
ret = cx18_try_fmt_vid_cap(file, fh, fmt);
if (ret)
return ret;
+ w = fmt->fmt.pix.width;
+ h = fmt->fmt.pix.height;
if (cx->params.width == w && cx->params.height == h)
return 0;
@@ -286,9 +287,9 @@
spin_lock_irqsave(&cx18_cards_lock, flags);
if (cmd == VIDIOC_DBG_G_REGISTER)
- regs->val = read_enc(regs->reg);
+ regs->val = cx18_read_enc(cx, regs->reg);
else
- write_enc(regs->val, regs->reg);
+ cx18_write_enc(cx, regs->val, regs->reg);
spin_unlock_irqrestore(&cx18_cards_lock, flags);
return 0;
}
@@ -345,7 +346,7 @@
strlcpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver));
strlcpy(vcap->card, cx->card_name, sizeof(vcap->card));
- strlcpy(vcap->bus_info, pci_name(cx->dev), sizeof(vcap->bus_info));
+ snprintf(vcap->bus_info, sizeof(vcap->bus_info), "PCI:%s", pci_name(cx->dev));
vcap->version = CX18_DRIVER_VERSION; /* version */
vcap->capabilities = cx->v4l2_cap; /* capabilities */
return 0;
@@ -622,6 +623,7 @@
{
struct cx18_open_id *id = fh;
struct cx18 *cx = id->cx;
+ u32 h;
switch (enc->cmd) {
case V4L2_ENC_CMD_START:
@@ -643,8 +645,14 @@
return -EPERM;
if (test_and_set_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
return 0;
+ h = cx18_find_handle(cx);
+ if (h == CX18_INVALID_TASK_HANDLE) {
+ CX18_ERR("Can't find valid task handle for "
+ "V4L2_ENC_CMD_PAUSE\n");
+ return -EBADFD;
+ }
cx18_mute(cx);
- cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, cx18_find_handle(cx));
+ cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, h);
break;
case V4L2_ENC_CMD_RESUME:
@@ -654,7 +662,13 @@
return -EPERM;
if (!test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
return 0;
- cx18_vapi(cx, CX18_CPU_CAPTURE_RESUME, 1, cx18_find_handle(cx));
+ h = cx18_find_handle(cx);
+ if (h == CX18_INVALID_TASK_HANDLE) {
+ CX18_ERR("Can't find valid task handle for "
+ "V4L2_ENC_CMD_RESUME\n");
+ return -EBADFD;
+ }
+ cx18_vapi(cx, CX18_CPU_CAPTURE_RESUME, 1, h);
cx18_unmute(cx);
break;
@@ -731,12 +745,14 @@
continue;
CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n",
s->name, s->s_flags,
- (s->buffers - s->q_free.buffers) * 100 / s->buffers,
+ (s->buffers - atomic_read(&s->q_free.buffers))
+ * 100 / s->buffers,
(s->buffers * s->buf_size) / 1024, s->buffers);
}
CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n",
(long long)cx->mpg_data_received,
(long long)cx->vbi_data_inserted);
+ cx18_log_statistics(cx);
CX18_INFO("================== END STATUS CARD #%d ==================\n", cx->num);
return 0;
}
diff --git a/drivers/media/video/cx18/cx18-irq.c b/drivers/media/video/cx18/cx18-irq.c
index ab21831..360330f 100644
--- a/drivers/media/video/cx18/cx18-irq.c
+++ b/drivers/media/video/cx18/cx18-irq.c
@@ -20,6 +20,7 @@
*/
#include "cx18-driver.h"
+#include "cx18-io.h"
#include "cx18-firmware.h"
#include "cx18-fileops.h"
#include "cx18-queue.h"
@@ -48,8 +49,8 @@
break;
}
if (i == CX18_MAX_STREAMS) {
- CX18_WARN("DMA done for unknown handle %d for stream %s\n",
- handle, s->name);
+ CX18_WARN("Got DMA done notification for unknown/inactive"
+ " handle %d\n", handle);
mb->error = CXERR_NOT_OPEN;
mb->cmd = 0;
cx18_mb_ack(cx, mb);
@@ -60,8 +61,8 @@
if (mb->args[2] != 1)
CX18_WARN("Ack struct = %d for %s\n",
mb->args[2], s->name);
- id = read_enc(off);
- buf = cx18_queue_get_buf_irq(s, id, read_enc(off + 4));
+ id = cx18_read_enc(cx, off);
+ buf = cx18_queue_get_buf_irq(s, id, cx18_read_enc(cx, off + 4));
CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id);
if (buf) {
cx18_buf_sync_for_cpu(s, buf);
@@ -81,7 +82,7 @@
set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags);
} else {
CX18_WARN("Could not find buf %d for stream %s\n",
- read_enc(off), s->name);
+ cx18_read_enc(cx, off), s->name);
}
mb->error = 0;
mb->cmd = 0;
@@ -97,8 +98,8 @@
char *p;
if (mb->args[1]) {
- setup_page(mb->args[1]);
- memcpy_fromio(str, cx->enc_mem + mb->args[1], 252);
+ cx18_setup_page(cx, mb->args[1]);
+ cx18_memcpy_fromio(cx, str, cx->enc_mem + mb->args[1], 252);
str[252] = 0;
}
cx18_mb_ack(cx, mb);
@@ -113,7 +114,7 @@
struct cx18_mailbox mb;
if (sw1 & IRQ_CPU_TO_EPU) {
- memcpy_fromio(&mb, &cx->scb->cpu2epu_mb, sizeof(mb));
+ cx18_memcpy_fromio(cx, &mb, &cx->scb->cpu2epu_mb, sizeof(mb));
mb.error = 0;
switch (mb.cmd) {
@@ -141,16 +142,16 @@
spin_lock(&cx->dma_reg_lock);
- hw2_mask = read_reg(HW2_INT_MASK5_PCI);
- hw2 = read_reg(HW2_INT_CLR_STATUS) & hw2_mask;
- sw2_mask = read_reg(SW2_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU_ACK;
- sw2 = read_reg(SW2_INT_STATUS) & sw2_mask;
- sw1_mask = read_reg(SW1_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU;
- sw1 = read_reg(SW1_INT_STATUS) & sw1_mask;
+ hw2_mask = cx18_read_reg(cx, HW2_INT_MASK5_PCI);
+ hw2 = cx18_read_reg(cx, HW2_INT_CLR_STATUS) & hw2_mask;
+ sw2_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU_ACK;
+ sw2 = cx18_read_reg(cx, SW2_INT_STATUS) & sw2_mask;
+ sw1_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU;
+ sw1 = cx18_read_reg(cx, SW1_INT_STATUS) & sw1_mask;
- write_reg(sw2&sw2_mask, SW2_INT_STATUS);
- write_reg(sw1&sw1_mask, SW1_INT_STATUS);
- write_reg(hw2&hw2_mask, HW2_INT_CLR_STATUS);
+ cx18_write_reg(cx, sw2&sw2_mask, SW2_INT_STATUS);
+ cx18_write_reg(cx, sw1&sw1_mask, SW1_INT_STATUS);
+ cx18_write_reg(cx, hw2&hw2_mask, HW2_INT_CLR_STATUS);
if (sw1 || sw2 || hw2)
CX18_DEBUG_HI_IRQ("SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2);
@@ -161,15 +162,15 @@
*/
if (sw2) {
- if (sw2 & (readl(&cx->scb->cpu2hpu_irq_ack) |
- readl(&cx->scb->cpu2epu_irq_ack)))
+ if (sw2 & (cx18_readl(cx, &cx->scb->cpu2hpu_irq_ack) |
+ cx18_readl(cx, &cx->scb->cpu2epu_irq_ack)))
wake_up(&cx->mb_cpu_waitq);
- if (sw2 & (readl(&cx->scb->apu2hpu_irq_ack) |
- readl(&cx->scb->apu2epu_irq_ack)))
+ if (sw2 & (cx18_readl(cx, &cx->scb->apu2hpu_irq_ack) |
+ cx18_readl(cx, &cx->scb->apu2epu_irq_ack)))
wake_up(&cx->mb_apu_waitq);
- if (sw2 & readl(&cx->scb->epu2hpu_irq_ack))
+ if (sw2 & cx18_readl(cx, &cx->scb->epu2hpu_irq_ack))
wake_up(&cx->mb_epu_waitq);
- if (sw2 & readl(&cx->scb->hpu2epu_irq_ack))
+ if (sw2 & cx18_readl(cx, &cx->scb->hpu2epu_irq_ack))
wake_up(&cx->mb_hpu_waitq);
}
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c
index 9317751..9d18dd2 100644
--- a/drivers/media/video/cx18/cx18-mailbox.c
+++ b/drivers/media/video/cx18/cx18-mailbox.c
@@ -22,6 +22,7 @@
#include <stdarg.h>
#include "cx18-driver.h"
+#include "cx18-io.h"
#include "cx18-scb.h"
#include "cx18-irq.h"
#include "cx18-mailbox.h"
@@ -82,6 +83,7 @@
API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0),
API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST),
API_ENTRY(CPU, CX18_APU_RESETAI, API_FAST),
+ API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL, 0),
API_ENTRY(0, 0, 0),
};
@@ -105,20 +107,20 @@
switch (rpu) {
case APU:
mb = &cx->scb->epu2apu_mb;
- *state = readl(&cx->scb->apu_state);
- *irq = readl(&cx->scb->epu2apu_irq);
+ *state = cx18_readl(cx, &cx->scb->apu_state);
+ *irq = cx18_readl(cx, &cx->scb->epu2apu_irq);
break;
case CPU:
mb = &cx->scb->epu2cpu_mb;
- *state = readl(&cx->scb->cpu_state);
- *irq = readl(&cx->scb->epu2cpu_irq);
+ *state = cx18_readl(cx, &cx->scb->cpu_state);
+ *irq = cx18_readl(cx, &cx->scb->epu2cpu_irq);
break;
case HPU:
mb = &cx->scb->epu2hpu_mb;
- *state = readl(&cx->scb->hpu_state);
- *irq = readl(&cx->scb->epu2hpu_irq);
+ *state = cx18_readl(cx, &cx->scb->hpu_state);
+ *irq = cx18_readl(cx, &cx->scb->epu2hpu_irq);
break;
}
@@ -126,8 +128,8 @@
return mb;
do {
- *req = readl(&mb->request);
- ack = readl(&mb->ack);
+ *req = cx18_readl(cx, &mb->request);
+ ack = cx18_readl(cx, &mb->ack);
wait_count++;
} while (*req != ack && wait_count < 600);
@@ -172,9 +174,9 @@
return -EINVAL;
}
- setup_page(SCB_OFFSET);
- write_sync(mb->request, &ack_mb->ack);
- write_reg(ack_irq, SW2_INT_SET);
+ cx18_setup_page(cx, SCB_OFFSET);
+ cx18_write_sync(cx, mb->request, &ack_mb->ack);
+ cx18_write_reg(cx, ack_irq, SW2_INT_SET);
return 0;
}
@@ -199,7 +201,7 @@
CX18_DEBUG_HI_API("%s\n", info->name);
else
CX18_DEBUG_API("%s\n", info->name);
- setup_page(SCB_OFFSET);
+ cx18_setup_page(cx, SCB_OFFSET);
mb = cx18_mb_is_complete(cx, info->rpu, &state, &irq, &req);
if (mb == NULL) {
@@ -208,11 +210,11 @@
}
oldreq = req - 1;
- writel(cmd, &mb->cmd);
+ cx18_writel(cx, cmd, &mb->cmd);
for (i = 0; i < args; i++)
- writel(data[i], &mb->args[i]);
- writel(0, &mb->error);
- writel(req, &mb->request);
+ cx18_writel(cx, data[i], &mb->args[i]);
+ cx18_writel(cx, 0, &mb->error);
+ cx18_writel(cx, req, &mb->request);
switch (info->rpu) {
case APU: waitq = &cx->mb_apu_waitq; break;
@@ -223,9 +225,10 @@
}
if (info->flags & API_FAST)
timeout /= 2;
- write_reg(irq, SW1_INT_SET);
+ cx18_write_reg(cx, irq, SW1_INT_SET);
- while (!sig && readl(&mb->ack) != readl(&mb->request) && cnt < 660) {
+ while (!sig && cx18_readl(cx, &mb->ack) != cx18_readl(cx, &mb->request)
+ && cnt < 660) {
if (cnt > 200 && !in_atomic())
sig = cx18_msleep_timeout(10, 1);
cnt++;
@@ -233,13 +236,13 @@
if (sig)
return -EINTR;
if (cnt == 660) {
- writel(oldreq, &mb->request);
+ cx18_writel(cx, oldreq, &mb->request);
CX18_ERR("mb %s failed\n", info->name);
return -EINVAL;
}
for (i = 0; i < MAX_MB_ARGUMENTS; i++)
- data[i] = readl(&mb->args[i]);
- err = readl(&mb->error);
+ data[i] = cx18_readl(cx, &mb->args[i]);
+ err = cx18_readl(cx, &mb->error);
if (!in_atomic() && (info->flags & API_SLOW))
cx18_msleep_timeout(300, 0);
if (err)
diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c
index dbe792a..a33ba04 100644
--- a/drivers/media/video/cx18/cx18-queue.c
+++ b/drivers/media/video/cx18/cx18-queue.c
@@ -37,8 +37,7 @@
void cx18_queue_init(struct cx18_queue *q)
{
INIT_LIST_HEAD(&q->list);
- q->buffers = 0;
- q->length = 0;
+ atomic_set(&q->buffers, 0);
q->bytesused = 0;
}
@@ -55,8 +54,7 @@
}
spin_lock_irqsave(&s->qlock, flags);
list_add_tail(&buf->list, &q->list);
- q->buffers++;
- q->length += s->buf_size;
+ atomic_inc(&q->buffers);
q->bytesused += buf->bytesused - buf->readpos;
spin_unlock_irqrestore(&s->qlock, flags);
}
@@ -70,8 +68,7 @@
if (!list_empty(&q->list)) {
buf = list_entry(q->list.next, struct cx18_buffer, list);
list_del_init(q->list.next);
- q->buffers--;
- q->length -= s->buf_size;
+ atomic_dec(&q->buffers);
q->bytesused -= buf->bytesused - buf->readpos;
}
spin_unlock_irqrestore(&s->qlock, flags);
@@ -95,10 +92,8 @@
/* the transport buffers are handled differently,
they are not moved to the full queue */
if (s->type != CX18_ENC_STREAM_TYPE_TS) {
- s->q_free.buffers--;
- s->q_free.length -= s->buf_size;
- s->q_full.buffers++;
- s->q_full.length += s->buf_size;
+ atomic_dec(&s->q_free.buffers);
+ atomic_inc(&s->q_full.buffers);
s->q_full.bytesused += buf->bytesused;
list_move_tail(&buf->list, &s->q_full.list);
}
@@ -124,8 +119,7 @@
buf = list_entry(q->list.next, struct cx18_buffer, list);
list_move_tail(q->list.next, &s->q_free.list);
buf->bytesused = buf->readpos = buf->b_flags = 0;
- s->q_free.buffers++;
- s->q_free.length += s->buf_size;
+ atomic_inc(&s->q_free.buffers);
}
cx18_queue_init(q);
spin_unlock_irqrestore(&s->qlock, flags);
diff --git a/drivers/media/video/cx18/cx18-scb.c b/drivers/media/video/cx18/cx18-scb.c
index 30bc803..f56d377 100644
--- a/drivers/media/video/cx18/cx18-scb.c
+++ b/drivers/media/video/cx18/cx18-scb.c
@@ -20,102 +20,103 @@
*/
#include "cx18-driver.h"
+#include "cx18-io.h"
#include "cx18-scb.h"
void cx18_init_scb(struct cx18 *cx)
{
- setup_page(SCB_OFFSET);
- memset_io(cx->scb, 0, 0x10000);
+ cx18_setup_page(cx, SCB_OFFSET);
+ cx18_memset_io(cx, cx->scb, 0, 0x10000);
- writel(IRQ_APU_TO_CPU, &cx->scb->apu2cpu_irq);
- writel(IRQ_CPU_TO_APU_ACK, &cx->scb->cpu2apu_irq_ack);
- writel(IRQ_HPU_TO_CPU, &cx->scb->hpu2cpu_irq);
- writel(IRQ_CPU_TO_HPU_ACK, &cx->scb->cpu2hpu_irq_ack);
- writel(IRQ_PPU_TO_CPU, &cx->scb->ppu2cpu_irq);
- writel(IRQ_CPU_TO_PPU_ACK, &cx->scb->cpu2ppu_irq_ack);
- writel(IRQ_EPU_TO_CPU, &cx->scb->epu2cpu_irq);
- writel(IRQ_CPU_TO_EPU_ACK, &cx->scb->cpu2epu_irq_ack);
+ cx18_writel(cx, IRQ_APU_TO_CPU, &cx->scb->apu2cpu_irq);
+ cx18_writel(cx, IRQ_CPU_TO_APU_ACK, &cx->scb->cpu2apu_irq_ack);
+ cx18_writel(cx, IRQ_HPU_TO_CPU, &cx->scb->hpu2cpu_irq);
+ cx18_writel(cx, IRQ_CPU_TO_HPU_ACK, &cx->scb->cpu2hpu_irq_ack);
+ cx18_writel(cx, IRQ_PPU_TO_CPU, &cx->scb->ppu2cpu_irq);
+ cx18_writel(cx, IRQ_CPU_TO_PPU_ACK, &cx->scb->cpu2ppu_irq_ack);
+ cx18_writel(cx, IRQ_EPU_TO_CPU, &cx->scb->epu2cpu_irq);
+ cx18_writel(cx, IRQ_CPU_TO_EPU_ACK, &cx->scb->cpu2epu_irq_ack);
- writel(IRQ_CPU_TO_APU, &cx->scb->cpu2apu_irq);
- writel(IRQ_APU_TO_CPU_ACK, &cx->scb->apu2cpu_irq_ack);
- writel(IRQ_HPU_TO_APU, &cx->scb->hpu2apu_irq);
- writel(IRQ_APU_TO_HPU_ACK, &cx->scb->apu2hpu_irq_ack);
- writel(IRQ_PPU_TO_APU, &cx->scb->ppu2apu_irq);
- writel(IRQ_APU_TO_PPU_ACK, &cx->scb->apu2ppu_irq_ack);
- writel(IRQ_EPU_TO_APU, &cx->scb->epu2apu_irq);
- writel(IRQ_APU_TO_EPU_ACK, &cx->scb->apu2epu_irq_ack);
+ cx18_writel(cx, IRQ_CPU_TO_APU, &cx->scb->cpu2apu_irq);
+ cx18_writel(cx, IRQ_APU_TO_CPU_ACK, &cx->scb->apu2cpu_irq_ack);
+ cx18_writel(cx, IRQ_HPU_TO_APU, &cx->scb->hpu2apu_irq);
+ cx18_writel(cx, IRQ_APU_TO_HPU_ACK, &cx->scb->apu2hpu_irq_ack);
+ cx18_writel(cx, IRQ_PPU_TO_APU, &cx->scb->ppu2apu_irq);
+ cx18_writel(cx, IRQ_APU_TO_PPU_ACK, &cx->scb->apu2ppu_irq_ack);
+ cx18_writel(cx, IRQ_EPU_TO_APU, &cx->scb->epu2apu_irq);
+ cx18_writel(cx, IRQ_APU_TO_EPU_ACK, &cx->scb->apu2epu_irq_ack);
- writel(IRQ_CPU_TO_HPU, &cx->scb->cpu2hpu_irq);
- writel(IRQ_HPU_TO_CPU_ACK, &cx->scb->hpu2cpu_irq_ack);
- writel(IRQ_APU_TO_HPU, &cx->scb->apu2hpu_irq);
- writel(IRQ_HPU_TO_APU_ACK, &cx->scb->hpu2apu_irq_ack);
- writel(IRQ_PPU_TO_HPU, &cx->scb->ppu2hpu_irq);
- writel(IRQ_HPU_TO_PPU_ACK, &cx->scb->hpu2ppu_irq_ack);
- writel(IRQ_EPU_TO_HPU, &cx->scb->epu2hpu_irq);
- writel(IRQ_HPU_TO_EPU_ACK, &cx->scb->hpu2epu_irq_ack);
+ cx18_writel(cx, IRQ_CPU_TO_HPU, &cx->scb->cpu2hpu_irq);
+ cx18_writel(cx, IRQ_HPU_TO_CPU_ACK, &cx->scb->hpu2cpu_irq_ack);
+ cx18_writel(cx, IRQ_APU_TO_HPU, &cx->scb->apu2hpu_irq);
+ cx18_writel(cx, IRQ_HPU_TO_APU_ACK, &cx->scb->hpu2apu_irq_ack);
+ cx18_writel(cx, IRQ_PPU_TO_HPU, &cx->scb->ppu2hpu_irq);
+ cx18_writel(cx, IRQ_HPU_TO_PPU_ACK, &cx->scb->hpu2ppu_irq_ack);
+ cx18_writel(cx, IRQ_EPU_TO_HPU, &cx->scb->epu2hpu_irq);
+ cx18_writel(cx, IRQ_HPU_TO_EPU_ACK, &cx->scb->hpu2epu_irq_ack);
- writel(IRQ_CPU_TO_PPU, &cx->scb->cpu2ppu_irq);
- writel(IRQ_PPU_TO_CPU_ACK, &cx->scb->ppu2cpu_irq_ack);
- writel(IRQ_APU_TO_PPU, &cx->scb->apu2ppu_irq);
- writel(IRQ_PPU_TO_APU_ACK, &cx->scb->ppu2apu_irq_ack);
- writel(IRQ_HPU_TO_PPU, &cx->scb->hpu2ppu_irq);
- writel(IRQ_PPU_TO_HPU_ACK, &cx->scb->ppu2hpu_irq_ack);
- writel(IRQ_EPU_TO_PPU, &cx->scb->epu2ppu_irq);
- writel(IRQ_PPU_TO_EPU_ACK, &cx->scb->ppu2epu_irq_ack);
+ cx18_writel(cx, IRQ_CPU_TO_PPU, &cx->scb->cpu2ppu_irq);
+ cx18_writel(cx, IRQ_PPU_TO_CPU_ACK, &cx->scb->ppu2cpu_irq_ack);
+ cx18_writel(cx, IRQ_APU_TO_PPU, &cx->scb->apu2ppu_irq);
+ cx18_writel(cx, IRQ_PPU_TO_APU_ACK, &cx->scb->ppu2apu_irq_ack);
+ cx18_writel(cx, IRQ_HPU_TO_PPU, &cx->scb->hpu2ppu_irq);
+ cx18_writel(cx, IRQ_PPU_TO_HPU_ACK, &cx->scb->ppu2hpu_irq_ack);
+ cx18_writel(cx, IRQ_EPU_TO_PPU, &cx->scb->epu2ppu_irq);
+ cx18_writel(cx, IRQ_PPU_TO_EPU_ACK, &cx->scb->ppu2epu_irq_ack);
- writel(IRQ_CPU_TO_EPU, &cx->scb->cpu2epu_irq);
- writel(IRQ_EPU_TO_CPU_ACK, &cx->scb->epu2cpu_irq_ack);
- writel(IRQ_APU_TO_EPU, &cx->scb->apu2epu_irq);
- writel(IRQ_EPU_TO_APU_ACK, &cx->scb->epu2apu_irq_ack);
- writel(IRQ_HPU_TO_EPU, &cx->scb->hpu2epu_irq);
- writel(IRQ_EPU_TO_HPU_ACK, &cx->scb->epu2hpu_irq_ack);
- writel(IRQ_PPU_TO_EPU, &cx->scb->ppu2epu_irq);
- writel(IRQ_EPU_TO_PPU_ACK, &cx->scb->epu2ppu_irq_ack);
+ cx18_writel(cx, IRQ_CPU_TO_EPU, &cx->scb->cpu2epu_irq);
+ cx18_writel(cx, IRQ_EPU_TO_CPU_ACK, &cx->scb->epu2cpu_irq_ack);
+ cx18_writel(cx, IRQ_APU_TO_EPU, &cx->scb->apu2epu_irq);
+ cx18_writel(cx, IRQ_EPU_TO_APU_ACK, &cx->scb->epu2apu_irq_ack);
+ cx18_writel(cx, IRQ_HPU_TO_EPU, &cx->scb->hpu2epu_irq);
+ cx18_writel(cx, IRQ_EPU_TO_HPU_ACK, &cx->scb->epu2hpu_irq_ack);
+ cx18_writel(cx, IRQ_PPU_TO_EPU, &cx->scb->ppu2epu_irq);
+ cx18_writel(cx, IRQ_EPU_TO_PPU_ACK, &cx->scb->epu2ppu_irq_ack);
- writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2cpu_mb),
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2cpu_mb),
&cx->scb->apu2cpu_mb_offset);
- writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2cpu_mb),
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2cpu_mb),
&cx->scb->hpu2cpu_mb_offset);
- writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2cpu_mb),
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2cpu_mb),
&cx->scb->ppu2cpu_mb_offset);
- writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2cpu_mb),
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2cpu_mb),
&cx->scb->epu2cpu_mb_offset);
- writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2apu_mb),
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2apu_mb),
&cx->scb->cpu2apu_mb_offset);
- writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2apu_mb),
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2apu_mb),
&cx->scb->hpu2apu_mb_offset);
- writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2apu_mb),
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2apu_mb),
&cx->scb->ppu2apu_mb_offset);
- writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2apu_mb),
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2apu_mb),
&cx->scb->epu2apu_mb_offset);
- writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2hpu_mb),
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2hpu_mb),
&cx->scb->cpu2hpu_mb_offset);
- writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2hpu_mb),
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2hpu_mb),
&cx->scb->apu2hpu_mb_offset);
- writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2hpu_mb),
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2hpu_mb),
&cx->scb->ppu2hpu_mb_offset);
- writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2hpu_mb),
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2hpu_mb),
&cx->scb->epu2hpu_mb_offset);
- writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2ppu_mb),
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2ppu_mb),
&cx->scb->cpu2ppu_mb_offset);
- writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2ppu_mb),
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2ppu_mb),
&cx->scb->apu2ppu_mb_offset);
- writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2ppu_mb),
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2ppu_mb),
&cx->scb->hpu2ppu_mb_offset);
- writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2ppu_mb),
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2ppu_mb),
&cx->scb->epu2ppu_mb_offset);
- writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2epu_mb),
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2epu_mb),
&cx->scb->cpu2epu_mb_offset);
- writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2epu_mb),
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2epu_mb),
&cx->scb->apu2epu_mb_offset);
- writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2epu_mb),
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2epu_mb),
&cx->scb->hpu2epu_mb_offset);
- writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2epu_mb),
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2epu_mb),
&cx->scb->ppu2epu_mb_offset);
- writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu_state),
+ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu_state),
&cx->scb->ipc_offset);
- writel(1, &cx->scb->hpu_state);
- writel(1, &cx->scb->epu_state);
+ cx18_writel(cx, 1, &cx->scb->hpu_state);
+ cx18_writel(cx, 1, &cx->scb->epu_state);
}
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c
index 0da57f5..0c8e754 100644
--- a/drivers/media/video/cx18/cx18-streams.c
+++ b/drivers/media/video/cx18/cx18-streams.c
@@ -22,6 +22,7 @@
*/
#include "cx18-driver.h"
+#include "cx18-io.h"
#include "cx18-fileops.h"
#include "cx18-mailbox.h"
#include "cx18-i2c.h"
@@ -56,7 +57,7 @@
static struct {
const char *name;
int vfl_type;
- int minor_offset;
+ int num_offset;
int dma;
enum v4l2_buf_type buf_type;
struct file_operations *fops;
@@ -119,7 +120,7 @@
s->cx = cx;
s->type = type;
s->name = cx18_stream_info[type].name;
- s->handle = 0xffffffff;
+ s->handle = CX18_INVALID_TASK_HANDLE;
s->dma = cx18_stream_info[type].dma;
s->buf_size = cx->stream_buf_size[type];
@@ -143,8 +144,8 @@
{
struct cx18_stream *s = &cx->streams[type];
u32 cap = cx->v4l2_cap;
- int minor_offset = cx18_stream_info[type].minor_offset;
- int minor;
+ int num_offset = cx18_stream_info[type].num_offset;
+ int num = cx->num + cx18_first_minor + num_offset;
/* These four fields are always initialized. If v4l2dev == NULL, then
this stream is not in use. In that case no other fields but these
@@ -163,9 +164,6 @@
!(cap & (V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE)))
return 0;
- /* card number + user defined offset + device offset */
- minor = cx->num + cx18_first_minor + minor_offset;
-
/* User explicitly selected 0 buffers for these streams, so don't
create them. */
if (cx18_stream_info[type].dma != PCI_DMA_NONE &&
@@ -176,7 +174,7 @@
cx18_stream_init(cx, type);
- if (minor_offset == -1)
+ if (num_offset == -1)
return 0;
/* allocate and initialize the v4l2 video device structure */
@@ -190,7 +188,7 @@
snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "cx18-%d",
cx->num);
- s->v4l2dev->minor = minor;
+ s->v4l2dev->num = num;
s->v4l2dev->parent = &cx->dev->dev;
s->v4l2dev->fops = cx18_stream_info[type].fops;
s->v4l2dev->release = video_device_release;
@@ -226,7 +224,7 @@
{
struct cx18_stream *s = &cx->streams[type];
int vfl_type = cx18_stream_info[type].vfl_type;
- int minor;
+ int num;
/* TODO: Shouldn't this be a VFL_TYPE_TRANSPORT or something?
* We need a VFL_TYPE_TS defined.
@@ -244,38 +242,44 @@
if (s->v4l2dev == NULL)
return 0;
- minor = s->v4l2dev->minor;
+ num = s->v4l2dev->num;
+ /* card number + user defined offset + device offset */
+ if (type != CX18_ENC_STREAM_TYPE_MPG) {
+ struct cx18_stream *s_mpg = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];
+
+ if (s_mpg->v4l2dev)
+ num = s_mpg->v4l2dev->num + cx18_stream_info[type].num_offset;
+ }
/* Register device. First try the desired minor, then any free one. */
- if (video_register_device(s->v4l2dev, vfl_type, minor) &&
- video_register_device(s->v4l2dev, vfl_type, -1)) {
- CX18_ERR("Couldn't register v4l2 device for %s minor %d\n",
- s->name, minor);
+ if (video_register_device(s->v4l2dev, vfl_type, num)) {
+ CX18_ERR("Couldn't register v4l2 device for %s kernel number %d\n",
+ s->name, num);
video_device_release(s->v4l2dev);
s->v4l2dev = NULL;
return -ENOMEM;
}
- minor = s->v4l2dev->minor;
+ num = s->v4l2dev->num;
switch (vfl_type) {
case VFL_TYPE_GRABBER:
CX18_INFO("Registered device video%d for %s (%d MB)\n",
- minor, s->name, cx->options.megabytes[type]);
+ num, s->name, cx->options.megabytes[type]);
break;
case VFL_TYPE_RADIO:
CX18_INFO("Registered device radio%d for %s\n",
- minor - MINOR_VFL_TYPE_RADIO_MIN, s->name);
+ num, s->name);
break;
case VFL_TYPE_VBI:
if (cx->options.megabytes[type])
CX18_INFO("Registered device vbi%d for %s (%d MB)\n",
- minor - MINOR_VFL_TYPE_VBI_MIN,
+ num,
s->name, cx->options.megabytes[type]);
else
CX18_INFO("Registered device vbi%d for %s\n",
- minor - MINOR_VFL_TYPE_VBI_MIN, s->name);
+ num, s->name);
break;
}
@@ -432,7 +436,6 @@
default:
return -EINVAL;
}
- s->buffers_stolen = 0;
/* mute/unmute video */
cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2,
@@ -470,7 +473,7 @@
if (atomic_read(&cx->tot_capturing) == 0) {
clear_bit(CX18_F_I_EOS, &cx->i_flags);
- write_reg(7, CX18_DSP0_INTERRUPT_MASK);
+ cx18_write_reg(cx, 7, CX18_DSP0_INTERRUPT_MASK);
}
cx18_vapi(cx, CX18_CPU_DE_SET_MDL_ACK, 3, s->handle,
@@ -480,8 +483,9 @@
list_for_each(p, &s->q_free.list) {
struct cx18_buffer *buf = list_entry(p, struct cx18_buffer, list);
- writel(buf->dma_handle, &cx->scb->cpu_mdl[buf->id].paddr);
- writel(s->buf_size, &cx->scb->cpu_mdl[buf->id].length);
+ cx18_writel(cx, buf->dma_handle,
+ &cx->scb->cpu_mdl[buf->id].paddr);
+ cx18_writel(cx, s->buf_size, &cx->scb->cpu_mdl[buf->id].length);
cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
(void __iomem *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem,
1, buf->id, s->buf_size);
@@ -489,7 +493,14 @@
/* begin_capture */
if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) {
CX18_DEBUG_WARN("Error starting capture!\n");
+ /* Ensure we're really not capturing before releasing MDLs */
+ if (s->type == CX18_ENC_STREAM_TYPE_MPG)
+ cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, 1);
+ else
+ cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle);
+ cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle);
cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
+ /* FIXME - clean-up DSP0_INT mask, i_flags, s_flags, etc. */
return -EINVAL;
}
@@ -541,6 +552,9 @@
CX18_INFO("ignoring gop_end: not (yet?) supported by the firmware\n");
}
+ /* Tell the CX23418 it can't use our buffers anymore */
+ cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle);
+
if (s->type != CX18_ENC_STREAM_TYPE_TS)
atomic_dec(&cx->ana_capturing);
atomic_dec(&cx->tot_capturing);
@@ -549,12 +563,12 @@
clear_bit(CX18_F_S_STREAMING, &s->s_flags);
cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
- s->handle = 0xffffffff;
+ s->handle = CX18_INVALID_TASK_HANDLE;
if (atomic_read(&cx->tot_capturing) > 0)
return 0;
- write_reg(5, CX18_DSP0_INTERRUPT_MASK);
+ cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK);
wake_up(&s->waitq);
return 0;
@@ -568,8 +582,8 @@
for (i = 0; i < CX18_MAX_STREAMS; i++) {
struct cx18_stream *s = &cx->streams[i];
- if (s->v4l2dev && s->handle)
+ if (s->v4l2dev && (s->handle != CX18_INVALID_TASK_HANDLE))
return s->handle;
}
- return 0;
+ return CX18_INVALID_TASK_HANDLE;
}
diff --git a/drivers/media/video/cx18/cx18-version.h b/drivers/media/video/cx18/cx18-version.h
index d5c7a6f..9f6be2d 100644
--- a/drivers/media/video/cx18/cx18-version.h
+++ b/drivers/media/video/cx18/cx18-version.h
@@ -25,7 +25,7 @@
#define CX18_DRIVER_NAME "cx18"
#define CX18_DRIVER_VERSION_MAJOR 1
#define CX18_DRIVER_VERSION_MINOR 0
-#define CX18_DRIVER_VERSION_PATCHLEVEL 0
+#define CX18_DRIVER_VERSION_PATCHLEVEL 1
#define CX18_VERSION __stringify(CX18_DRIVER_VERSION_MAJOR) "." __stringify(CX18_DRIVER_VERSION_MINOR) "." __stringify(CX18_DRIVER_VERSION_PATCHLEVEL)
#define CX18_DRIVER_VERSION KERNEL_VERSION(CX18_DRIVER_VERSION_MAJOR, \
diff --git a/drivers/media/video/cx18/cx23418.h b/drivers/media/video/cx18/cx23418.h
index e7ed053..668f968 100644
--- a/drivers/media/video/cx18/cx23418.h
+++ b/drivers/media/video/cx18/cx23418.h
@@ -351,7 +351,7 @@
Descriptor Lists to the driver
IN[0] - Task handle. Handle of the task to start
ReturnCode - One of the ERR_DE_... */
-/* #define CX18_CPU_DE_ReleaseMDL (CPU_CMD_MASK_DE | 0x0006) */
+#define CX18_CPU_DE_RELEASE_MDL (CPU_CMD_MASK_DE | 0x0006)
/* Description: This command signals the cpu that the dat buffer has been
consumed and ready for re-use.
diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c
index 22847a0..cbbe47f 100644
--- a/drivers/media/video/cx2341x.c
+++ b/drivers/media/video/cx2341x.c
@@ -508,7 +508,10 @@
/* this setting is read-only for the cx2341x since the
V4L2_CID_MPEG_STREAM_TYPE really determines the
MPEG-1/2 setting */
- err = v4l2_ctrl_query_fill_std(qctrl);
+ err = v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_1,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 1,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2);
if (err == 0)
qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
return err;
diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig
index e60bd31..8c1b7fa4 100644
--- a/drivers/media/video/cx23885/Kconfig
+++ b/drivers/media/video/cx23885/Kconfig
@@ -15,6 +15,7 @@
select DVB_S5H1409 if !DVB_FE_CUSTOMISE
select DVB_S5H1411 if !DVB_FE_CUSTOMISE
select DVB_LGDT330X if !DVB_FE_CUSTOMISE
+ select DVB_ZL10353 if !DVB_FE_CUSTOMISE
select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMIZE
select MEDIA_TUNER_TDA8290 if !DVB_FE_CUSTOMIZE
select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMIZE
diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c
index 7b0e8c0..395c11f 100644
--- a/drivers/media/video/cx23885/cx23885-417.c
+++ b/drivers/media/video/cx23885/cx23885-417.c
@@ -36,7 +36,6 @@
#include <media/cx2341x.h>
#include "cx23885.h"
-#include "media/cx2341x.h"
#define CX23885_FIRM_IMAGE_SIZE 376836
#define CX23885_FIRM_IMAGE_NAME "v4l-cx23885-enc.fw"
@@ -632,7 +631,7 @@
/* ------------------------------------------------------------------ */
/* MPEG encoder API */
-char *cmd_to_str(int cmd)
+static char *cmd_to_str(int cmd)
{
switch (cmd) {
case CX2341X_ENC_PING_FW:
@@ -1583,6 +1582,7 @@
dprintk(2, "%s()\n", __func__);
+ lock_kernel();
list_for_each(list, &cx23885_devlist) {
h = list_entry(list, struct cx23885_dev, devlist);
if (h->v4l_device->minor == minor) {
@@ -1591,13 +1591,17 @@
}
}
- if (dev == NULL)
+ if (dev == NULL) {
+ unlock_kernel();
return -ENODEV;
+ }
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (NULL == fh)
+ if (NULL == fh) {
+ unlock_kernel();
return -ENOMEM;
+ }
file->private_data = fh;
fh->dev = dev;
@@ -1608,6 +1612,7 @@
V4L2_FIELD_INTERLACED,
sizeof(struct cx23885_buffer),
fh);
+ unlock_kernel();
return 0;
}
diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c
index c36d3f6..2cda15f 100644
--- a/drivers/media/video/cx23885/cx23885-cards.c
+++ b/drivers/media/video/cx23885/cx23885-cards.c
@@ -26,6 +26,7 @@
#include <media/cx25840.h>
#include "cx23885.h"
+#include "tuner-xc2028.h"
/* ------------------------------------------------------------------ */
/* board config info */
@@ -148,6 +149,15 @@
.portb = CX23885_MPEG_DVB,
.portc = CX23885_MPEG_DVB,
},
+ [CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP] = {
+ .name = "DViCO FusionHDTV DVB-T Dual Express",
+ .portb = CX23885_MPEG_DVB,
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H] = {
+ .name = "Leadtek Winfast PxDVR3200 H",
+ .portc = CX23885_MPEG_DVB,
+ },
};
const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
@@ -219,6 +229,14 @@
.subvendor = 0x18ac,
.subdevice = 0xd618,
.card = CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP,
+ },{
+ .subvendor = 0x18ac,
+ .subdevice = 0xdb78,
+ .card = CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP,
+ }, {
+ .subvendor = 0x107d,
+ .subdevice = 0x6681,
+ .card = CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H,
},
};
const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
@@ -319,15 +337,15 @@
dev->name, tv.model);
}
-/* Tuner callback function for cx23885 boards. Currently only needed
- * for HVR1500Q, which has an xc5000 tuner.
- */
-int cx23885_tuner_callback(void *priv, int command, int arg)
+int cx23885_tuner_callback(void *priv, int component, int command, int arg)
{
- struct cx23885_i2c *bus = priv;
- struct cx23885_dev *dev = bus->dev;
+ struct cx23885_tsport *port = priv;
+ struct cx23885_dev *dev = port->dev;
u32 bitmask = 0;
+ if (command == XC2028_RESET_CLK)
+ return 0;
+
if (command != 0) {
printk(KERN_ERR "%s(): Unknown command 0x%x.\n",
__func__, command);
@@ -335,21 +353,21 @@
}
switch(dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1400:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500:
case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
- /* Tuner Reset Command from xc5000 */
- if (command == 0)
- bitmask = 0x04;
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ /* Tuner Reset Command */
+ bitmask = 0x04;
break;
case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP:
- if (command == 0) {
-
- /* Two identical tuners on two different i2c buses,
- * we need to reset the correct gpio. */
- if (bus->nr == 0)
- bitmask = 0x01;
- else if (bus->nr == 1)
- bitmask = 0x04;
- }
+ case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
+ /* Two identical tuners on two different i2c buses,
+ * we need to reset the correct gpio. */
+ if (port->nr == 0)
+ bitmask = 0x01;
+ else if (port->nr == 1)
+ bitmask = 0x04;
break;
}
@@ -465,6 +483,32 @@
mdelay(20);
cx_set(GP0_IO, 0x000f000f);
break;
+ case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
+ /* GPIO-0 portb xc3028 reset */
+ /* GPIO-1 portb zl10353 reset */
+ /* GPIO-2 portc xc3028 reset */
+ /* GPIO-3 portc zl10353 reset */
+
+ /* Put the parts into reset and back */
+ cx_set(GP0_IO, 0x000f0000);
+ mdelay(20);
+ cx_clear(GP0_IO, 0x0000000f);
+ mdelay(20);
+ cx_set(GP0_IO, 0x000f000f);
+ break;
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ /* GPIO-2 xc3028 tuner reset */
+
+ /* The following GPIO's are on the internal AVCore (cx25840) */
+ /* GPIO-? zl10353 demod reset */
+
+ /* Put the parts into reset and back */
+ cx_set(GP0_IO, 0x00040000);
+ mdelay(20);
+ cx_clear(GP0_IO, 0x00000004);
+ mdelay(20);
+ cx_set(GP0_IO, 0x00040004);
+ break;
}
}
@@ -479,6 +523,9 @@
case CX23885_BOARD_HAUPPAUGE_HVR1400:
/* FIXME: Implement me */
break;
+ case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
+ request_module("ir-kbd-i2c");
+ break;
}
return 0;
@@ -516,6 +563,7 @@
switch (dev->board) {
case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP:
+ case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
@@ -548,6 +596,7 @@
case CX23885_BOARD_HAUPPAUGE_HVR1200:
case CX23885_BOARD_HAUPPAUGE_HVR1700:
case CX23885_BOARD_HAUPPAUGE_HVR1400:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
default:
ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
@@ -561,6 +610,7 @@
case CX23885_BOARD_HAUPPAUGE_HVR1800:
case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
case CX23885_BOARD_HAUPPAUGE_HVR1700:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
request_module("cx25840");
break;
}
diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c
index 25fb099..beb3e61 100644
--- a/drivers/media/video/cx23885/cx23885-core.c
+++ b/drivers/media/video/cx23885/cx23885-core.c
@@ -1442,7 +1442,7 @@
struct cx23885_dev *dev = port->dev;
struct cx23885_dmaqueue *q = &port->mpegq;
- dprintk(1, "%s()\n", __FUNCTION__);
+ dprintk(1, "%s()\n", __func__);
del_timer_sync(&q->timeout);
cx23885_stop_dma(port);
do_cancel_buffers(port, "cancel", 0);
diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c
index 291b9d0..24bd183 100644
--- a/drivers/media/video/cx23885/cx23885-dvb.c
+++ b/drivers/media/video/cx23885/cx23885-dvb.c
@@ -42,6 +42,7 @@
#include "tuner-simple.h"
#include "dib7000p.h"
#include "dibx000_common.h"
+#include "zl10353.h"
static unsigned int debug;
@@ -188,13 +189,11 @@
static struct xc5000_config hauppauge_hvr1500q_tunerconfig = {
.i2c_address = 0x61,
.if_khz = 5380,
- .tuner_callback = cx23885_tuner_callback
};
static struct xc5000_config dvico_xc5000_tunerconfig = {
.i2c_address = 0x64,
.if_khz = 5380,
- .tuner_callback = cx23885_tuner_callback
};
static struct tda829x_config tda829x_no_probe = {
@@ -303,35 +302,11 @@
.output_mode = OUTMODE_MPEG2_SERIAL,
};
-static int cx23885_hvr1500_xc3028_callback(void *ptr, int command, int arg)
-{
- struct cx23885_tsport *port = ptr;
- struct cx23885_dev *dev = port->dev;
-
- switch (command) {
- case XC2028_TUNER_RESET:
- /* Send the tuner in then out of reset */
- /* GPIO-2 xc3028 tuner */
- dprintk(1, "%s: XC2028_TUNER_RESET %d\n", __func__, arg);
-
- cx_set(GP0_IO, 0x00040000);
- cx_clear(GP0_IO, 0x00000004);
- msleep(5);
-
- cx_set(GP0_IO, 0x00040004);
- msleep(5);
- break;
- case XC2028_RESET_CLK:
- dprintk(1, "%s: XC2028_RESET_CLK %d\n", __func__, arg);
- break;
- default:
- dprintk(1, "%s: unknown command %d, arg %d\n", __func__,
- command, arg);
- return -EINVAL;
- }
-
- return 0;
-}
+static struct zl10353_config dvico_fusionhdtv_xc3028 = {
+ .demod_address = 0x0f,
+ .if2 = 45600,
+ .no_tuner = 1,
+};
static int dvb_register(struct cx23885_tsport *port)
{
@@ -413,8 +388,8 @@
&dev->i2c_bus[0].i2c_adap);
if (port->dvb.frontend != NULL)
dvb_attach(xc5000_attach, port->dvb.frontend,
- &i2c_bus->i2c_adap,
- &hauppauge_hvr1500q_tunerconfig, i2c_bus);
+ &i2c_bus->i2c_adap,
+ &hauppauge_hvr1500q_tunerconfig);
break;
case CX23885_BOARD_HAUPPAUGE_HVR1500:
i2c_bus = &dev->i2c_bus[1];
@@ -426,10 +401,9 @@
struct xc2028_config cfg = {
.i2c_adap = &i2c_bus->i2c_adap,
.i2c_addr = 0x61,
- .callback = cx23885_hvr1500_xc3028_callback,
};
static struct xc2028_ctrl ctl = {
- .fname = "xc3028-v27.fw",
+ .fname = XC2028_DEFAULT_FIRMWARE,
.max_len = 64,
.scode_table = XC3028_FE_OREN538,
};
@@ -465,13 +439,13 @@
struct xc2028_config cfg = {
.i2c_adap = &dev->i2c_bus[1].i2c_adap,
.i2c_addr = 0x64,
- .callback = cx23885_hvr1500_xc3028_callback,
};
static struct xc2028_ctrl ctl = {
- .fname = "xc3028L-v36.fw",
+ .fname = XC3028L_DEFAULT_FIRMWARE,
.max_len = 64,
.demod = 5000,
- .d2633 = 1
+ /* This is true for all demods with v36 firmware? */
+ .type = XC2028_D2633,
};
fe = dvb_attach(xc2028_attach,
@@ -492,8 +466,57 @@
&i2c_bus->i2c_adap);
if (port->dvb.frontend != NULL)
dvb_attach(xc5000_attach, port->dvb.frontend,
- &i2c_bus->i2c_adap,
- &dvico_xc5000_tunerconfig, i2c_bus);
+ &i2c_bus->i2c_adap,
+ &dvico_xc5000_tunerconfig);
+ break;
+ case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: {
+ i2c_bus = &dev->i2c_bus[port->nr - 1];
+
+ port->dvb.frontend = dvb_attach(zl10353_attach,
+ &dvico_fusionhdtv_xc3028,
+ &i2c_bus->i2c_adap);
+ if (port->dvb.frontend != NULL) {
+ struct dvb_frontend *fe;
+ struct xc2028_config cfg = {
+ .i2c_adap = &i2c_bus->i2c_adap,
+ .i2c_addr = 0x61,
+ };
+ static struct xc2028_ctrl ctl = {
+ .fname = XC2028_DEFAULT_FIRMWARE,
+ .max_len = 64,
+ .demod = XC3028_FE_ZARLINK456,
+ };
+
+ fe = dvb_attach(xc2028_attach, port->dvb.frontend,
+ &cfg);
+ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+ fe->ops.tuner_ops.set_config(fe, &ctl);
+ }
+ break;
+ }
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ i2c_bus = &dev->i2c_bus[0];
+
+ port->dvb.frontend = dvb_attach(zl10353_attach,
+ &dvico_fusionhdtv_xc3028,
+ &i2c_bus->i2c_adap);
+ if (port->dvb.frontend != NULL) {
+ struct dvb_frontend *fe;
+ struct xc2028_config cfg = {
+ .i2c_adap = &dev->i2c_bus[1].i2c_adap,
+ .i2c_addr = 0x61,
+ };
+ static struct xc2028_ctrl ctl = {
+ .fname = XC2028_DEFAULT_FIRMWARE,
+ .max_len = 64,
+ .demod = XC3028_FE_ZARLINK456,
+ };
+
+ fe = dvb_attach(xc2028_attach, port->dvb.frontend,
+ &cfg);
+ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+ fe->ops.tuner_ops.set_config(fe, &ctl);
+ }
break;
default:
printk("%s: The frontend of your DVB/ATSC card isn't supported yet\n",
@@ -504,6 +527,8 @@
printk("%s: frontend initialization failed\n", dev->name);
return -1;
}
+ /* define general-purpose callback pointer */
+ port->dvb.frontend->callback = cx23885_tuner_callback;
/* Put the analog decoder in standby to keep it quiet */
cx23885_call_i2c_clients(i2c_bus, TUNER_SET_STANDBY, NULL);
diff --git a/drivers/media/video/cx23885/cx23885-vbi.c b/drivers/media/video/cx23885/cx23885-vbi.c
index 35e61cd..5b297f0 100644
--- a/drivers/media/video/cx23885/cx23885-vbi.c
+++ b/drivers/media/video/cx23885/cx23885-vbi.c
@@ -85,18 +85,8 @@
return 0;
}
-int cx23885_stop_vbi_dma(struct cx23885_dev *dev)
-{
- /* stop dma */
- cx_clear(VID_A_DMA_CTL, 0x00000022);
- /* disable irqs */
- cx_clear(PCI_INT_MSK, 0x000001);
- cx_clear(VID_A_INT_MSK, 0x00000022);
- return 0;
-}
-
-int cx23885_restart_vbi_queue(struct cx23885_dev *dev,
+static int cx23885_restart_vbi_queue(struct cx23885_dev *dev,
struct cx23885_dmaqueue *q)
{
struct cx23885_buffer *buf;
diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c
index 6047c78..f75ed1c 100644
--- a/drivers/media/video/cx23885/cx23885-video.c
+++ b/drivers/media/video/cx23885/cx23885-video.c
@@ -244,7 +244,7 @@
};
static const int CX23885_CTLS = ARRAY_SIZE(cx23885_ctls);
-const u32 cx23885_user_ctrls[] = {
+static const u32 cx23885_user_ctrls[] = {
V4L2_CID_USER_CLASS,
V4L2_CID_BRIGHTNESS,
V4L2_CID_CONTRAST,
@@ -254,14 +254,13 @@
V4L2_CID_AUDIO_MUTE,
0
};
-EXPORT_SYMBOL(cx23885_user_ctrls);
static const u32 *ctrl_classes[] = {
cx23885_user_ctrls,
NULL
};
-void cx23885_video_wakeup(struct cx23885_dev *dev,
+static void cx23885_video_wakeup(struct cx23885_dev *dev,
struct cx23885_dmaqueue *q, u32 count)
{
struct cx23885_buffer *buf;
@@ -296,7 +295,7 @@
__func__, bc);
}
-int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm)
+static int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm)
{
dprintk(1, "%s(norm = 0x%08x) name: [%s]\n",
__func__,
@@ -314,7 +313,7 @@
return 0;
}
-struct video_device *cx23885_vdev_init(struct cx23885_dev *dev,
+static struct video_device *cx23885_vdev_init(struct cx23885_dev *dev,
struct pci_dev *pci,
struct video_device *template,
char *type)
@@ -334,7 +333,7 @@
return vfd;
}
-int cx23885_ctrl_query(struct v4l2_queryctrl *qctrl)
+static int cx23885_ctrl_query(struct v4l2_queryctrl *qctrl)
{
int i;
@@ -351,7 +350,6 @@
*qctrl = cx23885_ctls[i].v;
return 0;
}
-EXPORT_SYMBOL(cx23885_ctrl_query);
/* ------------------------------------------------------------------- */
/* resource management */
@@ -402,7 +400,7 @@
mutex_unlock(&dev->lock);
}
-int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input)
+static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input)
{
struct v4l2_routing route;
memset(&route, 0, sizeof(route));
@@ -422,10 +420,9 @@
return 0;
}
-EXPORT_SYMBOL(cx23885_video_mux);
/* ------------------------------------------------------------------ */
-int cx23885_set_scale(struct cx23885_dev *dev, unsigned int width,
+static int cx23885_set_scale(struct cx23885_dev *dev, unsigned int width,
unsigned int height, enum v4l2_field field)
{
dprintk(1, "%s()\n", __func__);
@@ -731,6 +728,7 @@
enum v4l2_buf_type type = 0;
int radio = 0;
+ lock_kernel();
list_for_each(list, &cx23885_devlist) {
h = list_entry(list, struct cx23885_dev, devlist);
if (h->video_dev->minor == minor) {
@@ -748,16 +746,20 @@
dev = h;
}
}
- if (NULL == dev)
+ if (NULL == dev) {
+ unlock_kernel();
return -ENODEV;
+ }
dprintk(1, "open minor=%d radio=%d type=%s\n",
minor, radio, v4l2_type_names[type]);
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (NULL == fh)
+ if (NULL == fh) {
+ unlock_kernel();
return -ENOMEM;
+ }
file->private_data = fh;
fh->dev = dev;
fh->radio = radio;
@@ -775,6 +777,7 @@
dprintk(1, "post videobuf_queue_init()\n");
+ unlock_kernel();
return 0;
}
@@ -884,21 +887,19 @@
/* ------------------------------------------------------------------ */
/* VIDEO CTRL IOCTLS */
-int cx23885_get_control(struct cx23885_dev *dev, struct v4l2_control *ctl)
+static int cx23885_get_control(struct cx23885_dev *dev, struct v4l2_control *ctl)
{
dprintk(1, "%s() calling cx25840(VIDIOC_G_CTRL)\n", __func__);
cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_G_CTRL, ctl);
return 0;
}
-EXPORT_SYMBOL(cx23885_get_control);
-int cx23885_set_control(struct cx23885_dev *dev, struct v4l2_control *ctl)
+static int cx23885_set_control(struct cx23885_dev *dev, struct v4l2_control *ctl)
{
dprintk(1, "%s() calling cx25840(VIDIOC_S_CTRL)"
" (disabled - no action)\n", __func__);
return 0;
}
-EXPORT_SYMBOL(cx23885_set_control);
static void init_controls(struct cx23885_dev *dev)
{
@@ -1146,7 +1147,7 @@
return 0;
}
-int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i)
+static int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i)
{
static const char *iname[] = {
[CX23885_VMUX_COMPOSITE1] = "Composite1",
@@ -1179,7 +1180,6 @@
i->std = CX23885_NORMS;
return 0;
}
-EXPORT_SYMBOL(cx23885_enum_input);
static int vidioc_enum_input(struct file *file, void *priv,
struct v4l2_input *i)
@@ -1288,7 +1288,7 @@
return 0;
}
-int cx23885_set_freq(struct cx23885_dev *dev, struct v4l2_frequency *f)
+static int cx23885_set_freq(struct cx23885_dev *dev, struct v4l2_frequency *f)
{
if (unlikely(UNSET == dev->tuner_type))
return -EINVAL;
@@ -1307,7 +1307,6 @@
return 0;
}
-EXPORT_SYMBOL(cx23885_set_freq);
static int vidioc_s_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h
index e23d97c..ba4e0aa 100644
--- a/drivers/media/video/cx23885/cx23885.h
+++ b/drivers/media/video/cx23885/cx23885.h
@@ -64,6 +64,8 @@
#define CX23885_BOARD_HAUPPAUGE_HVR1700 8
#define CX23885_BOARD_HAUPPAUGE_HVR1400 9
#define CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP 10
+#define CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP 11
+#define CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H 12
/* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */
#define CX23885_NORMS (\
@@ -409,7 +411,7 @@
extern struct cx23885_subid cx23885_subids[];
extern const unsigned int cx23885_idcount;
-extern int cx23885_tuner_callback(void *priv, int command, int arg);
+extern int cx23885_tuner_callback(void *priv, int component, int command, int arg);
extern void cx23885_card_list(struct cx23885_dev *dev);
extern int cx23885_ir_init(struct cx23885_dev *dev);
extern void cx23885_gpio_setup(struct cx23885_dev *dev);
diff --git a/drivers/media/video/cx25840/cx25840-vbi.c b/drivers/media/video/cx25840/cx25840-vbi.c
index 69f2bbd..58e6ef1 100644
--- a/drivers/media/video/cx25840/cx25840-vbi.c
+++ b/drivers/media/video/cx25840/cx25840-vbi.c
@@ -141,10 +141,11 @@
u8 lcr[24];
fmt = arg;
- if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE &&
+ fmt->type != V4L2_BUF_TYPE_VBI_CAPTURE)
return -EINVAL;
svbi = &fmt->fmt.sliced;
- if (svbi->service_set == 0) {
+ if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
/* raw VBI */
memset(svbi, 0, sizeof(*svbi));
diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig
index 9dd7bdf..0b9e5fa 100644
--- a/drivers/media/video/cx88/Kconfig
+++ b/drivers/media/video/cx88/Kconfig
@@ -58,6 +58,10 @@
select DVB_ISL6421 if !DVB_FE_CUSTOMISE
select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE
select DVB_S5H1411 if !DVB_FE_CUSTOMISE
+ select DVB_CX24116 if !DVB_FE_CUSTOMISE
+ select DVB_STV0299 if !DVB_FE_CUSTOMISE
+ select DVB_STV0288 if !DVB_FE_CUSTOMISE
+ select DVB_STB6000 if !DVB_FE_CUSTOMISE
---help---
This adds support for DVB/ATSC cards based on the
Conexant 2388x chip.
diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c
index 9a1374a..e713697 100644
--- a/drivers/media/video/cx88/cx88-blackbird.c
+++ b/drivers/media/video/cx88/cx88-blackbird.c
@@ -1057,12 +1057,15 @@
struct cx8802_driver *drv = NULL;
int err;
+ lock_kernel();
dev = cx8802_get_device(inode);
dprintk( 1, "%s\n", __func__);
- if (dev == NULL)
+ if (dev == NULL) {
+ unlock_kernel();
return -ENODEV;
+ }
/* Make sure we can acquire the hardware */
drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD);
@@ -1070,6 +1073,7 @@
err = drv->request_acquire(drv);
if(err != 0) {
dprintk(1,"%s: Unable to acquire hardware, %d\n", __func__, err);
+ unlock_kernel();
return err;
}
}
@@ -1077,6 +1081,7 @@
if (blackbird_initialize_codec(dev) < 0) {
if (drv)
drv->request_release(drv);
+ unlock_kernel();
return -EINVAL;
}
dprintk(1,"open minor=%d\n",minor);
@@ -1086,6 +1091,7 @@
if (NULL == fh) {
if (drv)
drv->request_release(drv);
+ unlock_kernel();
return -ENOMEM;
}
file->private_data = fh;
@@ -1101,6 +1107,7 @@
/* FIXME: locking against other video device */
cx88_set_scale(dev->core, dev->width, dev->height,
fh->mpegq.field);
+ unlock_kernel();
return 0;
}
diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c
index de199a2..5da04e8 100644
--- a/drivers/media/video/cx88/cx88-cards.c
+++ b/drivers/media/video/cx88/cx88-cards.c
@@ -1349,27 +1349,30 @@
.radio_addr = ADDR_UNSET,
.tda9887_conf = TDA9887_PRESENT,
.audio_chip = V4L2_IDENT_WM8775,
+ /*
+ * gpio0 as reported by Mike Crash <mike AT mikecrash.com>
+ */
.input = {{
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
- .gpio0 = 0xe780,
+ .gpio0 = 0xef88,
.audioroute = 1,
},{
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
- .gpio0 = 0xe780,
+ .gpio0 = 0xef88,
.audioroute = 2,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
- .gpio0 = 0xe780,
+ .gpio0 = 0xef88,
.audioroute = 2,
}},
/* fixme: Add radio support */
.mpeg = CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD,
.radio = {
.type = CX88_RADIO,
- .gpio0 = 0xe780,
+ .gpio0 = 0xef88,
},
},
[CX88_BOARD_ADSTECH_PTV_390] = {
@@ -1446,15 +1449,26 @@
.name = "Pinnacle Hybrid PCTV",
.tuner_type = TUNER_XC2028,
.tuner_addr = 0x61,
+ .radio_type = TUNER_XC2028,
+ .radio_addr = 0x61,
.input = { {
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
+ .gpio0 = 0x004ff,
+ .gpio1 = 0x010ff,
+ .gpio2 = 0x00001,
}, {
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
+ .gpio0 = 0x004fb,
+ .gpio1 = 0x010ef,
+ .audioroute = 1,
}, {
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
+ .gpio0 = 0x004fb,
+ .gpio1 = 0x010ef,
+ .audioroute = 1,
} },
.radio = {
.type = CX88_RADIO,
@@ -1462,6 +1476,7 @@
.gpio1 = 0x010ff,
.gpio2 = 0x0ff,
},
+ .mpeg = CX88_MPEG_DVB,
},
[CX88_BOARD_WINFAST_TV2000_XP_GLOBAL] = {
.name = "Winfast TV2000 XP Global",
@@ -1566,9 +1581,9 @@
},
[CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO] = {
.name = "DViCO FusionHDTV DVB-T PRO",
- .tuner_type = TUNER_ABSENT, /* XXX: Has XC3028 */
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0x61,
.radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.input = { {
.type = CX88_VMUX_COMPOSITE1,
@@ -1625,6 +1640,36 @@
.gpio2 = 0x0cfb,
},
},
+ [CX88_BOARD_PROLINK_PV_GLOBAL_XTREME] = {
+ .name = "Prolink Pixelview Global Extreme",
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0x61,
+ .input = { {
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x04fb,
+ .gpio1 = 0x04080,
+ .gpio2 = 0x0cf7,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x04fb,
+ .gpio1 = 0x04080,
+ .gpio2 = 0x0cfb,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x04fb,
+ .gpio1 = 0x04080,
+ .gpio2 = 0x0cfb,
+ } },
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x04ff,
+ .gpio1 = 0x04080,
+ .gpio2 = 0x0cf7,
+ },
+ },
/* Both radio, analog and ATSC work with this board.
However, for analog to work, s5h1409 gate should be open,
otherwise, tuner-xc3028 won't be detected.
@@ -1664,6 +1709,131 @@
},
.mpeg = CX88_MPEG_DVB,
},
+ [CX88_BOARD_HAUPPAUGE_HVR4000] = {
+ .name = "Hauppauge WinTV-HVR4000 DVB-S/S2/T/Hybrid",
+ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ /*
+ * GPIO0 (WINTV2000)
+ *
+ * Analogue SAT DVB-T
+ * Antenna 0xc4bf 0xc4bb
+ * Composite 0xc4bf 0xc4bb
+ * S-Video 0xc4bf 0xc4bb
+ * Composite1 0xc4ff 0xc4fb
+ * S-Video1 0xc4ff 0xc4fb
+ *
+ * BIT VALUE FUNCTION GP{x}_IO
+ * 0 1 I:?
+ * 1 1 I:?
+ * 2 1 O:DVB-T DEMOD ENABLE LOW/ANALOG DEMOD ENABLE HIGH
+ * 3 1 I:?
+ * 4 1 I:?
+ * 5 1 I:?
+ * 6 0 O:INPUT SELECTOR 0=INTERNAL 1=EXPANSION
+ * 7 1 O:DVB-T DEMOD RESET LOW
+ *
+ * BIT VALUE FUNCTION GP{x}_OE
+ * 8 0 I
+ * 9 0 I
+ * a 1 O
+ * b 0 I
+ * c 0 I
+ * d 0 I
+ * e 1 O
+ * f 1 O
+ */
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0xc4bf,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0xc4bf,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0xc4bf,
+ } },
+ /* fixme: Add radio support */
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_HAUPPAUGE_HVR4000LITE] = {
+ .name = "Hauppauge WinTV-HVR4000(Lite) DVB-S/S2",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_TEVII_S420] = {
+ .name = "TeVii S420 DVB-S",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_TEVII_S460] = {
+ .name = "TeVii S460 DVB-S/S2",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_OMICOM_SS4_PCI] = {
+ .name = "Omicom SS4 DVB-S/S2 PCI",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_TBS_8920] = {
+ .name = "TBS 8920 DVB-S/S2",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 1,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_PROF_7300] = {
+ .name = "PROF 7300 DVB-S/S2",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
};
/* ------------------------------------------------------------------ */
@@ -2010,9 +2180,53 @@
.subdevice = 0x4935,
.card = CX88_BOARD_PROLINK_PV_8000GT,
}, {
+ .subvendor = 0x1554,
+ .subdevice = 0x4976,
+ .card = CX88_BOARD_PROLINK_PV_GLOBAL_XTREME,
+ }, {
.subvendor = 0x17de,
.subdevice = 0x08c1,
.card = CX88_BOARD_KWORLD_ATSC_120,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x6900,
+ .card = CX88_BOARD_HAUPPAUGE_HVR4000,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x6904,
+ .card = CX88_BOARD_HAUPPAUGE_HVR4000,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x6902,
+ .card = CX88_BOARD_HAUPPAUGE_HVR4000,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x6905,
+ .card = CX88_BOARD_HAUPPAUGE_HVR4000LITE,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x6906,
+ .card = CX88_BOARD_HAUPPAUGE_HVR4000LITE,
+ }, {
+ .subvendor = 0xd420,
+ .subdevice = 0x9022,
+ .card = CX88_BOARD_TEVII_S420,
+ }, {
+ .subvendor = 0xd460,
+ .subdevice = 0x9022,
+ .card = CX88_BOARD_TEVII_S460,
+ }, {
+ .subvendor = 0xA044,
+ .subdevice = 0x2011,
+ .card = CX88_BOARD_OMICOM_SS4_PCI,
+ }, {
+ .subvendor = 0x8920,
+ .subdevice = 0x8888,
+ .card = CX88_BOARD_TBS_8920,
+ }, {
+ .subvendor = 0xB033,
+ .subdevice = 0x3033,
+ .card = CX88_BOARD_PROF_7300,
},
};
@@ -2065,6 +2279,13 @@
case 14669: /* WinTV-HVR3000 (OEM, no IR, no b/panel video - Low profile) */
case 28552: /* WinTV-PVR 'Roslyn' (No IR) */
case 34519: /* WinTV-PCI-FM */
+ case 69009:
+ /* WinTV-HVR4000 (DVBS/S2/T, Video and IR, back panel inputs) */
+ case 69100: /* WinTV-HVR4000LITE (DVBS/S2, IR) */
+ case 69500: /* WinTV-HVR4000LITE (DVBS/S2, No IR) */
+ case 69559:
+ /* WinTV-HVR4000 (DVBS/S2/T, Video no IR, back panel inputs) */
+ case 69569: /* WinTV-HVR4000 (DVBS/S2/T, Video no IR) */
case 90002: /* Nova-T-PCI (9002) */
case 92001: /* Nova-S-Plus (Video and IR) */
case 92002: /* Nova-S-Plus (Video and IR) */
@@ -2149,9 +2370,21 @@
{
switch (command) {
case XC2028_TUNER_RESET:
- cx_write(MO_GP0_IO, 0x101000);
- mdelay(5);
- cx_set(MO_GP0_IO, 0x101010);
+ switch (core->boardnr) {
+ case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
+ /* GPIO-4 xc3028 tuner */
+
+ cx_set(MO_GP0_IO, 0x00001000);
+ cx_clear(MO_GP0_IO, 0x00000010);
+ msleep(100);
+ cx_set(MO_GP0_IO, 0x00000010);
+ msleep(100);
+ break;
+ default:
+ cx_write(MO_GP0_IO, 0x101000);
+ mdelay(5);
+ cx_set(MO_GP0_IO, 0x101010);
+ }
break;
default:
return -EINVAL;
@@ -2258,8 +2491,10 @@
return cx88_xc3028_geniatech_tuner_callback(core,
command, arg);
case CX88_BOARD_PROLINK_PV_8000GT:
+ case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME:
return cx88_pv_8000gt_callback(core, command, arg);
case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO:
+ case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
return cx88_dvico_xc2028_callback(core, command, arg);
}
@@ -2327,7 +2562,7 @@
return 0; /* Should never be here */
}
-int cx88_tuner_callback(void *priv, int command, int arg)
+int cx88_tuner_callback(void *priv, int component, int command, int arg)
{
struct i2c_algo_bit_data *i2c_algo = priv;
struct cx88_core *core;
@@ -2344,6 +2579,9 @@
return -EINVAL;
}
+ if (component != DVB_FRONTEND_COMPONENT_TUNER)
+ return -EINVAL;
+
switch (core->board.tuner_type) {
case TUNER_XC2028:
info_printk(core, "Calling XC2028/3028 callback\n");
@@ -2392,16 +2630,22 @@
{
switch (core->boardnr) {
case CX88_BOARD_HAUPPAUGE_HVR1300:
- /* Bring the 702 demod up before i2c scanning/attach or devices are hidden */
- /* We leave here with the 702 on the bus */
- cx_write(MO_GP0_IO, 0x0000e780);
+ /*
+ * Bring the 702 demod up before i2c scanning/attach or devices are hidden
+ * We leave here with the 702 on the bus
+ *
+ * "reset the IR receiver on GPIO[3]"
+ * Reported by Mike Crash <mike AT mikecrash.com>
+ */
+ cx_write(MO_GP0_IO, 0x0000ef88);
udelay(1000);
- cx_clear(MO_GP0_IO, 0x00000080);
+ cx_clear(MO_GP0_IO, 0x00000088);
udelay(50);
- cx_set(MO_GP0_IO, 0x00000080); /* 702 out of reset */
+ cx_set(MO_GP0_IO, 0x00000088); /* 702 out of reset */
udelay(1000);
break;
+ case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME:
case CX88_BOARD_PROLINK_PV_8000GT:
cx_write(MO_GP2_IO, 0xcf7);
mdelay(50);
@@ -2411,10 +2655,18 @@
msleep(10);
break;
- case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD:
+ case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD:
/* Enable the xc5000 tuner */
cx_set(MO_GP0_IO, 0x00001010);
break;
+
+ case CX88_BOARD_HAUPPAUGE_HVR3000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
+ /* Init GPIO */
+ cx_write(MO_GP0_IO, core->board.input[0].gpio0);
+ udelay(1000);
+ break;
}
}
@@ -2435,17 +2687,22 @@
core->i2c_algo.udelay = 16;
break;
case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO:
- ctl->scode_table = XC3028_FE_ZARLINK456;
+ ctl->demod = XC3028_FE_ZARLINK456;
break;
case CX88_BOARD_KWORLD_ATSC_120:
case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
ctl->demod = XC3028_FE_OREN538;
break;
+ case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME:
case CX88_BOARD_PROLINK_PV_8000GT:
/*
- * This board uses non-MTS firmware
+ * Those boards uses non-MTS firmware
*/
break;
+ case CX88_BOARD_PINNACLE_HYBRID_PCTV:
+ ctl->demod = XC3028_FE_ZARLINK456;
+ ctl->mts = 1;
+ break;
default:
ctl->demod = XC3028_FE_OREN538;
ctl->mts = 1;
@@ -2489,6 +2746,8 @@
case CX88_BOARD_HAUPPAUGE_HVR1100LP:
case CX88_BOARD_HAUPPAUGE_HVR3000:
case CX88_BOARD_HAUPPAUGE_HVR1300:
+ case CX88_BOARD_HAUPPAUGE_HVR4000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
if (0 == core->i2c_rc)
hauppauge_eeprom(core, eeprom);
break;
@@ -2570,7 +2829,18 @@
tea5767_cfg.priv = &ctl;
cx88_call_i2c_clients(core, TUNER_SET_CONFIG, &tea5767_cfg);
+ break;
}
+ case CX88_BOARD_TEVII_S420:
+ case CX88_BOARD_TEVII_S460:
+ case CX88_BOARD_OMICOM_SS4_PCI:
+ case CX88_BOARD_TBS_8920:
+ case CX88_BOARD_PROF_7300:
+ cx_write(MO_SRST_IO, 0);
+ msleep(100);
+ cx_write(MO_SRST_IO, 1);
+ msleep(100);
+ break;
} /*end switch() */
diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c
index d96173f..344ed26 100644
--- a/drivers/media/video/cx88/cx88-dvb.c
+++ b/drivers/media/video/cx88/cx88-dvb.c
@@ -48,6 +48,11 @@
#include "tuner-simple.h"
#include "tda9887.h"
#include "s5h1411.h"
+#include "stv0299.h"
+#include "z0194a.h"
+#include "stv0288.h"
+#include "stb6000.h"
+#include "cx24116.h"
MODULE_DESCRIPTION("driver for cx2388x based DVB cards");
MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
@@ -375,37 +380,28 @@
return 0;
}
-static int cx88_pci_nano_callback(void *ptr, int command, int arg)
+static int tevii_dvbs_set_voltage(struct dvb_frontend *fe,
+ fe_sec_voltage_t voltage)
{
- struct cx88_core *core = ptr;
+ struct cx8802_dev *dev= fe->dvb->priv;
+ struct cx88_core *core = dev->core;
- switch (command) {
- case XC2028_TUNER_RESET:
- /* Send the tuner in then out of reset */
- dprintk(1, "%s: XC2028_TUNER_RESET %d\n", __func__, arg);
-
- switch (core->boardnr) {
- case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
- /* GPIO-4 xc3028 tuner */
-
- cx_set(MO_GP0_IO, 0x00001000);
- cx_clear(MO_GP0_IO, 0x00000010);
- msleep(100);
- cx_set(MO_GP0_IO, 0x00000010);
- msleep(100);
+ switch (voltage) {
+ case SEC_VOLTAGE_13:
+ printk("LNB Voltage SEC_VOLTAGE_13\n");
+ cx_write(MO_GP0_IO, 0x00006040);
break;
- }
-
- break;
- case XC2028_RESET_CLK:
- dprintk(1, "%s: XC2028_RESET_CLK %d\n", __func__, arg);
- break;
- default:
- dprintk(1, "%s: unknown command %d, arg %d\n", __func__,
- command, arg);
- return -EINVAL;
+ case SEC_VOLTAGE_18:
+ printk("LNB Voltage SEC_VOLTAGE_18\n");
+ cx_write(MO_GP0_IO, 0x00006060);
+ break;
+ case SEC_VOLTAGE_OFF:
+ printk("LNB Voltage SEC_VOLTAGE_off\n");
+ break;
}
+ if (core->prev_set_voltage)
+ return core->prev_set_voltage(fe, voltage);
return 0;
}
@@ -456,7 +452,12 @@
static struct xc5000_config pinnacle_pctv_hd_800i_tuner_config = {
.i2c_address = 0x64,
.if_khz = 5380,
- .tuner_callback = cx88_tuner_callback,
+};
+
+static struct zl10353_config cx88_pinnacle_hybrid_pctv = {
+ .demod_address = (0x1e >> 1),
+ .no_tuner = 1,
+ .if2 = 45600,
};
static struct zl10353_config cx88_geniatech_x8000_mt = {
@@ -477,7 +478,6 @@
static struct xc5000_config dvico_fusionhdtv7_tuner_config = {
.i2c_address = 0xc2 >> 1,
.if_khz = 5380,
- .tuner_callback = cx88_tuner_callback,
};
static int attach_xc3028(u8 addr, struct cx8802_dev *dev)
@@ -488,7 +488,6 @@
.i2c_adap = &dev->core->i2c_adap,
.i2c_addr = addr,
.ctrl = &ctl,
- .callback = cx88_tuner_callback,
};
if (!dev->dvb.frontend) {
@@ -518,6 +517,60 @@
return 0;
}
+static int cx24116_set_ts_param(struct dvb_frontend *fe,
+ int is_punctured)
+{
+ struct cx8802_dev *dev = fe->dvb->priv;
+ dev->ts_gen_cntrl = 0x2;
+
+ return 0;
+}
+
+static int cx24116_reset_device(struct dvb_frontend *fe)
+{
+ struct cx8802_dev *dev = fe->dvb->priv;
+ struct cx88_core *core = dev->core;
+
+ /* Reset the part */
+ cx_write(MO_SRST_IO, 0);
+ msleep(10);
+ cx_write(MO_SRST_IO, 1);
+ msleep(10);
+
+ return 0;
+}
+
+static struct cx24116_config hauppauge_hvr4000_config = {
+ .demod_address = 0x05,
+ .set_ts_params = cx24116_set_ts_param,
+ .reset_device = cx24116_reset_device,
+};
+
+static struct cx24116_config tevii_s460_config = {
+ .demod_address = 0x55,
+ .set_ts_params = cx24116_set_ts_param,
+ .reset_device = cx24116_reset_device,
+};
+
+static struct stv0299_config tevii_tuner_sharp_config = {
+ .demod_address = 0x68,
+ .inittab = sharp_z0194a__inittab,
+ .mclk = 88000000UL,
+ .invert = 1,
+ .skip_reinit = 0,
+ .lock_output = 1,
+ .volt13_op0_op1 = STV0299_VOLT13_OP1,
+ .min_delay_ms = 100,
+ .set_symbol_rate = sharp_z0194a__set_symbol_rate,
+ .set_ts_params = cx24116_set_ts_param,
+};
+
+static struct stv0288_config tevii_tuner_earda_config = {
+ .demod_address = 0x68,
+ .min_delay_ms = 100,
+ .set_ts_params = cx24116_set_ts_param,
+};
+
static int dvb_register(struct cx8802_dev *dev)
{
struct cx88_core *core = dev->core;
@@ -786,7 +839,7 @@
&core->i2c_adap);
if (dev->dvb.frontend) {
if (!dvb_attach(isl6421_attach, dev->dvb.frontend,
- &core->i2c_adap, 0x08, 0x00, 0x00))
+ &core->i2c_adap, 0x08, ISL6421_DCL, 0x00))
goto frontend_detach;
}
break;
@@ -813,13 +866,9 @@
&pinnacle_pctv_hd_800i_config,
&core->i2c_adap);
if (dev->dvb.frontend != NULL) {
- /* tuner_config.video_dev must point to
- * i2c_adap.algo_data
- */
if (!dvb_attach(xc5000_attach, dev->dvb.frontend,
&core->i2c_adap,
- &pinnacle_pctv_hd_800i_tuner_config,
- core->i2c_adap.algo_data))
+ &pinnacle_pctv_hd_800i_tuner_config))
goto frontend_detach;
}
break;
@@ -832,10 +881,9 @@
struct xc2028_config cfg = {
.i2c_adap = &core->i2c_adap,
.i2c_addr = 0x61,
- .callback = cx88_pci_nano_callback,
};
static struct xc2028_ctrl ctl = {
- .fname = "xc3028-v27.fw",
+ .fname = XC2028_DEFAULT_FIRMWARE,
.max_len = 64,
.scode_table = XC3028_FE_OREN538,
};
@@ -848,10 +896,13 @@
break;
case CX88_BOARD_PINNACLE_HYBRID_PCTV:
dev->dvb.frontend = dvb_attach(zl10353_attach,
- &cx88_geniatech_x8000_mt,
+ &cx88_pinnacle_hybrid_pctv,
&core->i2c_adap);
- if (attach_xc3028(0x61, dev) < 0)
- goto frontend_detach;
+ if (dev->dvb.frontend) {
+ dev->dvb.frontend->ops.i2c_gate_ctrl = NULL;
+ if (attach_xc3028(0x61, dev) < 0)
+ goto frontend_detach;
+ }
break;
case CX88_BOARD_GENIATECH_X8000_MT:
dev->ts_gen_cntrl = 0x00;
@@ -874,16 +925,69 @@
&dvico_fusionhdtv7_config,
&core->i2c_adap);
if (dev->dvb.frontend != NULL) {
- /* tuner_config.video_dev must point to
- * i2c_adap.algo_data
- */
if (!dvb_attach(xc5000_attach, dev->dvb.frontend,
&core->i2c_adap,
- &dvico_fusionhdtv7_tuner_config,
- core->i2c_adap.algo_data))
+ &dvico_fusionhdtv7_tuner_config))
goto frontend_detach;
}
break;
+ case CX88_BOARD_HAUPPAUGE_HVR4000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
+ /* Support for DVB-S only, not DVB-T support */
+ dev->dvb.frontend = dvb_attach(cx24116_attach,
+ &hauppauge_hvr4000_config,
+ &dev->core->i2c_adap);
+ if (dev->dvb.frontend) {
+ dvb_attach(isl6421_attach, dev->dvb.frontend,
+ &dev->core->i2c_adap,
+ 0x08, ISL6421_DCL, 0x00);
+ }
+ break;
+ case CX88_BOARD_TEVII_S420:
+ dev->dvb.frontend = dvb_attach(stv0299_attach,
+ &tevii_tuner_sharp_config,
+ &core->i2c_adap);
+ if (dev->dvb.frontend != NULL) {
+ if (!dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x60,
+ &core->i2c_adap, DVB_PLL_OPERA1))
+ goto frontend_detach;
+ core->prev_set_voltage = dev->dvb.frontend->ops.set_voltage;
+ dev->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
+
+ } else {
+ dev->dvb.frontend = dvb_attach(stv0288_attach,
+ &tevii_tuner_earda_config,
+ &core->i2c_adap);
+ if (dev->dvb.frontend != NULL) {
+ if (!dvb_attach(stb6000_attach, dev->dvb.frontend, 0x61,
+ &core->i2c_adap))
+ goto frontend_detach;
+ core->prev_set_voltage = dev->dvb.frontend->ops.set_voltage;
+ dev->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
+
+ }
+ }
+ break;
+ case CX88_BOARD_TEVII_S460:
+ dev->dvb.frontend = dvb_attach(cx24116_attach,
+ &tevii_s460_config,
+ &core->i2c_adap);
+ if (dev->dvb.frontend != NULL) {
+ core->prev_set_voltage = dev->dvb.frontend->ops.set_voltage;
+ dev->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
+ }
+ break;
+ case CX88_BOARD_OMICOM_SS4_PCI:
+ case CX88_BOARD_TBS_8920:
+ case CX88_BOARD_PROF_7300:
+ dev->dvb.frontend = dvb_attach(cx24116_attach,
+ &hauppauge_hvr4000_config,
+ &core->i2c_adap);
+ if (dev->dvb.frontend != NULL) {
+ core->prev_set_voltage = dev->dvb.frontend->ops.set_voltage;
+ dev->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
+ }
+ break;
default:
printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n",
core->name);
@@ -895,6 +999,8 @@
core->name);
return -EINVAL;
}
+ /* define general-purpose callback pointer */
+ dev->dvb.frontend->callback = cx88_tuner_callback;
/* Ensure all frontends negotiate bus access */
dev->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl;
diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c
index d7406a9..8e74d64 100644
--- a/drivers/media/video/cx88/cx88-i2c.c
+++ b/drivers/media/video/cx88/cx88-i2c.c
@@ -201,7 +201,23 @@
core->i2c_rc = i2c_bit_add_bus(&core->i2c_adap);
if (0 == core->i2c_rc) {
+ static u8 tuner_data[] =
+ { 0x0b, 0xdc, 0x86, 0x52 };
+ static struct i2c_msg tuner_msg =
+ { .flags = 0, .addr = 0xc2 >> 1, .buf = tuner_data, .len = 4 };
+
dprintk(1, "i2c register ok\n");
+ switch( core->boardnr ) {
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
+ case CX88_BOARD_HAUPPAUGE_HVR3000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000:
+ printk("%s: i2c init: enabling analog demod on HVR1300/3000/4000 tuner\n",
+ core->name);
+ i2c_transfer(core->i2c_client.adapter, &tuner_msg, 1);
+ break;
+ default:
+ break;
+ }
if (i2c_scan)
do_i2c_scan(core->name,&core->i2c_client);
} else
diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c
index 53526d9..8683d10 100644
--- a/drivers/media/video/cx88/cx88-input.c
+++ b/drivers/media/video/cx88/cx88-input.c
@@ -224,6 +224,8 @@
case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
case CX88_BOARD_HAUPPAUGE_HVR1100:
case CX88_BOARD_HAUPPAUGE_HVR3000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
ir_codes = ir_codes_hauppauge_new;
ir_type = IR_TYPE_RC5;
ir->sampling = 1;
@@ -259,6 +261,7 @@
ir->polling = 1; /* ms */
break;
case CX88_BOARD_PROLINK_PV_8000GT:
+ case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME:
ir_codes = ir_codes_pixelview_new;
ir->gpio_addr = MO_GP1_IO;
ir->mask_keycode = 0x3f;
@@ -392,7 +395,7 @@
{
struct cx88_IR *ir = core->ir;
u32 samples, ircode;
- int i;
+ int i, start, range, toggle, dev, code;
if (NULL == ir)
return;
@@ -461,6 +464,34 @@
case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
case CX88_BOARD_HAUPPAUGE_HVR1100:
case CX88_BOARD_HAUPPAUGE_HVR3000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
+ ircode = ir_decode_biphase(ir->samples, ir->scount, 5, 7);
+ ir_dprintk("biphase decoded: %x\n", ircode);
+ /*
+ * RC5 has an extension bit which adds a new range
+ * of available codes, this is detected here. Also
+ * hauppauge remotes (black/silver) always use
+ * specific device ids. If we do not filter the
+ * device ids then messages destined for devices
+ * such as TVs (id=0) will get through to the
+ * device causing mis-fired events.
+ */
+ /* split rc5 data block ... */
+ start = (ircode & 0x2000) >> 13;
+ range = (ircode & 0x1000) >> 12;
+ toggle= (ircode & 0x0800) >> 11;
+ dev = (ircode & 0x07c0) >> 6;
+ code = (ircode & 0x003f) | ((range << 6) ^ 0x0040);
+ if( start != 1)
+ /* no key pressed */
+ break;
+ if ( dev != 0x1e && dev != 0x1f )
+ /* not a hauppauge remote */
+ break;
+ ir_input_keydown(ir->input, &ir->ir, code, ircode);
+ ir->release = jiffies + msecs_to_jiffies(120);
+ break;
case CX88_BOARD_PINNACLE_PCTV_HD_800i:
ircode = ir_decode_biphase(ir->samples, ir->scount, 5, 7);
ir_dprintk("biphase decoded: %x\n", ircode);
diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c
index ef4d56e..be45955 100644
--- a/drivers/media/video/cx88/cx88-video.c
+++ b/drivers/media/video/cx88/cx88-video.c
@@ -773,6 +773,7 @@
enum v4l2_buf_type type = 0;
int radio = 0;
+ lock_kernel();
list_for_each_entry(h, &cx8800_devlist, devlist) {
if (h->video_dev->minor == minor) {
dev = h;
@@ -788,8 +789,10 @@
dev = h;
}
}
- if (NULL == dev)
+ if (NULL == dev) {
+ unlock_kernel();
return -ENODEV;
+ }
core = dev->core;
@@ -798,8 +801,10 @@
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh),GFP_KERNEL);
- if (NULL == fh)
+ if (NULL == fh) {
+ unlock_kernel();
return -ENOMEM;
+ }
file->private_data = fh;
fh->dev = dev;
fh->radio = radio;
@@ -832,6 +837,9 @@
cx88_set_stereo(core,V4L2_TUNER_MODE_STEREO,1);
cx88_call_i2c_clients(core,AUDC_SET_RADIO,NULL);
}
+ unlock_kernel();
+
+ atomic_inc(&core->users);
return 0;
}
@@ -920,7 +928,8 @@
file->private_data = NULL;
kfree(fh);
- cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL);
+ if(atomic_dec_and_test(&dev->core->users))
+ cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL);
return 0;
}
diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h
index 54fe650..dbf01b8 100644
--- a/drivers/media/video/cx88/cx88.h
+++ b/drivers/media/video/cx88/cx88.h
@@ -221,6 +221,14 @@
#define CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD 65
#define CX88_BOARD_PROLINK_PV_8000GT 66
#define CX88_BOARD_KWORLD_ATSC_120 67
+#define CX88_BOARD_HAUPPAUGE_HVR4000 68
+#define CX88_BOARD_HAUPPAUGE_HVR4000LITE 69
+#define CX88_BOARD_TEVII_S460 70
+#define CX88_BOARD_OMICOM_SS4_PCI 71
+#define CX88_BOARD_TBS_8920 72
+#define CX88_BOARD_TEVII_S420 73
+#define CX88_BOARD_PROLINK_PV_GLOBAL_XTREME 74
+#define CX88_BOARD_PROF_7300 75
enum cx88_itype {
CX88_VMUX_COMPOSITE1 = 1,
@@ -342,6 +350,7 @@
struct mutex lock;
/* various v4l controls */
u32 freq;
+ atomic_t users;
/* cx88-video needs to access cx8802 for hybrid tuner pll access. */
struct cx8802_dev *dvbdev;
@@ -601,7 +610,7 @@
/* ----------------------------------------------------------- */
/* cx88-cards.c */
-extern int cx88_tuner_callback(void *dev, int command, int arg);
+extern int cx88_tuner_callback(void *dev, int component, int command, int arg);
extern int cx88_get_resources(const struct cx88_core *core,
struct pci_dev *pci);
extern struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr);
diff --git a/drivers/media/video/dabusb.c b/drivers/media/video/dabusb.c
index 79faedf..3aa538a 100644
--- a/drivers/media/video/dabusb.c
+++ b/drivers/media/video/dabusb.c
@@ -866,7 +866,8 @@
dbg("dabusb_init: driver registered");
- info(DRIVER_VERSION ":" DRIVER_DESC);
+ printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
+ DRIVER_DESC "\n");
out:
return retval;
diff --git a/drivers/media/video/dpc7146.c b/drivers/media/video/dpc7146.c
deleted file mode 100644
index 88d6df7..0000000
--- a/drivers/media/video/dpc7146.c
+++ /dev/null
@@ -1,408 +0,0 @@
-/*
- dpc7146.c - v4l2 driver for the dpc7146 demonstration board
-
- Copyright (C) 2000-2003 Michael Hunold <michael@mihu.de>
-
- 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., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#define DEBUG_VARIABLE debug
-
-#include <media/saa7146_vv.h>
-#include <linux/video_decoder.h> /* for saa7111a */
-
-#define I2C_SAA7111A 0x24
-
-/* All unused bytes are reserverd. */
-#define SAA711X_CHIP_VERSION 0x00
-#define SAA711X_ANALOG_INPUT_CONTROL_1 0x02
-#define SAA711X_ANALOG_INPUT_CONTROL_2 0x03
-#define SAA711X_ANALOG_INPUT_CONTROL_3 0x04
-#define SAA711X_ANALOG_INPUT_CONTROL_4 0x05
-#define SAA711X_HORIZONTAL_SYNC_START 0x06
-#define SAA711X_HORIZONTAL_SYNC_STOP 0x07
-#define SAA711X_SYNC_CONTROL 0x08
-#define SAA711X_LUMINANCE_CONTROL 0x09
-#define SAA711X_LUMINANCE_BRIGHTNESS 0x0A
-#define SAA711X_LUMINANCE_CONTRAST 0x0B
-#define SAA711X_CHROMA_SATURATION 0x0C
-#define SAA711X_CHROMA_HUE_CONTROL 0x0D
-#define SAA711X_CHROMA_CONTROL 0x0E
-#define SAA711X_FORMAT_DELAY_CONTROL 0x10
-#define SAA711X_OUTPUT_CONTROL_1 0x11
-#define SAA711X_OUTPUT_CONTROL_2 0x12
-#define SAA711X_OUTPUT_CONTROL_3 0x13
-#define SAA711X_V_GATE_1_START 0x15
-#define SAA711X_V_GATE_1_STOP 0x16
-#define SAA711X_V_GATE_1_MSB 0x17
-#define SAA711X_TEXT_SLICER_STATUS 0x1A
-#define SAA711X_DECODED_BYTES_OF_TS_1 0x1B
-#define SAA711X_DECODED_BYTES_OF_TS_2 0x1C
-#define SAA711X_STATUS_BYTE 0x1F
-
-#define DPC_BOARD_CAN_DO_VBI(dev) (dev->revision != 0)
-
-static int debug;
-module_param(debug, int, 0);
-MODULE_PARM_DESC(debug, "debug verbosity");
-
-static int dpc_num;
-
-#define DPC_INPUTS 2
-static struct v4l2_input dpc_inputs[DPC_INPUTS] = {
- { 0, "Port A", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
- { 1, "Port B", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
-};
-
-#define DPC_AUDIOS 0
-
-static struct saa7146_extension_ioctls ioctls[] = {
- { VIDIOC_G_INPUT, SAA7146_EXCLUSIVE },
- { VIDIOC_S_INPUT, SAA7146_EXCLUSIVE },
- { VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE },
- { VIDIOC_S_STD, SAA7146_AFTER },
- { 0, 0 }
-};
-
-struct dpc
-{
- struct video_device *video_dev;
- struct video_device *vbi_dev;
-
- struct i2c_adapter i2c_adapter;
- struct i2c_client *saa7111a;
-
- int cur_input; /* current input */
-};
-
-static int dpc_check_clients(struct device *dev, void *data)
-{
- struct dpc* dpc = data;
- struct i2c_client *client = i2c_verify_client(dev);
-
- if( !client )
- return 0;
-
- if( I2C_SAA7111A == client->addr )
- dpc->saa7111a = client;
-
- return 0;
-}
-
-/* fixme: add vbi stuff here */
-static int dpc_probe(struct saa7146_dev* dev)
-{
- struct dpc* dpc = NULL;
-
- dpc = kzalloc(sizeof(struct dpc), GFP_KERNEL);
- if( NULL == dpc ) {
- printk("dpc_v4l2.o: dpc_probe: not enough kernel memory.\n");
- return -ENOMEM;
- }
-
- /* FIXME: enable i2c-port pins, video-port-pins
- video port pins should be enabled here ?! */
- saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26));
-
- dpc->i2c_adapter = (struct i2c_adapter) {
- .class = I2C_CLASS_TV_ANALOG,
- .name = "dpc7146",
- };
- saa7146_i2c_adapter_prepare(dev, &dpc->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
- if(i2c_add_adapter(&dpc->i2c_adapter) < 0) {
- DEB_S(("cannot register i2c-device. skipping.\n"));
- kfree(dpc);
- return -EFAULT;
- }
-
- /* loop through all i2c-devices on the bus and look who is there */
- device_for_each_child(&dpc->i2c_adapter.dev, dpc, dpc_check_clients);
-
- /* check if all devices are present */
- if (!dpc->saa7111a) {
- DEB_D(("dpc_v4l2.o: dpc_attach failed for this device.\n"));
- i2c_del_adapter(&dpc->i2c_adapter);
- kfree(dpc);
- return -ENODEV;
- }
-
- /* all devices are present, probe was successful */
- DEB_D(("dpc_v4l2.o: dpc_probe succeeded for this device.\n"));
-
- /* we store the pointer in our private data field */
- dev->ext_priv = dpc;
-
- return 0;
-}
-
-/* bring hardware to a sane state. this has to be done, just in case someone
- wants to capture from this device before it has been properly initialized.
- the capture engine would badly fail, because no valid signal arrives on the
- saa7146, thus leading to timeouts and stuff. */
-static int dpc_init_done(struct saa7146_dev* dev)
-{
- struct dpc* dpc = (struct dpc*)dev->ext_priv;
-
- DEB_D(("dpc_v4l2.o: dpc_init_done called.\n"));
-
- /* initialize the helper ics to useful values */
- i2c_smbus_write_byte_data(dpc->saa7111a, 0x00, 0x11);
-
- i2c_smbus_write_byte_data(dpc->saa7111a, 0x02, 0xc0);
- i2c_smbus_write_byte_data(dpc->saa7111a, 0x03, 0x30);
- i2c_smbus_write_byte_data(dpc->saa7111a, 0x04, 0x00);
- i2c_smbus_write_byte_data(dpc->saa7111a, 0x05, 0x00);
- i2c_smbus_write_byte_data(dpc->saa7111a, 0x06, 0xde);
- i2c_smbus_write_byte_data(dpc->saa7111a, 0x07, 0xad);
- i2c_smbus_write_byte_data(dpc->saa7111a, 0x08, 0xa8);
- i2c_smbus_write_byte_data(dpc->saa7111a, 0x09, 0x00);
- i2c_smbus_write_byte_data(dpc->saa7111a, 0x0a, 0x80);
- i2c_smbus_write_byte_data(dpc->saa7111a, 0x0b, 0x47);
- i2c_smbus_write_byte_data(dpc->saa7111a, 0x0c, 0x40);
- i2c_smbus_write_byte_data(dpc->saa7111a, 0x0d, 0x00);
- i2c_smbus_write_byte_data(dpc->saa7111a, 0x0e, 0x03);
-
- i2c_smbus_write_byte_data(dpc->saa7111a, 0x10, 0xd0);
- i2c_smbus_write_byte_data(dpc->saa7111a, 0x11, 0x1c);
- i2c_smbus_write_byte_data(dpc->saa7111a, 0x12, 0xc1);
- i2c_smbus_write_byte_data(dpc->saa7111a, 0x13, 0x30);
-
- i2c_smbus_write_byte_data(dpc->saa7111a, 0x1f, 0x81);
-
- return 0;
-}
-
-static struct saa7146_ext_vv vv_data;
-
-/* this function only gets called when the probing was successful */
-static int dpc_attach(struct saa7146_dev* dev, struct saa7146_pci_extension_data *info)
-{
- struct dpc* dpc = (struct dpc*)dev->ext_priv;
-
- DEB_D(("dpc_v4l2.o: dpc_attach called.\n"));
-
- /* checking for i2c-devices can be omitted here, because we
- already did this in "dpc_vl42_probe" */
-
- saa7146_vv_init(dev,&vv_data);
- if( 0 != saa7146_register_device(&dpc->video_dev, dev, "dpc", VFL_TYPE_GRABBER)) {
- ERR(("cannot register capture v4l2 device. skipping.\n"));
- return -1;
- }
-
- /* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/
- if( 0 != DPC_BOARD_CAN_DO_VBI(dev)) {
- if( 0 != saa7146_register_device(&dpc->vbi_dev, dev, "dpc", VFL_TYPE_VBI)) {
- ERR(("cannot register vbi v4l2 device. skipping.\n"));
- }
- }
-
- i2c_use_client(dpc->saa7111a);
-
- printk("dpc: found 'dpc7146 demonstration board'-%d.\n",dpc_num);
- dpc_num++;
-
- /* the rest */
- dpc->cur_input = 0;
- dpc_init_done(dev);
-
- return 0;
-}
-
-static int dpc_detach(struct saa7146_dev* dev)
-{
- struct dpc* dpc = (struct dpc*)dev->ext_priv;
-
- DEB_EE(("dev:%p\n",dev));
-
- i2c_release_client(dpc->saa7111a);
-
- saa7146_unregister_device(&dpc->video_dev,dev);
- if( 0 != DPC_BOARD_CAN_DO_VBI(dev)) {
- saa7146_unregister_device(&dpc->vbi_dev,dev);
- }
- saa7146_vv_release(dev);
-
- dpc_num--;
-
- i2c_del_adapter(&dpc->i2c_adapter);
- kfree(dpc);
- return 0;
-}
-
-#ifdef axa
-int dpc_vbi_bypass(struct saa7146_dev* dev)
-{
- struct dpc* dpc = (struct dpc*)dev->ext_priv;
-
- int i = 1;
-
- /* switch bypass in saa7111a */
- if ( 0 != dpc->saa7111a->driver->command(dpc->saa7111a,SAA711X_VBI_BYPASS, &i)) {
- printk("dpc_v4l2.o: VBI_BYPASS: could not address saa7111a.\n");
- return -1;
- }
-
- return 0;
-}
-#endif
-
-static int dpc_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg)
-{
- struct saa7146_dev *dev = fh->dev;
- struct dpc* dpc = (struct dpc*)dev->ext_priv;
-/*
- struct saa7146_vv *vv = dev->vv_data;
-*/
- switch(cmd)
- {
- case VIDIOC_ENUMINPUT:
- {
- struct v4l2_input *i = arg;
- DEB_EE(("VIDIOC_ENUMINPUT %d.\n",i->index));
-
- if( i->index < 0 || i->index >= DPC_INPUTS) {
- return -EINVAL;
- }
-
- memcpy(i, &dpc_inputs[i->index], sizeof(struct v4l2_input));
-
- DEB_D(("dpc_v4l2.o: v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n",i->index));
- return 0;
- }
- case VIDIOC_G_INPUT:
- {
- int *input = (int *)arg;
- *input = dpc->cur_input;
-
- DEB_D(("dpc_v4l2.o: VIDIOC_G_INPUT: %d\n",*input));
- return 0;
- }
- case VIDIOC_S_INPUT:
- {
- int input = *(int *)arg;
-
- if (input < 0 || input >= DPC_INPUTS) {
- return -EINVAL;
- }
-
- dpc->cur_input = input;
-
- /* fixme: switch input here, switch audio, too! */
-// saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source, input_port_selection[input].hps_sync);
- printk("dpc_v4l2.o: VIDIOC_S_INPUT: fixme switch input.\n");
-
- return 0;
- }
- default:
-/*
- DEB_D(("dpc_v4l2.o: v4l2_ioctl does not handle this ioctl.\n"));
-*/
- return -ENOIOCTLCMD;
- }
- return 0;
-}
-
-static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std)
-{
- return 0;
-}
-
-static struct saa7146_standard standard[] = {
- {
- .name = "PAL", .id = V4L2_STD_PAL,
- .v_offset = 0x17, .v_field = 288,
- .h_offset = 0x14, .h_pixels = 680,
- .v_max_out = 576, .h_max_out = 768,
- }, {
- .name = "NTSC", .id = V4L2_STD_NTSC,
- .v_offset = 0x16, .v_field = 240,
- .h_offset = 0x06, .h_pixels = 708,
- .v_max_out = 480, .h_max_out = 640,
- }, {
- .name = "SECAM", .id = V4L2_STD_SECAM,
- .v_offset = 0x14, .v_field = 288,
- .h_offset = 0x14, .h_pixels = 720,
- .v_max_out = 576, .h_max_out = 768,
- }
-};
-
-static struct saa7146_extension extension;
-
-static struct saa7146_pci_extension_data dpc = {
- .ext_priv = "Multimedia eXtension Board",
- .ext = &extension,
-};
-
-static struct pci_device_id pci_tbl[] = {
- {
- .vendor = PCI_VENDOR_ID_PHILIPS,
- .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
- .subvendor = 0x0000,
- .subdevice = 0x0000,
- .driver_data = (unsigned long)&dpc,
- }, {
- .vendor = 0,
- }
-};
-
-MODULE_DEVICE_TABLE(pci, pci_tbl);
-
-static struct saa7146_ext_vv vv_data = {
- .inputs = DPC_INPUTS,
- .capabilities = V4L2_CAP_VBI_CAPTURE,
- .stds = &standard[0],
- .num_stds = sizeof(standard)/sizeof(struct saa7146_standard),
- .std_callback = &std_callback,
- .ioctls = &ioctls[0],
- .ioctl = dpc_ioctl,
-};
-
-static struct saa7146_extension extension = {
- .name = "dpc7146 demonstration board",
- .flags = SAA7146_USE_I2C_IRQ,
-
- .pci_tbl = &pci_tbl[0],
- .module = THIS_MODULE,
-
- .probe = dpc_probe,
- .attach = dpc_attach,
- .detach = dpc_detach,
-
- .irq_mask = 0,
- .irq_func = NULL,
-};
-
-static int __init dpc_init_module(void)
-{
- if( 0 != saa7146_register_extension(&extension)) {
- DEB_S(("failed to register extension.\n"));
- return -ENODEV;
- }
-
- return 0;
-}
-
-static void __exit dpc_cleanup_module(void)
-{
- saa7146_unregister_extension(&extension);
-}
-
-module_init(dpc_init_module);
-module_exit(dpc_cleanup_module);
-
-MODULE_DESCRIPTION("video4linux-2 driver for the 'dpc7146 demonstration board'");
-MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
index de943cf..d65d057 100644
--- a/drivers/media/video/em28xx/em28xx-cards.c
+++ b/drivers/media/video/em28xx/em28xx-cards.c
@@ -1101,7 +1101,7 @@
{ USB_DEVICE(0xeb1a, 0x2820),
.driver_info = EM2820_BOARD_UNKNOWN },
{ USB_DEVICE(0xeb1a, 0x2821),
- .driver_info = EM2820_BOARD_UNKNOWN },
+ .driver_info = EM2820_BOARD_PROLINK_PLAYTV_USB2 },
{ USB_DEVICE(0xeb1a, 0x2860),
.driver_info = EM2820_BOARD_UNKNOWN },
{ USB_DEVICE(0xeb1a, 0x2861),
@@ -1271,7 +1271,7 @@
{0x1ba50080, EM2860_BOARD_POINTNIX_INTRAORAL_CAMERA, TUNER_ABSENT},
};
-int em28xx_tuner_callback(void *ptr, int command, int arg)
+int em28xx_tuner_callback(void *ptr, int component, int command, int arg)
{
int rc = 0;
struct em28xx *dev = ptr;
diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c
index d2b1a1a..c99e238 100644
--- a/drivers/media/video/em28xx/em28xx-dvb.c
+++ b/drivers/media/video/em28xx/em28xx-dvb.c
@@ -249,7 +249,6 @@
memset(&cfg, 0, sizeof(cfg));
cfg.i2c_adap = &dev->i2c_adap;
cfg.i2c_addr = addr;
- cfg.callback = em28xx_tuner_callback;
if (!dev->dvb->frontend) {
printk(KERN_ERR "%s/2: dvb frontend not attached. "
@@ -274,7 +273,7 @@
/* ------------------------------------------------------------------ */
-int register_dvb(struct em28xx_dvb *dvb,
+static int register_dvb(struct em28xx_dvb *dvb,
struct module *module,
struct em28xx *dev,
struct device *device)
@@ -422,6 +421,8 @@
}
break;
case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
+ case EM2880_BOARD_TERRATEC_HYBRID_XS:
+ case EM2880_BOARD_KWORLD_DVB_310U:
dvb->frontend = dvb_attach(zl10353_attach,
&em28xx_zl10353_with_xc3028,
&dev->i2c_adap);
@@ -443,24 +444,6 @@
}
break;
#endif
- case EM2880_BOARD_TERRATEC_HYBRID_XS:
- dvb->frontend = dvb_attach(zl10353_attach,
- &em28xx_zl10353_with_xc3028,
- &dev->i2c_adap);
- if (attach_xc3028(0x61, dev) < 0) {
- result = -EINVAL;
- goto out_free;
- }
- break;
- case EM2880_BOARD_KWORLD_DVB_310U:
- dvb->frontend = dvb_attach(zl10353_attach,
- &em28xx_zl10353_with_xc3028,
- &dev->i2c_adap);
- if (attach_xc3028(0x61, dev) < 0) {
- result = -EINVAL;
- goto out_free;
- }
- break;
default:
printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card"
" isn't supported yet\n",
@@ -474,6 +457,8 @@
result = -EINVAL;
goto out_free;
}
+ /* define general-purpose callback pointer */
+ dvb->frontend->callback = em28xx_tuner_callback;
/* register everything */
result = register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev);
diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c
index 9785338..3bab56b 100644
--- a/drivers/media/video/em28xx/em28xx-i2c.c
+++ b/drivers/media/video/em28xx/em28xx-i2c.c
@@ -143,10 +143,11 @@
}
for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0;
write_timeout -= 5) {
- unsigned msg = dev->em28xx_read_reg(dev, 0x5);
- if (msg == 0x94)
+ unsigned reg = dev->em28xx_read_reg(dev, 0x5);
+
+ if (reg == 0x94)
return -ENODEV;
- else if (msg == 0x84)
+ else if (reg == 0x84)
return 0;
msleep(5);
}
@@ -335,8 +336,11 @@
/* Check if board has eeprom */
err = i2c_master_recv(&dev->i2c_client, &buf, 0);
- if (err < 0)
- return -1;
+ if (err < 0) {
+ em28xx_errdev("%s: i2c_master_recv failed! err [%d]\n",
+ __func__, err);
+ return err;
+ }
buf = 0;
@@ -344,7 +348,7 @@
if (err != 1) {
printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n",
dev->name, err);
- return -1;
+ return err;
}
while (size > 0) {
if (size > 16)
@@ -357,7 +361,7 @@
printk(KERN_WARNING
"%s: i2c eeprom read error (err=%d)\n",
dev->name, err);
- return -1;
+ return err;
}
size -= block;
p += block;
@@ -585,18 +589,31 @@
*/
int em28xx_i2c_register(struct em28xx *dev)
{
+ int retval;
+
BUG_ON(!dev->em28xx_write_regs || !dev->em28xx_read_reg);
BUG_ON(!dev->em28xx_write_regs_req || !dev->em28xx_read_reg_req);
dev->i2c_adap = em28xx_adap_template;
dev->i2c_adap.dev.parent = &dev->udev->dev;
strcpy(dev->i2c_adap.name, dev->name);
dev->i2c_adap.algo_data = dev;
- i2c_add_adapter(&dev->i2c_adap);
+
+ retval = i2c_add_adapter(&dev->i2c_adap);
+ if (retval < 0) {
+ em28xx_errdev("%s: i2c_add_adapter failed! retval [%d]\n",
+ __func__, retval);
+ return retval;
+ }
dev->i2c_client = em28xx_client_template;
dev->i2c_client.adapter = &dev->i2c_adap;
- em28xx_i2c_eeprom(dev, dev->eedata, sizeof(dev->eedata));
+ retval = em28xx_i2c_eeprom(dev, dev->eedata, sizeof(dev->eedata));
+ if (retval < 0) {
+ em28xx_errdev("%s: em28xx_i2_eeprom failed! retval [%d]\n",
+ __func__, retval);
+ return retval;
+ }
if (i2c_scan)
em28xx_do_i2c_scan(dev);
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index 49ab062..c53649e 100644
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -513,10 +513,17 @@
*/
static int em28xx_config(struct em28xx *dev)
{
+ int retval;
/* Sets I2C speed to 100 KHz */
- if (!dev->is_em2800)
- em28xx_write_regs_req(dev, 0x00, 0x06, "\x40", 1);
+ if (!dev->is_em2800) {
+ retval = em28xx_write_regs_req(dev, 0x00, 0x06, "\x40", 1);
+ if (retval < 0) {
+ em28xx_errdev("%s: em28xx_write_regs_req failed! retval [%d]\n",
+ __func__, retval);
+ return retval;
+ }
+ }
/* enable vbi capturing */
@@ -1512,6 +1519,7 @@
struct em28xx_fh *fh;
enum v4l2_buf_type fh_type = 0;
+ lock_kernel();
list_for_each_entry(h, &em28xx_devlist, devlist) {
if (h->vdev->minor == minor) {
dev = h;
@@ -1527,8 +1535,10 @@
dev = h;
}
}
- if (NULL == dev)
+ if (NULL == dev) {
+ unlock_kernel();
return -ENODEV;
+ }
em28xx_videodbg("open minor=%d type=%s users=%d\n",
minor, v4l2_type_names[fh_type], dev->users);
@@ -1537,6 +1547,7 @@
fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL);
if (!fh) {
em28xx_errdev("em28xx-video.c: Out of memory?!\n");
+ unlock_kernel();
return -ENOMEM;
}
mutex_lock(&dev->lock);
@@ -1573,6 +1584,7 @@
sizeof(struct em28xx_buffer), fh);
mutex_unlock(&dev->lock);
+ unlock_kernel();
return errCode;
}
@@ -1588,8 +1600,7 @@
/*FIXME: I2C IR should be disconnected */
em28xx_info("V4L2 devices /dev/video%d and /dev/vbi%d deregistered\n",
- dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN,
- dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN);
+ dev->vdev->num, dev->vbi_dev->num);
list_del(&dev->devlist);
if (dev->sbutton_input_dev)
em28xx_deregister_snapshot_button(dev);
@@ -1948,13 +1959,23 @@
}
/* register i2c bus */
- em28xx_i2c_register(dev);
+ errCode = em28xx_i2c_register(dev);
+ if (errCode < 0) {
+ em28xx_errdev("%s: em28xx_i2c_register - errCode [%d]!\n",
+ __func__, errCode);
+ return errCode;
+ }
/* Do board specific init and eeprom reading */
em28xx_card_setup(dev);
/* Configure audio */
- em28xx_audio_analog_set(dev);
+ errCode = em28xx_audio_analog_set(dev);
+ if (errCode < 0) {
+ em28xx_errdev("%s: em28xx_audio_analog_set - errCode [%d]!\n",
+ __func__, errCode);
+ return errCode;
+ }
/* configure the device */
em28xx_config_i2c(dev);
@@ -1974,6 +1995,11 @@
dev->ctl_input = 2;
errCode = em28xx_config(dev);
+ if (errCode < 0) {
+ em28xx_errdev("%s: em28xx_config - errCode [%d]!\n",
+ __func__, errCode);
+ return errCode;
+ }
list_add_tail(&dev->devlist, &em28xx_devlist);
@@ -2026,17 +2052,27 @@
if (dev->has_msp34xx) {
/* Send a reset to other chips via gpio */
- em28xx_write_regs_req(dev, 0x00, 0x08, "\xf7", 1);
+ errCode = em28xx_write_regs_req(dev, 0x00, 0x08, "\xf7", 1);
+ if (errCode < 0) {
+ em28xx_errdev("%s: em28xx_write_regs_req - msp34xx(1) failed! errCode [%d]\n",
+ __func__, errCode);
+ return errCode;
+ }
msleep(3);
- em28xx_write_regs_req(dev, 0x00, 0x08, "\xff", 1);
+
+ errCode = em28xx_write_regs_req(dev, 0x00, 0x08, "\xff", 1);
+ if (errCode < 0) {
+ em28xx_errdev("%s: em28xx_write_regs_req - msp34xx(2) failed! errCode [%d]\n",
+ __func__, errCode);
+ return errCode;
+ }
msleep(3);
}
video_mux(dev, 0);
em28xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n",
- dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN,
- dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN);
+ dev->vdev->num, dev->vbi_dev->num);
mutex_lock(&em28xx_extension_devlist_lock);
if (!list_empty(&em28xx_extension_devlist)) {
@@ -2236,7 +2272,7 @@
em28xx_warn
("device /dev/video%d is open! Deregistration and memory "
"deallocation are deferred on close.\n",
- dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN);
+ dev->vdev->num);
dev->state |= DEV_MISCONFIGURED;
em28xx_uninit_isoc(dev);
diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
index 9a33107..8278117 100644
--- a/drivers/media/video/em28xx/em28xx.h
+++ b/drivers/media/video/em28xx/em28xx.h
@@ -411,8 +411,8 @@
/* frame properties */
int width; /* current frame width */
int height; /* current frame height */
- int hscale; /* horizontal scale factor (see datasheet) */
- int vscale; /* vertical scale factor (see datasheet) */
+ unsigned hscale; /* horizontal scale factor (see datasheet) */
+ unsigned vscale; /* vertical scale factor (see datasheet) */
int interlaced; /* 1=interlace fileds, 0=just top fileds */
unsigned int video_bytesread; /* Number of bytes read */
@@ -528,7 +528,7 @@
extern struct usb_device_id em28xx_id_table[];
extern const unsigned int em28xx_bcount;
void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir);
-int em28xx_tuner_callback(void *ptr, int command, int arg);
+int em28xx_tuner_callback(void *ptr, int component, int command, int arg);
/* Provided by em28xx-input.c */
/* TODO: Check if the standard get_key handlers on ir-common can be used */
diff --git a/drivers/media/video/et61x251/et61x251_core.c b/drivers/media/video/et61x251/et61x251_core.c
index 8db2a05..7a85c41 100644
--- a/drivers/media/video/et61x251/et61x251_core.c
+++ b/drivers/media/video/et61x251/et61x251_core.c
@@ -1214,7 +1214,7 @@
if (!down_read_trylock(&et61x251_dev_lock))
return -ERESTARTSYS;
- cam = video_get_drvdata(video_devdata(filp));
+ cam = video_drvdata(filp);
if (wait_for_completion_interruptible(&cam->probe)) {
up_read(&et61x251_dev_lock);
@@ -1297,7 +1297,7 @@
down_write(&et61x251_dev_lock);
- cam = video_get_drvdata(video_devdata(filp));
+ cam = video_drvdata(filp);
et61x251_stop_transfer(cam);
et61x251_release_buffers(cam);
@@ -1318,7 +1318,7 @@
et61x251_read(struct file* filp, char __user * buf,
size_t count, loff_t* f_pos)
{
- struct et61x251_device* cam = video_get_drvdata(video_devdata(filp));
+ struct et61x251_device *cam = video_drvdata(filp);
struct et61x251_frame_t* f, * i;
unsigned long lock_flags;
long timeout;
@@ -1426,7 +1426,7 @@
static unsigned int et61x251_poll(struct file *filp, poll_table *wait)
{
- struct et61x251_device* cam = video_get_drvdata(video_devdata(filp));
+ struct et61x251_device *cam = video_drvdata(filp);
struct et61x251_frame_t* f;
unsigned long lock_flags;
unsigned int mask = 0;
@@ -1502,7 +1502,7 @@
static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma)
{
- struct et61x251_device* cam = video_get_drvdata(video_devdata(filp));
+ struct et61x251_device *cam = video_drvdata(filp);
unsigned long size = vma->vm_end - vma->vm_start,
start = vma->vm_start;
void *pos;
@@ -2395,7 +2395,7 @@
static int et61x251_ioctl_v4l2(struct inode* inode, struct file* filp,
unsigned int cmd, void __user * arg)
{
- struct et61x251_device* cam = video_get_drvdata(video_devdata(filp));
+ struct et61x251_device *cam = video_drvdata(filp);
switch (cmd) {
@@ -2490,7 +2490,7 @@
static int et61x251_ioctl(struct inode* inode, struct file* filp,
unsigned int cmd, unsigned long arg)
{
- struct et61x251_device* cam = video_get_drvdata(video_devdata(filp));
+ struct et61x251_device *cam = video_drvdata(filp);
int err = 0;
if (mutex_lock_interruptible(&cam->fileop_mutex))
diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig
index 42b9074..4d08174 100644
--- a/drivers/media/video/gspca/Kconfig
+++ b/drivers/media/video/gspca/Kconfig
@@ -1,13 +1,212 @@
-config USB_GSPCA
- tristate "USB GSPCA driver"
+menuconfig USB_GSPCA
+ tristate "GSPCA based webcams"
depends on VIDEO_V4L2
+ default m
---help---
- Say Y here if you want support for various USB webcams.
+ Say Y here if you want to enable selecting webcams based
+ on the GSPCA framework.
- See <file:Documentation/video4linux/gspca.txt> for more info.
+ See <file:Documentation/video4linux/gspca.txt> for more info.
- This driver uses the Video For Linux API. You must say Y or M to
- "Video For Linux" to use this driver.
+ This driver uses the Video For Linux API. You must say Y or M to
+ "Video For Linux" to use this driver.
- To compile this driver as modules, choose M here: the
- modules will be called gspca_xxxx.
+ To compile this driver as modules, choose M here: the
+ modules will be called gspca_main.
+
+
+if USB_GSPCA && VIDEO_V4L2
+
+source "drivers/media/video/gspca/m5602/Kconfig"
+
+config USB_GSPCA_CONEX
+ tristate "Conexant Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the Conexant chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_conex.
+
+config USB_GSPCA_ETOMS
+ tristate "Etoms USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the Etoms chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_etoms.
+
+config USB_GSPCA_FINEPIX
+ tristate "Fujifilm FinePix USB V4L2 driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the FinePix chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_finepix.
+
+config USB_GSPCA_MARS
+ tristate "Mars USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the Mars chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_mars.
+
+config USB_GSPCA_OV519
+ tristate "OV519 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the OV519 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_ov519.
+
+config USB_GSPCA_PAC207
+ tristate "Pixart PAC207 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the PAC207 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_pac207.
+
+config USB_GSPCA_PAC7311
+ tristate "Pixart PAC7311 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the PAC7311 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_pac7311.
+
+config USB_GSPCA_SONIXB
+ tristate "SN9C102 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SONIXB chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_sonixb.
+
+config USB_GSPCA_SONIXJ
+ tristate "SONIX JPEG USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SONIXJ chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_sonixj
+
+config USB_GSPCA_SPCA500
+ tristate "SPCA500 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SPCA500 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca500.
+
+config USB_GSPCA_SPCA501
+ tristate "SPCA501 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SPCA501 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca501.
+
+config USB_GSPCA_SPCA505
+ tristate "SPCA505 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SPCA505 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca505.
+
+config USB_GSPCA_SPCA506
+ tristate "SPCA506 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SPCA506 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca506.
+
+config USB_GSPCA_SPCA508
+ tristate "SPCA508 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SPCA508 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca508.
+
+config USB_GSPCA_SPCA561
+ tristate "SPCA561 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SPCA561 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca561.
+
+config USB_GSPCA_STK014
+ tristate "Syntek DV4000 (STK014) USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the STK014 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_stk014.
+
+config USB_GSPCA_SUNPLUS
+ tristate "SUNPLUS USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the Sunplus
+ SPCA504(abc) SPCA533 SPCA536 chips.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca5xx.
+
+config USB_GSPCA_T613
+ tristate "T613 (JPEG Compliance) USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the T613 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_t613.
+
+config USB_GSPCA_TV8532
+ tristate "TV8532 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the TV8531 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_tv8532.
+
+config USB_GSPCA_VC032X
+ tristate "VC032X USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the VC032X chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_vc032x.
+
+config USB_GSPCA_ZC3XX
+ tristate "VC3xx USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the ZC3XX chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_zc3xx.
+
+endif
diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile
index e68a896..22734f5 100644
--- a/drivers/media/video/gspca/Makefile
+++ b/drivers/media/video/gspca/Makefile
@@ -1,29 +1,48 @@
-obj-$(CONFIG_USB_GSPCA) += gspca_main.o \
- gspca_conex.o gspca_etoms.o gspca_mars.o \
- gspca_ov519.o gspca_pac207.o gspca_pac7311.o \
- gspca_sonixb.o gspca_sonixj.o gspca_spca500.o gspca_spca501.o \
- gspca_spca505.o gspca_spca506.o gspca_spca508.o gspca_spca561.o \
- gspca_sunplus.o gspca_stk014.o gspca_t613.o gspca_tv8532.o \
- gspca_vc032x.o gspca_zc3xx.o
+obj-$(CONFIG_USB_GSPCA) += gspca_main.o
+obj-$(CONFIG_USB_GSPCA_CONEX) += gspca_conex.o
+obj-$(CONFIG_USB_GSPCA_ETOMS) += gspca_etoms.o
+obj-$(CONFIG_USB_GSPCA_FINEPIX) += gspca_finepix.o
+obj-$(CONFIG_USB_GSPCA_MARS) += gspca_mars.o
+obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o
+obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o
+obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.o
+obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o
+obj-$(CONFIG_USB_GSPCA_SONIXJ) += gspca_sonixj.o
+obj-$(CONFIG_USB_GSPCA_SPCA500) += gspca_spca500.o
+obj-$(CONFIG_USB_GSPCA_SPCA501) += gspca_spca501.o
+obj-$(CONFIG_USB_GSPCA_SPCA505) += gspca_spca505.o
+obj-$(CONFIG_USB_GSPCA_SPCA506) += gspca_spca506.o
+obj-$(CONFIG_USB_GSPCA_SPCA508) += gspca_spca508.o
+obj-$(CONFIG_USB_GSPCA_SPCA561) += gspca_spca561.o
+obj-$(CONFIG_USB_GSPCA_SUNPLUS) += gspca_sunplus.o
+obj-$(CONFIG_USB_GSPCA_STK014) += gspca_stk014.o
+obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o
+obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o
+obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o
+obj-$(CONFIG_USB_GSPCA_ZC3XX) += gspca_zc3xx.o
-gspca_main-objs := gspca.o
-gspca_conex-objs := conex.o
-gspca_etoms-objs := etoms.o
-gspca_mars-objs := mars.o
-gspca_ov519-objs := ov519.o
-gspca_pac207-objs := pac207.o
-gspca_pac7311-objs := pac7311.o
-gspca_sonixb-objs := sonixb.o
-gspca_sonixj-objs := sonixj.o
-gspca_spca500-objs := spca500.o
-gspca_spca501-objs := spca501.o
-gspca_spca505-objs := spca505.o
-gspca_spca506-objs := spca506.o
-gspca_spca508-objs := spca508.o
-gspca_spca561-objs := spca561.o
-gspca_stk014-objs := stk014.o
-gspca_sunplus-objs := sunplus.o
-gspca_t613-objs := t613.o
-gspca_tv8532-objs := tv8532.o
-gspca_vc032x-objs := vc032x.o
-gspca_zc3xx-objs := zc3xx.o
+gspca_main-objs := gspca.o
+gspca_conex-objs := conex.o
+gspca_etoms-objs := etoms.o
+gspca_finepix-objs := finepix.o
+gspca_mars-objs := mars.o
+gspca_ov519-objs := ov519.o
+gspca_pac207-objs := pac207.o
+gspca_pac7311-objs := pac7311.o
+gspca_sonixb-objs := sonixb.o
+gspca_sonixj-objs := sonixj.o
+gspca_spca500-objs := spca500.o
+gspca_spca501-objs := spca501.o
+gspca_spca505-objs := spca505.o
+gspca_spca506-objs := spca506.o
+gspca_spca508-objs := spca508.o
+gspca_spca561-objs := spca561.o
+gspca_stk014-objs := stk014.o
+gspca_sunplus-objs := sunplus.o
+gspca_t613-objs := t613.o
+gspca_tv8532-objs := tv8532.o
+gspca_vc032x-objs := vc032x.o
+gspca_zc3xx-objs := zc3xx.o
+
+obj-$(CONFIG_USB_M5602) += m5602/
+
diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c
index 4d9f4cc..a9d51ba 100644
--- a/drivers/media/video/gspca/conex.c
+++ b/drivers/media/video/gspca/conex.c
@@ -837,12 +837,13 @@
return 0;
}
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
cx11646_initsize(gspca_dev);
cx11646_fw(gspca_dev);
cx_sensor(gspca_dev);
cx11646_jpeg(gspca_dev);
+ return 0;
}
static void sd_stop0(struct gspca_dev *gspca_dev)
diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/video/gspca/etoms.c
index 4ff0e38..3be30b4 100644
--- a/drivers/media/video/gspca/etoms.c
+++ b/drivers/media/video/gspca/etoms.c
@@ -691,7 +691,7 @@
}
/* -- start the camera -- */
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -704,6 +704,7 @@
reg_w_val(gspca_dev, ET_RESET_ALL, 0x08);
et_video(gspca_dev, 1); /* video on */
+ return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/drivers/media/video/gspca/finepix.c b/drivers/media/video/gspca/finepix.c
new file mode 100644
index 0000000..65d3cbf
--- /dev/null
+++ b/drivers/media/video/gspca/finepix.c
@@ -0,0 +1,466 @@
+/*
+ * Fujifilm Finepix subdriver
+ *
+ * Copyright (C) 2008 Frank Zago
+ *
+ * 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
+ * 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
+ */
+
+#define MODULE_NAME "finepix"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Frank Zago <frank@zago.net>");
+MODULE_DESCRIPTION("Fujifilm FinePix USB V4L2 driver");
+MODULE_LICENSE("GPL");
+
+/* Default timeout, in ms */
+#define FPIX_TIMEOUT (HZ / 10)
+
+/* Maximum transfer size to use. The windows driver reads by chunks of
+ * 0x2000 bytes, so do the same. Note: reading more seems to work
+ * too. */
+#define FPIX_MAX_TRANSFER 0x2000
+
+/* Structure to hold all of our device specific stuff */
+struct usb_fpix {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ /*
+ * USB stuff
+ */
+ struct usb_ctrlrequest ctrlreq;
+ struct urb *control_urb;
+ struct timer_list bulk_timer;
+
+ enum {
+ FPIX_NOP, /* inactive, else streaming */
+ FPIX_RESET, /* must reset */
+ FPIX_REQ_FRAME, /* requesting a frame */
+ FPIX_READ_FRAME, /* reading frame */
+ } state;
+
+ /*
+ * Driver stuff
+ */
+ struct delayed_work wqe;
+ struct completion can_close;
+ int streaming;
+};
+
+/* Delay after which claim the next frame. If the delay is too small,
+ * the camera will return old frames. On the 4800Z, 20ms is bad, 25ms
+ * will fail every 4 or 5 frames, but 30ms is perfect. */
+#define NEXT_FRAME_DELAY (((HZ * 30) + 999) / 1000)
+
+#define dev_new_state(new_state) { \
+ PDEBUG(D_STREAM, "new state from %d to %d at %s:%d", \
+ dev->state, new_state, __func__, __LINE__); \
+ dev->state = new_state; \
+}
+
+/* These cameras only support 320x200. */
+static struct v4l2_pix_format fpix_mode[1] = {
+ { 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0}
+};
+
+/* Reads part of a frame */
+static void read_frame_part(struct usb_fpix *dev)
+{
+ int ret;
+
+ PDEBUG(D_STREAM, "read_frame_part");
+
+ /* Reads part of a frame */
+ ret = usb_submit_urb(dev->gspca_dev.urb[0], GFP_ATOMIC);
+ if (ret) {
+ dev_new_state(FPIX_RESET);
+ schedule_delayed_work(&dev->wqe, 1);
+ PDEBUG(D_STREAM, "usb_submit_urb failed with %d",
+ ret);
+ } else {
+ /* Sometimes we never get a callback, so use a timer.
+ * Is this masking a bug somewhere else? */
+ dev->bulk_timer.expires = jiffies + msecs_to_jiffies(150);
+ add_timer(&dev->bulk_timer);
+ }
+}
+
+/* Callback for URBs. */
+static void urb_callback(struct urb *urb)
+{
+ struct gspca_dev *gspca_dev = urb->context;
+ struct usb_fpix *dev = (struct usb_fpix *) gspca_dev;
+
+ PDEBUG(D_PACK,
+ "enter urb_callback - status=%d, length=%d",
+ urb->status, urb->actual_length);
+
+ if (dev->state == FPIX_READ_FRAME)
+ del_timer(&dev->bulk_timer);
+
+ if (urb->status != 0) {
+ /* We kill a stuck urb every 50 frames on average, so don't
+ * display a log message for that. */
+ if (urb->status != -ECONNRESET)
+ PDEBUG(D_STREAM, "bad URB status %d", urb->status);
+ dev_new_state(FPIX_RESET);
+ schedule_delayed_work(&dev->wqe, 1);
+ }
+
+ switch (dev->state) {
+ case FPIX_REQ_FRAME:
+ dev_new_state(FPIX_READ_FRAME);
+ read_frame_part(dev);
+ break;
+
+ case FPIX_READ_FRAME: {
+ unsigned char *data = urb->transfer_buffer;
+ struct gspca_frame *frame;
+
+ frame = gspca_get_i_frame(&dev->gspca_dev);
+ if (frame == NULL)
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ if (urb->actual_length < FPIX_MAX_TRANSFER ||
+ (data[urb->actual_length-2] == 0xff &&
+ data[urb->actual_length-1] == 0xd9)) {
+
+ /* If the result is less than what was asked
+ * for, then it's the end of the
+ * frame. Sometime the jpeg is not complete,
+ * but there's nothing we can do. We also end
+ * here if the the jpeg ends right at the end
+ * of the frame. */
+ if (frame)
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ frame,
+ data, urb->actual_length);
+ dev_new_state(FPIX_REQ_FRAME);
+ schedule_delayed_work(&dev->wqe, NEXT_FRAME_DELAY);
+ } else {
+
+ /* got a partial image */
+ if (frame)
+ gspca_frame_add(gspca_dev,
+ gspca_dev->last_packet_type
+ == LAST_PACKET
+ ? FIRST_PACKET : INTER_PACKET,
+ frame,
+ data, urb->actual_length);
+ read_frame_part(dev);
+ }
+ break;
+ }
+
+ case FPIX_NOP:
+ case FPIX_RESET:
+ PDEBUG(D_STREAM, "invalid state %d", dev->state);
+ break;
+ }
+}
+
+/* Request a new frame */
+static void request_frame(struct usb_fpix *dev)
+{
+ int ret;
+ struct gspca_dev *gspca_dev = &dev->gspca_dev;
+
+ /* Setup command packet */
+ memset(gspca_dev->usb_buf, 0, 12);
+ gspca_dev->usb_buf[0] = 0xd3;
+ gspca_dev->usb_buf[7] = 0x01;
+
+ /* Request a frame */
+ dev->ctrlreq.bRequestType =
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+ dev->ctrlreq.bRequest = USB_REQ_GET_STATUS;
+ dev->ctrlreq.wValue = 0;
+ dev->ctrlreq.wIndex = 0;
+ dev->ctrlreq.wLength = cpu_to_le16(12);
+
+ usb_fill_control_urb(dev->control_urb,
+ gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ (unsigned char *) &dev->ctrlreq,
+ gspca_dev->usb_buf,
+ 12, urb_callback, gspca_dev);
+
+ ret = usb_submit_urb(dev->control_urb, GFP_ATOMIC);
+ if (ret) {
+ dev_new_state(FPIX_RESET);
+ schedule_delayed_work(&dev->wqe, 1);
+ PDEBUG(D_STREAM, "usb_submit_urb failed with %d", ret);
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+/* State machine. */
+static void fpix_sm(struct work_struct *work)
+{
+ struct usb_fpix *dev = container_of(work, struct usb_fpix, wqe.work);
+
+ PDEBUG(D_STREAM, "fpix_sm state %d", dev->state);
+
+ /* verify that the device wasn't unplugged */
+ if (!dev->gspca_dev.present) {
+ PDEBUG(D_STREAM, "device is gone");
+ dev_new_state(FPIX_NOP);
+ complete(&dev->can_close);
+ return;
+ }
+
+ if (!dev->streaming) {
+ PDEBUG(D_STREAM, "stopping state machine");
+ dev_new_state(FPIX_NOP);
+ complete(&dev->can_close);
+ return;
+ }
+
+ switch (dev->state) {
+ case FPIX_RESET:
+ dev_new_state(FPIX_REQ_FRAME);
+ schedule_delayed_work(&dev->wqe, HZ / 10);
+ break;
+
+ case FPIX_REQ_FRAME:
+ /* get an image */
+ request_frame(dev);
+ break;
+
+ case FPIX_NOP:
+ case FPIX_READ_FRAME:
+ PDEBUG(D_STREAM, "invalid state %d", dev->state);
+ break;
+ }
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct cam *cam = &gspca_dev->cam;
+
+ cam->cam_mode = fpix_mode;
+ cam->nmodes = 1;
+ cam->epaddr = 0x01; /* todo: correct for all cams? */
+ cam->bulk_size = FPIX_MAX_TRANSFER;
+
+/* gspca_dev->nbalt = 1; * use bulk transfer */
+ return 0;
+}
+
+/* Stop streaming and free the ressources allocated by sd_start. */
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct usb_fpix *dev = (struct usb_fpix *) gspca_dev;
+
+ dev->streaming = 0;
+
+ /* Stop the state machine */
+ if (dev->state != FPIX_NOP)
+ wait_for_completion(&dev->can_close);
+
+ usb_free_urb(dev->control_urb);
+ dev->control_urb = NULL;
+}
+
+/* Kill an URB that hasn't completed. */
+static void timeout_kill(unsigned long data)
+{
+ struct urb *urb = (struct urb *) data;
+
+ usb_unlink_urb(urb);
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct usb_fpix *dev = (struct usb_fpix *) gspca_dev;
+
+ INIT_DELAYED_WORK(&dev->wqe, fpix_sm);
+
+ init_timer(&dev->bulk_timer);
+ dev->bulk_timer.function = timeout_kill;
+
+ return 0;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct usb_fpix *dev = (struct usb_fpix *) gspca_dev;
+ int ret;
+ int size_ret;
+
+ /* Reset bulk in endpoint */
+ usb_clear_halt(gspca_dev->dev, gspca_dev->cam.epaddr);
+
+ /* Init the device */
+ memset(gspca_dev->usb_buf, 0, 12);
+ gspca_dev->usb_buf[0] = 0xc6;
+ gspca_dev->usb_buf[8] = 0x20;
+
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ USB_REQ_GET_STATUS,
+ USB_DIR_OUT | USB_TYPE_CLASS |
+ USB_RECIP_INTERFACE, 0, 0, gspca_dev->usb_buf,
+ 12, FPIX_TIMEOUT);
+
+ if (ret != 12) {
+ PDEBUG(D_STREAM, "usb_control_msg failed (%d)", ret);
+ ret = -EIO;
+ goto error;
+ }
+
+ /* Read the result of the command. Ignore the result, for it
+ * varies with the device. */
+ ret = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev,
+ gspca_dev->cam.epaddr),
+ gspca_dev->usb_buf, FPIX_MAX_TRANSFER, &size_ret,
+ FPIX_TIMEOUT);
+ if (ret != 0) {
+ PDEBUG(D_STREAM, "usb_bulk_msg failed (%d)", ret);
+ ret = -EIO;
+ goto error;
+ }
+
+ /* Request a frame, but don't read it */
+ memset(gspca_dev->usb_buf, 0, 12);
+ gspca_dev->usb_buf[0] = 0xd3;
+ gspca_dev->usb_buf[7] = 0x01;
+
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ USB_REQ_GET_STATUS,
+ USB_DIR_OUT | USB_TYPE_CLASS |
+ USB_RECIP_INTERFACE, 0, 0, gspca_dev->usb_buf,
+ 12, FPIX_TIMEOUT);
+ if (ret != 12) {
+ PDEBUG(D_STREAM, "usb_control_msg failed (%d)", ret);
+ ret = -EIO;
+ goto error;
+ }
+
+ /* Again, reset bulk in endpoint */
+ usb_clear_halt(gspca_dev->dev, gspca_dev->cam.epaddr);
+
+ /* Allocate a control URB */
+ dev->control_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->control_urb) {
+ PDEBUG(D_STREAM, "No free urbs available");
+ ret = -EIO;
+ goto error;
+ }
+
+ /* Various initializations. */
+ init_completion(&dev->can_close);
+ dev->bulk_timer.data = (unsigned long)dev->gspca_dev.urb[0];
+ dev->gspca_dev.urb[0]->complete = urb_callback;
+ dev->streaming = 1;
+
+ /* Schedule a frame request. */
+ dev_new_state(FPIX_REQ_FRAME);
+ schedule_delayed_work(&dev->wqe, 1);
+
+ return 0;
+
+error:
+ /* Free the ressources */
+ sd_stopN(gspca_dev);
+ return ret;
+}
+
+/* Table of supported USB devices */
+static const __devinitdata struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x04cb, 0x0104)},
+ {USB_DEVICE(0x04cb, 0x0109)},
+ {USB_DEVICE(0x04cb, 0x010b)},
+ {USB_DEVICE(0x04cb, 0x010f)},
+ {USB_DEVICE(0x04cb, 0x0111)},
+ {USB_DEVICE(0x04cb, 0x0113)},
+ {USB_DEVICE(0x04cb, 0x0115)},
+ {USB_DEVICE(0x04cb, 0x0117)},
+ {USB_DEVICE(0x04cb, 0x0119)},
+ {USB_DEVICE(0x04cb, 0x011b)},
+ {USB_DEVICE(0x04cb, 0x011d)},
+ {USB_DEVICE(0x04cb, 0x0121)},
+ {USB_DEVICE(0x04cb, 0x0123)},
+ {USB_DEVICE(0x04cb, 0x0125)},
+ {USB_DEVICE(0x04cb, 0x0127)},
+ {USB_DEVICE(0x04cb, 0x0129)},
+ {USB_DEVICE(0x04cb, 0x012b)},
+ {USB_DEVICE(0x04cb, 0x012d)},
+ {USB_DEVICE(0x04cb, 0x012f)},
+ {USB_DEVICE(0x04cb, 0x0131)},
+ {USB_DEVICE(0x04cb, 0x013b)},
+ {USB_DEVICE(0x04cb, 0x013d)},
+ {USB_DEVICE(0x04cb, 0x013f)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+};
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id,
+ &sd_desc,
+ sizeof(struct usb_fpix),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+ if (usb_register(&sd_driver) < 0)
+ return -1;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+ usb_deregister(&sd_driver);
+ PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c
index ac95c55..c21af31 100644
--- a/drivers/media/video/gspca/gspca.c
+++ b/drivers/media/video/gspca/gspca.c
@@ -29,6 +29,7 @@
#include <linux/string.h>
#include <linux/pagemap.h>
#include <linux/io.h>
+#include <linux/kref.h>
#include <asm/page.h>
#include <linux/uaccess.h>
#include <linux/jiffies.h>
@@ -43,7 +44,7 @@
MODULE_DESCRIPTION("GSPCA USB Camera Driver");
MODULE_LICENSE("GPL");
-#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 2, 0)
+#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 3, 0)
static int video_nr = -1;
@@ -102,6 +103,22 @@
.close = gspca_vm_close,
};
+/* get the current input frame buffer */
+struct gspca_frame *gspca_get_i_frame(struct gspca_dev *gspca_dev)
+{
+ struct gspca_frame *frame;
+ int i;
+
+ i = gspca_dev->fr_i;
+ i = gspca_dev->fr_queue[i];
+ frame = &gspca_dev->frame[i];
+ if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS)
+ != V4L2_BUF_FLAG_QUEUED)
+ return NULL;
+ return frame;
+}
+EXPORT_SYMBOL(gspca_get_i_frame);
+
/*
* fill a video frame from an URB and resubmit
*/
@@ -110,7 +127,7 @@
{
struct gspca_frame *frame;
__u8 *data; /* address of data in the iso message */
- int i, j, len, st;
+ int i, len, st;
cam_pkt_op pkt_scan;
if (urb->status != 0) {
@@ -124,11 +141,8 @@
for (i = 0; i < urb->number_of_packets; i++) {
/* check the availability of the frame buffer */
- j = gspca_dev->fr_i;
- j = gspca_dev->fr_queue[j];
- frame = &gspca_dev->frame[j];
- if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS)
- != V4L2_BUF_FLAG_QUEUED) {
+ frame = gspca_get_i_frame(gspca_dev);
+ if (!frame) {
gspca_dev->last_packet_type = DISCARD_PACKET;
break;
}
@@ -178,6 +192,39 @@
}
/*
+ * bulk message interrupt from the USB device
+ */
+static void bulk_irq(struct urb *urb
+)
+{
+ struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
+ struct gspca_frame *frame;
+
+ PDEBUG(D_PACK, "bulk irq");
+ if (!gspca_dev->streaming)
+ return;
+ if (urb->status != 0 && urb->status != -ECONNRESET) {
+#ifdef CONFIG_PM
+ if (!gspca_dev->frozen)
+#endif
+ PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
+ return; /* disconnection ? */
+ }
+
+ /* check the availability of the frame buffer */
+ frame = gspca_get_i_frame(gspca_dev);
+ if (!frame) {
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ } else {
+ PDEBUG(D_PACK, "packet l:%d", urb->actual_length);
+ gspca_dev->sd_desc->pkt_scan(gspca_dev,
+ frame,
+ urb->transfer_buffer,
+ urb->actual_length);
+ }
+}
+
+/*
* add data to the current frame
*
* This function is called by the subdrivers at interrupt level.
@@ -190,7 +237,7 @@
* On LAST_PACKET, a new frame is returned.
*/
struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,
- int packet_type,
+ enum gspca_packet_type packet_type,
struct gspca_frame *frame,
const __u8 *data,
int len)
@@ -232,7 +279,7 @@
}
gspca_dev->last_packet_type = packet_type;
- /* if last packet, wake the application and advance in the queue */
+ /* if last packet, wake up the application and advance in the queue */
if (packet_type == LAST_PACKET) {
frame->v4l2_buf.bytesused = frame->data_end - frame->data;
frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_QUEUED;
@@ -270,7 +317,6 @@
void *mem;
unsigned long adr;
-/* size = PAGE_ALIGN(size); (already done) */
mem = vmalloc_32(size);
if (mem != NULL) {
adr = (unsigned long) mem;
@@ -374,10 +420,11 @@
}
/*
- * search an input isochronous endpoint in an alternate setting
+ * look for an input transfer endpoint in an alternate setting
*/
-static struct usb_host_endpoint *alt_isoc(struct usb_host_interface *alt,
- __u8 epaddr)
+static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt,
+ __u8 epaddr,
+ __u8 xfer)
{
struct usb_host_endpoint *ep;
int i, attr;
@@ -388,7 +435,7 @@
if (ep->desc.bEndpointAddress == epaddr) {
attr = ep->desc.bmAttributes
& USB_ENDPOINT_XFERTYPE_MASK;
- if (attr == USB_ENDPOINT_XFER_ISOC)
+ if (attr == xfer)
return ep;
break;
}
@@ -397,14 +444,14 @@
}
/*
- * search an input isochronous endpoint
+ * look for an input (isoc or bulk) endpoint
*
* The endpoint is defined by the subdriver.
* Use only the first isoc (some Zoran - 0x0572:0x0001 - have two such ep).
* This routine may be called many times when the bandwidth is too small
* (the bandwidth is checked on urb submit).
*/
-static struct usb_host_endpoint *get_isoc_ep(struct gspca_dev *gspca_dev)
+static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev)
{
struct usb_interface *intf;
struct usb_host_endpoint *ep;
@@ -413,28 +460,41 @@
intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
ep = NULL;
i = gspca_dev->alt; /* previous alt setting */
+
+ /* try isoc */
while (--i > 0) { /* alt 0 is unusable */
- ep = alt_isoc(&intf->altsetting[i], gspca_dev->cam.epaddr);
+ ep = alt_xfer(&intf->altsetting[i],
+ gspca_dev->cam.epaddr,
+ USB_ENDPOINT_XFER_ISOC);
if (ep)
break;
}
+
+ /* if no isoc, try bulk */
if (ep == NULL) {
- err("no ISOC endpoint found");
- return NULL;
+ ep = alt_xfer(&intf->altsetting[0],
+ gspca_dev->cam.epaddr,
+ USB_ENDPOINT_XFER_BULK);
+ if (ep == NULL) {
+ err("no transfer endpoint found");
+ return NULL;
+ }
}
- PDEBUG(D_STREAM, "use ISOC alt %d ep 0x%02x",
+ PDEBUG(D_STREAM, "use alt %d ep 0x%02x",
i, ep->desc.bEndpointAddress);
- ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, i);
- if (ret < 0) {
- err("set interface err %d", ret);
- return NULL;
+ if (i > 0) {
+ ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, i);
+ if (ret < 0) {
+ err("set interface err %d", ret);
+ return NULL;
+ }
}
gspca_dev->alt = i; /* memorize the current alt setting */
return ep;
}
/*
- * create the isochronous URBs
+ * create the URBs for image transfer
*/
static int create_urbs(struct gspca_dev *gspca_dev,
struct usb_host_endpoint *ep)
@@ -445,15 +505,27 @@
/* calculate the packet size and the number of packets */
psize = le16_to_cpu(ep->desc.wMaxPacketSize);
- /* See paragraph 5.9 / table 5-11 of the usb 2.0 spec. */
- psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
- npkt = ISO_MAX_SIZE / psize;
- if (npkt > ISO_MAX_PKT)
- npkt = ISO_MAX_PKT;
- bsize = psize * npkt;
- PDEBUG(D_STREAM,
- "isoc %d pkts size %d (bsize:%d)", npkt, psize, bsize);
- nurbs = DEF_NURBS;
+ if (gspca_dev->alt != 0) { /* isoc */
+
+ /* See paragraph 5.9 / table 5-11 of the usb 2.0 spec. */
+ psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
+ npkt = ISO_MAX_SIZE / psize;
+ if (npkt > ISO_MAX_PKT)
+ npkt = ISO_MAX_PKT;
+ bsize = psize * npkt;
+ PDEBUG(D_STREAM,
+ "isoc %d pkts size %d = bsize:%d",
+ npkt, psize, bsize);
+ nurbs = DEF_NURBS;
+ } else { /* bulk */
+ npkt = 0;
+ bsize = gspca_dev->cam. bulk_size;
+ if (bsize == 0)
+ bsize = psize;
+ PDEBUG(D_STREAM, "bulk bsize:%d", bsize);
+ nurbs = 1;
+ }
+
gspca_dev->nurbs = nurbs;
for (n = 0; n < nurbs; n++) {
urb = usb_alloc_urb(npkt, GFP_KERNEL);
@@ -476,17 +548,24 @@
gspca_dev->urb[n] = urb;
urb->dev = gspca_dev->dev;
urb->context = gspca_dev;
- urb->pipe = usb_rcvisocpipe(gspca_dev->dev,
- ep->desc.bEndpointAddress);
- urb->transfer_flags = URB_ISO_ASAP
- | URB_NO_TRANSFER_DMA_MAP;
- urb->interval = ep->desc.bInterval;
- urb->complete = isoc_irq;
- urb->number_of_packets = npkt;
urb->transfer_buffer_length = bsize;
- for (i = 0; i < npkt; i++) {
- urb->iso_frame_desc[i].length = psize;
- urb->iso_frame_desc[i].offset = psize * i;
+ if (npkt != 0) { /* ISOC */
+ urb->pipe = usb_rcvisocpipe(gspca_dev->dev,
+ ep->desc.bEndpointAddress);
+ urb->transfer_flags = URB_ISO_ASAP
+ | URB_NO_TRANSFER_DMA_MAP;
+ urb->interval = ep->desc.bInterval;
+ urb->complete = isoc_irq;
+ urb->number_of_packets = npkt;
+ for (i = 0; i < npkt; i++) {
+ urb->iso_frame_desc[i].length = psize;
+ urb->iso_frame_desc[i].offset = psize * i;
+ }
+ } else { /* bulk */
+ urb->pipe = usb_rcvbulkpipe(gspca_dev->dev,
+ ep->desc.bEndpointAddress),
+ urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+ urb->complete = bulk_irq;
}
}
return 0;
@@ -508,7 +587,7 @@
gspca_dev->alt = gspca_dev->nbalt;
for (;;) {
PDEBUG(D_STREAM, "init transfer alt %d", gspca_dev->alt);
- ep = get_isoc_ep(gspca_dev);
+ ep = get_ep(gspca_dev);
if (ep == NULL) {
ret = -EIO;
goto out;
@@ -518,10 +597,18 @@
goto out;
/* start the cam */
- gspca_dev->sd_desc->start(gspca_dev);
+ ret = gspca_dev->sd_desc->start(gspca_dev);
+ if (ret < 0) {
+ destroy_urbs(gspca_dev);
+ goto out;
+ }
gspca_dev->streaming = 1;
atomic_set(&gspca_dev->nevent, 0);
+ /* bulk transfers are started by the subdriver */
+ if (gspca_dev->alt == 0)
+ break;
+
/* submit the URBs */
for (n = 0; n < gspca_dev->nurbs; n++) {
ret = usb_submit_urb(gspca_dev->urb[n], GFP_KERNEL);
@@ -553,7 +640,7 @@
return ret;
}
-/* Note both the queue and the usb lock should be hold when calling this */
+/* Note: both the queue and the usb locks should be held when calling this */
static void gspca_stream_off(struct gspca_dev *gspca_dev)
{
gspca_dev->streaming = 0;
@@ -759,6 +846,16 @@
return ret;
}
+static void gspca_delete(struct kref *kref)
+{
+ struct gspca_dev *gspca_dev = container_of(kref, struct gspca_dev, kref);
+
+ PDEBUG(D_STREAM, "device deleted");
+
+ kfree(gspca_dev->usb_buf);
+ kfree(gspca_dev);
+}
+
static int dev_open(struct inode *inode, struct file *file)
{
struct gspca_dev *gspca_dev;
@@ -778,13 +875,19 @@
goto out;
}
gspca_dev->users++;
+
+ /* one more user */
+ kref_get(&gspca_dev->kref);
+
file->private_data = gspca_dev;
#ifdef GSPCA_DEBUG
/* activate the v4l2 debug */
if (gspca_debug & D_V4L2)
- gspca_dev->vdev.debug |= 3;
+ gspca_dev->vdev.debug |= V4L2_DEBUG_IOCTL
+ | V4L2_DEBUG_IOCTL_ARG;
else
- gspca_dev->vdev.debug &= ~3;
+ gspca_dev->vdev.debug &= ~(V4L2_DEBUG_IOCTL
+ | V4L2_DEBUG_IOCTL_ARG);
#endif
ret = 0;
out:
@@ -818,7 +921,11 @@
}
file->private_data = NULL;
mutex_unlock(&gspca_dev->queue_lock);
+
PDEBUG(D_STREAM, "close done");
+
+ kref_put(&gspca_dev->kref, gspca_delete);
+
return 0;
}
@@ -829,7 +936,6 @@
memset(cap, 0, sizeof *cap);
strncpy(cap->driver, gspca_dev->sd_desc->name, sizeof cap->driver);
-/* strncpy(cap->card, gspca_dev->cam.dev_name, sizeof cap->card); */
if (gspca_dev->dev->product != NULL) {
strncpy(cap->card, gspca_dev->dev->product,
sizeof cap->card);
@@ -1463,7 +1569,6 @@
}
frame->v4l2_buf.flags |= V4L2_BUF_FLAG_QUEUED;
-/* frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE; */
if (frame->v4l2_buf.memory == V4L2_MEMORY_USERPTR) {
frame->v4l2_buf.m.userptr = v4l2_buf->m.userptr;
@@ -1610,7 +1715,7 @@
}
/* if the process slept for more than 1 second,
- * get anewer frame */
+ * get a newer frame */
frame = &gspca_dev->frame[v4l2_buf.index];
if (--n < 0)
break; /* avoid infinite loop */
@@ -1728,21 +1833,21 @@
if (dev_size < sizeof *gspca_dev)
dev_size = sizeof *gspca_dev;
gspca_dev = kzalloc(dev_size, GFP_KERNEL);
- if (gspca_dev == NULL) {
+ if (!gspca_dev) {
err("couldn't kzalloc gspca struct");
- return -EIO;
+ return -ENOMEM;
}
+ kref_init(&gspca_dev->kref);
gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL);
if (!gspca_dev->usb_buf) {
err("out of memory");
- ret = -EIO;
+ ret = -ENOMEM;
goto out;
}
gspca_dev->dev = dev;
gspca_dev->iface = interface->bInterfaceNumber;
gspca_dev->nbalt = intf->num_altsetting;
gspca_dev->sd_desc = sd_desc;
-/* gspca_dev->users = 0; (done by kzalloc) */
gspca_dev->nbufread = 2;
/* configure the subdriver and initialize the USB device */
@@ -1781,8 +1886,7 @@
PDEBUG(D_PROBE, "probe ok");
return 0;
out:
- kfree(gspca_dev->usb_buf);
- kfree(gspca_dev);
+ kref_put(&gspca_dev->kref, gspca_delete);
return ret;
}
EXPORT_SYMBOL(gspca_dev_probe);
@@ -1797,25 +1901,16 @@
{
struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
- if (!gspca_dev)
- return;
- gspca_dev->present = 0;
- mutex_lock(&gspca_dev->queue_lock);
- mutex_lock(&gspca_dev->usb_lock);
- gspca_dev->streaming = 0;
- destroy_urbs(gspca_dev);
- mutex_unlock(&gspca_dev->usb_lock);
- mutex_unlock(&gspca_dev->queue_lock);
- while (gspca_dev->users != 0) { /* wait until fully closed */
- atomic_inc(&gspca_dev->nevent);
- wake_up_interruptible(&gspca_dev->wq); /* wake processes */
- schedule();
- }
+ usb_set_intfdata(intf, NULL);
+
/* We don't want people trying to open up the device */
video_unregister_device(&gspca_dev->vdev);
-/* Free the memory */
- kfree(gspca_dev->usb_buf);
- kfree(gspca_dev);
+
+ gspca_dev->present = 0;
+ gspca_dev->streaming = 0;
+
+ kref_put(&gspca_dev->kref, gspca_delete);
+
PDEBUG(D_PROBE, "disconnect complete");
}
EXPORT_SYMBOL(gspca_disconnect);
diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h
index c17625c..4779dd0 100644
--- a/drivers/media/video/gspca/gspca.h
+++ b/drivers/media/video/gspca/gspca.h
@@ -49,13 +49,14 @@
} while (0)
#define GSPCA_MAX_FRAMES 16 /* maximum number of video frame buffers */
-/* ISOC transfers */
-#define MAX_NURBS 16 /* max number of URBs */
+/* image transfers */
+#define MAX_NURBS 4 /* max number of URBs */
#define ISO_MAX_PKT 32 /* max number of packets in an ISOC transfer */
#define ISO_MAX_SIZE 0x8000 /* max size of one URB buffer (32 Kb) */
/* device information - set at probe time */
struct cam {
+ int bulk_size; /* buffer size when image transfer by bulk */
struct v4l2_pix_format *cam_mode; /* size nmodes */
char nmodes;
__u8 epaddr;
@@ -93,7 +94,7 @@
/* mandatory operations */
cam_cf_op config; /* called on probe */
cam_op init; /* called on probe and resume */
- cam_v_op start; /* called on stream on */
+ cam_op start; /* called on stream on */
cam_pkt_op pkt_scan;
/* optional operations */
cam_v_op stopN; /* called on stream off - main alt */
@@ -105,10 +106,12 @@
};
/* packet types when moving from iso buf to frame buf */
-#define DISCARD_PACKET 0
-#define FIRST_PACKET 1
-#define INTER_PACKET 2
-#define LAST_PACKET 3
+enum gspca_packet_type {
+ DISCARD_PACKET,
+ FIRST_PACKET,
+ INTER_PACKET,
+ LAST_PACKET
+};
struct gspca_frame {
__u8 *data; /* frame buffer */
@@ -121,6 +124,7 @@
struct video_device vdev; /* !! must be the first item */
struct file_operations fops;
struct usb_device *dev;
+ struct kref kref;
struct file *capt_file; /* file doing video capture */
struct cam cam; /* device information */
@@ -173,10 +177,11 @@
struct module *module);
void gspca_disconnect(struct usb_interface *intf);
struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,
- int packet_type,
+ enum gspca_packet_type packet_type,
struct gspca_frame *frame,
const __u8 *data,
int len);
+struct gspca_frame *gspca_get_i_frame(struct gspca_dev *gspca_dev);
#ifdef CONFIG_PM
int gspca_suspend(struct usb_interface *intf, pm_message_t message);
int gspca_resume(struct usb_interface *intf);
diff --git a/drivers/media/video/gspca/m5602/Kconfig b/drivers/media/video/gspca/m5602/Kconfig
new file mode 100644
index 0000000..5a69016
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/Kconfig
@@ -0,0 +1,11 @@
+config USB_M5602
+ tristate "ALi USB m5602 Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the
+ ALi m5602 connected to various image sensors.
+
+ See <file:Documentation/video4linux/m5602.txt> for more info.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_m5602.
diff --git a/drivers/media/video/gspca/m5602/Makefile b/drivers/media/video/gspca/m5602/Makefile
new file mode 100644
index 0000000..226ab4f
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/Makefile
@@ -0,0 +1,11 @@
+obj-$(CONFIG_USB_M5602) += gspca_m5602.o
+
+gspca_m5602-objs := m5602_core.o \
+ m5602_ov9650.o \
+ m5602_mt9m111.o \
+ m5602_po1030.o \
+ m5602_s5k83a.o \
+ m5602_s5k4aa.o
+
+EXTRA_CFLAGS += -Idrivers/media/video/gspca
+
diff --git a/drivers/media/video/gspca/m5602/m5602_bridge.h b/drivers/media/video/gspca/m5602/m5602_bridge.h
new file mode 100644
index 0000000..c786d7d
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/m5602_bridge.h
@@ -0,0 +1,170 @@
+/*
+ * USB Driver for ALi m5602 based webcams
+ *
+ * Copyright (C) 2008 Erik Andren
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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.
+ *
+ */
+
+#ifndef M5602_BRIDGE_H_
+#define M5602_BRIDGE_H_
+
+#include "gspca.h"
+
+#define MODULE_NAME "ALi m5602"
+
+/*****************************************************************************/
+
+#undef PDEBUG
+#undef info
+#undef err
+
+#define err(format, arg...) printk(KERN_ERR KBUILD_MODNAME ": " \
+ format "\n" , ## arg)
+#define info(format, arg...) printk(KERN_INFO KBUILD_MODNAME ": " \
+ format "\n" , ## arg)
+
+/* Debug parameters */
+#define DBG_INIT 0x1
+#define DBG_PROBE 0x2
+#define DBG_V4L2 0x4
+#define DBG_TRACE 0x8
+#define DBG_DATA 0x10
+#define DBG_V4L2_CID 0x20
+#define DBG_GSPCA 0x40
+
+#define PDEBUG(level, fmt, args...) \
+ do { \
+ if (m5602_debug & level) \
+ info("[%s:%d] " fmt, __func__, __LINE__ , \
+ ## args); \
+ } while (0)
+
+/*****************************************************************************/
+
+#define M5602_XB_SENSOR_TYPE 0x00
+#define M5602_XB_SENSOR_CTRL 0x01
+#define M5602_XB_LINE_OF_FRAME_H 0x02
+#define M5602_XB_LINE_OF_FRAME_L 0x03
+#define M5602_XB_PIX_OF_LINE_H 0x04
+#define M5602_XB_PIX_OF_LINE_L 0x05
+#define M5602_XB_VSYNC_PARA 0x06
+#define M5602_XB_HSYNC_PARA 0x07
+#define M5602_XB_TEST_MODE_1 0x08
+#define M5602_XB_TEST_MODE_2 0x09
+#define M5602_XB_SIG_INI 0x0a
+#define M5602_XB_DS_PARA 0x0e
+#define M5602_XB_TRIG_PARA 0x0f
+#define M5602_XB_CLK_PD 0x10
+#define M5602_XB_MCU_CLK_CTRL 0x12
+#define M5602_XB_MCU_CLK_DIV 0x13
+#define M5602_XB_SEN_CLK_CTRL 0x14
+#define M5602_XB_SEN_CLK_DIV 0x15
+#define M5602_XB_AUD_CLK_CTRL 0x16
+#define M5602_XB_AUD_CLK_DIV 0x17
+#define M5602_XB_DEVCTR1 0x41
+#define M5602_XB_EPSETR0 0x42
+#define M5602_XB_EPAFCTR 0x47
+#define M5602_XB_EPBFCTR 0x49
+#define M5602_XB_EPEFCTR 0x4f
+#define M5602_XB_TEST_REG 0x53
+#define M5602_XB_ALT2SIZE 0x54
+#define M5602_XB_ALT3SIZE 0x55
+#define M5602_XB_OBSFRAME 0x56
+#define M5602_XB_PWR_CTL 0x59
+#define M5602_XB_ADC_CTRL 0x60
+#define M5602_XB_ADC_DATA 0x61
+#define M5602_XB_MISC_CTRL 0x62
+#define M5602_XB_SNAPSHOT 0x63
+#define M5602_XB_SCRATCH_1 0x64
+#define M5602_XB_SCRATCH_2 0x65
+#define M5602_XB_SCRATCH_3 0x66
+#define M5602_XB_SCRATCH_4 0x67
+#define M5602_XB_I2C_CTRL 0x68
+#define M5602_XB_I2C_CLK_DIV 0x69
+#define M5602_XB_I2C_DEV_ADDR 0x6a
+#define M5602_XB_I2C_REG_ADDR 0x6b
+#define M5602_XB_I2C_DATA 0x6c
+#define M5602_XB_I2C_STATUS 0x6d
+#define M5602_XB_GPIO_DAT_H 0x70
+#define M5602_XB_GPIO_DAT_L 0x71
+#define M5602_XB_GPIO_DIR_H 0x72
+#define M5602_XB_GPIO_DIR_L 0x73
+#define M5602_XB_GPIO_EN_H 0x74
+#define M5602_XB_GPIO_EN_L 0x75
+#define M5602_XB_GPIO_DAT 0x76
+#define M5602_XB_GPIO_DIR 0x77
+#define M5602_XB_MISC_CTL 0x70
+
+#define I2C_BUSY 0x80
+
+/*****************************************************************************/
+
+/* Driver info */
+#define DRIVER_AUTHOR "ALi m5602 Linux Driver Project"
+#define DRIVER_DESC "ALi m5602 webcam driver"
+
+#define M5602_ISOC_ENDPOINT_ADDR 0x81
+#define M5602_INTR_ENDPOINT_ADDR 0x82
+
+#define M5602_MAX_FRAMES 32
+#define M5602_URBS 2
+#define M5602_ISOC_PACKETS 14
+
+#define M5602_URB_TIMEOUT msecs_to_jiffies(2 * M5602_ISOC_PACKETS)
+#define M5602_URB_MSG_TIMEOUT 5000
+#define M5602_FRAME_TIMEOUT 2
+
+/*****************************************************************************/
+
+/* A skeleton used for sending messages to the m5602 bridge */
+static const unsigned char bridge_urb_skeleton[] = {
+ 0x13, 0x00, 0x81, 0x00
+};
+
+/* A skeleton used for sending messages to the sensor */
+static const unsigned char sensor_urb_skeleton[] = {
+ 0x23, M5602_XB_GPIO_EN_H, 0x81, 0x06,
+ 0x23, M5602_XB_MISC_CTRL, 0x81, 0x80,
+ 0x13, M5602_XB_I2C_DEV_ADDR, 0x81, 0x00,
+ 0x13, M5602_XB_I2C_REG_ADDR, 0x81, 0x00,
+ 0x13, M5602_XB_I2C_DATA, 0x81, 0x00,
+ 0x13, M5602_XB_I2C_CTRL, 0x81, 0x11
+};
+
+/* m5602 device descriptor, currently it just wraps the m5602_camera struct */
+struct sd {
+ struct gspca_dev gspca_dev;
+
+ /* The name of the m5602 camera */
+ char *name;
+
+ /* A pointer to the currently connected sensor */
+ struct m5602_sensor *sensor;
+
+ struct sd_desc *desc;
+
+ /* The current frame's id, used to detect frame boundaries */
+ u8 frame_id;
+
+ /* The current frame count */
+ u32 frame_count;
+};
+
+int m5602_read_bridge(
+ struct sd *sd, u8 address, u8 *i2c_data);
+
+int m5602_write_bridge(
+ struct sd *sd, u8 address, u8 i2c_data);
+
+#endif
diff --git a/drivers/media/video/gspca/m5602/m5602_core.c b/drivers/media/video/gspca/m5602/m5602_core.c
new file mode 100644
index 0000000..19d5e35
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/m5602_core.c
@@ -0,0 +1,313 @@
+/*
+ * USB Driver for ALi m5602 based webcams
+ *
+ * Copyright (C) 2008 Erik Andren
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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.
+ *
+ */
+
+#include "m5602_ov9650.h"
+#include "m5602_mt9m111.h"
+#include "m5602_po1030.h"
+#include "m5602_s5k83a.h"
+#include "m5602_s5k4aa.h"
+
+/* Kernel module parameters */
+int force_sensor;
+int dump_bridge;
+int dump_sensor;
+unsigned int m5602_debug;
+
+static const __devinitdata struct usb_device_id m5602_table[] = {
+ {USB_DEVICE(0x0402, 0x5602)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, m5602_table);
+
+/* Reads a byte from the m5602 */
+int m5602_read_bridge(struct sd *sd, u8 address, u8 *i2c_data)
+{
+ int err;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ 0x04, 0xc0, 0x14,
+ 0x8100 + address, buf,
+ 1, M5602_URB_MSG_TIMEOUT);
+ *i2c_data = buf[0];
+
+ PDEBUG(DBG_TRACE, "Reading bridge register 0x%x containing 0x%x",
+ address, *i2c_data);
+
+ /* usb_control_msg(...) returns the number of bytes sent upon success,
+ mask that and return zero upon success instead*/
+ return (err < 0) ? err : 0;
+}
+
+/* Writes a byte to to the m5602 */
+int m5602_write_bridge(struct sd *sd, u8 address, u8 i2c_data)
+{
+ int err;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ PDEBUG(DBG_TRACE, "Writing bridge register 0x%x with 0x%x",
+ address, i2c_data);
+
+ memcpy(buf, bridge_urb_skeleton,
+ sizeof(bridge_urb_skeleton));
+ buf[1] = address;
+ buf[3] = i2c_data;
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x19,
+ 0x0000, buf,
+ 4, M5602_URB_MSG_TIMEOUT);
+
+ /* usb_control_msg(...) returns the number of bytes sent upon success,
+ mask that and return zero upon success instead */
+ return (err < 0) ? err : 0;
+}
+
+/* Dump all the registers of the m5602 bridge,
+ unfortunately this breaks the camera until it's power cycled */
+static void m5602_dump_bridge(struct sd *sd)
+{
+ int i;
+ for (i = 0; i < 0x80; i++) {
+ unsigned char val = 0;
+ m5602_read_bridge(sd, i, &val);
+ info("ALi m5602 address 0x%x contains 0x%x", i, val);
+ }
+ info("Warning: The camera probably won't work until it's power cycled");
+}
+
+static int m5602_probe_sensor(struct sd *sd)
+{
+ /* Try the po1030 */
+ sd->sensor = &po1030;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ /* Try the mt9m111 sensor */
+ sd->sensor = &mt9m111;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ /* Try the s5k4aa */
+ sd->sensor = &s5k4aa;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ /* Try the ov9650 */
+ sd->sensor = &ov9650;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ /* Try the s5k83a */
+ sd->sensor = &s5k83a;
+ if (!sd->sensor->probe(sd))
+ return 0;
+
+ /* More sensor probe function goes here */
+ info("Failed to find a sensor");
+ sd->sensor = NULL;
+ return -ENODEV;
+}
+
+static int m5602_configure(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id);
+
+static int m5602_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int err;
+
+ PDEBUG(DBG_TRACE, "Initializing ALi m5602 webcam");
+ /* Run the init sequence */
+ err = sd->sensor->init(sd);
+
+ return err;
+}
+
+static int m5602_start_transfer(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ /* Send start command to the camera */
+ const u8 buffer[4] = {0x13, 0xf9, 0x0f, 0x01};
+ memcpy(buf, buffer, sizeof(buffer));
+ usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0x04, 0x40, 0x19, 0x0000, buf,
+ 4, M5602_URB_MSG_TIMEOUT);
+
+ PDEBUG(DBG_V4L2, "Transfer started");
+ return 0;
+}
+
+static void m5602_urb_complete(struct gspca_dev *gspca_dev,
+ struct gspca_frame *frame,
+ __u8 *data, int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (len < 6) {
+ PDEBUG(DBG_DATA, "Packet is less than 6 bytes");
+ return;
+ }
+
+ /* Frame delimiter: ff xx xx xx ff ff */
+ if (data[0] == 0xff && data[4] == 0xff && data[5] == 0xff &&
+ data[2] != sd->frame_id) {
+ PDEBUG(DBG_DATA, "Frame delimiter detected");
+ sd->frame_id = data[2];
+
+ /* Remove the extra fluff appended on each header */
+ data += 6;
+ len -= 6;
+
+ /* Complete the last frame (if any) */
+ frame = gspca_frame_add(gspca_dev, LAST_PACKET,
+ frame, data, 0);
+ sd->frame_count++;
+
+ /* Create a new frame */
+ gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len);
+
+ PDEBUG(DBG_V4L2, "Starting new frame %d",
+ sd->frame_count);
+
+ } else {
+ int cur_frame_len = frame->data_end - frame->data;
+
+ /* Remove urb header */
+ data += 4;
+ len -= 4;
+
+ if (cur_frame_len + len <= frame->v4l2_buf.length) {
+ PDEBUG(DBG_DATA, "Continuing frame %d copying %d bytes",
+ sd->frame_count, len);
+
+ gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+ data, len);
+ } else if (frame->v4l2_buf.length - cur_frame_len > 0) {
+ /* Add the remaining data up to frame size */
+ gspca_frame_add(gspca_dev, INTER_PACKET, frame, data,
+ frame->v4l2_buf.length - cur_frame_len);
+ }
+ }
+}
+
+static void m5602_stop_transfer(struct gspca_dev *gspca_dev)
+{
+ /* Is there are a command to stop a data transfer? */
+}
+
+/* sub-driver description, the ctrl and nctrl is filled at probe time */
+static struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .config = m5602_configure,
+ .init = m5602_init,
+ .start = m5602_start_transfer,
+ .stopN = m5602_stop_transfer,
+ .pkt_scan = m5602_urb_complete
+};
+
+/* this function is called at probe time */
+static int m5602_configure(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+ int err;
+
+ PDEBUG(DBG_GSPCA, "m5602_configure start");
+
+ cam = &gspca_dev->cam;
+ cam->epaddr = M5602_ISOC_ENDPOINT_ADDR;
+ sd->desc = &sd_desc;
+
+ if (dump_bridge)
+ m5602_dump_bridge(sd);
+
+ /* Probe sensor */
+ err = m5602_probe_sensor(sd);
+ if (err)
+ goto fail;
+
+ PDEBUG(DBG_GSPCA, "m5602_configure end");
+ return 0;
+
+fail:
+ PDEBUG(DBG_GSPCA, "m5602_configure failed");
+ cam->cam_mode = NULL;
+ cam->nmodes = 0;
+
+ return err;
+}
+
+static int m5602_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = m5602_table,
+ .probe = m5602_probe,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+ .disconnect = gspca_disconnect
+};
+
+/* -- module insert / remove -- */
+static int __init mod_m5602_init(void)
+{
+ if (usb_register(&sd_driver) < 0)
+ return -1;
+ PDEBUG(D_PROBE, "m5602 module registered");
+ return 0;
+}
+static void __exit mod_m5602_exit(void)
+{
+ usb_deregister(&sd_driver);
+ PDEBUG(D_PROBE, "m5602 module deregistered");
+}
+
+module_init(mod_m5602_init);
+module_exit(mod_m5602_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+module_param_named(debug, m5602_debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "toggles debug on/off");
+
+module_param(force_sensor, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(force_sensor,
+ "force detection of sensor, "
+ "1 = OV9650, 2 = S5K83A, 3 = S5K4AA, 4 = MT9M111, 5 = PO1030");
+
+module_param(dump_bridge, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dump_bridge, "Dumps all usb bridge registers at startup");
+
+module_param(dump_sensor, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dump_sensor, "Dumps all usb sensor registers "
+ "at startup providing a sensor is found");
diff --git a/drivers/media/video/gspca/m5602/m5602_mt9m111.c b/drivers/media/video/gspca/m5602/m5602_mt9m111.c
new file mode 100644
index 0000000..566d492
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/m5602_mt9m111.c
@@ -0,0 +1,345 @@
+/*
+ * Driver for the mt9m111 sensor
+ *
+ * Copyright (C) 2008 Erik Andren
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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.
+ *
+ */
+
+#include "m5602_mt9m111.h"
+
+int mt9m111_probe(struct sd *sd)
+{
+ u8 data[2] = {0x00, 0x00};
+ int i;
+
+ if (force_sensor) {
+ if (force_sensor == MT9M111_SENSOR) {
+ info("Forcing a %s sensor", mt9m111.name);
+ goto sensor_found;
+ }
+ /* If we want to force another sensor, don't try to probe this
+ * one */
+ return -ENODEV;
+ }
+
+ info("Probing for a mt9m111 sensor");
+
+ /* Do the preinit */
+ for (i = 0; i < ARRAY_SIZE(preinit_mt9m111); i++) {
+ if (preinit_mt9m111[i][0] == BRIDGE) {
+ m5602_write_bridge(sd,
+ preinit_mt9m111[i][1],
+ preinit_mt9m111[i][2]);
+ } else {
+ data[0] = preinit_mt9m111[i][2];
+ data[1] = preinit_mt9m111[i][3];
+ mt9m111_write_sensor(sd,
+ preinit_mt9m111[i][1], data, 2);
+ }
+ }
+
+ if (mt9m111_read_sensor(sd, MT9M111_SC_CHIPVER, data, 2))
+ return -ENODEV;
+
+ if ((data[0] == 0x14) && (data[1] == 0x3a)) {
+ info("Detected a mt9m111 sensor");
+ goto sensor_found;
+ }
+
+ return -ENODEV;
+
+sensor_found:
+ sd->gspca_dev.cam.cam_mode = mt9m111.modes;
+ sd->gspca_dev.cam.nmodes = mt9m111.nmodes;
+ sd->desc->ctrls = mt9m111.ctrls;
+ sd->desc->nctrls = mt9m111.nctrls;
+ return 0;
+}
+
+int mt9m111_init(struct sd *sd)
+{
+ int i, err = 0;
+
+ /* Init the sensor */
+ for (i = 0; i < ARRAY_SIZE(init_mt9m111); i++) {
+ u8 data[2];
+
+ if (init_mt9m111[i][0] == BRIDGE) {
+ err = m5602_write_bridge(sd,
+ init_mt9m111[i][1],
+ init_mt9m111[i][2]);
+ } else {
+ data[0] = init_mt9m111[i][2];
+ data[1] = init_mt9m111[i][3];
+ err = mt9m111_write_sensor(sd,
+ init_mt9m111[i][1], data, 2);
+ }
+ }
+
+ if (dump_sensor)
+ mt9m111_dump_registers(sd);
+
+ return (err < 0) ? err : 0;
+}
+
+int mt9m111_power_down(struct sd *sd)
+{
+ return 0;
+}
+
+int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u8 data[2] = {0x00, 0x00};
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = mt9m111_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
+ data, 2);
+ *val = data[0] & MT9M111_RMB_MIRROR_ROWS;
+ PDEBUG(DBG_V4L2_CID, "Read vertical flip %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 data[2] = {0x00, 0x00};
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(DBG_V4L2_CID, "Set vertical flip to %d", val);
+
+ /* Set the correct page map */
+ err = mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
+ if (err < 0)
+ goto out;
+
+ err = mt9m111_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
+ if (err < 0)
+ goto out;
+
+ data[0] = (data[0] & 0xfe) | val;
+ err = mt9m111_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
+ data, 2);
+out:
+ return (err < 0) ? err : 0;
+}
+
+int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u8 data[2] = {0x00, 0x00};
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = mt9m111_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
+ data, 2);
+ *val = data[0] & MT9M111_RMB_MIRROR_COLS;
+ PDEBUG(DBG_V4L2_CID, "Read horizontal flip %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 data[2] = {0x00, 0x00};
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(DBG_V4L2_CID, "Set horizontal flip to %d", val);
+
+ /* Set the correct page map */
+ err = mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
+ if (err < 0)
+ goto out;
+
+ err = mt9m111_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2);
+ if (err < 0)
+ goto out;
+
+ data[0] = (data[0] & 0xfd) | ((val << 1) & 0x02);
+ err = mt9m111_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B,
+ data, 2);
+out:
+ return (err < 0) ? err : 0;
+}
+
+int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err, tmp;
+ u8 data[2] = {0x00, 0x00};
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = mt9m111_read_sensor(sd, MT9M111_SC_GLOBAL_GAIN, data, 2);
+ tmp = ((data[1] << 8) | data[0]);
+
+ *val = ((tmp & (1 << 10)) * 2) |
+ ((tmp & (1 << 9)) * 2) |
+ ((tmp & (1 << 8)) * 2) |
+ (tmp & 0x7f);
+
+ PDEBUG(DBG_V4L2_CID, "Read gain %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err, tmp;
+ u8 data[2] = {0x00, 0x00};
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* Set the correct page map */
+ err = mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, data, 2);
+ if (err < 0)
+ goto out;
+
+ if (val >= INITIAL_MAX_GAIN * 2 * 2 * 2)
+ return -EINVAL;
+
+ if ((val >= INITIAL_MAX_GAIN * 2 * 2) &&
+ (val < (INITIAL_MAX_GAIN - 1) * 2 * 2 * 2))
+ tmp = (1 << 10) | (val << 9) |
+ (val << 8) | (val / 8);
+ else if ((val >= INITIAL_MAX_GAIN * 2) &&
+ (val < INITIAL_MAX_GAIN * 2 * 2))
+ tmp = (1 << 9) | (1 << 8) | (val / 4);
+ else if ((val >= INITIAL_MAX_GAIN) &&
+ (val < INITIAL_MAX_GAIN * 2))
+ tmp = (1 << 8) | (val / 2);
+ else
+ tmp = val;
+
+ data[1] = (tmp & 0xff00) >> 8;
+ data[0] = (tmp & 0xff);
+ PDEBUG(DBG_V4L2_CID, "tmp=%d, data[1]=%d, data[0]=%d", tmp,
+ data[1], data[0]);
+
+ err = mt9m111_write_sensor(sd, MT9M111_SC_GLOBAL_GAIN,
+ data, 2);
+out:
+ return (err < 0) ? err : 0;
+}
+
+int mt9m111_read_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len) {
+ int err, i;
+
+ do {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
+ } while ((*i2c_data & I2C_BUSY) && !err);
+ if (err < 0)
+ goto out;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
+ sd->sensor->i2c_slave_id);
+ if (err < 0)
+ goto out;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
+ if (err < 0)
+ goto out;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x1a);
+ if (err < 0)
+ goto out;
+
+ for (i = 0; i < len && !err; i++) {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
+
+ PDEBUG(DBG_TRACE, "Reading sensor register "
+ "0x%x contains 0x%x ", address, *i2c_data);
+ }
+out:
+ return (err < 0) ? err : 0;
+}
+
+int mt9m111_write_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len)
+{
+ int err, i;
+ u8 *p;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ /* No sensor with a data width larger
+ than 16 bits has yet been seen, nor with 0 :p*/
+ if (len > 2 || !len)
+ return -EINVAL;
+
+ memcpy(buf, sensor_urb_skeleton,
+ sizeof(sensor_urb_skeleton));
+
+ buf[11] = sd->sensor->i2c_slave_id;
+ buf[15] = address;
+
+ p = buf + 16;
+
+ /* Copy a four byte write sequence for each byte to be written to */
+ for (i = 0; i < len; i++) {
+ memcpy(p, sensor_urb_skeleton + 16, 4);
+ p[3] = i2c_data[i];
+ p += 4;
+ PDEBUG(DBG_TRACE, "Writing sensor register 0x%x with 0x%x",
+ address, i2c_data[i]);
+ }
+
+ /* Copy the tailer */
+ memcpy(p, sensor_urb_skeleton + 20, 4);
+
+ /* Set the total length */
+ p[3] = 0x10 + len;
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x19,
+ 0x0000, buf,
+ 20 + len * 4, M5602_URB_MSG_TIMEOUT);
+
+ return (err < 0) ? err : 0;
+}
+
+void mt9m111_dump_registers(struct sd *sd)
+{
+ u8 address, value[2] = {0x00, 0x00};
+
+ info("Dumping the mt9m111 register state");
+
+ info("Dumping the mt9m111 sensor core registers");
+ value[1] = MT9M111_SENSOR_CORE;
+ mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
+ for (address = 0; address < 0xff; address++) {
+ mt9m111_read_sensor(sd, address, value, 2);
+ info("register 0x%x contains 0x%x%x",
+ address, value[0], value[1]);
+ }
+
+ info("Dumping the mt9m111 color pipeline registers");
+ value[1] = MT9M111_COLORPIPE;
+ mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
+ for (address = 0; address < 0xff; address++) {
+ mt9m111_read_sensor(sd, address, value, 2);
+ info("register 0x%x contains 0x%x%x",
+ address, value[0], value[1]);
+ }
+
+ info("Dumping the mt9m111 camera control registers");
+ value[1] = MT9M111_CAMERA_CONTROL;
+ mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, value, 2);
+ for (address = 0; address < 0xff; address++) {
+ mt9m111_read_sensor(sd, address, value, 2);
+ info("register 0x%x contains 0x%x%x",
+ address, value[0], value[1]);
+ }
+
+ info("mt9m111 register state dump complete");
+}
diff --git a/drivers/media/video/gspca/m5602/m5602_mt9m111.h b/drivers/media/video/gspca/m5602/m5602_mt9m111.h
new file mode 100644
index 0000000..79a5d88
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/m5602_mt9m111.h
@@ -0,0 +1,1020 @@
+/*
+ * Driver for the mt9m111 sensor
+ *
+ * Copyright (C) 2008 Erik Andren
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * Some defines taken from the mt9m111 sensor driver
+ * Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr>
+ *
+ * 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.
+ *
+ */
+
+#ifndef M5602_MT9M111_H_
+#define M5602_MT9M111_H_
+
+#include "m5602_sensor.h"
+
+/*****************************************************************************/
+
+#define MT9M111_SC_CHIPVER 0x00
+#define MT9M111_SC_ROWSTART 0x01
+#define MT9M111_SC_COLSTART 0x02
+#define MT9M111_SC_WINDOW_HEIGHT 0x03
+#define MT9M111_SC_WINDOW_WIDTH 0x04
+#define MT9M111_SC_HBLANK_CONTEXT_B 0x05
+#define MT9M111_SC_VBLANK_CONTEXT_B 0x06
+#define MT9M111_SC_HBLANK_CONTEXT_A 0x07
+#define MT9M111_SC_VBLANK_CONTEXT_A 0x08
+#define MT9M111_SC_SHUTTER_WIDTH 0x09
+#define MT9M111_SC_ROW_SPEED 0x0a
+
+#define MT9M111_SC_EXTRA_DELAY 0x0b
+#define MT9M111_SC_SHUTTER_DELAY 0x0c
+#define MT9M111_SC_RESET 0x0d
+#define MT9M111_SC_R_MODE_CONTEXT_B 0x20
+#define MT9M111_SC_R_MODE_CONTEXT_A 0x21
+#define MT9M111_SC_FLASH_CONTROL 0x23
+#define MT9M111_SC_GREEN_1_GAIN 0x2b
+#define MT9M111_SC_BLUE_GAIN 0x2c
+#define MT9M111_SC_RED_GAIN 0x2d
+#define MT9M111_SC_GREEN_2_GAIN 0x2e
+#define MT9M111_SC_GLOBAL_GAIN 0x2f
+
+#define MT9M111_RMB_MIRROR_ROWS (1 << 0)
+#define MT9M111_RMB_MIRROR_COLS (1 << 1)
+
+#define MT9M111_CONTEXT_CONTROL 0xc8
+#define MT9M111_PAGE_MAP 0xf0
+#define MT9M111_BYTEWISE_ADDRESS 0xf1
+
+#define MT9M111_CP_OPERATING_MODE_CTL 0x06
+#define MT9M111_CP_LUMA_OFFSET 0x34
+#define MT9M111_CP_LUMA_CLIP 0x35
+#define MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A 0x3a
+#define MT9M111_CP_LENS_CORRECTION_1 0x3b
+#define MT9M111_CP_DEFECT_CORR_CONTEXT_A 0x4c
+#define MT9M111_CP_DEFECT_CORR_CONTEXT_B 0x4d
+#define MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B 0x9b
+#define MT9M111_CP_GLOBAL_CLK_CONTROL 0xb3
+
+#define MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18 0x65
+#define MT9M111_CC_AWB_PARAMETER_7 0x28
+
+#define MT9M111_SENSOR_CORE 0x00
+#define MT9M111_COLORPIPE 0x01
+#define MT9M111_CAMERA_CONTROL 0x02
+
+#define INITIAL_MAX_GAIN 64
+#define DEFAULT_GAIN 283
+
+/*****************************************************************************/
+
+/* Kernel module parameters */
+extern int force_sensor;
+extern int dump_sensor;
+extern unsigned int m5602_debug;
+
+int mt9m111_probe(struct sd *sd);
+int mt9m111_init(struct sd *sd);
+int mt9m111_power_down(struct sd *sd);
+
+int mt9m111_read_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+int mt9m111_write_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+void mt9m111_dump_registers(struct sd *sd);
+
+int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
+int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
+int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
+int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
+int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
+int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val);
+
+static struct m5602_sensor mt9m111 = {
+ .name = "MT9M111",
+
+ .i2c_slave_id = 0xba,
+
+ .probe = mt9m111_probe,
+ .init = mt9m111_init,
+ .power_down = mt9m111_power_down,
+
+ .read_sensor = mt9m111_read_sensor,
+ .write_sensor = mt9m111_write_sensor,
+
+ .nctrls = 3,
+ .ctrls = {
+ {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "vertical flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = mt9m111_set_vflip,
+ .get = mt9m111_get_vflip
+ }, {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "horizontal flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = mt9m111_set_hflip,
+ .get = mt9m111_get_hflip
+ }, {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "gain",
+ .minimum = 0,
+ .maximum = (INITIAL_MAX_GAIN - 1) * 2 * 2 * 2,
+ .step = 1,
+ .default_value = DEFAULT_GAIN,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = mt9m111_set_hflip,
+ .get = mt9m111_get_hflip
+ }
+ },
+
+ .nmodes = 1,
+ .modes = {
+ {
+ M5602_DEFAULT_FRAME_WIDTH,
+ M5602_DEFAULT_FRAME_HEIGHT,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ M5602_DEFAULT_FRAME_WIDTH * M5602_DEFAULT_FRAME_HEIGHT,
+ .bytesperline = M5602_DEFAULT_FRAME_WIDTH,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1
+ }
+ }
+};
+
+static const unsigned char preinit_mt9m111[][4] =
+{
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0xff, 0xf7},
+
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00}
+};
+
+static const unsigned char init_mt9m111[][4] =
+{
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0xff, 0xff},
+ {SENSOR, MT9M111_SC_RESET, 0xff, 0xff},
+ {SENSOR, MT9M111_SC_RESET, 0xff, 0xde},
+ {SENSOR, MT9M111_SC_RESET, 0xff, 0xff},
+ {SENSOR, MT9M111_SC_RESET, 0xff, 0xf7},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xb3, 0x00},
+
+ {SENSOR, MT9M111_CP_GLOBAL_CLK_CONTROL, 0xff, 0xff},
+
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00},
+
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x05},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00, 0x10},
+ {SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a},
+ {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00},
+ {SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00},
+ {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00},
+ {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xcd, 0x00},
+
+ {SENSOR, 0xcd, 0x00, 0x0e},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xd0, 0x00},
+ {SENSOR, 0xd0, 0x00, 0x40},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02},
+ {SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+ {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x07},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+ {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+ {SENSOR, 0x33, 0x03, 0x49},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+
+ {SENSOR, 0x33, 0x03, 0x49},
+ {SENSOR, 0x34, 0xc0, 0x19},
+ {SENSOR, 0x3f, 0x20, 0x20},
+ {SENSOR, 0x40, 0x20, 0x20},
+ {SENSOR, 0x5a, 0xc0, 0x0a},
+ {SENSOR, 0x70, 0x7b, 0x0a},
+ {SENSOR, 0x71, 0xff, 0x00},
+ {SENSOR, 0x72, 0x19, 0x0e},
+ {SENSOR, 0x73, 0x18, 0x0f},
+ {SENSOR, 0x74, 0x57, 0x32},
+ {SENSOR, 0x75, 0x56, 0x34},
+ {SENSOR, 0x76, 0x73, 0x35},
+ {SENSOR, 0x77, 0x30, 0x12},
+ {SENSOR, 0x78, 0x79, 0x02},
+ {SENSOR, 0x79, 0x75, 0x06},
+ {SENSOR, 0x7a, 0x77, 0x0a},
+ {SENSOR, 0x7b, 0x78, 0x09},
+ {SENSOR, 0x7c, 0x7d, 0x06},
+ {SENSOR, 0x7d, 0x31, 0x10},
+ {SENSOR, 0x7e, 0x00, 0x7e},
+ {SENSOR, 0x80, 0x59, 0x04},
+ {SENSOR, 0x81, 0x59, 0x04},
+ {SENSOR, 0x82, 0x57, 0x0a},
+ {SENSOR, 0x83, 0x58, 0x0b},
+ {SENSOR, 0x84, 0x47, 0x0c},
+ {SENSOR, 0x85, 0x48, 0x0e},
+ {SENSOR, 0x86, 0x5b, 0x02},
+ {SENSOR, 0x87, 0x00, 0x5c},
+ {SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, 0x08},
+ {SENSOR, 0x60, 0x00, 0x80},
+ {SENSOR, 0x61, 0x00, 0x00},
+ {SENSOR, 0x62, 0x00, 0x00},
+ {SENSOR, 0x63, 0x00, 0x00},
+ {SENSOR, 0x64, 0x00, 0x00},
+
+ {SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d},
+ {SENSOR, MT9M111_SC_COLSTART, 0x00, 0x18},
+ {SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x04},
+ {SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x08},
+ {SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x38},
+ {SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11},
+ {SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x38},
+ {SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11},
+ {SENSOR, MT9M111_SC_R_MODE_CONTEXT_B, 0x01, 0x03},
+ {SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x03},
+ {SENSOR, 0x30, 0x04, 0x00},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x07, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0xf4},
+ {SENSOR, MT9M111_SC_GLOBAL_GAIN, 0x00, 0xea},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x07, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x09},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x0c},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x04},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xb3, 0x00},
+ {SENSOR, MT9M111_CP_GLOBAL_CLK_CONTROL, 0x00, 0x03},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00},
+
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x05},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00, 0x10},
+ {SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a},
+ {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00},
+ {SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00},
+ {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00},
+ {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00},
+
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xcd, 0x00},
+ {SENSOR, 0xcd, 0x00, 0x0e},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xd0, 0x00},
+ {SENSOR, 0xd0, 0x00, 0x40},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02},
+ {SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+ {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x07},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+ {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+ {SENSOR, 0x33, 0x03, 0x49},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+
+ {SENSOR, 0x33, 0x03, 0x49},
+ {SENSOR, 0x34, 0xc0, 0x19},
+ {SENSOR, 0x3f, 0x20, 0x20},
+ {SENSOR, 0x40, 0x20, 0x20},
+ {SENSOR, 0x5a, 0xc0, 0x0a},
+ {SENSOR, 0x70, 0x7b, 0x0a},
+ {SENSOR, 0x71, 0xff, 0x00},
+ {SENSOR, 0x72, 0x19, 0x0e},
+ {SENSOR, 0x73, 0x18, 0x0f},
+ {SENSOR, 0x74, 0x57, 0x32},
+ {SENSOR, 0x75, 0x56, 0x34},
+ {SENSOR, 0x76, 0x73, 0x35},
+ {SENSOR, 0x77, 0x30, 0x12},
+ {SENSOR, 0x78, 0x79, 0x02},
+ {SENSOR, 0x79, 0x75, 0x06},
+ {SENSOR, 0x7a, 0x77, 0x0a},
+ {SENSOR, 0x7b, 0x78, 0x09},
+ {SENSOR, 0x7c, 0x7d, 0x06},
+ {SENSOR, 0x7d, 0x31, 0x10},
+ {SENSOR, 0x7e, 0x00, 0x7e},
+ {SENSOR, 0x80, 0x59, 0x04},
+ {SENSOR, 0x81, 0x59, 0x04},
+ {SENSOR, 0x82, 0x57, 0x0a},
+ {SENSOR, 0x83, 0x58, 0x0b},
+ {SENSOR, 0x84, 0x47, 0x0c},
+ {SENSOR, 0x85, 0x48, 0x0e},
+ {SENSOR, 0x86, 0x5b, 0x02},
+ {SENSOR, 0x87, 0x00, 0x5c},
+ {SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, 0x08},
+ {SENSOR, 0x60, 0x00, 0x80},
+ {SENSOR, 0x61, 0x00, 0x00},
+ {SENSOR, 0x62, 0x00, 0x00},
+ {SENSOR, 0x63, 0x00, 0x00},
+ {SENSOR, 0x64, 0x00, 0x00},
+
+ {SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d},
+ {SENSOR, MT9M111_SC_COLSTART, 0x00, 0x18},
+ {SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x04},
+ {SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x08},
+ {SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x38},
+ {SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11},
+ {SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x38},
+ {SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11},
+ {SENSOR, MT9M111_SC_R_MODE_CONTEXT_B, 0x01, 0x03},
+ {SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x03},
+ {SENSOR, 0x30, 0x04, 0x00},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x07, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0xf4},
+ {SENSOR, MT9M111_SC_GLOBAL_GAIN, 0x00, 0xea},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x09},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x0c},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x04},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xb3, 0x00},
+ {SENSOR, MT9M111_CP_GLOBAL_CLK_CONTROL, 0x00, 0x03},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00},
+
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x05},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00, 0x10},
+ {SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a},
+ {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00},
+ {SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00},
+ {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00},
+ {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00},
+
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xcd, 0x00},
+ {SENSOR, 0xcd, 0x00, 0x0e},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xd0, 0x00},
+ {SENSOR, 0xd0, 0x00, 0x40},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02},
+ {SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+ {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x07},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+ {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+ {SENSOR, 0x33, 0x03, 0x49},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+
+ {SENSOR, 0x33, 0x03, 0x49},
+ {SENSOR, 0x34, 0xc0, 0x19},
+ {SENSOR, 0x3f, 0x20, 0x20},
+ {SENSOR, 0x40, 0x20, 0x20},
+ {SENSOR, 0x5a, 0xc0, 0x0a},
+ {SENSOR, 0x70, 0x7b, 0x0a},
+ {SENSOR, 0x71, 0xff, 0x00},
+ {SENSOR, 0x72, 0x19, 0x0e},
+ {SENSOR, 0x73, 0x18, 0x0f},
+ {SENSOR, 0x74, 0x57, 0x32},
+ {SENSOR, 0x75, 0x56, 0x34},
+ {SENSOR, 0x76, 0x73, 0x35},
+ {SENSOR, 0x77, 0x30, 0x12},
+ {SENSOR, 0x78, 0x79, 0x02},
+ {SENSOR, 0x79, 0x75, 0x06},
+ {SENSOR, 0x7a, 0x77, 0x0a},
+ {SENSOR, 0x7b, 0x78, 0x09},
+ {SENSOR, 0x7c, 0x7d, 0x06},
+ {SENSOR, 0x7d, 0x31, 0x10},
+ {SENSOR, 0x7e, 0x00, 0x7e},
+ {SENSOR, 0x80, 0x59, 0x04},
+ {SENSOR, 0x81, 0x59, 0x04},
+ {SENSOR, 0x82, 0x57, 0x0a},
+ {SENSOR, 0x83, 0x58, 0x0b},
+ {SENSOR, 0x84, 0x47, 0x0c},
+ {SENSOR, 0x85, 0x48, 0x0e},
+ {SENSOR, 0x86, 0x5b, 0x02},
+ {SENSOR, 0x87, 0x00, 0x5c},
+ {SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, 0x08},
+ {SENSOR, 0x60, 0x00, 0x80},
+ {SENSOR, 0x61, 0x00, 0x00},
+ {SENSOR, 0x62, 0x00, 0x00},
+ {SENSOR, 0x63, 0x00, 0x00},
+ {SENSOR, 0x64, 0x00, 0x00},
+
+ {SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d},
+ {SENSOR, MT9M111_SC_COLSTART, 0x00, 0x18},
+ {SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x04},
+ {SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x08},
+ {SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x38},
+ {SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11},
+ {SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x38},
+ {SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11},
+ {SENSOR, MT9M111_SC_R_MODE_CONTEXT_B, 0x01, 0x03},
+ {SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x03},
+ {SENSOR, 0x30, 0x04, 0x00},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x07, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0xf4},
+ {SENSOR, MT9M111_SC_GLOBAL_GAIN, 0x00, 0xea},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x09},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x0c},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x04},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xb3, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_CP_GLOBAL_CLK_CONTROL, 0x00, 0x03},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x05},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00, 0x10},
+ {SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a},
+ {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00},
+ {SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00},
+ {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00},
+ {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00},
+
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xcd, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, 0xcd, 0x00, 0x0e},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xd0, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, 0xd0, 0x00, 0x40},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02},
+ {SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x07},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, 0x33, 0x03, 0x49},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+
+ {SENSOR, 0x33, 0x03, 0x49},
+ {SENSOR, 0x34, 0xc0, 0x19},
+ {SENSOR, 0x3f, 0x20, 0x20},
+ {SENSOR, 0x40, 0x20, 0x20},
+ {SENSOR, 0x5a, 0xc0, 0x0a},
+ {SENSOR, 0x70, 0x7b, 0x0a},
+ {SENSOR, 0x71, 0xff, 0x00},
+ {SENSOR, 0x72, 0x19, 0x0e},
+ {SENSOR, 0x73, 0x18, 0x0f},
+ {SENSOR, 0x74, 0x57, 0x32},
+ {SENSOR, 0x75, 0x56, 0x34},
+ {SENSOR, 0x76, 0x73, 0x35},
+ {SENSOR, 0x77, 0x30, 0x12},
+ {SENSOR, 0x78, 0x79, 0x02},
+ {SENSOR, 0x79, 0x75, 0x06},
+ {SENSOR, 0x7a, 0x77, 0x0a},
+ {SENSOR, 0x7b, 0x78, 0x09},
+ {SENSOR, 0x7c, 0x7d, 0x06},
+ {SENSOR, 0x7d, 0x31, 0x10},
+ {SENSOR, 0x7e, 0x00, 0x7e},
+ {SENSOR, 0x80, 0x59, 0x04},
+ {SENSOR, 0x81, 0x59, 0x04},
+ {SENSOR, 0x82, 0x57, 0x0a},
+ {SENSOR, 0x83, 0x58, 0x0b},
+ {SENSOR, 0x84, 0x47, 0x0c},
+ {SENSOR, 0x85, 0x48, 0x0e},
+ {SENSOR, 0x86, 0x5b, 0x02},
+ {SENSOR, 0x87, 0x00, 0x5c},
+ {SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, 0x08},
+ {SENSOR, 0x60, 0x00, 0x80},
+ {SENSOR, 0x61, 0x00, 0x00},
+ {SENSOR, 0x62, 0x00, 0x00},
+ {SENSOR, 0x63, 0x00, 0x00},
+ {SENSOR, 0x64, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d},
+ {SENSOR, MT9M111_SC_COLSTART, 0x00, 0x12},
+ {SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x00},
+ {SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x10},
+ {SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x60},
+ {SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11},
+ {SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x60},
+ {SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11},
+ {SENSOR, MT9M111_SC_R_MODE_CONTEXT_B, 0x01, 0x0f},
+ {SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x0f},
+ {SENSOR, 0x30, 0x04, 0x00},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xe3, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x87, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0x90},
+ {SENSOR, MT9M111_SC_GLOBAL_GAIN, 0x00, 0xe6},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x09},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x0c},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x04},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xb3, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_CP_GLOBAL_CLK_CONTROL, 0x00, 0x03},
+
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x05},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x29},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_SC_RESET, 0x00, 0x08},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00, 0x10},
+ {SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a},
+ {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00, 0x01},
+ {SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00},
+ {SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00},
+ {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00},
+ {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00},
+
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xcd, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, 0xcd, 0x00, 0x0e},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xd0, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, 0xd0, 0x00, 0x40},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02},
+ {SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x07},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03},
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+ {SENSOR, 0x33, 0x03, 0x49},
+ {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00},
+ {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00},
+ {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00},
+
+ {SENSOR, 0x33, 0x03, 0x49},
+ {SENSOR, 0x34, 0xc0, 0x19},
+ {SENSOR, 0x3f, 0x20, 0x20},
+ {SENSOR, 0x40, 0x20, 0x20},
+ {SENSOR, 0x5a, 0xc0, 0x0a},
+ {SENSOR, 0x70, 0x7b, 0x0a},
+ {SENSOR, 0x71, 0xff, 0x00},
+ {SENSOR, 0x72, 0x19, 0x0e},
+ {SENSOR, 0x73, 0x18, 0x0f},
+ {SENSOR, 0x74, 0x57, 0x32},
+ {SENSOR, 0x75, 0x56, 0x34},
+ {SENSOR, 0x76, 0x73, 0x35},
+ {SENSOR, 0x77, 0x30, 0x12},
+ {SENSOR, 0x78, 0x79, 0x02},
+ {SENSOR, 0x79, 0x75, 0x06},
+ {SENSOR, 0x7a, 0x77, 0x0a},
+ {SENSOR, 0x7b, 0x78, 0x09},
+ {SENSOR, 0x7c, 0x7d, 0x06},
+ {SENSOR, 0x7d, 0x31, 0x10},
+ {SENSOR, 0x7e, 0x00, 0x7e},
+ {SENSOR, 0x80, 0x59, 0x04},
+ {SENSOR, 0x81, 0x59, 0x04},
+ {SENSOR, 0x82, 0x57, 0x0a},
+ {SENSOR, 0x83, 0x58, 0x0b},
+ {SENSOR, 0x84, 0x47, 0x0c},
+ {SENSOR, 0x85, 0x48, 0x0e},
+ {SENSOR, 0x86, 0x5b, 0x02},
+ {SENSOR, 0x87, 0x00, 0x5c},
+ {SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, 0x08},
+ {SENSOR, 0x60, 0x00, 0x80},
+ {SENSOR, 0x61, 0x00, 0x00},
+ {SENSOR, 0x62, 0x00, 0x00},
+ {SENSOR, 0x63, 0x00, 0x00},
+ {SENSOR, 0x64, 0x00, 0x00},
+
+ {SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d},
+ {SENSOR, MT9M111_SC_COLSTART, 0x00, 0x12},
+ {SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x00},
+ {SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x10},
+ {SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x60},
+ {SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11},
+ {SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x60},
+ {SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11},
+ {SENSOR, MT9M111_SC_R_MODE_CONTEXT_B, 0x01, 0x0f},
+ {SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x0f},
+ {SENSOR, 0x30, 0x04, 0x00},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xe0, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+
+ {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00},
+ /* Set number of blank rows chosen to 400 */
+ {SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0x90},
+ /* Set the global gain to 283 (of 512) */
+ {SENSOR, MT9M111_SC_GLOBAL_GAIN, 0x03, 0x63}
+};
+
+#endif
diff --git a/drivers/media/video/gspca/m5602/m5602_ov9650.c b/drivers/media/video/gspca/m5602/m5602_ov9650.c
new file mode 100644
index 0000000..31c5896
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/m5602_ov9650.c
@@ -0,0 +1,546 @@
+/*
+ * Driver for the ov9650 sensor
+ *
+ * Copyright (C) 2008 Erik Andren
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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.
+ *
+ */
+
+#include "m5602_ov9650.h"
+
+int ov9650_read_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len)
+{
+ int err, i;
+
+ /* The ov9650 registers have a max depth of one byte */
+ if (len > 1 || !len)
+ return -EINVAL;
+
+ do {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
+ } while ((*i2c_data & I2C_BUSY) && !err);
+
+ m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
+ ov9650.i2c_slave_id);
+ m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
+ m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x10 + len);
+ m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x08);
+
+ for (i = 0; i < len; i++) {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
+
+ PDEBUG(DBG_TRACE, "Reading sensor register "
+ "0x%x containing 0x%x ", address, *i2c_data);
+ }
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_write_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len)
+{
+ int err, i;
+ u8 *p;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ /* The ov9650 only supports one byte writes */
+ if (len > 1 || !len)
+ return -EINVAL;
+
+ memcpy(buf, sensor_urb_skeleton,
+ sizeof(sensor_urb_skeleton));
+
+ buf[11] = sd->sensor->i2c_slave_id;
+ buf[15] = address;
+
+ /* Special case larger sensor writes */
+ p = buf + 16;
+
+ /* Copy a four byte write sequence for each byte to be written to */
+ for (i = 0; i < len; i++) {
+ memcpy(p, sensor_urb_skeleton + 16, 4);
+ p[3] = i2c_data[i];
+ p += 4;
+ PDEBUG(DBG_TRACE, "Writing sensor register 0x%x with 0x%x",
+ address, i2c_data[i]);
+ }
+
+ /* Copy the tailer */
+ memcpy(p, sensor_urb_skeleton + 20, 4);
+
+ /* Set the total length */
+ p[3] = 0x10 + len;
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x19,
+ 0x0000, buf,
+ 20 + len * 4, M5602_URB_MSG_TIMEOUT);
+
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_probe(struct sd *sd)
+{
+ u8 prod_id = 0, ver_id = 0, i;
+
+ if (force_sensor) {
+ if (force_sensor == OV9650_SENSOR) {
+ info("Forcing an %s sensor", ov9650.name);
+ goto sensor_found;
+ }
+ /* If we want to force another sensor,
+ don't try to probe this one */
+ return -ENODEV;
+ }
+
+ info("Probing for an ov9650 sensor");
+
+ /* Run the pre-init to actually probe the unit */
+ for (i = 0; i < ARRAY_SIZE(preinit_ov9650); i++) {
+ u8 data = preinit_ov9650[i][2];
+ if (preinit_ov9650[i][0] == SENSOR)
+ ov9650_write_sensor(sd,
+ preinit_ov9650[i][1], &data, 1);
+ else
+ m5602_write_bridge(sd, preinit_ov9650[i][1], data);
+ }
+
+ if (ov9650_read_sensor(sd, OV9650_PID, &prod_id, 1))
+ return -ENODEV;
+
+ if (ov9650_read_sensor(sd, OV9650_VER, &ver_id, 1))
+ return -ENODEV;
+
+ if ((prod_id == 0x96) && (ver_id == 0x52)) {
+ info("Detected an ov9650 sensor");
+ goto sensor_found;
+ }
+
+ return -ENODEV;
+
+sensor_found:
+ sd->gspca_dev.cam.cam_mode = ov9650.modes;
+ sd->gspca_dev.cam.nmodes = ov9650.nmodes;
+ sd->desc->ctrls = ov9650.ctrls;
+ sd->desc->nctrls = ov9650.nctrls;
+ return 0;
+}
+
+int ov9650_init(struct sd *sd)
+{
+ int i, err = 0;
+ u8 data;
+
+ if (dump_sensor)
+ ov9650_dump_registers(sd);
+
+ for (i = 0; i < ARRAY_SIZE(init_ov9650) && !err; i++) {
+ data = init_ov9650[i][2];
+ if (init_ov9650[i][0] == SENSOR)
+ err = ov9650_write_sensor(sd, init_ov9650[i][1],
+ &data, 1);
+ else
+ err = m5602_write_bridge(sd, init_ov9650[i][1], data);
+ }
+
+ if (!err && dmi_check_system(ov9650_flip_dmi_table)) {
+ info("vflip quirk active");
+ data = 0x30;
+ err = ov9650_write_sensor(sd, OV9650_MVFP, &data, 1);
+ }
+
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_power_down(struct sd *sd)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(power_down_ov9650); i++) {
+ u8 data = power_down_ov9650[i][2];
+ if (power_down_ov9650[i][0] == SENSOR)
+ ov9650_write_sensor(sd,
+ power_down_ov9650[i][1], &data, 1);
+ else
+ m5602_write_bridge(sd, power_down_ov9650[i][1], data);
+ }
+
+ return 0;
+}
+
+int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 i2c_data;
+ int err;
+
+ err = ov9650_read_sensor(sd, OV9650_COM1, &i2c_data, 1);
+ if (err < 0)
+ goto out;
+ *val = i2c_data & 0x03;
+
+ err = ov9650_read_sensor(sd, OV9650_AECH, &i2c_data, 1);
+ if (err < 0)
+ goto out;
+ *val |= (i2c_data << 2);
+
+ err = ov9650_read_sensor(sd, OV9650_AECHM, &i2c_data, 1);
+ if (err < 0)
+ goto out;
+ *val |= (i2c_data & 0x3f) << 10;
+
+ PDEBUG(DBG_V4L2_CID, "Read exposure %d", *val);
+out:
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 i2c_data;
+ int err;
+
+ PDEBUG(DBG_V4L2_CID, "Set exposure to %d",
+ val & 0xffff);
+
+ /* The 6 MSBs */
+ i2c_data = (val >> 10) & 0x3f;
+ err = ov9650_write_sensor(sd, OV9650_AECHM,
+ &i2c_data, 1);
+ if (err < 0)
+ goto out;
+
+ /* The 8 middle bits */
+ i2c_data = (val >> 2) & 0xff;
+ err = ov9650_write_sensor(sd, OV9650_AECH,
+ &i2c_data, 1);
+ if (err < 0)
+ goto out;
+
+ /* The 2 LSBs */
+ i2c_data = val & 0x03;
+ err = ov9650_write_sensor(sd, OV9650_COM1, &i2c_data, 1);
+
+out:
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ ov9650_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ *val = (i2c_data & 0x03) << 8;
+
+ err = ov9650_read_sensor(sd, OV9650_GAIN, &i2c_data, 1);
+ *val |= i2c_data;
+ PDEBUG(DBG_V4L2_CID, "Read gain %d", *val);
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* The 2 MSB */
+ /* Read the OV9650_VREF register first to avoid
+ corrupting the VREF high and low bits */
+ ov9650_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ /* Mask away all uninteresting bits */
+ i2c_data = ((val & 0x0300) >> 2) |
+ (i2c_data & 0x3F);
+ err = ov9650_write_sensor(sd, OV9650_VREF, &i2c_data, 1);
+
+ /* The 8 LSBs */
+ i2c_data = val & 0xff;
+ err = ov9650_write_sensor(sd, OV9650_GAIN, &i2c_data, 1);
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = ov9650_read_sensor(sd, OV9650_RED, &i2c_data, 1);
+ *val = i2c_data;
+
+ PDEBUG(DBG_V4L2_CID, "Read red gain %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(DBG_V4L2_CID, "Set red gain to %d",
+ val & 0xff);
+
+ i2c_data = val & 0xff;
+ err = ov9650_write_sensor(sd, OV9650_RED, &i2c_data, 1);
+
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = ov9650_read_sensor(sd, OV9650_BLUE, &i2c_data, 1);
+ *val = i2c_data;
+
+ PDEBUG(DBG_V4L2_CID, "Read blue gain %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(DBG_V4L2_CID, "Set blue gain to %d",
+ val & 0xff);
+
+ i2c_data = val & 0xff;
+ err = ov9650_write_sensor(sd, OV9650_BLUE, &i2c_data, 1);
+
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = ov9650_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+ if (dmi_check_system(ov9650_flip_dmi_table))
+ *val = ((i2c_data & OV9650_HFLIP) >> 5) ? 0 : 1;
+ else
+ *val = (i2c_data & OV9650_HFLIP) >> 5;
+ PDEBUG(DBG_V4L2_CID, "Read horizontal flip %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(DBG_V4L2_CID, "Set horizontal flip to %d", val);
+ err = ov9650_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+ if (err < 0)
+ goto out;
+
+ if (dmi_check_system(ov9650_flip_dmi_table))
+ i2c_data = ((i2c_data & 0xdf) |
+ (((val ? 0 : 1) & 0x01) << 5));
+ else
+ i2c_data = ((i2c_data & 0xdf) |
+ ((val & 0x01) << 5));
+
+ err = ov9650_write_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+out:
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = ov9650_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+ if (dmi_check_system(ov9650_flip_dmi_table))
+ *val = ((i2c_data & 0x10) >> 4) ? 0 : 1;
+ else
+ *val = (i2c_data & 0x10) >> 4;
+ PDEBUG(DBG_V4L2_CID, "Read vertical flip %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(DBG_V4L2_CID, "Set vertical flip to %d", val);
+ err = ov9650_read_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+ if (err < 0)
+ goto out;
+
+ if (dmi_check_system(ov9650_flip_dmi_table))
+ i2c_data = ((i2c_data & 0xef) |
+ (((val ? 0 : 1) & 0x01) << 4));
+ else
+ i2c_data = ((i2c_data & 0xef) |
+ ((val & 0x01) << 4));
+
+ err = ov9650_write_sensor(sd, OV9650_MVFP, &i2c_data, 1);
+out:
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_get_brightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = ov9650_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ if (err < 0)
+ goto out;
+ *val = (i2c_data & 0x03) << 8;
+
+ err = ov9650_read_sensor(sd, OV9650_GAIN, &i2c_data, 1);
+ *val |= i2c_data;
+ PDEBUG(DBG_V4L2_CID, "Read gain %d", *val);
+out:
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(DBG_V4L2_CID, "Set gain to %d", val & 0x3ff);
+
+ /* Read the OV9650_VREF register first to avoid
+ corrupting the VREF high and low bits */
+ err = ov9650_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ if (err < 0)
+ goto out;
+
+ /* Mask away all uninteresting bits */
+ i2c_data = ((val & 0x0300) >> 2) | (i2c_data & 0x3F);
+ err = ov9650_write_sensor(sd, OV9650_VREF, &i2c_data, 1);
+ if (err < 0)
+ goto out;
+
+ /* The 8 LSBs */
+ i2c_data = val & 0xff;
+ err = ov9650_write_sensor(sd, OV9650_GAIN, &i2c_data, 1);
+
+out:
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = ov9650_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ *val = (i2c_data & OV9650_AWB_EN) >> 1;
+ PDEBUG(DBG_V4L2_CID, "Read auto white balance %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(DBG_V4L2_CID, "Set auto white balance to %d", val);
+ err = ov9650_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ if (err < 0)
+ goto out;
+
+ i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1));
+ err = ov9650_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
+out:
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = ov9650_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ *val = (i2c_data & OV9650_AGC_EN) >> 2;
+ PDEBUG(DBG_V4L2_CID, "Read auto gain control %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(DBG_V4L2_CID, "Set auto gain control to %d", val);
+ err = ov9650_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
+ if (err < 0)
+ goto out;
+
+ i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2));
+ err = ov9650_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
+out:
+ return (err < 0) ? err : 0;
+}
+
+void ov9650_dump_registers(struct sd *sd)
+{
+ int address;
+ info("Dumping the ov9650 register state");
+ for (address = 0; address < 0xa9; address++) {
+ u8 value;
+ ov9650_read_sensor(sd, address, &value, 1);
+ info("register 0x%x contains 0x%x",
+ address, value);
+ }
+
+ info("ov9650 register state dump complete");
+
+ info("Probing for which registers that are read/write");
+ for (address = 0; address < 0xff; address++) {
+ u8 old_value, ctrl_value;
+ u8 test_value[2] = {0xff, 0xff};
+
+ ov9650_read_sensor(sd, address, &old_value, 1);
+ ov9650_write_sensor(sd, address, test_value, 1);
+ ov9650_read_sensor(sd, address, &ctrl_value, 1);
+
+ if (ctrl_value == test_value[0])
+ info("register 0x%x is writeable", address);
+ else
+ info("register 0x%x is read only", address);
+
+ /* Restore original value */
+ ov9650_write_sensor(sd, address, &old_value, 1);
+ }
+}
diff --git a/drivers/media/video/gspca/m5602/m5602_ov9650.h b/drivers/media/video/gspca/m5602/m5602_ov9650.h
new file mode 100644
index 0000000..2f29cb0
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/m5602_ov9650.h
@@ -0,0 +1,503 @@
+/*
+ * Driver for the ov9650 sensor
+ *
+ * Copyright (C) 2008 Erik Andren
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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.
+ *
+ */
+
+#ifndef M5602_OV9650_H_
+#define M5602_OV9650_H_
+
+#include <linux/dmi.h>
+
+#include "m5602_sensor.h"
+
+/*****************************************************************************/
+
+#define OV9650_GAIN 0x00
+#define OV9650_BLUE 0x01
+#define OV9650_RED 0x02
+#define OV9650_VREF 0x03
+#define OV9650_COM1 0x04
+#define OV9650_BAVE 0x05
+#define OV9650_GEAVE 0x06
+#define OV9650_RSVD7 0x07
+#define OV9650_PID 0x0a
+#define OV9650_VER 0x0b
+#define OV9650_COM3 0x0c
+#define OV9650_COM5 0x0e
+#define OV9650_COM6 0x0f
+#define OV9650_AECH 0x10
+#define OV9650_CLKRC 0x11
+#define OV9650_COM7 0x12
+#define OV9650_COM8 0x13
+#define OV9650_COM9 0x14
+#define OV9650_COM10 0x15
+#define OV9650_RSVD16 0x16
+#define OV9650_HSTART 0x17
+#define OV9650_HSTOP 0x18
+#define OV9650_VSTRT 0x19
+#define OV9650_VSTOP 0x1a
+#define OV9650_PSHFT 0x1b
+#define OV9650_MVFP 0x1e
+#define OV9650_AEW 0x24
+#define OV9650_AEB 0x25
+#define OV9650_VPT 0x26
+#define OV9650_BBIAS 0x27
+#define OV9650_GbBIAS 0x28
+#define OV9650_Gr_COM 0x29
+#define OV9650_RBIAS 0x2c
+#define OV9650_HREF 0x32
+#define OV9650_CHLF 0x33
+#define OV9650_ARBLM 0x34
+#define OV9650_RSVD35 0x35
+#define OV9650_RSVD36 0x36
+#define OV9650_ADC 0x37
+#define OV9650_ACOM38 0x38
+#define OV9650_OFON 0x39
+#define OV9650_TSLB 0x3a
+#define OV9650_COM12 0x3c
+#define OV9650_COM13 0x3d
+#define OV9650_COM15 0x40
+#define OV9650_COM16 0x41
+#define OV9650_LCC1 0x62
+#define OV9650_LCC2 0x63
+#define OV9650_LCC3 0x64
+#define OV9650_LCC4 0x65
+#define OV9650_LCC5 0x66
+#define OV9650_HV 0x69
+#define OV9650_DBLV 0x6b
+#define OV9650_COM21 0x8b
+#define OV9650_COM22 0x8c
+#define OV9650_COM24 0x8e
+#define OV9650_DBLC1 0x8f
+#define OV9650_RSVD94 0x94
+#define OV9650_RSVD95 0x95
+#define OV9650_RSVD96 0x96
+#define OV9650_LCCFB 0x9d
+#define OV9650_LCCFR 0x9e
+#define OV9650_AECHM 0xa1
+#define OV9650_COM26 0xa5
+#define OV9650_ACOMA8 0xa8
+#define OV9650_ACOMA9 0xa9
+
+#define OV9650_REGISTER_RESET (1 << 7)
+#define OV9650_VGA_SELECT (1 << 6)
+#define OV9650_RGB_SELECT (1 << 2)
+#define OV9650_RAW_RGB_SELECT (1 << 0)
+
+#define OV9650_FAST_AGC_AEC (1 << 7)
+#define OV9650_AEC_UNLIM_STEP_SIZE (1 << 6)
+#define OV9650_BANDING (1 << 5)
+#define OV9650_AGC_EN (1 << 2)
+#define OV9650_AWB_EN (1 << 1)
+#define OV9650_AEC_EN (1 << 0)
+
+#define OV9650_VARIOPIXEL (1 << 2)
+#define OV9650_SYSTEM_CLK_SEL (1 << 7)
+#define OV9650_SLAM_MODE (1 << 4)
+
+#define OV9650_VFLIP (1 << 4)
+#define OV9650_HFLIP (1 << 5)
+
+#define GAIN_DEFAULT 0x14
+#define RED_GAIN_DEFAULT 0x70
+#define BLUE_GAIN_DEFAULT 0x20
+#define EXPOSURE_DEFAULT 0x5003
+
+/*****************************************************************************/
+
+/* Kernel module parameters */
+extern int force_sensor;
+extern int dump_sensor;
+extern unsigned int m5602_debug;
+
+int ov9650_probe(struct sd *sd);
+int ov9650_init(struct sd *sd);
+int ov9650_power_down(struct sd *sd);
+
+int ov9650_read_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+int ov9650_write_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+void ov9650_dump_registers(struct sd *sd);
+
+int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
+int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
+int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
+int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val);
+int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val);
+int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val);
+int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val);
+int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val);
+int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
+int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
+int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
+int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
+int ov9650_get_brightness(struct gspca_dev *gspca_dev, __s32 *val);
+int ov9650_set_brightness(struct gspca_dev *gspca_dev, __s32 val);
+int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev, __s32 *val);
+int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, __s32 val);
+int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val);
+int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val);
+
+static struct m5602_sensor ov9650 = {
+ .name = "OV9650",
+ .i2c_slave_id = 0x60,
+ .probe = ov9650_probe,
+ .init = ov9650_init,
+ .power_down = ov9650_power_down,
+ .read_sensor = ov9650_read_sensor,
+ .write_sensor = ov9650_write_sensor,
+
+ .nctrls = 8,
+ .ctrls = {
+ {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x00,
+ .maximum = 0xffff,
+ .step = 0x1,
+ .default_value = EXPOSURE_DEFAULT,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = ov9650_set_exposure,
+ .get = ov9650_get_exposure
+ }, {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "gain",
+ .minimum = 0x00,
+ .maximum = 0x3ff,
+ .step = 0x1,
+ .default_value = GAIN_DEFAULT,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = ov9650_set_gain,
+ .get = ov9650_get_gain
+ }, {
+ {
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "red balance",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x1,
+ .default_value = RED_GAIN_DEFAULT,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = ov9650_set_red_balance,
+ .get = ov9650_get_red_balance
+ }, {
+ {
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "blue balance",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x1,
+ .default_value = BLUE_GAIN_DEFAULT,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = ov9650_set_blue_balance,
+ .get = ov9650_get_blue_balance
+ }, {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "horizontal flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = ov9650_set_hflip,
+ .get = ov9650_get_hflip
+ }, {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "vertical flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = ov9650_set_vflip,
+ .get = ov9650_get_vflip
+ }, {
+ {
+ .id = V4L2_CID_AUTO_WHITE_BALANCE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "auto white balance",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = ov9650_set_auto_white_balance,
+ .get = ov9650_get_auto_white_balance
+ }, {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "auto gain control",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = ov9650_set_auto_gain,
+ .get = ov9650_get_auto_gain
+ }
+ },
+
+ .nmodes = 1,
+ .modes = {
+ {
+ M5602_DEFAULT_FRAME_WIDTH,
+ M5602_DEFAULT_FRAME_HEIGHT,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ M5602_DEFAULT_FRAME_WIDTH * M5602_DEFAULT_FRAME_HEIGHT,
+ .bytesperline = M5602_DEFAULT_FRAME_WIDTH,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1
+ }
+ }
+};
+
+static const unsigned char preinit_ov9650[][3] =
+{
+ /* [INITCAM] */
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a},
+ /* Reset chip */
+ {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET},
+ /* Enable double clock */
+ {SENSOR, OV9650_CLKRC, 0x80},
+ /* Do something out of spec with the power */
+ {SENSOR, OV9650_OFON, 0x40}
+};
+
+static const unsigned char init_ov9650[][3] =
+{
+ /* [INITCAM] */
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a},
+ /* Reset chip */
+ {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET},
+ /* Enable double clock */
+ {SENSOR, OV9650_CLKRC, 0x80},
+ /* Do something out of spec with the power */
+ {SENSOR, OV9650_OFON, 0x40},
+
+ /* Set QQVGA */
+ {SENSOR, OV9650_COM1, 0x20},
+ /* Set fast AGC/AEC algorithm with unlimited step size */
+ {SENSOR, OV9650_COM8, OV9650_FAST_AGC_AEC |
+ OV9650_AEC_UNLIM_STEP_SIZE |
+ OV9650_AWB_EN | OV9650_AGC_EN},
+
+ {SENSOR, OV9650_CHLF, 0x10},
+ {SENSOR, OV9650_ARBLM, 0xbf},
+ {SENSOR, OV9650_ACOM38, 0x81},
+ /* Turn off color matrix coefficient double option */
+ {SENSOR, OV9650_COM16, 0x00},
+ /* Enable color matrix for RGB/YUV, Delay Y channel,
+ set output Y/UV delay to 1 */
+ {SENSOR, OV9650_COM13, 0x19},
+ /* Enable digital BLC, Set output mode to U Y V Y */
+ {SENSOR, OV9650_TSLB, 0x0c},
+ /* Limit the AGC/AEC stable upper region */
+ {SENSOR, OV9650_COM24, 0x00},
+ /* Enable HREF and some out of spec things */
+ {SENSOR, OV9650_COM12, 0x73},
+ /* Set all DBLC offset signs to positive and
+ do some out of spec stuff */
+ {SENSOR, OV9650_DBLC1, 0xdf},
+ {SENSOR, OV9650_COM21, 0x06},
+ {SENSOR, OV9650_RSVD35, 0x91},
+ /* Necessary, no camera stream without it */
+ {SENSOR, OV9650_RSVD16, 0x06},
+ {SENSOR, OV9650_RSVD94, 0x99},
+ {SENSOR, OV9650_RSVD95, 0x99},
+ {SENSOR, OV9650_RSVD96, 0x04},
+ /* Enable full range output */
+ {SENSOR, OV9650_COM15, 0x0},
+ /* Enable HREF at optical black, enable ADBLC bias,
+ enable ADBLC, reset timings at format change */
+ {SENSOR, OV9650_COM6, 0x4b},
+ /* Subtract 32 from the B channel bias */
+ {SENSOR, OV9650_BBIAS, 0xa0},
+ /* Subtract 32 from the Gb channel bias */
+ {SENSOR, OV9650_GbBIAS, 0xa0},
+ /* Do not bypass the analog BLC and to some out of spec stuff */
+ {SENSOR, OV9650_Gr_COM, 0x00},
+ /* Subtract 32 from the R channel bias */
+ {SENSOR, OV9650_RBIAS, 0xa0},
+ /* Subtract 32 from the R channel bias */
+ {SENSOR, OV9650_RBIAS, 0x0},
+ {SENSOR, OV9650_COM26, 0x80},
+ {SENSOR, OV9650_ACOMA9, 0x98},
+ /* Set the AGC/AEC stable region upper limit */
+ {SENSOR, OV9650_AEW, 0x68},
+ /* Set the AGC/AEC stable region lower limit */
+ {SENSOR, OV9650_AEB, 0x5c},
+ /* Set the high and low limit nibbles to 3 */
+ {SENSOR, OV9650_VPT, 0xc3},
+ /* Set the Automatic Gain Ceiling (AGC) to 128x,
+ drop VSYNC at frame drop,
+ limit exposure timing,
+ drop frame when the AEC step is larger than the exposure gap */
+ {SENSOR, OV9650_COM9, 0x6e},
+ /* Set VSYNC negative, Set RESET to SLHS (slave mode horizontal sync)
+ and set PWDN to SLVS (slave mode vertical sync) */
+ {SENSOR, OV9650_COM10, 0x42},
+ /* Set horizontal column start high to default value */
+ {SENSOR, OV9650_HSTART, 0x1a},
+ /* Set horizontal column end */
+ {SENSOR, OV9650_HSTOP, 0xbf},
+ /* Complementing register to the two writes above */
+ {SENSOR, OV9650_HREF, 0xb2},
+ /* Set vertical row start high bits */
+ {SENSOR, OV9650_VSTRT, 0x02},
+ /* Set vertical row end low bits */
+ {SENSOR, OV9650_VSTOP, 0x7e},
+ /* Set complementing vertical frame control */
+ {SENSOR, OV9650_VREF, 0x10},
+ /* Set raw RGB output format with VGA resolution */
+ {SENSOR, OV9650_COM7, OV9650_VGA_SELECT |
+ OV9650_RGB_SELECT |
+ OV9650_RAW_RGB_SELECT},
+ {SENSOR, OV9650_ADC, 0x04},
+ {SENSOR, OV9650_HV, 0x40},
+ /* Enable denoise, and white-pixel erase */
+ {SENSOR, OV9650_COM22, 0x23},
+
+ /* Set the high bits of the exposure value */
+ {SENSOR, OV9650_AECH, ((EXPOSURE_DEFAULT & 0xff00) >> 8)},
+
+ /* Set the low bits of the exposure value */
+ {SENSOR, OV9650_COM1, (EXPOSURE_DEFAULT & 0xff)},
+ {SENSOR, OV9650_GAIN, GAIN_DEFAULT},
+ {SENSOR, OV9650_BLUE, BLUE_GAIN_DEFAULT},
+ {SENSOR, OV9650_RED, RED_GAIN_DEFAULT},
+
+ {SENSOR, OV9650_COM3, OV9650_VARIOPIXEL},
+ {SENSOR, OV9650_COM5, OV9650_SYSTEM_CLK_SEL},
+
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x82},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_L, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_L, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x09},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xe0},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x5e},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0xde}
+};
+
+static const unsigned char power_down_ov9650[][3] =
+{
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {SENSOR, OV9650_COM7, 0x80},
+ {SENSOR, OV9650_OFON, 0xf4},
+ {SENSOR, OV9650_MVFP, 0x80},
+ {SENSOR, OV9650_DBLV, 0x3f},
+ {SENSOR, OV9650_RSVD36, 0x49},
+ {SENSOR, OV9650_COM7, 0x05},
+
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}
+};
+
+/* Vertically and horizontally flips the image if matched, needed for machines
+ where the sensor is mounted upside down */
+static
+ const
+ struct dmi_system_id ov9650_flip_dmi_table[] = {
+ {
+ .ident = "ASUS A6VC",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A6VC")
+ }
+ },
+ {
+ .ident = "ASUS A6VM",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A6VM")
+ }
+ },
+ {
+ .ident = "ASUS A6JC",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A6JC")
+ }
+ },
+ {
+ .ident = "ASUS A6Kt",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "A6Kt")
+ }
+ },
+ { }
+};
+
+#endif
diff --git a/drivers/media/video/gspca/m5602/m5602_po1030.c b/drivers/media/video/gspca/m5602/m5602_po1030.c
new file mode 100644
index 0000000..08c015b
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/m5602_po1030.c
@@ -0,0 +1,336 @@
+/*
+ * Driver for the po1030 sensor
+ *
+ * Copyright (c) 2008 Erik Andren
+ * Copyright (c) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (c) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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.
+ *
+ */
+
+#include "m5602_po1030.h"
+
+int po1030_probe(struct sd *sd)
+{
+ u8 prod_id = 0, ver_id = 0, i;
+
+ if (force_sensor) {
+ if (force_sensor == PO1030_SENSOR) {
+ info("Forcing a %s sensor", po1030.name);
+ goto sensor_found;
+ }
+ /* If we want to force another sensor, don't try to probe this
+ * one */
+ return -ENODEV;
+ }
+
+ info("Probing for a po1030 sensor");
+
+ /* Run the pre-init to actually probe the unit */
+ for (i = 0; i < ARRAY_SIZE(preinit_po1030); i++) {
+ u8 data = preinit_po1030[i][2];
+ if (preinit_po1030[i][0] == SENSOR)
+ po1030_write_sensor(sd,
+ preinit_po1030[i][1], &data, 1);
+ else
+ m5602_write_bridge(sd, preinit_po1030[i][1], data);
+ }
+
+ if (po1030_read_sensor(sd, 0x3, &prod_id, 1))
+ return -ENODEV;
+
+ if (po1030_read_sensor(sd, 0x4, &ver_id, 1))
+ return -ENODEV;
+
+ if ((prod_id == 0x02) && (ver_id == 0xef)) {
+ info("Detected a po1030 sensor");
+ goto sensor_found;
+ }
+ return -ENODEV;
+
+sensor_found:
+ sd->gspca_dev.cam.cam_mode = po1030.modes;
+ sd->gspca_dev.cam.nmodes = po1030.nmodes;
+ sd->desc->ctrls = po1030.ctrls;
+ sd->desc->nctrls = po1030.nctrls;
+ return 0;
+}
+
+int po1030_read_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len)
+{
+ int err, i;
+
+ do {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
+ } while ((*i2c_data & I2C_BUSY) && !err);
+
+ m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
+ sd->sensor->i2c_slave_id);
+ m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
+ m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x10 + len);
+ m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x08);
+
+ for (i = 0; i < len; i++) {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
+
+ PDEBUG(DBG_TRACE, "Reading sensor register "
+ "0x%x containing 0x%x ", address, *i2c_data);
+ }
+ return (err < 0) ? err : 0;
+}
+
+int po1030_write_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len)
+{
+ int err, i;
+ u8 *p;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ /* The po1030 only supports one byte writes */
+ if (len > 1 || !len)
+ return -EINVAL;
+
+ memcpy(buf, sensor_urb_skeleton, sizeof(sensor_urb_skeleton));
+
+ buf[11] = sd->sensor->i2c_slave_id;
+ buf[15] = address;
+
+ p = buf + 16;
+
+ /* Copy a four byte write sequence for each byte to be written to */
+ for (i = 0; i < len; i++) {
+ memcpy(p, sensor_urb_skeleton + 16, 4);
+ p[3] = i2c_data[i];
+ p += 4;
+ PDEBUG(DBG_TRACE, "Writing sensor register 0x%x with 0x%x",
+ address, i2c_data[i]);
+ }
+
+ /* Copy the footer */
+ memcpy(p, sensor_urb_skeleton + 20, 4);
+
+ /* Set the total length */
+ p[3] = 0x10 + len;
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x19,
+ 0x0000, buf,
+ 20 + len * 4, M5602_URB_MSG_TIMEOUT);
+
+ return (err < 0) ? err : 0;
+}
+
+int po1030_init(struct sd *sd)
+{
+ int i, err = 0;
+
+ /* Init the sensor */
+ for (i = 0; i < ARRAY_SIZE(init_po1030); i++) {
+ u8 data[2] = {0x00, 0x00};
+
+ switch (init_po1030[i][0]) {
+ case BRIDGE:
+ err = m5602_write_bridge(sd,
+ init_po1030[i][1],
+ init_po1030[i][2]);
+ break;
+
+ case SENSOR:
+ data[0] = init_po1030[i][2];
+ err = po1030_write_sensor(sd,
+ init_po1030[i][1], data, 1);
+ break;
+
+ case SENSOR_LONG:
+ data[0] = init_po1030[i][2];
+ data[1] = init_po1030[i][3];
+ err = po1030_write_sensor(sd,
+ init_po1030[i][1], data, 2);
+ break;
+ default:
+ info("Invalid stream command, exiting init");
+ return -EINVAL;
+ }
+ }
+
+ if (dump_sensor)
+ po1030_dump_registers(sd);
+
+ return (err < 0) ? err : 0;
+}
+
+int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 i2c_data;
+ int err;
+
+ err = po1030_read_sensor(sd, PO1030_REG_INTEGLINES_H,
+ &i2c_data, 1);
+ if (err < 0)
+ goto out;
+ *val = (i2c_data << 8);
+
+ err = po1030_read_sensor(sd, PO1030_REG_INTEGLINES_M,
+ &i2c_data, 1);
+ *val |= i2c_data;
+
+ PDEBUG(DBG_V4L2_CID, "Exposure read as %d", *val);
+out:
+ return (err < 0) ? err : 0;
+}
+
+int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 i2c_data;
+ int err;
+
+ PDEBUG(DBG_V4L2, "Set exposure to %d", val & 0xffff);
+
+ i2c_data = ((val & 0xff00) >> 8);
+ PDEBUG(DBG_V4L2, "Set exposure to high byte to 0x%x",
+ i2c_data);
+
+ err = po1030_write_sensor(sd, PO1030_REG_INTEGLINES_H,
+ &i2c_data, 1);
+ if (err < 0)
+ goto out;
+
+ i2c_data = (val & 0xff);
+ PDEBUG(DBG_V4L2, "Set exposure to low byte to 0x%x",
+ i2c_data);
+ err = po1030_write_sensor(sd, PO1030_REG_INTEGLINES_M,
+ &i2c_data, 1);
+
+out:
+ return (err < 0) ? err : 0;
+}
+
+int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 i2c_data;
+ int err;
+
+ err = po1030_read_sensor(sd, PO1030_REG_GLOBALGAIN,
+ &i2c_data, 1);
+ *val = i2c_data;
+ PDEBUG(DBG_V4L2_CID, "Read global gain %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 i2c_data;
+ int err;
+
+ i2c_data = val & 0xff;
+ PDEBUG(DBG_V4L2, "Set global gain to %d", i2c_data);
+ err = po1030_write_sensor(sd, PO1030_REG_GLOBALGAIN,
+ &i2c_data, 1);
+ return (err < 0) ? err : 0;
+}
+
+int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 i2c_data;
+ int err;
+
+ err = po1030_read_sensor(sd, PO1030_REG_RED_GAIN,
+ &i2c_data, 1);
+ *val = i2c_data;
+ PDEBUG(DBG_V4L2_CID, "Read red gain %d", *val);
+ return (err < 0) ? err : 0;
+}
+
+int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 i2c_data;
+ int err;
+
+ i2c_data = val & 0xff;
+ PDEBUG(DBG_V4L2, "Set red gain to %d", i2c_data);
+ err = po1030_write_sensor(sd, PO1030_REG_RED_GAIN,
+ &i2c_data, 1);
+ return (err < 0) ? err : 0;
+}
+
+int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 i2c_data;
+ int err;
+
+ err = po1030_read_sensor(sd, PO1030_REG_BLUE_GAIN,
+ &i2c_data, 1);
+ *val = i2c_data;
+ PDEBUG(DBG_V4L2_CID, "Read blue gain %d", *val);
+
+ return (err < 0) ? err : 0;
+}
+
+int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 i2c_data;
+ int err;
+ i2c_data = val & 0xff;
+ PDEBUG(DBG_V4L2, "Set blue gain to %d", i2c_data);
+ err = po1030_write_sensor(sd, PO1030_REG_BLUE_GAIN,
+ &i2c_data, 1);
+
+ return (err < 0) ? err : 0;
+}
+
+int po1030_power_down(struct sd *sd)
+{
+ return 0;
+}
+
+void po1030_dump_registers(struct sd *sd)
+{
+ int address;
+ u8 value = 0;
+
+ info("Dumping the po1030 sensor core registers");
+ for (address = 0; address < 0x7f; address++) {
+ po1030_read_sensor(sd, address, &value, 1);
+ info("register 0x%x contains 0x%x",
+ address, value);
+ }
+
+ info("po1030 register state dump complete");
+
+ info("Probing for which registers that are read/write");
+ for (address = 0; address < 0xff; address++) {
+ u8 old_value, ctrl_value;
+ u8 test_value[2] = {0xff, 0xff};
+
+ po1030_read_sensor(sd, address, &old_value, 1);
+ po1030_write_sensor(sd, address, test_value, 1);
+ po1030_read_sensor(sd, address, &ctrl_value, 1);
+
+ if (ctrl_value == test_value[0])
+ info("register 0x%x is writeable", address);
+ else
+ info("register 0x%x is read only", address);
+
+ /* Restore original value */
+ po1030_write_sensor(sd, address, &old_value, 1);
+ }
+}
diff --git a/drivers/media/video/gspca/m5602/m5602_po1030.h b/drivers/media/video/gspca/m5602/m5602_po1030.h
new file mode 100644
index 0000000..68f34c9
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/m5602_po1030.h
@@ -0,0 +1,478 @@
+/*
+ * Driver for the po1030 sensor.
+ * This is probably a pixel plus sensor but we haven't identified it yet
+ *
+ * Copyright (c) 2008 Erik Andren
+ * Copyright (c) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (c) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * Register defines taken from Pascal Stangs Proxycon Armlib
+ *
+ * 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.
+ *
+ */
+
+#ifndef M5602_PO1030_H_
+#define M5602_PO1030_H_
+
+#include "m5602_sensor.h"
+
+/*****************************************************************************/
+
+#define PO1030_REG_DEVID_H 0x00
+#define PO1030_REG_DEVID_L 0x01
+#define PO1030_REG_FRAMEWIDTH_H 0x04
+#define PO1030_REG_FRAMEWIDTH_L 0x05
+#define PO1030_REG_FRAMEHEIGHT_H 0x06
+#define PO1030_REG_FRAMEHEIGHT_L 0x07
+#define PO1030_REG_WINDOWX_H 0x08
+#define PO1030_REG_WINDOWX_L 0x09
+#define PO1030_REG_WINDOWY_H 0x0a
+#define PO1030_REG_WINDOWY_L 0x0b
+#define PO1030_REG_WINDOWWIDTH_H 0x0c
+#define PO1030_REG_WINDOWWIDTH_L 0x0d
+#define PO1030_REG_WINDOWHEIGHT_H 0x0e
+#define PO1030_REG_WINDOWHEIGHT_L 0x0f
+
+#define PO1030_REG_GLOBALIBIAS 0x12
+#define PO1030_REG_PIXELIBIAS 0x13
+
+#define PO1030_REG_GLOBALGAIN 0x15
+#define PO1030_REG_RED_GAIN 0x16
+#define PO1030_REG_GREEN_1_GAIN 0x17
+#define PO1030_REG_BLUE_GAIN 0x18
+#define PO1030_REG_GREEN_2_GAIN 0x19
+
+#define PO1030_REG_INTEGLINES_H 0x1a
+#define PO1030_REG_INTEGLINES_M 0x1b
+#define PO1030_REG_INTEGLINES_L 0x1c
+
+#define PO1030_REG_CONTROL1 0x1d
+#define PO1030_REG_CONTROL2 0x1e
+#define PO1030_REG_CONTROL3 0x1f
+#define PO1030_REG_CONTROL4 0x20
+
+#define PO1030_REG_PERIOD50_H 0x23
+#define PO1030_REG_PERIOD50_L 0x24
+#define PO1030_REG_PERIOD60_H 0x25
+#define PO1030_REG_PERIOD60_L 0x26
+#define PO1030_REG_REGCLK167 0x27
+#define PO1030_REG_DELTA50 0x28
+#define PO1030_REG_DELTA60 0x29
+
+#define PO1030_REG_ADCOFFSET 0x2c
+
+/* Gamma Correction Coeffs */
+#define PO1030_REG_GC0 0x2d
+#define PO1030_REG_GC1 0x2e
+#define PO1030_REG_GC2 0x2f
+#define PO1030_REG_GC3 0x30
+#define PO1030_REG_GC4 0x31
+#define PO1030_REG_GC5 0x32
+#define PO1030_REG_GC6 0x33
+#define PO1030_REG_GC7 0x34
+
+/* Color Transform Matrix */
+#define PO1030_REG_CT0 0x35
+#define PO1030_REG_CT1 0x36
+#define PO1030_REG_CT2 0x37
+#define PO1030_REG_CT3 0x38
+#define PO1030_REG_CT4 0x39
+#define PO1030_REG_CT5 0x3a
+#define PO1030_REG_CT6 0x3b
+#define PO1030_REG_CT7 0x3c
+#define PO1030_REG_CT8 0x3d
+
+#define PO1030_REG_AUTOCTRL1 0x3e
+#define PO1030_REG_AUTOCTRL2 0x3f
+
+#define PO1030_REG_YTARGET 0x40
+#define PO1030_REG_GLOBALGAINMIN 0x41
+#define PO1030_REG_GLOBALGAINMAX 0x42
+
+/* Output format control */
+#define PO1030_REG_OUTFORMCTRL1 0x5a
+#define PO1030_REG_OUTFORMCTRL2 0x5b
+#define PO1030_REG_OUTFORMCTRL3 0x5c
+#define PO1030_REG_OUTFORMCTRL4 0x5d
+#define PO1030_REG_OUTFORMCTRL5 0x5e
+
+/* Imaging coefficients */
+#define PO1030_REG_YBRIGHT 0x73
+#define PO1030_REG_YCONTRAST 0x74
+#define PO1030_REG_YSATURATION 0x75
+
+/*****************************************************************************/
+
+#define PO1030_GLOBAL_GAIN_DEFAULT 0x12
+#define PO1030_EXPOSURE_DEFAULT 0xf0ff
+#define PO1030_BLUE_GAIN_DEFAULT 0x40
+#define PO1030_RED_GAIN_DEFAULT 0x40
+
+/*****************************************************************************/
+
+/* Kernel module parameters */
+extern int force_sensor;
+extern int dump_sensor;
+extern unsigned int m5602_debug;
+
+int po1030_probe(struct sd *sd);
+int po1030_init(struct sd *sd);
+int po1030_power_down(struct sd *sd);
+
+void po1030_dump_registers(struct sd *sd);
+
+int po1030_read_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+int po1030_write_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
+int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
+int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
+int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val);
+int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val);
+int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val);
+int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val);
+int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val);
+
+static struct m5602_sensor po1030 = {
+ .name = "PO1030",
+
+ .i2c_slave_id = 0xdc,
+
+ .probe = po1030_probe,
+ .init = po1030_init,
+ .power_down = po1030_power_down,
+
+ .nctrls = 4,
+ .ctrls = {
+ {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "gain",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x1,
+ .default_value = PO1030_GLOBAL_GAIN_DEFAULT,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = po1030_set_gain,
+ .get = po1030_get_gain
+ }, {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "exposure",
+ .minimum = 0x00,
+ .maximum = 0xffff,
+ .step = 0x1,
+ .default_value = PO1030_EXPOSURE_DEFAULT,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = po1030_set_exposure,
+ .get = po1030_get_exposure
+ }, {
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "red balance",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x1,
+ .default_value = PO1030_RED_GAIN_DEFAULT,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = po1030_set_red_balance,
+ .get = po1030_get_red_balance
+ }, {
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "blue balance",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x1,
+ .default_value = PO1030_BLUE_GAIN_DEFAULT,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = po1030_set_blue_balance,
+ .get = po1030_get_blue_balance
+ }
+ },
+ .nmodes = 1,
+ .modes = {
+ {
+ M5602_DEFAULT_FRAME_WIDTH,
+ M5602_DEFAULT_FRAME_HEIGHT,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ M5602_DEFAULT_FRAME_WIDTH * M5602_DEFAULT_FRAME_HEIGHT,
+ .bytesperline = M5602_DEFAULT_FRAME_WIDTH,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1
+ }
+ }
+};
+
+static const unsigned char preinit_po1030[][3] =
+{
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d},
+ {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+
+ {SENSOR, PO1030_REG_AUTOCTRL2, 0x24},
+
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x02},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xec},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x02},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x87},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00},
+
+ {SENSOR, PO1030_REG_AUTOCTRL2, 0x24},
+
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x00}
+};
+
+static const unsigned char init_po1030[][4] =
+{
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
+ /*sequence 1*/
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d},
+
+ {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+ /*end of sequence 1*/
+
+ /*sequence 2 (same as stop sequence)*/
+ {SENSOR, PO1030_REG_AUTOCTRL2, 0x24},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ /*end of sequence 2*/
+
+ /*sequence 5*/
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x02},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xec},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x02},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x87},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00},
+ /*end of sequence 5*/
+
+ /*sequence 2 stop */
+ {SENSOR, PO1030_REG_AUTOCTRL2, 0x24},
+
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x04},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ /*end of sequence 2 stop */
+
+/* ---------------------------------
+ * end of init - begin of start
+ * --------------------------------- */
+
+ /*sequence 3*/
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+ /*end of sequence 3*/
+ /*sequence 4*/
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x05},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
+
+ {SENSOR, PO1030_REG_AUTOCTRL2, 0x04},
+
+ /* Set the width to 751 */
+ {SENSOR, PO1030_REG_FRAMEWIDTH_H, 0x02},
+ {SENSOR, PO1030_REG_FRAMEWIDTH_L, 0xef},
+
+ /* Set the height to 540 */
+ {SENSOR, PO1030_REG_FRAMEHEIGHT_H, 0x02},
+ {SENSOR, PO1030_REG_FRAMEHEIGHT_L, 0x1c},
+
+ /* Set the x window to 1 */
+ {SENSOR, PO1030_REG_WINDOWX_H, 0x00},
+ {SENSOR, PO1030_REG_WINDOWX_L, 0x01},
+
+ /* Set the y window to 1 */
+ {SENSOR, PO1030_REG_WINDOWY_H, 0x00},
+ {SENSOR, PO1030_REG_WINDOWX_L, 0x01},
+
+ {SENSOR, PO1030_REG_WINDOWWIDTH_H, 0x02},
+ {SENSOR, PO1030_REG_WINDOWWIDTH_L, 0x87},
+ {SENSOR, PO1030_REG_WINDOWHEIGHT_H, 0x01},
+ {SENSOR, PO1030_REG_WINDOWHEIGHT_L, 0xe3},
+
+ {SENSOR, PO1030_REG_OUTFORMCTRL2, 0x04},
+ {SENSOR, PO1030_REG_OUTFORMCTRL2, 0x04},
+ {SENSOR, PO1030_REG_AUTOCTRL1, 0x08},
+ {SENSOR, PO1030_REG_CONTROL2, 0x03},
+ {SENSOR, 0x21, 0x90},
+ {SENSOR, PO1030_REG_YTARGET, 0x60},
+ {SENSOR, 0x59, 0x13},
+ {SENSOR, PO1030_REG_OUTFORMCTRL1, 0x40},
+ {SENSOR, 0x5f, 0x00},
+ {SENSOR, 0x60, 0x80},
+ {SENSOR, 0x78, 0x14},
+ {SENSOR, 0x6f, 0x01},
+ {SENSOR, PO1030_REG_CONTROL1, 0x18},
+ {SENSOR, PO1030_REG_GLOBALGAINMAX, 0x14},
+ {SENSOR, 0x63, 0x38},
+ {SENSOR, 0x64, 0x38},
+ {SENSOR, PO1030_REG_CONTROL1, 0x58},
+ {SENSOR, PO1030_REG_RED_GAIN, 0x30},
+ {SENSOR, PO1030_REG_GREEN_1_GAIN, 0x30},
+ {SENSOR, PO1030_REG_BLUE_GAIN, 0x30},
+ {SENSOR, PO1030_REG_GREEN_2_GAIN, 0x30},
+ {SENSOR, PO1030_REG_GC0, 0x10},
+ {SENSOR, PO1030_REG_GC1, 0x20},
+ {SENSOR, PO1030_REG_GC2, 0x40},
+ {SENSOR, PO1030_REG_GC3, 0x60},
+ {SENSOR, PO1030_REG_GC4, 0x80},
+ {SENSOR, PO1030_REG_GC5, 0xa0},
+ {SENSOR, PO1030_REG_GC6, 0xc0},
+ {SENSOR, PO1030_REG_GC7, 0xff},
+ /*end of sequence 4*/
+ /*sequence 5*/
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x02},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xec},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x7e},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00},
+ /*end of sequence 5*/
+
+ /*sequence 6*/
+ /* Changing 40 in f0 the image becomes green in bayer mode and red in
+ * rgb mode */
+ {SENSOR, PO1030_REG_RED_GAIN, PO1030_RED_GAIN_DEFAULT},
+ /* in changing 40 in f0 the image becomes green in bayer mode and red in
+ * rgb mode */
+ {SENSOR, PO1030_REG_BLUE_GAIN, PO1030_BLUE_GAIN_DEFAULT},
+
+ /* with a very low lighted environment increase the exposure but
+ * decrease the FPS (Frame Per Second) */
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
+
+ /* Controls high exposure more than SENSOR_LOW_EXPOSURE, use only in
+ * low lighted environment (f0 is more than ff ?)*/
+ {SENSOR, PO1030_REG_INTEGLINES_H, ((PO1030_EXPOSURE_DEFAULT >> 2)
+ & 0xff)},
+
+ /* Controls middle exposure, use only in high lighted environment */
+ {SENSOR, PO1030_REG_INTEGLINES_M, PO1030_EXPOSURE_DEFAULT & 0xff},
+
+ /* Controls clarity (not sure) */
+ {SENSOR, PO1030_REG_INTEGLINES_L, 0x00},
+ /* Controls gain (the image is more lighted) */
+ {SENSOR, PO1030_REG_GLOBALGAIN, PO1030_GLOBAL_GAIN_DEFAULT},
+
+ /* Sets the width */
+ {SENSOR, PO1030_REG_FRAMEWIDTH_H, 0x02},
+ {SENSOR, PO1030_REG_FRAMEWIDTH_L, 0xef}
+ /*end of sequence 6*/
+};
+
+#endif
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
new file mode 100644
index 0000000..6820256
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
@@ -0,0 +1,463 @@
+/*
+ * Driver for the s5k4aa sensor
+ *
+ * Copyright (C) 2008 Erik Andren
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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.
+ *
+ */
+
+#include "m5602_s5k4aa.h"
+
+int s5k4aa_probe(struct sd *sd)
+{
+ u8 prod_id[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ const u8 expected_prod_id[6] = {0x00, 0x10, 0x00, 0x4b, 0x33, 0x75};
+ int i, err = 0;
+
+ if (force_sensor) {
+ if (force_sensor == S5K4AA_SENSOR) {
+ info("Forcing a %s sensor", s5k4aa.name);
+ goto sensor_found;
+ }
+ /* If we want to force another sensor, don't try to probe this
+ * one */
+ return -ENODEV;
+ }
+
+ info("Probing for a s5k4aa sensor");
+
+ /* Preinit the sensor */
+ for (i = 0; i < ARRAY_SIZE(preinit_s5k4aa) && !err; i++) {
+ u8 data[2] = {0x00, 0x00};
+
+ switch (preinit_s5k4aa[i][0]) {
+ case BRIDGE:
+ err = m5602_write_bridge(sd,
+ preinit_s5k4aa[i][1],
+ preinit_s5k4aa[i][2]);
+ break;
+
+ case SENSOR:
+ data[0] = preinit_s5k4aa[i][2];
+ err = s5k4aa_write_sensor(sd,
+ preinit_s5k4aa[i][1],
+ data, 1);
+ break;
+
+ case SENSOR_LONG:
+ data[0] = preinit_s5k4aa[i][2];
+ data[1] = preinit_s5k4aa[i][3];
+ err = s5k4aa_write_sensor(sd,
+ preinit_s5k4aa[i][1],
+ data, 2);
+ break;
+ default:
+ info("Invalid stream command, exiting init");
+ return -EINVAL;
+ }
+ }
+
+ /* Test some registers, but we don't know their exact meaning yet */
+ if (s5k4aa_read_sensor(sd, 0x00, prod_id, sizeof(prod_id)))
+ return -ENODEV;
+
+ if (memcmp(prod_id, expected_prod_id, sizeof(prod_id)))
+ return -ENODEV;
+ else
+ info("Detected a s5k4aa sensor");
+sensor_found:
+ sd->gspca_dev.cam.cam_mode = s5k4aa.modes;
+ sd->gspca_dev.cam.nmodes = s5k4aa.nmodes;
+ sd->desc->ctrls = s5k4aa.ctrls;
+ sd->desc->nctrls = s5k4aa.nctrls;
+
+ return 0;
+}
+
+int s5k4aa_read_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len)
+{
+ int err, i;
+
+ do {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
+ } while ((*i2c_data & I2C_BUSY) && !err);
+ if (err < 0)
+ goto out;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
+ sd->sensor->i2c_slave_id);
+ if (err < 0)
+ goto out;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
+ if (err < 0)
+ goto out;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x18 + len);
+ if (err < 0)
+ goto out;
+
+ do {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
+ } while ((*i2c_data & I2C_BUSY) && !err);
+ if (err < 0)
+ goto out;
+
+ for (i = 0; (i < len) & !err; i++) {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
+
+ PDEBUG(DBG_TRACE, "Reading sensor register "
+ "0x%x containing 0x%x ", address, *i2c_data);
+ }
+out:
+ return (err < 0) ? err : 0;
+}
+
+int s5k4aa_write_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len)
+{
+ int err, i;
+ u8 *p;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ /* No sensor with a data width larger than 16 bits has yet been seen */
+ if (len > 2 || !len)
+ return -EINVAL;
+
+ memcpy(buf, sensor_urb_skeleton,
+ sizeof(sensor_urb_skeleton));
+
+ buf[11] = sd->sensor->i2c_slave_id;
+ buf[15] = address;
+
+ /* Special case larger sensor writes */
+ p = buf + 16;
+
+ /* Copy a four byte write sequence for each byte to be written to */
+ for (i = 0; i < len; i++) {
+ memcpy(p, sensor_urb_skeleton + 16, 4);
+ p[3] = i2c_data[i];
+ p += 4;
+ PDEBUG(DBG_TRACE, "Writing sensor register 0x%x with 0x%x",
+ address, i2c_data[i]);
+ }
+
+ /* Copy the tailer */
+ memcpy(p, sensor_urb_skeleton + 20, 4);
+
+ /* Set the total length */
+ p[3] = 0x10 + len;
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x19,
+ 0x0000, buf,
+ 20 + len * 4, M5602_URB_MSG_TIMEOUT);
+
+ return (err < 0) ? err : 0;
+}
+
+int s5k4aa_init(struct sd *sd)
+{
+ int i, err = 0;
+
+ for (i = 0; i < ARRAY_SIZE(init_s5k4aa) && !err; i++) {
+ u8 data[2] = {0x00, 0x00};
+
+ switch (init_s5k4aa[i][0]) {
+ case BRIDGE:
+ err = m5602_write_bridge(sd,
+ init_s5k4aa[i][1],
+ init_s5k4aa[i][2]);
+ break;
+
+ case SENSOR:
+ data[0] = init_s5k4aa[i][2];
+ err = s5k4aa_write_sensor(sd,
+ init_s5k4aa[i][1], data, 1);
+ break;
+
+ case SENSOR_LONG:
+ data[0] = init_s5k4aa[i][2];
+ data[1] = init_s5k4aa[i][3];
+ err = s5k4aa_write_sensor(sd,
+ init_s5k4aa[i][1], data, 2);
+ break;
+ default:
+ info("Invalid stream command, exiting init");
+ return -EINVAL;
+ }
+ }
+
+ if (dump_sensor)
+ s5k4aa_dump_registers(sd);
+
+ if (!err && dmi_check_system(s5k4aa_vflip_dmi_table)) {
+ u8 data = 0x02;
+ info("vertical flip quirk active");
+ s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ s5k4aa_read_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ data |= S5K4AA_RM_V_FLIP;
+ data &= ~S5K4AA_RM_H_FLIP;
+ s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+
+ /* Decrement COLSTART to preserve color order (BGGR) */
+ s5k4aa_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ data--;
+ s5k4aa_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+
+ /* Increment ROWSTART to preserve color order (BGGR) */
+ s5k4aa_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ data++;
+ s5k4aa_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ }
+
+ return (err < 0) ? err : 0;
+}
+
+int s5k4aa_power_down(struct sd *sd)
+{
+ return 0;
+}
+
+int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data = S5K4AA_PAGE_MAP_2;
+ int err;
+
+ err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ if (err < 0)
+ goto out;
+
+ err = s5k4aa_read_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1);
+ if (err < 0)
+ goto out;
+
+ *val = data << 8;
+ err = s5k4aa_read_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1);
+ *val |= data;
+ PDEBUG(DBG_V4L2_CID, "Read exposure %d", *val);
+out:
+ return (err < 0) ? err : 0;
+}
+
+int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data = S5K4AA_PAGE_MAP_2;
+ int err;
+
+ PDEBUG(DBG_V4L2_CID, "Set exposure to %d", val);
+ err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ if (err < 0)
+ goto out;
+ data = (val >> 8) & 0xff;
+ err = s5k4aa_write_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1);
+ if (err < 0)
+ goto out;
+ data = val & 0xff;
+ err = s5k4aa_write_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1);
+out:
+ return (err < 0) ? err : 0;
+}
+
+int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data = S5K4AA_PAGE_MAP_2;
+ int err;
+
+ err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ if (err < 0)
+ goto out;
+
+ err = s5k4aa_read_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ *val = (data & S5K4AA_RM_V_FLIP) >> 7;
+ PDEBUG(DBG_V4L2_CID, "Read vertical flip %d", *val);
+
+out:
+ return (err < 0) ? err : 0;
+}
+
+int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data = S5K4AA_PAGE_MAP_2;
+ int err;
+
+ PDEBUG(DBG_V4L2_CID, "Set vertical flip to %d", val);
+ err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ if (err < 0)
+ goto out;
+ err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ if (err < 0)
+ goto out;
+ data = ((data & ~S5K4AA_RM_V_FLIP)
+ | ((val & 0x01) << 7));
+ err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ if (err < 0)
+ goto out;
+
+ if (val) {
+ err = s5k4aa_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ if (err < 0)
+ goto out;
+
+ data++;
+ err = s5k4aa_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ } else {
+ err = s5k4aa_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ if (err < 0)
+ goto out;
+
+ data--;
+ err = s5k4aa_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ }
+out:
+ return (err < 0) ? err : 0;
+}
+
+int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data = S5K4AA_PAGE_MAP_2;
+ int err;
+
+ err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ if (err < 0)
+ goto out;
+
+ err = s5k4aa_read_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ *val = (data & S5K4AA_RM_H_FLIP) >> 6;
+ PDEBUG(DBG_V4L2_CID, "Read horizontal flip %d", *val);
+out:
+ return (err < 0) ? err : 0;
+}
+
+int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data = S5K4AA_PAGE_MAP_2;
+ int err;
+
+ PDEBUG(DBG_V4L2_CID, "Set horizontal flip to %d",
+ val);
+ err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ if (err < 0)
+ goto out;
+ err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ if (err < 0)
+ goto out;
+
+ data = ((data & ~S5K4AA_RM_H_FLIP) | ((val & 0x01) << 6));
+ err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
+ if (err < 0)
+ goto out;
+
+ if (val) {
+ err = s5k4aa_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ if (err < 0)
+ goto out;
+ data++;
+ err = s5k4aa_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ if (err < 0)
+ goto out;
+ } else {
+ err = s5k4aa_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ if (err < 0)
+ goto out;
+ data--;
+ err = s5k4aa_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ }
+out:
+ return (err < 0) ? err : 0;
+}
+
+int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data = S5K4AA_PAGE_MAP_2;
+ int err;
+
+ err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ if (err < 0)
+ goto out;
+
+ err = s5k4aa_read_sensor(sd, S5K4AA_GAIN_2, &data, 1);
+ *val = data;
+ PDEBUG(DBG_V4L2_CID, "Read gain %d", *val);
+
+out:
+ return (err < 0) ? err : 0;
+}
+
+int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data = S5K4AA_PAGE_MAP_2;
+ int err;
+
+ PDEBUG(DBG_V4L2_CID, "Set gain to %d", val);
+ err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
+ if (err < 0)
+ goto out;
+
+ data = val & 0xff;
+ err = s5k4aa_write_sensor(sd, S5K4AA_GAIN_2, &data, 1);
+
+out:
+ return (err < 0) ? err : 0;
+}
+
+void s5k4aa_dump_registers(struct sd *sd)
+{
+ int address;
+ u8 page, old_page;
+ s5k4aa_read_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
+ for (page = 0; page < 16; page++) {
+ s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
+ info("Dumping the s5k4aa register state for page 0x%x", page);
+ for (address = 0; address <= 0xff; address++) {
+ u8 value = 0;
+ s5k4aa_read_sensor(sd, address, &value, 1);
+ info("register 0x%x contains 0x%x",
+ address, value);
+ }
+ }
+ info("s5k4aa register state dump complete");
+
+ for (page = 0; page < 16; page++) {
+ s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
+ info("Probing for which registers that are "
+ "read/write for page 0x%x", page);
+ for (address = 0; address <= 0xff; address++) {
+ u8 old_value, ctrl_value, test_value = 0xff;
+
+ s5k4aa_read_sensor(sd, address, &old_value, 1);
+ s5k4aa_write_sensor(sd, address, &test_value, 1);
+ s5k4aa_read_sensor(sd, address, &ctrl_value, 1);
+
+ if (ctrl_value == test_value)
+ info("register 0x%x is writeable", address);
+ else
+ info("register 0x%x is read only", address);
+
+ /* Restore original value */
+ s5k4aa_write_sensor(sd, address, &old_value, 1);
+ }
+ }
+ info("Read/write register probing complete");
+ s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
+}
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.h b/drivers/media/video/gspca/m5602/m5602_s5k4aa.h
new file mode 100644
index 0000000..bb7f7e3
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/m5602_s5k4aa.h
@@ -0,0 +1,370 @@
+/*
+ * Driver for the s5k4aa sensor
+ *
+ * Copyright (C) 2008 Erik Andren
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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.
+ *
+ */
+
+#ifndef M5602_S5K4AA_H_
+#define M5602_S5K4AA_H_
+
+#include <linux/dmi.h>
+
+#include "m5602_sensor.h"
+
+/*****************************************************************************/
+
+#define S5K4AA_PAGE_MAP 0xec
+
+#define S5K4AA_PAGE_MAP_0 0x00
+#define S5K4AA_PAGE_MAP_1 0x01
+#define S5K4AA_PAGE_MAP_2 0x02
+
+/* Sensor register definitions for page 0x02 */
+#define S5K4AA_READ_MODE 0x03
+#define S5K4AA_ROWSTART_HI 0x04
+#define S5K4AA_ROWSTART_LO 0x05
+#define S5K4AA_COLSTART_HI 0x06
+#define S5K4AA_COLSTART_LO 0x07
+#define S5K4AA_WINDOW_HEIGHT_HI 0x08
+#define S5K4AA_WINDOW_HEIGHT_LO 0x09
+#define S5K4AA_WINDOW_WIDTH_HI 0x0a
+#define S5K4AA_WINDOW_WIDTH_LO 0x0b
+#define S5K4AA_GLOBAL_GAIN__ 0x0f /* Only a guess ATM !!! */
+#define S5K4AA_H_BLANK_HI__ 0x1d /* Only a guess ATM !!! sync lost
+ if too low, reduces frame rate
+ if too high */
+#define S5K4AA_H_BLANK_LO__ 0x1e /* Only a guess ATM !!! */
+#define S5K4AA_EXPOSURE_HI 0x17
+#define S5K4AA_EXPOSURE_LO 0x18
+#define S5K4AA_GAIN_1 0x1f /* (digital?) gain : 5 bits */
+#define S5K4AA_GAIN_2 0x20 /* (analogue?) gain : 7 bits */
+
+#define S5K4AA_RM_ROW_SKIP_4X 0x08
+#define S5K4AA_RM_ROW_SKIP_2X 0x04
+#define S5K4AA_RM_COL_SKIP_4X 0x02
+#define S5K4AA_RM_COL_SKIP_2X 0x01
+#define S5K4AA_RM_H_FLIP 0x40
+#define S5K4AA_RM_V_FLIP 0x80
+
+/*****************************************************************************/
+
+/* Kernel module parameters */
+extern int force_sensor;
+extern int dump_sensor;
+extern unsigned int m5602_debug;
+
+int s5k4aa_probe(struct sd *sd);
+int s5k4aa_init(struct sd *sd);
+int s5k4aa_power_down(struct sd *sd);
+
+void s5k4aa_dump_registers(struct sd *sd);
+
+int s5k4aa_read_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+int s5k4aa_write_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
+int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
+int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
+int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
+int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
+int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
+int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
+int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val);
+
+static struct m5602_sensor s5k4aa = {
+ .name = "S5K4AA",
+ .probe = s5k4aa_probe,
+ .init = s5k4aa_init,
+ .power_down = s5k4aa_power_down,
+ .read_sensor = s5k4aa_read_sensor,
+ .write_sensor = s5k4aa_write_sensor,
+ .i2c_slave_id = 0x5a,
+ .nctrls = 4,
+ .ctrls = {
+ {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "vertical flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = s5k4aa_set_vflip,
+ .get = s5k4aa_get_vflip
+
+ }, {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "horizontal flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = s5k4aa_set_hflip,
+ .get = s5k4aa_get_hflip
+
+ }, {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 0xa0,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = s5k4aa_set_gain,
+ .get = s5k4aa_get_gain
+ }, {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 13,
+ .maximum = 0xfff,
+ .step = 1,
+ .default_value = 0x100,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = s5k4aa_set_exposure,
+ .get = s5k4aa_get_exposure
+ }
+ },
+
+ .nmodes = 1,
+ .modes = {
+ {
+ M5602_DEFAULT_FRAME_WIDTH,
+ M5602_DEFAULT_FRAME_HEIGHT,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ M5602_DEFAULT_FRAME_WIDTH * M5602_DEFAULT_FRAME_HEIGHT,
+ .bytesperline = M5602_DEFAULT_FRAME_WIDTH,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1
+ }
+ }
+};
+
+static const unsigned char preinit_s5k4aa[][4] =
+{
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
+
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
+
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x14, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
+
+ {SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00}
+};
+
+static const unsigned char init_s5k4aa[][4] =
+{
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
+
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
+
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x14, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
+
+ {SENSOR, S5K4AA_PAGE_MAP, 0x07, 0x00},
+ {SENSOR, 0x36, 0x01, 0x00},
+ {SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, 0x7b, 0xff, 0x00},
+ {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+ {SENSOR, 0x0c, 0x05, 0x00},
+ {SENSOR, 0x02, 0x0e, 0x00},
+ {SENSOR, S5K4AA_GAIN_1, 0x0f, 0x00},
+ {SENSOR, S5K4AA_GAIN_2, 0x00, 0x00},
+ {SENSOR, S5K4AA_GLOBAL_GAIN__, 0x01, 0x00},
+ {SENSOR, 0x11, 0x00, 0x00},
+ {SENSOR, 0x12, 0x00, 0x00},
+ {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+ {SENSOR, S5K4AA_READ_MODE, 0xa0, 0x00},
+ {SENSOR, 0x37, 0x00, 0x00},
+ {SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00},
+ {SENSOR, S5K4AA_ROWSTART_LO, 0x2a, 0x00},
+ {SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00},
+ {SENSOR, S5K4AA_COLSTART_LO, 0x0b, 0x00},
+ {SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x03, 0x00},
+ {SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0xc4, 0x00},
+ {SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00},
+ {SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x08, 0x00},
+ {SENSOR, S5K4AA_H_BLANK_HI__, 0x00, 0x00},
+ {SENSOR, S5K4AA_H_BLANK_LO__, 0x48, 0x00},
+ {SENSOR, S5K4AA_EXPOSURE_HI, 0x00, 0x00},
+ {SENSOR, S5K4AA_EXPOSURE_LO, 0x43, 0x00},
+ {SENSOR, 0x11, 0x04, 0x00},
+ {SENSOR, 0x12, 0xc3, 0x00},
+ {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ /* VSYNC_PARA, VSYNC_PARA : img height 480 = 0x01e0 */
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xe0, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ /* HSYNC_PARA, HSYNC_PARA : img width 640 = 0x0280 */
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x80, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, /* 48 MHz */
+
+ {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+ {SENSOR, S5K4AA_READ_MODE, S5K4AA_RM_H_FLIP | S5K4AA_RM_ROW_SKIP_2X
+ | S5K4AA_RM_COL_SKIP_2X, 0x00},
+ /* 0x37 : Fix image stability when light is too bright and improves
+ * image quality in 640x480, but worsens it in 1280x1024 */
+ {SENSOR, 0x37, 0x01, 0x00},
+ /* ROWSTART_HI, ROWSTART_LO : 10 + (1024-960)/2 = 42 = 0x002a */
+ {SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00},
+ {SENSOR, S5K4AA_ROWSTART_LO, 0x2a, 0x00},
+ {SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00},
+ {SENSOR, S5K4AA_COLSTART_LO, 0x0c, 0x00},
+ /* window_height_hi, window_height_lo : 960 = 0x03c0 */
+ {SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x03, 0x00},
+ {SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0xc0, 0x00},
+ /* window_width_hi, window_width_lo : 1280 = 0x0500 */
+ {SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00},
+ {SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x00, 0x00},
+ {SENSOR, S5K4AA_H_BLANK_HI__, 0x00, 0x00},
+ {SENSOR, S5K4AA_H_BLANK_LO__, 0xa8, 0x00}, /* helps to sync... */
+ {SENSOR, S5K4AA_EXPOSURE_HI, 0x01, 0x00},
+ {SENSOR, S5K4AA_EXPOSURE_LO, 0x00, 0x00},
+ {SENSOR, 0x11, 0x04, 0x00},
+ {SENSOR, 0x12, 0xc3, 0x00},
+ {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00},
+ {SENSOR, 0x02, 0x0e, 0x00},
+ {SENSOR_LONG, S5K4AA_GLOBAL_GAIN__, 0x0f, 0x00},
+ {SENSOR, S5K4AA_GAIN_1, 0x0b, 0x00},
+ {SENSOR, S5K4AA_GAIN_2, 0xa0, 0x00}
+};
+
+static
+ const
+ struct dmi_system_id s5k4aa_vflip_dmi_table[] = {
+ {
+ .ident = "Fujitsu-Siemens Amilo Xa 2528",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xa 2528")
+ }
+ },
+ {
+ .ident = "Fujitsu-Siemens Amilo Xi 2550",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2550")
+ }
+ },
+ {
+ .ident = "MSI GX700",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),
+ DMI_MATCH(DMI_BIOS_DATE, "07/26/2007")
+ }
+ },
+ { }
+};
+
+#endif
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k83a.c b/drivers/media/video/gspca/m5602/m5602_s5k83a.c
new file mode 100644
index 0000000..b4b33c2
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/m5602_s5k83a.c
@@ -0,0 +1,423 @@
+/*
+ * Driver for the s5k83a sensor
+ *
+ * Copyright (C) 2008 Erik Andren
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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.
+ *
+ */
+
+#include "m5602_s5k83a.h"
+
+int s5k83a_probe(struct sd *sd)
+{
+ u8 prod_id = 0, ver_id = 0;
+ int i, err = 0;
+
+ if (force_sensor) {
+ if (force_sensor == S5K83A_SENSOR) {
+ info("Forcing a %s sensor", s5k83a.name);
+ goto sensor_found;
+ }
+ /* If we want to force another sensor, don't try to probe this
+ * one */
+ return -ENODEV;
+ }
+
+ info("Probing for a s5k83a sensor");
+
+ /* Preinit the sensor */
+ for (i = 0; i < ARRAY_SIZE(preinit_s5k83a) && !err; i++) {
+ u8 data[2] = {preinit_s5k83a[i][2], preinit_s5k83a[i][3]};
+ if (preinit_s5k83a[i][0] == SENSOR)
+ err = s5k83a_write_sensor(sd, preinit_s5k83a[i][1],
+ data, 2);
+ else
+ err = m5602_write_bridge(sd, preinit_s5k83a[i][1],
+ data[0]);
+ }
+
+ /* We don't know what register (if any) that contain the product id
+ * Just pick the first addresses that seem to produce the same results
+ * on multiple machines */
+ if (s5k83a_read_sensor(sd, 0x00, &prod_id, 1))
+ return -ENODEV;
+
+ if (s5k83a_read_sensor(sd, 0x01, &ver_id, 1))
+ return -ENODEV;
+
+ if ((prod_id == 0xff) || (ver_id == 0xff))
+ return -ENODEV;
+ else
+ info("Detected a s5k83a sensor");
+
+sensor_found:
+ sd->gspca_dev.cam.cam_mode = s5k83a.modes;
+ sd->gspca_dev.cam.nmodes = s5k83a.nmodes;
+ sd->desc->ctrls = s5k83a.ctrls;
+ sd->desc->nctrls = s5k83a.nctrls;
+ return 0;
+}
+
+int s5k83a_read_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len)
+{
+ int err, i;
+
+ do {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
+ } while ((*i2c_data & I2C_BUSY) && !err);
+ if (err < 0)
+ goto out;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR,
+ sd->sensor->i2c_slave_id);
+ if (err < 0)
+ goto out;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address);
+ if (err < 0)
+ goto out;
+
+ err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x18 + len);
+ if (err < 0)
+ goto out;
+
+ do {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data);
+ } while ((*i2c_data & I2C_BUSY) && !err);
+
+ if (err < 0)
+ goto out;
+ for (i = 0; i < len && !len; i++) {
+ err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i]));
+
+ PDEBUG(DBG_TRACE, "Reading sensor register "
+ "0x%x containing 0x%x ", address, *i2c_data);
+ }
+
+out:
+ return (err < 0) ? err : 0;
+}
+
+int s5k83a_write_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len)
+{
+ int err, i;
+ u8 *p;
+ struct usb_device *udev = sd->gspca_dev.dev;
+ __u8 *buf = sd->gspca_dev.usb_buf;
+
+ /* No sensor with a data width larger than 16 bits has yet been seen */
+ if (len > 2 || !len)
+ return -EINVAL;
+
+ memcpy(buf, sensor_urb_skeleton,
+ sizeof(sensor_urb_skeleton));
+
+ buf[11] = sd->sensor->i2c_slave_id;
+ buf[15] = address;
+
+ /* Special case larger sensor writes */
+ p = buf + 16;
+
+ /* Copy a four byte write sequence for each byte to be written to */
+ for (i = 0; i < len; i++) {
+ memcpy(p, sensor_urb_skeleton + 16, 4);
+ p[3] = i2c_data[i];
+ p += 4;
+ PDEBUG(DBG_TRACE, "Writing sensor register 0x%x with 0x%x",
+ address, i2c_data[i]);
+ }
+
+ /* Copy the tailer */
+ memcpy(p, sensor_urb_skeleton + 20, 4);
+
+ /* Set the total length */
+ p[3] = 0x10 + len;
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x04, 0x40, 0x19,
+ 0x0000, buf,
+ 20 + len * 4, M5602_URB_MSG_TIMEOUT);
+
+ return (err < 0) ? err : 0;
+}
+
+int s5k83a_init(struct sd *sd)
+{
+ int i, err = 0;
+
+ for (i = 0; i < ARRAY_SIZE(init_s5k83a) && !err; i++) {
+ u8 data[2] = {0x00, 0x00};
+
+ switch (init_s5k83a[i][0]) {
+ case BRIDGE:
+ err = m5602_write_bridge(sd,
+ init_s5k83a[i][1],
+ init_s5k83a[i][2]);
+ break;
+
+ case SENSOR:
+ data[0] = init_s5k83a[i][2];
+ err = s5k83a_write_sensor(sd,
+ init_s5k83a[i][1], data, 1);
+ break;
+
+ case SENSOR_LONG:
+ data[0] = init_s5k83a[i][2];
+ data[1] = init_s5k83a[i][3];
+ err = s5k83a_write_sensor(sd,
+ init_s5k83a[i][1], data, 2);
+ break;
+ default:
+ info("Invalid stream command, exiting init");
+ return -EINVAL;
+ }
+ }
+
+ if (dump_sensor)
+ s5k83a_dump_registers(sd);
+
+ return (err < 0) ? err : 0;
+}
+
+int s5k83a_power_down(struct sd *sd)
+{
+ return 0;
+}
+
+void s5k83a_dump_registers(struct sd *sd)
+{
+ int address;
+ u8 page, old_page;
+ s5k83a_read_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
+
+ for (page = 0; page < 16; page++) {
+ s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
+ info("Dumping the s5k83a register state for page 0x%x", page);
+ for (address = 0; address <= 0xff; address++) {
+ u8 val = 0;
+ s5k83a_read_sensor(sd, address, &val, 1);
+ info("register 0x%x contains 0x%x",
+ address, val);
+ }
+ }
+ info("s5k83a register state dump complete");
+
+ for (page = 0; page < 16; page++) {
+ s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
+ info("Probing for which registers that are read/write "
+ "for page 0x%x", page);
+ for (address = 0; address <= 0xff; address++) {
+ u8 old_val, ctrl_val, test_val = 0xff;
+
+ s5k83a_read_sensor(sd, address, &old_val, 1);
+ s5k83a_write_sensor(sd, address, &test_val, 1);
+ s5k83a_read_sensor(sd, address, &ctrl_val, 1);
+
+ if (ctrl_val == test_val)
+ info("register 0x%x is writeable", address);
+ else
+ info("register 0x%x is read only", address);
+
+ /* Restore original val */
+ s5k83a_write_sensor(sd, address, &old_val, 1);
+ }
+ }
+ info("Read/write register probing complete");
+ s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
+}
+
+int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u8 data[2];
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = s5k83a_read_sensor(sd, S5K83A_BRIGHTNESS, data, 2);
+ data[1] = data[1] << 1;
+ *val = data[1];
+
+ return (err < 0) ? err : 0;
+}
+
+int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 data[2];
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ data[0] = 0x00;
+ data[1] = 0x20;
+ err = s5k83a_write_sensor(sd, 0x14, data, 2);
+ if (err < 0)
+ return err;
+
+ data[0] = 0x01;
+ data[1] = 0x00;
+ err = s5k83a_write_sensor(sd, 0x0d, data, 2);
+ if (err < 0)
+ return err;
+
+ /* FIXME: This is not sane, we need to figure out the composition
+ of these registers */
+ data[0] = val >> 3; /* brightness, high 5 bits */
+ data[1] = val >> 1; /* brightness, high 7 bits */
+ err = s5k83a_write_sensor(sd, S5K83A_BRIGHTNESS, data, 2);
+
+ return (err < 0) ? err : 0;
+}
+
+int s5k83a_get_whiteness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u8 data;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = s5k83a_read_sensor(sd, S5K83A_WHITENESS, &data, 1);
+
+ *val = data;
+ return (err < 0) ? err : 0;
+}
+
+int s5k83a_set_whiteness(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 data[1];
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ data[0] = val;
+ err = s5k83a_write_sensor(sd, S5K83A_WHITENESS, data, 1);
+
+ return (err < 0) ? err : 0;
+}
+
+int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u8 data[2];
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ err = s5k83a_read_sensor(sd, S5K83A_GAIN, data, 2);
+
+ data[1] = data[1] & 0x3f;
+ if (data[1] > S5K83A_MAXIMUM_GAIN)
+ data[1] = S5K83A_MAXIMUM_GAIN;
+
+ *val = data[1];
+
+ return (err < 0) ? err : 0;
+}
+
+int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 data[2];
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ data[0] = 0;
+ data[1] = val;
+ err = s5k83a_write_sensor(sd, S5K83A_GAIN, data, 2);
+
+ return (err < 0) ? err : 0;
+}
+
+int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u8 data[1];
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ data[0] = 0x05;
+ err = s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
+ if (err < 0)
+ return err;
+
+ err = s5k83a_read_sensor(sd, S5K83A_FLIP, data, 1);
+ *val = (data[0] | 0x40) ? 1 : 0;
+
+ return (err < 0) ? err : 0;
+}
+
+int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 data[1];
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ data[0] = 0x05;
+ err = s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
+ if (err < 0)
+ return err;
+
+ err = s5k83a_read_sensor(sd, S5K83A_FLIP, data, 1);
+ if (err < 0)
+ return err;
+
+ /* set or zero six bit, seven is hflip */
+ data[0] = (val) ? (data[0] & 0x80) | 0x40 | S5K83A_FLIP_MASK
+ : (data[0] & 0x80) | S5K83A_FLIP_MASK;
+ err = s5k83a_write_sensor(sd, S5K83A_FLIP, data, 1);
+ if (err < 0)
+ return err;
+
+ data[0] = (val) ? 0x0b : 0x0a;
+ err = s5k83a_write_sensor(sd, S5K83A_VFLIP_TUNE, data, 1);
+
+ return (err < 0) ? err : 0;
+}
+
+int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ int err;
+ u8 data[1];
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ data[0] = 0x05;
+ err = s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
+ if (err < 0)
+ return err;
+
+ err = s5k83a_read_sensor(sd, S5K83A_FLIP, data, 1);
+ *val = (data[0] | 0x80) ? 1 : 0;
+
+ return (err < 0) ? err : 0;
+}
+
+int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ int err;
+ u8 data[1];
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ data[0] = 0x05;
+ err = s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
+ if (err < 0)
+ return err;
+
+ err = s5k83a_read_sensor(sd, S5K83A_FLIP, data, 1);
+ if (err < 0)
+ return err;
+
+ /* set or zero seven bit, six is vflip */
+ data[0] = (val) ? (data[0] & 0x40) | 0x80 | S5K83A_FLIP_MASK
+ : (data[0] & 0x40) | S5K83A_FLIP_MASK;
+ err = s5k83a_write_sensor(sd, S5K83A_FLIP, data, 1);
+ if (err < 0)
+ return err;
+
+ data[0] = (val) ? 0x0a : 0x0b;
+ err = s5k83a_write_sensor(sd, S5K83A_HFLIP_TUNE, data, 1);
+
+ return (err < 0) ? err : 0;
+}
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k83a.h b/drivers/media/video/gspca/m5602/m5602_s5k83a.h
new file mode 100644
index 0000000..833708e
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/m5602_s5k83a.h
@@ -0,0 +1,484 @@
+/*
+ * Driver for the s5k83a sensor
+ *
+ * Copyright (C) 2008 Erik Andren
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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.
+ *
+ */
+
+#ifndef M5602_S5K83A_H_
+#define M5602_S5K83A_H_
+
+#include "m5602_sensor.h"
+
+#define S5K83A_FLIP 0x01
+#define S5K83A_HFLIP_TUNE 0x03
+#define S5K83A_VFLIP_TUNE 0x05
+#define S5K83A_WHITENESS 0x0a
+#define S5K83A_GAIN 0x18
+#define S5K83A_BRIGHTNESS 0x1b
+#define S5K83A_PAGE_MAP 0xec
+
+#define S5K83A_DEFAULT_BRIGHTNESS 0x71
+#define S5K83A_DEFAULT_WHITENESS 0x7e
+#define S5K83A_DEFAULT_GAIN 0x00
+#define S5K83A_MAXIMUM_GAIN 0x3c
+#define S5K83A_FLIP_MASK 0x10
+
+
+/*****************************************************************************/
+
+/* Kernel module parameters */
+extern int force_sensor;
+extern int dump_sensor;
+extern unsigned int m5602_debug;
+
+
+int s5k83a_probe(struct sd *sd);
+int s5k83a_init(struct sd *sd);
+int s5k83a_power_down(struct sd *sd);
+
+void s5k83a_dump_registers(struct sd *sd);
+
+int s5k83a_read_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+int s5k83a_write_sensor(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val);
+int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val);
+int s5k83a_set_whiteness(struct gspca_dev *gspca_dev, __s32 val);
+int s5k83a_get_whiteness(struct gspca_dev *gspca_dev, __s32 *val);
+int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val);
+int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
+int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
+int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
+int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
+int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
+
+
+static struct m5602_sensor s5k83a = {
+ .name = "S5K83A",
+ .probe = s5k83a_probe,
+ .init = s5k83a_init,
+ .power_down = s5k83a_power_down,
+ .read_sensor = s5k83a_read_sensor,
+ .write_sensor = s5k83a_write_sensor,
+ .i2c_slave_id = 0x5a,
+ .nctrls = 5,
+ .ctrls = {
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "brightness",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x01,
+ .default_value = S5K83A_DEFAULT_BRIGHTNESS,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = s5k83a_set_brightness,
+ .get = s5k83a_get_brightness
+
+ }, {
+ {
+ .id = V4L2_CID_WHITENESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "whiteness",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 0x01,
+ .default_value = S5K83A_DEFAULT_WHITENESS,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = s5k83a_set_whiteness,
+ .get = s5k83a_get_whiteness,
+ }, {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "gain",
+ .minimum = 0x00,
+ .maximum = S5K83A_MAXIMUM_GAIN,
+ .step = 0x01,
+ .default_value = S5K83A_DEFAULT_GAIN,
+ .flags = V4L2_CTRL_FLAG_SLIDER
+ },
+ .set = s5k83a_set_gain,
+ .get = s5k83a_get_gain
+ }, {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "horizontal flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = s5k83a_set_hflip,
+ .get = s5k83a_get_hflip
+ }, {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "vertical flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0
+ },
+ .set = s5k83a_set_vflip,
+ .get = s5k83a_get_vflip
+ }
+ },
+ .nmodes = 1,
+ .modes = {
+ {
+ M5602_DEFAULT_FRAME_WIDTH,
+ M5602_DEFAULT_FRAME_HEIGHT,
+ V4L2_PIX_FMT_SBGGR8,
+ V4L2_FIELD_NONE,
+ .sizeimage =
+ M5602_DEFAULT_FRAME_WIDTH * M5602_DEFAULT_FRAME_HEIGHT,
+ .bytesperline = M5602_DEFAULT_FRAME_WIDTH,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1
+ }
+ }
+};
+
+static const unsigned char preinit_s5k83a[][4] =
+{
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
+
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
+
+ {SENSOR, S5K83A_PAGE_MAP, 0x00, 0x00}
+};
+
+/* This could probably be considerably shortened.
+ I don't have the hardware to experiment with it, patches welcome
+*/
+static const unsigned char init_s5k83a[][4] =
+{
+ {SENSOR, S5K83A_PAGE_MAP, 0x04, 0x00},
+ {SENSOR, 0xaf, 0x01, 0x00},
+ {SENSOR, S5K83A_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, 0x7b, 0xff, 0x00},
+ {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+ {SENSOR, 0x01, 0x50, 0x00},
+ {SENSOR, 0x12, 0x20, 0x00},
+ {SENSOR, 0x17, 0x40, 0x00},
+ {SENSOR, S5K83A_BRIGHTNESS, 0x0f, 0x00},
+ {SENSOR, 0x1c, 0x00, 0x00},
+ {SENSOR, 0x02, 0x70, 0x00},
+ {SENSOR, 0x03, 0x0b, 0x00},
+ {SENSOR, 0x04, 0xf0, 0x00},
+ {SENSOR, 0x05, 0x0b, 0x00},
+ {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x87, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+
+ {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+ {SENSOR, 0x06, 0x71, 0x00},
+ {SENSOR, 0x07, 0xe8, 0x00},
+ {SENSOR, 0x08, 0x02, 0x00},
+ {SENSOR, 0x09, 0x88, 0x00},
+ {SENSOR, 0x14, 0x00, 0x00},
+ {SENSOR, 0x15, 0x20, 0x00},
+ {SENSOR, 0x19, 0x00, 0x00},
+ {SENSOR, 0x1a, 0x98, 0x00},
+ {SENSOR, 0x0f, 0x02, 0x00},
+ {SENSOR, 0x10, 0xe5, 0x00},
+ {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+ {SENSOR_LONG, 0x14, 0x00, 0x20},
+ {SENSOR_LONG, 0x0d, 0x00, 0x7d},
+ {SENSOR_LONG, 0x1b, 0x0d, 0x05},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x87, 0x00},
+
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
+
+ {SENSOR, S5K83A_PAGE_MAP, 0x04, 0x00},
+ {SENSOR, 0xaf, 0x01, 0x00},
+ {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+ /* ff ( init value )is very dark) || 71 and f0 better */
+ {SENSOR, 0x7b, 0xff, 0x00},
+ {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+ {SENSOR, 0x01, 0x50, 0x00},
+ {SENSOR, 0x12, 0x20, 0x00},
+ {SENSOR, 0x17, 0x40, 0x00},
+ {SENSOR, S5K83A_BRIGHTNESS, 0x0f, 0x00},
+ {SENSOR, 0x1c, 0x00, 0x00},
+ {SENSOR, 0x02, 0x70, 0x00},
+ /* some values like 0x10 give a blue-purple image */
+ {SENSOR, 0x03, 0x0b, 0x00},
+ {SENSOR, 0x04, 0xf0, 0x00},
+ {SENSOR, 0x05, 0x0b, 0x00},
+ {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+ /* under 80 don't work, highter depend on value */
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00},
+
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+
+ {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+ {SENSOR, 0x06, 0x71, 0x00},
+ {SENSOR, 0x07, 0xe8, 0x00},
+ {SENSOR, 0x08, 0x02, 0x00},
+ {SENSOR, 0x09, 0x88, 0x00},
+ {SENSOR, 0x14, 0x00, 0x00},
+ {SENSOR, 0x15, 0x20, 0x00},
+ {SENSOR, 0x19, 0x00, 0x00},
+ {SENSOR, 0x1a, 0x98, 0x00},
+ {SENSOR, 0x0f, 0x02, 0x00},
+ {SENSOR, 0x10, 0xe5, 0x00},
+ {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+ {SENSOR_LONG, 0x14, 0x00, 0x20},
+ {SENSOR_LONG, 0x0d, 0x00, 0x7d},
+ {SENSOR_LONG, 0x1b, 0x0d, 0x05},
+
+ /* The following sequence is useless after a clean boot
+ but is necessary after resume from suspend */
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
+ {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
+ {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
+ {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
+ {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
+
+ {SENSOR, S5K83A_PAGE_MAP, 0x04, 0x00},
+ {SENSOR, 0xaf, 0x01, 0x00},
+ {SENSOR, S5K83A_PAGE_MAP, 0x00, 0x00},
+ {SENSOR, 0x7b, 0xff, 0x00},
+ {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+ {SENSOR, 0x01, 0x50, 0x00},
+ {SENSOR, 0x12, 0x20, 0x00},
+ {SENSOR, 0x17, 0x40, 0x00},
+ {SENSOR, S5K83A_BRIGHTNESS, 0x0f, 0x00},
+ {SENSOR, 0x1c, 0x00, 0x00},
+ {SENSOR, 0x02, 0x70, 0x00},
+ {SENSOR, 0x03, 0x0b, 0x00},
+ {SENSOR, 0x04, 0xf0, 0x00},
+ {SENSOR, 0x05, 0x0b, 0x00},
+ {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+ {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
+ {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
+ {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
+ {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
+ {BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00},
+ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
+ {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
+
+ {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+ {SENSOR, 0x06, 0x71, 0x00},
+ {SENSOR, 0x07, 0xe8, 0x00},
+ {SENSOR, 0x08, 0x02, 0x00},
+ {SENSOR, 0x09, 0x88, 0x00},
+ {SENSOR, 0x14, 0x00, 0x00},
+ {SENSOR, 0x15, 0x20, 0x00},
+ {SENSOR, 0x19, 0x00, 0x00},
+ {SENSOR, 0x1a, 0x98, 0x00},
+ {SENSOR, 0x0f, 0x02, 0x00},
+
+ {SENSOR, 0x10, 0xe5, 0x00},
+ {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+ {SENSOR_LONG, 0x14, 0x00, 0x20},
+ {SENSOR_LONG, 0x0d, 0x00, 0x7d},
+ {SENSOR_LONG, 0x1b, 0x0d, 0x05},
+
+ /* normal colors
+ (this is value after boot, but after tries can be different) */
+ {SENSOR, 0x00, 0x06, 0x00},
+
+ /* set default brightness */
+ {SENSOR_LONG, 0x14, 0x00, 0x20},
+ {SENSOR_LONG, 0x0d, 0x01, 0x00},
+ {SENSOR_LONG, 0x1b, S5K83A_DEFAULT_BRIGHTNESS >> 3,
+ S5K83A_DEFAULT_BRIGHTNESS >> 1},
+
+ /* set default whiteness */
+ {SENSOR, S5K83A_WHITENESS, S5K83A_DEFAULT_WHITENESS, 0x00},
+
+ /* set default gain */
+ {SENSOR_LONG, 0x18, 0x00, S5K83A_DEFAULT_GAIN},
+
+ /* set default flip */
+ {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
+ {SENSOR, S5K83A_FLIP, 0x00 | S5K83A_FLIP_MASK, 0x00},
+ {SENSOR, S5K83A_HFLIP_TUNE, 0x0b, 0x00},
+ {SENSOR, S5K83A_VFLIP_TUNE, 0x0a, 0x00}
+
+};
+
+#endif
diff --git a/drivers/media/video/gspca/m5602/m5602_sensor.h b/drivers/media/video/gspca/m5602/m5602_sensor.h
new file mode 100644
index 0000000..930fcaa
--- /dev/null
+++ b/drivers/media/video/gspca/m5602/m5602_sensor.h
@@ -0,0 +1,76 @@
+/*
+ * USB Driver for ALi m5602 based webcams
+ *
+ * Copyright (C) 2008 Erik Andren
+ * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
+ * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
+ *
+ * Portions of code to USB interface and ALi driver software,
+ * Copyright (c) 2006 Willem Duinker
+ * v4l2 interface modeled after the V4L2 driver
+ * for SN9C10x PC Camera Controllers
+ *
+ * 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.
+ *
+ */
+
+#ifndef M5602_SENSOR_H_
+#define M5602_SENSOR_H_
+
+#include "m5602_bridge.h"
+
+#define M5602_DEFAULT_FRAME_WIDTH 640
+#define M5602_DEFAULT_FRAME_HEIGHT 480
+
+#define M5602_MAX_CTRLS (V4L2_CID_LASTP1 - V4L2_CID_BASE + 10)
+
+/* Enumerates all supported sensors */
+enum sensors {
+ OV9650_SENSOR = 1,
+ S5K83A_SENSOR = 2,
+ S5K4AA_SENSOR = 3,
+ MT9M111_SENSOR = 4,
+ PO1030_SENSOR = 5
+};
+
+/* Enumerates all possible instruction types */
+enum instruction {
+ BRIDGE,
+ SENSOR,
+ SENSOR_LONG
+};
+
+struct m5602_sensor {
+ /* Defines the name of a sensor */
+ char name[32];
+
+ /* What i2c address the sensor is connected to */
+ u8 i2c_slave_id;
+
+ /* Probes if the sensor is connected */
+ int (*probe)(struct sd *sd);
+
+ /* Performs a initialization sequence */
+ int (*init)(struct sd *sd);
+
+ /* Performs a power down sequence */
+ int (*power_down)(struct sd *sd);
+
+ /* Reads a sensor register */
+ int (*read_sensor)(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+ /* Writes to a sensor register */
+ int (*write_sensor)(struct sd *sd, const u8 address,
+ u8 *i2c_data, const u8 len);
+
+ int nctrls;
+ struct ctrl ctrls[M5602_MAX_CTRLS];
+
+ char nmodes;
+ struct v4l2_pix_format modes[];
+};
+
+#endif
diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c
index 4d5db47..277ca34 100644
--- a/drivers/media/video/gspca/mars.c
+++ b/drivers/media/video/gspca/mars.c
@@ -134,7 +134,7 @@
return 0;
}
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
int err_code;
__u8 *data;
@@ -143,9 +143,10 @@
int intpipe;
PDEBUG(D_STREAM, "camera start, iface %d, alt 8", gspca_dev->iface);
- if (usb_set_interface(gspca_dev->dev, gspca_dev->iface, 8) < 0) {
+ err_code = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 8);
+ if (err_code < 0) {
PDEBUG(D_ERR|D_STREAM, "Set packet size: set interface error");
- return;
+ return err_code;
}
data = gspca_dev->usb_buf;
@@ -154,7 +155,7 @@
err_code = reg_w(gspca_dev, data[0], 2);
if (err_code < 0)
- return;
+ return err_code;
/*
Initialize the MR97113 chip register
@@ -180,14 +181,14 @@
err_code = reg_w(gspca_dev, data[0], 11);
if (err_code < 0)
- return;
+ return err_code;
data[0] = 0x23; /* address */
data[1] = 0x09; /* reg 35, append frame header */
err_code = reg_w(gspca_dev, data[0], 2);
if (err_code < 0)
- return;
+ return err_code;
data[0] = 0x3c; /* address */
/* if (gspca_dev->width == 1280) */
@@ -198,7 +199,7 @@
* (unit: 4KB) 200KB */
err_code = reg_w(gspca_dev, data[0], 2);
if (err_code < 0)
- return;
+ return err_code;
if (0) { /* fixed dark-gain */
data[1] = 0; /* reg 94, Y Gain (1.75) */
@@ -240,13 +241,13 @@
err_code = reg_w(gspca_dev, data[0], 6);
if (err_code < 0)
- return;
+ return err_code;
data[0] = 0x67;
data[1] = 0x13; /* reg 103, first pixel B, disable sharpness */
err_code = reg_w(gspca_dev, data[0], 2);
if (err_code < 0)
- return;
+ return err_code;
/*
* initialize the value of MI sensor...
@@ -326,6 +327,7 @@
data[0] = 0x00;
data[1] = 0x4d; /* ISOC transfering enable... */
reg_w(gspca_dev, data[0], 2);
+ return err_code;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c
index 4df4eec..ca67119 100644
--- a/drivers/media/video/gspca/ov519.c
+++ b/drivers/media/video/gspca/ov519.c
@@ -1854,7 +1854,7 @@
}
/* -- start the camera -- */
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int ret;
@@ -1871,9 +1871,10 @@
goto out;
PDEBUG(D_STREAM, "camera started alt: 0x%02x", gspca_dev->alt);
ov51x_led_control(sd, 1);
- return;
+ return 0;
out:
PDEBUG(D_ERR, "camera start error:%d", ret);
+ return ret;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c
index 83b5f74..0b0c573 100644
--- a/drivers/media/video/gspca/pac207.c
+++ b/drivers/media/video/gspca/pac207.c
@@ -281,7 +281,7 @@
}
/* -- start the camera -- */
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
__u8 mode;
@@ -323,6 +323,7 @@
sd->sof_read = 0;
sd->autogain_ignore_frames = 0;
atomic_set(&sd->avg_lum, -1);
+ return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
@@ -534,6 +535,7 @@
{USB_DEVICE(0x093a, 0x2470)},
{USB_DEVICE(0x093a, 0x2471)},
{USB_DEVICE(0x093a, 0x2472)},
+ {USB_DEVICE(0x093a, 0x2476)},
{USB_DEVICE(0x2001, 0xf115)},
{}
};
diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c
index ba865b7..e5ff9a6 100644
--- a/drivers/media/video/gspca/pac7311.c
+++ b/drivers/media/video/gspca/pac7311.c
@@ -675,7 +675,7 @@
return 0;
}
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -724,6 +724,7 @@
reg_w(gspca_dev, 0x78, 0x01);
else
reg_w(gspca_dev, 0x78, 0x05);
+ return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c
index 12b81ae..6c69bc7 100644
--- a/drivers/media/video/gspca/sonixb.c
+++ b/drivers/media/video/gspca/sonixb.c
@@ -490,7 +490,7 @@
{0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10},
};
-struct sensor_data sensor_data[] = {
+static struct sensor_data sensor_data[] = {
SENS(initHv7131, NULL, hv7131_sensor_init, NULL, NULL, 0, NO_EXPO|NO_FREQ, 0),
SENS(initOv6650, NULL, ov6650_sensor_init, NULL, NULL, F_GAIN|F_SIF, 0, 0x60),
SENS(initOv7630, initOv7630_3, ov7630_sensor_init, NULL, ov7630_sensor_init_3,
@@ -892,7 +892,7 @@
}
/* -- start the camera -- */
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
struct cam *cam = &gspca_dev->cam;
@@ -976,6 +976,7 @@
sd->frames_to_drop = 0;
sd->autogain_ignore_frames = 0;
atomic_set(&sd->avg_lum, -1);
+ return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c
index 572b0f3..53cb82d 100644
--- a/drivers/media/video/gspca/sonixj.c
+++ b/drivers/media/video/gspca/sonixj.c
@@ -39,6 +39,7 @@
unsigned char contrast;
unsigned char colors;
unsigned char autogain;
+ __u8 vflip; /* ov7630 only */
signed char ag_cnt;
#define AG_CNT_START 13
@@ -70,6 +71,8 @@
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val);
static struct ctrl sd_ctrls[] = {
{
@@ -131,6 +134,22 @@
.set = sd_setautogain,
.get = sd_getautogain,
},
+/* ov7630 only */
+#define VFLIP_IDX 4
+ {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Vflip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define VFLIP_DEF 1
+ .default_value = VFLIP_DEF,
+ },
+ .set = sd_setvflip,
+ .get = sd_getvflip,
+ },
};
static struct v4l2_pix_format vga_mode[] = {
@@ -248,10 +267,12 @@
0xa6, 0xb2, 0xbf, 0xca, 0xd5, 0xe0, 0xeb, 0xf5, 0xff
};
+/* color matrix and offsets */
static const __u8 reg84[] = {
- 0x14, 0x00, 0x27, 0x00, 0x07, 0x00, 0xe5, 0x0f,
- 0xe4, 0x0f, 0x38, 0x00, 0x3e, 0x00, 0xc3, 0x0f,
- 0xf7, 0x0f, 0x00, 0x00, 0x00
+ 0x14, 0x00, 0x27, 0x00, 0x07, 0x00, /* YR YG YB gains */
+ 0xe8, 0x0f, 0xda, 0x0f, 0x40, 0x00, /* UR UG UB */
+ 0x3e, 0x00, 0xcd, 0x0f, 0xf7, 0x0f, /* VR VG VB */
+ 0x00, 0x00, 0x00 /* YUV offsets */
};
static const __u8 hv7131r_sensor_init[][8] = {
{0xC1, 0x11, 0x01, 0x08, 0x01, 0x00, 0x00, 0x10},
@@ -434,7 +455,8 @@
{0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10},
{0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10},
/*fixme: + 0x12, 0x04*/
- {0xa1, 0x21, 0x75, 0x82, 0x00, 0x00, 0x00, 0x10},
+/* {0xa1, 0x21, 0x75, 0x82, 0x00, 0x00, 0x00, 0x10}, * COMN
+ * set by setvflip */
{0xa1, 0x21, 0x10, 0x32, 0x00, 0x00, 0x00, 0x10},
{0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
{0xb1, 0x21, 0x01, 0x80, 0x80, 0x00, 0x00, 0x10},
@@ -949,6 +971,8 @@
gspca_dev->ctrl_dis = (1 << AUTOGAIN_IDX);
break;
}
+ if (sd->sensor != SENSOR_OV7630)
+ gspca_dev->ctrl_dis |= (1 << VFLIP_IDX);
return 0;
}
@@ -1080,20 +1104,17 @@
static void setbrightcont(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- unsigned val;
+ int val;
__u8 reg84_full[0x15];
- memset(reg84_full, 0, sizeof reg84_full);
- val = sd->contrast * 0x20 / CONTRAST_MAX + 0x10; /* 10..30 */
- reg84_full[2] = val;
- reg84_full[0] = (val + 1) / 2;
- reg84_full[4] = (val + 1) / 5;
- if (val > BRIGHTNESS_DEF)
- val = (sd->brightness - BRIGHTNESS_DEF) * 0x20
+ memcpy(reg84_full, reg84, sizeof reg84_full);
+ val = sd->contrast * 0x30 / CONTRAST_MAX + 0x10; /* 10..40 */
+ reg84_full[0] = (val + 1) / 2; /* red */
+ reg84_full[2] = val; /* green */
+ reg84_full[4] = (val + 1) / 5; /* blue */
+ val = (sd->brightness - BRIGHTNESS_DEF) * 0x10
/ BRIGHTNESS_MAX;
- else
- val = 0;
- reg84_full[0x12] = val; /* 00..1f */
+ reg84_full[0x12] = val & 0x1f; /* 5:0 signed value */
reg_w(gspca_dev, 0x84, reg84_full, sizeof reg84_full);
}
@@ -1172,8 +1193,16 @@
sd->ag_cnt = -1;
}
+static void setvflip(struct sd *sd)
+{
+ if (sd->sensor != SENSOR_OV7630)
+ return;
+ i2c_w1(&sd->gspca_dev, 0x75, /* COMN */
+ sd->vflip ? 0x82 : 0x02);
+}
+
/* -- start the camera -- */
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int i;
@@ -1263,6 +1292,7 @@
break;
case SENSOR_OV7630:
ov7630_InitSensor(gspca_dev);
+ setvflip(sd);
reg17 = 0xe2;
reg1 = 0x44;
break;
@@ -1320,12 +1350,16 @@
setbrightness(gspca_dev);
setcontrast(gspca_dev);
break;
+ case SENSOR_OV7630:
+ setvflip(sd);
+ /* fall thru */
default: /* OV76xx */
setbrightcont(gspca_dev);
break;
}
setautogain(gspca_dev);
reg_w1(gspca_dev, 0x01, reg1);
+ return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
@@ -1546,6 +1580,24 @@
return 0;
}
+static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->vflip = val;
+ if (gspca_dev->streaming)
+ setvflip(sd);
+ return 0;
+}
+
+static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->vflip;
+ return 0;
+}
+
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
@@ -1567,6 +1619,7 @@
static const __devinitdata struct usb_device_id device_table[] = {
#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
{USB_DEVICE(0x0458, 0x7025), BSI(SN9C120, MI0360, 0x5d)},
+ {USB_DEVICE(0x0458, 0x702e), BSI(SN9C120, OV7660, 0x21)},
{USB_DEVICE(0x045e, 0x00f5), BSI(SN9C105, OV7660, 0x21)},
{USB_DEVICE(0x045e, 0x00f7), BSI(SN9C105, OV7660, 0x21)},
{USB_DEVICE(0x0471, 0x0327), BSI(SN9C105, MI0360, 0x5d)},
@@ -1588,7 +1641,9 @@
/* {USB_DEVICE(0x0c45, 0x60fa), BSI(SN9C105, OV7648, 0x??)}, */
{USB_DEVICE(0x0c45, 0x60fb), BSI(SN9C105, OV7660, 0x21)},
{USB_DEVICE(0x0c45, 0x60fc), BSI(SN9C105, HV7131R, 0x11)},
-/* {USB_DEVICE(0x0c45, 0x60fe), BSI(SN9C105, OV7630, 0x??)}, */
+#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
+ {USB_DEVICE(0x0c45, 0x60fe), BSI(SN9C105, OV7630, 0x21)},
+#endif
/* {USB_DEVICE(0x0c45, 0x6108), BSI(SN9C120, OM6801, 0x??)}, */
/* {USB_DEVICE(0x0c45, 0x6122), BSI(SN9C110, ICM105C, 0x??)}, */
/* {USB_DEVICE(0x0c45, 0x6123), BSI(SN9C110, SanyoCCD, 0x??)}, */
diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c
index 6e73390..bca106c 100644
--- a/drivers/media/video/gspca/spca500.c
+++ b/drivers/media/video/gspca/spca500.c
@@ -660,7 +660,7 @@
return 0;
}
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int err;
@@ -867,6 +867,7 @@
write_vector(gspca_dev, Clicksmart510_defaults);
break;
}
+ return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c
index e9eb59b..b742f26 100644
--- a/drivers/media/video/gspca/spca501.c
+++ b/drivers/media/video/gspca/spca501.c
@@ -1980,7 +1980,7 @@
return 0;
}
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
struct usb_device *dev = gspca_dev->dev;
int mode;
@@ -2012,6 +2012,7 @@
setbrightness(gspca_dev);
setcontrast(gspca_dev);
setcolors(gspca_dev);
+ return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c
index f601daf..b345749 100644
--- a/drivers/media/video/gspca/spca505.c
+++ b/drivers/media/video/gspca/spca505.c
@@ -688,7 +688,7 @@
return 0;
}
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
struct usb_device *dev = gspca_dev->dev;
int ret;
@@ -733,6 +733,7 @@
/* reg_write(dev, 0x5, 0x0, 0x0); */
/* reg_write(dev, 0x5, 0x0, 0x1); */
/* reg_write(dev, 0x5, 0x11, 0x2); */
+ return ret;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/drivers/media/video/gspca/spca506.c b/drivers/media/video/gspca/spca506.c
index 195dce9..645ee9d 100644
--- a/drivers/media/video/gspca/spca506.c
+++ b/drivers/media/video/gspca/spca506.c
@@ -422,7 +422,7 @@
return 0;
}
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
struct usb_device *dev = gspca_dev->dev;
__u16 norme;
@@ -549,6 +549,7 @@
PDEBUG(D_STREAM, "webcam started");
spca506_GetNormeInput(gspca_dev, &norme, &channel);
spca506_SetNormeInput(gspca_dev, norme, channel);
+ return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c
index 281ce02..63ec902 100644
--- a/drivers/media/video/gspca/spca508.c
+++ b/drivers/media/video/gspca/spca508.c
@@ -1528,7 +1528,7 @@
return 0;
}
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
int mode;
@@ -1546,6 +1546,7 @@
break;
}
reg_write(gspca_dev->dev, 0x8112, 0x10 | 0x20);
+ return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c
index 95fcfcb..020a03c 100644
--- a/drivers/media/video/gspca/spca561.c
+++ b/drivers/media/video/gspca/spca561.c
@@ -152,7 +152,7 @@
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
0, /* request */
- USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
value, index, NULL, 0, 500);
PDEBUG(D_USBO, "reg write: 0x%02x:0x%02x", index, value);
if (ret < 0)
@@ -699,7 +699,7 @@
sd->ag_cnt = -1;
}
-static void sd_start_12a(struct gspca_dev *gspca_dev)
+static int sd_start_12a(struct gspca_dev *gspca_dev)
{
struct usb_device *dev = gspca_dev->dev;
int Clck = 0x8a; /* lower 0x8X values lead to fps > 30 */
@@ -725,8 +725,9 @@
setwhite(gspca_dev);
setautogain(gspca_dev);
setexposure(gspca_dev);
+ return 0;
}
-static void sd_start_72a(struct gspca_dev *gspca_dev)
+static int sd_start_72a(struct gspca_dev *gspca_dev)
{
struct usb_device *dev = gspca_dev->dev;
int Clck;
@@ -750,6 +751,7 @@
reg_w_val(dev, 0x8700, Clck); /* 0x27 clock */
reg_w_val(dev, 0x8112, 0x10 | 0x20);
setautogain(gspca_dev);
+ return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
@@ -1064,7 +1066,7 @@
{
.id = V4L2_CID_DO_WHITE_BALANCE,
.type = V4L2_CTRL_TYPE_INTEGER,
- .name = "While Balance",
+ .name = "White Balance",
.minimum = WHITE_MIN,
.maximum = WHITE_MAX,
.step = 1,
diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c
index 2f2de42..d9d6491 100644
--- a/drivers/media/video/gspca/stk014.c
+++ b/drivers/media/video/gspca/stk014.c
@@ -324,7 +324,7 @@
}
/* -- start the camera -- */
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
int ret, value;
@@ -374,9 +374,10 @@
set_par(gspca_dev, 0x01000000);
set_par(gspca_dev, 0x01000000);
PDEBUG(D_STREAM, "camera started alt: 0x%02x", gspca_dev->alt);
- return;
+ return 0;
out:
PDEBUG(D_ERR|D_STREAM, "camera start err %d", ret);
+ return ret;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c
index 1cfcc6c..bd92886 100644
--- a/drivers/media/video/gspca/sunplus.c
+++ b/drivers/media/video/gspca/sunplus.c
@@ -961,7 +961,7 @@
return 0;
}
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
struct usb_device *dev = gspca_dev->dev;
@@ -1042,6 +1042,7 @@
break;
}
sp5xx_initContBrigHueRegisters(gspca_dev);
+ return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c
index f034c74..b561f7c 100644
--- a/drivers/media/video/gspca/t613.c
+++ b/drivers/media/video/gspca/t613.c
@@ -28,8 +28,6 @@
#include "gspca.h"
-#define MAX_GAMMA 0x10 /* 0 to 15 */
-
#define V4L2_CID_EFFECTS (V4L2_CID_PRIVATE_BASE + 0)
MODULE_AUTHOR("Leandro Costantino <le_costantino@pixartargentina.com.ar>");
@@ -49,6 +47,10 @@
unsigned char whitebalance;
unsigned char mirror;
unsigned char effect;
+
+ __u8 sensor;
+#define SENSOR_TAS5130A 0
+#define SENSOR_OTHER 1
};
/* V4L2 controls supported by the driver */
@@ -83,9 +85,9 @@
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Brightness",
.minimum = 0,
- .maximum = 0x0f,
+ .maximum = 14,
.step = 1,
- .default_value = 0x09,
+ .default_value = 8,
},
.set = sd_setbrightness,
.get = sd_getbrightness,
@@ -118,16 +120,17 @@
.set = sd_setcolors,
.get = sd_getcolors,
},
-#define SD_GAMMA 3
+#define GAMMA_MAX 16
+#define GAMMA_DEF 10
{
{
.id = V4L2_CID_GAMMA, /* (gamma on win) */
.type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gamma (Untested)",
+ .name = "Gamma",
.minimum = 0,
- .maximum = MAX_GAMMA,
+ .maximum = GAMMA_MAX - 1,
.step = 1,
- .default_value = 0x09,
+ .default_value = GAMMA_DEF,
},
.set = sd_setgamma,
.get = sd_getgamma,
@@ -197,7 +200,7 @@
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Sharpness",
.minimum = 0,
- .maximum = MAX_GAMMA, /* 0 to 16 */
+ .maximum = 15,
.step = 1,
.default_value = 0x06,
},
@@ -258,7 +261,6 @@
.priv = 0},
};
-#define T16_OFFSET_DATA 631
#define MAX_EFFECTS 7
/* easily done by soft, this table could be removed,
* i keep it here just in case */
@@ -272,87 +274,87 @@
{0xa8, 0xc8, 0xc6, 0xd2, 0xc0, 0x40}, /* Negative */
};
-static const __u8 gamma_table[MAX_GAMMA][34] = {
- {0x90, 0x00, 0x91, 0x3e, 0x92, 0x69, 0x93, 0x85,
+static const __u8 gamma_table[GAMMA_MAX][34] = {
+ {0x90, 0x00, 0x91, 0x3e, 0x92, 0x69, 0x93, 0x85, /* 0 */
0x94, 0x95, 0x95, 0xa1, 0x96, 0xae, 0x97, 0xb9,
0x98, 0xc2, 0x99, 0xcb, 0x9a, 0xd4, 0x9b, 0xdb,
0x9c, 0xe3, 0x9d, 0xea, 0x9e, 0xf1, 0x9f, 0xf8,
0xa0, 0xff},
- {0x90, 0x00, 0x91, 0x33, 0x92, 0x5A, 0x93, 0x75,
- 0x94, 0x85, 0x95, 0x93, 0x96, 0xA1, 0x97, 0xAD,
- 0x98, 0xB7, 0x99, 0xC2, 0x9A, 0xCB, 0x9B, 0xD4,
- 0x9C, 0xDE, 0x9D, 0xE7, 0x9E, 0xF0, 0x9F, 0xF7,
+ {0x90, 0x00, 0x91, 0x33, 0x92, 0x5a, 0x93, 0x75, /* 1 */
+ 0x94, 0x85, 0x95, 0x93, 0x96, 0xa1, 0x97, 0xad,
+ 0x98, 0xb7, 0x99, 0xc2, 0x9a, 0xcb, 0x9b, 0xd4,
+ 0x9c, 0xde, 0x9D, 0xe7, 0x9e, 0xf0, 0x9f, 0xf7,
0xa0, 0xff},
- {0x90, 0x00, 0x91, 0x2F, 0x92, 0x51, 0x93, 0x6B,
- 0x94, 0x7C, 0x95, 0x8A, 0x96, 0x99, 0x97, 0xA6,
- 0x98, 0xB1, 0x99, 0xBC, 0x9A, 0xC6, 0x9B, 0xD0,
- 0x9C, 0xDB, 0x9D, 0xE4, 0x9E, 0xED, 0x9F, 0xF6,
+ {0x90, 0x00, 0x91, 0x2f, 0x92, 0x51, 0x93, 0x6b, /* 2 */
+ 0x94, 0x7c, 0x95, 0x8a, 0x96, 0x99, 0x97, 0xa6,
+ 0x98, 0xb1, 0x99, 0xbc, 0x9a, 0xc6, 0x9b, 0xd0,
+ 0x9c, 0xdb, 0x9d, 0xe4, 0x9e, 0xed, 0x9f, 0xf6,
0xa0, 0xff},
- {0x90, 0x00, 0x91, 0x29, 0x92, 0x48, 0x93, 0x60,
- 0x94, 0x72, 0x95, 0x81, 0x96, 0x90, 0x97, 0x9E,
- 0x98, 0xAA, 0x99, 0xB5, 0x9A, 0xBF, 0x9B, 0xCB,
- 0x9C, 0xD6, 0x9D, 0xE1, 0x9E, 0xEB, 0x9F, 0xF5,
+ {0x90, 0x00, 0x91, 0x29, 0x92, 0x48, 0x93, 0x60, /* 3 */
+ 0x94, 0x72, 0x95, 0x81, 0x96, 0x90, 0x97, 0x9e,
+ 0x98, 0xaa, 0x99, 0xb5, 0x9a, 0xbf, 0x9b, 0xcb,
+ 0x9c, 0xd6, 0x9d, 0xe1, 0x9e, 0xeb, 0x9f, 0xf5,
0xa0, 0xff},
- {0x90, 0x00, 0x91, 0x23, 0x92, 0x3F, 0x93, 0x55,
+ {0x90, 0x00, 0x91, 0x23, 0x92, 0x3f, 0x93, 0x55, /* 4 */
0x94, 0x68, 0x95, 0x77, 0x96, 0x86, 0x97, 0x95,
- 0x98, 0xA2, 0x99, 0xAD, 0x9A, 0xB9, 0x9B, 0xC6,
- 0x9C, 0xD2, 0x9D, 0xDE, 0x9E, 0xE9, 0x9F, 0xF4,
+ 0x98, 0xa2, 0x99, 0xad, 0x9a, 0xb9, 0x9b, 0xc6,
+ 0x9c, 0xd2, 0x9d, 0xde, 0x9e, 0xe9, 0x9f, 0xf4,
0xa0, 0xff},
- {0x90, 0x00, 0x91, 0x1B, 0x92, 0x33, 0x93, 0x48,
+ {0x90, 0x00, 0x91, 0x1b, 0x92, 0x33, 0x93, 0x48, /* 5 */
0x94, 0x59, 0x95, 0x69, 0x96, 0x79, 0x97, 0x87,
- 0x98, 0x96, 0x99, 0xA3, 0x9A, 0xB1, 0x9B, 0xBE,
- 0x9C, 0xCC, 0x9D, 0xDA, 0x9E, 0xE7, 0x9F, 0xF3,
+ 0x98, 0x96, 0x99, 0xa3, 0x9a, 0xb1, 0x9b, 0xbe,
+ 0x9c, 0xcc, 0x9d, 0xda, 0x9e, 0xe7, 0x9f, 0xf3,
0xa0, 0xff},
- {0x90, 0x00, 0x91, 0x02, 0x92, 0x10, 0x93, 0x20,
+ {0x90, 0x00, 0x91, 0x02, 0x92, 0x10, 0x93, 0x20, /* 6 */
0x94, 0x32, 0x95, 0x40, 0x96, 0x57, 0x97, 0x67,
0x98, 0x77, 0x99, 0x88, 0x9a, 0x99, 0x9b, 0xaa,
0x9c, 0xbb, 0x9d, 0xcc, 0x9e, 0xdd, 0x9f, 0xee,
0xa0, 0xff},
- {0x90, 0x00, 0x91, 0x02, 0x92, 0x14, 0x93, 0x26,
- 0x94, 0x38, 0x95, 0x4A, 0x96, 0x60, 0x97, 0x70,
- 0x98, 0x80, 0x99, 0x90, 0x9A, 0xA0, 0x9B, 0xB0,
- 0x9C, 0xC0, 0x9D, 0xD0, 0x9E, 0xE0, 0x9F, 0xF0,
+ {0x90, 0x00, 0x91, 0x02, 0x92, 0x14, 0x93, 0x26, /* 7 */
+ 0x94, 0x38, 0x95, 0x4a, 0x96, 0x60, 0x97, 0x70,
+ 0x98, 0x80, 0x99, 0x90, 0x9a, 0xa0, 0x9b, 0xb0,
+ 0x9c, 0xc0, 0x9D, 0xd0, 0x9e, 0xe0, 0x9f, 0xf0,
0xa0, 0xff},
- {0x90, 0x00, 0x91, 0x10, 0x92, 0x22, 0x93, 0x35,
- 0x94, 0x47, 0x95, 0x5A, 0x96, 0x69, 0x97, 0x79,
- 0x98, 0x88, 0x99, 0x97, 0x9A, 0xA7, 0x9B, 0xB6,
- 0x9C, 0xC4, 0x9D, 0xD3, 0x9E, 0xE0, 0x9F, 0xF0,
+ {0x90, 0x00, 0x91, 0x10, 0x92, 0x22, 0x93, 0x35, /* 8 */
+ 0x94, 0x47, 0x95, 0x5a, 0x96, 0x69, 0x97, 0x79,
+ 0x98, 0x88, 0x99, 0x97, 0x9a, 0xa7, 0x9b, 0xb6,
+ 0x9c, 0xc4, 0x9d, 0xd3, 0x9e, 0xe0, 0x9f, 0xf0,
0xa0, 0xff},
- {0x90, 0x00, 0x91, 0x10, 0x92, 0x26, 0x93, 0x40,
+ {0x90, 0x00, 0x91, 0x10, 0x92, 0x26, 0x93, 0x40, /* 9 */
0x94, 0x54, 0x95, 0x65, 0x96, 0x75, 0x97, 0x84,
0x98, 0x93, 0x99, 0xa1, 0x9a, 0xb0, 0x9b, 0xbd,
0x9c, 0xca, 0x9d, 0xd6, 0x9e, 0xe0, 0x9f, 0xf0,
0xa0, 0xff},
- {0x90, 0x00, 0x91, 0x18, 0x92, 0x2B, 0x93, 0x44,
- 0x94, 0x60, 0x95, 0x70, 0x96, 0x80, 0x97, 0x8E,
- 0x98, 0x9C, 0x99, 0xAA, 0x9A, 0xB7, 0x9B, 0xC4,
- 0x9C, 0xD0, 0x9D, 0xD8, 0x9E, 0xE2, 0x9F, 0xF0,
+ {0x90, 0x00, 0x91, 0x18, 0x92, 0x2b, 0x93, 0x44, /* 10 */
+ 0x94, 0x60, 0x95, 0x70, 0x96, 0x80, 0x97, 0x8e,
+ 0x98, 0x9c, 0x99, 0xaa, 0x9a, 0xb7, 0x9b, 0xc4,
+ 0x9c, 0xd0, 0x9d, 0xd8, 0x9e, 0xe2, 0x9f, 0xf0,
0xa0, 0xff},
- {0x90, 0x00, 0x91, 0x1A, 0x92, 0x34, 0x93, 0x52,
- 0x94, 0x66, 0x95, 0x7E, 0x96, 0x8D, 0x97, 0x9B,
- 0x98, 0xA8, 0x99, 0xB4, 0x9A, 0xC0, 0x9B, 0xCB,
- 0x9C, 0xD6, 0x9D, 0xE1, 0x9E, 0xEB, 0x9F, 0xF5,
+ {0x90, 0x00, 0x91, 0x1a, 0x92, 0x34, 0x93, 0x52, /* 11 */
+ 0x94, 0x66, 0x95, 0x7e, 0x96, 0x8D, 0x97, 0x9B,
+ 0x98, 0xa8, 0x99, 0xb4, 0x9a, 0xc0, 0x9b, 0xcb,
+ 0x9c, 0xd6, 0x9d, 0xe1, 0x9e, 0xeb, 0x9f, 0xf5,
0xa0, 0xff},
- {0x90, 0x00, 0x91, 0x3F, 0x92, 0x5A, 0x93, 0x6E,
- 0x94, 0x7F, 0x95, 0x8E, 0x96, 0x9C, 0x97, 0xA8,
- 0x98, 0xB4, 0x99, 0xBF, 0x9A, 0xC9, 0x9B, 0xD3,
- 0x9C, 0xDC, 0x9D, 0xE5, 0x9E, 0xEE, 0x9F, 0xF6,
- 0xA0, 0xFF},
- {0x90, 0x00, 0x91, 0x54, 0x92, 0x6F, 0x93, 0x83,
- 0x94, 0x93, 0x95, 0xA0, 0x96, 0xAD, 0x97, 0xB7,
- 0x98, 0xC2, 0x99, 0xCB, 0x9A, 0xD4, 0x9B, 0xDC,
- 0x9C, 0xE4, 0x9D, 0xEB, 0x9E, 0xF2, 0x9F, 0xF9,
+ {0x90, 0x00, 0x91, 0x3f, 0x92, 0x5a, 0x93, 0x6e, /* 12 */
+ 0x94, 0x7f, 0x95, 0x8e, 0x96, 0x9c, 0x97, 0xa8,
+ 0x98, 0xb4, 0x99, 0xbf, 0x9a, 0xc9, 0x9b, 0xd3,
+ 0x9c, 0xdc, 0x9d, 0xe5, 0x9e, 0xee, 0x9f, 0xf6,
0xa0, 0xff},
- {0x90, 0x00, 0x91, 0x6E, 0x92, 0x88, 0x93, 0x9A,
- 0x94, 0xA8, 0x95, 0xB3, 0x96, 0xBD, 0x97, 0xC6,
- 0x98, 0xCF, 0x99, 0xD6, 0x9A, 0xDD, 0x9B, 0xE3,
- 0x9C, 0xE9, 0x9D, 0xEF, 0x9E, 0xF4, 0x9F, 0xFA,
+ {0x90, 0x00, 0x91, 0x54, 0x92, 0x6f, 0x93, 0x83, /* 13 */
+ 0x94, 0x93, 0x95, 0xa0, 0x96, 0xad, 0x97, 0xb7,
+ 0x98, 0xc2, 0x99, 0xcb, 0x9a, 0xd4, 0x9b, 0xdc,
+ 0x9c, 0xe4, 0x9d, 0xeb, 0x9e, 0xf2, 0x9f, 0xf9,
0xa0, 0xff},
- {0x90, 0x00, 0x91, 0x93, 0x92, 0xA8, 0x93, 0xB7,
- 0x94, 0xC1, 0x95, 0xCA, 0x96, 0xD2, 0x97, 0xD8,
- 0x98, 0xDE, 0x99, 0xE3, 0x9A, 0xE8, 0x9B, 0xED,
- 0x9C, 0xF1, 0x9D, 0xF5, 0x9E, 0xF8, 0x9F, 0xFC,
- 0xA0, 0xFF}
+ {0x90, 0x00, 0x91, 0x6e, 0x92, 0x88, 0x93, 0x9a, /* 14 */
+ 0x94, 0xa8, 0x95, 0xb3, 0x96, 0xbd, 0x97, 0xc6,
+ 0x98, 0xcf, 0x99, 0xd6, 0x9a, 0xdd, 0x9b, 0xe3,
+ 0x9c, 0xe9, 0x9d, 0xef, 0x9e, 0xf4, 0x9f, 0xfa,
+ 0xa0, 0xff},
+ {0x90, 0x00, 0x91, 0x93, 0x92, 0xa8, 0x93, 0xb7, /* 15 */
+ 0x94, 0xc1, 0x95, 0xca, 0x96, 0xd2, 0x97, 0xd8,
+ 0x98, 0xde, 0x99, 0xe3, 0x9a, 0xe8, 0x9b, 0xed,
+ 0x9c, 0xf1, 0x9d, 0xf5, 0x9e, 0xf8, 0x9f, 0xfc,
+ 0xa0, 0xff}
};
static const __u8 tas5130a_sensor_init[][8] = {
@@ -364,7 +366,7 @@
};
/* read 1 byte */
-static int reg_r_1(struct gspca_dev *gspca_dev,
+static int reg_r(struct gspca_dev *gspca_dev,
__u16 index)
{
usb_control_msg(gspca_dev->dev,
@@ -378,26 +380,26 @@
}
static void reg_w(struct gspca_dev *gspca_dev,
- __u16 value,
- __u16 index,
- const __u8 *buffer, __u16 len)
+ __u16 index)
{
- if (buffer == NULL) {
- usb_control_msg(gspca_dev->dev,
- usb_sndctrlpipe(gspca_dev->dev, 0),
- 0,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
- value, index,
- NULL, 0, 500);
- return;
- }
+ usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ 0, index,
+ NULL, 0, 500);
+}
+
+static void i2c_w(struct gspca_dev *gspca_dev,
+ const __u8 *buffer, __u16 len)
+{
if (len <= USB_BUF_SZ) {
memcpy(gspca_dev->usb_buf, buffer, len);
usb_control_msg(gspca_dev->dev,
usb_sndctrlpipe(gspca_dev->dev, 0),
0,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
- value, index,
+ 0x01, 0,
gspca_dev->usb_buf, len, 500);
} else {
__u8 *tmpbuf;
@@ -408,12 +410,56 @@
usb_sndctrlpipe(gspca_dev->dev, 0),
0,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
- value, index,
+ 0x01, 0,
tmpbuf, len, 500);
kfree(tmpbuf);
}
}
+static void other_sensor_init(struct gspca_dev *gspca_dev)
+{
+ int i;
+ const __u8 *p;
+ __u8 byte;
+ __u8 val[6] = {0x62, 0, 0x64, 0, 0x60, 0x05};
+ static const __u8 sensor_init[] = {
+ 0xdf, 0x6d,
+ 0xdd, 0x18,
+ 0x5a, 0xe0,
+ 0x5c, 0x07,
+ 0x5d, 0xb0,
+ 0x5e, 0x1e,
+ 0x60, 0x71,
+ 0xef, 0x00,
+ 0xe9, 0x00,
+ 0xea, 0x00,
+ 0x90, 0x24,
+ 0x91, 0xb2,
+ 0x82, 0x32,
+ 0xfd, 0x00,
+ 0xfd, 0x01,
+ 0xfd, 0x41,
+ 0x00 /* table end */
+ };
+
+ p = sensor_init;
+ while (*p != 0) {
+ val[1] = *p++;
+ val[3] = *p++;
+ if (*p == 0)
+ reg_w(gspca_dev, 0x3c80);
+ i2c_w(gspca_dev, val, sizeof val);
+ i = 4;
+ while (--i >= 0) {
+ msleep(15);
+ byte = reg_r(gspca_dev, 0x60);
+ if (!(byte & 0x01))
+ break;
+ }
+ }
+ reg_w(gspca_dev, 0x3c80);
+}
+
/* this function is called at probe time */
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
@@ -430,7 +476,7 @@
sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value;
sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value;
sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value;
- sd->gamma = sd_ctrls[SD_GAMMA].qctrl.default_value;
+ sd->gamma = GAMMA_DEF;
sd->mirror = sd_ctrls[SD_MIRROR].qctrl.default_value;
sd->freq = sd_ctrls[SD_LIGHTFREQ].qctrl.default_value;
sd->whitebalance = sd_ctrls[SD_WHITE_BALANCE].qctrl.default_value;
@@ -439,27 +485,37 @@
return 0;
}
-static int init_default_parameters(struct gspca_dev *gspca_dev)
+static void setgamma(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(D_CONF, "Gamma: %d", sd->gamma);
+ i2c_w(gspca_dev, gamma_table[sd->gamma], sizeof gamma_table[0]);
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
{
/* some of this registers are not really neded, because
* they are overriden by setbrigthness, setcontrast, etc,
* but wont hurt anyway, and can help someone with similar webcam
* to see the initial parameters.*/
- int i = 0;
- __u8 test_byte;
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+ __u8 byte, test_byte;
static const __u8 read_indexs[] =
{ 0x06, 0x07, 0x0a, 0x0b, 0x66, 0x80, 0x81, 0x8e, 0x8f, 0xa5,
0xa6, 0xa8, 0xbb, 0xbc, 0xc6, 0x00, 0x00 };
- static const __u8 n1[6] =
+ static const __u8 n1[] =
{0x08, 0x03, 0x09, 0x03, 0x12, 0x04};
- static const __u8 n2[2] =
+ static const __u8 n2[] =
{0x08, 0x00};
- static const __u8 nset[6] =
- { 0x61, 0x68, 0x62, 0xff, 0x60, 0x07 };
- static const __u8 n3[6] =
+ static const __u8 nset[] =
+ { 0x61, 0x68, 0x62, 0xff, 0x60, 0x07 };
+ static const __u8 n3[] =
{0x61, 0x68, 0x65, 0x0a, 0x60, 0x04};
- static const __u8 n4[0x46] =
+ static const __u8 n4[] =
{0x09, 0x01, 0x12, 0x04, 0x66, 0x8a, 0x80, 0x3c,
0x81, 0x22, 0x84, 0x50, 0x8a, 0x78, 0x8b, 0x68,
0x8c, 0x88, 0x8e, 0x33, 0x8f, 0x24, 0xaa, 0xb1,
@@ -469,33 +525,26 @@
0x65, 0x0a, 0xbb, 0x86, 0xaf, 0x58, 0xb0, 0x68,
0x87, 0x40, 0x89, 0x2b, 0x8d, 0xff, 0x83, 0x40,
0xac, 0x84, 0xad, 0x86, 0xaf, 0x46};
- static const __u8 nset4[18] = {
+ static const __u8 nset4[] = {
0xe0, 0x60, 0xe1, 0xa8, 0xe2, 0xe0, 0xe3, 0x60, 0xe4, 0xa8,
0xe5, 0xe0, 0xe6, 0x60, 0xe7, 0xa8,
0xe8, 0xe0
};
/* ojo puede ser 0xe6 en vez de 0xe9 */
- static const __u8 nset2[20] = {
+ static const __u8 nset2[] = {
0xd0, 0xbb, 0xd1, 0x28, 0xd2, 0x10, 0xd3, 0x10, 0xd4, 0xbb,
0xd5, 0x28, 0xd6, 0x1e, 0xd7, 0x27,
0xd8, 0xc8, 0xd9, 0xfc
};
- static const __u8 missing[8] =
+ static const __u8 missing[] =
{ 0x87, 0x20, 0x88, 0x20, 0x89, 0x20, 0x80, 0x38 };
- static const __u8 nset3[18] = {
+ static const __u8 nset3[] = {
0xc7, 0x60, 0xc8, 0xa8, 0xc9, 0xe0, 0xca, 0x60, 0xcb, 0xa8,
0xcc, 0xe0, 0xcd, 0x60, 0xce, 0xa8,
0xcf, 0xe0
};
- static const __u8 nset5[4] =
- { 0x8f, 0x24, 0xc3, 0x00 }; /* bright */
- static const __u8 nset6[34] = {
- 0x90, 0x00, 0x91, 0x1c, 0x92, 0x30, 0x93, 0x43, 0x94, 0x54,
- 0x95, 0x65, 0x96, 0x75, 0x97, 0x84,
- 0x98, 0x93, 0x99, 0xa1, 0x9a, 0xb0, 0x9b, 0xbd, 0x9c, 0xca,
- 0x9d, 0xd8, 0x9e, 0xe5, 0x9f, 0xf2,
- 0xa0, 0xff
- }; /* Gamma */
+ static const __u8 nset5[] =
+ { 0x8f, 0x24, 0xc3, 0x00 }; /* bright */
static const __u8 nset7[4] =
{ 0x66, 0xca, 0xa8, 0xf8 }; /* 50/60 Hz */
static const __u8 nset9[4] =
@@ -505,95 +554,111 @@
static const __u8 nset10[6] =
{ 0x0c, 0x03, 0xab, 0x10, 0x81, 0x20 };
- reg_w(gspca_dev, 0x01, 0x0000, n1, 0x06);
- reg_w(gspca_dev, 0x01, 0x0000, nset, 0x06);
- reg_r_1(gspca_dev, 0x0063);
- reg_w(gspca_dev, 0x01, 0x0000, n2, 0x02);
+ byte = reg_r(gspca_dev, 0x06);
+ test_byte = reg_r(gspca_dev, 0x07);
+ if (byte == 0x08 && test_byte == 0x07) {
+ PDEBUG(D_CONF, "other sensor");
+ sd->sensor = SENSOR_OTHER;
+ } else {
+ PDEBUG(D_CONF, "sensor %02x %02x", byte, test_byte);
+ sd->sensor = SENSOR_TAS5130A;
+ }
+ i2c_w(gspca_dev, n1, sizeof n1);
+ test_byte = 0;
+ i = 5;
+ while (--i >= 0) {
+ i2c_w(gspca_dev, nset, sizeof nset);
+ msleep(5);
+ test_byte = reg_r(gspca_dev, 0x0063);
+ msleep(100);
+ if (test_byte == 0x17)
+ break; /* OK */
+ }
+ if (i < 0) {
+ err("Bad sensor reset %02x", test_byte);
+/* return -EIO; */
+/*fixme: test - continue */
+ }
+ i2c_w(gspca_dev, n2, sizeof n2);
+
+ i = 0;
while (read_indexs[i] != 0x00) {
- test_byte = reg_r_1(gspca_dev, read_indexs[i]);
- PDEBUG(D_CONF, "Reg 0x%02x => 0x%02x", read_indexs[i],
+ test_byte = reg_r(gspca_dev, read_indexs[i]);
+ PDEBUG(D_STREAM, "Reg 0x%02x = 0x%02x", read_indexs[i],
test_byte);
i++;
}
- reg_w(gspca_dev, 0x01, 0x0000, n3, 0x06);
- reg_w(gspca_dev, 0x01, 0x0000, n4, 0x46);
- reg_r_1(gspca_dev, 0x0080);
- reg_w(gspca_dev, 0x00, 0x2c80, NULL, 0);
- reg_w(gspca_dev, 0x01, 0x0000, nset2, 0x14);
- reg_w(gspca_dev, 0x01, 0x0000, nset3, 0x12);
- reg_w(gspca_dev, 0x01, 0x0000, nset4, 0x12);
- reg_w(gspca_dev, 0x00, 0x3880, NULL, 0);
- reg_w(gspca_dev, 0x00, 0x3880, NULL, 0);
- reg_w(gspca_dev, 0x00, 0x338e, NULL, 0);
- reg_w(gspca_dev, 0x01, 0x0000, nset5, 0x04);
- reg_w(gspca_dev, 0x00, 0x00a9, NULL, 0);
- reg_w(gspca_dev, 0x01, 0x0000, nset6, 0x22);
- reg_w(gspca_dev, 0x00, 0x86bb, NULL, 0);
- reg_w(gspca_dev, 0x00, 0x4aa6, NULL, 0);
+ i2c_w(gspca_dev, n3, sizeof n3);
+ i2c_w(gspca_dev, n4, sizeof n4);
+ reg_r(gspca_dev, 0x0080);
+ reg_w(gspca_dev, 0x2c80);
+ i2c_w(gspca_dev, nset2, sizeof nset2);
+ i2c_w(gspca_dev, nset3, sizeof nset3);
+ i2c_w(gspca_dev, nset4, sizeof nset4);
+ reg_w(gspca_dev, 0x3880);
+ reg_w(gspca_dev, 0x3880);
+ reg_w(gspca_dev, 0x338e);
+ i2c_w(gspca_dev, nset5, sizeof nset5);
+ reg_w(gspca_dev, 0x00a9);
+ setgamma(gspca_dev);
+ reg_w(gspca_dev, 0x86bb);
+ reg_w(gspca_dev, 0x4aa6);
- reg_w(gspca_dev, 0x01, 0x0000, missing, 0x08);
+ i2c_w(gspca_dev, missing, sizeof missing);
- reg_w(gspca_dev, 0x00, 0x2087, NULL, 0);
- reg_w(gspca_dev, 0x00, 0x2088, NULL, 0);
- reg_w(gspca_dev, 0x00, 0x2089, NULL, 0);
+ reg_w(gspca_dev, 0x2087);
+ reg_w(gspca_dev, 0x2088);
+ reg_w(gspca_dev, 0x2089);
- reg_w(gspca_dev, 0x01, 0x0000, nset7, 0x04);
- reg_w(gspca_dev, 0x01, 0x0000, nset10, 0x06);
- reg_w(gspca_dev, 0x01, 0x0000, nset8, 0x06);
- reg_w(gspca_dev, 0x01, 0x0000, nset9, 0x04);
+ i2c_w(gspca_dev, nset7, sizeof nset7);
+ i2c_w(gspca_dev, nset10, sizeof nset10);
+ i2c_w(gspca_dev, nset8, sizeof nset8);
+ i2c_w(gspca_dev, nset9, sizeof nset9);
- reg_w(gspca_dev, 0x00, 0x2880, NULL, 0);
- reg_w(gspca_dev, 0x01, 0x0000, nset2, 0x14);
- reg_w(gspca_dev, 0x01, 0x0000, nset3, 0x12);
- reg_w(gspca_dev, 0x01, 0x0000, nset4, 0x12);
+ reg_w(gspca_dev, 0x2880);
+ i2c_w(gspca_dev, nset2, sizeof nset2);
+ i2c_w(gspca_dev, nset3, sizeof nset3);
+ i2c_w(gspca_dev, nset4, sizeof nset4);
return 0;
}
-/* this function is called at probe and resume time */
-static int sd_init(struct gspca_dev *gspca_dev)
-{
- init_default_parameters(gspca_dev);
- return 0;
-}
-
static void setbrightness(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
unsigned int brightness;
- __u8 set6[4] = { 0x8f, 0x26, 0xc3, 0x80 };
- brightness = sd->brightness;
+ __u8 set6[4] = { 0x8f, 0x26, 0xc3, 0x00 };
+ brightness = sd->brightness;
if (brightness < 7) {
- set6[3] = 0x70 - (brightness * 0xa);
+ set6[3] = 0x70 - brightness * 0x10;
} else {
set6[1] = 0x24;
- set6[3] = 0x00 + ((brightness - 7) * 0xa);
+ set6[3] = 0x00 + ((brightness - 7) * 0x10);
}
- reg_w(gspca_dev, 0x01, 0x0000, set6, 4);
+ i2c_w(gspca_dev, set6, sizeof set6);
}
static void setflip(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
-
__u8 flipcmd[8] =
- { 0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09 };
+ {0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09};
- if (sd->mirror == 1)
+ if (sd->mirror)
flipcmd[3] = 0x01;
- reg_w(gspca_dev, 0x01, 0x0000, flipcmd, 8);
+ i2c_w(gspca_dev, flipcmd, sizeof flipcmd);
}
static void seteffect(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- reg_w(gspca_dev, 0x01, 0x0000, effects_table[sd->effect], 0x06);
+ i2c_w(gspca_dev, effects_table[sd->effect], sizeof effects_table[0]);
if (sd->effect == 1 || sd->effect == 5) {
PDEBUG(D_CONF,
"This effect have been disabled for webcam \"safety\"");
@@ -601,9 +666,9 @@
}
if (sd->effect == 1 || sd->effect == 4)
- reg_w(gspca_dev, 0x00, 0x4aa6, NULL, 0);
+ reg_w(gspca_dev, 0x4aa6);
else
- reg_w(gspca_dev, 0x00, 0xfaa6, NULL, 0);
+ reg_w(gspca_dev, 0xfaa6);
}
static void setwhitebalance(struct gspca_dev *gspca_dev)
@@ -616,7 +681,7 @@
if (sd->whitebalance == 1)
white_balance[7] = 0x3c;
- reg_w(gspca_dev, 0x01, 0x0000, white_balance, 8);
+ i2c_w(gspca_dev, white_balance, sizeof white_balance);
}
static void setlightfreq(struct gspca_dev *gspca_dev)
@@ -627,21 +692,21 @@
if (sd->freq == 2) /* 60hz */
freq[1] = 0x00;
- reg_w(gspca_dev, 0x1, 0x0000, freq, 0x4);
+ i2c_w(gspca_dev, freq, sizeof freq);
}
static void setcontrast(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
unsigned int contrast = sd->contrast;
- __u16 reg_to_write = 0x00;
+ __u16 reg_to_write;
if (contrast < 7)
reg_to_write = 0x8ea9 - (0x200 * contrast);
else
reg_to_write = (0x00a9 + ((contrast - 7) * 0x200));
- reg_w(gspca_dev, 0x00, reg_to_write, NULL, 0);
+ reg_w(gspca_dev, reg_to_write);
}
static void setcolors(struct gspca_dev *gspca_dev)
@@ -650,11 +715,7 @@
__u16 reg_to_write;
reg_to_write = 0xc0bb + sd->colors * 0x100;
- reg_w(gspca_dev, 0x00, reg_to_write, NULL, 0);
-}
-
-static void setgamma(struct gspca_dev *gspca_dev)
-{
+ reg_w(gspca_dev, reg_to_write);
}
static void setsharpness(struct gspca_dev *gspca_dev)
@@ -664,7 +725,99 @@
reg_to_write = 0x0aa6 + 0x1000 * sd->sharpness;
- reg_w(gspca_dev, 0x00, reg_to_write, NULL, 0);
+ reg_w(gspca_dev, reg_to_write);
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, mode;
+ static const __u8 t1[] = { 0x66, 0x00, 0xa8, 0xe8 };
+ __u8 t2[] = { 0x07, 0x00, 0x0d, 0x60, 0x0e, 0x80 };
+ static const __u8 t3[] =
+ { 0xb3, 0x07, 0xb4, 0x00, 0xb5, 0x88, 0xb6, 0x02, 0xb7, 0x06,
+ 0xb8, 0x00, 0xb9, 0xe7, 0xba, 0x01 };
+ static const __u8 t4[] = { 0x0b, 0x04, 0x0a, 0x40 };
+
+ mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode]. priv;
+ switch (mode) {
+ case 1: /* 352x288 */
+ t2[1] = 0x40;
+ break;
+ case 2: /* 320x240 */
+ t2[1] = 0x10;
+ break;
+ case 3: /* 176x144 */
+ t2[1] = 0x50;
+ break;
+ case 4: /* 160x120 */
+ t2[1] = 0x20;
+ break;
+ default: /* 640x480 (0x00) */
+ break;
+ }
+
+ if (sd->sensor == SENSOR_TAS5130A) {
+ i = 0;
+ while (tas5130a_sensor_init[i][0] != 0) {
+ i2c_w(gspca_dev, tas5130a_sensor_init[i],
+ sizeof tas5130a_sensor_init[0]);
+ i++;
+ }
+ reg_w(gspca_dev, 0x3c80);
+ /* just in case and to keep sync with logs (for mine) */
+ i2c_w(gspca_dev, tas5130a_sensor_init[3],
+ sizeof tas5130a_sensor_init[0]);
+ reg_w(gspca_dev, 0x3c80);
+ } else {
+ other_sensor_init(gspca_dev);
+ }
+ /* just in case and to keep sync with logs (for mine) */
+ i2c_w(gspca_dev, t1, sizeof t1);
+ i2c_w(gspca_dev, t2, sizeof t2);
+ reg_r(gspca_dev, 0x0012);
+ i2c_w(gspca_dev, t3, sizeof t3);
+ reg_w(gspca_dev, 0x0013);
+ i2c_w(gspca_dev, t4, sizeof t4);
+ /* restart on each start, just in case, sometimes regs goes wrong
+ * when using controls from app */
+ setbrightness(gspca_dev);
+ setcontrast(gspca_dev);
+ setcolors(gspca_dev);
+ return 0;
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ struct gspca_frame *frame, /* target */
+ __u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ static __u8 ffd9[] = { 0xff, 0xd9 };
+
+ if (data[0] == 0x5a) {
+ /* Control Packet, after this came the header again,
+ * but extra bytes came in the packet before this,
+ * sometimes an EOF arrives, sometimes not... */
+ return;
+ }
+ data += 2;
+ len -= 2;
+ if (data[0] == 0xff && data[1] == 0xd8) {
+ /* extra bytes....., could be processed too but would be
+ * a waste of time, right now leave the application and
+ * libjpeg do it for ourserlves.. */
+ frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+ ffd9, 2);
+ gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len);
+ return;
+ }
+
+ if (data[len - 2] == 0xff && data[len - 1] == 0xd9) {
+ /* Just in case, i have seen packets with the marker,
+ * other's do not include it... */
+ len -= 2;
+ }
+ gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
}
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
@@ -788,6 +941,7 @@
static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
+
*val = sd->gamma;
return 0;
}
@@ -835,9 +989,9 @@
sd->autogain = val;
if (val != 0)
- reg_w(gspca_dev, 0x00, 0xf48e, NULL, 0);
+ reg_w(gspca_dev, 0xf48e);
else
- reg_w(gspca_dev, 0x00, 0xb48e, NULL, 0);
+ reg_w(gspca_dev, 0xb48e);
return 0;
}
@@ -849,99 +1003,6 @@
return 0;
}
-static void sd_start(struct gspca_dev *gspca_dev)
-{
- int mode;
-
- static const __u8 t1[] = { 0x66, 0x00, 0xa8, 0xe8 };
- __u8 t2[] = { 0x07, 0x00, 0x0d, 0x60, 0x0e, 0x80 };
- static const __u8 t3[] =
- { 0xb3, 0x07, 0xb4, 0x00, 0xb5, 0x88, 0xb6, 0x02, 0xb7, 0x06,
- 0xb8, 0x00, 0xb9, 0xe7, 0xba, 0x01 };
- static const __u8 t4[] = { 0x0b, 0x04, 0x0a, 0x40 };
-
- mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode]. priv;
- switch (mode) {
- case 1: /* 352x288 */
- t2[1] = 0x40;
- break;
- case 2: /* 320x240 */
- t2[1] = 0x10;
- break;
- case 3: /* 176x144 */
- t2[1] = 0x50;
- break;
- case 4: /* 160x120 */
- t2[1] = 0x20;
- break;
- default: /* 640x480 (0x00) */
- break;
- }
-
- reg_w(gspca_dev, 0x01, 0x0000, tas5130a_sensor_init[0], 0x8);
- reg_w(gspca_dev, 0x01, 0x0000, tas5130a_sensor_init[1], 0x8);
- reg_w(gspca_dev, 0x01, 0x0000, tas5130a_sensor_init[2], 0x8);
- reg_w(gspca_dev, 0x01, 0x0000, tas5130a_sensor_init[3], 0x8);
- reg_w(gspca_dev, 0x00, 0x3c80, NULL, 0);
- /* just in case and to keep sync with logs (for mine) */
- reg_w(gspca_dev, 0x01, 0x0000, tas5130a_sensor_init[3], 0x8);
- reg_w(gspca_dev, 0x00, 0x3c80, NULL, 0);
- /* just in case and to keep sync with logs (for mine) */
- reg_w(gspca_dev, 0x01, 0x0000, t1, 4);
- reg_w(gspca_dev, 0x01, 0x0000, t2, 6);
- reg_r_1(gspca_dev, 0x0012);
- reg_w(gspca_dev, 0x01, 0x0000, t3, 0x10);
- reg_w(gspca_dev, 0x00, 0x0013, NULL, 0);
- reg_w(gspca_dev, 0x01, 0x0000, t4, 0x4);
- /* restart on each start, just in case, sometimes regs goes wrong
- * when using controls from app */
- setbrightness(gspca_dev);
- setcontrast(gspca_dev);
- setcolors(gspca_dev);
-}
-
-static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- struct gspca_frame *frame, /* target */
- __u8 *data, /* isoc packet */
- int len) /* iso packet length */
-{
- int sof = 0;
- static __u8 ffd9[] = { 0xff, 0xd9 };
-
- if (data[0] == 0x5a) {
- /* Control Packet, after this came the header again,
- * but extra bytes came in the packet before this,
- * sometimes an EOF arrives, sometimes not... */
- return;
- }
-
- if (data[len - 1] == 0xff && data[len] == 0xd9) {
- /* Just in case, i have seen packets with the marker,
- * other's do not include it... */
- data += 2;
- len -= 4;
- } else if (data[2] == 0xff && data[3] == 0xd8) {
- sof = 1;
- data += 2;
- len -= 2;
- } else {
- data += 2;
- len -= 2;
- }
-
- if (sof) {
- /* extra bytes....., could be processed too but would be
- * a waste of time, right now leave the application and
- * libjpeg do it for ourserlves.. */
- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
- ffd9, 2);
- gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len);
- return;
- }
-
- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
-}
-
static int sd_querymenu(struct gspca_dev *gspca_dev,
struct v4l2_querymenu *menu)
{
diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/video/gspca/tv8532.c
index 084af05..968a591 100644
--- a/drivers/media/video/gspca/tv8532.c
+++ b/drivers/media/video/gspca/tv8532.c
@@ -390,7 +390,7 @@
}
/* -- start the camera -- */
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
reg_w_1(gspca_dev, TV8532_AD_SLOPE, 0x32);
reg_w_1(gspca_dev, TV8532_AD_BITCTRL, 0x00);
@@ -443,6 +443,7 @@
/************************************************/
tv_8532_PollReg(gspca_dev);
reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x00); /* 0x31 */
+ return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c
index bd4c226..be46d92 100644
--- a/drivers/media/video/gspca/vc032x.c
+++ b/drivers/media/video/gspca/vc032x.c
@@ -80,7 +80,6 @@
.step = 1,
#define FREQ_DEF 1
.default_value = FREQ_DEF,
- .default_value = 1,
},
.set = sd_setfreq,
.get = sd_getfreq,
@@ -1502,7 +1501,7 @@
usb_exchange(gspca_dev, ov7660_freq_tb[sd->lightfreq]);
}
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
const __u8 *GammaT = NULL;
@@ -1586,7 +1585,7 @@
break;
default:
PDEBUG(D_PROBE, "Damned !! no sensor found Bye");
- return;
+ return -EMEDIUMTYPE;
}
if (GammaT && MatrixT) {
put_tab_to_reg(gspca_dev, GammaT, 17, 0xb84a);
@@ -1622,6 +1621,7 @@
setautogain(gspca_dev);
setlightfreq(gspca_dev);
}
+ return 0;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c
index d61ef72..d0a4451 100644
--- a/drivers/media/video/gspca/zc3xx.c
+++ b/drivers/media/video/gspca/zc3xx.c
@@ -7178,7 +7178,7 @@
return 0;
}
-static void sd_start(struct gspca_dev *gspca_dev)
+static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
struct usb_device *dev = gspca_dev->dev;
@@ -7331,6 +7331,7 @@
reg_w(dev, 0x02, 0x0008);
break;
}
+ return 0;
}
static void sd_stop0(struct gspca_dev *gspca_dev)
diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c
index a30254b..efe8499 100644
--- a/drivers/media/video/ir-kbd-i2c.c
+++ b/drivers/media/video/ir-kbd-i2c.c
@@ -12,6 +12,10 @@
* Markus Rechberger <mrechberger@gmail.com>
* modified for DViCO Fusion HDTV 5 RT GOLD by
* Chaogui Zhang <czhang1974@gmail.com>
+ * modified for MSI TV@nywhere Plus by
+ * Henry Wong <henry@stuffedcow.net>
+ * Mark Schultz <n9xmj@yahoo.com>
+ * Brian Rogers <brian_rogers@comcast.net>
*
* 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
@@ -65,7 +69,7 @@
int size, int offset)
{
unsigned char buf[6];
- int start, range, toggle, dev, code;
+ int start, range, toggle, dev, code, ircode;
/* poll IR chip */
if (size != i2c_master_recv(&ir->c,buf,size))
@@ -85,6 +89,24 @@
if (!start)
/* no key pressed */
return 0;
+ /*
+ * Hauppauge remotes (black/silver) always use
+ * specific device ids. If we do not filter the
+ * device ids then messages destined for devices
+ * such as TVs (id=0) will get through causing
+ * mis-fired events.
+ *
+ * We also filter out invalid key presses which
+ * produce annoying debug log entries.
+ */
+ ircode= (start << 12) | (toggle << 11) | (dev << 6) | code;
+ if ((ircode & 0x1fff)==0x1fff)
+ /* invalid key press */
+ return 0;
+
+ if (dev!=0x1e && dev!=0x1f)
+ /* not a hauppauge remote */
+ return 0;
if (!range)
code += 64;
@@ -94,7 +116,7 @@
/* return key */
*ir_key = code;
- *ir_raw = (start << 12) | (toggle << 11) | (dev << 6) | code;
+ *ir_raw = ircode;
return 1;
}
@@ -224,9 +246,15 @@
static void ir_work(struct work_struct *work)
{
struct IR_i2c *ir = container_of(work, struct IR_i2c, work);
+ int polling_interval = 100;
+
+ /* MSI TV@nywhere Plus requires more frequent polling
+ otherwise it will miss some keypresses */
+ if (ir->c.adapter->id == I2C_HW_SAA7134 && ir->c.addr == 0x30)
+ polling_interval = 50;
ir_key_poll(ir);
- mod_timer(&ir->timer, jiffies + msecs_to_jiffies(100));
+ mod_timer(&ir->timer, jiffies + msecs_to_jiffies(polling_interval));
}
/* ----------------------------------------------------------------------- */
@@ -465,9 +493,37 @@
(1 == rc) ? "yes" : "no");
if (1 == rc) {
ir_attach(adap, probe[i], 0, 0);
- break;
+ return 0;
}
}
+
+ /* Special case for MSI TV@nywhere Plus remote */
+ if (adap->id == I2C_HW_SAA7134) {
+ u8 temp;
+
+ /* MSI TV@nywhere Plus controller doesn't seem to
+ respond to probes unless we read something from
+ an existing device. Weird... */
+
+ msg.addr = 0x50;
+ rc = i2c_transfer(adap, &msg, 1);
+ dprintk(1, "probe 0x%02x @ %s: %s\n",
+ msg.addr, adap->name,
+ (1 == rc) ? "yes" : "no");
+
+ /* Now do the probe. The controller does not respond
+ to 0-byte reads, so we use a 1-byte read instead. */
+ msg.addr = 0x30;
+ msg.len = 1;
+ msg.buf = &temp;
+ rc = i2c_transfer(adap, &msg, 1);
+ dprintk(1, "probe 0x%02x @ %s: %s\n",
+ msg.addr, adap->name,
+ (1 == rc) ? "yes" : "no");
+ if (1 == rc)
+ ir_attach(adap, msg.addr, 0, 0);
+ }
+
return 0;
}
diff --git a/drivers/media/video/ivtv/ivtv-cards.h b/drivers/media/video/ivtv/ivtv-cards.h
index 381af1b..0b8fe85 100644
--- a/drivers/media/video/ivtv/ivtv-cards.h
+++ b/drivers/media/video/ivtv/ivtv-cards.h
@@ -154,7 +154,7 @@
#define IVTV_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \
V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE | \
V4L2_CAP_SLICED_VBI_CAPTURE)
-#define IVTV_CAP_DECODER (V4L2_CAP_VBI_OUTPUT | V4L2_CAP_VIDEO_OUTPUT | \
+#define IVTV_CAP_DECODER (V4L2_CAP_VIDEO_OUTPUT | \
V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_OVERLAY)
struct ivtv_card_video_input {
diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c
index 4afc7ea..aeaa13f 100644
--- a/drivers/media/video/ivtv/ivtv-driver.c
+++ b/drivers/media/video/ivtv/ivtv-driver.c
@@ -61,14 +61,14 @@
#include "tuner-xc2028.h"
/* var to keep track of the number of array elements in use */
-int ivtv_cards_active = 0;
+int ivtv_cards_active;
/* If you have already X v4l cards, then set this to X. This way
the device numbers stay matched. Example: you have a WinTV card
without radio and a PVR-350 with. Normally this would give a
video1 device together with a radio0 device for the PVR. By
setting this to 1 you ensure that radio0 is now also radio1. */
-int ivtv_first_minor = 0;
+int ivtv_first_minor;
/* Master variable for all ivtv info */
struct ivtv *ivtv_cards[IVTV_MAX_CARDS];
@@ -251,7 +251,7 @@
"\t\t\t-1 is autodetect, 0 is off, 1 is on\n"
"\t\t\tDefault is autodetect");
-MODULE_PARM_DESC(ivtv_first_minor, "Set minor assigned to first card");
+MODULE_PARM_DESC(ivtv_first_minor, "Set kernel number assigned to first card");
MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil");
MODULE_DESCRIPTION("CX23415/CX23416 driver");
@@ -655,9 +655,9 @@
if (itv->card == NULL) {
itv->card = ivtv_get_card(IVTV_CARD_PVR_150);
- IVTV_ERR("Unknown card: vendor/device: %04x/%04x\n",
+ IVTV_ERR("Unknown card: vendor/device: [%04x:%04x]\n",
itv->dev->vendor, itv->dev->device);
- IVTV_ERR(" subsystem vendor/device: %04x/%04x\n",
+ IVTV_ERR(" subsystem vendor/device: [%04x:%04x]\n",
itv->dev->subsystem_vendor, itv->dev->subsystem_device);
IVTV_ERR(" %s based\n", chipname);
IVTV_ERR("Defaulting to %s card\n", itv->card->name);
@@ -720,7 +720,7 @@
itv->speed = 1000;
/* VBI */
- itv->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
+ itv->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
itv->vbi.sliced_in = &itv->vbi.in.fmt.sliced;
/* Init the sg table for osd/yuv output */
diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h
index 2ceb522..bc29436 100644
--- a/drivers/media/video/ivtv/ivtv-driver.h
+++ b/drivers/media/video/ivtv/ivtv-driver.h
@@ -49,7 +49,6 @@
#include <linux/i2c-algo-bit.h>
#include <linux/list.h>
#include <linux/unistd.h>
-#include <linux/byteorder/swab.h>
#include <linux/pagemap.h>
#include <linux/scatterlist.h>
#include <linux/workqueue.h>
@@ -507,6 +506,8 @@
struct v4l2_rect main_rect;
u32 v4l2_src_w;
u32 v4l2_src_h;
+
+ u8 running; /* Have any frames been displayed */
};
#define IVTV_VBI_FRAMES 32
@@ -751,6 +752,12 @@
/* First-open initialization: load firmware, init cx25840, etc. */
int ivtv_init_on_first_open(struct ivtv *itv);
+/* Test if the current VBI mode is raw (1) or sliced (0) */
+static inline int ivtv_raw_vbi(const struct ivtv *itv)
+{
+ return itv->vbi.in.type == V4L2_BUF_TYPE_VBI_CAPTURE;
+}
+
/* This is a PCI post thing, where if the pci register is not read, then
the write doesn't always take effect right away. By reading back the
register any pending PCI writes will be performed (in order), and so
diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c
index 7ec5c99..b7457fc 100644
--- a/drivers/media/video/ivtv/ivtv-fileops.c
+++ b/drivers/media/video/ivtv/ivtv-fileops.c
@@ -39,7 +39,7 @@
associated VBI streams are also automatically claimed.
Possible error returns: -EBUSY if someone else has claimed
the stream or 0 on success. */
-int ivtv_claim_stream(struct ivtv_open_id *id, int type)
+static int ivtv_claim_stream(struct ivtv_open_id *id, int type)
{
struct ivtv *itv = id->itv;
struct ivtv_stream *s = &itv->streams[type];
@@ -78,7 +78,7 @@
if (type == IVTV_DEC_STREAM_TYPE_MPG) {
vbi_type = IVTV_DEC_STREAM_TYPE_VBI;
} else if (type == IVTV_ENC_STREAM_TYPE_MPG &&
- itv->vbi.insert_mpeg && itv->vbi.sliced_in->service_set) {
+ itv->vbi.insert_mpeg && !ivtv_raw_vbi(itv)) {
vbi_type = IVTV_ENC_STREAM_TYPE_VBI;
} else {
return 0;
@@ -305,7 +305,7 @@
if (len > ucount) len = ucount;
if (itv->vbi.insert_mpeg && s->type == IVTV_ENC_STREAM_TYPE_MPG &&
- itv->vbi.sliced_in->service_set && buf != &itv->vbi.sliced_mpeg_buf) {
+ !ivtv_raw_vbi(itv) && buf != &itv->vbi.sliced_mpeg_buf) {
const char *start = buf->buf + buf->readpos;
const char *p = start + 1;
const u8 *q;
@@ -372,7 +372,7 @@
/* Each VBI buffer is one frame, the v4l2 API says that for VBI the frames should
arrive one-by-one, so make sure we never output more than one VBI frame at a time */
if (s->type == IVTV_DEC_STREAM_TYPE_VBI ||
- (s->type == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set))
+ (s->type == IVTV_ENC_STREAM_TYPE_VBI && !ivtv_raw_vbi(itv)))
single_frame = 1;
for (;;) {
diff --git a/drivers/media/video/ivtv/ivtv-fileops.h b/drivers/media/video/ivtv/ivtv-fileops.h
index 2c8d518..df81e79 100644
--- a/drivers/media/video/ivtv/ivtv-fileops.h
+++ b/drivers/media/video/ivtv/ivtv-fileops.h
@@ -38,11 +38,6 @@
/* Utilities */
-/* Try to claim a stream for the filehandle. Return 0 on success,
- -EBUSY if stream already claimed. Once a stream is claimed, it
- remains claimed until the associated filehandle is closed. */
-int ivtv_claim_stream(struct ivtv_open_id *id, int type);
-
/* Release a previously claimed stream. */
void ivtv_release_stream(struct ivtv_stream *s);
diff --git a/drivers/media/video/ivtv/ivtv-gpio.c b/drivers/media/video/ivtv/ivtv-gpio.c
index bc22905..74a4484 100644
--- a/drivers/media/video/ivtv/ivtv-gpio.c
+++ b/drivers/media/video/ivtv/ivtv-gpio.c
@@ -124,7 +124,7 @@
}
/* Xceive tuner reset function */
-int ivtv_reset_tuner_gpio(void *dev, int cmd, int value)
+int ivtv_reset_tuner_gpio(void *dev, int component, int cmd, int value)
{
struct i2c_algo_bit_data *algo = dev;
struct ivtv *itv = algo->data;
diff --git a/drivers/media/video/ivtv/ivtv-gpio.h b/drivers/media/video/ivtv/ivtv-gpio.h
index 964a265..48b6291 100644
--- a/drivers/media/video/ivtv/ivtv-gpio.h
+++ b/drivers/media/video/ivtv/ivtv-gpio.h
@@ -24,7 +24,7 @@
/* GPIO stuff */
void ivtv_gpio_init(struct ivtv *itv);
void ivtv_reset_ir_gpio(struct ivtv *itv);
-int ivtv_reset_tuner_gpio(void *dev, int cmd, int value);
+int ivtv_reset_tuner_gpio(void *dev, int component, int cmd, int value);
int ivtv_gpio(struct ivtv *itv, unsigned int command, void *arg);
#endif
diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c
index af15423..24700c2 100644
--- a/drivers/media/video/ivtv/ivtv-i2c.c
+++ b/drivers/media/video/ivtv/ivtv-i2c.c
@@ -64,8 +64,6 @@
#include "ivtv-gpio.h"
#include "ivtv-i2c.h"
-#include <media/ir-kbd-i2c.h>
-
/* i2c implementation for cx23415/6 chip, ivtv project.
* Author: Kevin Thayer (nufan_wfk at yahoo.com)
*/
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c
index 6103030..8696527 100644
--- a/drivers/media/video/ivtv/ivtv-ioctl.c
+++ b/drivers/media/video/ivtv/ivtv-ioctl.c
@@ -101,18 +101,15 @@
}
}
-static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
+static void check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
{
int f, l;
- u16 set = 0;
for (f = 0; f < 2; f++) {
for (l = 0; l < 24; l++) {
fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal);
- set |= fmt->service_lines[f][l];
}
}
- return set != 0;
}
u16 ivtv_get_service_set(struct v4l2_sliced_vbi_format *fmt)
@@ -474,7 +471,7 @@
int h = fmt->fmt.pix.height;
w = min(w, 720);
- w = max(w, 1);
+ w = max(w, 2);
h = min(h, itv->is_50hz ? 576 : 480);
h = max(h, 2);
ivtv_g_fmt_vid_cap(file, fh, fmt);
@@ -512,27 +509,20 @@
static int ivtv_try_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *fmt)
{
struct ivtv_open_id *id = fh;
- s32 w, h;
- int field;
- int ret;
+ struct ivtv *itv = id->itv;
+ s32 w = fmt->fmt.pix.width;
+ s32 h = fmt->fmt.pix.height;
+ int field = fmt->fmt.pix.field;
+ int ret = ivtv_g_fmt_vid_out(file, fh, fmt);
- w = fmt->fmt.pix.width;
- h = fmt->fmt.pix.height;
- field = fmt->fmt.pix.field;
- ret = ivtv_g_fmt_vid_out(file, fh, fmt);
+ w = min(w, 720);
+ w = max(w, 2);
+ h = min(h, itv->is_out_50hz ? 576 : 480);
+ h = max(h, 2);
+ if (id->type == IVTV_DEC_STREAM_TYPE_YUV)
+ fmt->fmt.pix.field = field;
fmt->fmt.pix.width = w;
fmt->fmt.pix.height = h;
- if (!ret && id->type == IVTV_DEC_STREAM_TYPE_YUV) {
- fmt->fmt.pix.field = field;
- if (fmt->fmt.pix.width < 2)
- fmt->fmt.pix.width = 2;
- if (fmt->fmt.pix.width > 720)
- fmt->fmt.pix.width = 720;
- if (fmt->fmt.pix.height < 2)
- fmt->fmt.pix.height = 2;
- if (fmt->fmt.pix.height > 576)
- fmt->fmt.pix.height = 576;
- }
return ret;
}
@@ -560,9 +550,9 @@
struct ivtv_open_id *id = fh;
struct ivtv *itv = id->itv;
struct cx2341x_mpeg_params *p = &itv->params;
+ int ret = ivtv_try_fmt_vid_cap(file, fh, fmt);
int w = fmt->fmt.pix.width;
int h = fmt->fmt.pix.height;
- int ret = ivtv_try_fmt_vid_cap(file, fh, fmt);
if (ret)
return ret;
@@ -585,8 +575,11 @@
{
struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
+ if (!ivtv_raw_vbi(itv) && atomic_read(&itv->capturing) > 0)
+ return -EBUSY;
itv->vbi.sliced_in->service_set = 0;
- itv->video_dec_func(itv, VIDIOC_S_FMT, &itv->vbi.in);
+ itv->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ itv->video_dec_func(itv, VIDIOC_S_FMT, fmt);
return ivtv_g_fmt_vbi_cap(file, fh, fmt);
}
@@ -600,10 +593,10 @@
if (ret || id->type == IVTV_DEC_STREAM_TYPE_VBI)
return ret;
- if (check_service_set(vbifmt, itv->is_50hz) == 0)
- return -EINVAL;
- if (atomic_read(&itv->capturing) > 0)
+ check_service_set(vbifmt, itv->is_50hz);
+ if (ivtv_raw_vbi(itv) && atomic_read(&itv->capturing) > 0)
return -EBUSY;
+ itv->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
itv->video_dec_func(itv, VIDIOC_S_FMT, fmt);
memcpy(itv->vbi.sliced_in, vbifmt, sizeof(*itv->vbi.sliced_in));
return 0;
@@ -651,8 +644,6 @@
itv->dma_data_req_size =
1080 * ((yi->v4l2_src_h + 31) & ~31);
- /* Force update of yuv registers */
- yi->yuv_forced_update = 1;
return 0;
}
@@ -761,7 +752,7 @@
strlcpy(vcap->driver, IVTV_DRIVER_NAME, sizeof(vcap->driver));
strlcpy(vcap->card, itv->card_name, sizeof(vcap->card));
- strlcpy(vcap->bus_info, pci_name(itv->dev), sizeof(vcap->bus_info));
+ snprintf(vcap->bus_info, sizeof(vcap->bus_info), "PCI:%s", pci_name(itv->dev));
vcap->version = IVTV_DRIVER_VERSION; /* version */
vcap->capabilities = itv->v4l2_cap; /* capabilities */
return 0;
@@ -1370,6 +1361,9 @@
if (itv->osd_global_alpha_state)
fb->flags |= V4L2_FBUF_FLAG_GLOBAL_ALPHA;
+ if (yi->track_osd)
+ fb->flags |= V4L2_FBUF_FLAG_OVERLAY;
+
pixfmt &= 7;
/* no local alpha for RGB565 or unknown formats */
@@ -1389,8 +1383,6 @@
else
fb->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA;
}
- if (yi->track_osd)
- fb->flags |= V4L2_FBUF_FLAG_OVERLAY;
return 0;
}
diff --git a/drivers/media/video/ivtv/ivtv-irq.c b/drivers/media/video/ivtv/ivtv-irq.c
index 34f3ab8..f5d00ec 100644
--- a/drivers/media/video/ivtv/ivtv-irq.c
+++ b/drivers/media/video/ivtv/ivtv-irq.c
@@ -753,7 +753,7 @@
*/
unsigned int frame = read_reg(0x28c0) & 1;
struct yuv_playback_info *yi = &itv->yuv_info;
- int last_dma_frame = atomic_read(&itv->yuv_info.next_dma_frame);
+ int last_dma_frame = atomic_read(&yi->next_dma_frame);
struct yuv_frame_info *f = &yi->new_frame_info[last_dma_frame];
if (0) IVTV_DEBUG_IRQ("DEC VSYNC\n");
@@ -772,6 +772,7 @@
next_dma_frame = (next_dma_frame + 1) % IVTV_YUV_BUFFERS;
atomic_set(&yi->next_dma_frame, next_dma_frame);
yi->fields_lapsed = -1;
+ yi->running = 1;
}
}
}
@@ -804,9 +805,11 @@
}
/* Check if we need to update the yuv registers */
- if ((yi->yuv_forced_update || f->update) && last_dma_frame != -1) {
+ if (yi->running && (yi->yuv_forced_update || f->update)) {
if (!f->update) {
- last_dma_frame = (u8)(last_dma_frame - 1) % IVTV_YUV_BUFFERS;
+ last_dma_frame =
+ (u8)(atomic_read(&yi->next_dma_frame) -
+ 1) % IVTV_YUV_BUFFERS;
f = &yi->new_frame_info[last_dma_frame];
}
diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c
index 730e85d..5bbf31e 100644
--- a/drivers/media/video/ivtv/ivtv-streams.c
+++ b/drivers/media/video/ivtv/ivtv-streams.c
@@ -75,7 +75,7 @@
static struct {
const char *name;
int vfl_type;
- int minor_offset;
+ int num_offset;
int dma, pio;
enum v4l2_buf_type buf_type;
const struct file_operations *fops;
@@ -171,8 +171,8 @@
static int ivtv_prep_dev(struct ivtv *itv, int type)
{
struct ivtv_stream *s = &itv->streams[type];
- int minor_offset = ivtv_stream_info[type].minor_offset;
- int minor;
+ int num_offset = ivtv_stream_info[type].num_offset;
+ int num = itv->num + ivtv_first_minor + num_offset;
/* These four fields are always initialized. If v4l2dev == NULL, then
this stream is not in use. In that case no other fields but these
@@ -188,9 +188,6 @@
if (type >= IVTV_DEC_STREAM_TYPE_MPG && !(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
return 0;
- /* card number + user defined offset + device offset */
- minor = itv->num + ivtv_first_minor + minor_offset;
-
/* User explicitly selected 0 buffers for these streams, so don't
create them. */
if (ivtv_stream_info[type].dma != PCI_DMA_NONE &&
@@ -211,7 +208,7 @@
snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "ivtv%d %s",
itv->num, s->name);
- s->v4l2dev->minor = minor;
+ s->v4l2dev->num = num;
s->v4l2dev->parent = &itv->dev->dev;
s->v4l2dev->fops = ivtv_stream_info[type].fops;
s->v4l2dev->release = video_device_release;
@@ -250,39 +247,46 @@
{
struct ivtv_stream *s = &itv->streams[type];
int vfl_type = ivtv_stream_info[type].vfl_type;
- int minor;
+ int num;
if (s->v4l2dev == NULL)
return 0;
- minor = s->v4l2dev->minor;
+ num = s->v4l2dev->num;
+ /* card number + user defined offset + device offset */
+ if (type != IVTV_ENC_STREAM_TYPE_MPG) {
+ struct ivtv_stream *s_mpg = &itv->streams[IVTV_ENC_STREAM_TYPE_MPG];
+
+ if (s_mpg->v4l2dev)
+ num = s_mpg->v4l2dev->num + ivtv_stream_info[type].num_offset;
+ }
+
/* Register device. First try the desired minor, then any free one. */
- if (video_register_device(s->v4l2dev, vfl_type, minor) &&
- video_register_device(s->v4l2dev, vfl_type, -1)) {
- IVTV_ERR("Couldn't register v4l2 device for %s minor %d\n",
- s->name, minor);
+ if (video_register_device(s->v4l2dev, vfl_type, num)) {
+ IVTV_ERR("Couldn't register v4l2 device for %s kernel number %d\n",
+ s->name, num);
video_device_release(s->v4l2dev);
s->v4l2dev = NULL;
return -ENOMEM;
}
+ num = s->v4l2dev->num;
switch (vfl_type) {
case VFL_TYPE_GRABBER:
IVTV_INFO("Registered device video%d for %s (%d kB)\n",
- s->v4l2dev->minor, s->name, itv->options.kilobytes[type]);
+ num, s->name, itv->options.kilobytes[type]);
break;
case VFL_TYPE_RADIO:
IVTV_INFO("Registered device radio%d for %s\n",
- s->v4l2dev->minor - MINOR_VFL_TYPE_RADIO_MIN, s->name);
+ num, s->name);
break;
case VFL_TYPE_VBI:
if (itv->options.kilobytes[type])
IVTV_INFO("Registered device vbi%d for %s (%d kB)\n",
- s->v4l2dev->minor - MINOR_VFL_TYPE_VBI_MIN,
- s->name, itv->options.kilobytes[type]);
+ num, s->name, itv->options.kilobytes[type]);
else
IVTV_INFO("Registered device vbi%d for %s\n",
- s->v4l2dev->minor - MINOR_VFL_TYPE_VBI_MIN, s->name);
+ num, s->name);
break;
}
return 0;
@@ -330,7 +334,7 @@
static void ivtv_vbi_setup(struct ivtv *itv)
{
- int raw = itv->vbi.sliced_in->service_set == 0;
+ int raw = ivtv_raw_vbi(itv);
u32 data[CX2341X_MBOX_MAX_DATA];
int lines;
int i;
diff --git a/drivers/media/video/ivtv/ivtv-vbi.c b/drivers/media/video/ivtv/ivtv-vbi.c
index 1ce9deb..4a37a7d 100644
--- a/drivers/media/video/ivtv/ivtv-vbi.c
+++ b/drivers/media/video/ivtv/ivtv-vbi.c
@@ -334,7 +334,7 @@
int y;
/* Raw VBI data */
- if (streamtype == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set == 0) {
+ if (streamtype == IVTV_ENC_STREAM_TYPE_VBI && ivtv_raw_vbi(itv)) {
u8 type;
ivtv_buf_swap(buf);
diff --git a/drivers/media/video/ivtv/ivtv-yuv.c b/drivers/media/video/ivtv/ivtv-yuv.c
index 3092ff1..ee91107 100644
--- a/drivers/media/video/ivtv/ivtv-yuv.c
+++ b/drivers/media/video/ivtv/ivtv-yuv.c
@@ -1147,6 +1147,7 @@
IVTV_DEBUG_YUV("ivtv_yuv_close\n");
ivtv_waitq(&itv->vsync_waitq);
+ yi->running = 0;
atomic_set(&yi->next_dma_frame, -1);
atomic_set(&yi->next_fill_frame, 0);
diff --git a/drivers/media/video/ivtv/ivtvfb.c b/drivers/media/video/ivtv/ivtvfb.c
index bdfda48..8a4a150 100644
--- a/drivers/media/video/ivtv/ivtvfb.c
+++ b/drivers/media/video/ivtv/ivtvfb.c
@@ -275,7 +275,6 @@
int size_in_bytes)
{
DEFINE_WAIT(wait);
- int ret = 0;
int got_sig = 0;
mutex_lock(&itv->udma.lock);
@@ -316,7 +315,7 @@
return -EINTR;
}
- return ret;
+ return 0;
}
static int ivtvfb_prep_frame(struct ivtv *itv, int cmd, void __user *source,
@@ -368,11 +367,12 @@
}
static ssize_t ivtvfb_write(struct fb_info *info, const char __user *buf,
- size_t count, loff_t *ppos)
+ size_t count, loff_t *ppos)
{
unsigned long p = *ppos;
void *dst;
int err = 0;
+ int dma_err;
unsigned long total_size;
struct ivtv *itv = (struct ivtv *) info->par;
unsigned long dma_offset =
@@ -399,7 +399,6 @@
if (count + p > total_size) {
if (!err)
err = -ENOSPC;
-
count = total_size - p;
}
@@ -408,39 +407,34 @@
if (info->fbops->fb_sync)
info->fbops->fb_sync(info);
- if (!access_ok(VERIFY_READ, buf, count)) {
- IVTVFB_WARN("Invalid userspace pointer 0x%08lx\n",
- (unsigned long)buf);
- err = -EFAULT;
- }
-
- if (!err) {
- /* If transfer size > threshold and both src/dst
- addresses are aligned, use DMA */
- if (count >= 4096 &&
- ((unsigned long)buf & 3) == ((unsigned long)dst & 3)) {
- /* Odd address = can't DMA. Align */
- if ((unsigned long)dst & 3) {
- lead = 4 - ((unsigned long)dst & 3);
- memcpy(dst, buf, lead);
- buf += lead;
- dst += lead;
- }
- /* DMA resolution is 32 bits */
- if ((count - lead) & 3)
- tail = (count - lead) & 3;
- /* DMA the data */
- dma_size = count - lead - tail;
- err = ivtvfb_prep_dec_dma_to_device(itv,
- p + lead + dma_offset, (void *)buf, dma_size);
- dst += dma_size;
- buf += dma_size;
- /* Copy any leftover data */
- if (tail)
- memcpy(dst, buf, tail);
- } else {
- memcpy(dst, buf, count);
+ /* If transfer size > threshold and both src/dst
+ addresses are aligned, use DMA */
+ if (count >= 4096 &&
+ ((unsigned long)buf & 3) == ((unsigned long)dst & 3)) {
+ /* Odd address = can't DMA. Align */
+ if ((unsigned long)dst & 3) {
+ lead = 4 - ((unsigned long)dst & 3);
+ if (copy_from_user(dst, buf, lead))
+ return -EFAULT;
+ buf += lead;
+ dst += lead;
}
+ /* DMA resolution is 32 bits */
+ if ((count - lead) & 3)
+ tail = (count - lead) & 3;
+ /* DMA the data */
+ dma_size = count - lead - tail;
+ dma_err = ivtvfb_prep_dec_dma_to_device(itv,
+ p + lead + dma_offset, (void __user *)buf, dma_size);
+ if (dma_err)
+ return dma_err;
+ dst += dma_size;
+ buf += dma_size;
+ /* Copy any leftover data */
+ if (tail && copy_from_user(dst, buf, tail))
+ return -EFAULT;
+ } else if (copy_from_user(dst, buf, count)) {
+ return -EFAULT;
}
if (!err)
@@ -463,9 +457,12 @@
vblank.flags = FB_VBLANK_HAVE_COUNT |FB_VBLANK_HAVE_VCOUNT |
FB_VBLANK_HAVE_VSYNC;
trace = read_reg(0x028c0) >> 16;
- if (itv->is_50hz && trace > 312) trace -= 312;
- else if (itv->is_60hz && trace > 262) trace -= 262;
- if (trace == 1) vblank.flags |= FB_VBLANK_VSYNCING;
+ if (itv->is_50hz && trace > 312)
+ trace -= 312;
+ else if (itv->is_60hz && trace > 262)
+ trace -= 262;
+ if (trace == 1)
+ vblank.flags |= FB_VBLANK_VSYNCING;
vblank.count = itv->last_vsync_field;
vblank.vcount = trace;
vblank.hcount = 0;
@@ -476,7 +473,8 @@
case FBIO_WAITFORVSYNC:
prepare_to_wait(&itv->vsync_waitq, &wait, TASK_INTERRUPTIBLE);
- if (!schedule_timeout(msecs_to_jiffies(50))) rc = -ETIMEDOUT;
+ if (!schedule_timeout(msecs_to_jiffies(50)))
+ rc = -ETIMEDOUT;
finish_wait(&itv->vsync_waitq, &wait);
return rc;
diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c
index a9ef780..6418f4a 100644
--- a/drivers/media/video/meye.c
+++ b/drivers/media/video/meye.c
@@ -843,17 +843,16 @@
static int meye_open(struct inode *inode, struct file *file)
{
- int i, err;
+ int i;
- err = video_exclusive_open(inode, file);
- if (err < 0)
- return err;
+ if (test_and_set_bit(0, &meye.in_use))
+ return -EBUSY;
mchip_hic_stop();
if (mchip_dma_alloc()) {
printk(KERN_ERR "meye: mchip framebuffer allocation failed\n");
- video_exclusive_release(inode, file);
+ clear_bit(0, &meye.in_use);
return -ENOBUFS;
}
@@ -868,7 +867,7 @@
{
mchip_hic_stop();
mchip_dma_free();
- video_exclusive_release(inode, file);
+ clear_bit(0, &meye.in_use);
return 0;
}
@@ -1774,6 +1773,7 @@
goto outnotdev;
}
+ ret = -ENOMEM;
meye.mchip_dev = pcidev;
meye.video_dev = video_device_alloc();
if (!meye.video_dev) {
@@ -1781,7 +1781,6 @@
goto outnotdev;
}
- ret = -ENOMEM;
meye.grab_temp = vmalloc(MCHIP_NB_PAGES_MJPEG * PAGE_SIZE);
if (!meye.grab_temp) {
printk(KERN_ERR "meye: grab buffer allocation failed\n");
diff --git a/drivers/media/video/meye.h b/drivers/media/video/meye.h
index d535748..5f70a10 100644
--- a/drivers/media/video/meye.h
+++ b/drivers/media/video/meye.h
@@ -311,6 +311,7 @@
struct video_device *video_dev; /* video device parameters */
struct video_picture picture; /* video picture parameters */
struct meye_params params; /* additional parameters */
+ unsigned long in_use; /* set to 1 if the device is in use */
#ifdef CONFIG_PM
u8 pm_mchip_mode; /* old mchip mode */
#endif
diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c
index 554d229..0c52437 100644
--- a/drivers/media/video/mt9m001.c
+++ b/drivers/media/video/mt9m001.c
@@ -117,24 +117,51 @@
static int mt9m001_init(struct soc_camera_device *icd)
{
+ struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
+ struct soc_camera_link *icl = mt9m001->client->dev.platform_data;
int ret;
- /* Disable chip, synchronous option update */
dev_dbg(icd->vdev->parent, "%s\n", __func__);
- ret = reg_write(icd, MT9M001_RESET, 1);
- if (ret >= 0)
- ret = reg_write(icd, MT9M001_RESET, 0);
- if (ret >= 0)
+ if (icl->power) {
+ ret = icl->power(&mt9m001->client->dev, 1);
+ if (ret < 0) {
+ dev_err(icd->vdev->parent,
+ "Platform failed to power-on the camera.\n");
+ return ret;
+ }
+ }
+
+ /* The camera could have been already on, we reset it additionally */
+ if (icl->reset)
+ ret = icl->reset(&mt9m001->client->dev);
+ else
+ ret = -ENODEV;
+
+ if (ret < 0) {
+ /* Either no platform reset, or platform reset failed */
+ ret = reg_write(icd, MT9M001_RESET, 1);
+ if (!ret)
+ ret = reg_write(icd, MT9M001_RESET, 0);
+ }
+ /* Disable chip, synchronous option update */
+ if (!ret)
ret = reg_write(icd, MT9M001_OUTPUT_CONTROL, 0);
- return ret >= 0 ? 0 : -EIO;
+ return ret;
}
static int mt9m001_release(struct soc_camera_device *icd)
{
+ struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
+ struct soc_camera_link *icl = mt9m001->client->dev.platform_data;
+
/* Disable the chip */
reg_write(icd, MT9M001_OUTPUT_CONTROL, 0);
+
+ if (icl->power)
+ icl->power(&mt9m001->client->dev, 0);
+
return 0;
}
@@ -267,24 +294,24 @@
/* Blanking and start values - default... */
ret = reg_write(icd, MT9M001_HORIZONTAL_BLANKING, hblank);
- if (ret >= 0)
+ if (!ret)
ret = reg_write(icd, MT9M001_VERTICAL_BLANKING, vblank);
/* The caller provides a supported format, as verified per
* call to icd->try_fmt_cap() */
- if (ret >= 0)
+ if (!ret)
ret = reg_write(icd, MT9M001_COLUMN_START, rect->left);
- if (ret >= 0)
+ if (!ret)
ret = reg_write(icd, MT9M001_ROW_START, rect->top);
- if (ret >= 0)
+ if (!ret)
ret = reg_write(icd, MT9M001_WINDOW_WIDTH, rect->width - 1);
- if (ret >= 0)
+ if (!ret)
ret = reg_write(icd, MT9M001_WINDOW_HEIGHT,
rect->height + icd->y_skip_top - 1);
- if (ret >= 0 && mt9m001->autoexposure) {
+ if (!ret && mt9m001->autoexposure) {
ret = reg_write(icd, MT9M001_SHUTTER_WIDTH,
rect->height + icd->y_skip_top + vblank);
- if (ret >= 0) {
+ if (!ret) {
const struct v4l2_queryctrl *qctrl =
soc_camera_find_qctrl(icd->ops,
V4L2_CID_EXPOSURE);
@@ -295,7 +322,7 @@
}
}
- return ret < 0 ? ret : 0;
+ return ret;
}
static int mt9m001_try_fmt_cap(struct soc_camera_device *icd,
diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c
new file mode 100644
index 0000000..da0b2d5
--- /dev/null
+++ b/drivers/media/video/mt9m111.c
@@ -0,0 +1,973 @@
+/*
+ * Driver for MT9M111 CMOS Image Sensor from Micron
+ *
+ * Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/log2.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/soc_camera.h>
+
+/*
+ * mt9m111 i2c address is 0x5d or 0x48 (depending on SAddr pin)
+ * The platform has to define i2c_board_info and call i2c_register_board_info()
+ */
+
+/* mt9m111: Sensor register addresses */
+#define MT9M111_CHIP_VERSION 0x000
+#define MT9M111_ROW_START 0x001
+#define MT9M111_COLUMN_START 0x002
+#define MT9M111_WINDOW_HEIGHT 0x003
+#define MT9M111_WINDOW_WIDTH 0x004
+#define MT9M111_HORIZONTAL_BLANKING_B 0x005
+#define MT9M111_VERTICAL_BLANKING_B 0x006
+#define MT9M111_HORIZONTAL_BLANKING_A 0x007
+#define MT9M111_VERTICAL_BLANKING_A 0x008
+#define MT9M111_SHUTTER_WIDTH 0x009
+#define MT9M111_ROW_SPEED 0x00a
+#define MT9M111_EXTRA_DELAY 0x00b
+#define MT9M111_SHUTTER_DELAY 0x00c
+#define MT9M111_RESET 0x00d
+#define MT9M111_READ_MODE_B 0x020
+#define MT9M111_READ_MODE_A 0x021
+#define MT9M111_FLASH_CONTROL 0x023
+#define MT9M111_GREEN1_GAIN 0x02b
+#define MT9M111_BLUE_GAIN 0x02c
+#define MT9M111_RED_GAIN 0x02d
+#define MT9M111_GREEN2_GAIN 0x02e
+#define MT9M111_GLOBAL_GAIN 0x02f
+#define MT9M111_CONTEXT_CONTROL 0x0c8
+#define MT9M111_PAGE_MAP 0x0f0
+#define MT9M111_BYTE_WISE_ADDR 0x0f1
+
+#define MT9M111_RESET_SYNC_CHANGES (1 << 15)
+#define MT9M111_RESET_RESTART_BAD_FRAME (1 << 9)
+#define MT9M111_RESET_SHOW_BAD_FRAMES (1 << 8)
+#define MT9M111_RESET_RESET_SOC (1 << 5)
+#define MT9M111_RESET_OUTPUT_DISABLE (1 << 4)
+#define MT9M111_RESET_CHIP_ENABLE (1 << 3)
+#define MT9M111_RESET_ANALOG_STANDBY (1 << 2)
+#define MT9M111_RESET_RESTART_FRAME (1 << 1)
+#define MT9M111_RESET_RESET_MODE (1 << 0)
+
+#define MT9M111_RMB_MIRROR_COLS (1 << 1)
+#define MT9M111_RMB_MIRROR_ROWS (1 << 0)
+#define MT9M111_CTXT_CTRL_RESTART (1 << 15)
+#define MT9M111_CTXT_CTRL_DEFECTCOR_B (1 << 12)
+#define MT9M111_CTXT_CTRL_RESIZE_B (1 << 10)
+#define MT9M111_CTXT_CTRL_CTRL2_B (1 << 9)
+#define MT9M111_CTXT_CTRL_GAMMA_B (1 << 8)
+#define MT9M111_CTXT_CTRL_XENON_EN (1 << 7)
+#define MT9M111_CTXT_CTRL_READ_MODE_B (1 << 3)
+#define MT9M111_CTXT_CTRL_LED_FLASH_EN (1 << 2)
+#define MT9M111_CTXT_CTRL_VBLANK_SEL_B (1 << 1)
+#define MT9M111_CTXT_CTRL_HBLANK_SEL_B (1 << 0)
+/*
+ * mt9m111: Colorpipe register addresses (0x100..0x1ff)
+ */
+#define MT9M111_OPER_MODE_CTRL 0x106
+#define MT9M111_OUTPUT_FORMAT_CTRL 0x108
+#define MT9M111_REDUCER_XZOOM_B 0x1a0
+#define MT9M111_REDUCER_XSIZE_B 0x1a1
+#define MT9M111_REDUCER_YZOOM_B 0x1a3
+#define MT9M111_REDUCER_YSIZE_B 0x1a4
+#define MT9M111_REDUCER_XZOOM_A 0x1a6
+#define MT9M111_REDUCER_XSIZE_A 0x1a7
+#define MT9M111_REDUCER_YZOOM_A 0x1a9
+#define MT9M111_REDUCER_YSIZE_A 0x1aa
+
+#define MT9M111_OUTPUT_FORMAT_CTRL2_A 0x13a
+#define MT9M111_OUTPUT_FORMAT_CTRL2_B 0x19b
+
+#define MT9M111_OPMODE_AUTOEXPO_EN (1 << 14)
+
+
+#define MT9M111_OUTFMT_PROCESSED_BAYER (1 << 14)
+#define MT9M111_OUTFMT_BYPASS_IFP (1 << 10)
+#define MT9M111_OUTFMT_INV_PIX_CLOCK (1 << 9)
+#define MT9M111_OUTFMT_RGB (1 << 8)
+#define MT9M111_OUTFMT_RGB565 (0x0 << 6)
+#define MT9M111_OUTFMT_RGB555 (0x1 << 6)
+#define MT9M111_OUTFMT_RGB444x (0x2 << 6)
+#define MT9M111_OUTFMT_RGBx444 (0x3 << 6)
+#define MT9M111_OUTFMT_TST_RAMP_OFF (0x0 << 4)
+#define MT9M111_OUTFMT_TST_RAMP_COL (0x1 << 4)
+#define MT9M111_OUTFMT_TST_RAMP_ROW (0x2 << 4)
+#define MT9M111_OUTFMT_TST_RAMP_FRAME (0x3 << 4)
+#define MT9M111_OUTFMT_SHIFT_3_UP (1 << 3)
+#define MT9M111_OUTFMT_AVG_CHROMA (1 << 2)
+#define MT9M111_OUTFMT_SWAP_YCbCr_C_Y (1 << 1)
+#define MT9M111_OUTFMT_SWAP_RGB_EVEN (1 << 1)
+#define MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr (1 << 0)
+/*
+ * mt9m111: Camera control register addresses (0x200..0x2ff not implemented)
+ */
+
+#define reg_read(reg) mt9m111_reg_read(icd, MT9M111_##reg)
+#define reg_write(reg, val) mt9m111_reg_write(icd, MT9M111_##reg, (val))
+#define reg_set(reg, val) mt9m111_reg_set(icd, MT9M111_##reg, (val))
+#define reg_clear(reg, val) mt9m111_reg_clear(icd, MT9M111_##reg, (val))
+
+#define MT9M111_MIN_DARK_ROWS 8
+#define MT9M111_MIN_DARK_COLS 24
+#define MT9M111_MAX_HEIGHT 1024
+#define MT9M111_MAX_WIDTH 1280
+
+#define COL_FMT(_name, _depth, _fourcc, _colorspace) \
+ { .name = _name, .depth = _depth, .fourcc = _fourcc, \
+ .colorspace = _colorspace }
+#define RGB_FMT(_name, _depth, _fourcc) \
+ COL_FMT(_name, _depth, _fourcc, V4L2_COLORSPACE_SRGB)
+
+static const struct soc_camera_data_format mt9m111_colour_formats[] = {
+ COL_FMT("YCrYCb 8 bit", 8, V4L2_PIX_FMT_YUYV, V4L2_COLORSPACE_JPEG),
+ RGB_FMT("RGB 565", 16, V4L2_PIX_FMT_RGB565),
+ RGB_FMT("RGB 555", 16, V4L2_PIX_FMT_RGB555),
+ RGB_FMT("Bayer (sRGB) 10 bit", 10, V4L2_PIX_FMT_SBGGR16),
+ RGB_FMT("Bayer (sRGB) 8 bit", 8, V4L2_PIX_FMT_SBGGR8),
+};
+
+enum mt9m111_context {
+ HIGHPOWER = 0,
+ LOWPOWER,
+};
+
+struct mt9m111 {
+ struct i2c_client *client;
+ struct soc_camera_device icd;
+ int model; /* V4L2_IDENT_MT9M111* codes from v4l2-chip-ident.h */
+ enum mt9m111_context context;
+ unsigned int left, top, width, height;
+ u32 pixfmt;
+ unsigned char autoexposure;
+ unsigned char datawidth;
+ unsigned int powered:1;
+ unsigned int hflip:1;
+ unsigned int vflip:1;
+ unsigned int swap_rgb_even_odd:1;
+ unsigned int swap_rgb_red_blue:1;
+ unsigned int swap_yuv_y_chromas:1;
+ unsigned int swap_yuv_cb_cr:1;
+};
+
+static int reg_page_map_set(struct i2c_client *client, const u16 reg)
+{
+ int ret;
+ u16 page;
+ static int lastpage = -1; /* PageMap cache value */
+
+ page = (reg >> 8);
+ if (page == lastpage)
+ return 0;
+ if (page > 2)
+ return -EINVAL;
+
+ ret = i2c_smbus_write_word_data(client, MT9M111_PAGE_MAP, swab16(page));
+ if (!ret)
+ lastpage = page;
+ return ret;
+}
+
+static int mt9m111_reg_read(struct soc_camera_device *icd, const u16 reg)
+{
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ struct i2c_client *client = mt9m111->client;
+ int ret;
+
+ ret = reg_page_map_set(client, reg);
+ if (!ret)
+ ret = swab16(i2c_smbus_read_word_data(client, (reg & 0xff)));
+
+ dev_dbg(&icd->dev, "read reg.%03x -> %04x\n", reg, ret);
+ return ret;
+}
+
+static int mt9m111_reg_write(struct soc_camera_device *icd, const u16 reg,
+ const u16 data)
+{
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ struct i2c_client *client = mt9m111->client;
+ int ret;
+
+ ret = reg_page_map_set(client, reg);
+ if (!ret)
+ ret = i2c_smbus_write_word_data(mt9m111->client, (reg & 0xff),
+ swab16(data));
+ dev_dbg(&icd->dev, "write reg.%03x = %04x -> %d\n", reg, data, ret);
+ return ret;
+}
+
+static int mt9m111_reg_set(struct soc_camera_device *icd, const u16 reg,
+ const u16 data)
+{
+ int ret;
+
+ ret = mt9m111_reg_read(icd, reg);
+ if (ret >= 0)
+ ret = mt9m111_reg_write(icd, reg, ret | data);
+ return ret;
+}
+
+static int mt9m111_reg_clear(struct soc_camera_device *icd, const u16 reg,
+ const u16 data)
+{
+ int ret;
+
+ ret = mt9m111_reg_read(icd, reg);
+ return mt9m111_reg_write(icd, reg, ret & ~data);
+}
+
+static int mt9m111_set_context(struct soc_camera_device *icd,
+ enum mt9m111_context ctxt)
+{
+ int valB = MT9M111_CTXT_CTRL_RESTART | MT9M111_CTXT_CTRL_DEFECTCOR_B
+ | MT9M111_CTXT_CTRL_RESIZE_B | MT9M111_CTXT_CTRL_CTRL2_B
+ | MT9M111_CTXT_CTRL_GAMMA_B | MT9M111_CTXT_CTRL_READ_MODE_B
+ | MT9M111_CTXT_CTRL_VBLANK_SEL_B
+ | MT9M111_CTXT_CTRL_HBLANK_SEL_B;
+ int valA = MT9M111_CTXT_CTRL_RESTART;
+
+ if (ctxt == HIGHPOWER)
+ return reg_write(CONTEXT_CONTROL, valB);
+ else
+ return reg_write(CONTEXT_CONTROL, valA);
+}
+
+static int mt9m111_setup_rect(struct soc_camera_device *icd)
+{
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ int ret, is_raw_format;
+ int width = mt9m111->width;
+ int height = mt9m111->height;
+
+ if ((mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR8)
+ || (mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR16))
+ is_raw_format = 1;
+ else
+ is_raw_format = 0;
+
+ ret = reg_write(COLUMN_START, mt9m111->left);
+ if (!ret)
+ ret = reg_write(ROW_START, mt9m111->top);
+
+ if (is_raw_format) {
+ if (!ret)
+ ret = reg_write(WINDOW_WIDTH, width);
+ if (!ret)
+ ret = reg_write(WINDOW_HEIGHT, height);
+ } else {
+ if (!ret)
+ ret = reg_write(REDUCER_XZOOM_B, MT9M111_MAX_WIDTH);
+ if (!ret)
+ ret = reg_write(REDUCER_YZOOM_B, MT9M111_MAX_HEIGHT);
+ if (!ret)
+ ret = reg_write(REDUCER_XSIZE_B, width);
+ if (!ret)
+ ret = reg_write(REDUCER_YSIZE_B, height);
+ if (!ret)
+ ret = reg_write(REDUCER_XZOOM_A, MT9M111_MAX_WIDTH);
+ if (!ret)
+ ret = reg_write(REDUCER_YZOOM_A, MT9M111_MAX_HEIGHT);
+ if (!ret)
+ ret = reg_write(REDUCER_XSIZE_A, width);
+ if (!ret)
+ ret = reg_write(REDUCER_YSIZE_A, height);
+ }
+
+ return ret;
+}
+
+static int mt9m111_setup_pixfmt(struct soc_camera_device *icd, u16 outfmt)
+{
+ int ret;
+
+ ret = reg_write(OUTPUT_FORMAT_CTRL2_A, outfmt);
+ if (!ret)
+ ret = reg_write(OUTPUT_FORMAT_CTRL2_B, outfmt);
+ return ret;
+}
+
+static int mt9m111_setfmt_bayer8(struct soc_camera_device *icd)
+{
+ return mt9m111_setup_pixfmt(icd, MT9M111_OUTFMT_PROCESSED_BAYER);
+}
+
+static int mt9m111_setfmt_bayer10(struct soc_camera_device *icd)
+{
+ return mt9m111_setup_pixfmt(icd, MT9M111_OUTFMT_BYPASS_IFP);
+}
+
+static int mt9m111_setfmt_rgb565(struct soc_camera_device *icd)
+{
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ int val = 0;
+
+ if (mt9m111->swap_rgb_red_blue)
+ val |= MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr;
+ if (mt9m111->swap_rgb_even_odd)
+ val |= MT9M111_OUTFMT_SWAP_RGB_EVEN;
+ val |= MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565;
+
+ return mt9m111_setup_pixfmt(icd, val);
+}
+
+static int mt9m111_setfmt_rgb555(struct soc_camera_device *icd)
+{
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ int val = 0;
+
+ if (mt9m111->swap_rgb_red_blue)
+ val |= MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr;
+ if (mt9m111->swap_rgb_even_odd)
+ val |= MT9M111_OUTFMT_SWAP_RGB_EVEN;
+ val |= MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555;
+
+ return mt9m111_setup_pixfmt(icd, val);
+}
+
+static int mt9m111_setfmt_yuv(struct soc_camera_device *icd)
+{
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ int val = 0;
+
+ if (mt9m111->swap_yuv_cb_cr)
+ val |= MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr;
+ if (mt9m111->swap_yuv_y_chromas)
+ val |= MT9M111_OUTFMT_SWAP_YCbCr_C_Y;
+
+ return mt9m111_setup_pixfmt(icd, val);
+}
+
+static int mt9m111_enable(struct soc_camera_device *icd)
+{
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ struct soc_camera_link *icl = mt9m111->client->dev.platform_data;
+ int ret;
+
+ if (icl->power) {
+ ret = icl->power(&mt9m111->client->dev, 1);
+ if (ret < 0) {
+ dev_err(icd->vdev->parent,
+ "Platform failed to power-on the camera.\n");
+ return ret;
+ }
+ }
+
+ ret = reg_set(RESET, MT9M111_RESET_CHIP_ENABLE);
+ if (!ret)
+ mt9m111->powered = 1;
+ return ret;
+}
+
+static int mt9m111_disable(struct soc_camera_device *icd)
+{
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ struct soc_camera_link *icl = mt9m111->client->dev.platform_data;
+ int ret;
+
+ ret = reg_clear(RESET, MT9M111_RESET_CHIP_ENABLE);
+ if (!ret)
+ mt9m111->powered = 0;
+
+ if (icl->power)
+ icl->power(&mt9m111->client->dev, 0);
+
+ return ret;
+}
+
+static int mt9m111_reset(struct soc_camera_device *icd)
+{
+ int ret;
+
+ ret = reg_set(RESET, MT9M111_RESET_RESET_MODE);
+ if (!ret)
+ ret = reg_set(RESET, MT9M111_RESET_RESET_SOC);
+ if (!ret)
+ ret = reg_clear(RESET, MT9M111_RESET_RESET_MODE
+ | MT9M111_RESET_RESET_SOC);
+ return ret;
+}
+
+static int mt9m111_start_capture(struct soc_camera_device *icd)
+{
+ return 0;
+}
+
+static int mt9m111_stop_capture(struct soc_camera_device *icd)
+{
+ return 0;
+}
+
+static unsigned long mt9m111_query_bus_param(struct soc_camera_device *icd)
+{
+ return SOCAM_MASTER | SOCAM_PCLK_SAMPLE_RISING |
+ SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
+ SOCAM_DATAWIDTH_8;
+}
+
+static int mt9m111_set_bus_param(struct soc_camera_device *icd, unsigned long f)
+{
+ return 0;
+}
+
+static int mt9m111_set_pixfmt(struct soc_camera_device *icd, u32 pixfmt)
+{
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ int ret;
+
+ switch (pixfmt) {
+ case V4L2_PIX_FMT_SBGGR8:
+ ret = mt9m111_setfmt_bayer8(icd);
+ break;
+ case V4L2_PIX_FMT_SBGGR16:
+ ret = mt9m111_setfmt_bayer10(icd);
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ ret = mt9m111_setfmt_rgb555(icd);
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ ret = mt9m111_setfmt_rgb565(icd);
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ ret = mt9m111_setfmt_yuv(icd);
+ break;
+ default:
+ dev_err(&icd->dev, "Pixel format not handled : %x\n", pixfmt);
+ ret = -EINVAL;
+ }
+
+ if (!ret)
+ mt9m111->pixfmt = pixfmt;
+
+ return ret;
+}
+
+static int mt9m111_set_fmt_cap(struct soc_camera_device *icd,
+ __u32 pixfmt, struct v4l2_rect *rect)
+{
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ int ret;
+
+ mt9m111->left = rect->left;
+ mt9m111->top = rect->top;
+ mt9m111->width = rect->width;
+ mt9m111->height = rect->height;
+
+ dev_dbg(&icd->dev, "%s fmt=%x left=%d, top=%d, width=%d, height=%d\n",
+ __func__, pixfmt, mt9m111->left, mt9m111->top, mt9m111->width,
+ mt9m111->height);
+
+ ret = mt9m111_setup_rect(icd);
+ if (!ret)
+ ret = mt9m111_set_pixfmt(icd, pixfmt);
+ return ret;
+}
+
+static int mt9m111_try_fmt_cap(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ if (f->fmt.pix.height > MT9M111_MAX_HEIGHT)
+ f->fmt.pix.height = MT9M111_MAX_HEIGHT;
+ if (f->fmt.pix.width > MT9M111_MAX_WIDTH)
+ f->fmt.pix.width = MT9M111_MAX_WIDTH;
+
+ return 0;
+}
+
+static int mt9m111_get_chip_id(struct soc_camera_device *icd,
+ struct v4l2_chip_ident *id)
+{
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+
+ if (id->match_type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+
+ if (id->match_chip != mt9m111->client->addr)
+ return -ENODEV;
+
+ id->ident = mt9m111->model;
+ id->revision = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int mt9m111_get_register(struct soc_camera_device *icd,
+ struct v4l2_register *reg)
+{
+ int val;
+
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+
+ if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff)
+ return -EINVAL;
+ if (reg->match_chip != mt9m111->client->addr)
+ return -ENODEV;
+
+ val = mt9m111_reg_read(icd, reg->reg);
+ reg->val = (u64)val;
+
+ if (reg->val > 0xffff)
+ return -EIO;
+
+ return 0;
+}
+
+static int mt9m111_set_register(struct soc_camera_device *icd,
+ struct v4l2_register *reg)
+{
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+
+ if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff)
+ return -EINVAL;
+
+ if (reg->match_chip != mt9m111->client->addr)
+ return -ENODEV;
+
+ if (mt9m111_reg_write(icd, reg->reg, reg->val) < 0)
+ return -EIO;
+
+ return 0;
+}
+#endif
+
+static const struct v4l2_queryctrl mt9m111_controls[] = {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Flip Verticaly",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ }, {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Flip Horizontaly",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ }, { /* gain = 1/32*val (=>gain=1 if val==32) */
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 63 * 2 * 2,
+ .step = 1,
+ .default_value = 32,
+ .flags = V4L2_CTRL_FLAG_SLIDER,
+ }, {
+ .id = V4L2_CID_EXPOSURE_AUTO,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto Exposure",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ }
+};
+
+static int mt9m111_video_probe(struct soc_camera_device *);
+static void mt9m111_video_remove(struct soc_camera_device *);
+static int mt9m111_get_control(struct soc_camera_device *,
+ struct v4l2_control *);
+static int mt9m111_set_control(struct soc_camera_device *,
+ struct v4l2_control *);
+static int mt9m111_resume(struct soc_camera_device *icd);
+static int mt9m111_init(struct soc_camera_device *icd);
+static int mt9m111_release(struct soc_camera_device *icd);
+
+static struct soc_camera_ops mt9m111_ops = {
+ .owner = THIS_MODULE,
+ .probe = mt9m111_video_probe,
+ .remove = mt9m111_video_remove,
+ .init = mt9m111_init,
+ .resume = mt9m111_resume,
+ .release = mt9m111_release,
+ .start_capture = mt9m111_start_capture,
+ .stop_capture = mt9m111_stop_capture,
+ .set_fmt_cap = mt9m111_set_fmt_cap,
+ .try_fmt_cap = mt9m111_try_fmt_cap,
+ .query_bus_param = mt9m111_query_bus_param,
+ .set_bus_param = mt9m111_set_bus_param,
+ .controls = mt9m111_controls,
+ .num_controls = ARRAY_SIZE(mt9m111_controls),
+ .get_control = mt9m111_get_control,
+ .set_control = mt9m111_set_control,
+ .get_chip_id = mt9m111_get_chip_id,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .get_register = mt9m111_get_register,
+ .set_register = mt9m111_set_register,
+#endif
+};
+
+static int mt9m111_set_flip(struct soc_camera_device *icd, int flip, int mask)
+{
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ int ret;
+
+ if (mt9m111->context == HIGHPOWER) {
+ if (flip)
+ ret = reg_set(READ_MODE_B, mask);
+ else
+ ret = reg_clear(READ_MODE_B, mask);
+ } else {
+ if (flip)
+ ret = reg_set(READ_MODE_A, mask);
+ else
+ ret = reg_clear(READ_MODE_A, mask);
+ }
+
+ return ret;
+}
+
+static int mt9m111_get_global_gain(struct soc_camera_device *icd)
+{
+ unsigned int data, gain;
+
+ data = reg_read(GLOBAL_GAIN);
+ if (data >= 0)
+ gain = ((data & (1 << 10)) * 2)
+ | ((data & (1 << 9)) * 2)
+ | (data & 0x2f);
+ else
+ gain = data;
+
+ return gain;
+}
+static int mt9m111_set_global_gain(struct soc_camera_device *icd, int gain)
+{
+ u16 val;
+
+ if (gain > 63 * 2 * 2)
+ return -EINVAL;
+
+ icd->gain = gain;
+ if ((gain >= 64 * 2) && (gain < 63 * 2 * 2))
+ val = (1 << 10) | (1 << 9) | (gain / 4);
+ else if ((gain >= 64) && (gain < 64 * 2))
+ val = (1 << 9) | (gain / 2);
+ else
+ val = gain;
+
+ return reg_write(GLOBAL_GAIN, val);
+}
+
+static int mt9m111_set_autoexposure(struct soc_camera_device *icd, int on)
+{
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ int ret;
+
+ if (on)
+ ret = reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
+ else
+ ret = reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
+
+ if (!ret)
+ mt9m111->autoexposure = on;
+
+ return ret;
+}
+static int mt9m111_get_control(struct soc_camera_device *icd,
+ struct v4l2_control *ctrl)
+{
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ int data;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ if (mt9m111->context == HIGHPOWER)
+ data = reg_read(READ_MODE_B);
+ else
+ data = reg_read(READ_MODE_A);
+
+ if (data < 0)
+ return -EIO;
+ ctrl->value = !!(data & MT9M111_RMB_MIRROR_ROWS);
+ break;
+ case V4L2_CID_HFLIP:
+ if (mt9m111->context == HIGHPOWER)
+ data = reg_read(READ_MODE_B);
+ else
+ data = reg_read(READ_MODE_A);
+
+ if (data < 0)
+ return -EIO;
+ ctrl->value = !!(data & MT9M111_RMB_MIRROR_COLS);
+ break;
+ case V4L2_CID_GAIN:
+ data = mt9m111_get_global_gain(icd);
+ if (data < 0)
+ return data;
+ ctrl->value = data;
+ break;
+ case V4L2_CID_EXPOSURE_AUTO:
+ ctrl->value = mt9m111->autoexposure;
+ break;
+ }
+ return 0;
+}
+
+static int mt9m111_set_control(struct soc_camera_device *icd,
+ struct v4l2_control *ctrl)
+{
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ const struct v4l2_queryctrl *qctrl;
+ int ret;
+
+ qctrl = soc_camera_find_qctrl(&mt9m111_ops, ctrl->id);
+
+ if (!qctrl)
+ return -EINVAL;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ mt9m111->vflip = ctrl->value;
+ ret = mt9m111_set_flip(icd, ctrl->value,
+ MT9M111_RMB_MIRROR_ROWS);
+ break;
+ case V4L2_CID_HFLIP:
+ mt9m111->hflip = ctrl->value;
+ ret = mt9m111_set_flip(icd, ctrl->value,
+ MT9M111_RMB_MIRROR_COLS);
+ break;
+ case V4L2_CID_GAIN:
+ ret = mt9m111_set_global_gain(icd, ctrl->value);
+ break;
+ case V4L2_CID_EXPOSURE_AUTO:
+ ret = mt9m111_set_autoexposure(icd, ctrl->value);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int mt9m111_restore_state(struct soc_camera_device *icd)
+{
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+
+ mt9m111_set_context(icd, mt9m111->context);
+ mt9m111_set_pixfmt(icd, mt9m111->pixfmt);
+ mt9m111_setup_rect(icd);
+ mt9m111_set_flip(icd, mt9m111->hflip, MT9M111_RMB_MIRROR_COLS);
+ mt9m111_set_flip(icd, mt9m111->vflip, MT9M111_RMB_MIRROR_ROWS);
+ mt9m111_set_global_gain(icd, icd->gain);
+ mt9m111_set_autoexposure(icd, mt9m111->autoexposure);
+ return 0;
+}
+
+static int mt9m111_resume(struct soc_camera_device *icd)
+{
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ int ret = 0;
+
+ if (mt9m111->powered) {
+ ret = mt9m111_enable(icd);
+ if (!ret)
+ ret = mt9m111_reset(icd);
+ if (!ret)
+ ret = mt9m111_restore_state(icd);
+ }
+ return ret;
+}
+
+static int mt9m111_init(struct soc_camera_device *icd)
+{
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ int ret;
+
+ mt9m111->context = HIGHPOWER;
+ ret = mt9m111_enable(icd);
+ if (!ret)
+ ret = mt9m111_reset(icd);
+ if (!ret)
+ ret = mt9m111_set_context(icd, mt9m111->context);
+ if (!ret)
+ ret = mt9m111_set_autoexposure(icd, mt9m111->autoexposure);
+ if (ret)
+ dev_err(&icd->dev, "mt9m111 init failed: %d\n", ret);
+ return ret;
+}
+
+static int mt9m111_release(struct soc_camera_device *icd)
+{
+ int ret;
+
+ ret = mt9m111_disable(icd);
+ if (ret < 0)
+ dev_err(&icd->dev, "mt9m111 release failed: %d\n", ret);
+
+ return ret;
+}
+
+/*
+ * Interface active, can use i2c. If it fails, it can indeed mean, that
+ * this wasn't our capture interface, so, we wait for the right one
+ */
+static int mt9m111_video_probe(struct soc_camera_device *icd)
+{
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ s32 data;
+ int ret;
+
+ /*
+ * We must have a parent by now. And it cannot be a wrong one.
+ * So this entire test is completely redundant.
+ */
+ if (!icd->dev.parent ||
+ to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
+ return -ENODEV;
+
+ ret = mt9m111_enable(icd);
+ if (ret)
+ goto ei2c;
+ ret = mt9m111_reset(icd);
+ if (ret)
+ goto ei2c;
+
+ data = reg_read(CHIP_VERSION);
+
+ switch (data) {
+ case 0x143a:
+ mt9m111->model = V4L2_IDENT_MT9M111;
+ icd->formats = mt9m111_colour_formats;
+ icd->num_formats = ARRAY_SIZE(mt9m111_colour_formats);
+ break;
+ default:
+ ret = -ENODEV;
+ dev_err(&icd->dev,
+ "No MT9M111 chip detected, register read %x\n", data);
+ goto ei2c;
+ }
+
+ dev_info(&icd->dev, "Detected a MT9M111 chip ID 0x143a\n");
+
+ ret = soc_camera_video_start(icd);
+ if (ret)
+ goto eisis;
+
+ mt9m111->autoexposure = 1;
+
+ mt9m111->swap_rgb_even_odd = 1;
+ mt9m111->swap_rgb_red_blue = 1;
+
+ return 0;
+eisis:
+ei2c:
+ return ret;
+}
+
+static void mt9m111_video_remove(struct soc_camera_device *icd)
+{
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+
+ dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9m111->client->addr,
+ mt9m111->icd.dev.parent, mt9m111->icd.vdev);
+ soc_camera_video_stop(&mt9m111->icd);
+}
+
+static int mt9m111_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct mt9m111 *mt9m111;
+ struct soc_camera_device *icd;
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct soc_camera_link *icl = client->dev.platform_data;
+ int ret;
+
+ if (!icl) {
+ dev_err(&client->dev, "MT9M111 driver needs platform data\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_warn(&adapter->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
+ return -EIO;
+ }
+
+ mt9m111 = kzalloc(sizeof(struct mt9m111), GFP_KERNEL);
+ if (!mt9m111)
+ return -ENOMEM;
+
+ mt9m111->client = client;
+ i2c_set_clientdata(client, mt9m111);
+
+ /* Second stage probe - when a capture adapter is there */
+ icd = &mt9m111->icd;
+ icd->ops = &mt9m111_ops;
+ icd->control = &client->dev;
+ icd->x_min = MT9M111_MIN_DARK_COLS;
+ icd->y_min = MT9M111_MIN_DARK_ROWS;
+ icd->x_current = icd->x_min;
+ icd->y_current = icd->y_min;
+ icd->width_min = MT9M111_MIN_DARK_ROWS;
+ icd->width_max = MT9M111_MAX_WIDTH;
+ icd->height_min = MT9M111_MIN_DARK_COLS;
+ icd->height_max = MT9M111_MAX_HEIGHT;
+ icd->y_skip_top = 0;
+ icd->iface = icl->bus_id;
+
+ ret = soc_camera_device_register(icd);
+ if (ret)
+ goto eisdr;
+ return 0;
+
+eisdr:
+ kfree(mt9m111);
+ return ret;
+}
+
+static int mt9m111_remove(struct i2c_client *client)
+{
+ struct mt9m111 *mt9m111 = i2c_get_clientdata(client);
+ soc_camera_device_unregister(&mt9m111->icd);
+ kfree(mt9m111);
+
+ return 0;
+}
+
+static const struct i2c_device_id mt9m111_id[] = {
+ { "mt9m111", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mt9m111_id);
+
+static struct i2c_driver mt9m111_i2c_driver = {
+ .driver = {
+ .name = "mt9m111",
+ },
+ .probe = mt9m111_probe,
+ .remove = mt9m111_remove,
+ .id_table = mt9m111_id,
+};
+
+static int __init mt9m111_mod_init(void)
+{
+ return i2c_add_driver(&mt9m111_i2c_driver);
+}
+
+static void __exit mt9m111_mod_exit(void)
+{
+ i2c_del_driver(&mt9m111_i2c_driver);
+}
+
+module_init(mt9m111_mod_init);
+module_exit(mt9m111_mod_exit);
+
+MODULE_DESCRIPTION("Micron MT9M111 Camera driver");
+MODULE_AUTHOR("Robert Jarzmik");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c
index 56808cd..2584201 100644
--- a/drivers/media/video/mt9v022.c
+++ b/drivers/media/video/mt9v022.c
@@ -134,34 +134,56 @@
static int mt9v022_init(struct soc_camera_device *icd)
{
struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
+ struct soc_camera_link *icl = mt9v022->client->dev.platform_data;
int ret;
+ if (icl->power) {
+ ret = icl->power(&mt9v022->client->dev, 1);
+ if (ret < 0) {
+ dev_err(icd->vdev->parent,
+ "Platform failed to power-on the camera.\n");
+ return ret;
+ }
+ }
+
+ /*
+ * The camera could have been already on, we hard-reset it additionally,
+ * if available. Soft reset is done in video_probe().
+ */
+ if (icl->reset)
+ icl->reset(&mt9v022->client->dev);
+
/* Almost the default mode: master, parallel, simultaneous, and an
* undocumented bit 0x200, which is present in table 7, but not in 8,
* plus snapshot mode to disable scan for now */
mt9v022->chip_control |= 0x10;
ret = reg_write(icd, MT9V022_CHIP_CONTROL, mt9v022->chip_control);
- if (ret >= 0)
- reg_write(icd, MT9V022_READ_MODE, 0x300);
+ if (!ret)
+ ret = reg_write(icd, MT9V022_READ_MODE, 0x300);
/* All defaults */
- if (ret >= 0)
+ if (!ret)
/* AEC, AGC on */
ret = reg_set(icd, MT9V022_AEC_AGC_ENABLE, 0x3);
- if (ret >= 0)
+ if (!ret)
ret = reg_write(icd, MT9V022_MAX_TOTAL_SHUTTER_WIDTH, 480);
- if (ret >= 0)
+ if (!ret)
/* default - auto */
ret = reg_clear(icd, MT9V022_BLACK_LEVEL_CALIB_CTRL, 1);
- if (ret >= 0)
+ if (!ret)
ret = reg_write(icd, MT9V022_DIGITAL_TEST_PATTERN, 0);
- return ret >= 0 ? 0 : -EIO;
+ return ret;
}
static int mt9v022_release(struct soc_camera_device *icd)
{
- /* Nothing? */
+ struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
+ struct soc_camera_link *icl = mt9v022->client->dev.platform_data;
+
+ if (icl->power)
+ icl->power(&mt9v022->client->dev, 0);
+
return 0;
}
@@ -352,21 +374,21 @@
rect->height + icd->y_skip_top + 43);
}
/* Setup frame format: defaults apart from width and height */
- if (ret >= 0)
+ if (!ret)
ret = reg_write(icd, MT9V022_COLUMN_START, rect->left);
- if (ret >= 0)
+ if (!ret)
ret = reg_write(icd, MT9V022_ROW_START, rect->top);
- if (ret >= 0)
+ if (!ret)
/* Default 94, Phytec driver says:
* "width + horizontal blank >= 660" */
ret = reg_write(icd, MT9V022_HORIZONTAL_BLANKING,
rect->width > 660 - 43 ? 43 :
660 - rect->width);
- if (ret >= 0)
+ if (!ret)
ret = reg_write(icd, MT9V022_VERTICAL_BLANKING, 45);
- if (ret >= 0)
+ if (!ret)
ret = reg_write(icd, MT9V022_WINDOW_WIDTH, rect->width);
- if (ret >= 0)
+ if (!ret)
ret = reg_write(icd, MT9V022_WINDOW_HEIGHT,
rect->height + icd->y_skip_top);
@@ -717,7 +739,7 @@
icd->num_formats = 1;
}
- if (ret >= 0)
+ if (!ret)
ret = soc_camera_video_start(icd);
if (ret < 0)
goto eisis;
diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c
index 8ef578c..7f13028 100644
--- a/drivers/media/video/mxb.c
+++ b/drivers/media/video/mxb.c
@@ -27,6 +27,7 @@
#include <media/tuner.h>
#include <linux/video_decoder.h>
#include <media/v4l2-common.h>
+#include <media/saa7115.h>
#include "mxb.h"
#include "tea6415c.h"
@@ -122,6 +123,8 @@
{ VIDIOC_S_FREQUENCY, SAA7146_EXCLUSIVE },
{ VIDIOC_G_AUDIO, SAA7146_EXCLUSIVE },
{ VIDIOC_S_AUDIO, SAA7146_EXCLUSIVE },
+ { VIDIOC_DBG_G_REGISTER, SAA7146_EXCLUSIVE },
+ { VIDIOC_DBG_S_REGISTER, SAA7146_EXCLUSIVE },
{ MXB_S_AUDIO_CD, SAA7146_EXCLUSIVE }, /* custom control */
{ MXB_S_AUDIO_LINE, SAA7146_EXCLUSIVE }, /* custom control */
{ 0, 0 }
@@ -134,12 +137,12 @@
struct i2c_adapter i2c_adapter;
- struct i2c_client* saa7111a;
- struct i2c_client* tda9840;
- struct i2c_client* tea6415c;
- struct i2c_client* tuner;
- struct i2c_client* tea6420_1;
- struct i2c_client* tea6420_2;
+ struct i2c_client *saa7111a;
+ struct i2c_client *tda9840;
+ struct i2c_client *tea6415c;
+ struct i2c_client *tuner;
+ struct i2c_client *tea6420_1;
+ struct i2c_client *tea6420_2;
int cur_mode; /* current audio mode (mono, stereo, ...) */
int cur_input; /* current input */
@@ -151,23 +154,23 @@
static int mxb_check_clients(struct device *dev, void *data)
{
- struct mxb* mxb = data;
+ struct mxb *mxb = data;
struct i2c_client *client = i2c_verify_client(dev);
- if( !client )
+ if (!client)
return 0;
- if( I2C_ADDR_TEA6420_1 == client->addr )
+ if (I2C_ADDR_TEA6420_1 == client->addr)
mxb->tea6420_1 = client;
- if( I2C_ADDR_TEA6420_2 == client->addr )
+ if (I2C_ADDR_TEA6420_2 == client->addr)
mxb->tea6420_2 = client;
- if( I2C_TEA6415C_2 == client->addr )
+ if (I2C_TEA6415C_2 == client->addr)
mxb->tea6415c = client;
- if( I2C_ADDR_TDA9840 == client->addr )
+ if (I2C_ADDR_TDA9840 == client->addr)
mxb->tda9840 = client;
- if( I2C_SAA7111 == client->addr )
+ if (I2C_SAA7111 == client->addr)
mxb->saa7111a = client;
- if( 0x60 == client->addr )
+ if (0x60 == client->addr)
mxb->tuner = client;
return 0;
@@ -178,23 +181,28 @@
struct mxb* mxb = NULL;
int result;
- if ((result = request_module("saa7111")) < 0) {
+ result = request_module("saa7115");
+ if (result < 0) {
printk("mxb: saa7111 i2c module not available.\n");
return -ENODEV;
}
- if ((result = request_module("tea6420")) < 0) {
+ result = request_module("tea6420");
+ if (result < 0) {
printk("mxb: tea6420 i2c module not available.\n");
return -ENODEV;
}
- if ((result = request_module("tea6415c")) < 0) {
+ result = request_module("tea6415c");
+ if (result < 0) {
printk("mxb: tea6415c i2c module not available.\n");
return -ENODEV;
}
- if ((result = request_module("tda9840")) < 0) {
+ result = request_module("tda9840");
+ if (result < 0) {
printk("mxb: tda9840 i2c module not available.\n");
return -ENODEV;
}
- if ((result = request_module("tuner")) < 0) {
+ result = request_module("tuner");
+ if (result < 0) {
printk("mxb: tuner i2c module not available.\n");
return -ENODEV;
}
@@ -207,9 +215,10 @@
mxb->i2c_adapter = (struct i2c_adapter) {
.class = I2C_CLASS_TV_ANALOG,
- .name = "mxb",
};
+ snprintf(mxb->i2c_adapter.name, sizeof(mxb->i2c_adapter.name), "mxb%d", mxb_num);
+
saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
if(i2c_add_adapter(&mxb->i2c_adapter) < 0) {
DEB_S(("cannot register i2c-device. skipping.\n"));
@@ -290,38 +299,7 @@
{ 9, { 0x1d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } },
{ 9, { 0x3d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } },
{ 3, { 0x80, 0xb3, 0x0a } },
- {-1, { 0} }
-};
-
-static const unsigned char mxb_saa7111_init[] = {
- 0x00, 0x00, /* 00 - ID byte */
- 0x01, 0x00, /* 01 - reserved */
-
- /*front end */
- 0x02, 0xd8, /* 02 - FUSE=x, GUDL=x, MODE=x */
- 0x03, 0x23, /* 03 - HLNRS=0, VBSL=1, WPOFF=0, HOLDG=0, GAFIX=0, GAI1=256, GAI2=256 */
- 0x04, 0x00, /* 04 - GAI1=256 */
- 0x05, 0x00, /* 05 - GAI2=256 */
-
- /* decoder */
- 0x06, 0xf0, /* 06 - HSB at xx(50Hz) / xx(60Hz) pixels after end of last line */
- 0x07, 0x30, /* 07 - HSS at xx(50Hz) / xx(60Hz) pixels after end of last line */
- 0x08, 0xa8, /* 08 - AUFD=x, FSEL=x, EXFIL=x, VTRC=x, HPLL=x, VNOI=x */
- 0x09, 0x02, /* 09 - BYPS=x, PREF=x, BPSS=x, VBLB=x, UPTCV=x, APER=x */
- 0x0a, 0x80, /* 0a - BRIG=128 */
- 0x0b, 0x47, /* 0b - CONT=1.109 */
- 0x0c, 0x40, /* 0c - SATN=1.0 */
- 0x0d, 0x00, /* 0d - HUE=0 */
- 0x0e, 0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0, FCTC=0, CHBW=1 */
- 0x0f, 0x00, /* 0f - reserved */
- 0x10, 0xd0, /* 10 - OFTS=x, HDEL=x, VRLN=x, YDEL=x */
- 0x11, 0x8c, /* 11 - GPSW=x, CM99=x, FECO=x, COMPO=x, OEYC=1, OEHV=1, VIPB=0, COLO=0 */
- 0x12, 0x80, /* 12 - xx output control 2 */
- 0x13, 0x30, /* 13 - xx output control 3 */
- 0x14, 0x00, /* 14 - reserved */
- 0x15, 0x15, /* 15 - VBI */
- 0x16, 0x04, /* 16 - VBI */
- 0x17, 0x00, /* 17 - VBI */
+ {-1, { 0 } }
};
/* bring hardware to a sane state. this has to be done, just in case someone
@@ -331,37 +309,28 @@
static int mxb_init_done(struct saa7146_dev* dev)
{
struct mxb* mxb = (struct mxb*)dev->ext_priv;
- struct video_decoder_init init;
struct i2c_msg msg;
struct tuner_setup tun_setup;
v4l2_std_id std = V4L2_STD_PAL_BG;
+ struct v4l2_routing route;
int i = 0, err = 0;
- struct tea6415c_multiplex vm;
+ struct tea6415c_multiplex vm;
/* select video mode in saa7111a */
- i = VIDEO_MODE_PAL;
- /* fixme: currently pointless: gets overwritten by configuration below */
- mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_NORM, &i);
-
- /* write configuration to saa7111a */
- init.data = mxb_saa7111_init;
- init.len = sizeof(mxb_saa7111_init);
- mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_INIT, &init);
+ mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_S_STD, &std);
/* select tuner-output on saa7111a */
i = 0;
- mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_INPUT, &i);
-
- /* enable vbi bypass */
- i = 1;
- mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_VBI_BYPASS, &i);
+ route.input = SAA7115_COMPOSITE0;
+ route.output = SAA7111_FMT_CCIR | SAA7111_VBI_BYPASS;
+ mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_VIDEO_ROUTING, &route);
/* select a tuner type */
tun_setup.mode_mask = T_ANALOG_TV;
tun_setup.addr = ADDR_UNSET;
tun_setup.type = TUNER_PHILIPS_PAL;
- mxb->tuner->driver->command(mxb->tuner,TUNER_SET_TYPE_ADDR, &tun_setup);
+ mxb->tuner->driver->command(mxb->tuner, TUNER_SET_TYPE_ADDR, &tun_setup);
/* tune in some frequency on tuner */
mxb->cur_freq.tuner = 0;
mxb->cur_freq.type = V4L2_TUNER_ANALOG_TV;
@@ -373,27 +342,26 @@
mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_STD, &std);
/* mute audio on tea6420s */
- mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[6][0]);
- mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[6][1]);
- mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_cd[6][0]);
- mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_cd[6][1]);
+ mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, &TEA6420_line[6][0]);
+ mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, &TEA6420_line[6][1]);
+ mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, &TEA6420_cd[6][0]);
+ mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, &TEA6420_cd[6][1]);
/* switch to tuner-channel on tea6415c*/
vm.out = 17;
vm.in = 3;
- mxb->tea6415c->driver->command(mxb->tea6415c,TEA6415C_SWITCH, &vm);
+ mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm);
/* select tuner-output on multicable on tea6415c*/
vm.in = 3;
vm.out = 13;
- mxb->tea6415c->driver->command(mxb->tea6415c,TEA6415C_SWITCH, &vm);
+ mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm);
/* the rest for mxb */
mxb->cur_input = 0;
mxb->cur_mute = 1;
mxb->cur_mode = V4L2_TUNER_MODE_STEREO;
- mxb->tda9840->driver->command(mxb->tda9840, TDA9840_SWITCH, &mxb->cur_mode);
/* check if the saa7740 (aka 'sound arena module') is present
on the mxb. if so, we must initialize it. due to lack of
@@ -404,21 +372,22 @@
msg.len = mxb_saa7740_init[0].length;
msg.buf = &mxb_saa7740_init[0].data[0];
- if( 1 == (err = i2c_transfer(&mxb->i2c_adapter, &msg, 1))) {
+ err = i2c_transfer(&mxb->i2c_adapter, &msg, 1);
+ if (err == 1) {
/* the sound arena module is a pos, that's probably the reason
philips refuses to hand out a datasheet for the saa7740...
it seems to screw up the i2c bus, so we disable fast irq
based i2c transactions here and rely on the slow and safe
polling method ... */
extension.flags &= ~SAA7146_USE_I2C_IRQ;
- for(i = 1;;i++) {
- if( -1 == mxb_saa7740_init[i].length ) {
+ for (i = 1; ; i++) {
+ if (-1 == mxb_saa7740_init[i].length)
break;
- }
msg.len = mxb_saa7740_init[i].length;
msg.buf = &mxb_saa7740_init[i].data[0];
- if( 1 != (err = i2c_transfer(&mxb->i2c_adapter, &msg, 1))) {
+ err = i2c_transfer(&mxb->i2c_adapter, &msg, 1);
+ if (err != 1) {
DEB_D(("failed to initialize 'sound arena module'.\n"));
goto err;
}
@@ -432,7 +401,8 @@
/* ext->saa has been filled by the core driver */
/* some stuff is done via variables */
- saa7146_set_hps_source_and_sync(dev, input_port_selection[mxb->cur_input].hps_source, input_port_selection[mxb->cur_input].hps_sync);
+ saa7146_set_hps_source_and_sync(dev, input_port_selection[mxb->cur_input].hps_source,
+ input_port_selection[mxb->cur_input].hps_sync);
/* some stuff is done via direct write to the registers */
@@ -457,24 +427,24 @@
static struct saa7146_ext_vv vv_data;
/* this function only gets called when the probing was successful */
-static int mxb_attach(struct saa7146_dev* dev, struct saa7146_pci_extension_data *info)
+static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
{
- struct mxb* mxb = (struct mxb*)dev->ext_priv;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
- DEB_EE(("dev:%p\n",dev));
+ DEB_EE(("dev:%p\n", dev));
/* checking for i2c-devices can be omitted here, because we
already did this in "mxb_vl42_probe" */
- saa7146_vv_init(dev,&vv_data);
- if( 0 != saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) {
+ saa7146_vv_init(dev, &vv_data);
+ if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) {
ERR(("cannot register capture v4l2 device. skipping.\n"));
return -1;
}
/* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/
- if( 0 != MXB_BOARD_CAN_DO_VBI(dev)) {
- if( 0 != saa7146_register_device(&mxb->vbi_dev, dev, "mxb", VFL_TYPE_VBI)) {
+ if (MXB_BOARD_CAN_DO_VBI(dev)) {
+ if (saa7146_register_device(&mxb->vbi_dev, dev, "mxb", VFL_TYPE_VBI)) {
ERR(("cannot register vbi v4l2 device. skipping.\n"));
}
}
@@ -486,18 +456,18 @@
i2c_use_client(mxb->saa7111a);
i2c_use_client(mxb->tuner);
- printk("mxb: found 'Multimedia eXtension Board'-%d.\n",mxb_num);
+ printk("mxb: found Multimedia eXtension Board #%d.\n", mxb_num);
mxb_num++;
mxb_init_done(dev);
return 0;
}
-static int mxb_detach(struct saa7146_dev* dev)
+static int mxb_detach(struct saa7146_dev *dev)
{
- struct mxb* mxb = (struct mxb*)dev->ext_priv;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
- DEB_EE(("dev:%p\n",dev));
+ DEB_EE(("dev:%p\n", dev));
i2c_release_client(mxb->tea6420_1);
i2c_release_client(mxb->tea6420_2);
@@ -507,9 +477,8 @@
i2c_release_client(mxb->tuner);
saa7146_unregister_device(&mxb->video_dev,dev);
- if( 0 != MXB_BOARD_CAN_DO_VBI(dev)) {
- saa7146_unregister_device(&mxb->vbi_dev,dev);
- }
+ if (MXB_BOARD_CAN_DO_VBI(dev))
+ saa7146_unregister_device(&mxb->vbi_dev, dev);
saa7146_vv_release(dev);
mxb_num--;
@@ -523,7 +492,7 @@
static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg)
{
struct saa7146_dev *dev = fh->dev;
- struct mxb* mxb = (struct mxb*)dev->ext_priv;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
struct saa7146_vv *vv = dev->vv_data;
switch(cmd) {
@@ -532,11 +501,9 @@
struct v4l2_input *i = arg;
DEB_EE(("VIDIOC_ENUMINPUT %d.\n",i->index));
- if( i->index < 0 || i->index >= MXB_INPUTS) {
+ if (i->index < 0 || i->index >= MXB_INPUTS)
return -EINVAL;
- }
memcpy(i, &mxb_inputs[i->index], sizeof(struct v4l2_input));
-
return 0;
}
/* the saa7146 provides some controls (brightness, contrast, saturation)
@@ -550,7 +517,7 @@
for (i = MAXCONTROLS - 1; i >= 0; i--) {
if (mxb_controls[i].id == qc->id) {
*qc = mxb_controls[i];
- DEB_D(("VIDIOC_QUERYCTRL %d.\n",qc->id));
+ DEB_D(("VIDIOC_QUERYCTRL %d.\n", qc->id));
return 0;
}
}
@@ -562,56 +529,51 @@
int i;
for (i = MAXCONTROLS - 1; i >= 0; i--) {
- if (mxb_controls[i].id == vc->id) {
+ if (mxb_controls[i].id == vc->id)
break;
- }
}
- if( i < 0 ) {
+ if (i < 0)
return -EAGAIN;
+
+ if (vc->id == V4L2_CID_AUDIO_MUTE) {
+ vc->value = mxb->cur_mute;
+ DEB_D(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n", vc->value));
+ return 0;
}
- switch (vc->id ) {
- case V4L2_CID_AUDIO_MUTE: {
- vc->value = mxb->cur_mute;
- DEB_D(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n",vc->value));
- return 0;
- }
- }
-
- DEB_EE(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n",vc->value));
+ DEB_EE(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n", vc->value));
return 0;
}
case VIDIOC_S_CTRL:
{
- struct v4l2_control *vc = arg;
+ struct v4l2_control *vc = arg;
int i = 0;
for (i = MAXCONTROLS - 1; i >= 0; i--) {
- if (mxb_controls[i].id == vc->id) {
+ if (mxb_controls[i].id == vc->id)
break;
- }
}
- if( i < 0 ) {
+ if (i < 0)
return -EAGAIN;
- }
- switch (vc->id ) {
- case V4L2_CID_AUDIO_MUTE: {
- mxb->cur_mute = vc->value;
- if( 0 == vc->value ) {
- /* switch the audio-source */
- mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[video_audio_connect[mxb->cur_input]][0]);
- mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[video_audio_connect[mxb->cur_input]][1]);
- } else {
- mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[6][0]);
- mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[6][1]);
- }
- DEB_EE(("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d.\n",vc->value));
- break;
+ if (vc->id == V4L2_CID_AUDIO_MUTE) {
+ mxb->cur_mute = vc->value;
+ if (!vc->value) {
+ /* switch the audio-source */
+ mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH,
+ &TEA6420_line[video_audio_connect[mxb->cur_input]][0]);
+ mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH,
+ &TEA6420_line[video_audio_connect[mxb->cur_input]][1]);
+ } else {
+ mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH,
+ &TEA6420_line[6][0]);
+ mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH,
+ &TEA6420_line[6][1]);
}
+ DEB_EE(("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d.\n", vc->value));
}
return 0;
}
@@ -620,106 +582,84 @@
int *input = (int *)arg;
*input = mxb->cur_input;
- DEB_EE(("VIDIOC_G_INPUT %d.\n",*input));
+ DEB_EE(("VIDIOC_G_INPUT %d.\n", *input));
return 0;
}
case VIDIOC_S_INPUT:
{
int input = *(int *)arg;
- struct tea6415c_multiplex vm;
+ struct tea6415c_multiplex vm;
+ struct v4l2_routing route;
int i = 0;
- DEB_EE(("VIDIOC_S_INPUT %d.\n",input));
+ DEB_EE(("VIDIOC_S_INPUT %d.\n", input));
- if (input < 0 || input >= MXB_INPUTS) {
+ if (input < 0 || input >= MXB_INPUTS)
return -EINVAL;
- }
-
- /* fixme: locke das setzen des inputs mit hilfe des mutexes
- mutex_lock(&dev->lock);
- video_mux(dev,*i);
- mutex_unlock(&dev->lock);
- */
-
- /* fixme: check if streaming capture
- if ( 0 != dev->streaming ) {
- DEB_D(("VIDIOC_S_INPUT illegal while streaming.\n"));
- return -EPERM;
- }
- */
mxb->cur_input = input;
- saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source, input_port_selection[input].hps_sync);
+ saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source,
+ input_port_selection[input].hps_sync);
/* prepare switching of tea6415c and saa7111a;
have a look at the 'background'-file for further informations */
- switch( input ) {
+ switch (input) {
+ case TUNER:
+ i = SAA7115_COMPOSITE0;
+ vm.in = 3;
+ vm.out = 17;
- case TUNER:
- {
- i = 0;
- vm.in = 3;
- vm.out = 17;
-
- if ( 0 != mxb->tea6415c->driver->command(mxb->tea6415c,TEA6415C_SWITCH, &vm)) {
- printk("VIDIOC_S_INPUT: could not address tea6415c #1\n");
- return -EFAULT;
- }
- /* connect tuner-output always to multicable */
- vm.in = 3;
- vm.out = 13;
- break;
+ if (mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm)) {
+ printk(KERN_ERR "VIDIOC_S_INPUT: could not address tea6415c #1\n");
+ return -EFAULT;
}
- case AUX3_YC:
- {
- /* nothing to be done here. aux3_yc is
- directly connected to the saa711a */
- i = 5;
- break;
- }
- case AUX3:
- {
- /* nothing to be done here. aux3 is
- directly connected to the saa711a */
- i = 1;
- break;
- }
- case AUX1:
- {
- i = 0;
- vm.in = 1;
- vm.out = 17;
- break;
- }
+ /* connect tuner-output always to multicable */
+ vm.in = 3;
+ vm.out = 13;
+ break;
+ case AUX3_YC:
+ /* nothing to be done here. aux3_yc is
+ directly connected to the saa711a */
+ i = SAA7115_SVIDEO1;
+ break;
+ case AUX3:
+ /* nothing to be done here. aux3 is
+ directly connected to the saa711a */
+ i = SAA7115_COMPOSITE1;
+ break;
+ case AUX1:
+ i = SAA7115_COMPOSITE0;
+ vm.in = 1;
+ vm.out = 17;
+ break;
}
/* switch video in tea6415c only if necessary */
- switch( input ) {
- case TUNER:
- case AUX1:
- {
- if ( 0 != mxb->tea6415c->driver->command(mxb->tea6415c,TEA6415C_SWITCH, &vm)) {
- printk("VIDIOC_S_INPUT: could not address tea6415c #3\n");
- return -EFAULT;
- }
- break;
+ switch (input) {
+ case TUNER:
+ case AUX1:
+ if (mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm)) {
+ printk(KERN_ERR "VIDIOC_S_INPUT: could not address tea6415c #3\n");
+ return -EFAULT;
}
- default:
- {
- break;
- }
+ break;
+ default:
+ break;
}
/* switch video in saa7111a */
- if ( 0 != mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_INPUT, &i)) {
+ route.input = i;
+ route.output = 0;
+ if (mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_VIDEO_ROUTING, &route))
printk("VIDIOC_S_INPUT: could not address saa7111a #1.\n");
- }
/* switch the audio-source only if necessary */
if( 0 == mxb->cur_mute ) {
- mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[video_audio_connect[input]][0]);
- mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[video_audio_connect[input]][1]);
+ mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH,
+ &TEA6420_line[video_audio_connect[input]][0]);
+ mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH,
+ &TEA6420_line[video_audio_connect[input]][1]);
}
return 0;
@@ -727,114 +667,44 @@
case VIDIOC_G_TUNER:
{
struct v4l2_tuner *t = arg;
- int byte = 0;
- if( 0 != t->index ) {
+ if (t->index) {
DEB_D(("VIDIOC_G_TUNER: channel %d does not have a tuner attached.\n", t->index));
return -EINVAL;
}
DEB_EE(("VIDIOC_G_TUNER: %d\n", t->index));
- memset(t,0,sizeof(*t));
- strcpy(t->name, "Television");
+ memset(t, 0, sizeof(*t));
+ i2c_clients_command(&mxb->i2c_adapter, cmd, arg);
+ strlcpy(t->name, "TV Tuner", sizeof(t->name));
t->type = V4L2_TUNER_ANALOG_TV;
- t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
- t->rangelow = 772; /* 48.25 MHZ / 62.5 kHz = 772, see fi1216mk2-specs, page 2 */
- t->rangehigh = 13684; /* 855.25 MHz / 62.5 kHz = 13684 */
- /* FIXME: add the real signal strength here */
- t->signal = 0xffff;
- t->afc = 0;
-
- mxb->tda9840->driver->command(mxb->tda9840,TDA9840_DETECT, &byte);
+ t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | \
+ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
t->audmode = mxb->cur_mode;
-
- if( byte < 0 ) {
- t->rxsubchans = V4L2_TUNER_SUB_MONO;
- } else {
- switch(byte) {
- case TDA9840_MONO_DETECT: {
- t->rxsubchans = V4L2_TUNER_SUB_MONO;
- DEB_D(("VIDIOC_G_TUNER: V4L2_TUNER_MODE_MONO.\n"));
- break;
- }
- case TDA9840_DUAL_DETECT: {
- t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
- DEB_D(("VIDIOC_G_TUNER: V4L2_TUNER_MODE_LANG1.\n"));
- break;
- }
- case TDA9840_STEREO_DETECT: {
- t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO;
- DEB_D(("VIDIOC_G_TUNER: V4L2_TUNER_MODE_STEREO.\n"));
- break;
- }
- default: { /* TDA9840_INCORRECT_DETECT */
- t->rxsubchans = V4L2_TUNER_MODE_MONO;
- DEB_D(("VIDIOC_G_TUNER: TDA9840_INCORRECT_DETECT => V4L2_TUNER_MODE_MONO\n"));
- break;
- }
- }
- }
-
return 0;
}
case VIDIOC_S_TUNER:
{
struct v4l2_tuner *t = arg;
- int result = 0;
- int byte = 0;
- if( 0 != t->index ) {
+ if (t->index) {
DEB_D(("VIDIOC_S_TUNER: channel %d does not have a tuner attached.\n",t->index));
return -EINVAL;
}
- switch(t->audmode) {
- case V4L2_TUNER_MODE_STEREO: {
- mxb->cur_mode = V4L2_TUNER_MODE_STEREO;
- byte = TDA9840_SET_STEREO;
- DEB_D(("VIDIOC_S_TUNER: V4L2_TUNER_MODE_STEREO\n"));
- break;
- }
- case V4L2_TUNER_MODE_LANG1_LANG2: {
- mxb->cur_mode = V4L2_TUNER_MODE_LANG1_LANG2;
- byte = TDA9840_SET_BOTH;
- DEB_D(("VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1_LANG2\n"));
- break;
- }
- case V4L2_TUNER_MODE_LANG1: {
- mxb->cur_mode = V4L2_TUNER_MODE_LANG1;
- byte = TDA9840_SET_LANG1;
- DEB_D(("VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1\n"));
- break;
- }
- case V4L2_TUNER_MODE_LANG2: {
- mxb->cur_mode = V4L2_TUNER_MODE_LANG2;
- byte = TDA9840_SET_LANG2;
- DEB_D(("VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG2\n"));
- break;
- }
- default: { /* case V4L2_TUNER_MODE_MONO: {*/
- mxb->cur_mode = V4L2_TUNER_MODE_MONO;
- byte = TDA9840_SET_MONO;
- DEB_D(("VIDIOC_S_TUNER: TDA9840_SET_MONO\n"));
- break;
- }
- }
-
- if( 0 != (result = mxb->tda9840->driver->command(mxb->tda9840, TDA9840_SWITCH, &byte))) {
- printk("VIDIOC_S_TUNER error. result:%d, byte:%d\n",result,byte);
- }
-
+ mxb->cur_mode = t->audmode;
+ i2c_clients_command(&mxb->i2c_adapter, cmd, arg);
return 0;
}
case VIDIOC_G_FREQUENCY:
{
struct v4l2_frequency *f = arg;
- if(0 != mxb->cur_input) {
- DEB_D(("VIDIOC_G_FREQ: channel %d does not have a tuner!\n",mxb->cur_input));
+ if (mxb->cur_input) {
+ DEB_D(("VIDIOC_G_FREQ: channel %d does not have a tuner!\n",
+ mxb->cur_input));
return -EINVAL;
}
@@ -847,14 +717,14 @@
{
struct v4l2_frequency *f = arg;
- if (0 != f->tuner)
+ if (f->tuner)
return -EINVAL;
if (V4L2_TUNER_ANALOG_TV != f->type)
return -EINVAL;
- if(0 != mxb->cur_input) {
- DEB_D(("VIDIOC_S_FREQ: channel %d does not have a tuner!\n",mxb->cur_input));
+ if (mxb->cur_input) {
+ DEB_D(("VIDIOC_S_FREQ: channel %d does not have a tuner!\n", mxb->cur_input));
return -EINVAL;
}
@@ -875,7 +745,7 @@
{
int i = *(int*)arg;
- if( i < 0 || i >= MXB_AUDIOS ) {
+ if (i < 0 || i >= MXB_AUDIOS) {
DEB_D(("illegal argument to MXB_S_AUDIO_CD: i:%d.\n",i));
return -EINVAL;
}
@@ -891,7 +761,7 @@
{
int i = *(int*)arg;
- if( i < 0 || i >= MXB_AUDIOS ) {
+ if (i < 0 || i >= MXB_AUDIOS) {
DEB_D(("illegal argument to MXB_S_AUDIO_LINE: i:%d.\n",i));
return -EINVAL;
}
@@ -906,12 +776,12 @@
{
struct v4l2_audio *a = arg;
- if( a->index < 0 || a->index > MXB_INPUTS ) {
- DEB_D(("VIDIOC_G_AUDIO %d out of range.\n",a->index));
+ if (a->index < 0 || a->index > MXB_INPUTS) {
+ DEB_D(("VIDIOC_G_AUDIO %d out of range.\n", a->index));
return -EINVAL;
}
- DEB_EE(("VIDIOC_G_AUDIO %d.\n",a->index));
+ DEB_EE(("VIDIOC_G_AUDIO %d.\n", a->index));
memcpy(a, &mxb_audios[video_audio_connect[mxb->cur_input]], sizeof(struct v4l2_audio));
return 0;
@@ -919,9 +789,16 @@
case VIDIOC_S_AUDIO:
{
struct v4l2_audio *a = arg;
- DEB_D(("VIDIOC_S_AUDIO %d.\n",a->index));
+
+ DEB_D(("VIDIOC_S_AUDIO %d.\n", a->index));
return 0;
}
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ case VIDIOC_DBG_S_REGISTER:
+ case VIDIOC_DBG_G_REGISTER:
+ i2c_clients_command(&mxb->i2c_adapter, cmd, arg);
+ return 0;
+#endif
default:
/*
DEB2(printk("does not handle this ioctl.\n"));
@@ -944,7 +821,7 @@
/* set the 7146 gpio register -- I don't know what this does exactly */
saa7146_write(dev, GPIO_CTRL, 0x00404050);
/* unset the 7111 gpio register -- I don't know what this does exactly */
- mxb->saa7111a->driver->command(mxb->saa7111a, DECODER_SET_GPIO, &zero);
+ mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_GPIO, &zero);
mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_STD, &std);
} else {
v4l2_std_id std = V4L2_STD_PAL_BG;
@@ -953,7 +830,7 @@
/* set the 7146 gpio register -- I don't know what this does exactly */
saa7146_write(dev, GPIO_CTRL, 0x00404050);
/* set the 7111 gpio register -- I don't know what this does exactly */
- mxb->saa7111a->driver->command(mxb->saa7111a, DECODER_SET_GPIO, &one);
+ mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_GPIO, &one);
mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_STD, &std);
}
return 0;
@@ -1029,7 +906,7 @@
static int __init mxb_init_module(void)
{
- if( 0 != saa7146_register_extension(&extension)) {
+ if (saa7146_register_extension(&extension)) {
DEB_S(("failed to register extension.\n"));
return -ENODEV;
}
diff --git a/drivers/media/video/ov511.c b/drivers/media/video/ov511.c
index c685240..935d73d 100644
--- a/drivers/media/video/ov511.c
+++ b/drivers/media/video/ov511.c
@@ -974,14 +974,14 @@
for (i = reg1; i <= regn; i++) {
rc = i2c_r(ov, i);
- info("Sensor[0x%02X] = 0x%02X", i, rc);
+ dev_info(&ov->dev->dev, "Sensor[0x%02X] = 0x%02X\n", i, rc);
}
}
static void
dump_i2c_regs(struct usb_ov511 *ov)
{
- info("I2C REGS");
+ dev_info(&ov->dev->dev, "I2C REGS\n");
dump_i2c_range(ov, 0x00, 0x7C);
}
@@ -992,28 +992,28 @@
for (i = reg1; i <= regn; i++) {
rc = reg_r(ov, i);
- info("OV511[0x%02X] = 0x%02X", i, rc);
+ dev_info(&ov->dev->dev, "OV511[0x%02X] = 0x%02X\n", i, rc);
}
}
static void
ov511_dump_regs(struct usb_ov511 *ov)
{
- info("CAMERA INTERFACE REGS");
+ dev_info(&ov->dev->dev, "CAMERA INTERFACE REGS\n");
dump_reg_range(ov, 0x10, 0x1f);
- info("DRAM INTERFACE REGS");
+ dev_info(&ov->dev->dev, "DRAM INTERFACE REGS\n");
dump_reg_range(ov, 0x20, 0x23);
- info("ISO FIFO REGS");
+ dev_info(&ov->dev->dev, "ISO FIFO REGS\n");
dump_reg_range(ov, 0x30, 0x31);
- info("PIO REGS");
+ dev_info(&ov->dev->dev, "PIO REGS\n");
dump_reg_range(ov, 0x38, 0x39);
dump_reg_range(ov, 0x3e, 0x3e);
- info("I2C REGS");
+ dev_info(&ov->dev->dev, "I2C REGS\n");
dump_reg_range(ov, 0x40, 0x49);
- info("SYSTEM CONTROL REGS");
+ dev_info(&ov->dev->dev, "SYSTEM CONTROL REGS\n");
dump_reg_range(ov, 0x50, 0x55);
dump_reg_range(ov, 0x5e, 0x5f);
- info("OmniCE REGS");
+ dev_info(&ov->dev->dev, "OmniCE REGS\n");
dump_reg_range(ov, 0x70, 0x79);
/* NOTE: Quantization tables are not readable. You will get the value
* in reg. 0x79 for every table register */
@@ -1025,25 +1025,25 @@
static void
ov518_dump_regs(struct usb_ov511 *ov)
{
- info("VIDEO MODE REGS");
+ dev_info(&ov->dev->dev, "VIDEO MODE REGS\n");
dump_reg_range(ov, 0x20, 0x2f);
- info("DATA PUMP AND SNAPSHOT REGS");
+ dev_info(&ov->dev->dev, "DATA PUMP AND SNAPSHOT REGS\n");
dump_reg_range(ov, 0x30, 0x3f);
- info("I2C REGS");
+ dev_info(&ov->dev->dev, "I2C REGS\n");
dump_reg_range(ov, 0x40, 0x4f);
- info("SYSTEM CONTROL AND VENDOR REGS");
+ dev_info(&ov->dev->dev, "SYSTEM CONTROL AND VENDOR REGS\n");
dump_reg_range(ov, 0x50, 0x5f);
- info("60 - 6F");
+ dev_info(&ov->dev->dev, "60 - 6F\n");
dump_reg_range(ov, 0x60, 0x6f);
- info("70 - 7F");
+ dev_info(&ov->dev->dev, "70 - 7F\n");
dump_reg_range(ov, 0x70, 0x7f);
- info("Y QUANTIZATION TABLE");
+ dev_info(&ov->dev->dev, "Y QUANTIZATION TABLE\n");
dump_reg_range(ov, 0x80, 0x8f);
- info("UV QUANTIZATION TABLE");
+ dev_info(&ov->dev->dev, "UV QUANTIZATION TABLE\n");
dump_reg_range(ov, 0x90, 0x9f);
- info("A0 - BF");
+ dev_info(&ov->dev->dev, "A0 - BF\n");
dump_reg_range(ov, 0xa0, 0xbf);
- info("CBR");
+ dev_info(&ov->dev->dev, "CBR\n");
dump_reg_range(ov, 0xc0, 0xcf);
}
#endif
@@ -3205,9 +3205,10 @@
*/
if (printph) {
- info("ph(%3d): %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x",
- pnum, in[0], in[1], in[2], in[3], in[4], in[5], in[6],
- in[7], in[8], in[9], in[10], in[11]);
+ dev_info(&ov->dev->dev,
+ "ph(%3d): %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x\n",
+ pnum, in[0], in[1], in[2], in[3], in[4], in[5], in[6],
+ in[7], in[8], in[9], in[10], in[11]);
}
/* Check for SOF/EOF packet */
@@ -3366,8 +3367,10 @@
* the definitive SOF/EOF format */
if ((!(in[0] | in[1] | in[2] | in[3] | in[5])) && in[6]) {
if (printph) {
- info("ph: %2x %2x %2x %2x %2x %2x %2x %2x", in[0],
- in[1], in[2], in[3], in[4], in[5], in[6], in[7]);
+ dev_info(&ov->dev->dev,
+ "ph: %2x %2x %2x %2x %2x %2x %2x %2x\n",
+ in[0], in[1], in[2], in[3], in[4], in[5],
+ in[6], in[7]);
}
if (frame->scanstate == STATE_LINES) {
@@ -3646,14 +3649,16 @@
if (packetsize == -1) {
ov518_set_packet_size(ov, 640);
} else {
- info("Forcing packet size to %d", packetsize);
+ dev_info(&ov->dev->dev, "Forcing packet size to %d\n",
+ packetsize);
ov518_set_packet_size(ov, packetsize);
}
} else {
if (packetsize == -1) {
ov511_set_packet_size(ov, size);
} else {
- info("Forcing packet size to %d", packetsize);
+ dev_info(&ov->dev->dev, "Forcing packet size to %d\n",
+ packetsize);
ov511_set_packet_size(ov, packetsize);
}
}
@@ -4121,7 +4126,7 @@
return -EIO;
if (force_palette && p->palette != force_palette) {
- info("Palette rejected (%s)",
+ dev_info(&ov->dev->dev, "Palette rejected (%s)\n",
symbolic(v4l1_plist, p->palette));
return -EINVAL;
}
@@ -4849,26 +4854,27 @@
err("Error detecting sensor type");
return -1;
} else if ((rc & 3) == 3) {
- info("Sensor is an OV7610");
+ dev_info(&ov->dev->dev, "Sensor is an OV7610\n");
ov->sensor = SEN_OV7610;
} else if ((rc & 3) == 1) {
/* I don't know what's different about the 76BE yet. */
if (i2c_r(ov, 0x15) & 1)
- info("Sensor is an OV7620AE");
+ dev_info(&ov->dev->dev, "Sensor is an OV7620AE\n");
else
- info("Sensor is an OV76BE");
+ dev_info(&ov->dev->dev, "Sensor is an OV76BE\n");
/* OV511+ will return all zero isoc data unless we
* configure the sensor as a 7620. Someone needs to
* find the exact reg. setting that causes this. */
if (ov->bridge == BRG_OV511PLUS) {
- info("Enabling 511+/7620AE workaround");
+ dev_info(&ov->dev->dev,
+ "Enabling 511+/7620AE workaround\n");
ov->sensor = SEN_OV7620;
} else {
ov->sensor = SEN_OV76BE;
}
} else if ((rc & 3) == 0) {
- info("Sensor is an OV7620");
+ dev_info(&ov->dev->dev, "Sensor is an OV7620\n");
ov->sensor = SEN_OV7620;
} else {
err("Unknown image sensor version: %d", rc & 3);
@@ -5024,16 +5030,16 @@
if ((rc & 3) == 0) {
ov->sensor = SEN_OV6630;
- info("Sensor is an OV6630");
+ dev_info(&ov->dev->dev, "Sensor is an OV6630\n");
} else if ((rc & 3) == 1) {
ov->sensor = SEN_OV6620;
- info("Sensor is an OV6620");
+ dev_info(&ov->dev->dev, "Sensor is an OV6620\n");
} else if ((rc & 3) == 2) {
ov->sensor = SEN_OV6630;
- info("Sensor is an OV6630AE");
+ dev_info(&ov->dev->dev, "Sensor is an OV6630AE\n");
} else if ((rc & 3) == 3) {
ov->sensor = SEN_OV6630;
- info("Sensor is an OV6630AF");
+ dev_info(&ov->dev->dev, "Sensor is an OV6630AF\n");
}
/* Set sensor-specific vars */
@@ -5088,10 +5094,10 @@
err("Error detecting sensor type");
return -1;
} else if ((rc & 0x0f) == 0) {
- info("Sensor is a KS0127");
+ dev_info(&ov->dev->dev, "Sensor is a KS0127\n");
ov->sensor = SEN_KS0127;
} else if ((rc & 0x0f) == 9) {
- info("Sensor is a KS0127B Rev. A");
+ dev_info(&ov->dev->dev, "Sensor is a KS0127B Rev. A\n");
ov->sensor = SEN_KS0127B;
}
} else {
@@ -5200,7 +5206,8 @@
err("Error detecting sensor version");
return -1;
} else {
- info("Sensor is an SAA7111A (version 0x%x)", rc);
+ dev_info(&ov->dev->dev,
+ "Sensor is an SAA7111A (version 0x%x)\n", rc);
ov->sensor = SEN_SAA7111A;
}
@@ -5262,7 +5269,7 @@
PDEBUG (1, "CustomID = %d", ov->customid);
ov->desc = symbolic(camlist, ov->customid);
- info("model: %s", ov->desc);
+ dev_info(&ov->dev->dev, "model: %s\n", ov->desc);
if (0 == strcmp(ov->desc, NOT_DEFINED_STR)) {
err("Camera type (%d) not recognized", ov->customid);
@@ -5426,7 +5433,8 @@
PDEBUG(4, "");
/* First 5 bits of custom ID reg are a revision ID on OV518 */
- info("Device revision %d", 0x1F & reg_r(ov, R511_SYS_CUST_ID));
+ dev_info(&ov->dev->dev, "Device revision %d\n",
+ 0x1F & reg_r(ov, R511_SYS_CUST_ID));
/* Give it the default description */
ov->desc = symbolic(camlist, 0);
@@ -5773,7 +5781,8 @@
goto error;
}
- info("USB %s video device found", symbolic(brglist, ov->bridge));
+ dev_info(&intf->dev, "USB %s video device found\n",
+ symbolic(brglist, ov->bridge));
init_waitqueue_head(&ov->wq);
@@ -5854,8 +5863,8 @@
goto error;
}
- info("Device at %s registered to minor %d", ov->usb_path,
- ov->vdev->minor);
+ dev_info(&intf->dev, "Device at %s registered to minor %d\n",
+ ov->usb_path, ov->vdev->minor);
usb_set_intfdata(intf, ov);
if (ov_create_sysfs(ov->vdev)) {
@@ -5958,7 +5967,8 @@
if (retval)
goto out;
- info(DRIVER_VERSION " : " DRIVER_DESC);
+ printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
+ DRIVER_DESC "\n");
out:
return retval;
@@ -5968,8 +5978,7 @@
usb_ov511_exit(void)
{
usb_deregister(&ov511_driver);
- info("driver deregistered");
-
+ printk(KERN_INFO KBUILD_MODNAME ": driver deregistered\n");
}
module_init(usb_ov511_init);
diff --git a/drivers/media/video/ov511.h b/drivers/media/video/ov511.h
index baded12..70d99e5 100644
--- a/drivers/media/video/ov511.h
+++ b/drivers/media/video/ov511.h
@@ -12,7 +12,8 @@
#ifdef OV511_DEBUG
#define PDEBUG(level, fmt, args...) \
- if (debug >= (level)) info("[%s:%d] " fmt, \
+ if (debug >= (level)) \
+ printk(KERN_INFO KBUILD_MODNAME "[%s:%d] \n" fmt, \
__func__, __LINE__ , ## args)
#else
#define PDEBUG(level, fmt, args...) do {} while(0)
diff --git a/drivers/media/video/ovcamchip/ovcamchip_core.c b/drivers/media/video/ovcamchip/ovcamchip_core.c
index 065c245..2c4acbf 100644
--- a/drivers/media/video/ovcamchip/ovcamchip_core.c
+++ b/drivers/media/video/ovcamchip/ovcamchip_core.c
@@ -49,12 +49,6 @@
#define GENERIC_REG_ID_LOW 0x1D /* manufacturer ID LSB */
#define GENERIC_REG_COM_I 0x29 /* misc ID bits */
-extern struct ovcamchip_ops ov6x20_ops;
-extern struct ovcamchip_ops ov6x30_ops;
-extern struct ovcamchip_ops ov7x10_ops;
-extern struct ovcamchip_ops ov7x20_ops;
-extern struct ovcamchip_ops ov76be_ops;
-
static char *chip_names[NUM_CC_TYPES] = {
[CC_UNKNOWN] = "Unknown chip",
[CC_OV76BE] = "OV76BE",
diff --git a/drivers/media/video/ovcamchip/ovcamchip_priv.h b/drivers/media/video/ovcamchip/ovcamchip_priv.h
index 9afa4fe..a05650f 100644
--- a/drivers/media/video/ovcamchip/ovcamchip_priv.h
+++ b/drivers/media/video/ovcamchip/ovcamchip_priv.h
@@ -53,6 +53,12 @@
int initialized; /* OVCAMCHIP_CMD_INITIALIZE was successful */
};
+extern struct ovcamchip_ops ov6x20_ops;
+extern struct ovcamchip_ops ov6x30_ops;
+extern struct ovcamchip_ops ov7x10_ops;
+extern struct ovcamchip_ops ov7x20_ops;
+extern struct ovcamchip_ops ov76be_ops;
+
/* --------------------------------- */
/* I2C I/O */
/* --------------------------------- */
diff --git a/drivers/media/video/pms.c b/drivers/media/video/pms.c
index 7c84f79..9948078 100644
--- a/drivers/media/video/pms.c
+++ b/drivers/media/video/pms.c
@@ -47,6 +47,7 @@
struct video_picture picture;
int height;
int width;
+ unsigned long in_use;
struct mutex lock;
};
@@ -881,10 +882,27 @@
return len;
}
+static int pms_exclusive_open(struct inode *inode, struct file *file)
+{
+ struct video_device *v = video_devdata(file);
+ struct pms_device *pd = (struct pms_device *)v;
+
+ return test_and_set_bit(0, &pd->in_use) ? -EBUSY : 0;
+}
+
+static int pms_exclusive_release(struct inode *inode, struct file *file)
+{
+ struct video_device *v = video_devdata(file);
+ struct pms_device *pd = (struct pms_device *)v;
+
+ clear_bit(0, &pd->in_use);
+ return 0;
+}
+
static const struct file_operations pms_fops = {
.owner = THIS_MODULE,
- .open = video_exclusive_open,
- .release = video_exclusive_release,
+ .open = pms_exclusive_open,
+ .release = pms_exclusive_release,
.ioctl = pms_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l_compat_ioctl32,
@@ -897,6 +915,7 @@
{
.name = "Mediavision PMS",
.fops = &pms_fops,
+ .release = video_device_release_empty,
};
static struct pms_device pms_device;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-ctrl.c b/drivers/media/video/pvrusb2/pvrusb2-ctrl.c
index 0764fbf..203f54c 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-ctrl.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-ctrl.c
@@ -134,13 +134,17 @@
/* Retrieve control's default value (any type) */
-int pvr2_ctrl_get_def(struct pvr2_ctrl *cptr)
+int pvr2_ctrl_get_def(struct pvr2_ctrl *cptr, int *valptr)
{
int ret = 0;
if (!cptr) return 0;
LOCK_TAKE(cptr->hdw->big_lock); do {
if (cptr->info->type == pvr2_ctl_int) {
- ret = cptr->info->default_value;
+ if (cptr->info->get_def_value) {
+ ret = cptr->info->get_def_value(cptr, valptr);
+ } else {
+ *valptr = cptr->info->default_value;
+ }
}
} while(0); LOCK_GIVE(cptr->hdw->big_lock);
return ret;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-ctrl.h b/drivers/media/video/pvrusb2/pvrusb2-ctrl.h
index 0371ae6..794ff90 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-ctrl.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-ctrl.h
@@ -49,7 +49,7 @@
int pvr2_ctrl_get_min(struct pvr2_ctrl *);
/* Retrieve control's default value (any type) */
-int pvr2_ctrl_get_def(struct pvr2_ctrl *);
+int pvr2_ctrl_get_def(struct pvr2_ctrl *, int *valptr);
/* Retrieve control's enumeration count (enum only) */
int pvr2_ctrl_get_cnt(struct pvr2_ctrl *);
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
index 657f861..de7ee72 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
@@ -82,6 +82,7 @@
/* Control's implementation */
pvr2_ctlf_get_value get_value; /* Get its value */
+ pvr2_ctlf_get_value get_def_value; /* Get its default value */
pvr2_ctlf_get_value get_min_value; /* Get minimum allowed value */
pvr2_ctlf_get_value get_max_value; /* Get maximum allowed value */
pvr2_ctlf_set_value set_value; /* Set its value */
@@ -307,6 +308,10 @@
struct v4l2_tuner tuner_signal_info;
int tuner_signal_stale;
+ /* Cropping capability info */
+ struct v4l2_cropcap cropcap_info;
+ int cropcap_stale;
+
/* Video standard handling */
v4l2_std_id std_mask_eeprom; // Hardware supported selections
v4l2_std_id std_mask_avail; // Which standards we may select from
@@ -367,6 +372,10 @@
VCREATE_DATA(bass);
VCREATE_DATA(treble);
VCREATE_DATA(mute);
+ VCREATE_DATA(cropl);
+ VCREATE_DATA(cropt);
+ VCREATE_DATA(cropw);
+ VCREATE_DATA(croph);
VCREATE_DATA(input);
VCREATE_DATA(audiomode);
VCREATE_DATA(res_hor);
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
index f051c6a..94265bd 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
@@ -298,6 +298,7 @@
unsigned int timeout,int probe_fl,
void *write_data,unsigned int write_len,
void *read_data,unsigned int read_len);
+static int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw);
static void trace_stbit(const char *name,int val)
@@ -402,6 +403,194 @@
return 0;
}
+static int ctrl_cropl_min_get(struct pvr2_ctrl *cptr, int *left)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *left = cap->bounds.left;
+ return 0;
+}
+
+static int ctrl_cropl_max_get(struct pvr2_ctrl *cptr, int *left)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *left = cap->bounds.left;
+ if (cap->bounds.width > cptr->hdw->cropw_val) {
+ *left += cap->bounds.width - cptr->hdw->cropw_val;
+ }
+ return 0;
+}
+
+static int ctrl_cropt_min_get(struct pvr2_ctrl *cptr, int *top)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *top = cap->bounds.top;
+ return 0;
+}
+
+static int ctrl_cropt_max_get(struct pvr2_ctrl *cptr, int *top)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *top = cap->bounds.top;
+ if (cap->bounds.height > cptr->hdw->croph_val) {
+ *top += cap->bounds.height - cptr->hdw->croph_val;
+ }
+ return 0;
+}
+
+static int ctrl_cropw_max_get(struct pvr2_ctrl *cptr, int *val)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *val = 0;
+ if (cap->bounds.width > cptr->hdw->cropl_val) {
+ *val = cap->bounds.width - cptr->hdw->cropl_val;
+ }
+ return 0;
+}
+
+static int ctrl_croph_max_get(struct pvr2_ctrl *cptr, int *val)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *val = 0;
+ if (cap->bounds.height > cptr->hdw->cropt_val) {
+ *val = cap->bounds.height - cptr->hdw->cropt_val;
+ }
+ return 0;
+}
+
+static int ctrl_get_cropcapbl(struct pvr2_ctrl *cptr, int *val)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *val = cap->bounds.left;
+ return 0;
+}
+
+static int ctrl_get_cropcapbt(struct pvr2_ctrl *cptr, int *val)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *val = cap->bounds.top;
+ return 0;
+}
+
+static int ctrl_get_cropcapbw(struct pvr2_ctrl *cptr, int *val)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *val = cap->bounds.width;
+ return 0;
+}
+
+static int ctrl_get_cropcapbh(struct pvr2_ctrl *cptr, int *val)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *val = cap->bounds.height;
+ return 0;
+}
+
+static int ctrl_get_cropcapdl(struct pvr2_ctrl *cptr, int *val)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *val = cap->defrect.left;
+ return 0;
+}
+
+static int ctrl_get_cropcapdt(struct pvr2_ctrl *cptr, int *val)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *val = cap->defrect.top;
+ return 0;
+}
+
+static int ctrl_get_cropcapdw(struct pvr2_ctrl *cptr, int *val)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *val = cap->defrect.width;
+ return 0;
+}
+
+static int ctrl_get_cropcapdh(struct pvr2_ctrl *cptr, int *val)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *val = cap->defrect.height;
+ return 0;
+}
+
+static int ctrl_get_cropcappan(struct pvr2_ctrl *cptr, int *val)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *val = cap->pixelaspect.numerator;
+ return 0;
+}
+
+static int ctrl_get_cropcappad(struct pvr2_ctrl *cptr, int *val)
+{
+ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
+ int stat = pvr2_hdw_check_cropcap(cptr->hdw);
+ if (stat != 0) {
+ return stat;
+ }
+ *val = cap->pixelaspect.denominator;
+ return 0;
+}
+
static int ctrl_vres_max_get(struct pvr2_ctrl *cptr,int *vp)
{
/* Actual maximum depends on the video standard in effect. */
@@ -779,6 +968,10 @@
VCREATE_FUNCS(bass)
VCREATE_FUNCS(treble)
VCREATE_FUNCS(mute)
+VCREATE_FUNCS(cropl)
+VCREATE_FUNCS(cropt)
+VCREATE_FUNCS(cropw)
+VCREATE_FUNCS(croph)
VCREATE_FUNCS(audiomode)
VCREATE_FUNCS(res_hor)
VCREATE_FUNCS(res_ver)
@@ -849,6 +1042,72 @@
.default_value = 0,
DEFREF(mute),
DEFBOOL,
+ }, {
+ .desc = "Capture crop left margin",
+ .name = "crop_left",
+ .internal_id = PVR2_CID_CROPL,
+ .default_value = 0,
+ DEFREF(cropl),
+ DEFINT(-129, 340),
+ .get_min_value = ctrl_cropl_min_get,
+ .get_max_value = ctrl_cropl_max_get,
+ .get_def_value = ctrl_get_cropcapdl,
+ }, {
+ .desc = "Capture crop top margin",
+ .name = "crop_top",
+ .internal_id = PVR2_CID_CROPT,
+ .default_value = 0,
+ DEFREF(cropt),
+ DEFINT(-35, 544),
+ .get_min_value = ctrl_cropt_min_get,
+ .get_max_value = ctrl_cropt_max_get,
+ .get_def_value = ctrl_get_cropcapdt,
+ }, {
+ .desc = "Capture crop width",
+ .name = "crop_width",
+ .internal_id = PVR2_CID_CROPW,
+ .default_value = 720,
+ DEFREF(cropw),
+ .get_max_value = ctrl_cropw_max_get,
+ .get_def_value = ctrl_get_cropcapdw,
+ }, {
+ .desc = "Capture crop height",
+ .name = "crop_height",
+ .internal_id = PVR2_CID_CROPH,
+ .default_value = 480,
+ DEFREF(croph),
+ .get_max_value = ctrl_croph_max_get,
+ .get_def_value = ctrl_get_cropcapdh,
+ }, {
+ .desc = "Capture capability pixel aspect numerator",
+ .name = "cropcap_pixel_numerator",
+ .internal_id = PVR2_CID_CROPCAPPAN,
+ .get_value = ctrl_get_cropcappan,
+ }, {
+ .desc = "Capture capability pixel aspect denominator",
+ .name = "cropcap_pixel_denominator",
+ .internal_id = PVR2_CID_CROPCAPPAD,
+ .get_value = ctrl_get_cropcappad,
+ }, {
+ .desc = "Capture capability bounds top",
+ .name = "cropcap_bounds_top",
+ .internal_id = PVR2_CID_CROPCAPBT,
+ .get_value = ctrl_get_cropcapbt,
+ }, {
+ .desc = "Capture capability bounds left",
+ .name = "cropcap_bounds_left",
+ .internal_id = PVR2_CID_CROPCAPBL,
+ .get_value = ctrl_get_cropcapbl,
+ }, {
+ .desc = "Capture capability bounds width",
+ .name = "cropcap_bounds_width",
+ .internal_id = PVR2_CID_CROPCAPBW,
+ .get_value = ctrl_get_cropcapbw,
+ }, {
+ .desc = "Capture capability bounds height",
+ .name = "cropcap_bounds_height",
+ .internal_id = PVR2_CID_CROPCAPBH,
+ .get_value = ctrl_get_cropcapbh,
},{
.desc = "Video Source",
.name = "input",
@@ -1313,9 +1572,19 @@
if (bcnt > FIRMWARE_CHUNK_SIZE) bcnt = FIRMWARE_CHUNK_SIZE;
memcpy(fw_ptr, fw_entry->data + fw_done, bcnt);
/* Usbsnoop log shows that we must swap bytes... */
+ /* Some background info: The data being swapped here is a
+ firmware image destined for the mpeg encoder chip that
+ lives at the other end of a USB endpoint. The encoder
+ chip always talks in 32 bit chunks and its storage is
+ organized into 32 bit words. However from the file
+ system to the encoder chip everything is purely a byte
+ stream. The firmware file's contents are always 32 bit
+ swapped from what the encoder expects. Thus the need
+ always exists to swap the bytes regardless of the endian
+ type of the host processor and therefore swab32() makes
+ the most sense. */
for (icnt = 0; icnt < bcnt/4 ; icnt++)
- ((u32 *)fw_ptr)[icnt] =
- ___swab32(((u32 *)fw_ptr)[icnt]);
+ ((u32 *)fw_ptr)[icnt] = swab32(((u32 *)fw_ptr)[icnt]);
ret |= usb_bulk_msg(hdw->usb_dev, pipe, fw_ptr,bcnt,
&actual_length, HZ);
@@ -1905,7 +2174,7 @@
const struct usb_device_id *devid)
{
unsigned int idx,cnt1,cnt2,m;
- struct pvr2_hdw *hdw;
+ struct pvr2_hdw *hdw = NULL;
int valid_std_mask;
struct pvr2_ctrl *cptr;
const struct pvr2_device_desc *hdw_desc;
@@ -1915,6 +2184,16 @@
hdw_desc = (const struct pvr2_device_desc *)(devid->driver_info);
+ if (hdw_desc == NULL) {
+ pvr2_trace(PVR2_TRACE_INIT, "pvr2_hdw_create:"
+ " No device description pointer,"
+ " unable to continue.");
+ pvr2_trace(PVR2_TRACE_INIT, "If you have a new device type,"
+ " please contact Mike Isely <isely@pobox.com>"
+ " to get it included in the driver\n");
+ goto fail;
+ }
+
hdw = kzalloc(sizeof(*hdw),GFP_KERNEL);
pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_create: hdw=%p, type \"%s\"",
hdw,hdw_desc->description);
@@ -2072,6 +2351,7 @@
valid_std_mask;
}
+ hdw->cropcap_stale = !0;
hdw->eeprom_addr = -1;
hdw->unit_number = -1;
hdw->v4l_minor_number_video = -1;
@@ -2508,6 +2788,28 @@
/* Can't commit anything until pathway is ok. */
return 0;
}
+ /* The broadcast decoder can only scale down, so if
+ * res_*_dirty && crop window < output format ==> enlarge crop.
+ *
+ * The mpeg encoder receives fields of res_hor_val dots and
+ * res_ver_val halflines. Limits: hor<=720, ver<=576.
+ */
+ if (hdw->res_hor_dirty && hdw->cropw_val < hdw->res_hor_val) {
+ hdw->cropw_val = hdw->res_hor_val;
+ hdw->cropw_dirty = !0;
+ } else if (hdw->cropw_dirty) {
+ hdw->res_hor_dirty = !0; /* must rescale */
+ hdw->res_hor_val = min(720, hdw->cropw_val);
+ }
+ if (hdw->res_ver_dirty && hdw->croph_val < hdw->res_ver_val) {
+ hdw->croph_val = hdw->res_ver_val;
+ hdw->croph_dirty = !0;
+ } else if (hdw->croph_dirty) {
+ int nvres = hdw->std_mask_cur & V4L2_STD_525_60 ? 480 : 576;
+ hdw->res_ver_dirty = !0;
+ hdw->res_ver_val = min(nvres, hdw->croph_val);
+ }
+
/* If any of the below has changed, then we can't do the update
while the pipeline is running. Pipeline must be paused first
and decoder -> encoder connection be made quiescent before we
@@ -2518,6 +2820,8 @@
hdw->srate_dirty ||
hdw->res_ver_dirty ||
hdw->res_hor_dirty ||
+ hdw->cropw_dirty ||
+ hdw->croph_dirty ||
hdw->input_dirty ||
(hdw->active_stream_type != hdw->desired_stream_type));
if (disruptive_change && !hdw->state_pipeline_idle) {
@@ -2587,6 +2891,9 @@
}
hdw->state_pipeline_config = !0;
+ /* Hardware state may have changed in a way to cause the cropping
+ capabilities to have changed. So mark it stale, which will
+ cause a later re-fetch. */
trace_stbit("state_pipeline_config",hdw->state_pipeline_config);
return !0;
}
@@ -2677,6 +2984,33 @@
}
+static int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw)
+{
+ if (!hdw->cropcap_stale) {
+ return 0;
+ }
+ pvr2_i2c_core_status_poll(hdw);
+ if (hdw->cropcap_stale) {
+ return -EIO;
+ }
+ return 0;
+}
+
+
+/* Return information about cropping capabilities */
+int pvr2_hdw_get_cropcap(struct pvr2_hdw *hdw, struct v4l2_cropcap *pp)
+{
+ int stat = 0;
+ LOCK_TAKE(hdw->big_lock);
+ stat = pvr2_hdw_check_cropcap(hdw);
+ if (!stat) {
+ memcpy(pp, &hdw->cropcap_info, sizeof(hdw->cropcap_info));
+ }
+ LOCK_GIVE(hdw->big_lock);
+ return stat;
+}
+
+
/* Return information about the tuner */
int pvr2_hdw_get_tuner_status(struct pvr2_hdw *hdw,struct v4l2_tuner *vtp)
{
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
index c04956d..49482d1 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
@@ -36,6 +36,16 @@
#define PVR2_CID_FREQUENCY 6
#define PVR2_CID_HRES 7
#define PVR2_CID_VRES 8
+#define PVR2_CID_CROPL 9
+#define PVR2_CID_CROPT 10
+#define PVR2_CID_CROPW 11
+#define PVR2_CID_CROPH 12
+#define PVR2_CID_CROPCAPPAN 13
+#define PVR2_CID_CROPCAPPAD 14
+#define PVR2_CID_CROPCAPBL 15
+#define PVR2_CID_CROPCAPBT 16
+#define PVR2_CID_CROPCAPBW 17
+#define PVR2_CID_CROPCAPBH 18
/* Legal values for the INPUT state variable */
#define PVR2_CVAL_INPUT_TV 0
@@ -170,6 +180,9 @@
/* Return information about the tuner */
int pvr2_hdw_get_tuner_status(struct pvr2_hdw *,struct v4l2_tuner *);
+/* Return information about cropping capabilities */
+int pvr2_hdw_get_cropcap(struct pvr2_hdw *, struct v4l2_cropcap *);
+
/* Query device and see if it thinks it is on a high-speed USB link */
int pvr2_hdw_is_hsm(struct pvr2_hdw *);
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c
index ccdb429..94a4771 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c
@@ -37,8 +37,9 @@
#define OP_VOLUME 3
#define OP_FREQ 4
#define OP_AUDIORATE 5
-#define OP_SIZE 6
-#define OP_LOG 7
+#define OP_CROP 6
+#define OP_SIZE 7
+#define OP_LOG 8
static const struct pvr2_i2c_op * const ops[] = {
[OP_STANDARD] = &pvr2_i2c_op_v4l2_standard,
@@ -46,6 +47,7 @@
[OP_BCSH] = &pvr2_i2c_op_v4l2_bcsh,
[OP_VOLUME] = &pvr2_i2c_op_v4l2_volume,
[OP_FREQ] = &pvr2_i2c_op_v4l2_frequency,
+ [OP_CROP] = &pvr2_i2c_op_v4l2_crop,
[OP_SIZE] = &pvr2_i2c_op_v4l2_size,
[OP_LOG] = &pvr2_i2c_op_v4l2_log,
};
@@ -59,6 +61,7 @@
(1 << OP_BCSH) |
(1 << OP_VOLUME) |
(1 << OP_FREQ) |
+ (1 << OP_CROP) |
(1 << OP_SIZE) |
(1 << OP_LOG));
cp->status_poll = pvr2_v4l2_cmd_status_poll;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c
index 55f04a0..16bb119 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c
@@ -37,6 +37,7 @@
pvr2_i2c_core_cmd(hdw,VIDIOC_S_STD,&vs);
}
hdw->tuner_signal_stale = !0;
+ hdw->cropcap_stale = !0;
}
@@ -233,6 +234,37 @@
};
+static void set_crop(struct pvr2_hdw *hdw)
+{
+ struct v4l2_crop crop;
+
+ memset(&crop, 0, sizeof crop);
+ crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ crop.c.left = hdw->cropl_val;
+ crop.c.top = hdw->cropt_val;
+ crop.c.height = hdw->croph_val;
+ crop.c.width = hdw->cropw_val;
+
+ pvr2_trace(PVR2_TRACE_CHIPS,
+ "i2c v4l2 set_crop crop=%d:%d:%d:%d",
+ crop.c.width, crop.c.height, crop.c.left, crop.c.top);
+
+ pvr2_i2c_core_cmd(hdw, VIDIOC_S_CROP, &crop);
+}
+
+static int check_crop(struct pvr2_hdw *hdw)
+{
+ return (hdw->cropl_dirty || hdw->cropt_dirty ||
+ hdw->cropw_dirty || hdw->croph_dirty);
+}
+
+const struct pvr2_i2c_op pvr2_i2c_op_v4l2_crop = {
+ .check = check_crop,
+ .update = set_crop,
+ .name = "v4l2_crop",
+};
+
+
static void do_log(struct pvr2_hdw *hdw)
{
pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 do_log()");
@@ -263,7 +295,19 @@
void pvr2_v4l2_cmd_status_poll(struct pvr2_i2c_client *cp)
{
- pvr2_i2c_client_cmd(cp,VIDIOC_G_TUNER,&cp->hdw->tuner_signal_info);
+ int stat;
+ struct pvr2_hdw *hdw = cp->hdw;
+ if (hdw->cropcap_stale) {
+ hdw->cropcap_info.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ stat = pvr2_i2c_client_cmd(cp, VIDIOC_CROPCAP,
+ &hdw->cropcap_info);
+ if (stat == 0) {
+ /* Check was successful, so the data is no
+ longer considered stale. */
+ hdw->cropcap_stale = 0;
+ }
+ }
+ pvr2_i2c_client_cmd(cp, VIDIOC_G_TUNER, &hdw->tuner_signal_info);
}
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h
index 7fa3868..eb744a2 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h
@@ -29,6 +29,7 @@
extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_bcsh;
extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_volume;
extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_frequency;
+extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_crop;
extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_size;
extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_audiomode;
extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_log;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
index e600576..d6a3540 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
@@ -827,7 +827,7 @@
if ((detail & PVR2_I2C_DETAIL_CTLMASK) && cp->ctl_mask) {
unsigned int idx;
unsigned long msk,sm;
- int spcfl;
+
bcnt = scnprintf(buf,maxlen," [");
ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
sm = 0;
@@ -891,6 +891,7 @@
INIT_LIST_HEAD(&cp->list);
cp->client = client;
mutex_lock(&hdw->i2c_list_lock); do {
+ hdw->cropcap_stale = !0;
list_add_tail(&cp->list,&hdw->i2c_clients);
hdw->i2c_pend_types |= PVR2_I2C_PEND_DETECT;
} while (0); mutex_unlock(&hdw->i2c_list_lock);
@@ -905,6 +906,7 @@
unsigned long amask = 0;
int foundfl = 0;
mutex_lock(&hdw->i2c_list_lock); do {
+ hdw->cropcap_stale = !0;
list_for_each_entry_safe(cp, ncp, &hdw->i2c_clients, list) {
if (cp->client == client) {
trace_i2c("pvr2_i2c_detach"
@@ -946,22 +948,32 @@
.client_unregister = pvr2_i2c_detach_inform,
};
-static void do_i2c_scan(struct pvr2_hdw *hdw)
+
+/* Return true if device exists at given address */
+static int do_i2c_probe(struct pvr2_hdw *hdw, int addr)
{
struct i2c_msg msg[1];
- int i,rc;
+ int rc;
msg[0].addr = 0;
msg[0].flags = I2C_M_RD;
msg[0].len = 0;
msg[0].buf = NULL;
- printk("%s: i2c scan beginning\n",hdw->name);
+ msg[0].addr = addr;
+ rc = i2c_transfer(&hdw->i2c_adap, msg, ARRAY_SIZE(msg));
+ return rc == 1;
+}
+
+static void do_i2c_scan(struct pvr2_hdw *hdw)
+{
+ int i;
+ printk(KERN_INFO "%s: i2c scan beginning\n", hdw->name);
for (i = 0; i < 128; i++) {
- msg[0].addr = i;
- rc = i2c_transfer(&hdw->i2c_adap,msg, ARRAY_SIZE(msg));
- if (rc != 1) continue;
- printk("%s: i2c scan: found device @ 0x%x\n",hdw->name,i);
+ if (do_i2c_probe(hdw, i)) {
+ printk(KERN_INFO "%s: i2c scan: found device @ 0x%x\n",
+ hdw->name, i);
+ }
}
- printk("%s: i2c scan done.\n",hdw->name);
+ printk(KERN_INFO "%s: i2c scan done.\n", hdw->name);
}
void pvr2_i2c_core_init(struct pvr2_hdw *hdw)
@@ -980,8 +992,6 @@
hdw->i2c_func[0x18] = i2c_black_hole;
} else if (ir_mode[hdw->unit_number] == 1) {
if (hdw->hdw_desc->ir_scheme == PVR2_IR_SCHEME_24XXX) {
- /* This comment is present PURELY to get
- checkpatch.pl to STFU. Lovely, eh? */
hdw->i2c_func[0x18] = i2c_24xxx_ir;
}
}
@@ -1006,6 +1016,16 @@
mutex_init(&hdw->i2c_list_lock);
hdw->i2c_linked = !0;
i2c_add_adapter(&hdw->i2c_adap);
+ if (hdw->i2c_func[0x18] == i2c_24xxx_ir) {
+ /* Probe for a different type of IR receiver on this
+ device. If present, disable the emulated IR receiver. */
+ if (do_i2c_probe(hdw, 0x71)) {
+ pvr2_trace(PVR2_TRACE_INFO,
+ "Device has newer IR hardware;"
+ " disabling unneeded virtual IR device");
+ hdw->i2c_func[0x18] = NULL;
+ }
+ }
if (i2c_scan) do_i2c_scan(hdw);
}
diff --git a/drivers/media/video/pvrusb2/pvrusb2-main.c b/drivers/media/video/pvrusb2/pvrusb2-main.c
index ad0d98c..9b3c874 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-main.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-main.c
@@ -137,9 +137,11 @@
ret = usb_register(&pvr_driver);
if (ret == 0)
- info(DRIVER_DESC " : " DRIVER_VERSION);
- if (pvrusb2_debug) info("Debug mask is %d (0x%x)",
- pvrusb2_debug,pvrusb2_debug);
+ printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
+ DRIVER_DESC "\n");
+ if (pvrusb2_debug)
+ printk(KERN_INFO KBUILD_MODNAME ": Debug mask is %d (0x%x)\n",
+ pvrusb2_debug,pvrusb2_debug);
pvr2_trace(PVR2_TRACE_INIT,"pvr_init complete");
diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
index 46a8c39..733680f 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
@@ -65,6 +65,7 @@
struct device_attribute attr_type;
struct device_attribute attr_min;
struct device_attribute attr_max;
+ struct device_attribute attr_def;
struct device_attribute attr_enum;
struct device_attribute attr_bits;
struct device_attribute attr_val;
@@ -145,6 +146,23 @@
return scnprintf(buf, PAGE_SIZE, "%ld\n", val);
}
+static ssize_t show_def(struct device *class_dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pvr2_sysfs_ctl_item *cip;
+ int val;
+ int ret;
+ cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_def);
+ ret = pvr2_ctrl_get_def(cip->cptr, &val);
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_def(cid=%d) is %d, stat=%d",
+ cip->chptr, cip->ctl_id, val, ret);
+ if (ret < 0) {
+ return ret;
+ }
+ return scnprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+
static ssize_t show_val_norm(struct device *class_dev,
struct device_attribute *attr,
char *buf)
@@ -320,6 +338,10 @@
cip->attr_max.attr.mode = S_IRUGO;
cip->attr_max.show = show_max;
+ cip->attr_def.attr.name = "def_val";
+ cip->attr_def.attr.mode = S_IRUGO;
+ cip->attr_def.show = show_def;
+
cip->attr_val.attr.name = "cur_val";
cip->attr_val.attr.mode = S_IRUGO;
@@ -343,6 +365,7 @@
cip->attr_gen[acnt++] = &cip->attr_name.attr;
cip->attr_gen[acnt++] = &cip->attr_type.attr;
cip->attr_gen[acnt++] = &cip->attr_val.attr;
+ cip->attr_gen[acnt++] = &cip->attr_def.attr;
cip->attr_val.show = show_val_norm;
cip->attr_val.store = store_val_norm;
if (pvr2_ctrl_has_custom_symbols(cptr)) {
diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
index 00306fa..f048d80 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
@@ -533,7 +533,7 @@
lmin = pvr2_ctrl_get_min(hcp);
lmax = pvr2_ctrl_get_max(hcp);
- ldef = pvr2_ctrl_get_def(hcp);
+ pvr2_ctrl_get_def(hcp, &ldef);
if (w == -1) {
w = ldef;
} else if (w < lmin) {
@@ -543,7 +543,7 @@
}
lmin = pvr2_ctrl_get_min(vcp);
lmax = pvr2_ctrl_get_max(vcp);
- ldef = pvr2_ctrl_get_def(vcp);
+ pvr2_ctrl_get_def(vcp, &ldef);
if (h == -1) {
h = ldef;
} else if (h < lmin) {
@@ -604,6 +604,7 @@
case VIDIOC_QUERYCTRL:
{
struct pvr2_ctrl *cptr;
+ int val;
struct v4l2_queryctrl *vc = (struct v4l2_queryctrl *)arg;
ret = 0;
if (vc->id & V4L2_CTRL_FLAG_NEXT_CTRL) {
@@ -627,7 +628,8 @@
pvr2_ctrl_get_desc(cptr));
strlcpy(vc->name,pvr2_ctrl_get_desc(cptr),sizeof(vc->name));
vc->flags = pvr2_ctrl_get_v4lflags(cptr);
- vc->default_value = pvr2_ctrl_get_def(cptr);
+ pvr2_ctrl_get_def(cptr, &val);
+ vc->default_value = val;
switch (pvr2_ctrl_get_type(cptr)) {
case pvr2_ctl_enum:
vc->type = V4L2_CTRL_TYPE_MENU;
@@ -753,6 +755,92 @@
break;
}
+ case VIDIOC_CROPCAP:
+ {
+ struct v4l2_cropcap *cap = (struct v4l2_cropcap *)arg;
+ if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = pvr2_hdw_get_cropcap(hdw, cap);
+ cap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* paranoia */
+ break;
+ }
+ case VIDIOC_G_CROP:
+ {
+ struct v4l2_crop *crop = (struct v4l2_crop *)arg;
+ int val = 0;
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), &val);
+ if (ret != 0) {
+ ret = -EINVAL;
+ break;
+ }
+ crop->c.left = val;
+ ret = pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), &val);
+ if (ret != 0) {
+ ret = -EINVAL;
+ break;
+ }
+ crop->c.top = val;
+ ret = pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), &val);
+ if (ret != 0) {
+ ret = -EINVAL;
+ break;
+ }
+ crop->c.width = val;
+ ret = pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), &val);
+ if (ret != 0) {
+ ret = -EINVAL;
+ break;
+ }
+ crop->c.height = val;
+ }
+ case VIDIOC_S_CROP:
+ {
+ struct v4l2_crop *crop = (struct v4l2_crop *)arg;
+ struct v4l2_cropcap cap;
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ ret = -EINVAL;
+ break;
+ }
+ cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ ret = pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL),
+ crop->c.left);
+ if (ret != 0) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT),
+ crop->c.top);
+ if (ret != 0) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW),
+ crop->c.width);
+ if (ret != 0) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH),
+ crop->c.height);
+ if (ret != 0) {
+ ret = -EINVAL;
+ break;
+ }
+ }
case VIDIOC_LOG_STATUS:
{
pvr2_hdw_trigger_module_log(hdw);
diff --git a/drivers/media/video/pwc/pwc-ctrl.c b/drivers/media/video/pwc/pwc-ctrl.c
index dbc5607..c665302 100644
--- a/drivers/media/video/pwc/pwc-ctrl.c
+++ b/drivers/media/video/pwc/pwc-ctrl.c
@@ -41,7 +41,6 @@
#include <asm/uaccess.h>
#endif
#include <asm/errno.h>
-#include <linux/version.h>
#include "pwc.h"
#include "pwc-uncompress.h"
diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c
index 9aee7cb..ab28389 100644
--- a/drivers/media/video/pwc/pwc-if.c
+++ b/drivers/media/video/pwc/pwc-if.c
@@ -1112,7 +1112,7 @@
PWC_DEBUG_OPEN(">> video_open called(vdev = 0x%p).\n", vdev);
- pdev = (struct pwc_device *)vdev->priv;
+ pdev = video_get_drvdata(vdev);
BUG_ON(!pdev);
if (pdev->vopen) {
PWC_DEBUG_OPEN("I'm busy, someone is using the device.\n");
@@ -1233,7 +1233,7 @@
PWC_DEBUG_OPEN(">> video_close called(vdev = 0x%p).\n", vdev);
lock_kernel();
- pdev = (struct pwc_device *)vdev->priv;
+ pdev = video_get_drvdata(vdev);
if (pdev->vopen == 0)
PWC_DEBUG_MODULE("video_close() called on closed device?\n");
@@ -1304,7 +1304,7 @@
vdev, buf, count);
if (vdev == NULL)
return -EFAULT;
- pdev = vdev->priv;
+ pdev = video_get_drvdata(vdev);
if (pdev == NULL)
return -EFAULT;
@@ -1386,7 +1386,7 @@
if (vdev == NULL)
return -EFAULT;
- pdev = vdev->priv;
+ pdev = video_get_drvdata(vdev);
if (pdev == NULL)
return -EFAULT;
@@ -1408,7 +1408,7 @@
if (!vdev)
goto out;
- pdev = vdev->priv;
+ pdev = video_get_drvdata(vdev);
mutex_lock(&pdev->modlock);
if (!pdev->unplugged)
@@ -1428,7 +1428,7 @@
int index;
PWC_DEBUG_MEMORY(">> %s\n", __func__);
- pdev = vdev->priv;
+ pdev = video_get_drvdata(vdev);
size = vma->vm_end - vma->vm_start;
start = vma->vm_start;
diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c
index 1742889..76a1376 100644
--- a/drivers/media/video/pwc/pwc-v4l.c
+++ b/drivers/media/video/pwc/pwc-v4l.c
@@ -346,7 +346,7 @@
if (vdev == NULL)
return -EFAULT;
- pdev = vdev->priv;
+ pdev = video_get_drvdata(vdev);
if (pdev == NULL)
return -EFAULT;
diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c
index cf96b2c..eb6be58 100644
--- a/drivers/media/video/pxa_camera.c
+++ b/drivers/media/video/pxa_camera.c
@@ -629,17 +629,6 @@
pdata->init(pcdev->dev);
}
- if (pdata && pdata->power) {
- dev_dbg(pcdev->dev, "%s: Power on camera\n", __func__);
- pdata->power(pcdev->dev, 1);
- }
-
- if (pdata && pdata->reset) {
- dev_dbg(pcdev->dev, "%s: Releasing camera reset\n",
- __func__);
- pdata->reset(pcdev->dev, 1);
- }
-
CICR0 = 0x3FF; /* disable all interrupts */
if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN)
@@ -660,20 +649,7 @@
static void pxa_camera_deactivate(struct pxa_camera_dev *pcdev)
{
- struct pxacamera_platform_data *board = pcdev->pdata;
-
clk_disable(pcdev->clk);
-
- if (board && board->reset) {
- dev_dbg(pcdev->dev, "%s: Asserting camera reset\n",
- __func__);
- board->reset(pcdev->dev, 0);
- }
-
- if (board && board->power) {
- dev_dbg(pcdev->dev, "%s: Power off camera\n", __func__);
- board->power(pcdev->dev, 0);
- }
}
static irqreturn_t pxa_camera_irq(int irq, void *data)
@@ -1144,31 +1120,31 @@
pcdev->dev = &pdev->dev;
/* request dma */
- pcdev->dma_chans[0] = pxa_request_dma("CI_Y", DMA_PRIO_HIGH,
- pxa_camera_dma_irq_y, pcdev);
- if (pcdev->dma_chans[0] < 0) {
+ err = pxa_request_dma("CI_Y", DMA_PRIO_HIGH,
+ pxa_camera_dma_irq_y, pcdev);
+ if (err < 0) {
dev_err(pcdev->dev, "Can't request DMA for Y\n");
- err = -ENOMEM;
goto exit_iounmap;
}
+ pcdev->dma_chans[0] = err;
dev_dbg(pcdev->dev, "got DMA channel %d\n", pcdev->dma_chans[0]);
- pcdev->dma_chans[1] = pxa_request_dma("CI_U", DMA_PRIO_HIGH,
- pxa_camera_dma_irq_u, pcdev);
- if (pcdev->dma_chans[1] < 0) {
+ err = pxa_request_dma("CI_U", DMA_PRIO_HIGH,
+ pxa_camera_dma_irq_u, pcdev);
+ if (err < 0) {
dev_err(pcdev->dev, "Can't request DMA for U\n");
- err = -ENOMEM;
goto exit_free_dma_y;
}
+ pcdev->dma_chans[1] = err;
dev_dbg(pcdev->dev, "got DMA channel (U) %d\n", pcdev->dma_chans[1]);
- pcdev->dma_chans[2] = pxa_request_dma("CI_V", DMA_PRIO_HIGH,
- pxa_camera_dma_irq_v, pcdev);
- if (pcdev->dma_chans[0] < 0) {
+ err = pxa_request_dma("CI_V", DMA_PRIO_HIGH,
+ pxa_camera_dma_irq_v, pcdev);
+ if (err < 0) {
dev_err(pcdev->dev, "Can't request DMA for V\n");
- err = -ENOMEM;
goto exit_free_dma_u;
}
+ pcdev->dma_chans[2] = err;
dev_dbg(pcdev->dev, "got DMA channel (V) %d\n", pcdev->dma_chans[2]);
DRCMR(68) = pcdev->dma_chans[0] | DRCMR_MAPVLD;
diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c
index 92b83fe..5272926 100644
--- a/drivers/media/video/s2255drv.c
+++ b/drivers/media/video/s2255drv.c
@@ -58,6 +58,8 @@
+/* default JPEG quality */
+#define S2255_DEF_JPEG_QUAL 50
/* vendor request in */
#define S2255_VR_IN 0
/* vendor request out */
@@ -67,20 +69,21 @@
/* USB endpoint number for configuring the device */
#define S2255_CONFIG_EP 2
/* maximum time for DSP to start responding after last FW word loaded(ms) */
-#define S2255_DSP_BOOTTIME 400
+#define S2255_DSP_BOOTTIME 800
/* maximum time to wait for firmware to load (ms) */
#define S2255_LOAD_TIMEOUT (5000 + S2255_DSP_BOOTTIME)
#define S2255_DEF_BUFS 16
+#define S2255_SETMODE_TIMEOUT 500
#define MAX_CHANNELS 4
-#define FRAME_MARKER 0x2255DA4AL
-#define MAX_PIPE_USBBLOCK (40 * 1024)
-#define DEFAULT_PIPE_USBBLOCK (16 * 1024)
+#define S2255_MARKER_FRAME 0x2255DA4AL
+#define S2255_MARKER_RESPONSE 0x2255ACACL
+#define S2255_USB_XFER_SIZE (16 * 1024)
#define MAX_CHANNELS 4
#define MAX_PIPE_BUFFERS 1
#define SYS_FRAMES 4
/* maximum size is PAL full size plus room for the marker header(s) */
-#define SYS_FRAMES_MAXSIZE (720 * 288 * 2 * 2 + 4096)
-#define DEF_USB_BLOCK (4096)
+#define SYS_FRAMES_MAXSIZE (720*288*2*2 + 4096)
+#define DEF_USB_BLOCK S2255_USB_XFER_SIZE
#define LINE_SZ_4CIFS_NTSC 640
#define LINE_SZ_2CIFS_NTSC 640
#define LINE_SZ_1CIFS_NTSC 320
@@ -108,6 +111,9 @@
#define COLOR_YUVPL 1 /* YUV planar */
#define COLOR_YUVPK 2 /* YUV packed */
#define COLOR_Y8 4 /* monochrome */
+#define COLOR_JPG 5 /* JPEG */
+#define MASK_COLOR 0xff
+#define MASK_JPG_QUALITY 0xff00
/* frame decimation. Not implemented by V4L yet(experimental in V4L) */
#define FDEC_1 1 /* capture every frame. default */
@@ -148,16 +154,14 @@
u32 restart; /* if DSP requires restart */
};
+
+#define S2255_READ_IDLE 0
+#define S2255_READ_FRAME 1
+
/* frame structure */
-#define FRAME_STATE_UNUSED 0
-#define FRAME_STATE_FILLING 1
-#define FRAME_STATE_FULL 2
-
-
struct s2255_framei {
unsigned long size;
-
- unsigned long ulState; /* ulState ==0 unused, 1 being filled, 2 full */
+ unsigned long ulState; /* ulState:S2255_READ_IDLE, S2255_READ_FRAME*/
void *lpvbits; /* image data */
unsigned long cur_size; /* current data copied to it */
};
@@ -188,6 +192,10 @@
#define S2255_FW_FAILED 3
#define S2255_FW_DISCONNECTING 4
+#define S2255_FW_MARKER 0x22552f2f
+/* 2255 read states */
+#define S2255_READ_IDLE 0
+#define S2255_READ_FRAME 1
struct s2255_fw {
int fw_loaded;
int fw_size;
@@ -195,7 +203,6 @@
atomic_t fw_state;
void *pfw_data;
wait_queue_head_t wait_fw;
- struct timer_list dsp_wait;
const struct firmware *fw;
};
@@ -237,15 +244,27 @@
struct s2255_pipeinfo pipes[MAX_PIPE_BUFFERS];
struct s2255_bufferi buffer[MAX_CHANNELS];
struct s2255_mode mode[MAX_CHANNELS];
+ /* jpeg compression */
+ struct v4l2_jpegcompression jc[MAX_CHANNELS];
const struct s2255_fmt *cur_fmt[MAX_CHANNELS];
int cur_frame[MAX_CHANNELS];
int last_frame[MAX_CHANNELS];
u32 cc; /* current channel */
int b_acquire[MAX_CHANNELS];
+ /* allocated image size */
unsigned long req_image_size[MAX_CHANNELS];
+ /* received packet size */
+ unsigned long pkt_size[MAX_CHANNELS];
int bad_payload[MAX_CHANNELS];
unsigned long frame_count[MAX_CHANNELS];
int frame_ready;
+ /* if JPEG image */
+ int jpg_size[MAX_CHANNELS];
+ /* if channel configured to default state */
+ int chn_configured[MAX_CHANNELS];
+ wait_queue_head_t wait_setmode[MAX_CHANNELS];
+ int setmode_ready[MAX_CHANNELS];
+ int chn_ready;
struct kref kref;
spinlock_t slock;
};
@@ -306,12 +325,16 @@
static int s2255_start_acquire(struct s2255_dev *dev, unsigned long chn);
static int s2255_stop_acquire(struct s2255_dev *dev, unsigned long chn);
static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf,
- int chn);
+ int chn, int jpgsize);
static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn,
struct s2255_mode *mode);
static int s2255_board_shutdown(struct s2255_dev *dev);
static void s2255_exit_v4l(struct s2255_dev *dev);
-static void s2255_fwload_start(struct s2255_dev *dev);
+static void s2255_fwload_start(struct s2255_dev *dev, int reset);
+static void s2255_destroy(struct kref *kref);
+static long s2255_vendor_req(struct s2255_dev *dev, unsigned char req,
+ u16 index, u16 value, void *buf,
+ s32 buf_len, int bOut);
#define dprintk(level, fmt, arg...) \
do { \
@@ -407,6 +430,10 @@
.fourcc = V4L2_PIX_FMT_UYVY,
.depth = 16
}, {
+ .name = "JPG",
+ .fourcc = V4L2_PIX_FMT_JPEG,
+ .depth = 24
+ }, {
.name = "8bpp GREY",
.fourcc = V4L2_PIX_FMT_GREY,
.depth = 8
@@ -464,6 +491,13 @@
return;
}
+static void s2255_reset_dsppower(struct s2255_dev *dev)
+{
+ s2255_vendor_req(dev, 0x40, 0x0b0b, 0x0b0b, NULL, 0, 1);
+ msleep(10);
+ s2255_vendor_req(dev, 0x50, 0x0000, 0x0000, NULL, 0, 1);
+ return;
+}
/* kickstarts the firmware loading. from probe
*/
@@ -480,18 +514,6 @@
}
}
-/* called when DSP is up and running. DSP is guaranteed to
- be running after S2255_DSP_BOOTTIME */
-static void s2255_dsp_running(unsigned long user_data)
-{
- struct s2255_fw *data = (struct s2255_fw *)user_data;
- dprintk(1, "dsp running\n");
- atomic_set(&data->fw_state, S2255_FW_SUCCESS);
- wake_up(&data->wait_fw);
- printk(KERN_INFO "s2255: firmware loaded successfully\n");
- return;
-}
-
/* this loads the firmware asynchronously.
Originally this was done synchroously in probe.
@@ -549,19 +571,14 @@
}
data->fw_loaded += len;
} else {
- init_timer(&data->dsp_wait);
- data->dsp_wait.function = s2255_dsp_running;
- data->dsp_wait.data = (unsigned long)data;
atomic_set(&data->fw_state, S2255_FW_LOADED_DSPWAIT);
- mod_timer(&data->dsp_wait, msecs_to_jiffies(S2255_DSP_BOOTTIME)
- + jiffies);
}
dprintk(100, "2255 complete done\n");
return;
}
-static int s2255_got_frame(struct s2255_dev *dev, int chn)
+static int s2255_got_frame(struct s2255_dev *dev, int chn, int jpgsize)
{
struct s2255_dmaqueue *dma_q = &dev->vidq[chn];
struct s2255_buffer *buf;
@@ -586,8 +603,7 @@
list_del(&buf->vb.queue);
do_gettimeofday(&buf->vb.ts);
dprintk(100, "[%p/%d] wakeup\n", buf, buf->vb.i);
-
- s2255_fillbuff(dev, buf, dma_q->channel);
+ s2255_fillbuff(dev, buf, dma_q->channel, jpgsize);
wake_up(&buf->vb.done);
dprintk(2, "wakeup [buf/i] [%p/%d]\n", buf, buf->vb.i);
unlock:
@@ -621,7 +637,7 @@
*
*/
static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf,
- int chn)
+ int chn, int jpgsize)
{
int pos = 0;
struct timeval ts;
@@ -649,6 +665,10 @@
case V4L2_PIX_FMT_GREY:
memcpy(vbuf, tmpbuf, buf->vb.width * buf->vb.height);
break;
+ case V4L2_PIX_FMT_JPEG:
+ buf->vb.size = jpgsize;
+ memcpy(vbuf, tmpbuf, buf->vb.size);
+ break;
case V4L2_PIX_FMT_YUV422P:
memcpy(vbuf, tmpbuf,
buf->vb.width * buf->vb.height * 2);
@@ -657,9 +677,6 @@
printk(KERN_DEBUG "s2255: unknown format?\n");
}
dev->last_frame[chn] = -1;
- /* done with the frame, free it */
- frm->ulState = 0;
- dprintk(4, "freeing buffer\n");
} else {
printk(KERN_ERR "s2255: =======no frame\n");
return;
@@ -1021,6 +1038,10 @@
case V4L2_PIX_FMT_GREY:
fh->mode.color = COLOR_Y8;
break;
+ case V4L2_PIX_FMT_JPEG:
+ fh->mode.color = COLOR_JPG |
+ (fh->dev->jc[fh->channel].quality << 8);
+ break;
case V4L2_PIX_FMT_YUV422P:
fh->mode.color = COLOR_YUVPL;
break;
@@ -1139,7 +1160,7 @@
}
}
outImageSize = linesPerFrame * pixelsPerLine;
- if (mode->color != COLOR_Y8) {
+ if ((mode->color & MASK_COLOR) != COLOR_Y8) {
/* 2 bytes/pixel if not monochrome */
outImageSize *= 2;
}
@@ -1185,12 +1206,17 @@
u32 *buffer;
unsigned long chn_rev;
+ mutex_lock(&dev->lock);
chn_rev = G_chnmap[chn];
dprintk(3, "mode scale [%ld] %p %d\n", chn, mode, mode->scale);
dprintk(3, "mode scale [%ld] %p %d\n", chn, &dev->mode[chn],
dev->mode[chn].scale);
dprintk(2, "mode contrast %x\n", mode->contrast);
+ /* if JPEG, set the quality */
+ if ((mode->color & MASK_COLOR) == COLOR_JPG)
+ mode->color = (dev->jc[chn].quality << 8) | COLOR_JPG;
+
/* save the mode */
dev->mode[chn] = *mode;
dev->req_image_size[chn] = get_transfer_size(mode);
@@ -1199,6 +1225,7 @@
buffer = kzalloc(512, GFP_KERNEL);
if (buffer == NULL) {
dev_err(&dev->udev->dev, "out of mem\n");
+ mutex_unlock(&dev->lock);
return -ENOMEM;
}
@@ -1214,12 +1241,20 @@
dprintk(1, "set mode done chn %lu, %d\n", chn, res);
/* wait at least 3 frames before continuing */
- if (mode->restart)
- msleep(125);
+ if (mode->restart) {
+ dev->setmode_ready[chn] = 0;
+ wait_event_timeout(dev->wait_setmode[chn],
+ (dev->setmode_ready[chn] != 0),
+ msecs_to_jiffies(S2255_SETMODE_TIMEOUT));
+ if (dev->setmode_ready[chn] != 1) {
+ printk(KERN_DEBUG "s2255: no set mode response\n");
+ res = -EFAULT;
+ }
+ }
/* clear the restart flag */
dev->mode[chn].restart = 0;
-
+ mutex_unlock(&dev->lock);
return res;
}
@@ -1270,7 +1305,7 @@
dev->cur_frame[chn] = 0;
dev->frame_count[chn] = 0;
for (j = 0; j < SYS_FRAMES; j++) {
- dev->buffer[chn].frame[j].ulState = 0;
+ dev->buffer[chn].frame[j].ulState = S2255_READ_IDLE;
dev->buffer[chn].frame[j].cur_size = 0;
}
res = videobuf_streamon(&fh->vb_vidq);
@@ -1446,6 +1481,27 @@
return -EINVAL;
}
+static int vidioc_g_jpegcomp(struct file *file, void *priv,
+ struct v4l2_jpegcompression *jc)
+{
+ struct s2255_fh *fh = priv;
+ struct s2255_dev *dev = fh->dev;
+ *jc = dev->jc[fh->channel];
+ dprintk(2, "getting jpegcompression, quality %d\n", jc->quality);
+ return 0;
+}
+
+static int vidioc_s_jpegcomp(struct file *file, void *priv,
+ struct v4l2_jpegcompression *jc)
+{
+ struct s2255_fh *fh = priv;
+ struct s2255_dev *dev = fh->dev;
+ if (jc->quality < 0 || jc->quality > 100)
+ return -EINVAL;
+ dev->jc[fh->channel].quality = jc->quality;
+ dprintk(2, "setting jpeg quality %d\n", jc->quality);
+ return 0;
+}
static int s2255_open(struct inode *inode, struct file *file)
{
int minor = iminor(inode);
@@ -1455,8 +1511,10 @@
enum v4l2_buf_type type = 0;
int i = 0;
int cur_channel = -1;
+ int state;
dprintk(1, "s2255: open called (minor=%d)\n", minor);
+ lock_kernel();
list_for_each(list, &s2255_devlist) {
h = list_entry(list, struct s2255_dev, s2255_devlist);
for (i = 0; i < MAX_CHANNELS; i++) {
@@ -1469,45 +1527,78 @@
}
if ((NULL == dev) || (cur_channel == -1)) {
- dprintk(1, "s2255: openv4l no dev\n");
+ unlock_kernel();
+ printk(KERN_INFO "s2255: openv4l no dev\n");
return -ENODEV;
}
+ if (atomic_read(&dev->fw_data->fw_state) == S2255_FW_DISCONNECTING) {
+ unlock_kernel();
+ printk(KERN_INFO "disconnecting\n");
+ return -ENODEV;
+ }
+ kref_get(&dev->kref);
mutex_lock(&dev->open_lock);
dev->users[cur_channel]++;
dprintk(4, "s2255: open_handles %d\n", dev->users[cur_channel]);
- if (atomic_read(&dev->fw_data->fw_state) == S2255_FW_FAILED) {
+ switch (atomic_read(&dev->fw_data->fw_state)) {
+ case S2255_FW_FAILED:
err("2255 firmware load failed. retrying.\n");
- s2255_fwload_start(dev);
+ s2255_fwload_start(dev, 1);
wait_event_timeout(dev->fw_data->wait_fw,
- (atomic_read(&dev->fw_data->fw_state)
- != S2255_FW_NOTLOADED),
+ ((atomic_read(&dev->fw_data->fw_state)
+ == S2255_FW_SUCCESS) ||
+ (atomic_read(&dev->fw_data->fw_state)
+ == S2255_FW_DISCONNECTING)),
msecs_to_jiffies(S2255_LOAD_TIMEOUT));
- if (atomic_read(&dev->fw_data->fw_state)
- != S2255_FW_SUCCESS) {
- printk(KERN_INFO "2255 FW load failed.\n");
- dev->users[cur_channel]--;
- mutex_unlock(&dev->open_lock);
- return -EFAULT;
- }
- } else if (atomic_read(&dev->fw_data->fw_state) == S2255_FW_NOTLOADED) {
+ break;
+ case S2255_FW_NOTLOADED:
+ case S2255_FW_LOADED_DSPWAIT:
/* give S2255_LOAD_TIMEOUT time for firmware to load in case
driver loaded and then device immediately opened */
printk(KERN_INFO "%s waiting for firmware load\n", __func__);
wait_event_timeout(dev->fw_data->wait_fw,
- (atomic_read(&dev->fw_data->fw_state)
- != S2255_FW_NOTLOADED),
- msecs_to_jiffies(S2255_LOAD_TIMEOUT));
- if (atomic_read(&dev->fw_data->fw_state)
- != S2255_FW_SUCCESS) {
- printk(KERN_INFO "2255 firmware not loaded"
- "try again\n");
- dev->users[cur_channel]--;
- mutex_unlock(&dev->open_lock);
- return -EBUSY;
+ ((atomic_read(&dev->fw_data->fw_state)
+ == S2255_FW_SUCCESS) ||
+ (atomic_read(&dev->fw_data->fw_state)
+ == S2255_FW_DISCONNECTING)),
+ msecs_to_jiffies(S2255_LOAD_TIMEOUT));
+ break;
+ case S2255_FW_SUCCESS:
+ default:
+ break;
+ }
+ state = atomic_read(&dev->fw_data->fw_state);
+ if (state != S2255_FW_SUCCESS) {
+ int rc;
+ switch (state) {
+ case S2255_FW_FAILED:
+ printk(KERN_INFO "2255 FW load failed. %d\n", state);
+ rc = -ENODEV;
+ break;
+ case S2255_FW_DISCONNECTING:
+ printk(KERN_INFO "%s: disconnecting\n", __func__);
+ rc = -ENODEV;
+ break;
+ case S2255_FW_LOADED_DSPWAIT:
+ case S2255_FW_NOTLOADED:
+ printk(KERN_INFO "%s: firmware not loaded yet"
+ "please try again later\n",
+ __func__);
+ rc = -EAGAIN;
+ break;
+ default:
+ printk(KERN_INFO "%s: unknown state\n", __func__);
+ rc = -EFAULT;
+ break;
}
+ dev->users[cur_channel]--;
+ mutex_unlock(&dev->open_lock);
+ kref_put(&dev->kref, s2255_destroy);
+ unlock_kernel();
+ return rc;
}
/* allocate + initialize per filehandle data */
@@ -1515,6 +1606,8 @@
if (NULL == fh) {
dev->users[cur_channel]--;
mutex_unlock(&dev->open_lock);
+ kref_put(&dev->kref, s2255_destroy);
+ unlock_kernel();
return -ENOMEM;
}
@@ -1528,6 +1621,13 @@
fh->height = NUM_LINES_4CIFS_NTSC * 2;
fh->channel = cur_channel;
+ /* configure channel to default state */
+ if (!dev->chn_configured[cur_channel]) {
+ s2255_set_mode(dev, cur_channel, &fh->mode);
+ dev->chn_configured[cur_channel] = 1;
+ }
+
+
/* Put all controls at a sane state */
for (i = 0; i < ARRAY_SIZE(s2255_qctrl); i++)
qctl_regs[i] = s2255_qctrl[i].default_value;
@@ -1546,8 +1646,8 @@
V4L2_FIELD_INTERLACED,
sizeof(struct s2255_buffer), fh);
- kref_get(&dev->kref);
mutex_unlock(&dev->open_lock);
+ unlock_kernel();
return 0;
}
@@ -1569,30 +1669,24 @@
static void s2255_destroy(struct kref *kref)
{
struct s2255_dev *dev = to_s2255_dev(kref);
+ struct list_head *list;
+ int i;
if (!dev) {
printk(KERN_ERR "s2255drv: kref problem\n");
return;
}
-
- /*
- * Wake up any firmware load waiting (only done in .open,
- * which holds the open_lock mutex)
- */
atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING);
wake_up(&dev->fw_data->wait_fw);
-
- /* prevent s2255_disconnect from racing s2255_open */
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ dev->setmode_ready[i] = 1;
+ wake_up(&dev->wait_setmode[i]);
+ }
mutex_lock(&dev->open_lock);
+ /* reset the DSP so firmware can be reload next time */
+ s2255_reset_dsppower(dev);
s2255_exit_v4l(dev);
- /*
- * device unregistered so no longer possible to open. open_mutex
- * can be unlocked and timers deleted afterwards.
- */
- mutex_unlock(&dev->open_lock);
-
/* board shutdown stops the read pipe if it is running */
s2255_board_shutdown(dev);
-
/* make sure firmware still not trying to load */
del_timer(&dev->timer); /* only started in .probe and .open */
@@ -1602,23 +1696,19 @@
usb_free_urb(dev->fw_data->fw_urb);
dev->fw_data->fw_urb = NULL;
}
-
- /*
- * delete the dsp_wait timer, which sets the firmware
- * state on completion. This is done before fw_data
- * is freed below.
- */
-
- del_timer(&dev->fw_data->dsp_wait); /* only started in .open */
-
if (dev->fw_data->fw)
release_firmware(dev->fw_data->fw);
kfree(dev->fw_data->pfw_data);
kfree(dev->fw_data);
-
usb_put_dev(dev->udev);
dprintk(1, "%s", __func__);
kfree(dev);
+
+ while (!list_empty(&s2255_devlist)) {
+ list = s2255_devlist.next;
+ list_del(list);
+ }
+ mutex_unlock(&dev->open_lock);
}
static int s2255_close(struct inode *inode, struct file *file)
@@ -1702,6 +1792,8 @@
#ifdef CONFIG_VIDEO_V4L1_COMPAT
.vidiocgmbuf = vidioc_cgmbuf,
#endif
+ .vidioc_s_jpegcomp = vidioc_s_jpegcomp,
+ .vidioc_g_jpegcomp = vidioc_g_jpegcomp,
};
static struct video_device template = {
@@ -1740,7 +1832,7 @@
ret = video_register_device(dev->vdev[i],
VFL_TYPE_GRABBER,
cur_nr + i);
- dev->vdev[i]->priv = dev;
+ video_set_drvdata(dev->vdev[i], dev);
if (ret != 0) {
dev_err(&dev->udev->dev,
@@ -1754,18 +1846,16 @@
static void s2255_exit_v4l(struct s2255_dev *dev)
{
- struct list_head *list;
+
int i;
- /* unregister the video devices */
- while (!list_empty(&s2255_devlist)) {
- list = s2255_devlist.next;
- list_del(list);
- }
for (i = 0; i < MAX_CHANNELS; i++) {
- if (-1 != dev->vdev[i]->minor)
+ if (-1 != dev->vdev[i]->minor) {
video_unregister_device(dev->vdev[i]);
- else
+ printk(KERN_INFO "s2255 unregistered\n");
+ } else {
video_device_release(dev->vdev[i]);
+ printk(KERN_INFO "s2255 released\n");
+ }
}
}
@@ -1775,133 +1865,122 @@
* function again).
*
* Received frame structure:
- * bytes 0-3: marker : 0x2255DA4AL (FRAME_MARKER)
+ * bytes 0-3: marker : 0x2255DA4AL (S2255_MARKER_FRAME)
* bytes 4-7: channel: 0-3
* bytes 8-11: payload size: size of the frame
* bytes 12-payloadsize+12: frame data
*/
static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info)
{
- static int dbgsync; /* = 0; */
char *pdest;
u32 offset = 0;
- int bsync = 0;
- int btrunc = 0;
+ int bframe = 0;
char *psrc;
unsigned long copy_size;
unsigned long size;
s32 idx = -1;
struct s2255_framei *frm;
unsigned char *pdata;
- unsigned long cur_size;
- int bsearch = 0;
- struct s2255_bufferi *buf;
+
dprintk(100, "buffer to user\n");
idx = dev->cur_frame[dev->cc];
- buf = &dev->buffer[dev->cc];
- frm = &buf->frame[idx];
-
- if (frm->ulState == 0) {
- frm->ulState = 1;
- frm->cur_size = 0;
- bsearch = 1;
- } else if (frm->ulState == 2) {
- /* system frame was not freed */
- dprintk(2, "sys frame not free. overrun ringbuf\n");
- bsearch = 1;
- frm->ulState = 1;
- frm->cur_size = 0;
- }
-
- if (bsearch) {
- if (*(s32 *) pipe_info->transfer_buffer != FRAME_MARKER) {
- u32 jj;
- if (dbgsync == 0) {
- dprintk(3, "not synched, discarding all packets"
- "until marker\n");
-
- dbgsync++;
- }
- pdata = (unsigned char *)pipe_info->transfer_buffer;
- for (jj = 0; jj < (pipe_info->cur_transfer_size - 12);
- jj++) {
- if (*(s32 *) pdata == FRAME_MARKER) {
- int cc;
- dprintk(3,
- "found frame marker at offset:"
- " %d [%x %x]\n", jj, pdata[0],
- pdata[1]);
- offset = jj;
- bsync = 1;
- cc = *(u32 *) (pdata + sizeof(u32));
- if (cc >= MAX_CHANNELS) {
- printk(KERN_ERR
- "bad channel\n");
- return -EINVAL;
- }
- /* reverse it */
- dev->cc = G_chnmap[cc];
- break;
- }
- pdata++;
- }
- if (bsync == 0)
- return -EINVAL;
- } else {
- u32 *pword;
- u32 payload;
- int cc;
- dbgsync = 0;
- bsync = 1;
- pword = (u32 *) pipe_info->transfer_buffer;
- cc = pword[1];
-
- if (cc >= MAX_CHANNELS) {
- printk("invalid channel found. "
- "throwing out data!\n");
- return -EINVAL;
- }
- dev->cc = G_chnmap[cc];
- payload = pword[2];
- if (payload != dev->req_image_size[dev->cc]) {
- dprintk(1, "[%d][%d]unexpected payload: %d"
- "required: %lu \n", cc, dev->cc,
- payload, dev->req_image_size[dev->cc]);
- dev->bad_payload[dev->cc]++;
- /* discard the bad frame */
- return -EINVAL;
- }
-
- }
- }
- /* search done. now find out if should be acquiring
- on this channel */
- if (!dev->b_acquire[dev->cc]) {
- frm->ulState = 0;
- return -EINVAL;
- }
-
- idx = dev->cur_frame[dev->cc];
frm = &dev->buffer[dev->cc].frame[idx];
- if (frm->ulState == 0) {
- frm->ulState = 1;
- frm->cur_size = 0;
- } else if (frm->ulState == 2) {
- /* system frame ring buffer overrun */
- dprintk(2, "sys frame overrun. overwriting frame %d %d\n",
- dev->cc, idx);
- frm->ulState = 1;
+ if (frm->ulState == S2255_READ_IDLE) {
+ int jj;
+ unsigned int cc;
+ s32 *pdword;
+ int payload;
+ /* search for marker codes */
+ pdata = (unsigned char *)pipe_info->transfer_buffer;
+ for (jj = 0; jj < (pipe_info->cur_transfer_size - 12); jj++) {
+ switch (*(s32 *) pdata) {
+ case S2255_MARKER_FRAME:
+ pdword = (s32 *)pdata;
+ dprintk(4, "found frame marker at offset:"
+ " %d [%x %x]\n", jj, pdata[0],
+ pdata[1]);
+ offset = jj + PREFIX_SIZE;
+ bframe = 1;
+ cc = pdword[1];
+ if (cc >= MAX_CHANNELS) {
+ printk(KERN_ERR
+ "bad channel\n");
+ return -EINVAL;
+ }
+ /* reverse it */
+ dev->cc = G_chnmap[cc];
+ payload = pdword[3];
+ if (payload > dev->req_image_size[dev->cc]) {
+ dev->bad_payload[dev->cc]++;
+ /* discard the bad frame */
+ return -EINVAL;
+ }
+ dev->pkt_size[dev->cc] = payload;
+ dev->jpg_size[dev->cc] = pdword[4];
+ break;
+ case S2255_MARKER_RESPONSE:
+ pdword = (s32 *)pdata;
+ pdata += DEF_USB_BLOCK;
+ jj += DEF_USB_BLOCK;
+ if (pdword[1] >= MAX_CHANNELS)
+ break;
+ cc = G_chnmap[pdword[1]];
+ if (!(cc >= 0 && cc < MAX_CHANNELS))
+ break;
+ switch (pdword[2]) {
+ case 0x01:
+ /* check if channel valid */
+ /* set mode ready */
+ dev->setmode_ready[cc] = 1;
+ wake_up(&dev->wait_setmode[cc]);
+ dprintk(5, "setmode ready %d\n", cc);
+ break;
+ case 0x10:
+
+ dev->chn_ready |= (1 << cc);
+ if ((dev->chn_ready & 0x0f) != 0x0f)
+ break;
+ /* all channels ready */
+ printk(KERN_INFO "s2255: fw loaded\n");
+ atomic_set(&dev->fw_data->fw_state,
+ S2255_FW_SUCCESS);
+ wake_up(&dev->fw_data->wait_fw);
+ break;
+ default:
+ printk(KERN_INFO "s2255 unknwn resp\n");
+ }
+ default:
+ pdata++;
+ break;
+ }
+ if (bframe)
+ break;
+ } /* for */
+ if (!bframe)
+ return -EINVAL;
+ }
+
+
+ idx = dev->cur_frame[dev->cc];
+ frm = &dev->buffer[dev->cc].frame[idx];
+
+ /* search done. now find out if should be acquiring on this channel */
+ if (!dev->b_acquire[dev->cc]) {
+ /* we found a frame, but this channel is turned off */
+ frm->ulState = S2255_READ_IDLE;
+ return -EINVAL;
+ }
+
+ if (frm->ulState == S2255_READ_IDLE) {
+ frm->ulState = S2255_READ_FRAME;
frm->cur_size = 0;
}
- if (bsync) {
- /* skip the marker 512 bytes (and offset if out of sync) */
- psrc = (u8 *)pipe_info->transfer_buffer + offset + PREFIX_SIZE;
- } else {
- psrc = (u8 *)pipe_info->transfer_buffer;
- }
+ /* skip the marker 512 bytes (and offset if out of sync) */
+ psrc = (u8 *)pipe_info->transfer_buffer + offset;
+
if (frm->lpvbits == NULL) {
dprintk(1, "s2255 frame buffer == NULL.%p %p %d %d",
@@ -1911,33 +1990,20 @@
pdest = frm->lpvbits + frm->cur_size;
- if (bsync) {
- copy_size =
- (pipe_info->cur_transfer_size - offset) - PREFIX_SIZE;
- if (copy_size > pipe_info->cur_transfer_size) {
- printk("invalid copy size, overflow!\n");
- return -ENOMEM;
- }
- } else {
- copy_size = pipe_info->cur_transfer_size;
- }
+ copy_size = (pipe_info->cur_transfer_size - offset);
- cur_size = frm->cur_size;
- size = dev->req_image_size[dev->cc];
+ size = dev->pkt_size[dev->cc] - PREFIX_SIZE;
- if ((copy_size + cur_size) > size) {
- copy_size = size - cur_size;
- btrunc = 1;
- }
+ /* sanity check on pdest */
+ if ((copy_size + frm->cur_size) < dev->req_image_size[dev->cc])
+ memcpy(pdest, psrc, copy_size);
- memcpy(pdest, psrc, copy_size);
- cur_size += copy_size;
frm->cur_size += copy_size;
- dprintk(50, "cur_size size %lu size %lu \n", cur_size, size);
+ dprintk(4, "cur_size size %lu size %lu \n", frm->cur_size, size);
- if (cur_size >= (size - PREFIX_SIZE)) {
+ if (frm->cur_size >= size) {
+
u32 cc = dev->cc;
- frm->ulState = 2;
dprintk(2, "****************[%d]Buffer[%d]full*************\n",
cc, idx);
dev->last_frame[cc] = dev->cur_frame[cc];
@@ -1946,16 +2012,13 @@
if ((dev->cur_frame[cc] == SYS_FRAMES) ||
(dev->cur_frame[cc] == dev->buffer[cc].dwFrames))
dev->cur_frame[cc] = 0;
-
- /* signal the semaphore for this channel */
+ /* frame ready */
if (dev->b_acquire[cc])
- s2255_got_frame(dev, cc);
+ s2255_got_frame(dev, cc, dev->jpg_size[cc]);
dev->frame_count[cc]++;
- }
- /* frame was truncated */
- if (btrunc) {
- /* return more data to process */
- return EAGAIN;
+ frm->ulState = S2255_READ_IDLE;
+ frm->cur_size = 0;
+
}
/* done successfully */
return 0;
@@ -1974,8 +2037,8 @@
}
/* otherwise copy to the system buffers */
res = save_frame(dev, pipe_info);
- if (res == EAGAIN)
- save_frame(dev, pipe_info);
+ if (res != 0)
+ dprintk(4, "s2255: read callback failed\n");
dprintk(50, "callback read video done\n");
return;
@@ -2095,11 +2158,9 @@
memset(pipe, 0, sizeof(*pipe));
pipe->dev = dev;
- pipe->cur_transfer_size = DEFAULT_PIPE_USBBLOCK;
- pipe->max_transfer_size = MAX_PIPE_USBBLOCK;
+ pipe->cur_transfer_size = S2255_USB_XFER_SIZE;
+ pipe->max_transfer_size = S2255_USB_XFER_SIZE;
- if (pipe->cur_transfer_size > pipe->max_transfer_size)
- pipe->cur_transfer_size = pipe->max_transfer_size;
pipe->transfer_buffer = kzalloc(pipe->max_transfer_size,
GFP_KERNEL);
if (pipe->transfer_buffer == NULL) {
@@ -2119,6 +2180,7 @@
for (j = 0; j < MAX_CHANNELS; j++) {
dev->b_acquire[j] = 0;
dev->mode[j] = mode_def;
+ dev->jc[j].quality = S2255_DEF_JPEG_QUAL;
dev->cur_fmt[j] = &formats[0];
dev->mode[j].restart = 1;
dev->req_image_size[j] = get_transfer_size(&mode_def);
@@ -2323,7 +2385,7 @@
kfree(buffer);
dev->b_acquire[chn] = 0;
- return 0;
+ return res;
}
static void s2255_stop_readpipe(struct s2255_dev *dev)
@@ -2359,8 +2421,10 @@
return;
}
-static void s2255_fwload_start(struct s2255_dev *dev)
+static void s2255_fwload_start(struct s2255_dev *dev, int reset)
{
+ if (reset)
+ s2255_reset_dsppower(dev);
dev->fw_data->fw_size = dev->fw_data->fw->size;
atomic_set(&dev->fw_data->fw_state, S2255_FW_NOTLOADED);
memcpy(dev->fw_data->pfw_data,
@@ -2383,6 +2447,8 @@
struct usb_endpoint_descriptor *endpoint;
int i;
int retval = -ENOMEM;
+ __le32 *pdata;
+ int fw_size;
dprintk(2, "s2255: probe\n");
@@ -2437,6 +2503,8 @@
dev->timer.data = (unsigned long)dev->fw_data;
init_waitqueue_head(&dev->fw_data->wait_fw);
+ for (i = 0; i < MAX_CHANNELS; i++)
+ init_waitqueue_head(&dev->wait_setmode[i]);
dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL);
@@ -2456,16 +2524,30 @@
printk(KERN_ERR "sensoray 2255 failed to get firmware\n");
goto error;
}
+ /* check the firmware is valid */
+ fw_size = dev->fw_data->fw->size;
+ pdata = (__le32 *) &dev->fw_data->fw->data[fw_size - 8];
+ if (*pdata != S2255_FW_MARKER) {
+ printk(KERN_INFO "Firmware invalid.\n");
+ retval = -ENODEV;
+ goto error;
+ } else {
+ /* make sure firmware is the latest */
+ __le32 *pRel;
+ pRel = (__le32 *) &dev->fw_data->fw->data[fw_size - 4];
+ printk(KERN_INFO "s2255 dsp fw version %x\n", *pRel);
+ }
/* loads v4l specific */
s2255_probe_v4l(dev);
+ usb_reset_device(dev->udev);
/* load 2255 board specific */
s2255_board_init(dev);
dprintk(4, "before probe done %p\n", dev);
spin_lock_init(&dev->slock);
- s2255_fwload_start(dev);
+ s2255_fwload_start(dev, 0);
dev_info(&interface->dev, "Sensoray 2255 detected\n");
return 0;
error:
@@ -2476,14 +2558,30 @@
static void s2255_disconnect(struct usb_interface *interface)
{
struct s2255_dev *dev = NULL;
+ int i;
dprintk(1, "s2255: disconnect interface %p\n", interface);
dev = usb_get_intfdata(interface);
+
+ /*
+ * wake up any of the timers to allow open_lock to be
+ * acquired sooner
+ */
+ atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING);
+ wake_up(&dev->fw_data->wait_fw);
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ dev->setmode_ready[i] = 1;
+ wake_up(&dev->wait_setmode[i]);
+ }
+
+ mutex_lock(&dev->open_lock);
+ usb_set_intfdata(interface, NULL);
+ mutex_unlock(&dev->open_lock);
+
if (dev) {
kref_put(&dev->kref, s2255_destroy);
dprintk(1, "s2255drv: disconnect\n");
dev_info(&interface->dev, "s2255usb now disconnected\n");
}
- usb_set_intfdata(interface, NULL);
}
static struct usb_driver s2255_driver = {
diff --git a/drivers/media/video/saa5246a.c b/drivers/media/video/saa5246a.c
index 6ee63e6..4a21b8a 100644
--- a/drivers/media/video/saa5246a.c
+++ b/drivers/media/video/saa5246a.c
@@ -43,136 +43,364 @@
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/i2c.h>
+#include <linux/smp_lock.h>
+#include <linux/mutex.h>
#include <linux/videotext.h>
#include <linux/videodev.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
-#include <linux/mutex.h>
-
-#include "saa5246a.h"
+#include <media/v4l2-i2c-drv-legacy.h>
MODULE_AUTHOR("Michael Geng <linux@MichaelGeng.de>");
MODULE_DESCRIPTION("Philips SAA5246A, SAA5281 Teletext decoder driver");
MODULE_LICENSE("GPL");
+#define MAJOR_VERSION 1 /* driver major version number */
+#define MINOR_VERSION 8 /* driver minor version number */
+
+/* Number of DAUs = number of pages that can be searched at the same time. */
+#define NUM_DAUS 4
+
+#define NUM_ROWS_PER_PAGE 40
+
+/* first column is 0 (not 1) */
+#define POS_TIME_START 32
+#define POS_TIME_END 39
+
+#define POS_HEADER_START 7
+#define POS_HEADER_END 31
+
+/* Returns 'true' if the part of the videotext page described with req contains
+ (at least parts of) the time field */
+#define REQ_CONTAINS_TIME(p_req) \
+ ((p_req)->start <= POS_TIME_END && \
+ (p_req)->end >= POS_TIME_START)
+
+/* Returns 'true' if the part of the videotext page described with req contains
+ (at least parts of) the page header */
+#define REQ_CONTAINS_HEADER(p_req) \
+ ((p_req)->start <= POS_HEADER_END && \
+ (p_req)->end >= POS_HEADER_START)
+
+/*****************************************************************************/
+/* Mode register numbers of the SAA5246A */
+/*****************************************************************************/
+#define SAA5246A_REGISTER_R0 0
+#define SAA5246A_REGISTER_R1 1
+#define SAA5246A_REGISTER_R2 2
+#define SAA5246A_REGISTER_R3 3
+#define SAA5246A_REGISTER_R4 4
+#define SAA5246A_REGISTER_R5 5
+#define SAA5246A_REGISTER_R6 6
+#define SAA5246A_REGISTER_R7 7
+#define SAA5246A_REGISTER_R8 8
+#define SAA5246A_REGISTER_R9 9
+#define SAA5246A_REGISTER_R10 10
+#define SAA5246A_REGISTER_R11 11
+#define SAA5246A_REGISTER_R11B 11
+
+/* SAA5246A mode registers often autoincrement to the next register.
+ Therefore we use variable argument lists. The following macro indicates
+ the end of a command list. */
+#define COMMAND_END (-1)
+
+/*****************************************************************************/
+/* Contents of the mode registers of the SAA5246A */
+/*****************************************************************************/
+/* Register R0 (Advanced Control) */
+#define R0_SELECT_R11 0x00
+#define R0_SELECT_R11B 0x01
+
+#define R0_PLL_TIME_CONSTANT_LONG 0x00
+#define R0_PLL_TIME_CONSTANT_SHORT 0x02
+
+#define R0_ENABLE_nODD_EVEN_OUTPUT 0x00
+#define R0_DISABLE_nODD_EVEN_OUTPUT 0x04
+
+#define R0_ENABLE_HDR_POLL 0x00
+#define R0_DISABLE_HDR_POLL 0x10
+
+#define R0_DO_NOT_FORCE_nODD_EVEN_LOW_IF_PICTURE_DISPLAYED 0x00
+#define R0_FORCE_nODD_EVEN_LOW_IF_PICTURE_DISPLAYED 0x20
+
+#define R0_NO_FREE_RUN_PLL 0x00
+#define R0_FREE_RUN_PLL 0x40
+
+#define R0_NO_AUTOMATIC_FASTEXT_PROMPT 0x00
+#define R0_AUTOMATIC_FASTEXT_PROMPT 0x80
+
+/* Register R1 (Mode) */
+#define R1_INTERLACED_312_AND_HALF_312_AND_HALF_LINES 0x00
+#define R1_NON_INTERLACED_312_313_LINES 0x01
+#define R1_NON_INTERLACED_312_312_LINES 0x02
+#define R1_FFB_LEADING_EDGE_IN_FIRST_BROAD_PULSE 0x03
+#define R1_FFB_LEADING_EDGE_IN_SECOND_BROAD_PULSE 0x07
+
+#define R1_DEW 0x00
+#define R1_FULL_FIELD 0x08
+
+#define R1_EXTENDED_PACKET_DISABLE 0x00
+#define R1_EXTENDED_PACKET_ENABLE 0x10
+
+#define R1_DAUS_ALL_ON 0x00
+#define R1_DAUS_ALL_OFF 0x20
+
+#define R1_7_BITS_PLUS_PARITY 0x00
+#define R1_8_BITS_NO_PARITY 0x40
+
+#define R1_VCS_TO_SCS 0x00
+#define R1_NO_VCS_TO_SCS 0x80
+
+/* Register R2 (Page request address) */
+#define R2_IN_R3_SELECT_PAGE_HUNDREDS 0x00
+#define R2_IN_R3_SELECT_PAGE_TENS 0x01
+#define R2_IN_R3_SELECT_PAGE_UNITS 0x02
+#define R2_IN_R3_SELECT_HOURS_TENS 0x03
+#define R2_IN_R3_SELECT_HOURS_UNITS 0x04
+#define R2_IN_R3_SELECT_MINUTES_TENS 0x05
+#define R2_IN_R3_SELECT_MINUTES_UNITS 0x06
+
+#define R2_DAU_0 0x00
+#define R2_DAU_1 0x10
+#define R2_DAU_2 0x20
+#define R2_DAU_3 0x30
+
+#define R2_BANK_0 0x00
+#define R2_BANK 1 0x40
+
+#define R2_HAMMING_CHECK_ON 0x80
+#define R2_HAMMING_CHECK_OFF 0x00
+
+/* Register R3 (Page request data) */
+#define R3_PAGE_HUNDREDS_0 0x00
+#define R3_PAGE_HUNDREDS_1 0x01
+#define R3_PAGE_HUNDREDS_2 0x02
+#define R3_PAGE_HUNDREDS_3 0x03
+#define R3_PAGE_HUNDREDS_4 0x04
+#define R3_PAGE_HUNDREDS_5 0x05
+#define R3_PAGE_HUNDREDS_6 0x06
+#define R3_PAGE_HUNDREDS_7 0x07
+
+#define R3_HOLD_PAGE 0x00
+#define R3_UPDATE_PAGE 0x08
+
+#define R3_PAGE_HUNDREDS_DO_NOT_CARE 0x00
+#define R3_PAGE_HUNDREDS_DO_CARE 0x10
+
+#define R3_PAGE_TENS_DO_NOT_CARE 0x00
+#define R3_PAGE_TENS_DO_CARE 0x10
+
+#define R3_PAGE_UNITS_DO_NOT_CARE 0x00
+#define R3_PAGE_UNITS_DO_CARE 0x10
+
+#define R3_HOURS_TENS_DO_NOT_CARE 0x00
+#define R3_HOURS_TENS_DO_CARE 0x10
+
+#define R3_HOURS_UNITS_DO_NOT_CARE 0x00
+#define R3_HOURS_UNITS_DO_CARE 0x10
+
+#define R3_MINUTES_TENS_DO_NOT_CARE 0x00
+#define R3_MINUTES_TENS_DO_CARE 0x10
+
+#define R3_MINUTES_UNITS_DO_NOT_CARE 0x00
+#define R3_MINUTES_UNITS_DO_CARE 0x10
+
+/* Register R4 (Display chapter) */
+#define R4_DISPLAY_PAGE_0 0x00
+#define R4_DISPLAY_PAGE_1 0x01
+#define R4_DISPLAY_PAGE_2 0x02
+#define R4_DISPLAY_PAGE_3 0x03
+#define R4_DISPLAY_PAGE_4 0x04
+#define R4_DISPLAY_PAGE_5 0x05
+#define R4_DISPLAY_PAGE_6 0x06
+#define R4_DISPLAY_PAGE_7 0x07
+
+/* Register R5 (Normal display control) */
+#define R5_PICTURE_INSIDE_BOXING_OFF 0x00
+#define R5_PICTURE_INSIDE_BOXING_ON 0x01
+
+#define R5_PICTURE_OUTSIDE_BOXING_OFF 0x00
+#define R5_PICTURE_OUTSIDE_BOXING_ON 0x02
+
+#define R5_TEXT_INSIDE_BOXING_OFF 0x00
+#define R5_TEXT_INSIDE_BOXING_ON 0x04
+
+#define R5_TEXT_OUTSIDE_BOXING_OFF 0x00
+#define R5_TEXT_OUTSIDE_BOXING_ON 0x08
+
+#define R5_CONTRAST_REDUCTION_INSIDE_BOXING_OFF 0x00
+#define R5_CONTRAST_REDUCTION_INSIDE_BOXING_ON 0x10
+
+#define R5_CONTRAST_REDUCTION_OUTSIDE_BOXING_OFF 0x00
+#define R5_CONTRAST_REDUCTION_OUTSIDE_BOXING_ON 0x20
+
+#define R5_BACKGROUND_COLOR_INSIDE_BOXING_OFF 0x00
+#define R5_BACKGROUND_COLOR_INSIDE_BOXING_ON 0x40
+
+#define R5_BACKGROUND_COLOR_OUTSIDE_BOXING_OFF 0x00
+#define R5_BACKGROUND_COLOR_OUTSIDE_BOXING_ON 0x80
+
+/* Register R6 (Newsflash display) */
+#define R6_NEWSFLASH_PICTURE_INSIDE_BOXING_OFF 0x00
+#define R6_NEWSFLASH_PICTURE_INSIDE_BOXING_ON 0x01
+
+#define R6_NEWSFLASH_PICTURE_OUTSIDE_BOXING_OFF 0x00
+#define R6_NEWSFLASH_PICTURE_OUTSIDE_BOXING_ON 0x02
+
+#define R6_NEWSFLASH_TEXT_INSIDE_BOXING_OFF 0x00
+#define R6_NEWSFLASH_TEXT_INSIDE_BOXING_ON 0x04
+
+#define R6_NEWSFLASH_TEXT_OUTSIDE_BOXING_OFF 0x00
+#define R6_NEWSFLASH_TEXT_OUTSIDE_BOXING_ON 0x08
+
+#define R6_NEWSFLASH_CONTRAST_REDUCTION_INSIDE_BOXING_OFF 0x00
+#define R6_NEWSFLASH_CONTRAST_REDUCTION_INSIDE_BOXING_ON 0x10
+
+#define R6_NEWSFLASH_CONTRAST_REDUCTION_OUTSIDE_BOXING_OFF 0x00
+#define R6_NEWSFLASH_CONTRAST_REDUCTION_OUTSIDE_BOXING_ON 0x20
+
+#define R6_NEWSFLASH_BACKGROUND_COLOR_INSIDE_BOXING_OFF 0x00
+#define R6_NEWSFLASH_BACKGROUND_COLOR_INSIDE_BOXING_ON 0x40
+
+#define R6_NEWSFLASH_BACKGROUND_COLOR_OUTSIDE_BOXING_OFF 0x00
+#define R6_NEWSFLASH_BACKGROUND_COLOR_OUTSIDE_BOXING_ON 0x80
+
+/* Register R7 (Display mode) */
+#define R7_BOX_OFF_ROW_0 0x00
+#define R7_BOX_ON_ROW_0 0x01
+
+#define R7_BOX_OFF_ROW_1_TO_23 0x00
+#define R7_BOX_ON_ROW_1_TO_23 0x02
+
+#define R7_BOX_OFF_ROW_24 0x00
+#define R7_BOX_ON_ROW_24 0x04
+
+#define R7_SINGLE_HEIGHT 0x00
+#define R7_DOUBLE_HEIGHT 0x08
+
+#define R7_TOP_HALF 0x00
+#define R7_BOTTOM_HALF 0x10
+
+#define R7_REVEAL_OFF 0x00
+#define R7_REVEAL_ON 0x20
+
+#define R7_CURSER_OFF 0x00
+#define R7_CURSER_ON 0x40
+
+#define R7_STATUS_BOTTOM 0x00
+#define R7_STATUS_TOP 0x80
+
+/* Register R8 (Active chapter) */
+#define R8_ACTIVE_CHAPTER_0 0x00
+#define R8_ACTIVE_CHAPTER_1 0x01
+#define R8_ACTIVE_CHAPTER_2 0x02
+#define R8_ACTIVE_CHAPTER_3 0x03
+#define R8_ACTIVE_CHAPTER_4 0x04
+#define R8_ACTIVE_CHAPTER_5 0x05
+#define R8_ACTIVE_CHAPTER_6 0x06
+#define R8_ACTIVE_CHAPTER_7 0x07
+
+#define R8_CLEAR_MEMORY 0x08
+#define R8_DO_NOT_CLEAR_MEMORY 0x00
+
+/* Register R9 (Curser row) */
+#define R9_CURSER_ROW_0 0x00
+#define R9_CURSER_ROW_1 0x01
+#define R9_CURSER_ROW_2 0x02
+#define R9_CURSER_ROW_25 0x19
+
+/* Register R10 (Curser column) */
+#define R10_CURSER_COLUMN_0 0x00
+#define R10_CURSER_COLUMN_6 0x06
+#define R10_CURSER_COLUMN_8 0x08
+
+/*****************************************************************************/
+/* Row 25 control data in column 0 to 9 */
+/*****************************************************************************/
+#define ROW25_COLUMN0_PAGE_UNITS 0x0F
+
+#define ROW25_COLUMN1_PAGE_TENS 0x0F
+
+#define ROW25_COLUMN2_MINUTES_UNITS 0x0F
+
+#define ROW25_COLUMN3_MINUTES_TENS 0x07
+#define ROW25_COLUMN3_DELETE_PAGE 0x08
+
+#define ROW25_COLUMN4_HOUR_UNITS 0x0F
+
+#define ROW25_COLUMN5_HOUR_TENS 0x03
+#define ROW25_COLUMN5_INSERT_HEADLINE 0x04
+#define ROW25_COLUMN5_INSERT_SUBTITLE 0x08
+
+#define ROW25_COLUMN6_SUPPRESS_HEADER 0x01
+#define ROW25_COLUMN6_UPDATE_PAGE 0x02
+#define ROW25_COLUMN6_INTERRUPTED_SEQUENCE 0x04
+#define ROW25_COLUMN6_SUPPRESS_DISPLAY 0x08
+
+#define ROW25_COLUMN7_SERIAL_MODE 0x01
+#define ROW25_COLUMN7_CHARACTER_SET 0x0E
+
+#define ROW25_COLUMN8_PAGE_HUNDREDS 0x07
+#define ROW25_COLUMN8_PAGE_NOT_FOUND 0x10
+
+#define ROW25_COLUMN9_PAGE_BEING_LOOKED_FOR 0x20
+
+#define ROW25_COLUMN0_TO_7_HAMMING_ERROR 0x10
+
+/*****************************************************************************/
+/* Helper macros for extracting page, hour and minute digits */
+/*****************************************************************************/
+/* BYTE_POS 0 is at row 0, column 0,
+ BYTE_POS 1 is at row 0, column 1,
+ BYTE_POS 40 is at row 1, column 0, (with NUM_ROWS_PER_PAGE = 40)
+ BYTE_POS 41 is at row 1, column 1, (with NUM_ROWS_PER_PAGE = 40),
+ ... */
+#define ROW(BYTE_POS) (BYTE_POS / NUM_ROWS_PER_PAGE)
+#define COLUMN(BYTE_POS) (BYTE_POS % NUM_ROWS_PER_PAGE)
+
+/*****************************************************************************/
+/* Helper macros for extracting page, hour and minute digits */
+/*****************************************************************************/
+/* Macros for extracting hundreds, tens and units of a page number which
+ must be in the range 0 ... 0x799.
+ Note that page is coded in hexadecimal, i.e. 0x123 means page 123.
+ page 0x.. means page 8.. */
+#define HUNDREDS_OF_PAGE(page) (((page) / 0x100) & 0x7)
+#define TENS_OF_PAGE(page) (((page) / 0x10) & 0xF)
+#define UNITS_OF_PAGE(page) ((page) & 0xF)
+
+/* Macros for extracting tens and units of a hour information which
+ must be in the range 0 ... 0x24.
+ Note that hour is coded in hexadecimal, i.e. 0x12 means 12 hours */
+#define TENS_OF_HOUR(hour) ((hour) / 0x10)
+#define UNITS_OF_HOUR(hour) ((hour) & 0xF)
+
+/* Macros for extracting tens and units of a minute information which
+ must be in the range 0 ... 0x59.
+ Note that minute is coded in hexadecimal, i.e. 0x12 means 12 minutes */
+#define TENS_OF_MINUTE(minute) ((minute) / 0x10)
+#define UNITS_OF_MINUTE(minute) ((minute) & 0xF)
+
+#define HOUR_MAX 0x23
+#define MINUTE_MAX 0x59
+#define PAGE_MAX 0x8FF
+
+
struct saa5246a_device
{
u8 pgbuf[NUM_DAUS][VTX_VIRTUALSIZE];
int is_searching[NUM_DAUS];
struct i2c_client *client;
+ unsigned long in_use;
struct mutex lock;
};
static struct video_device saa_template; /* Declared near bottom */
-/* Addresses to scan */
-static unsigned short normal_i2c[] = { I2C_ADDRESS, I2C_CLIENT_END };
-
-I2C_CLIENT_INSMOD;
-
-static struct i2c_client client_template;
-
-static int saa5246a_attach(struct i2c_adapter *adap, int addr, int kind)
-{
- int pgbuf;
- int err;
- struct i2c_client *client;
- struct video_device *vd;
- struct saa5246a_device *t;
-
- printk(KERN_INFO "saa5246a: teletext chip found.\n");
- client=kmalloc(sizeof(*client), GFP_KERNEL);
- if(client==NULL)
- return -ENOMEM;
- client_template.adapter = adap;
- client_template.addr = addr;
- memcpy(client, &client_template, sizeof(*client));
- t = kzalloc(sizeof(*t), GFP_KERNEL);
- if(t==NULL)
- {
- kfree(client);
- return -ENOMEM;
- }
- strlcpy(client->name, IF_NAME, I2C_NAME_SIZE);
- mutex_init(&t->lock);
-
- /*
- * Now create a video4linux device
- */
-
- vd = video_device_alloc();
- if(vd==NULL)
- {
- kfree(t);
- kfree(client);
- return -ENOMEM;
- }
- i2c_set_clientdata(client, vd);
- memcpy(vd, &saa_template, sizeof(*vd));
-
- for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++)
- {
- memset(t->pgbuf[pgbuf], ' ', sizeof(t->pgbuf[0]));
- t->is_searching[pgbuf] = false;
- }
- vd->priv=t;
-
-
- /*
- * Register it
- */
-
- if((err=video_register_device(vd, VFL_TYPE_VTX,-1))<0)
- {
- kfree(t);
- kfree(client);
- video_device_release(vd);
- return err;
- }
- t->client = client;
- i2c_attach_client(client);
- return 0;
-}
-
-/*
- * We do most of the hard work when we become a device on the i2c.
- */
-static int saa5246a_probe(struct i2c_adapter *adap)
-{
- if (adap->class & I2C_CLASS_TV_ANALOG)
- return i2c_probe(adap, &addr_data, saa5246a_attach);
- return 0;
-}
-
-static int saa5246a_detach(struct i2c_client *client)
-{
- struct video_device *vd = i2c_get_clientdata(client);
- i2c_detach_client(client);
- video_unregister_device(vd);
- kfree(vd->priv);
- kfree(client);
- return 0;
-}
-
/*
* I2C interfaces
*/
-static struct i2c_driver i2c_driver_videotext =
-{
- .driver = {
- .name = IF_NAME, /* name */
- },
- .id = I2C_DRIVERID_SAA5249, /* in i2c.h */
- .attach_adapter = saa5246a_probe,
- .detach_client = saa5246a_detach,
-};
-
-static struct i2c_client client_template = {
- .driver = &i2c_driver_videotext,
- .name = "(unset)",
-};
-
static int i2c_sendbuf(struct saa5246a_device *t, int reg, int count, u8 *data)
{
char buf[64];
@@ -579,8 +807,8 @@
static int do_saa5246a_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, void *arg)
{
- struct video_device *vd = video_devdata(file);
- struct saa5246a_device *t=vd->priv;
+ struct saa5246a_device *t = video_drvdata(file);
+
switch(cmd)
{
case VTXIOCGETINFO:
@@ -720,8 +948,7 @@
static int saa5246a_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- struct video_device *vd = video_devdata(file);
- struct saa5246a_device *t = vd->priv;
+ struct saa5246a_device *t = video_drvdata(file);
int err;
cmd = vtx_fix_command(cmd);
@@ -733,21 +960,15 @@
static int saa5246a_open(struct inode *inode, struct file *file)
{
- struct video_device *vd = video_devdata(file);
- struct saa5246a_device *t = vd->priv;
- int err;
+ struct saa5246a_device *t = video_drvdata(file);
- err = video_exclusive_open(inode,file);
- if (err < 0)
- return err;
+ if (t->client == NULL)
+ return -ENODEV;
- if (t->client==NULL) {
- err = -ENODEV;
- goto fail;
- }
+ if (test_and_set_bit(0, &t->in_use))
+ return -EBUSY;
if (i2c_senddata(t, SAA5246A_REGISTER_R0,
-
R0_SELECT_R11 |
R0_PLL_TIME_CONSTANT_LONG |
R0_ENABLE_nODD_EVEN_OUTPUT |
@@ -773,21 +994,15 @@
COMMAND_END))
{
- err = -EIO;
- goto fail;
+ clear_bit(0, &t->in_use);
+ return -EIO;
}
-
return 0;
-
-fail:
- video_exclusive_release(inode,file);
- return err;
}
static int saa5246a_release(struct inode *inode, struct file *file)
{
- struct video_device *vd = video_devdata(file);
- struct saa5246a_device *t = vd->priv;
+ struct saa5246a_device *t = video_drvdata(file);
/* Stop all acquisition circuits. */
i2c_senddata(t, SAA5246A_REGISTER_R1,
@@ -800,26 +1015,10 @@
R1_VCS_TO_SCS,
COMMAND_END);
- video_exclusive_release(inode,file);
+ clear_bit(0, &t->in_use);
return 0;
}
-static int __init init_saa_5246a (void)
-{
- printk(KERN_INFO
- "SAA5246A (or compatible) Teletext decoder driver version %d.%d\n",
- MAJOR_VERSION, MINOR_VERSION);
- return i2c_add_driver(&i2c_driver_videotext);
-}
-
-static void __exit cleanup_saa_5246a (void)
-{
- i2c_del_driver(&i2c_driver_videotext);
-}
-
-module_init(init_saa_5246a);
-module_exit(cleanup_saa_5246a);
-
static const struct file_operations saa_fops = {
.owner = THIS_MODULE,
.open = saa5246a_open,
@@ -830,8 +1029,79 @@
static struct video_device saa_template =
{
- .name = IF_NAME,
+ .name = "saa5246a",
.fops = &saa_fops,
.release = video_device_release,
.minor = -1,
};
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { 0x22 >> 1, I2C_CLIENT_END };
+
+I2C_CLIENT_INSMOD;
+
+static int saa5246a_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int pgbuf;
+ int err;
+ struct video_device *vd;
+ struct saa5246a_device *t;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+ v4l_info(client, "VideoText version %d.%d\n",
+ MAJOR_VERSION, MINOR_VERSION);
+ t = kzalloc(sizeof(*t), GFP_KERNEL);
+ if (t == NULL)
+ return -ENOMEM;
+ mutex_init(&t->lock);
+
+ /* Now create a video4linux device */
+ vd = video_device_alloc();
+ if (vd == NULL) {
+ kfree(t);
+ return -ENOMEM;
+ }
+ i2c_set_clientdata(client, vd);
+ memcpy(vd, &saa_template, sizeof(*vd));
+
+ for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) {
+ memset(t->pgbuf[pgbuf], ' ', sizeof(t->pgbuf[0]));
+ t->is_searching[pgbuf] = false;
+ }
+ video_set_drvdata(vd, t);
+
+ /* Register it */
+ err = video_register_device(vd, VFL_TYPE_VTX, -1);
+ if (err < 0) {
+ kfree(t);
+ video_device_release(vd);
+ return err;
+ }
+ t->client = client;
+ return 0;
+}
+
+static int saa5246a_remove(struct i2c_client *client)
+{
+ struct video_device *vd = i2c_get_clientdata(client);
+
+ video_unregister_device(vd);
+ kfree(video_get_drvdata(vd));
+ return 0;
+}
+
+static const struct i2c_device_id saa5246a_id[] = {
+ { "saa5246a", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, saa5246a_id);
+
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "saa5246a",
+ .driverid = I2C_DRIVERID_SAA5249,
+ .probe = saa5246a_probe,
+ .remove = saa5246a_remove,
+ .id_table = saa5246a_id,
+};
diff --git a/drivers/media/video/saa5246a.h b/drivers/media/video/saa5246a.h
deleted file mode 100644
index 64394c0..0000000
--- a/drivers/media/video/saa5246a.h
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- Driver for the SAA5246A or SAA5281 Teletext (=Videotext) decoder chips from
- Philips.
-
- Copyright (C) 2004 Michael Geng (linux@MichaelGeng.de)
-
- 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., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- */
-#ifndef __SAA5246A_H__
-#define __SAA5246A_H__
-
-#define MAJOR_VERSION 1 /* driver major version number */
-#define MINOR_VERSION 8 /* driver minor version number */
-
-#define IF_NAME "SAA5246A"
-
-#define I2C_ADDRESS 17
-
-/* Number of DAUs = number of pages that can be searched at the same time. */
-#define NUM_DAUS 4
-
-#define NUM_ROWS_PER_PAGE 40
-
-/* first column is 0 (not 1) */
-#define POS_TIME_START 32
-#define POS_TIME_END 39
-
-#define POS_HEADER_START 7
-#define POS_HEADER_END 31
-
-/* Returns 'true' if the part of the videotext page described with req contains
- (at least parts of) the time field */
-#define REQ_CONTAINS_TIME(p_req) \
- ((p_req)->start <= POS_TIME_END && \
- (p_req)->end >= POS_TIME_START)
-
-/* Returns 'true' if the part of the videotext page described with req contains
- (at least parts of) the page header */
-#define REQ_CONTAINS_HEADER(p_req) \
- ((p_req)->start <= POS_HEADER_END && \
- (p_req)->end >= POS_HEADER_START)
-
-/*****************************************************************************/
-/* Mode register numbers of the SAA5246A */
-/*****************************************************************************/
-#define SAA5246A_REGISTER_R0 0
-#define SAA5246A_REGISTER_R1 1
-#define SAA5246A_REGISTER_R2 2
-#define SAA5246A_REGISTER_R3 3
-#define SAA5246A_REGISTER_R4 4
-#define SAA5246A_REGISTER_R5 5
-#define SAA5246A_REGISTER_R6 6
-#define SAA5246A_REGISTER_R7 7
-#define SAA5246A_REGISTER_R8 8
-#define SAA5246A_REGISTER_R9 9
-#define SAA5246A_REGISTER_R10 10
-#define SAA5246A_REGISTER_R11 11
-#define SAA5246A_REGISTER_R11B 11
-
-/* SAA5246A mode registers often autoincrement to the next register.
- Therefore we use variable argument lists. The following macro indicates
- the end of a command list. */
-#define COMMAND_END (- 1)
-
-/*****************************************************************************/
-/* Contents of the mode registers of the SAA5246A */
-/*****************************************************************************/
-/* Register R0 (Advanced Control) */
-#define R0_SELECT_R11 0x00
-#define R0_SELECT_R11B 0x01
-
-#define R0_PLL_TIME_CONSTANT_LONG 0x00
-#define R0_PLL_TIME_CONSTANT_SHORT 0x02
-
-#define R0_ENABLE_nODD_EVEN_OUTPUT 0x00
-#define R0_DISABLE_nODD_EVEN_OUTPUT 0x04
-
-#define R0_ENABLE_HDR_POLL 0x00
-#define R0_DISABLE_HDR_POLL 0x10
-
-#define R0_DO_NOT_FORCE_nODD_EVEN_LOW_IF_PICTURE_DISPLAYED 0x00
-#define R0_FORCE_nODD_EVEN_LOW_IF_PICTURE_DISPLAYED 0x20
-
-#define R0_NO_FREE_RUN_PLL 0x00
-#define R0_FREE_RUN_PLL 0x40
-
-#define R0_NO_AUTOMATIC_FASTEXT_PROMPT 0x00
-#define R0_AUTOMATIC_FASTEXT_PROMPT 0x80
-
-/* Register R1 (Mode) */
-#define R1_INTERLACED_312_AND_HALF_312_AND_HALF_LINES 0x00
-#define R1_NON_INTERLACED_312_313_LINES 0x01
-#define R1_NON_INTERLACED_312_312_LINES 0x02
-#define R1_FFB_LEADING_EDGE_IN_FIRST_BROAD_PULSE 0x03
-#define R1_FFB_LEADING_EDGE_IN_SECOND_BROAD_PULSE 0x07
-
-#define R1_DEW 0x00
-#define R1_FULL_FIELD 0x08
-
-#define R1_EXTENDED_PACKET_DISABLE 0x00
-#define R1_EXTENDED_PACKET_ENABLE 0x10
-
-#define R1_DAUS_ALL_ON 0x00
-#define R1_DAUS_ALL_OFF 0x20
-
-#define R1_7_BITS_PLUS_PARITY 0x00
-#define R1_8_BITS_NO_PARITY 0x40
-
-#define R1_VCS_TO_SCS 0x00
-#define R1_NO_VCS_TO_SCS 0x80
-
-/* Register R2 (Page request address) */
-#define R2_IN_R3_SELECT_PAGE_HUNDREDS 0x00
-#define R2_IN_R3_SELECT_PAGE_TENS 0x01
-#define R2_IN_R3_SELECT_PAGE_UNITS 0x02
-#define R2_IN_R3_SELECT_HOURS_TENS 0x03
-#define R2_IN_R3_SELECT_HOURS_UNITS 0x04
-#define R2_IN_R3_SELECT_MINUTES_TENS 0x05
-#define R2_IN_R3_SELECT_MINUTES_UNITS 0x06
-
-#define R2_DAU_0 0x00
-#define R2_DAU_1 0x10
-#define R2_DAU_2 0x20
-#define R2_DAU_3 0x30
-
-#define R2_BANK_0 0x00
-#define R2_BANK 1 0x40
-
-#define R2_HAMMING_CHECK_ON 0x80
-#define R2_HAMMING_CHECK_OFF 0x00
-
-/* Register R3 (Page request data) */
-#define R3_PAGE_HUNDREDS_0 0x00
-#define R3_PAGE_HUNDREDS_1 0x01
-#define R3_PAGE_HUNDREDS_2 0x02
-#define R3_PAGE_HUNDREDS_3 0x03
-#define R3_PAGE_HUNDREDS_4 0x04
-#define R3_PAGE_HUNDREDS_5 0x05
-#define R3_PAGE_HUNDREDS_6 0x06
-#define R3_PAGE_HUNDREDS_7 0x07
-
-#define R3_HOLD_PAGE 0x00
-#define R3_UPDATE_PAGE 0x08
-
-#define R3_PAGE_HUNDREDS_DO_NOT_CARE 0x00
-#define R3_PAGE_HUNDREDS_DO_CARE 0x10
-
-#define R3_PAGE_TENS_DO_NOT_CARE 0x00
-#define R3_PAGE_TENS_DO_CARE 0x10
-
-#define R3_PAGE_UNITS_DO_NOT_CARE 0x00
-#define R3_PAGE_UNITS_DO_CARE 0x10
-
-#define R3_HOURS_TENS_DO_NOT_CARE 0x00
-#define R3_HOURS_TENS_DO_CARE 0x10
-
-#define R3_HOURS_UNITS_DO_NOT_CARE 0x00
-#define R3_HOURS_UNITS_DO_CARE 0x10
-
-#define R3_MINUTES_TENS_DO_NOT_CARE 0x00
-#define R3_MINUTES_TENS_DO_CARE 0x10
-
-#define R3_MINUTES_UNITS_DO_NOT_CARE 0x00
-#define R3_MINUTES_UNITS_DO_CARE 0x10
-
-/* Register R4 (Display chapter) */
-#define R4_DISPLAY_PAGE_0 0x00
-#define R4_DISPLAY_PAGE_1 0x01
-#define R4_DISPLAY_PAGE_2 0x02
-#define R4_DISPLAY_PAGE_3 0x03
-#define R4_DISPLAY_PAGE_4 0x04
-#define R4_DISPLAY_PAGE_5 0x05
-#define R4_DISPLAY_PAGE_6 0x06
-#define R4_DISPLAY_PAGE_7 0x07
-
-/* Register R5 (Normal display control) */
-#define R5_PICTURE_INSIDE_BOXING_OFF 0x00
-#define R5_PICTURE_INSIDE_BOXING_ON 0x01
-
-#define R5_PICTURE_OUTSIDE_BOXING_OFF 0x00
-#define R5_PICTURE_OUTSIDE_BOXING_ON 0x02
-
-#define R5_TEXT_INSIDE_BOXING_OFF 0x00
-#define R5_TEXT_INSIDE_BOXING_ON 0x04
-
-#define R5_TEXT_OUTSIDE_BOXING_OFF 0x00
-#define R5_TEXT_OUTSIDE_BOXING_ON 0x08
-
-#define R5_CONTRAST_REDUCTION_INSIDE_BOXING_OFF 0x00
-#define R5_CONTRAST_REDUCTION_INSIDE_BOXING_ON 0x10
-
-#define R5_CONTRAST_REDUCTION_OUTSIDE_BOXING_OFF 0x00
-#define R5_CONTRAST_REDUCTION_OUTSIDE_BOXING_ON 0x20
-
-#define R5_BACKGROUND_COLOR_INSIDE_BOXING_OFF 0x00
-#define R5_BACKGROUND_COLOR_INSIDE_BOXING_ON 0x40
-
-#define R5_BACKGROUND_COLOR_OUTSIDE_BOXING_OFF 0x00
-#define R5_BACKGROUND_COLOR_OUTSIDE_BOXING_ON 0x80
-
-/* Register R6 (Newsflash display) */
-#define R6_NEWSFLASH_PICTURE_INSIDE_BOXING_OFF 0x00
-#define R6_NEWSFLASH_PICTURE_INSIDE_BOXING_ON 0x01
-
-#define R6_NEWSFLASH_PICTURE_OUTSIDE_BOXING_OFF 0x00
-#define R6_NEWSFLASH_PICTURE_OUTSIDE_BOXING_ON 0x02
-
-#define R6_NEWSFLASH_TEXT_INSIDE_BOXING_OFF 0x00
-#define R6_NEWSFLASH_TEXT_INSIDE_BOXING_ON 0x04
-
-#define R6_NEWSFLASH_TEXT_OUTSIDE_BOXING_OFF 0x00
-#define R6_NEWSFLASH_TEXT_OUTSIDE_BOXING_ON 0x08
-
-#define R6_NEWSFLASH_CONTRAST_REDUCTION_INSIDE_BOXING_OFF 0x00
-#define R6_NEWSFLASH_CONTRAST_REDUCTION_INSIDE_BOXING_ON 0x10
-
-#define R6_NEWSFLASH_CONTRAST_REDUCTION_OUTSIDE_BOXING_OFF 0x00
-#define R6_NEWSFLASH_CONTRAST_REDUCTION_OUTSIDE_BOXING_ON 0x20
-
-#define R6_NEWSFLASH_BACKGROUND_COLOR_INSIDE_BOXING_OFF 0x00
-#define R6_NEWSFLASH_BACKGROUND_COLOR_INSIDE_BOXING_ON 0x40
-
-#define R6_NEWSFLASH_BACKGROUND_COLOR_OUTSIDE_BOXING_OFF 0x00
-#define R6_NEWSFLASH_BACKGROUND_COLOR_OUTSIDE_BOXING_ON 0x80
-
-/* Register R7 (Display mode) */
-#define R7_BOX_OFF_ROW_0 0x00
-#define R7_BOX_ON_ROW_0 0x01
-
-#define R7_BOX_OFF_ROW_1_TO_23 0x00
-#define R7_BOX_ON_ROW_1_TO_23 0x02
-
-#define R7_BOX_OFF_ROW_24 0x00
-#define R7_BOX_ON_ROW_24 0x04
-
-#define R7_SINGLE_HEIGHT 0x00
-#define R7_DOUBLE_HEIGHT 0x08
-
-#define R7_TOP_HALF 0x00
-#define R7_BOTTOM_HALF 0x10
-
-#define R7_REVEAL_OFF 0x00
-#define R7_REVEAL_ON 0x20
-
-#define R7_CURSER_OFF 0x00
-#define R7_CURSER_ON 0x40
-
-#define R7_STATUS_BOTTOM 0x00
-#define R7_STATUS_TOP 0x80
-
-/* Register R8 (Active chapter) */
-#define R8_ACTIVE_CHAPTER_0 0x00
-#define R8_ACTIVE_CHAPTER_1 0x01
-#define R8_ACTIVE_CHAPTER_2 0x02
-#define R8_ACTIVE_CHAPTER_3 0x03
-#define R8_ACTIVE_CHAPTER_4 0x04
-#define R8_ACTIVE_CHAPTER_5 0x05
-#define R8_ACTIVE_CHAPTER_6 0x06
-#define R8_ACTIVE_CHAPTER_7 0x07
-
-#define R8_CLEAR_MEMORY 0x08
-#define R8_DO_NOT_CLEAR_MEMORY 0x00
-
-/* Register R9 (Curser row) */
-#define R9_CURSER_ROW_0 0x00
-#define R9_CURSER_ROW_1 0x01
-#define R9_CURSER_ROW_2 0x02
-#define R9_CURSER_ROW_25 0x19
-
-/* Register R10 (Curser column) */
-#define R10_CURSER_COLUMN_0 0x00
-#define R10_CURSER_COLUMN_6 0x06
-#define R10_CURSER_COLUMN_8 0x08
-
-/*****************************************************************************/
-/* Row 25 control data in column 0 to 9 */
-/*****************************************************************************/
-#define ROW25_COLUMN0_PAGE_UNITS 0x0F
-
-#define ROW25_COLUMN1_PAGE_TENS 0x0F
-
-#define ROW25_COLUMN2_MINUTES_UNITS 0x0F
-
-#define ROW25_COLUMN3_MINUTES_TENS 0x07
-#define ROW25_COLUMN3_DELETE_PAGE 0x08
-
-#define ROW25_COLUMN4_HOUR_UNITS 0x0F
-
-#define ROW25_COLUMN5_HOUR_TENS 0x03
-#define ROW25_COLUMN5_INSERT_HEADLINE 0x04
-#define ROW25_COLUMN5_INSERT_SUBTITLE 0x08
-
-#define ROW25_COLUMN6_SUPPRESS_HEADER 0x01
-#define ROW25_COLUMN6_UPDATE_PAGE 0x02
-#define ROW25_COLUMN6_INTERRUPTED_SEQUENCE 0x04
-#define ROW25_COLUMN6_SUPPRESS_DISPLAY 0x08
-
-#define ROW25_COLUMN7_SERIAL_MODE 0x01
-#define ROW25_COLUMN7_CHARACTER_SET 0x0E
-
-#define ROW25_COLUMN8_PAGE_HUNDREDS 0x07
-#define ROW25_COLUMN8_PAGE_NOT_FOUND 0x10
-
-#define ROW25_COLUMN9_PAGE_BEING_LOOKED_FOR 0x20
-
-#define ROW25_COLUMN0_TO_7_HAMMING_ERROR 0x10
-
-/*****************************************************************************/
-/* Helper macros for extracting page, hour and minute digits */
-/*****************************************************************************/
-/* BYTE_POS 0 is at row 0, column 0,
- BYTE_POS 1 is at row 0, column 1,
- BYTE_POS 40 is at row 1, column 0, (with NUM_ROWS_PER_PAGE = 40)
- BYTE_POS 41 is at row 1, column 1, (with NUM_ROWS_PER_PAGE = 40),
- ... */
-#define ROW(BYTE_POS) (BYTE_POS / NUM_ROWS_PER_PAGE)
-#define COLUMN(BYTE_POS) (BYTE_POS % NUM_ROWS_PER_PAGE)
-
-/*****************************************************************************/
-/* Helper macros for extracting page, hour and minute digits */
-/*****************************************************************************/
-/* Macros for extracting hundreds, tens and units of a page number which
- must be in the range 0 ... 0x799.
- Note that page is coded in hexadecimal, i.e. 0x123 means page 123.
- page 0x.. means page 8.. */
-#define HUNDREDS_OF_PAGE(page) (((page) / 0x100) & 0x7)
-#define TENS_OF_PAGE(page) (((page) / 0x10) & 0xF)
-#define UNITS_OF_PAGE(page) ((page) & 0xF)
-
-/* Macros for extracting tens and units of a hour information which
- must be in the range 0 ... 0x24.
- Note that hour is coded in hexadecimal, i.e. 0x12 means 12 hours */
-#define TENS_OF_HOUR(hour) ((hour) / 0x10)
-#define UNITS_OF_HOUR(hour) ((hour) & 0xF)
-
-/* Macros for extracting tens and units of a minute information which
- must be in the range 0 ... 0x59.
- Note that minute is coded in hexadecimal, i.e. 0x12 means 12 minutes */
-#define TENS_OF_MINUTE(minute) ((minute) / 0x10)
-#define UNITS_OF_MINUTE(minute) ((minute) & 0xF)
-
-#define HOUR_MAX 0x23
-#define MINUTE_MAX 0x59
-#define PAGE_MAX 0x8FF
-
-#endif /* __SAA5246A_H__ */
diff --git a/drivers/media/video/saa5249.c b/drivers/media/video/saa5249.c
index 0d63973..3bb959c 100644
--- a/drivers/media/video/saa5249.c
+++ b/drivers/media/video/saa5249.c
@@ -15,8 +15,6 @@
*
* Copyright (c) 1998 Richard Guenther <richard.guenther@student.uni-tuebingen.de>
*
- * $Id: saa5249.c,v 1.1 1998/03/30 22:23:23 alan Exp $
- *
* Derived From
*
* vtx.c:
@@ -45,33 +43,28 @@
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/sched.h>
#include <linux/mm.h>
-#include <linux/errno.h>
-#include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/slab.h>
#include <linux/init.h>
-#include <stdarg.h>
#include <linux/i2c.h>
+#include <linux/smp_lock.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
#include <linux/videotext.h>
#include <linux/videodev.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
-#include <linux/mutex.h>
+#include <media/v4l2-i2c-drv-legacy.h>
-
-#include <asm/io.h>
-#include <asm/uaccess.h>
+MODULE_AUTHOR("Michael Geng <linux@MichaelGeng.de>");
+MODULE_DESCRIPTION("Philips SAA5249 Teletext decoder driver");
+MODULE_LICENSE("GPL");
#define VTX_VER_MAJ 1
#define VTX_VER_MIN 8
-
#define NUM_DAUS 4
#define NUM_BUFS 8
-#define IF_NAME "SAA5249"
static const int disp_modes[8][3] =
{
@@ -109,6 +102,7 @@
int disp_mode;
int virtual_mode;
struct i2c_client *client;
+ unsigned long in_use;
struct mutex lock;
};
@@ -123,125 +117,8 @@
#define VTX_DEV_MINOR 0
-/* General defines and debugging support */
-
-#define RESCHED do { cond_resched(); } while(0)
-
static struct video_device saa_template; /* Declared near bottom */
-/* Addresses to scan */
-static unsigned short normal_i2c[] = {34>>1,I2C_CLIENT_END};
-
-I2C_CLIENT_INSMOD;
-
-static struct i2c_client client_template;
-
-static int saa5249_attach(struct i2c_adapter *adap, int addr, int kind)
-{
- int pgbuf;
- int err;
- struct i2c_client *client;
- struct video_device *vd;
- struct saa5249_device *t;
-
- printk(KERN_INFO "saa5249: teletext chip found.\n");
- client=kmalloc(sizeof(*client), GFP_KERNEL);
- if(client==NULL)
- return -ENOMEM;
- client_template.adapter = adap;
- client_template.addr = addr;
- memcpy(client, &client_template, sizeof(*client));
- t = kzalloc(sizeof(*t), GFP_KERNEL);
- if(t==NULL)
- {
- kfree(client);
- return -ENOMEM;
- }
- strlcpy(client->name, IF_NAME, I2C_NAME_SIZE);
- mutex_init(&t->lock);
-
- /*
- * Now create a video4linux device
- */
-
- vd = kmalloc(sizeof(struct video_device), GFP_KERNEL);
- if(vd==NULL)
- {
- kfree(t);
- kfree(client);
- return -ENOMEM;
- }
- i2c_set_clientdata(client, vd);
- memcpy(vd, &saa_template, sizeof(*vd));
-
- for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++)
- {
- memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf));
- memset(t->vdau[pgbuf].sregs, 0, sizeof(t->vdau[0].sregs));
- memset(t->vdau[pgbuf].laststat, 0, sizeof(t->vdau[0].laststat));
- t->vdau[pgbuf].expire = 0;
- t->vdau[pgbuf].clrfound = true;
- t->vdau[pgbuf].stopped = true;
- t->is_searching[pgbuf] = false;
- }
- vd->priv=t;
-
-
- /*
- * Register it
- */
-
- if((err=video_register_device(vd, VFL_TYPE_VTX,-1))<0)
- {
- kfree(t);
- kfree(vd);
- kfree(client);
- return err;
- }
- t->client = client;
- i2c_attach_client(client);
- return 0;
-}
-
-/*
- * We do most of the hard work when we become a device on the i2c.
- */
-
-static int saa5249_probe(struct i2c_adapter *adap)
-{
- if (adap->class & I2C_CLASS_TV_ANALOG)
- return i2c_probe(adap, &addr_data, saa5249_attach);
- return 0;
-}
-
-static int saa5249_detach(struct i2c_client *client)
-{
- struct video_device *vd = i2c_get_clientdata(client);
- i2c_detach_client(client);
- video_unregister_device(vd);
- kfree(vd->priv);
- kfree(vd);
- kfree(client);
- return 0;
-}
-
-/* new I2C driver support */
-
-static struct i2c_driver i2c_driver_videotext =
-{
- .driver = {
- .name = IF_NAME, /* name */
- },
- .id = I2C_DRIVERID_SAA5249, /* in i2c.h */
- .attach_adapter = saa5249_probe,
- .detach_client = saa5249_detach,
-};
-
-static struct i2c_client client_template = {
- .driver = &i2c_driver_videotext,
- .name = "(unset)",
-};
-
/*
* Wait the given number of jiffies (10ms). This calls the scheduler, so the actual
* delay may be longer.
@@ -275,7 +152,7 @@
buf[0] = reg;
memcpy(buf+1, data, count);
- if(i2c_master_send(t->client, buf, count+1)==count+1)
+ if (i2c_master_send(t->client, buf, count + 1) == count + 1)
return 0;
return -1;
}
@@ -317,246 +194,236 @@
unsigned int cmd, void *arg)
{
static int virtual_mode = false;
- struct video_device *vd = video_devdata(file);
- struct saa5249_device *t=vd->priv;
+ struct saa5249_device *t = video_drvdata(file);
- switch(cmd)
+ switch (cmd) {
+ case VTXIOCGETINFO:
{
- case VTXIOCGETINFO:
- {
- vtx_info_t *info = arg;
- info->version_major = VTX_VER_MAJ;
- info->version_minor = VTX_VER_MIN;
- info->numpages = NUM_DAUS;
- /*info->cct_type = CCT_TYPE;*/
- return 0;
- }
+ vtx_info_t *info = arg;
+ info->version_major = VTX_VER_MAJ;
+ info->version_minor = VTX_VER_MIN;
+ info->numpages = NUM_DAUS;
+ /*info->cct_type = CCT_TYPE;*/
+ return 0;
+ }
- case VTXIOCCLRPAGE:
- {
- vtx_pagereq_t *req = arg;
+ case VTXIOCCLRPAGE:
+ {
+ vtx_pagereq_t *req = arg;
- if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS)
- return -EINVAL;
- memset(t->vdau[req->pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf));
- t->vdau[req->pgbuf].clrfound = true;
- return 0;
- }
+ if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS)
+ return -EINVAL;
+ memset(t->vdau[req->pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf));
+ t->vdau[req->pgbuf].clrfound = true;
+ return 0;
+ }
- case VTXIOCCLRFOUND:
- {
- vtx_pagereq_t *req = arg;
+ case VTXIOCCLRFOUND:
+ {
+ vtx_pagereq_t *req = arg;
- if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS)
- return -EINVAL;
- t->vdau[req->pgbuf].clrfound = true;
- return 0;
- }
+ if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS)
+ return -EINVAL;
+ t->vdau[req->pgbuf].clrfound = true;
+ return 0;
+ }
- case VTXIOCPAGEREQ:
- {
- vtx_pagereq_t *req = arg;
- if (!(req->pagemask & PGMASK_PAGE))
- req->page = 0;
- if (!(req->pagemask & PGMASK_HOUR))
- req->hour = 0;
- if (!(req->pagemask & PGMASK_MINUTE))
- req->minute = 0;
- if (req->page < 0 || req->page > 0x8ff) /* 7FF ?? */
- return -EINVAL;
- req->page &= 0x7ff;
- if (req->hour < 0 || req->hour > 0x3f || req->minute < 0 || req->minute > 0x7f ||
- req->pagemask < 0 || req->pagemask >= PGMASK_MAX || req->pgbuf < 0 || req->pgbuf >= NUM_DAUS)
- return -EINVAL;
- t->vdau[req->pgbuf].sregs[0] = (req->pagemask & PG_HUND ? 0x10 : 0) | (req->page / 0x100);
- t->vdau[req->pgbuf].sregs[1] = (req->pagemask & PG_TEN ? 0x10 : 0) | ((req->page / 0x10) & 0xf);
- t->vdau[req->pgbuf].sregs[2] = (req->pagemask & PG_UNIT ? 0x10 : 0) | (req->page & 0xf);
- t->vdau[req->pgbuf].sregs[3] = (req->pagemask & HR_TEN ? 0x10 : 0) | (req->hour / 0x10);
- t->vdau[req->pgbuf].sregs[4] = (req->pagemask & HR_UNIT ? 0x10 : 0) | (req->hour & 0xf);
- t->vdau[req->pgbuf].sregs[5] = (req->pagemask & MIN_TEN ? 0x10 : 0) | (req->minute / 0x10);
- t->vdau[req->pgbuf].sregs[6] = (req->pagemask & MIN_UNIT ? 0x10 : 0) | (req->minute & 0xf);
- t->vdau[req->pgbuf].stopped = false;
- t->vdau[req->pgbuf].clrfound = true;
- t->is_searching[req->pgbuf] = true;
- return 0;
- }
+ case VTXIOCPAGEREQ:
+ {
+ vtx_pagereq_t *req = arg;
+ if (!(req->pagemask & PGMASK_PAGE))
+ req->page = 0;
+ if (!(req->pagemask & PGMASK_HOUR))
+ req->hour = 0;
+ if (!(req->pagemask & PGMASK_MINUTE))
+ req->minute = 0;
+ if (req->page < 0 || req->page > 0x8ff) /* 7FF ?? */
+ return -EINVAL;
+ req->page &= 0x7ff;
+ if (req->hour < 0 || req->hour > 0x3f || req->minute < 0 || req->minute > 0x7f ||
+ req->pagemask < 0 || req->pagemask >= PGMASK_MAX || req->pgbuf < 0 || req->pgbuf >= NUM_DAUS)
+ return -EINVAL;
+ t->vdau[req->pgbuf].sregs[0] = (req->pagemask & PG_HUND ? 0x10 : 0) | (req->page / 0x100);
+ t->vdau[req->pgbuf].sregs[1] = (req->pagemask & PG_TEN ? 0x10 : 0) | ((req->page / 0x10) & 0xf);
+ t->vdau[req->pgbuf].sregs[2] = (req->pagemask & PG_UNIT ? 0x10 : 0) | (req->page & 0xf);
+ t->vdau[req->pgbuf].sregs[3] = (req->pagemask & HR_TEN ? 0x10 : 0) | (req->hour / 0x10);
+ t->vdau[req->pgbuf].sregs[4] = (req->pagemask & HR_UNIT ? 0x10 : 0) | (req->hour & 0xf);
+ t->vdau[req->pgbuf].sregs[5] = (req->pagemask & MIN_TEN ? 0x10 : 0) | (req->minute / 0x10);
+ t->vdau[req->pgbuf].sregs[6] = (req->pagemask & MIN_UNIT ? 0x10 : 0) | (req->minute & 0xf);
+ t->vdau[req->pgbuf].stopped = false;
+ t->vdau[req->pgbuf].clrfound = true;
+ t->is_searching[req->pgbuf] = true;
+ return 0;
+ }
- case VTXIOCGETSTAT:
- {
- vtx_pagereq_t *req = arg;
- u8 infobits[10];
- vtx_pageinfo_t info;
- int a;
+ case VTXIOCGETSTAT:
+ {
+ vtx_pagereq_t *req = arg;
+ u8 infobits[10];
+ vtx_pageinfo_t info;
+ int a;
- if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS)
- return -EINVAL;
- if (!t->vdau[req->pgbuf].stopped)
- {
- if (i2c_senddata(t, 2, 0, -1) ||
- i2c_sendbuf(t, 3, sizeof(t->vdau[0].sregs), t->vdau[req->pgbuf].sregs) ||
- i2c_senddata(t, 8, 0, 25, 0, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', -1) ||
- i2c_senddata(t, 2, 0, t->vdau[req->pgbuf].sregs[0] | 8, -1) ||
- i2c_senddata(t, 8, 0, 25, 0, -1))
+ if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS)
+ return -EINVAL;
+ if (!t->vdau[req->pgbuf].stopped) {
+ if (i2c_senddata(t, 2, 0, -1) ||
+ i2c_sendbuf(t, 3, sizeof(t->vdau[0].sregs), t->vdau[req->pgbuf].sregs) ||
+ i2c_senddata(t, 8, 0, 25, 0, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', -1) ||
+ i2c_senddata(t, 2, 0, t->vdau[req->pgbuf].sregs[0] | 8, -1) ||
+ i2c_senddata(t, 8, 0, 25, 0, -1))
+ return -EIO;
+ jdelay(PAGE_WAIT);
+ if (i2c_getdata(t, 10, infobits))
+ return -EIO;
+
+ if (!(infobits[8] & 0x10) && !(infobits[7] & 0xf0) && /* check FOUND-bit */
+ (memcmp(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits)) ||
+ time_after_eq(jiffies, t->vdau[req->pgbuf].expire)))
+ { /* check if new page arrived */
+ if (i2c_senddata(t, 8, 0, 0, 0, -1) ||
+ i2c_getdata(t, VTX_PAGESIZE, t->vdau[req->pgbuf].pgbuf))
return -EIO;
- jdelay(PAGE_WAIT);
- if (i2c_getdata(t, 10, infobits))
- return -EIO;
-
- if (!(infobits[8] & 0x10) && !(infobits[7] & 0xf0) && /* check FOUND-bit */
- (memcmp(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits)) ||
- time_after_eq(jiffies, t->vdau[req->pgbuf].expire)))
- { /* check if new page arrived */
- if (i2c_senddata(t, 8, 0, 0, 0, -1) ||
- i2c_getdata(t, VTX_PAGESIZE, t->vdau[req->pgbuf].pgbuf))
+ t->vdau[req->pgbuf].expire = jiffies + PGBUF_EXPIRE;
+ memset(t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE, ' ', VTX_VIRTUALSIZE - VTX_PAGESIZE);
+ if (t->virtual_mode) {
+ /* Packet X/24 */
+ if (i2c_senddata(t, 8, 0, 0x20, 0, -1) ||
+ i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 20 * 40))
return -EIO;
- t->vdau[req->pgbuf].expire = jiffies + PGBUF_EXPIRE;
- memset(t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE, ' ', VTX_VIRTUALSIZE - VTX_PAGESIZE);
- if (t->virtual_mode)
- {
- /* Packet X/24 */
- if (i2c_senddata(t, 8, 0, 0x20, 0, -1) ||
- i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 20 * 40))
- return -EIO;
- /* Packet X/27/0 */
- if (i2c_senddata(t, 8, 0, 0x21, 0, -1) ||
- i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 16 * 40))
- return -EIO;
- /* Packet 8/30/0...8/30/15
- * FIXME: AFAIK, the 5249 does hamming-decoding for some bytes in packet 8/30,
- * so we should undo this here.
- */
- if (i2c_senddata(t, 8, 0, 0x22, 0, -1) ||
- i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 23 * 40))
- return -EIO;
- }
- t->vdau[req->pgbuf].clrfound = false;
- memcpy(t->vdau[req->pgbuf].laststat, infobits, sizeof(infobits));
+ /* Packet X/27/0 */
+ if (i2c_senddata(t, 8, 0, 0x21, 0, -1) ||
+ i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 16 * 40))
+ return -EIO;
+ /* Packet 8/30/0...8/30/15
+ * FIXME: AFAIK, the 5249 does hamming-decoding for some bytes in packet 8/30,
+ * so we should undo this here.
+ */
+ if (i2c_senddata(t, 8, 0, 0x22, 0, -1) ||
+ i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 23 * 40))
+ return -EIO;
}
- else
- {
- memcpy(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits));
- }
- }
- else
- {
+ t->vdau[req->pgbuf].clrfound = false;
+ memcpy(t->vdau[req->pgbuf].laststat, infobits, sizeof(infobits));
+ } else {
memcpy(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits));
}
-
- info.pagenum = ((infobits[8] << 8) & 0x700) | ((infobits[1] << 4) & 0xf0) | (infobits[0] & 0x0f);
- if (info.pagenum < 0x100)
- info.pagenum += 0x800;
- info.hour = ((infobits[5] << 4) & 0x30) | (infobits[4] & 0x0f);
- info.minute = ((infobits[3] << 4) & 0x70) | (infobits[2] & 0x0f);
- info.charset = ((infobits[7] >> 1) & 7);
- info.delete = !!(infobits[3] & 8);
- info.headline = !!(infobits[5] & 4);
- info.subtitle = !!(infobits[5] & 8);
- info.supp_header = !!(infobits[6] & 1);
- info.update = !!(infobits[6] & 2);
- info.inter_seq = !!(infobits[6] & 4);
- info.dis_disp = !!(infobits[6] & 8);
- info.serial = !!(infobits[7] & 1);
- info.notfound = !!(infobits[8] & 0x10);
- info.pblf = !!(infobits[9] & 0x20);
- info.hamming = 0;
- for (a = 0; a <= 7; a++)
- {
- if (infobits[a] & 0xf0)
- {
- info.hamming = 1;
- break;
- }
- }
- if (t->vdau[req->pgbuf].clrfound)
- info.notfound = 1;
- if(copy_to_user(req->buffer, &info, sizeof(vtx_pageinfo_t)))
- return -EFAULT;
- if (!info.hamming && !info.notfound)
- {
- t->is_searching[req->pgbuf] = false;
- }
- return 0;
+ } else {
+ memcpy(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits));
}
- case VTXIOCGETPAGE:
- {
- vtx_pagereq_t *req = arg;
- int start, end;
-
- if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS || req->start < 0 ||
- req->start > req->end || req->end >= (virtual_mode ? VTX_VIRTUALSIZE : VTX_PAGESIZE))
- return -EINVAL;
- if(copy_to_user(req->buffer, &t->vdau[req->pgbuf].pgbuf[req->start], req->end - req->start + 1))
- return -EFAULT;
-
- /*
- * Always read the time directly from SAA5249
- */
-
- if (req->start <= 39 && req->end >= 32)
- {
- int len;
- char buf[16];
- start = max(req->start, 32);
- end = min(req->end, 39);
- len=end-start+1;
- if (i2c_senddata(t, 8, 0, 0, start, -1) ||
- i2c_getdata(t, len, buf))
- return -EIO;
- if(copy_to_user(req->buffer+start-req->start, buf, len))
- return -EFAULT;
+ info.pagenum = ((infobits[8] << 8) & 0x700) | ((infobits[1] << 4) & 0xf0) | (infobits[0] & 0x0f);
+ if (info.pagenum < 0x100)
+ info.pagenum += 0x800;
+ info.hour = ((infobits[5] << 4) & 0x30) | (infobits[4] & 0x0f);
+ info.minute = ((infobits[3] << 4) & 0x70) | (infobits[2] & 0x0f);
+ info.charset = ((infobits[7] >> 1) & 7);
+ info.delete = !!(infobits[3] & 8);
+ info.headline = !!(infobits[5] & 4);
+ info.subtitle = !!(infobits[5] & 8);
+ info.supp_header = !!(infobits[6] & 1);
+ info.update = !!(infobits[6] & 2);
+ info.inter_seq = !!(infobits[6] & 4);
+ info.dis_disp = !!(infobits[6] & 8);
+ info.serial = !!(infobits[7] & 1);
+ info.notfound = !!(infobits[8] & 0x10);
+ info.pblf = !!(infobits[9] & 0x20);
+ info.hamming = 0;
+ for (a = 0; a <= 7; a++) {
+ if (infobits[a] & 0xf0) {
+ info.hamming = 1;
+ break;
}
- /* Insert the current header if DAU is still searching for a page */
- if (req->start <= 31 && req->end >= 7 && t->is_searching[req->pgbuf])
- {
- char buf[32];
- int len;
- start = max(req->start, 7);
- end = min(req->end, 31);
- len=end-start+1;
- if (i2c_senddata(t, 8, 0, 0, start, -1) ||
- i2c_getdata(t, len, buf))
- return -EIO;
- if(copy_to_user(req->buffer+start-req->start, buf, len))
- return -EFAULT;
- }
- return 0;
}
-
- case VTXIOCSTOPDAU:
- {
- vtx_pagereq_t *req = arg;
-
- if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS)
- return -EINVAL;
- t->vdau[req->pgbuf].stopped = true;
+ if (t->vdau[req->pgbuf].clrfound)
+ info.notfound = 1;
+ if (copy_to_user(req->buffer, &info, sizeof(vtx_pageinfo_t)))
+ return -EFAULT;
+ if (!info.hamming && !info.notfound)
t->is_searching[req->pgbuf] = false;
- return 0;
- }
+ return 0;
+ }
- case VTXIOCPUTPAGE:
- case VTXIOCSETDISP:
- case VTXIOCPUTSTAT:
- return 0;
+ case VTXIOCGETPAGE:
+ {
+ vtx_pagereq_t *req = arg;
+ int start, end;
- case VTXIOCCLRCACHE:
- {
- if (i2c_senddata(t, 0, NUM_DAUS, 0, 8, -1) || i2c_senddata(t, 11,
- ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
- ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', -1))
+ if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS || req->start < 0 ||
+ req->start > req->end || req->end >= (virtual_mode ? VTX_VIRTUALSIZE : VTX_PAGESIZE))
+ return -EINVAL;
+ if (copy_to_user(req->buffer, &t->vdau[req->pgbuf].pgbuf[req->start], req->end - req->start + 1))
+ return -EFAULT;
+
+ /*
+ * Always read the time directly from SAA5249
+ */
+
+ if (req->start <= 39 && req->end >= 32) {
+ int len;
+ char buf[16];
+ start = max(req->start, 32);
+ end = min(req->end, 39);
+ len = end - start + 1;
+ if (i2c_senddata(t, 8, 0, 0, start, -1) ||
+ i2c_getdata(t, len, buf))
return -EIO;
- if (i2c_senddata(t, 3, 0x20, -1))
- return -EIO;
- jdelay(10 * CLEAR_DELAY); /* I have no idea how long we have to wait here */
- return 0;
+ if (copy_to_user(req->buffer + start - req->start, buf, len))
+ return -EFAULT;
}
+ /* Insert the current header if DAU is still searching for a page */
+ if (req->start <= 31 && req->end >= 7 && t->is_searching[req->pgbuf]) {
+ char buf[32];
+ int len;
- case VTXIOCSETVIRT:
- {
- /* The SAA5249 has virtual-row reception turned on always */
- t->virtual_mode = (int)(long)arg;
- return 0;
+ start = max(req->start, 7);
+ end = min(req->end, 31);
+ len = end - start + 1;
+ if (i2c_senddata(t, 8, 0, 0, start, -1) ||
+ i2c_getdata(t, len, buf))
+ return -EIO;
+ if (copy_to_user(req->buffer + start - req->start, buf, len))
+ return -EFAULT;
}
+ return 0;
+ }
+
+ case VTXIOCSTOPDAU:
+ {
+ vtx_pagereq_t *req = arg;
+
+ if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS)
+ return -EINVAL;
+ t->vdau[req->pgbuf].stopped = true;
+ t->is_searching[req->pgbuf] = false;
+ return 0;
+ }
+
+ case VTXIOCPUTPAGE:
+ case VTXIOCSETDISP:
+ case VTXIOCPUTSTAT:
+ return 0;
+
+ case VTXIOCCLRCACHE:
+ {
+ if (i2c_senddata(t, 0, NUM_DAUS, 0, 8, -1) || i2c_senddata(t, 11,
+ ' ', ' ', ' ', ' ', ' ', ' ',
+ ' ', ' ', ' ', ' ', ' ', ' ',
+ ' ', ' ', ' ', ' ', ' ', ' ',
+ ' ', ' ', ' ', ' ', ' ', ' ',
+ -1))
+ return -EIO;
+ if (i2c_senddata(t, 3, 0x20, -1))
+ return -EIO;
+ jdelay(10 * CLEAR_DELAY); /* I have no idea how long we have to wait here */
+ return 0;
+ }
+
+ case VTXIOCSETVIRT:
+ {
+ /* The SAA5249 has virtual-row reception turned on always */
+ t->virtual_mode = (int)(long)arg;
+ return 0;
+ }
}
return -EINVAL;
}
@@ -616,8 +483,7 @@
static int saa5249_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- struct video_device *vd = video_devdata(file);
- struct saa5249_device *t=vd->priv;
+ struct saa5249_device *t = video_drvdata(file);
int err;
cmd = vtx_fix_command(cmd);
@@ -629,32 +495,27 @@
static int saa5249_open(struct inode *inode, struct file *file)
{
- struct video_device *vd = video_devdata(file);
- struct saa5249_device *t=vd->priv;
- int err,pgbuf;
+ struct saa5249_device *t = video_drvdata(file);
+ int pgbuf;
- err = video_exclusive_open(inode,file);
- if (err < 0)
- return err;
+ if (t->client == NULL)
+ return -ENODEV;
- if (t->client==NULL) {
- err = -ENODEV;
- goto fail;
- }
+ if (test_and_set_bit(0, &t->in_use))
+ return -EBUSY;
- if (i2c_senddata(t, 0, 0, -1) || /* Select R11 */
- /* Turn off parity checks (we do this ourselves) */
+ if (i2c_senddata(t, 0, 0, -1) || /* Select R11 */
+ /* Turn off parity checks (we do this ourselves) */
i2c_senddata(t, 1, disp_modes[t->disp_mode][0], 0, -1) ||
- /* Display TV-picture, no virtual rows */
- i2c_senddata(t, 4, NUM_DAUS, disp_modes[t->disp_mode][1], disp_modes[t->disp_mode][2], 7, -1)) /* Set display to page 4 */
-
+ /* Display TV-picture, no virtual rows */
+ i2c_senddata(t, 4, NUM_DAUS, disp_modes[t->disp_mode][1], disp_modes[t->disp_mode][2], 7, -1))
+ /* Set display to page 4 */
{
- err = -EIO;
- goto fail;
+ clear_bit(0, &t->in_use);
+ return -EIO;
}
- for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++)
- {
+ for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) {
memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf));
memset(t->vdau[pgbuf].sregs, 0, sizeof(t->vdau[0].sregs));
memset(t->vdau[pgbuf].laststat, 0, sizeof(t->vdau[0].laststat));
@@ -665,39 +526,20 @@
}
t->virtual_mode = false;
return 0;
-
- fail:
- video_exclusive_release(inode,file);
- return err;
}
static int saa5249_release(struct inode *inode, struct file *file)
{
- struct video_device *vd = video_devdata(file);
- struct saa5249_device *t=vd->priv;
+ struct saa5249_device *t = video_drvdata(file);
+
i2c_senddata(t, 1, 0x20, -1); /* Turn off CCT */
i2c_senddata(t, 5, 3, 3, -1); /* Turn off TV-display */
- video_exclusive_release(inode,file);
+ clear_bit(0, &t->in_use);
return 0;
}
-static int __init init_saa_5249 (void)
-{
- printk(KERN_INFO "SAA5249 driver (" IF_NAME " interface) for VideoText version %d.%d\n",
- VTX_VER_MAJ, VTX_VER_MIN);
- return i2c_add_driver(&i2c_driver_videotext);
-}
-
-static void __exit cleanup_saa_5249 (void)
-{
- i2c_del_driver(&i2c_driver_videotext);
-}
-
-module_init(init_saa_5249);
-module_exit(cleanup_saa_5249);
-
static const struct file_operations saa_fops = {
.owner = THIS_MODULE,
.open = saa5249_open,
@@ -711,8 +553,84 @@
static struct video_device saa_template =
{
- .name = IF_NAME,
+ .name = "saa5249",
.fops = &saa_fops,
+ .release = video_device_release,
};
-MODULE_LICENSE("GPL");
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { 0x22 >> 1, I2C_CLIENT_END };
+
+I2C_CLIENT_INSMOD;
+
+static int saa5249_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int pgbuf;
+ int err;
+ struct video_device *vd;
+ struct saa5249_device *t;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+ v4l_info(client, "VideoText version %d.%d\n",
+ VTX_VER_MAJ, VTX_VER_MIN);
+ t = kzalloc(sizeof(*t), GFP_KERNEL);
+ if (t == NULL)
+ return -ENOMEM;
+ mutex_init(&t->lock);
+
+ /* Now create a video4linux device */
+ vd = kmalloc(sizeof(struct video_device), GFP_KERNEL);
+ if (vd == NULL) {
+ kfree(client);
+ return -ENOMEM;
+ }
+ i2c_set_clientdata(client, vd);
+ memcpy(vd, &saa_template, sizeof(*vd));
+
+ for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) {
+ memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf));
+ memset(t->vdau[pgbuf].sregs, 0, sizeof(t->vdau[0].sregs));
+ memset(t->vdau[pgbuf].laststat, 0, sizeof(t->vdau[0].laststat));
+ t->vdau[pgbuf].expire = 0;
+ t->vdau[pgbuf].clrfound = true;
+ t->vdau[pgbuf].stopped = true;
+ t->is_searching[pgbuf] = false;
+ }
+ video_set_drvdata(vd, t);
+
+ /* Register it */
+ err = video_register_device(vd, VFL_TYPE_VTX, -1);
+ if (err < 0) {
+ kfree(t);
+ kfree(vd);
+ return err;
+ }
+ t->client = client;
+ return 0;
+}
+
+static int saa5249_remove(struct i2c_client *client)
+{
+ struct video_device *vd = i2c_get_clientdata(client);
+
+ video_unregister_device(vd);
+ kfree(video_get_drvdata(vd));
+ kfree(vd);
+ return 0;
+}
+
+static const struct i2c_device_id saa5249_id[] = {
+ { "saa5249", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, saa5249_id);
+
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "saa5249",
+ .driverid = I2C_DRIVERID_SAA5249,
+ .probe = saa5249_probe,
+ .remove = saa5249_remove,
+ .id_table = saa5249_id,
+};
diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c
index ad733ca..c8e9cb3 100644
--- a/drivers/media/video/saa7115.c
+++ b/drivers/media/video/saa7115.c
@@ -1057,7 +1057,7 @@
for (i = 0; i <= 23; i++)
lcr[i] = 0xff;
- if (fmt->service_set == 0) {
+ if (fmt == NULL) {
/* raw VBI */
if (is_50hz)
for (i = 6; i <= 23; i++)
@@ -1113,7 +1113,7 @@
}
/* enable/disable raw VBI capturing */
- saa711x_writeregs(client, fmt->service_set == 0 ?
+ saa711x_writeregs(client, fmt == NULL ?
saa7115_cfg_vbi_on :
saa7115_cfg_vbi_off);
}
@@ -1153,6 +1153,10 @@
saa711x_set_lcr(client, &fmt->fmt.sliced);
return 0;
}
+ if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ saa711x_set_lcr(client, NULL);
+ return 0;
+ }
if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
@@ -1309,10 +1313,13 @@
case VIDIOC_INT_S_VIDEO_ROUTING:
{
struct v4l2_routing *route = arg;
+ u32 input = route->input;
+ u8 mask = (state->ident == V4L2_IDENT_SAA7111) ? 0xf8 : 0xf0;
v4l_dbg(1, debug, client, "decoder set input %d output %d\n", route->input, route->output);
- /* saa7113 does not have these inputs */
- if (state->ident == V4L2_IDENT_SAA7113 &&
+ /* saa7111/3 does not have these inputs */
+ if ((state->ident == V4L2_IDENT_SAA7113 ||
+ state->ident == V4L2_IDENT_SAA7111) &&
(route->input == SAA7115_COMPOSITE4 ||
route->input == SAA7115_COMPOSITE5)) {
return -EINVAL;
@@ -1327,10 +1334,23 @@
(route->input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite", (route->output == SAA7115_IPORT_ON) ? "iport on" : "iport off");
state->input = route->input;
+ /* saa7111 has slightly different input numbering */
+ if (state->ident == V4L2_IDENT_SAA7111) {
+ if (input >= SAA7115_COMPOSITE4)
+ input -= 2;
+ /* saa7111 specific */
+ saa711x_write(client, R_10_CHROMA_CNTL_2,
+ (saa711x_read(client, R_10_CHROMA_CNTL_2) & 0x3f) |
+ ((route->output & 0xc0) ^ 0x40));
+ saa711x_write(client, R_13_RT_X_PORT_OUT_CNTL,
+ (saa711x_read(client, R_13_RT_X_PORT_OUT_CNTL) & 0xf0) |
+ ((route->output & 2) ? 0x0a : 0));
+ }
+
/* select mode */
saa711x_write(client, R_02_INPUT_CNTL_1,
- (saa711x_read(client, R_02_INPUT_CNTL_1) & 0xf0) |
- state->input);
+ (saa711x_read(client, R_02_INPUT_CNTL_1) & mask) |
+ input);
/* bypass chrominance trap for S-Video modes */
saa711x_write(client, R_09_LUMA_CNTL,
@@ -1384,6 +1404,13 @@
saa711x_writeregs(client, saa7115_cfg_reset_scaler);
break;
+ case VIDIOC_INT_S_GPIO:
+ if (state->ident != V4L2_IDENT_SAA7111)
+ return -EINVAL;
+ saa711x_write(client, 0x11, (saa711x_read(client, 0x11) & 0x7f) |
+ (*(u32 *)arg ? 0x80 : 0));
+ break;
+
case VIDIOC_INT_G_VBI_DATA:
{
struct v4l2_sliced_vbi_data *data = arg;
@@ -1539,7 +1566,8 @@
state->crystal_freq = SAA7115_FREQ_32_11_MHZ;
saa711x_writeregs(client, saa7115_init_auto_input);
}
- saa711x_writeregs(client, saa7115_init_misc);
+ if (state->ident != V4L2_IDENT_SAA7111)
+ saa711x_writeregs(client, saa7115_init_misc);
saa711x_set_v4lstd(client, V4L2_STD_NTSC);
v4l_dbg(1, debug, client, "status: (1E) 0x%02x, (1F) 0x%02x\n",
diff --git a/drivers/media/video/saa7134/saa6752hs.c b/drivers/media/video/saa7134/saa6752hs.c
index 707be17..1fb6ecc 100644
--- a/drivers/media/video/saa7134/saa6752hs.c
+++ b/drivers/media/video/saa7134/saa6752hs.c
@@ -1,3 +1,27 @@
+ /*
+ saa6752hs - i2c-driver for the saa6752hs by Philips
+
+ Copyright (C) 2004 Andrew de Quincey
+
+ AC-3 support:
+
+ Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License vs 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., 675 Mvss Ave, Cambridge, MA 02139, USA.
+ */
+
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
@@ -10,6 +34,8 @@
#include <linux/types.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-i2c-drv-legacy.h>
#include <linux/init.h>
#include <linux/crc32.h>
@@ -27,9 +53,6 @@
MODULE_AUTHOR("Andrew de Quincey");
MODULE_LICENSE("GPL");
-static struct i2c_driver driver;
-static struct i2c_client client_template;
-
enum saa6752hs_videoformat {
SAA6752HS_VF_D1 = 0, /* standard D1 video format: 720x576 */
SAA6752HS_VF_2_3_D1 = 1,/* 2/3D1 video format: 480x576 */
@@ -46,7 +69,9 @@
__u16 ts_pid_pcr;
/* audio */
- enum v4l2_mpeg_audio_l2_bitrate au_l2_bitrate;
+ enum v4l2_mpeg_audio_encoding au_encoding;
+ enum v4l2_mpeg_audio_l2_bitrate au_l2_bitrate;
+ enum v4l2_mpeg_audio_ac3_bitrate au_ac3_bitrate;
/* video */
enum v4l2_mpeg_video_aspect vi_aspect;
@@ -70,7 +95,9 @@
};
struct saa6752hs_state {
- struct i2c_client client;
+ int chip;
+ u32 revision;
+ int has_ac3;
struct saa6752hs_mpeg_params params;
enum saa6752hs_videoformat video_format;
v4l2_std_id standard;
@@ -145,6 +172,39 @@
0x00, 0x00, 0x00, 0x00 /* CRC32 */
};
+static u8 PMT_AC3[] = {
+ 0xc2, /* i2c register */
+ 0x01, /* table number for encoder(1) */
+ 0x47, /* sync */
+
+ 0x40, /* transport_error_indicator(0), payload_unit_start(1), transport_priority(0) */
+ 0x10, /* PMT PID (0x0010) */
+ 0x10, /* transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0) */
+
+ 0x00, /* PSI pointer to start of table */
+
+ 0x02, /* TID (2) */
+ 0xb0, 0x1a, /* section_syntax_indicator(1), section_length(26) */
+
+ 0x00, 0x01, /* program_number(1) */
+
+ 0xc1, /* version_number(0), current_next_indicator(1) */
+
+ 0x00, 0x00, /* section_number(0), last_section_number(0) */
+
+ 0xe1, 0x04, /* PCR_PID (0x0104) */
+
+ 0xf0, 0x00, /* program_info_length(0) */
+
+ 0x02, 0xe1, 0x00, 0xf0, 0x00, /* video stream type(2), pid */
+ 0x06, 0xe1, 0x03, 0xf0, 0x03, /* audio stream type(6), pid */
+ 0x6a, /* AC3 */
+ 0x01, /* Descriptor_length(1) */
+ 0x00, /* component_type_flag(0), bsid_flag(0), mainid_flag(0), asvc_flag(0), reserved flags(0) */
+
+ 0xED, 0xDE, 0x2D, 0xF3 /* CRC32 BE */
+};
+
static struct saa6752hs_mpeg_params param_defaults =
{
.ts_pid_pmt = 16,
@@ -157,12 +217,14 @@
.vi_bitrate_peak = 6000,
.vi_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
+ .au_encoding = V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
.au_l2_bitrate = V4L2_MPEG_AUDIO_L2_BITRATE_256K,
+ .au_ac3_bitrate = V4L2_MPEG_AUDIO_AC3_BITRATE_256K,
};
/* ---------------------------------------------------------------------- */
-static int saa6752hs_chip_command(struct i2c_client* client,
+static int saa6752hs_chip_command(struct i2c_client *client,
enum saa6752hs_command command)
{
unsigned char buf[3];
@@ -229,45 +291,61 @@
}
-static int saa6752hs_set_bitrate(struct i2c_client* client,
- struct saa6752hs_mpeg_params* params)
+static inline void set_reg8(struct i2c_client *client, uint8_t reg, uint8_t val)
+{
+ u8 buf[2];
+
+ buf[0] = reg;
+ buf[1] = val;
+ i2c_master_send(client, buf, 2);
+}
+
+static inline void set_reg16(struct i2c_client *client, uint8_t reg, uint16_t val)
{
u8 buf[3];
+
+ buf[0] = reg;
+ buf[1] = val >> 8;
+ buf[2] = val & 0xff;
+ i2c_master_send(client, buf, 3);
+}
+
+static int saa6752hs_set_bitrate(struct i2c_client *client,
+ struct saa6752hs_state *h)
+{
+ struct saa6752hs_mpeg_params *params = &h->params;
int tot_bitrate;
+ int is_384k;
/* set the bitrate mode */
- buf[0] = 0x71;
- buf[1] = (params->vi_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) ? 0 : 1;
- i2c_master_send(client, buf, 2);
+ set_reg8(client, 0x71,
+ params->vi_bitrate_mode != V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
/* set the video bitrate */
if (params->vi_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) {
/* set the target bitrate */
- buf[0] = 0x80;
- buf[1] = params->vi_bitrate >> 8;
- buf[2] = params->vi_bitrate & 0xff;
- i2c_master_send(client, buf, 3);
+ set_reg16(client, 0x80, params->vi_bitrate);
/* set the max bitrate */
- buf[0] = 0x81;
- buf[1] = params->vi_bitrate_peak >> 8;
- buf[2] = params->vi_bitrate_peak & 0xff;
- i2c_master_send(client, buf, 3);
+ set_reg16(client, 0x81, params->vi_bitrate_peak);
tot_bitrate = params->vi_bitrate_peak;
} else {
/* set the target bitrate (no max bitrate for CBR) */
- buf[0] = 0x81;
- buf[1] = params->vi_bitrate >> 8;
- buf[2] = params->vi_bitrate & 0xff;
- i2c_master_send(client, buf, 3);
+ set_reg16(client, 0x81, params->vi_bitrate);
tot_bitrate = params->vi_bitrate;
}
+ /* set the audio encoding */
+ set_reg8(client, 0x93,
+ params->au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3);
+
/* set the audio bitrate */
- buf[0] = 0x94;
- buf[1] = (V4L2_MPEG_AUDIO_L2_BITRATE_256K == params->au_l2_bitrate) ? 0 : 1;
- i2c_master_send(client, buf, 2);
- tot_bitrate += (V4L2_MPEG_AUDIO_L2_BITRATE_256K == params->au_l2_bitrate) ? 256 : 384;
+ if (params->au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3)
+ is_384k = V4L2_MPEG_AUDIO_AC3_BITRATE_384K == params->au_ac3_bitrate;
+ else
+ is_384k = V4L2_MPEG_AUDIO_L2_BITRATE_384K == params->au_l2_bitrate;
+ set_reg8(client, 0x94, is_384k);
+ tot_bitrate += is_384k ? 384 : 256;
/* Note: the total max bitrate is determined by adding the video and audio
bitrates together and also adding an extra 768kbit/s to stay on the
@@ -278,16 +356,12 @@
tot_bitrate = MPEG_TOTAL_TARGET_BITRATE_MAX;
/* set the total bitrate */
- buf[0] = 0xb1;
- buf[1] = tot_bitrate >> 8;
- buf[2] = tot_bitrate & 0xff;
- i2c_master_send(client, buf, 3);
-
+ set_reg16(client, 0xb1, tot_bitrate);
return 0;
}
-static void saa6752hs_set_subsampling(struct i2c_client* client,
- struct v4l2_format* f)
+static void saa6752hs_set_subsampling(struct i2c_client *client,
+ struct v4l2_format *f)
{
struct saa6752hs_state *h = i2c_get_clientdata(client);
int dist_352, dist_480, dist_720;
@@ -332,7 +406,7 @@
}
-static int handle_ctrl(struct saa6752hs_mpeg_params *params,
+static int handle_ctrl(int has_ac3, struct saa6752hs_mpeg_params *params,
struct v4l2_ext_control *ctrl, unsigned int cmd)
{
int old = 0, new;
@@ -379,8 +453,9 @@
params->ts_pid_pcr = new;
break;
case V4L2_CID_MPEG_AUDIO_ENCODING:
- old = V4L2_MPEG_AUDIO_ENCODING_LAYER_2;
- if (set && new != old)
+ old = params->au_encoding;
+ if (set && new != V4L2_MPEG_AUDIO_ENCODING_LAYER_2 &&
+ (!has_ac3 || new != V4L2_MPEG_AUDIO_ENCODING_AC3))
return -ERANGE;
new = old;
break;
@@ -395,6 +470,19 @@
new = V4L2_MPEG_AUDIO_L2_BITRATE_384K;
params->au_l2_bitrate = new;
break;
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+ if (!has_ac3)
+ return -EINVAL;
+ old = params->au_ac3_bitrate;
+ if (set && new != V4L2_MPEG_AUDIO_AC3_BITRATE_256K &&
+ new != V4L2_MPEG_AUDIO_AC3_BITRATE_384K)
+ return -ERANGE;
+ if (new <= V4L2_MPEG_AUDIO_AC3_BITRATE_256K)
+ new = V4L2_MPEG_AUDIO_AC3_BITRATE_256K;
+ else
+ new = V4L2_MPEG_AUDIO_AC3_BITRATE_384K;
+ params->au_ac3_bitrate = new;
+ break;
case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
old = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000;
if (set && new != old)
@@ -448,17 +536,19 @@
return 0;
}
-static int saa6752hs_qctrl(struct saa6752hs_mpeg_params *params,
+static int saa6752hs_qctrl(struct saa6752hs_state *h,
struct v4l2_queryctrl *qctrl)
{
+ struct saa6752hs_mpeg_params *params = &h->params;
int err;
switch (qctrl->id) {
case V4L2_CID_MPEG_AUDIO_ENCODING:
return v4l2_ctrl_query_fill(qctrl,
V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
- V4L2_MPEG_AUDIO_ENCODING_LAYER_2, 1,
- V4L2_MPEG_AUDIO_ENCODING_LAYER_2);
+ h->has_ac3 ? V4L2_MPEG_AUDIO_ENCODING_AC3 :
+ V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
+ 1, V4L2_MPEG_AUDIO_ENCODING_LAYER_2);
case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
return v4l2_ctrl_query_fill(qctrl,
@@ -466,6 +556,14 @@
V4L2_MPEG_AUDIO_L2_BITRATE_384K, 1,
V4L2_MPEG_AUDIO_L2_BITRATE_256K);
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+ if (!h->has_ac3)
+ return -EINVAL;
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_256K,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_384K, 1,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_256K);
+
case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
return v4l2_ctrl_query_fill(qctrl,
V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000,
@@ -512,44 +610,57 @@
return -EINVAL;
}
-static int saa6752hs_qmenu(struct saa6752hs_mpeg_params *params,
+static int saa6752hs_qmenu(struct saa6752hs_state *h,
struct v4l2_querymenu *qmenu)
{
- static const char *mpeg_audio_l2_bitrate[] = {
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "",
- "256 kbps",
- "",
- "384 kbps",
- NULL
+ static const u32 mpeg_audio_encoding[] = {
+ V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
+ V4L2_CTRL_MENU_IDS_END
+ };
+ static const u32 mpeg_audio_ac3_encoding[] = {
+ V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
+ V4L2_MPEG_AUDIO_ENCODING_AC3,
+ V4L2_CTRL_MENU_IDS_END
+ };
+ static u32 mpeg_audio_l2_bitrate[] = {
+ V4L2_MPEG_AUDIO_L2_BITRATE_256K,
+ V4L2_MPEG_AUDIO_L2_BITRATE_384K,
+ V4L2_CTRL_MENU_IDS_END
+ };
+ static u32 mpeg_audio_ac3_bitrate[] = {
+ V4L2_MPEG_AUDIO_AC3_BITRATE_256K,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_384K,
+ V4L2_CTRL_MENU_IDS_END
};
struct v4l2_queryctrl qctrl;
int err;
qctrl.id = qmenu->id;
- err = saa6752hs_qctrl(params, &qctrl);
+ err = saa6752hs_qctrl(h, &qctrl);
if (err)
return err;
- if (qmenu->id == V4L2_CID_MPEG_AUDIO_L2_BITRATE)
- return v4l2_ctrl_query_menu(qmenu, &qctrl,
+ switch (qmenu->id) {
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+ return v4l2_ctrl_query_menu_valid_items(qmenu,
mpeg_audio_l2_bitrate);
- return v4l2_ctrl_query_menu(qmenu, &qctrl,
- v4l2_ctrl_get_menu(qmenu->id));
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+ if (!h->has_ac3)
+ return -EINVAL;
+ return v4l2_ctrl_query_menu_valid_items(qmenu,
+ mpeg_audio_ac3_bitrate);
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ return v4l2_ctrl_query_menu_valid_items(qmenu,
+ h->has_ac3 ? mpeg_audio_ac3_encoding :
+ mpeg_audio_encoding);
+ }
+ return v4l2_ctrl_query_menu(qmenu, &qctrl, NULL);
}
-static int saa6752hs_init(struct i2c_client* client)
+static int saa6752hs_init(struct i2c_client *client, u32 leading_null_bytes)
{
unsigned char buf[9], buf2[4];
struct saa6752hs_state *h;
+ unsigned size;
u32 crc;
unsigned char localPAT[256];
unsigned char localPMT[256];
@@ -557,45 +668,31 @@
h = i2c_get_clientdata(client);
/* Set video format - must be done first as it resets other settings */
- buf[0] = 0x41;
- buf[1] = h->video_format;
- i2c_master_send(client, buf, 2);
+ set_reg8(client, 0x41, h->video_format);
/* Set number of lines in input signal */
- buf[0] = 0x40;
- buf[1] = 0x00;
- if (h->standard & V4L2_STD_525_60)
- buf[1] = 0x01;
- i2c_master_send(client, buf, 2);
+ set_reg8(client, 0x40, (h->standard & V4L2_STD_525_60) ? 1 : 0);
/* set bitrate */
- saa6752hs_set_bitrate(client, &h->params);
+ saa6752hs_set_bitrate(client, h);
/* Set GOP structure {3, 13} */
- buf[0] = 0x72;
- buf[1] = 0x03;
- buf[2] = 0x0D;
- i2c_master_send(client,buf,3);
+ set_reg16(client, 0x72, 0x030d);
/* Set minimum Q-scale {4} */
- buf[0] = 0x82;
- buf[1] = 0x04;
- i2c_master_send(client,buf,2);
+ set_reg8(client, 0x82, 0x04);
/* Set maximum Q-scale {12} */
- buf[0] = 0x83;
- buf[1] = 0x0C;
- i2c_master_send(client,buf,2);
+ set_reg8(client, 0x83, 0x0c);
/* Set Output Protocol */
- buf[0] = 0xD0;
- buf[1] = 0x81;
- i2c_master_send(client,buf,2);
+ set_reg8(client, 0xd0, 0x81);
/* Set video output stream format {TS} */
- buf[0] = 0xB0;
- buf[1] = 0x05;
- i2c_master_send(client,buf,2);
+ set_reg8(client, 0xb0, 0x05);
+
+ /* Set leading null byte for TS */
+ set_reg16(client, 0xf6, leading_null_bytes);
/* compute PAT */
memcpy(localPAT, PAT, sizeof(PAT));
@@ -608,7 +705,13 @@
localPAT[sizeof(PAT) - 1] = crc & 0xFF;
/* compute PMT */
- memcpy(localPMT, PMT, sizeof(PMT));
+ if (h->params.au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3) {
+ size = sizeof(PMT_AC3);
+ memcpy(localPMT, PMT_AC3, size);
+ } else {
+ size = sizeof(PMT);
+ memcpy(localPMT, PMT, size);
+ }
localPMT[3] = 0x40 | ((h->params.ts_pid_pmt >> 8) & 0x0f);
localPMT[4] = h->params.ts_pid_pmt & 0xff;
localPMT[15] = 0xE0 | ((h->params.ts_pid_pcr >> 8) & 0x0F);
@@ -617,40 +720,28 @@
localPMT[21] = h->params.ts_pid_video & 0xFF;
localPMT[25] = 0xE0 | ((h->params.ts_pid_audio >> 8) & 0x0F);
localPMT[26] = h->params.ts_pid_audio & 0xFF;
- crc = crc32_be(~0, &localPMT[7], sizeof(PMT) - 7 - 4);
- localPMT[sizeof(PMT) - 4] = (crc >> 24) & 0xFF;
- localPMT[sizeof(PMT) - 3] = (crc >> 16) & 0xFF;
- localPMT[sizeof(PMT) - 2] = (crc >> 8) & 0xFF;
- localPMT[sizeof(PMT) - 1] = crc & 0xFF;
+ crc = crc32_be(~0, &localPMT[7], size - 7 - 4);
+ localPMT[size - 4] = (crc >> 24) & 0xFF;
+ localPMT[size - 3] = (crc >> 16) & 0xFF;
+ localPMT[size - 2] = (crc >> 8) & 0xFF;
+ localPMT[size - 1] = crc & 0xFF;
/* Set Audio PID */
- buf[0] = 0xC1;
- buf[1] = (h->params.ts_pid_audio >> 8) & 0xFF;
- buf[2] = h->params.ts_pid_audio & 0xFF;
- i2c_master_send(client,buf,3);
+ set_reg16(client, 0xc1, h->params.ts_pid_audio);
/* Set Video PID */
- buf[0] = 0xC0;
- buf[1] = (h->params.ts_pid_video >> 8) & 0xFF;
- buf[2] = h->params.ts_pid_video & 0xFF;
- i2c_master_send(client,buf,3);
+ set_reg16(client, 0xc0, h->params.ts_pid_video);
/* Set PCR PID */
- buf[0] = 0xC4;
- buf[1] = (h->params.ts_pid_pcr >> 8) & 0xFF;
- buf[2] = h->params.ts_pid_pcr & 0xFF;
- i2c_master_send(client,buf,3);
+ set_reg16(client, 0xc4, h->params.ts_pid_pcr);
/* Send SI tables */
- i2c_master_send(client,localPAT,sizeof(PAT));
- i2c_master_send(client,localPMT,sizeof(PMT));
+ i2c_master_send(client, localPAT, sizeof(PAT));
+ i2c_master_send(client, localPMT, size);
/* mute then unmute audio. This removes buzzing artefacts */
- buf[0] = 0xa4;
- buf[1] = 1;
- i2c_master_send(client, buf, 2);
- buf[1] = 0;
- i2c_master_send(client, buf, 2);
+ set_reg8(client, 0xa4, 1);
+ set_reg8(client, 0xa4, 0);
/* start it going */
saa6752hs_chip_command(client, SAA6752HS_COMMAND_START);
@@ -688,45 +779,6 @@
return 0;
}
-static int saa6752hs_attach(struct i2c_adapter *adap, int addr, int kind)
-{
- struct saa6752hs_state *h;
-
-
- if (NULL == (h = kzalloc(sizeof(*h), GFP_KERNEL)))
- return -ENOMEM;
- h->client = client_template;
- h->params = param_defaults;
- h->client.adapter = adap;
- h->client.addr = addr;
-
- /* Assume 625 input lines */
- h->standard = 0;
-
- i2c_set_clientdata(&h->client, h);
- i2c_attach_client(&h->client);
-
- v4l_info(&h->client,"saa6752hs: chip found @ 0x%x\n", addr<<1);
- return 0;
-}
-
-static int saa6752hs_probe(struct i2c_adapter *adap)
-{
- if (adap->class & I2C_CLASS_TV_ANALOG)
- return i2c_probe(adap, &addr_data, saa6752hs_attach);
- return 0;
-}
-
-static int saa6752hs_detach(struct i2c_client *client)
-{
- struct saa6752hs_state *h;
-
- h = i2c_get_clientdata(client);
- i2c_detach_client(client);
- kfree(h);
- return 0;
-}
-
static int
saa6752hs_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
@@ -737,14 +789,13 @@
int i;
switch (cmd) {
+ case VIDIOC_INT_INIT:
+ /* apply settings and start encoder */
+ saa6752hs_init(client, *(u32 *)arg);
+ break;
case VIDIOC_S_EXT_CTRLS:
if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
return -EINVAL;
- if (ctrls->count == 0) {
- /* apply settings and start encoder */
- saa6752hs_init(client);
- break;
- }
/* fall through */
case VIDIOC_TRY_EXT_CTRLS:
case VIDIOC_G_EXT_CTRLS:
@@ -752,7 +803,8 @@
return -EINVAL;
params = h->params;
for (i = 0; i < ctrls->count; i++) {
- if ((err = handle_ctrl(¶ms, ctrls->controls + i, cmd))) {
+ err = handle_ctrl(h->has_ac3, ¶ms, ctrls->controls + i, cmd);
+ if (err) {
ctrls->error_idx = i;
return err;
}
@@ -760,9 +812,9 @@
h->params = params;
break;
case VIDIOC_QUERYCTRL:
- return saa6752hs_qctrl(&h->params, arg);
+ return saa6752hs_qctrl(h, arg);
case VIDIOC_QUERYMENU:
- return saa6752hs_qmenu(&h->params, arg);
+ return saa6752hs_qmenu(h, arg);
case VIDIOC_G_FMT:
{
struct v4l2_format *f = arg;
@@ -785,6 +837,11 @@
case VIDIOC_S_STD:
h->standard = *((v4l2_std_id *) arg);
break;
+
+ case VIDIOC_G_CHIP_IDENT:
+ return v4l2_chip_ident_i2c_client(client,
+ arg, h->chip, h->revision);
+
default:
/* nothing */
break;
@@ -793,36 +850,55 @@
return err;
}
-/* ----------------------------------------------------------------------- */
-
-static struct i2c_driver driver = {
- .driver = {
- .name = "saa6752hs",
- },
- .id = I2C_DRIVERID_SAA6752HS,
- .attach_adapter = saa6752hs_probe,
- .detach_client = saa6752hs_detach,
- .command = saa6752hs_command,
-};
-
-static struct i2c_client client_template =
+static int saa6752hs_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
- .name = "saa6752hs",
- .driver = &driver,
-};
+ struct saa6752hs_state *h = kzalloc(sizeof(*h), GFP_KERNEL);
+ u8 addr = 0x13;
+ u8 data[12];
-static int __init saa6752hs_init_module(void)
-{
- return i2c_add_driver(&driver);
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+ if (h == NULL)
+ return -ENOMEM;
+
+ i2c_master_send(client, &addr, 1);
+ i2c_master_recv(client, data, sizeof(data));
+ h->chip = V4L2_IDENT_SAA6752HS;
+ h->revision = (data[8] << 8) | data[9];
+ h->has_ac3 = 0;
+ if (h->revision == 0x0206) {
+ h->chip = V4L2_IDENT_SAA6752HS_AC3;
+ h->has_ac3 = 1;
+ v4l_info(client, "support AC-3\n");
+ }
+ h->params = param_defaults;
+ h->standard = 0; /* Assume 625 input lines */
+
+ i2c_set_clientdata(client, h);
+ return 0;
}
-static void __exit saa6752hs_cleanup_module(void)
+static int saa6752hs_remove(struct i2c_client *client)
{
- i2c_del_driver(&driver);
+ kfree(i2c_get_clientdata(client));
+ return 0;
}
-module_init(saa6752hs_init_module);
-module_exit(saa6752hs_cleanup_module);
+static const struct i2c_device_id saa6752hs_id[] = {
+ { "saa6752hs", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, saa6752hs_id);
+
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "saa6752hs",
+ .driverid = I2C_DRIVERID_SAA6752HS,
+ .command = saa6752hs_command,
+ .probe = saa6752hs_probe,
+ .remove = saa6752hs_remove,
+ .id_table = saa6752hs_id,
+};
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c
index 98364d1..ddc5402 100644
--- a/drivers/media/video/saa7134/saa7134-cards.c
+++ b/drivers/media/video/saa7134/saa7134-cards.c
@@ -3260,6 +3260,7 @@
},
[SAA7134_BOARD_HAUPPAUGE_HVR1110] = {
/* Thomas Genty <tomlohave@gmail.com> */
+ /* David Bentham <db260179@hotmail.com> */
.name = "Hauppauge WinTV-HVR1110 DVB-T/Hybrid",
.audio_clock = 0x00187de7,
.tuner_type = TUNER_PHILIPS_TDA8290,
@@ -3268,23 +3269,26 @@
.radio_addr = ADDR_UNSET,
.tuner_config = 1,
.mpeg = SAA7134_MPEG_DVB,
+ .gpiomask = 0x0200100,
.inputs = {{
.name = name_tv,
.vmux = 1,
.amux = TV,
.tv = 1,
- },{
- .name = name_comp1,
- .vmux = 3,
- .amux = LINE2, /* FIXME: audio doesn't work on svideo/composite */
- },{
- .name = name_svideo,
- .vmux = 8,
- .amux = LINE2, /* FIXME: audio doesn't work on svideo/composite */
- }},
+ .gpio = 0x0000100,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
.radio = {
.name = name_radio,
- .amux = TV,
+ .amux = TV,
+ .gpio = 0x0200100,
},
},
[SAA7134_BOARD_CINERGY_HT_PCMCIA] = {
@@ -3388,6 +3392,42 @@
.amux = 0,
},
},
+ [SAA7134_BOARD_ENCORE_ENLTV_FM53] = {
+ .name = "Encore ENLTV-FM v5.3",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_TNF_5335MF,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x7000,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 1,
+ .amux = 1,
+ .tv = 1,
+ .gpio = 0x50000,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = 2,
+ .gpio = 0x2000,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = 2,
+ .gpio = 0x2000,
+ } },
+ .radio = {
+ .name = name_radio,
+ .vmux = 1,
+ .amux = 1,
+ },
+ .mute = {
+ .name = name_mute,
+ .gpio = 0xf000,
+ .amux = 0,
+ },
+ },
[SAA7134_BOARD_CINERGY_HT_PCI] = {
.name = "Terratec Cinergy HT PCI",
.audio_clock = 0x00187de7,
@@ -3631,6 +3671,40 @@
.tv = 1,
}},
},
+ [SAA7134_BOARD_AVERMEDIA_M135A] = {
+ .name = "Avermedia PCI pure analog (M135A)",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tuner_config = 2,
+ .gpiomask = 0x020200000,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x00200000,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = TV,
+ .gpio = 0x01,
+ },
+ },
[SAA7134_BOARD_BEHOLD_401] = {
/* Beholder Intl. Ltd. 2008 */
/*Dmitry Belimov <d.belimov@gmail.com> */
@@ -4409,6 +4483,129 @@
/* no DVB support for now */
/* .mpeg = SAA7134_MPEG_DVB, */
},
+ [SAA7134_BOARD_ASUSTeK_TIGER_3IN1] = {
+ .name = "Asus Tiger 3in1",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tuner_config = 2,
+ .gpiomask = 1 << 21,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp,
+ .vmux = 0,
+ .amux = LINE2,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x0200000,
+ },
+ },
+ [SAA7134_BOARD_REAL_ANGEL_220] = {
+ .name = "Zogis Real Angel 220",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_TNF_5335MF,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x801a8087,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 3,
+ .amux = LINE2,
+ .tv = 1,
+ .gpio = 0x624000,
+ }, {
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ .gpio = 0x624000,
+ }, {
+ .name = name_svideo,
+ .vmux = 1,
+ .amux = LINE1,
+ .gpio = 0x624000,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ .gpio = 0x624001,
+ },
+ .mute = {
+ .name = name_mute,
+ .amux = TV,
+ },
+ },
+ [SAA7134_BOARD_ADS_INSTANT_HDTV_PCI] = {
+ .name = "ADS Tech Instant HDTV",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TUV1236D,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp,
+ .vmux = 4,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ },
+ [SAA7134_BOARD_ASUSTeK_TIGER] = {
+ .name = "Asus Tiger Rev:1.00",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tuner_config = 0,
+ .mpeg = SAA7134_MPEG_DVB,
+ .gpiomask = 0x0200000,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE2,
+ }, {
+ .name = name_comp2,
+ .vmux = 0,
+ .amux = LINE2,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x0200000,
+ },
+ },
};
const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards);
@@ -4777,6 +4974,12 @@
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1461, /* Avermedia Technologies Inc */
+ .subdevice = 0xf11d,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_M135A,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7130,
.subvendor = PCI_VENDOR_ID_PHILIPS,
.subdevice = 0x2004,
@@ -5157,6 +5360,12 @@
.driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM,
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x1a7f,
+ .subdevice = 0x2008,
+ .driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM53,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
.subvendor = 0x153b,
.subdevice = 0x1175,
@@ -5183,8 +5392,8 @@
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
.subvendor = 0x1043,
- .subdevice = 0x4857,
- .driver_data = SAA7134_BOARD_ASUSTeK_P7131_DUAL,
+ .subdevice = 0x4857, /* REV:1.00 */
+ .driver_data = SAA7134_BOARD_ASUSTeK_TIGER,
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7134,
@@ -5415,6 +5624,12 @@
.driver_data = SAA7134_BOARD_VIDEOMATE_T750,
}, {
.vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA7135HL */
+ .subvendor = 0x1421,
+ .subdevice = 0x0380,
+ .driver_data = SAA7134_BOARD_ADS_INSTANT_HDTV_PCI,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
.subvendor = 0x5169,
.subdevice = 0x1502,
@@ -5432,6 +5647,12 @@
.subdevice = 0xf636,
.driver_data = SAA7134_BOARD_AVERMEDIA_M103,
}, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1043,
+ .subdevice = 0x4878, /* REV:1.02G */
+ .driver_data = SAA7134_BOARD_ASUSTeK_TIGER_3IN1,
+ }, {
/* --- boards without eeprom + subsystem ID --- */
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7134,
@@ -5540,7 +5761,7 @@
return 0;
}
-int saa7134_tuner_callback(void *priv, int command, int arg)
+int saa7134_tuner_callback(void *priv, int component, int command, int arg)
{
struct saa7134_dev *dev = priv;
if (dev != NULL) {
@@ -5620,6 +5841,7 @@
case SAA7134_BOARD_AVERMEDIA_STUDIO_507:
case SAA7134_BOARD_AVERMEDIA_GO_007_FM:
case SAA7134_BOARD_AVERMEDIA_777:
+ case SAA7134_BOARD_AVERMEDIA_M135A:
/* case SAA7134_BOARD_SABRENT_SBTTVFM: */ /* not finished yet */
case SAA7134_BOARD_VIDEOMATE_TV_PVR:
case SAA7134_BOARD_VIDEOMATE_GOLD_PLUS:
@@ -5644,6 +5866,7 @@
case SAA7134_BOARD_AVERMEDIA_A16AR:
case SAA7134_BOARD_ENCORE_ENLTV:
case SAA7134_BOARD_ENCORE_ENLTV_FM:
+ case SAA7134_BOARD_ENCORE_ENLTV_FM53:
case SAA7134_BOARD_10MOONSTVMASTER3:
case SAA7134_BOARD_BEHOLD_401:
case SAA7134_BOARD_BEHOLD_403:
@@ -5656,6 +5879,7 @@
case SAA7134_BOARD_BEHOLD_505FM:
case SAA7134_BOARD_BEHOLD_507_9FM:
case SAA7134_BOARD_GENIUS_TVGO_A11MCE:
+ case SAA7134_BOARD_REAL_ANGEL_220:
dev->has_remote = SAA7134_REMOTE_GPIO;
break;
case SAA7134_BOARD_FLYDVBS_LR300:
@@ -5745,6 +5969,7 @@
case SAA7134_BOARD_PINNACLE_PCTV_110i:
case SAA7134_BOARD_PINNACLE_PCTV_310i:
case SAA7134_BOARD_UPMOST_PURPLE_TV:
+ case SAA7134_BOARD_MSI_TVATANYWHERE_PLUS:
case SAA7134_BOARD_HAUPPAUGE_HVR1110:
case SAA7134_BOARD_BEHOLD_607_9FM:
case SAA7134_BOARD_BEHOLD_M6:
@@ -5987,6 +6212,7 @@
case SAA7134_BOARD_PINNACLE_PCTV_310i:
case SAA7134_BOARD_KWORLD_DVBT_210:
case SAA7134_BOARD_TEVION_DVBT_220RF:
+ case SAA7134_BOARD_ASUSTeK_TIGER:
case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
case SAA7134_BOARD_MEDION_MD8800_QUADRO:
@@ -6002,6 +6228,14 @@
i2c_transfer(&dev->i2c_adap, &msg, 1);
break;
}
+ case SAA7134_BOARD_ASUSTeK_TIGER_3IN1:
+ {
+ u8 data[] = { 0x3c, 0x33, 0x60};
+ struct i2c_msg msg = {.addr = 0x0b, .flags = 0, .buf = data,
+ .len = sizeof(data)};
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
+ break;
+ }
case SAA7134_BOARD_FLYDVB_TRIO:
{
u8 data[] = { 0x3c, 0x33, 0x62};
@@ -6027,6 +6261,7 @@
i2c_transfer(&dev->i2c_adap, &msg, 1);
break;
}
+ case SAA7134_BOARD_ADS_INSTANT_HDTV_PCI:
case SAA7134_BOARD_KWORLD_ATSC110:
{
/* enable tuner */
diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c
index 75d6184..b686bfa 100644
--- a/drivers/media/video/saa7134/saa7134-core.c
+++ b/drivers/media/video/saa7134/saa7134-core.c
@@ -215,7 +215,7 @@
int saa7134_pgtable_alloc(struct pci_dev *pci, struct saa7134_pgtable *pt)
{
__le32 *cpu;
- dma_addr_t dma_addr;
+ dma_addr_t dma_addr = 0;
cpu = pci_alloc_consistent(pci, SAA7134_PGTABLE_SIZE, &dma_addr);
if (NULL == cpu)
@@ -359,32 +359,6 @@
spin_unlock_irqrestore(&dev->slock,flags);
}
-/* resends a current buffer in queue after resume */
-
-static int saa7134_buffer_requeue(struct saa7134_dev *dev,
- struct saa7134_dmaqueue *q)
-{
- struct saa7134_buf *buf, *next;
-
- assert_spin_locked(&dev->slock);
-
- buf = q->curr;
- next = buf;
- dprintk("buffer_requeue\n");
-
- if (!buf)
- return 0;
-
- dprintk("buffer_requeue : resending active buffers \n");
-
- if (!list_empty(&q->queue))
- next = list_entry(q->queue.next, struct saa7134_buf,
- vb.queue);
- buf->activate(dev, buf, next);
-
- return 0;
-}
-
/* ------------------------------------------------------------------ */
int saa7134_set_dmabits(struct saa7134_dev *dev)
@@ -442,9 +416,7 @@
/* TS capture -- dma 5 */
if (dev->ts_q.curr) {
ctrl |= SAA7134_MAIN_CTRL_TE5;
- irq |= SAA7134_IRQ1_INTE_RA2_3 |
- SAA7134_IRQ1_INTE_RA2_2 |
- SAA7134_IRQ1_INTE_RA2_1 |
+ irq |= SAA7134_IRQ1_INTE_RA2_1 |
SAA7134_IRQ1_INTE_RA2_0;
}
@@ -727,6 +699,10 @@
irq2_mask |= SAA7134_IRQ2_INTE_GPIO18A;
}
+ if (dev->has_remote == SAA7134_REMOTE_I2C) {
+ request_module("ir-kbd-i2c");
+ }
+
saa_writel(SAA7134_IRQ1, 0);
saa_writel(SAA7134_IRQ2, irq2_mask);
@@ -1139,6 +1115,32 @@
}
#ifdef CONFIG_PM
+
+/* resends a current buffer in queue after resume */
+static int saa7134_buffer_requeue(struct saa7134_dev *dev,
+ struct saa7134_dmaqueue *q)
+{
+ struct saa7134_buf *buf, *next;
+
+ assert_spin_locked(&dev->slock);
+
+ buf = q->curr;
+ next = buf;
+ dprintk("buffer_requeue\n");
+
+ if (!buf)
+ return 0;
+
+ dprintk("buffer_requeue : resending active buffers \n");
+
+ if (!list_empty(&q->queue))
+ next = list_entry(q->queue.next, struct saa7134_buf,
+ vb.queue);
+ buf->activate(dev, buf, next);
+
+ return 0;
+}
+
static int saa7134_suspend(struct pci_dev *pci_dev , pm_message_t state)
{
diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c
index be48b9b..87c1098 100644
--- a/drivers/media/video/saa7134/saa7134-dvb.c
+++ b/drivers/media/video/saa7134/saa7134-dvb.c
@@ -553,7 +553,6 @@
/* ------------------------------------------------------------------ */
static struct tda827x_config tda827x_cfg_0 = {
- .tuner_callback = saa7134_tuner_callback,
.init = philips_tda827x_tuner_init,
.sleep = philips_tda827x_tuner_sleep,
.config = 0,
@@ -561,7 +560,6 @@
};
static struct tda827x_config tda827x_cfg_1 = {
- .tuner_callback = saa7134_tuner_callback,
.init = philips_tda827x_tuner_init,
.sleep = philips_tda827x_tuner_sleep,
.config = 1,
@@ -569,7 +567,6 @@
};
static struct tda827x_config tda827x_cfg_2 = {
- .tuner_callback = saa7134_tuner_callback,
.init = philips_tda827x_tuner_init,
.sleep = philips_tda827x_tuner_sleep,
.config = 2,
@@ -577,7 +574,6 @@
};
static struct tda827x_config tda827x_cfg_2_sw42 = {
- .tuner_callback = saa7134_tuner_callback,
.init = philips_tda827x_tuner_init,
.sleep = philips_tda827x_tuner_sleep,
.config = 2,
@@ -799,6 +795,20 @@
.request_firmware = philips_tda1004x_request_firmware
};
+static struct tda1004x_config asus_tiger_3in1_config = {
+ .demod_address = 0x0b,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .antenna_switch = 1,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
/* ------------------------------------------------------------------
* special case: this card uses saa713x GPIO22 for the mode switch
*/
@@ -822,7 +832,6 @@
}
static struct tda827x_config ads_duo_cfg = {
- .tuner_callback = saa7134_tuner_callback,
.init = ads_duo_tuner_init,
.sleep = ads_duo_tuner_sleep,
.config = 0
@@ -1147,6 +1156,7 @@
dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
NULL, DVB_PLL_TDHU2);
break;
+ case SAA7134_BOARD_ADS_INSTANT_HDTV_PCI:
case SAA7134_BOARD_KWORLD_ATSC110:
dev->dvb.frontend = dvb_attach(nxt200x_attach, &kworldatsc110,
&dev->i2c_adap);
@@ -1300,6 +1310,36 @@
&dev->i2c_adap);
attach_xc3028 = 1;
break;
+ case SAA7134_BOARD_ASUSTeK_TIGER_3IN1:
+ if (!use_frontend) { /* terrestrial */
+ if (configure_tda827x_fe(dev, &asus_tiger_3in1_config,
+ &tda827x_cfg_2) < 0)
+ goto dettach_frontend;
+ } else { /* satellite */
+ dev->dvb.frontend = dvb_attach(tda10086_attach,
+ &flydvbs, &dev->i2c_adap);
+ if (dev->dvb.frontend) {
+ if (dvb_attach(tda826x_attach,
+ dev->dvb.frontend, 0x60,
+ &dev->i2c_adap, 0) == NULL) {
+ wprintk("%s: Asus Tiger 3in1, no "
+ "tda826x found!\n", __func__);
+ goto dettach_frontend;
+ }
+ if (dvb_attach(lnbp21_attach, dev->dvb.frontend,
+ &dev->i2c_adap, 0, 0) == NULL) {
+ wprintk("%s: Asus Tiger 3in1, no lnbp21"
+ " found!\n", __func__);
+ goto dettach_frontend;
+ }
+ }
+ }
+ break;
+ case SAA7134_BOARD_ASUSTeK_TIGER:
+ if (configure_tda827x_fe(dev, &philips_tiger_config,
+ &tda827x_cfg_0) < 0)
+ goto dettach_frontend;
+ break;
default:
wprintk("Huh? unknown DVB card?\n");
break;
@@ -1327,6 +1367,8 @@
printk(KERN_ERR "%s/dvb: frontend initialization failed\n", dev->name);
return -1;
}
+ /* define general-purpose callback pointer */
+ dev->dvb.frontend->callback = saa7134_tuner_callback;
/* register everything else */
ret = videobuf_dvb_register(&dev->dvb, THIS_MODULE, dev, &dev->pci->dev,
diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c
index c0c5d75..9a8766a 100644
--- a/drivers/media/video/saa7134/saa7134-empress.c
+++ b/drivers/media/video/saa7134/saa7134-empress.c
@@ -29,6 +29,7 @@
#include <media/saa6752hs.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
/* ------------------------------------------------------------------ */
@@ -63,10 +64,19 @@
static int ts_init_encoder(struct saa7134_dev* dev)
{
- struct v4l2_ext_controls ctrls = { V4L2_CTRL_CLASS_MPEG, 0 };
+ u32 leading_null_bytes = 0;
+ /* If more cards start to need this, then this
+ should probably be added to the card definitions. */
+ switch (dev->board) {
+ case SAA7134_BOARD_BEHOLD_M6:
+ case SAA7134_BOARD_BEHOLD_M63:
+ case SAA7134_BOARD_BEHOLD_M6_EXTRA:
+ leading_null_bytes = 1;
+ break;
+ }
ts_reset_encoder(dev);
- saa7134_i2c_call_clients(dev, VIDIOC_S_EXT_CTRLS, &ctrls);
+ saa7134_i2c_call_clients(dev, VIDIOC_INT_INIT, &leading_null_bytes);
dev->empress_started = 1;
return 0;
}
@@ -79,9 +89,11 @@
struct saa7134_dev *dev;
int err;
+ lock_kernel();
list_for_each_entry(dev, &saa7134_devlist, devlist)
if (dev->empress_dev && dev->empress_dev->minor == minor)
goto found;
+ unlock_kernel();
return -ENODEV;
found:
@@ -103,6 +115,7 @@
done_up:
mutex_unlock(&dev->empress_tsq.vb_lock);
done:
+ unlock_kernel();
return err;
}
@@ -290,15 +303,6 @@
return videobuf_streamoff(&dev->empress_tsq);
}
-static int saa7134_i2c_call_saa6752(struct saa7134_dev *dev,
- unsigned int cmd, void *arg)
-{
- if (dev->mpeg_i2c_client == NULL)
- return -EINVAL;
- return dev->mpeg_i2c_client->driver->command(dev->mpeg_i2c_client,
- cmd, arg);
-}
-
static int empress_s_ext_ctrls(struct file *file, void *priv,
struct v4l2_ext_controls *ctrls)
{
@@ -400,6 +404,39 @@
return saa7134_i2c_call_saa6752(dev, VIDIOC_QUERYMENU, c);
}
+static int empress_g_chip_ident(struct file *file, void *fh,
+ struct v4l2_chip_ident *chip)
+{
+ struct saa7134_dev *dev = file->private_data;
+
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+ if (dev->mpeg_i2c_client == NULL)
+ return -EINVAL;
+ if (chip->match_type == V4L2_CHIP_MATCH_I2C_DRIVER &&
+ chip->match_chip == I2C_DRIVERID_SAA6752HS)
+ return saa7134_i2c_call_saa6752(dev, VIDIOC_G_CHIP_IDENT, chip);
+ if (chip->match_type == V4L2_CHIP_MATCH_I2C_ADDR &&
+ chip->match_chip == dev->mpeg_i2c_client->addr)
+ return saa7134_i2c_call_saa6752(dev, VIDIOC_G_CHIP_IDENT, chip);
+ return -EINVAL;
+}
+
+static int empress_s_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct saa7134_dev *dev = file->private_data;
+
+ return saa7134_s_std_internal(dev, NULL, id);
+}
+
+static int empress_g_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct saa7134_dev *dev = file->private_data;
+
+ *id = dev->tvnorm->id;
+ return 0;
+}
+
static const struct file_operations ts_fops =
{
.owner = THIS_MODULE,
@@ -428,11 +465,13 @@
.vidioc_enum_input = empress_enum_input,
.vidioc_g_input = empress_g_input,
.vidioc_s_input = empress_s_input,
-
.vidioc_queryctrl = empress_queryctrl,
.vidioc_querymenu = empress_querymenu,
.vidioc_g_ctrl = empress_g_ctrl,
.vidioc_s_ctrl = empress_s_ctrl,
+ .vidioc_g_chip_ident = empress_g_chip_ident,
+ .vidioc_s_std = empress_s_std,
+ .vidioc_g_std = empress_g_std,
};
/* ----------------------------------------------------------- */
diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c
index 5f713e6..20c1b33 100644
--- a/drivers/media/video/saa7134/saa7134-i2c.c
+++ b/drivers/media/video/saa7134/saa7134-i2c.c
@@ -337,6 +337,7 @@
case 0x47:
case 0x71:
case 0x2d:
+ case 0x30:
{
struct IR_i2c *ir = i2c_get_clientdata(client);
d1printk("%s i2c IR detected (%s).\n",
@@ -427,6 +428,16 @@
i2c_clients_command(&dev->i2c_adap, cmd, arg);
}
+int saa7134_i2c_call_saa6752(struct saa7134_dev *dev,
+ unsigned int cmd, void *arg)
+{
+ if (dev->mpeg_i2c_client == NULL)
+ return -EINVAL;
+ return dev->mpeg_i2c_client->driver->command(dev->mpeg_i2c_client,
+ cmd, arg);
+}
+EXPORT_SYMBOL_GPL(saa7134_i2c_call_saa6752);
+
int saa7134_i2c_register(struct saa7134_dev *dev)
{
dev->i2c_adap = saa7134_adap_template;
diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c
index ad08d13..c53fd5f 100644
--- a/drivers/media/video/saa7134/saa7134-input.c
+++ b/drivers/media/video/saa7134/saa7134-input.c
@@ -62,8 +62,11 @@
#define i2cdprintk(fmt, arg...) if (ir_debug) \
printk(KERN_DEBUG "%s/ir: " fmt, ir->c.name , ## arg)
-/** rc5 functions */
+/* Helper functions for RC5 and NEC decoding at GPIO16 or GPIO18 */
static int saa7134_rc5_irq(struct saa7134_dev *dev);
+static int saa7134_nec_irq(struct saa7134_dev *dev);
+static void nec_task(unsigned long data);
+static void saa7134_nec_timer(unsigned long data);
/* -------------------- GPIO generic keycode builder -------------------- */
@@ -115,6 +118,53 @@
/* --------------------- Chip specific I2C key builders ----------------- */
+static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir, u32 *ir_key,
+ u32 *ir_raw)
+{
+ unsigned char b;
+ int gpio;
+
+ /* <dev> is needed to access GPIO. Used by the saa_readl macro. */
+ struct saa7134_dev *dev = ir->c.adapter->algo_data;
+ if (dev == NULL) {
+ dprintk("get_key_msi_tvanywhere_plus: "
+ "gir->c.adapter->algo_data is NULL!\n");
+ return -EIO;
+ }
+
+ /* rising SAA7134_GPIO_GPRESCAN reads the status */
+
+ saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+ saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+
+ gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
+
+ /* GPIO&0x40 is pulsed low when a button is pressed. Don't do
+ I2C receive if gpio&0x40 is not low. */
+
+ if (gpio & 0x40)
+ return 0; /* No button press */
+
+ /* GPIO says there is a button press. Get it. */
+
+ if (1 != i2c_master_recv(&ir->c, &b, 1)) {
+ i2cdprintk("read error\n");
+ return -EIO;
+ }
+
+ /* No button press */
+
+ if (b == 0xff)
+ return 0;
+
+ /* Button pressed */
+
+ dprintk("get_key_msi_tvanywhere_plus: Key = 0x%02X\n", b);
+ *ir_key = b;
+ *ir_raw = b;
+ return 1;
+}
+
static int get_key_purpletv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
{
unsigned char b;
@@ -280,7 +330,9 @@
{
struct card_ir *ir = dev->remote;
- if (!ir->polling && !ir->rc5_gpio) {
+ if (ir->nec_gpio) {
+ saa7134_nec_irq(dev);
+ } else if (!ir->polling && !ir->rc5_gpio) {
build_key(dev);
} else if (ir->rc5_gpio) {
saa7134_rc5_irq(dev);
@@ -316,6 +368,10 @@
ir->addr = 0x17;
ir->rc5_key_timeout = ir_rc5_key_timeout;
ir->rc5_remote_gap = ir_rc5_remote_gap;
+ } else if (ir->nec_gpio) {
+ setup_timer(&ir->timer_keyup, saa7134_nec_timer,
+ (unsigned long)dev);
+ tasklet_init(&ir->tlet, nec_task, (unsigned long)dev);
}
}
@@ -335,6 +391,7 @@
u32 mask_keyup = 0;
int polling = 0;
int rc5_gpio = 0;
+ int nec_gpio = 0;
int ir_type = IR_TYPE_OTHER;
int err;
@@ -391,6 +448,12 @@
saa_setb(SAA7134_GPIO_GPMODE0, 0x4);
saa_setb(SAA7134_GPIO_GPSTATUS0, 0x4);
break;
+ case SAA7134_BOARD_AVERMEDIA_M135A:
+ ir_codes = ir_codes_avermedia_m135a;
+ mask_keydown = 0x0040000;
+ mask_keycode = 0x00013f;
+ nec_gpio = 1;
+ break;
case SAA7134_BOARD_AVERMEDIA_777:
case SAA7134_BOARD_AVERMEDIA_A16AR:
ir_codes = ir_codes_avermedia;
@@ -499,6 +562,12 @@
mask_keyup = 0x040000;
polling = 50; // ms
break;
+ case SAA7134_BOARD_ENCORE_ENLTV_FM53:
+ ir_codes = ir_codes_encore_enltv_fm53;
+ mask_keydown = 0x0040000;
+ mask_keycode = 0x00007f;
+ nec_gpio = 1;
+ break;
case SAA7134_BOARD_10MOONSTVMASTER3:
ir_codes = ir_codes_encore_enltv;
mask_keycode = 0x5f80000;
@@ -511,6 +580,12 @@
mask_keydown = 0xf00000;
polling = 50; /* ms */
break;
+ case SAA7134_BOARD_REAL_ANGEL_220:
+ ir_codes = ir_codes_real_audio_220_32_keys;
+ mask_keycode = 0x3f00;
+ mask_keyup = 0x4000;
+ polling = 50; /* ms */
+ break;
}
if (NULL == ir_codes) {
printk("%s: Oops: IR config error [card=%d]\n",
@@ -533,6 +608,7 @@
ir->mask_keyup = mask_keyup;
ir->polling = polling;
ir->rc5_gpio = rc5_gpio;
+ ir->nec_gpio = nec_gpio;
/* init input device */
snprintf(ir->name, sizeof(ir->name), "saa7134 IR (%s)",
@@ -612,6 +688,11 @@
ir->get_key = get_key_purpletv;
ir->ir_codes = ir_codes_purpletv;
break;
+ case SAA7134_BOARD_MSI_TVATANYWHERE_PLUS:
+ snprintf(ir->c.name, sizeof(ir->c.name), "MSI TV@nywhere Plus");
+ ir->get_key = get_key_msi_tvanywhere_plus;
+ ir->ir_codes = ir_codes_msi_tvanywhere_plus;
+ break;
case SAA7134_BOARD_HAUPPAUGE_HVR1110:
snprintf(ir->c.name, sizeof(ir->c.name), "HVR 1110");
ir->get_key = get_key_hvr1110;
@@ -675,8 +756,125 @@
return 1;
}
-/* ----------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
+
+/* On NEC protocol, One has 2.25 ms, and zero has 1.125 ms
+ The first pulse (start) has 9 + 4.5 ms
*/
+
+static void saa7134_nec_timer(unsigned long data)
+{
+ struct saa7134_dev *dev = (struct saa7134_dev *) data;
+ struct card_ir *ir = dev->remote;
+
+ dprintk("Cancel key repeat\n");
+
+ ir_input_nokey(ir->dev, &ir->ir);
+}
+
+static void nec_task(unsigned long data)
+{
+ struct saa7134_dev *dev = (struct saa7134_dev *) data;
+ struct card_ir *ir;
+ struct timeval tv;
+ int count, pulse, oldpulse, gap;
+ u32 ircode = 0, not_code = 0;
+ int ngap = 0;
+
+ if (!data) {
+ printk(KERN_ERR "saa713x/ir: Can't recover dev struct\n");
+ /* GPIO will be kept disabled */
+ return;
+ }
+
+ ir = dev->remote;
+
+ /* rising SAA7134_GPIO_GPRESCAN reads the status */
+ saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+ saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+
+ oldpulse = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2) & ir->mask_keydown;
+ pulse = oldpulse;
+
+ do_gettimeofday(&tv);
+ ir->base_time = tv;
+
+ /* Decode NEC pulsecode. This code can take up to 76.5 ms to run.
+ Unfortunately, using IRQ to decode pulse didn't work, since it uses
+ a pulse train of 38KHz. This means one pulse on each 52 us
+ */
+ do {
+ /* Wait until the end of pulse/space or 5 ms */
+ for (count = 0; count < 500; count++) {
+ udelay(10);
+ /* rising SAA7134_GPIO_GPRESCAN reads the status */
+ saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+ saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+ pulse = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2)
+ & ir->mask_keydown;
+ if (pulse != oldpulse)
+ break;
+ }
+
+ do_gettimeofday(&tv);
+ gap = 1000000 * (tv.tv_sec - ir->base_time.tv_sec) +
+ tv.tv_usec - ir->base_time.tv_usec;
+
+ if (!pulse) {
+ /* Bit 0 has 560 us, while bit 1 has 1120 us.
+ Do something only if bit == 1
+ */
+ if (ngap && (gap > 560 + 280)) {
+ unsigned int shift = ngap - 1;
+
+ /* Address first, then command */
+ if (shift < 8) {
+ shift += 8;
+ ircode |= 1 << shift;
+ } else if (shift < 16) {
+ not_code |= 1 << shift;
+ } else if (shift < 24) {
+ shift -= 16;
+ ircode |= 1 << shift;
+ } else {
+ shift -= 24;
+ not_code |= 1 << shift;
+ }
+ }
+ ngap++;
+ }
+
+
+ ir->base_time = tv;
+
+ /* TIMEOUT - Long pulse */
+ if (gap >= 5000)
+ break;
+ oldpulse = pulse;
+ } while (ngap < 32);
+
+ if (ngap == 32) {
+ /* FIXME: should check if not_code == ~ircode */
+ ir->code = ir_extract_bits(ircode, ir->mask_keycode);
+
+ dprintk("scancode = 0x%02x (code = 0x%02x, notcode= 0x%02x)\n",
+ ir->code, ircode, not_code);
+
+ ir_input_keydown(ir->dev, &ir->ir, ir->code, ir->code);
+ } else
+ dprintk("Repeat last key\n");
+
+ /* Keep repeating the last key */
+ mod_timer(&ir->timer_keyup, jiffies + msecs_to_jiffies(150));
+
+ saa_setl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18);
+}
+
+static int saa7134_nec_irq(struct saa7134_dev *dev)
+{
+ struct card_ir *ir = dev->remote;
+
+ saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18);
+ tasklet_schedule(&ir->tlet);
+
+ return 1;
+}
diff --git a/drivers/media/video/saa7134/saa7134-ts.c b/drivers/media/video/saa7134/saa7134-ts.c
index eae72fd..ef55a59 100644
--- a/drivers/media/video/saa7134/saa7134-ts.c
+++ b/drivers/media/video/saa7134/saa7134-ts.c
@@ -66,11 +66,29 @@
saa7134_set_dmabits(dev);
mod_timer(&dev->ts_q.timeout, jiffies+BUFFER_TIMEOUT);
+
+ if (dev->ts_state == SAA7134_TS_BUFF_DONE) {
+ /* Clear TS cache */
+ dev->buff_cnt = 0;
+ saa_writeb(SAA7134_TS_SERIAL1, 0x00);
+ saa_writeb(SAA7134_TS_SERIAL1, 0x03);
+ saa_writeb(SAA7134_TS_SERIAL1, 0x00);
+ saa_writeb(SAA7134_TS_SERIAL1, 0x01);
+
+ /* TS clock non-inverted */
+ saa_writeb(SAA7134_TS_SERIAL1, 0x00);
+
+ /* Start TS stream */
+ saa_writeb(SAA7134_TS_SERIAL0, 0x40);
+ saa_writeb(SAA7134_TS_PARALLEL, 0xEC);
+ dev->ts_state = SAA7134_TS_STARTED;
+ }
+
return 0;
}
static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
- enum v4l2_field field)
+ enum v4l2_field field)
{
struct saa7134_dev *dev = q->priv_data;
struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
@@ -110,16 +128,22 @@
goto oops;
}
- /* dma: setup channel 5 (= TS) */
- control = SAA7134_RS_CONTROL_BURST_16 |
- SAA7134_RS_CONTROL_ME |
- (buf->pt->dma >> 12);
+ dev->buff_cnt++;
- saa_writeb(SAA7134_TS_DMA0, ((lines-1)&0xff));
- saa_writeb(SAA7134_TS_DMA1, (((lines-1)>>8)&0xff));
- saa_writeb(SAA7134_TS_DMA2, ((((lines-1)>>16)&0x3f) | 0x00)); /* TSNOPIT=0, TSCOLAP=0 */
- saa_writel(SAA7134_RS_PITCH(5),TS_PACKET_SIZE);
- saa_writel(SAA7134_RS_CONTROL(5),control);
+ if (dev->buff_cnt == dev->ts.nr_bufs) {
+ dev->ts_state = SAA7134_TS_BUFF_DONE;
+ /* dma: setup channel 5 (= TS) */
+ control = SAA7134_RS_CONTROL_BURST_16 |
+ SAA7134_RS_CONTROL_ME |
+ (buf->pt->dma >> 12);
+
+ saa_writeb(SAA7134_TS_DMA0, (lines - 1) & 0xff);
+ saa_writeb(SAA7134_TS_DMA1, ((lines - 1) >> 8) & 0xff);
+ /* TSNOPIT=0, TSCOLAP=0 */
+ saa_writeb(SAA7134_TS_DMA2, (((lines - 1) >> 16) & 0x3f) | 0x00);
+ saa_writel(SAA7134_RS_PITCH(5), TS_PACKET_SIZE);
+ saa_writel(SAA7134_RS_CONTROL(5), control);
+ }
buf->vb.state = VIDEOBUF_PREPARED;
buf->activate = buffer_activate;
@@ -140,6 +164,8 @@
if (0 == *count)
*count = dev->ts.nr_bufs;
*count = saa7134_buffer_count(*size,*count);
+ dev->buff_cnt = 0;
+ dev->ts_state = SAA7134_TS_STOPPED;
return 0;
}
@@ -154,7 +180,13 @@
static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
{
struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+ struct saa7134_dev *dev = q->priv_data;
+ if (dev->ts_state == SAA7134_TS_STARTED) {
+ /* Stop TS transport */
+ saa_writeb(SAA7134_TS_PARALLEL, 0x6c);
+ dev->ts_state = SAA7134_TS_STOPPED;
+ }
saa7134_dma_free(q,buf);
}
@@ -182,7 +214,7 @@
/* deactivate TS softreset */
saa_writeb(SAA7134_TS_SERIAL1, 0x00);
/* TSSOP high active, TSVAL high active, TSLOCK ignored */
- saa_writeb(SAA7134_TS_PARALLEL, 0xec);
+ saa_writeb(SAA7134_TS_PARALLEL, 0x6c);
saa_writeb(SAA7134_TS_PARALLEL_SERIAL, (TS_PACKET_SIZE-1));
saa_writeb(SAA7134_TS_DMA0, ((dev->ts.nr_packets-1)&0xff));
saa_writeb(SAA7134_TS_DMA1, (((dev->ts.nr_packets-1)>>8)&0xff));
diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c
index 68c2689..02bb674 100644
--- a/drivers/media/video/saa7134/saa7134-video.c
+++ b/drivers/media/video/saa7134/saa7134-video.c
@@ -628,6 +628,9 @@
if (card_in(dev, dev->ctl_input).tv)
saa7134_i2c_call_clients(dev, VIDIOC_S_STD, &dev->tvnorm->id);
+ /* Set the correct norm for the saa6752hs. This function
+ does nothing if there is no saa6752hs. */
+ saa7134_i2c_call_saa6752(dev, VIDIOC_S_STD, &dev->tvnorm->id);
}
static void set_h_prescale(struct saa7134_dev *dev, int task, int prescale)
@@ -1330,6 +1333,8 @@
struct saa7134_fh *fh;
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
int radio = 0;
+
+ lock_kernel();
list_for_each_entry(dev, &saa7134_devlist, devlist) {
if (dev->video_dev && (dev->video_dev->minor == minor))
goto found;
@@ -1342,6 +1347,7 @@
goto found;
}
}
+ unlock_kernel();
return -ENODEV;
found:
@@ -1350,8 +1356,10 @@
/* allocate + initialize per filehandle data */
fh = kzalloc(sizeof(*fh),GFP_KERNEL);
- if (NULL == fh)
+ if (NULL == fh) {
+ unlock_kernel();
return -ENOMEM;
+ }
file->private_data = fh;
fh->dev = dev;
fh->radio = radio;
@@ -1384,6 +1392,7 @@
/* switch to video/vbi mode */
video_mux(dev,dev->ctl_input);
}
+ unlock_kernel();
return 0;
}
@@ -1790,18 +1799,25 @@
return 0;
}
-static int saa7134_s_std(struct file *file, void *priv, v4l2_std_id *id)
+int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_std_id *id)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
unsigned long flags;
unsigned int i;
v4l2_std_id fixup;
int err;
- err = v4l2_prio_check(&dev->prio, &fh->prio);
- if (0 != err)
- return err;
+ /* When called from the empress code fh == NULL.
+ That needs to be fixed somehow, but for now this is
+ good enough. */
+ if (fh) {
+ err = v4l2_prio_check(&dev->prio, &fh->prio);
+ if (0 != err)
+ return err;
+ } else if (res_locked(dev, RESOURCE_OVERLAY)) {
+ /* Don't change the std from the mpeg device
+ if overlay is active. */
+ return -EBUSY;
+ }
for (i = 0; i < TVNORMS; i++)
if (*id == tvnorms[i].id)
@@ -1834,7 +1850,7 @@
*id = tvnorms[i].id;
mutex_lock(&dev->lock);
- if (res_check(fh, RESOURCE_OVERLAY)) {
+ if (fh && res_check(fh, RESOURCE_OVERLAY)) {
spin_lock_irqsave(&dev->slock, flags);
stop_preview(dev, fh);
spin_unlock_irqrestore(&dev->slock, flags);
@@ -1851,6 +1867,23 @@
mutex_unlock(&dev->lock);
return 0;
}
+EXPORT_SYMBOL_GPL(saa7134_s_std_internal);
+
+static int saa7134_s_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct saa7134_fh *fh = priv;
+
+ return saa7134_s_std_internal(fh->dev, fh, id);
+}
+
+static int saa7134_g_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+
+ *id = dev->tvnorm->id;
+ return 0;
+}
static int saa7134_cropcap(struct file *file, void *priv,
struct v4l2_cropcap *cap)
@@ -2077,18 +2110,6 @@
return 0;
}
-static int saa7134_enum_fmt_vbi_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- if (0 != f->index)
- return -EINVAL;
-
- f->pixelformat = V4L2_PIX_FMT_GREY;
- strcpy(f->description, "vbi data");
-
- return 0;
-}
-
static int saa7134_g_fbuf(struct file *file, void *f,
struct v4l2_framebuffer *fb)
{
@@ -2379,7 +2400,6 @@
.vidioc_g_fmt_vid_overlay = saa7134_g_fmt_vid_overlay,
.vidioc_try_fmt_vid_overlay = saa7134_try_fmt_vid_overlay,
.vidioc_s_fmt_vid_overlay = saa7134_s_fmt_vid_overlay,
- .vidioc_enum_fmt_vbi_cap = saa7134_enum_fmt_vbi_cap,
.vidioc_g_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap,
.vidioc_try_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap,
.vidioc_s_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap,
@@ -2391,6 +2411,7 @@
.vidioc_qbuf = saa7134_qbuf,
.vidioc_dqbuf = saa7134_dqbuf,
.vidioc_s_std = saa7134_s_std,
+ .vidioc_g_std = saa7134_g_std,
.vidioc_enum_input = saa7134_enum_input,
.vidioc_g_input = saa7134_g_input,
.vidioc_s_input = saa7134_s_input,
diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
index a0884f6..491ab1f 100644
--- a/drivers/media/video/saa7134/saa7134.h
+++ b/drivers/media/video/saa7134/saa7134.h
@@ -269,6 +269,12 @@
#define SAA7134_BOARD_BEHOLD_M6_EXTRA 144
#define SAA7134_BOARD_AVERMEDIA_M103 145
#define SAA7134_BOARD_ASUSTeK_P7131_ANALOG 146
+#define SAA7134_BOARD_ASUSTeK_TIGER_3IN1 147
+#define SAA7134_BOARD_ENCORE_ENLTV_FM53 148
+#define SAA7134_BOARD_AVERMEDIA_M135A 149
+#define SAA7134_BOARD_REAL_ANGEL_220 150
+#define SAA7134_BOARD_ADS_INSTANT_HDTV_PCI 151
+#define SAA7134_BOARD_ASUSTeK_TIGER 152
#define SAA7134_MAXBOARDS 8
#define SAA7134_INPUT_MAX 8
@@ -462,6 +468,12 @@
void (*signal_change)(struct saa7134_dev *dev);
};
+enum saa7134_ts_status {
+ SAA7134_TS_STOPPED,
+ SAA7134_TS_BUFF_DONE,
+ SAA7134_TS_STARTED,
+};
+
/* global device status */
struct saa7134_dev {
struct list_head devlist;
@@ -555,6 +567,8 @@
/* SAA7134_MPEG_* */
struct saa7134_ts ts;
struct saa7134_dmaqueue ts_q;
+ enum saa7134_ts_status ts_state;
+ unsigned int buff_cnt;
struct saa7134_mpeg_ops *mops;
struct i2c_client *mpeg_i2c_client;
@@ -644,7 +658,7 @@
extern int saa7134_board_init1(struct saa7134_dev *dev);
extern int saa7134_board_init2(struct saa7134_dev *dev);
-int saa7134_tuner_callback(void *priv, int command, int arg);
+int saa7134_tuner_callback(void *priv, int component, int command, int arg);
/* ----------------------------------------------------------- */
@@ -654,6 +668,8 @@
int saa7134_i2c_unregister(struct saa7134_dev *dev);
void saa7134_i2c_call_clients(struct saa7134_dev *dev,
unsigned int cmd, void *arg);
+int saa7134_i2c_call_saa6752(struct saa7134_dev *dev,
+ unsigned int cmd, void *arg);
/* ----------------------------------------------------------- */
@@ -666,6 +682,7 @@
int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c);
int saa7134_g_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c);
int saa7134_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c);
+int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_std_id *id);
int saa7134_videoport_init(struct saa7134_dev *dev);
void saa7134_set_tvnorm_hw(struct saa7134_dev *dev);
diff --git a/drivers/media/video/se401.c b/drivers/media/video/se401.c
index acceed5..ae39491 100644
--- a/drivers/media/video/se401.c
+++ b/drivers/media/video/se401.c
@@ -288,7 +288,7 @@
int status;
if (!se401->dev) {
- info("ohoh: device vapourished");
+ dev_info(&urb->dev->dev, "device vapourished\n");
return;
}
@@ -328,7 +328,7 @@
return;
if (!se401->dev) {
- info ("ohoh: device vapourished");
+ dev_info(&urb->dev->dev, "device vapourished\n");
return;
}
@@ -375,7 +375,7 @@
urb->status=0;
urb->dev=se401->dev;
if(usb_submit_urb(urb, GFP_KERNEL))
- info("urb burned down");
+ dev_info(&urb->dev->dev, "urb burned down\n");
return;
}
@@ -860,7 +860,8 @@
);
if (se401->nullpackets > SE401_MAX_NULLPACKETS) {
se401->nullpackets=0;
- info("to many null length packets, restarting capture");
+ dev_info(&se401->dev->dev,
+ "too many null length packets, restarting capture\n");
se401_stop_stream(se401);
se401_start_stream(se401);
} else {
@@ -880,7 +881,8 @@
se401->scratch_use=0;
if (errors > SE401_MAX_ERRORS) {
errors=0;
- info("to much errors, restarting capture");
+ dev_info(&se401->dev->dev,
+ "too many errors, restarting capture\n");
se401_stop_stream(se401);
se401_start_stream(se401);
}
@@ -913,7 +915,7 @@
usb_kill_urb(se401->inturb);
usb_free_urb(se401->inturb);
}
- info("%s disconnected", se401->camera_name);
+ dev_info(&se401->dev->dev, "%s disconnected", se401->camera_name);
/* Free the memory */
kfree(se401->width);
@@ -936,14 +938,18 @@
struct usb_se401 *se401 = (struct usb_se401 *)dev;
int err = 0;
- if (se401->user)
+ lock_kernel();
+ if (se401->user) {
+ unlock_kernel();
return -EBUSY;
+ }
se401->fbuf = rvmalloc(se401->maxframesize * SE401_NUMFRAMES);
if (se401->fbuf)
file->private_data = dev;
else
err = -ENOMEM;
se401->user = !err;
+ unlock_kernel();
return err;
}
@@ -956,8 +962,8 @@
rvfree(se401->fbuf, se401->maxframesize * SE401_NUMFRAMES);
if (se401->removed) {
+ dev_info(&se401->dev->dev, "device unregistered\n");
usb_se401_remove_disconnected(se401);
- info("device unregistered");
} else {
for (i=0; i<SE401_NUMFRAMES; i++)
se401->frame[i].grabstate=FRAME_UNUSED;
@@ -1232,6 +1238,7 @@
static struct video_device se401_template = {
.name = "se401 USB camera",
.fops = &se401_fops,
+ .release = video_device_release_empty,
};
@@ -1271,7 +1278,7 @@
for (i=0; i<se401->sizes; i++) {
sprintf(temp, "%s %dx%d", temp, se401->width[i], se401->height[i]);
}
- info("%s", temp);
+ dev_info(&se401->dev->dev, "%s\n", temp);
se401->maxframesize=se401->width[se401->sizes-1]*se401->height[se401->sizes-1]*3;
rc=se401_sndctrl(0, se401, SE401_REQ_GET_WIDTH, 0, cp, sizeof(cp));
@@ -1305,7 +1312,8 @@
if (button) {
se401->inturb=usb_alloc_urb(0, GFP_KERNEL);
if (!se401->inturb) {
- info("Allocation of inturb failed");
+ dev_info(&se401->dev->dev,
+ "Allocation of inturb failed\n");
return 1;
}
usb_fill_int_urb(se401->inturb, se401->dev,
@@ -1316,7 +1324,7 @@
8
);
if (usb_submit_urb(se401->inturb, GFP_KERNEL)) {
- info("int urb burned down");
+ dev_info(&se401->dev->dev, "int urb burned down\n");
return 1;
}
} else
@@ -1373,7 +1381,7 @@
return -ENODEV;
/* We found one */
- info("SE401 camera found: %s", camera_name);
+ dev_info(&intf->dev, "SE401 camera found: %s\n", camera_name);
if ((se401 = kzalloc(sizeof(*se401), GFP_KERNEL)) == NULL) {
err("couldn't kmalloc se401 struct");
@@ -1384,7 +1392,8 @@
se401->iface = interface->bInterfaceNumber;
se401->camera_name = camera_name;
- info("firmware version: %02x", le16_to_cpu(dev->descriptor.bcdDevice) & 255);
+ dev_info(&intf->dev, "firmware version: %02x\n",
+ le16_to_cpu(dev->descriptor.bcdDevice) & 255);
if (se401_init(se401, button)) {
kfree(se401);
@@ -1402,7 +1411,8 @@
err("video_register_device failed");
return -EIO;
}
- info("registered new video device: video%d", se401->vdev.minor);
+ dev_info(&intf->dev, "registered new video device: video%d\n",
+ se401->vdev.minor);
usb_set_intfdata (intf, se401);
return 0;
@@ -1446,10 +1456,10 @@
static int __init usb_se401_init(void)
{
- info("SE401 usb camera driver version %s registering", version);
+ printk(KERN_INFO "SE401 usb camera driver version %s registering\n", version);
if (flickerless)
if (flickerless!=50 && flickerless!=60) {
- info("Invallid flickerless value, use 0, 50 or 60.");
+ printk(KERN_ERR "Invallid flickerless value, use 0, 50 or 60.\n");
return -1;
}
return usb_register(&se401_driver);
@@ -1458,7 +1468,7 @@
static void __exit usb_se401_exit(void)
{
usb_deregister(&se401_driver);
- info("SE401 driver deregistered");
+ printk(KERN_INFO "SE401 driver deregistered\frame");
}
module_init(usb_se401_init);
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c
index 318754e..7683809 100644
--- a/drivers/media/video/sh_mobile_ceu_camera.c
+++ b/drivers/media/video/sh_mobile_ceu_camera.c
@@ -304,9 +304,6 @@
"SuperH Mobile CEU driver attached to camera %d\n",
icd->devnum);
- if (pcdev->pdata->enable_camera)
- pcdev->pdata->enable_camera();
-
ret = icd->ops->init(icd);
if (ret)
goto err;
@@ -333,8 +330,6 @@
ceu_write(pcdev, CEIER, 0);
ceu_write(pcdev, CAPSR, 1 << 16); /* reset */
icd->ops->release(icd);
- if (pcdev->pdata->disable_camera)
- pcdev->pdata->disable_camera();
dev_info(&icd->dev,
"SuperH Mobile CEU driver detached from camera %d\n",
diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c
index 2da6938..20e30bd 100644
--- a/drivers/media/video/sn9c102/sn9c102_core.c
+++ b/drivers/media/video/sn9c102/sn9c102_core.c
@@ -116,6 +116,26 @@
"\n");
#endif
+/*
+ Add the probe entries to this table. Be sure to add the entry in the right
+ place, since, on failure, the next probing routine is called according to
+ the order of the list below, from top to bottom.
+*/
+static int (*sn9c102_sensor_table[])(struct sn9c102_device *) = {
+ &sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */
+ &sn9c102_probe_hv7131r, /* strong detection based on SENSOR ids */
+ &sn9c102_probe_mi0343, /* strong detection based on SENSOR ids */
+ &sn9c102_probe_mi0360, /* strong detection based on SENSOR ids */
+ &sn9c102_probe_mt9v111, /* strong detection based on SENSOR ids */
+ &sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */
+ &sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */
+ &sn9c102_probe_ov7630, /* strong detection based on SENSOR ids */
+ &sn9c102_probe_ov7660, /* strong detection based on SENSOR ids */
+ &sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */
+ &sn9c102_probe_tas5110d, /* detection based on USB pid/vid */
+ &sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */
+};
+
/*****************************************************************************/
static u32
@@ -1746,7 +1766,7 @@
if (!down_read_trylock(&sn9c102_dev_lock))
return -ERESTARTSYS;
- cam = video_get_drvdata(video_devdata(filp));
+ cam = video_drvdata(filp);
if (wait_for_completion_interruptible(&cam->probe)) {
up_read(&sn9c102_dev_lock);
@@ -1843,7 +1863,7 @@
down_write(&sn9c102_dev_lock);
- cam = video_get_drvdata(video_devdata(filp));
+ cam = video_drvdata(filp);
sn9c102_stop_transfer(cam);
sn9c102_release_buffers(cam);
@@ -1863,7 +1883,7 @@
static ssize_t
sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
{
- struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
+ struct sn9c102_device *cam = video_drvdata(filp);
struct sn9c102_frame_t* f, * i;
unsigned long lock_flags;
long timeout;
@@ -1987,7 +2007,7 @@
static unsigned int sn9c102_poll(struct file *filp, poll_table *wait)
{
- struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
+ struct sn9c102_device *cam = video_drvdata(filp);
struct sn9c102_frame_t* f;
unsigned long lock_flags;
unsigned int mask = 0;
@@ -2063,7 +2083,7 @@
static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma)
{
- struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
+ struct sn9c102_device *cam = video_drvdata(filp);
unsigned long size = vma->vm_end - vma->vm_start,
start = vma->vm_start;
void *pos;
@@ -3075,7 +3095,7 @@
static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
unsigned int cmd, void __user * arg)
{
- struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
+ struct sn9c102_device *cam = video_drvdata(filp);
switch (cmd) {
@@ -3179,7 +3199,7 @@
static int sn9c102_ioctl(struct inode* inode, struct file* filp,
unsigned int cmd, unsigned long arg)
{
- struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
+ struct sn9c102_device *cam = video_drvdata(filp);
int err = 0;
if (mutex_lock_interruptible(&cam->fileop_mutex))
diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h
index 90a401d..e23734f 100644
--- a/drivers/media/video/sn9c102/sn9c102_devtable.h
+++ b/drivers/media/video/sn9c102/sn9c102_devtable.h
@@ -140,24 +140,4 @@
extern int sn9c102_probe_tas5110d(struct sn9c102_device* cam);
extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam);
-/*
- Add the above entries to this table. Be sure to add the entry in the right
- place, since, on failure, the next probing routine is called according to
- the order of the list below, from top to bottom.
-*/
-static int (*sn9c102_sensor_table[])(struct sn9c102_device*) = {
- &sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */
- &sn9c102_probe_hv7131r, /* strong detection based on SENSOR ids */
- &sn9c102_probe_mi0343, /* strong detection based on SENSOR ids */
- &sn9c102_probe_mi0360, /* strong detection based on SENSOR ids */
- &sn9c102_probe_mt9v111, /* strong detection based on SENSOR ids */
- &sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */
- &sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */
- &sn9c102_probe_ov7630, /* strong detection based on SENSOR ids */
- &sn9c102_probe_ov7660, /* strong detection based on SENSOR ids */
- &sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */
- &sn9c102_probe_tas5110d, /* detection based on USB pid/vid */
- &sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */
-};
-
#endif /* _SN9C102_DEVTABLE_H_ */
diff --git a/drivers/media/video/sn9c102/sn9c102_hv7131d.c b/drivers/media/video/sn9c102/sn9c102_hv7131d.c
index eaf9ad0..db24349 100644
--- a/drivers/media/video/sn9c102/sn9c102_hv7131d.c
+++ b/drivers/media/video/sn9c102/sn9c102_hv7131d.c
@@ -20,6 +20,7 @@
***************************************************************************/
#include "sn9c102_sensor.h"
+#include "sn9c102_devtable.h"
static int hv7131d_init(struct sn9c102_device* cam)
diff --git a/drivers/media/video/sn9c102/sn9c102_hv7131r.c b/drivers/media/video/sn9c102/sn9c102_hv7131r.c
index 0fc4012..4295887 100644
--- a/drivers/media/video/sn9c102/sn9c102_hv7131r.c
+++ b/drivers/media/video/sn9c102/sn9c102_hv7131r.c
@@ -20,6 +20,7 @@
***************************************************************************/
#include "sn9c102_sensor.h"
+#include "sn9c102_devtable.h"
static int hv7131r_init(struct sn9c102_device* cam)
diff --git a/drivers/media/video/sn9c102/sn9c102_mi0343.c b/drivers/media/video/sn9c102/sn9c102_mi0343.c
index 00b134c..1f5b09b 100644
--- a/drivers/media/video/sn9c102/sn9c102_mi0343.c
+++ b/drivers/media/video/sn9c102/sn9c102_mi0343.c
@@ -20,6 +20,7 @@
***************************************************************************/
#include "sn9c102_sensor.h"
+#include "sn9c102_devtable.h"
static int mi0343_init(struct sn9c102_device* cam)
diff --git a/drivers/media/video/sn9c102/sn9c102_mi0360.c b/drivers/media/video/sn9c102/sn9c102_mi0360.c
index f8d81d8..d973fc1 100644
--- a/drivers/media/video/sn9c102/sn9c102_mi0360.c
+++ b/drivers/media/video/sn9c102/sn9c102_mi0360.c
@@ -20,6 +20,7 @@
***************************************************************************/
#include "sn9c102_sensor.h"
+#include "sn9c102_devtable.h"
static int mi0360_init(struct sn9c102_device* cam)
diff --git a/drivers/media/video/sn9c102/sn9c102_mt9v111.c b/drivers/media/video/sn9c102/sn9c102_mt9v111.c
index 3b98ac3..95986eb 100644
--- a/drivers/media/video/sn9c102/sn9c102_mt9v111.c
+++ b/drivers/media/video/sn9c102/sn9c102_mt9v111.c
@@ -20,6 +20,7 @@
***************************************************************************/
#include "sn9c102_sensor.h"
+#include "sn9c102_devtable.h"
static int mt9v111_init(struct sn9c102_device *cam)
diff --git a/drivers/media/video/sn9c102/sn9c102_ov7630.c b/drivers/media/video/sn9c102/sn9c102_ov7630.c
index e4856fd..803712c 100644
--- a/drivers/media/video/sn9c102/sn9c102_ov7630.c
+++ b/drivers/media/video/sn9c102/sn9c102_ov7630.c
@@ -20,6 +20,7 @@
***************************************************************************/
#include "sn9c102_sensor.h"
+#include "sn9c102_devtable.h"
static int ov7630_init(struct sn9c102_device* cam)
diff --git a/drivers/media/video/sn9c102/sn9c102_ov7660.c b/drivers/media/video/sn9c102/sn9c102_ov7660.c
index 8aae416..7977795 100644
--- a/drivers/media/video/sn9c102/sn9c102_ov7660.c
+++ b/drivers/media/video/sn9c102/sn9c102_ov7660.c
@@ -20,6 +20,7 @@
***************************************************************************/
#include "sn9c102_sensor.h"
+#include "sn9c102_devtable.h"
static int ov7660_init(struct sn9c102_device* cam)
diff --git a/drivers/media/video/sn9c102/sn9c102_pas106b.c b/drivers/media/video/sn9c102/sn9c102_pas106b.c
index 360f2a8..81cd969 100644
--- a/drivers/media/video/sn9c102/sn9c102_pas106b.c
+++ b/drivers/media/video/sn9c102/sn9c102_pas106b.c
@@ -21,6 +21,7 @@
#include <linux/delay.h>
#include "sn9c102_sensor.h"
+#include "sn9c102_devtable.h"
static int pas106b_init(struct sn9c102_device* cam)
diff --git a/drivers/media/video/sn9c102/sn9c102_pas202bcb.c b/drivers/media/video/sn9c102/sn9c102_pas202bcb.c
index ca4a150..2782f94 100644
--- a/drivers/media/video/sn9c102/sn9c102_pas202bcb.c
+++ b/drivers/media/video/sn9c102/sn9c102_pas202bcb.c
@@ -26,6 +26,7 @@
#include <linux/delay.h>
#include "sn9c102_sensor.h"
+#include "sn9c102_devtable.h"
static int pas202bcb_init(struct sn9c102_device* cam)
diff --git a/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c b/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c
index e7d2de2..04cdfdd 100644
--- a/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c
+++ b/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c
@@ -20,6 +20,7 @@
***************************************************************************/
#include "sn9c102_sensor.h"
+#include "sn9c102_devtable.h"
static int tas5110c1b_init(struct sn9c102_device* cam)
diff --git a/drivers/media/video/sn9c102/sn9c102_tas5110d.c b/drivers/media/video/sn9c102/sn9c102_tas5110d.c
index d32fdbc..9372e6f 100644
--- a/drivers/media/video/sn9c102/sn9c102_tas5110d.c
+++ b/drivers/media/video/sn9c102/sn9c102_tas5110d.c
@@ -20,6 +20,7 @@
***************************************************************************/
#include "sn9c102_sensor.h"
+#include "sn9c102_devtable.h"
static int tas5110d_init(struct sn9c102_device* cam)
diff --git a/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c b/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c
index 56fb1d5..a30bbc4 100644
--- a/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c
+++ b/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c
@@ -20,6 +20,7 @@
***************************************************************************/
#include "sn9c102_sensor.h"
+#include "sn9c102_devtable.h"
static int tas5130d1b_init(struct sn9c102_device* cam)
diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c
index ad36af3..db69bc5 100644
--- a/drivers/media/video/stk-webcam.c
+++ b/drivers/media/video/stk-webcam.c
@@ -65,22 +65,6 @@
};
MODULE_DEVICE_TABLE(usb, stkwebcam_table);
-static void stk_camera_cleanup(struct kref *kref)
-{
- struct stk_camera *dev = to_stk_camera(kref);
-
- STK_INFO("Syntek USB2.0 Camera release resources"
- " video device /dev/video%d\n", dev->vdev.minor);
- video_unregister_device(&dev->vdev);
- dev->vdev.priv = NULL;
-
- if (dev->sio_bufs != NULL || dev->isobufs != NULL)
- STK_ERROR("We are leaking memory\n");
- usb_put_intf(dev->interface);
- kfree(dev);
-}
-
-
/*
* Basic stuff
*/
@@ -689,34 +673,24 @@
vdev = video_devdata(fp);
dev = vdev_to_camera(vdev);
- if (dev == NULL || !is_present(dev))
+ lock_kernel();
+ if (dev == NULL || !is_present(dev)) {
+ unlock_kernel();
return -ENXIO;
- fp->private_data = vdev;
- kref_get(&dev->kref);
+ }
+ fp->private_data = dev;
usb_autopm_get_interface(dev->interface);
+ unlock_kernel();
return 0;
}
static int v4l_stk_release(struct inode *inode, struct file *fp)
{
- struct stk_camera *dev;
- struct video_device *vdev;
-
- vdev = video_devdata(fp);
- if (vdev == NULL) {
- STK_ERROR("v4l_release called w/o video devdata\n");
- return -EFAULT;
- }
- dev = vdev_to_camera(vdev);
- if (dev == NULL) {
- STK_ERROR("v4l_release called on removed device\n");
- return -ENODEV;
- }
+ struct stk_camera *dev = fp->private_data;
if (dev->owner != fp) {
usb_autopm_put_interface(dev->interface);
- kref_put(&dev->kref, stk_camera_cleanup);
return 0;
}
@@ -727,7 +701,6 @@
dev->owner = NULL;
usb_autopm_put_interface(dev->interface);
- kref_put(&dev->kref, stk_camera_cleanup);
return 0;
}
@@ -738,14 +711,8 @@
int i;
int ret;
unsigned long flags;
- struct stk_camera *dev;
- struct video_device *vdev;
struct stk_sio_buffer *sbuf;
-
- vdev = video_devdata(fp);
- if (vdev == NULL)
- return -EFAULT;
- dev = vdev_to_camera(vdev);
+ struct stk_camera *dev = fp->private_data;
if (dev == NULL)
return -EIO;
@@ -804,15 +771,8 @@
static unsigned int v4l_stk_poll(struct file *fp, poll_table *wait)
{
- struct stk_camera *dev;
- struct video_device *vdev;
+ struct stk_camera *dev = fp->private_data;
- vdev = video_devdata(fp);
-
- if (vdev == NULL)
- return -EFAULT;
-
- dev = vdev_to_camera(vdev);
if (dev == NULL)
return -ENODEV;
@@ -850,16 +810,12 @@
unsigned int i;
int ret;
unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
- struct stk_camera *dev;
- struct video_device *vdev;
+ struct stk_camera *dev = fp->private_data;
struct stk_sio_buffer *sbuf = NULL;
if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED))
return -EINVAL;
- vdev = video_devdata(fp);
- dev = vdev_to_camera(vdev);
-
for (i = 0; i < dev->n_sbufs; i++) {
if (dev->sio_bufs[i].v4lbuf.m.offset == offset) {
sbuf = dev->sio_bufs + i;
@@ -1355,6 +1311,12 @@
static void stk_v4l_dev_release(struct video_device *vd)
{
+ struct stk_camera *dev = vdev_to_camera(vd);
+
+ if (dev->sio_bufs != NULL || dev->isobufs != NULL)
+ STK_ERROR("We are leaking memory\n");
+ usb_put_intf(dev->interface);
+ kfree(dev);
}
static struct video_device stk_v4l_data = {
@@ -1375,7 +1337,6 @@
dev->vdev = stk_v4l_data;
dev->vdev.debug = debug;
dev->vdev.parent = &dev->interface->dev;
- dev->vdev.priv = dev;
err = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1);
if (err)
STK_ERROR("v4l registration failed\n");
@@ -1392,7 +1353,7 @@
const struct usb_device_id *id)
{
int i;
- int err;
+ int err = 0;
struct stk_camera *dev = NULL;
struct usb_device *udev = interface_to_usbdev(interface);
@@ -1405,7 +1366,6 @@
return -ENOMEM;
}
- kref_init(&dev->kref);
spin_lock_init(&dev->spinlock);
init_waitqueue_head(&dev->wait_frame);
@@ -1438,8 +1398,8 @@
}
if (!dev->isoc_ep) {
STK_ERROR("Could not find isoc-in endpoint");
- kref_put(&dev->kref, stk_camera_cleanup);
- return -ENODEV;
+ err = -ENODEV;
+ goto error;
}
dev->vsettings.brightness = 0x7fff;
dev->vsettings.palette = V4L2_PIX_FMT_RGB565;
@@ -1453,14 +1413,17 @@
err = stk_register_video_device(dev);
if (err) {
- kref_put(&dev->kref, stk_camera_cleanup);
- return err;
+ goto error;
}
stk_create_sysfs_files(&dev->vdev);
usb_autopm_enable(dev->interface);
return 0;
+
+error:
+ kfree(dev);
+ return err;
}
static void stk_camera_disconnect(struct usb_interface *interface)
@@ -1473,7 +1436,10 @@
wake_up_interruptible(&dev->wait_frame);
stk_remove_sysfs_files(&dev->vdev);
- kref_put(&dev->kref, stk_camera_cleanup);
+ STK_INFO("Syntek USB2.0 Camera release resources"
+ "video device /dev/video%d\n", dev->vdev.minor);
+
+ video_unregister_device(&dev->vdev);
}
#ifdef CONFIG_PM
diff --git a/drivers/media/video/stk-webcam.h b/drivers/media/video/stk-webcam.h
index df4dfef..084a85b 100644
--- a/drivers/media/video/stk-webcam.h
+++ b/drivers/media/video/stk-webcam.h
@@ -99,7 +99,6 @@
u8 isoc_ep;
- struct kref kref;
/* Not sure if this is right */
atomic_t urbs_used;
@@ -121,7 +120,6 @@
unsigned sequence;
};
-#define to_stk_camera(d) container_of(d, struct stk_camera, kref)
#define vdev_to_camera(d) container_of(d, struct stk_camera, vdev)
void stk_camera_delete(struct kref *);
diff --git a/drivers/media/video/stradis.c b/drivers/media/video/stradis.c
index 276bded..bbad54f 100644
--- a/drivers/media/video/stradis.c
+++ b/drivers/media/video/stradis.c
@@ -1882,12 +1882,16 @@
struct video_device *vdev = video_devdata(file);
struct saa7146 *saa = container_of(vdev, struct saa7146, video_dev);
+ lock_kernel();
file->private_data = saa;
saa->user++;
- if (saa->user > 1)
+ if (saa->user > 1) {
+ unlock_kernel();
return 0; /* device open already, don't reset */
+ }
saa->writemode = VID_WRITE_MPEG_VID; /* default to video */
+ unlock_kernel();
return 0;
}
@@ -1921,6 +1925,7 @@
.name = "SAA7146A",
.fops = &saa_fops,
.minor = -1,
+ .release = video_device_release_empty,
};
static int __devinit configure_saa7146(struct pci_dev *pdev, int num)
diff --git a/drivers/media/video/stv680.c b/drivers/media/video/stv680.c
index dce9474..9c549d9 100644
--- a/drivers/media/video/stv680.c
+++ b/drivers/media/video/stv680.c
@@ -84,7 +84,8 @@
#define PDEBUG(level, fmt, args...) \
do { \
if (debug >= level) \
- info("[%s:%d] " fmt, __func__, __LINE__ , ## args); \
+ printk(KERN_INFO KBUILD_MODNAME " [%s:%d] \n" fmt, \
+ __func__, __LINE__ , ## args); \
} while (0)
@@ -1086,6 +1087,7 @@
int err = 0;
/* we are called with the BKL held */
+ lock_kernel();
stv680->user = 1;
err = stv_init (stv680); /* main initialization routine for camera */
@@ -1099,6 +1101,7 @@
}
if (err)
stv680->user = 0;
+ unlock_kernel();
return err;
}
@@ -1550,7 +1553,8 @@
}
PDEBUG (0, "STV(i): usb camera driver version %s registering", DRIVER_VERSION);
- info(DRIVER_DESC " " DRIVER_VERSION);
+ printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
+ DRIVER_DESC "\n");
return 0;
}
diff --git a/drivers/media/video/tda9840.c b/drivers/media/video/tda9840.c
index 2437c1a..1c391f0 100644
--- a/drivers/media/video/tda9840.c
+++ b/drivers/media/video/tda9840.c
@@ -2,6 +2,7 @@
tda9840 - i2c-driver for the tda9840 by SGS Thomson
Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de>
+ Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
The tda9840 is a stereo/dual sound processor with digital
identification. It can be found at address 0x84 on the i2c-bus.
@@ -28,59 +29,118 @@
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/i2c.h>
-
+#include <media/v4l2-common.h>
+#include <media/v4l2-i2c-drv-legacy.h>
#include "tda9840.h"
-static int debug; /* insmod parameter */
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off).");
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_DESCRIPTION("tda9840 driver");
+MODULE_LICENSE("GPL");
-#define dprintk(args...) \
- do { if (debug) { printk("%s: %s()[%d]: ", KBUILD_MODNAME, __func__, __LINE__); printk(args); } } while (0)
+static int debug;
+module_param(debug, int, 0644);
+
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
#define SWITCH 0x00
#define LEVEL_ADJUST 0x02
#define STEREO_ADJUST 0x03
#define TEST 0x04
+#define TDA9840_SET_MUTE 0x00
+#define TDA9840_SET_MONO 0x10
+#define TDA9840_SET_STEREO 0x2a
+#define TDA9840_SET_LANG1 0x12
+#define TDA9840_SET_LANG2 0x1e
+#define TDA9840_SET_BOTH 0x1a
+#define TDA9840_SET_BOTH_R 0x16
+#define TDA9840_SET_EXTERNAL 0x7a
+
/* addresses to scan, found only at 0x42 (7-Bit) */
static unsigned short normal_i2c[] = { I2C_ADDR_TDA9840, I2C_CLIENT_END };
/* magic definition of all other variables and things */
I2C_CLIENT_INSMOD;
-static struct i2c_driver driver;
-static struct i2c_client client_template;
-
-static int command(struct i2c_client *client, unsigned int cmd, void *arg)
+static void tda9840_write(struct i2c_client *client, u8 reg, u8 val)
{
- int result;
+ if (i2c_smbus_write_byte_data(client, reg, val))
+ v4l_dbg(1, debug, client, "error writing %02x to %02x\n",
+ val, reg);
+}
+
+static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
int byte = *(int *)arg;
switch (cmd) {
- case TDA9840_SWITCH:
+ case VIDIOC_S_TUNER: {
+ struct v4l2_tuner *t = arg;
+ int byte;
- dprintk("TDA9840_SWITCH: 0x%02x\n", byte);
+ if (t->index)
+ return -EINVAL;
- if (byte != TDA9840_SET_MONO
- && byte != TDA9840_SET_MUTE
- && byte != TDA9840_SET_STEREO
- && byte != TDA9840_SET_LANG1
- && byte != TDA9840_SET_LANG2
- && byte != TDA9840_SET_BOTH
- && byte != TDA9840_SET_BOTH_R
- && byte != TDA9840_SET_EXTERNAL) {
+ switch (t->audmode) {
+ case V4L2_TUNER_MODE_STEREO:
+ byte = TDA9840_SET_STEREO;
+ break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ byte = TDA9840_SET_BOTH;
+ break;
+ case V4L2_TUNER_MODE_LANG1:
+ byte = TDA9840_SET_LANG1;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ byte = TDA9840_SET_LANG2;
+ break;
+ default:
+ byte = TDA9840_SET_MONO;
+ break;
+ }
+ v4l_dbg(1, debug, client, "TDA9840_SWITCH: 0x%02x\n", byte);
+ tda9840_write(client, SWITCH, byte);
+ break;
+ }
+
+ case VIDIOC_G_TUNER: {
+ struct v4l2_tuner *t = arg;
+ u8 byte;
+
+ t->rxsubchans = V4L2_TUNER_SUB_MONO;
+ if (1 != i2c_master_recv(client, &byte, 1)) {
+ v4l_dbg(1, debug, client,
+ "i2c_master_recv() failed\n");
+ return -EIO;
+ }
+
+ if (byte & 0x80) {
+ v4l_dbg(1, debug, client,
+ "TDA9840_DETECT: register contents invalid\n");
return -EINVAL;
}
- result = i2c_smbus_write_byte_data(client, SWITCH, byte);
- if (result)
- dprintk("i2c_smbus_write_byte() failed, ret:%d\n", result);
+ v4l_dbg(1, debug, client, "TDA9840_DETECT: byte: 0x%02x\n", byte);
+
+ switch (byte & 0x60) {
+ case 0x00:
+ t->rxsubchans = V4L2_TUNER_SUB_MONO;
+ break;
+ case 0x20:
+ t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ break;
+ case 0x40:
+ t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO;
+ break;
+ default: /* Incorrect detect */
+ t->rxsubchans = V4L2_TUNER_MODE_MONO;
+ break;
+ }
break;
+ }
case TDA9840_LEVEL_ADJUST:
-
- dprintk("TDA9840_LEVEL_ADJUST: %d\n", byte);
+ v4l_dbg(1, debug, client, "TDA9840_LEVEL_ADJUST: %d\n", byte);
/* check for correct range */
if (byte > 25 || byte < -20)
@@ -92,15 +152,11 @@
byte += 0x8;
else
byte = -byte;
-
- result = i2c_smbus_write_byte_data(client, LEVEL_ADJUST, byte);
- if (result)
- dprintk("i2c_smbus_write_byte() failed, ret:%d\n", result);
+ tda9840_write(client, LEVEL_ADJUST, byte);
break;
case TDA9840_STEREO_ADJUST:
-
- dprintk("TDA9840_STEREO_ADJUST: %d\n", byte);
+ v4l_dbg(1, debug, client, "TDA9840_STEREO_ADJUST: %d\n", byte);
/* check for correct range */
if (byte > 25 || byte < -24)
@@ -113,143 +169,59 @@
else
byte = -byte;
- result = i2c_smbus_write_byte_data(client, STEREO_ADJUST, byte);
- if (result)
- dprintk("i2c_smbus_write_byte() failed, ret:%d\n", result);
- break;
-
- case TDA9840_DETECT: {
- int *ret = (int *)arg;
-
- byte = i2c_smbus_read_byte_data(client, STEREO_ADJUST);
- if (byte == -1) {
- dprintk("i2c_smbus_read_byte_data() failed\n");
- return -EIO;
- }
-
- if (0 != (byte & 0x80)) {
- dprintk("TDA9840_DETECT: register contents invalid\n");
- return -EINVAL;
- }
-
- dprintk("TDA9840_DETECT: byte: 0x%02x\n", byte);
- *ret = ((byte & 0x60) >> 5);
- result = 0;
- break;
- }
- case TDA9840_TEST:
- dprintk("TDA9840_TEST: 0x%02x\n", byte);
-
- /* mask out irrelevant bits */
- byte &= 0x3;
-
- result = i2c_smbus_write_byte_data(client, TEST, byte);
- if (result)
- dprintk("i2c_smbus_write_byte() failed, ret:%d\n", result);
+ tda9840_write(client, STEREO_ADJUST, byte);
break;
default:
return -ENOIOCTLCMD;
}
- if (result)
- return -EIO;
-
return 0;
}
-static int detect(struct i2c_adapter *adapter, int address, int kind)
+static int tda9840_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
- struct i2c_client *client;
- int result = 0;
-
- int byte = 0x0;
+ int result;
+ int byte;
/* let's see whether this adapter can support what we need */
- if (0 == i2c_check_functionality(adapter,
- I2C_FUNC_SMBUS_READ_BYTE_DATA |
- I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) {
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_BYTE_DATA |
+ I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
return 0;
- }
- /* allocate memory for client structure */
- client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (!client) {
- printk("not enough kernel memory\n");
- return -ENOMEM;
- }
-
- /* fill client structure */
- memcpy(client, &client_template, sizeof(struct i2c_client));
- client->addr = address;
- client->adapter = adapter;
-
- /* tell the i2c layer a new client has arrived */
- if (0 != (result = i2c_attach_client(client))) {
- kfree(client);
- return result;
- }
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
/* set initial values for level & stereo - adjustment, mode */
byte = 0;
- result = command(client, TDA9840_LEVEL_ADJUST, &byte);
- result += command(client, TDA9840_STEREO_ADJUST, &byte);
- byte = TDA9840_SET_MONO;
- result = command(client, TDA9840_SWITCH, &byte);
+ result = tda9840_command(client, TDA9840_LEVEL_ADJUST, &byte);
+ result += tda9840_command(client, TDA9840_STEREO_ADJUST, &byte);
+ tda9840_write(client, SWITCH, TDA9840_SET_STEREO);
if (result) {
- dprintk("could not initialize tda9840\n");
+ v4l_dbg(1, debug, client, "could not initialize tda9840\n");
return -ENODEV;
}
-
- printk("tda9840: detected @ 0x%02x on adapter %s\n", address, &client->adapter->name[0]);
return 0;
}
-static int attach(struct i2c_adapter *adapter)
+static int tda9840_legacy_probe(struct i2c_adapter *adapter)
{
- /* let's see whether this is a know adapter we can attach to */
- if (adapter->id != I2C_HW_SAA7146) {
- dprintk("refusing to probe on unknown adapter [name='%s',id=0x%x]\n", adapter->name, adapter->id);
- return -ENODEV;
- }
-
- return i2c_probe(adapter, &addr_data, &detect);
+ /* Let's see whether this is a known adapter we can attach to.
+ Prevents conflicts with tvaudio.c. */
+ return adapter->id == I2C_HW_SAA7146;
}
-
-static int detach(struct i2c_client *client)
-{
- int ret = i2c_detach_client(client);
- kfree(client);
- return ret;
-}
-
-static struct i2c_driver driver = {
- .driver = {
- .name = "tda9840",
- },
- .id = I2C_DRIVERID_TDA9840,
- .attach_adapter = attach,
- .detach_client = detach,
- .command = command,
+static const struct i2c_device_id tda9840_id[] = {
+ { "tda9840", 0 },
+ { }
};
+MODULE_DEVICE_TABLE(i2c, tda9840_id);
-static struct i2c_client client_template = {
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.name = "tda9840",
- .driver = &driver,
+ .driverid = I2C_DRIVERID_TDA9840,
+ .command = tda9840_command,
+ .probe = tda9840_probe,
+ .legacy_probe = tda9840_legacy_probe,
+ .id_table = tda9840_id,
};
-
-static int __init this_module_init(void)
-{
- return i2c_add_driver(&driver);
-}
-
-static void __exit this_module_exit(void)
-{
- i2c_del_driver(&driver);
-}
-
-module_init(this_module_init);
-module_exit(this_module_exit);
-
-MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
-MODULE_DESCRIPTION("tda9840 driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/tda9840.h b/drivers/media/video/tda9840.h
index 7da8432..dc12ae7 100644
--- a/drivers/media/video/tda9840.h
+++ b/drivers/media/video/tda9840.h
@@ -3,24 +3,6 @@
#define I2C_ADDR_TDA9840 0x42
-#define TDA9840_DETECT _IOR('v',1,int)
-/* return values for TDA9840_DETCT */
-#define TDA9840_MONO_DETECT 0x0
-#define TDA9840_DUAL_DETECT 0x1
-#define TDA9840_STEREO_DETECT 0x2
-#define TDA9840_INCORRECT_DETECT 0x3
-
-#define TDA9840_SWITCH _IOW('v',2,int)
-/* modes than can be set with TDA9840_SWITCH */
-#define TDA9840_SET_MUTE 0x00
-#define TDA9840_SET_MONO 0x10
-#define TDA9840_SET_STEREO 0x2a
-#define TDA9840_SET_LANG1 0x12
-#define TDA9840_SET_LANG2 0x1e
-#define TDA9840_SET_BOTH 0x1a
-#define TDA9840_SET_BOTH_R 0x16
-#define TDA9840_SET_EXTERNAL 0x7a
-
/* values may range between +2.5 and -2.0;
the value has to be multiplied with 10 */
#define TDA9840_LEVEL_ADJUST _IOW('v',3,int)
@@ -29,7 +11,4 @@
the value has to be multiplied with 10 */
#define TDA9840_STEREO_ADJUST _IOW('v',4,int)
-/* currently not implemented */
-#define TDA9840_TEST _IOW('v',5,int)
-
#endif
diff --git a/drivers/media/video/tea6415c.c b/drivers/media/video/tea6415c.c
index 421c144..cde092a 100644
--- a/drivers/media/video/tea6415c.c
+++ b/drivers/media/video/tea6415c.c
@@ -2,6 +2,7 @@
tea6415c - i2c-driver for the tea6415c by SGS Thomson
Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de>
+ Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
The tea6415c is a bus controlled video-matrix-switch
with 8 inputs and 6 outputs.
@@ -30,18 +31,18 @@
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/i2c.h>
-
+#include <media/v4l2-common.h>
+#include <media/v4l2-i2c-drv-legacy.h>
#include "tea6415c.h"
-static int debug; /* insmod parameter */
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_DESCRIPTION("tea6415c driver");
+MODULE_LICENSE("GPL");
+
+static int debug;
module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off).");
-#define dprintk(args...) \
- do { if (debug) { printk("%s: %s()[%d]: ", KBUILD_MODNAME, __func__, __LINE__); printk(args); } } while (0)
-
-#define TEA6415C_NUM_INPUTS 8
-#define TEA6415C_NUM_OUTPUTS 6
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
/* addresses to scan, found only at 0x03 and/or 0x43 (7-bit) */
static unsigned short normal_i2c[] = { I2C_TEA6415C_1, I2C_TEA6415C_2, I2C_CLIENT_END };
@@ -49,60 +50,6 @@
/* magic definition of all other variables and things */
I2C_CLIENT_INSMOD;
-static struct i2c_driver driver;
-static struct i2c_client client_template;
-
-/* this function is called by i2c_probe */
-static int detect(struct i2c_adapter *adapter, int address, int kind)
-{
- struct i2c_client *client = NULL;
- int err = 0;
-
- /* let's see whether this adapter can support what we need */
- if (0 == i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE)) {
- return 0;
- }
-
- /* allocate memory for client structure */
- client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (!client) {
- return -ENOMEM;
- }
-
- /* fill client structure */
- memcpy(client, &client_template, sizeof(struct i2c_client));
- client->addr = address;
- client->adapter = adapter;
-
- /* tell the i2c layer a new client has arrived */
- if (0 != (err = i2c_attach_client(client))) {
- kfree(client);
- return err;
- }
-
- printk("tea6415c: detected @ 0x%02x on adapter %s\n", address, &client->adapter->name[0]);
-
- return 0;
-}
-
-static int attach(struct i2c_adapter *adapter)
-{
- /* let's see whether this is a know adapter we can attach to */
- if (adapter->id != I2C_HW_SAA7146) {
- dprintk("refusing to probe on unknown adapter [name='%s',id=0x%x]\n", adapter->name, adapter->id);
- return -ENODEV;
- }
-
- return i2c_probe(adapter, &addr_data, &detect);
-}
-
-static int detach(struct i2c_client *client)
-{
- int ret = i2c_detach_client(client);
- kfree(client);
- return ret;
-}
-
/* makes a connection between the input-pin 'i' and the output-pin 'o'
for the tea6415c-client 'client' */
static int switch_matrix(struct i2c_client *client, int i, int o)
@@ -110,7 +57,7 @@
u8 byte = 0;
int ret;
- dprintk("adr:0x%02x, i:%d, o:%d\n", client->addr, i, o);
+ v4l_dbg(1, debug, client, "i=%d, o=%d\n", i, o);
/* check if the pins are valid */
if (0 == ((1 == i || 3 == i || 5 == i || 6 == i || 8 == i || 10 == i || 20 == i || 11 == i)
@@ -168,14 +115,14 @@
ret = i2c_smbus_write_byte(client, byte);
if (ret) {
- dprintk("i2c_smbus_write_byte() failed, ret:%d\n", ret);
+ v4l_dbg(1, debug, client,
+ "i2c_smbus_write_byte() failed, ret:%d\n", ret);
return -EIO;
}
-
return ret;
}
-static int command(struct i2c_client *client, unsigned int cmd, void *arg)
+static int tea6415c_command(struct i2c_client *client, unsigned cmd, void *arg)
{
struct tea6415c_multiplex *v = (struct tea6415c_multiplex *)arg;
int result = 0;
@@ -187,38 +134,40 @@
default:
return -ENOIOCTLCMD;
}
-
return result;
}
-static struct i2c_driver driver = {
- .driver = {
- .name = "tea6415c",
- },
- .id = I2C_DRIVERID_TEA6415C,
- .attach_adapter = attach,
- .detach_client = detach,
- .command = command,
-};
+/* this function is called by i2c_probe */
+static int tea6415c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ /* let's see whether this adapter can support what we need */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE))
+ return 0;
-static struct i2c_client client_template = {
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+ return 0;
+}
+
+static int tea6415c_legacy_probe(struct i2c_adapter *adapter)
+{
+ /* Let's see whether this is a known adapter we can attach to.
+ Prevents conflicts with tvaudio.c. */
+ return adapter->id == I2C_HW_SAA7146;
+}
+
+static const struct i2c_device_id tea6415c_id[] = {
+ { "tea6415c", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tea6415c_id);
+
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.name = "tea6415c",
- .driver = &driver,
+ .driverid = I2C_DRIVERID_TEA6415C,
+ .command = tea6415c_command,
+ .probe = tea6415c_probe,
+ .legacy_probe = tea6415c_legacy_probe,
+ .id_table = tea6415c_id,
};
-
-static int __init this_module_init(void)
-{
- return i2c_add_driver(&driver);
-}
-
-static void __exit this_module_exit(void)
-{
- i2c_del_driver(&driver);
-}
-
-module_init(this_module_init);
-module_exit(this_module_exit);
-
-MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
-MODULE_DESCRIPTION("tea6415c driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/tea6420.c b/drivers/media/video/tea6420.c
index b5c8957..e508209 100644
--- a/drivers/media/video/tea6420.c
+++ b/drivers/media/video/tea6420.c
@@ -2,6 +2,7 @@
tea6420 - i2c-driver for the tea6420 by SGS Thomson
Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de>
+ Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
The tea6420 is a bus controlled audio-matrix with 5 stereo inputs,
4 stereo outputs and gain control for each output.
@@ -30,15 +31,18 @@
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/i2c.h>
-
+#include <media/v4l2-common.h>
+#include <media/v4l2-i2c-drv-legacy.h>
#include "tea6420.h"
-static int debug; /* insmod parameter */
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off).");
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_DESCRIPTION("tea6420 driver");
+MODULE_LICENSE("GPL");
-#define dprintk(args...) \
- do { if (debug) { printk("%s: %s()[%d]: ", KBUILD_MODNAME, __func__, __LINE__); printk(args); } } while (0)
+static int debug;
+module_param(debug, int, 0644);
+
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
/* addresses to scan, found only at 0x4c and/or 0x4d (7-Bit) */
static unsigned short normal_i2c[] = { I2C_ADDR_TEA6420_1, I2C_ADDR_TEA6420_2, I2C_CLIENT_END };
@@ -46,23 +50,20 @@
/* magic definition of all other variables and things */
I2C_CLIENT_INSMOD;
-static struct i2c_driver driver;
-static struct i2c_client client_template;
-
/* make a connection between the input 'i' and the output 'o'
with gain 'g' for the tea6420-client 'client' (note: i = 6 means 'mute') */
static int tea6420_switch(struct i2c_client *client, int i, int o, int g)
{
- u8 byte = 0;
+ u8 byte;
int ret;
- dprintk("adr:0x%02x, i:%d, o:%d, g:%d\n", client->addr, i, o, g);
+ v4l_dbg(1, debug, client, "i=%d, o=%d, g=%d\n", i, o, g);
/* check if the parameters are valid */
if (i < 1 || i > 6 || o < 1 || o > 4 || g < 0 || g > 6 || g % 2 != 0)
return -1;
- byte = ((o - 1) << 5);
+ byte = ((o - 1) << 5);
byte |= (i - 1);
/* to understand this, have a look at the tea6420-specs (p.5) */
@@ -82,76 +83,14 @@
ret = i2c_smbus_write_byte(client, byte);
if (ret) {
- dprintk("i2c_smbus_write_byte() failed, ret:%d\n", ret);
+ v4l_dbg(1, debug, client,
+ "i2c_smbus_write_byte() failed, ret:%d\n", ret);
return -EIO;
}
-
return 0;
}
-/* this function is called by i2c_probe */
-static int tea6420_detect(struct i2c_adapter *adapter, int address, int kind)
-{
- struct i2c_client *client;
- int err = 0, i = 0;
-
- /* let's see whether this adapter can support what we need */
- if (0 == i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE)) {
- return 0;
- }
-
- /* allocate memory for client structure */
- client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (!client) {
- return -ENOMEM;
- }
-
- /* fill client structure */
- memcpy(client, &client_template, sizeof(struct i2c_client));
- client->addr = address;
- client->adapter = adapter;
-
- /* tell the i2c layer a new client has arrived */
- if (0 != (err = i2c_attach_client(client))) {
- kfree(client);
- return err;
- }
-
- /* set initial values: set "mute"-input to all outputs at gain 0 */
- err = 0;
- for (i = 1; i < 5; i++) {
- err += tea6420_switch(client, 6, i, 0);
- }
- if (err) {
- dprintk("could not initialize tea6420\n");
- kfree(client);
- return -ENODEV;
- }
-
- printk("tea6420: detected @ 0x%02x on adapter %s\n", address, &client->adapter->name[0]);
-
- return 0;
-}
-
-static int attach(struct i2c_adapter *adapter)
-{
- /* let's see whether this is a know adapter we can attach to */
- if (adapter->id != I2C_HW_SAA7146) {
- dprintk("refusing to probe on unknown adapter [name='%s',id=0x%x]\n", adapter->name, adapter->id);
- return -ENODEV;
- }
-
- return i2c_probe(adapter, &addr_data, &tea6420_detect);
-}
-
-static int detach(struct i2c_client *client)
-{
- int ret = i2c_detach_client(client);
- kfree(client);
- return ret;
-}
-
-static int command(struct i2c_client *client, unsigned int cmd, void *arg)
+static int tea6420_command(struct i2c_client *client, unsigned cmd, void *arg)
{
struct tea6420_multiplex *a = (struct tea6420_multiplex *)arg;
int result = 0;
@@ -167,34 +106,50 @@
return result;
}
-static struct i2c_driver driver = {
- .driver = {
- .name = "tea6420",
- },
- .id = I2C_DRIVERID_TEA6420,
- .attach_adapter = attach,
- .detach_client = detach,
- .command = command,
-};
+/* this function is called by i2c_probe */
+static int tea6420_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int err, i;
-static struct i2c_client client_template = {
+ /* let's see whether this adapter can support what we need */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE))
+ return -EIO;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ /* set initial values: set "mute"-input to all outputs at gain 0 */
+ err = 0;
+ for (i = 1; i < 5; i++) {
+ err += tea6420_switch(client, 6, i, 0);
+ }
+ if (err) {
+ v4l_dbg(1, debug, client, "could not initialize tea6420\n");
+ kfree(client);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int tea6420_legacy_probe(struct i2c_adapter *adapter)
+{
+ /* Let's see whether this is a known adapter we can attach to.
+ Prevents conflicts with tvaudio.c. */
+ return adapter->id == I2C_HW_SAA7146;
+}
+
+static const struct i2c_device_id tea6420_id[] = {
+ { "tea6420", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tea6420_id);
+
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.name = "tea6420",
- .driver = &driver,
+ .driverid = I2C_DRIVERID_TEA6420,
+ .command = tea6420_command,
+ .probe = tea6420_probe,
+ .legacy_probe = tea6420_legacy_probe,
+ .id_table = tea6420_id,
};
-
-static int __init this_module_init(void)
-{
- return i2c_add_driver(&driver);
-}
-
-static void __exit this_module_exit(void)
-{
- i2c_del_driver(&driver);
-}
-
-module_init(this_module_init);
-module_exit(this_module_exit);
-
-MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
-MODULE_DESCRIPTION("tea6420 driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/tuner-3036.c b/drivers/media/video/tuner-3036.c
deleted file mode 100644
index bdf506e..0000000
--- a/drivers/media/video/tuner-3036.c
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Driver for Philips SAB3036 "CITAC" tuner control chip.
- *
- * Author: Phil Blundell <philb@gnu.org>
- *
- * The SAB3036 is just about different enough from the chips that
- * tuner.c copes with to make it not worth the effort to crowbar
- * the support into that file. So instead we have a separate driver.
- *
- * 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 <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-
-#include <linux/i2c.h>
-#include <linux/videodev.h>
-#include <media/v4l2-common.h>
-
-#include <media/tuner.h>
-
-static int debug; /* insmod parameter */
-static int this_adap;
-
-static struct i2c_client client_template;
-
-/* Addresses to scan */
-static unsigned short normal_i2c[] = { 0x60, 0x61, I2C_CLIENT_END };
-static unsigned short ignore = I2C_CLIENT_END;
-
-static struct i2c_client_address_data addr_data = {
- .normal_i2c = normal_i2c,
- .probe = &ignore,
- .ignore = &ignore,
-};
-
-/* ---------------------------------------------------------------------- */
-
-static unsigned char
-tuner_getstatus (struct i2c_client *c)
-{
- unsigned char byte;
- if (i2c_master_recv(c, &byte, 1) != 1)
- printk(KERN_ERR "tuner-3036: I/O error.\n");
- return byte;
-}
-
-#define TUNER_FL 0x80
-
-static int
-tuner_islocked (struct i2c_client *c)
-{
- return (tuner_getstatus(c) & TUNER_FL);
-}
-
-/* ---------------------------------------------------------------------- */
-
-static void
-set_tv_freq(struct i2c_client *c, int freq)
-{
- u16 div = ((freq * 20) / 16);
- unsigned long give_up = jiffies + HZ;
- unsigned char buffer[2];
-
- if (debug)
- printk(KERN_DEBUG "tuner: setting frequency %dMHz, divisor %x\n", freq / 16, div);
-
- /* Select high tuning current */
- buffer[0] = 0x29;
- buffer[1] = 0x3e;
-
- if (i2c_master_send(c, buffer, 2) != 2)
- printk("tuner: i2c i/o error 1\n");
-
- buffer[0] = 0x80 | ((div>>8) & 0x7f);
- buffer[1] = div & 0xff;
-
- if (i2c_master_send(c, buffer, 2) != 2)
- printk("tuner: i2c i/o error 2\n");
-
- while (!tuner_islocked(c) && time_before(jiffies, give_up))
- schedule();
-
- if (!tuner_islocked(c))
- printk(KERN_WARNING "tuner: failed to achieve PLL lock\n");
-
- /* Select low tuning current and engage AFC */
- buffer[0] = 0x29;
- buffer[1] = 0xb2;
-
- if (i2c_master_send(c, buffer, 2) != 2)
- printk("tuner: i2c i/o error 3\n");
-
- if (debug)
- printk(KERN_DEBUG "tuner: status %02x\n", tuner_getstatus(c));
-}
-
-/* ---------------------------------------------------------------------- */
-
-static int
-tuner_attach(struct i2c_adapter *adap, int addr, int kind)
-{
- static unsigned char buffer[] = { 0x29, 0x32, 0x2a, 0, 0x2b, 0 };
-
- struct i2c_client *client;
-
- if (this_adap > 0)
- return -1;
- this_adap++;
-
- client_template.adapter = adap;
- client_template.addr = addr;
-
- client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (client == NULL)
- return -ENOMEM;
- memcpy(client, &client_template, sizeof(struct i2c_client));
-
- printk("tuner: SAB3036 found, status %02x\n", tuner_getstatus(client));
-
- i2c_attach_client(client);
-
- if (i2c_master_send(client, buffer, 2) != 2)
- printk("tuner: i2c i/o error 1\n");
- if (i2c_master_send(client, buffer+2, 2) != 2)
- printk("tuner: i2c i/o error 2\n");
- if (i2c_master_send(client, buffer+4, 2) != 2)
- printk("tuner: i2c i/o error 3\n");
- return 0;
-}
-
-static int
-tuner_detach(struct i2c_client *c)
-{
- return 0;
-}
-
-static int
-tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
-{
- int *iarg = (int*)arg;
-
- switch (cmd)
- {
- case VIDIOCSFREQ:
- set_tv_freq(client, *iarg);
- break;
-
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int
-tuner_probe(struct i2c_adapter *adap)
-{
- this_adap = 0;
- if (adap->id == I2C_HW_B_LP)
- return i2c_probe(adap, &addr_data, tuner_attach);
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-static struct i2c_driver
-i2c_driver_tuner =
-{
- .driver = {
- .name = "sab3036",
- },
- .id = I2C_DRIVERID_SAB3036,
- .attach_adapter = tuner_probe,
- .detach_client = tuner_detach,
- .command = tuner_command
-};
-
-static struct i2c_client client_template =
-{
- .driver = &i2c_driver_tuner,
- .name = "SAB3036",
-};
-
-static int __init
-tuner3036_init(void)
-{
- return i2c_add_driver(&i2c_driver_tuner);
-}
-
-static void __exit
-tuner3036_exit(void)
-{
- i2c_del_driver(&i2c_driver_tuner);
-}
-
-MODULE_DESCRIPTION("SAB3036 tuner driver");
-MODULE_AUTHOR("Philip Blundell <philb@gnu.org>");
-MODULE_LICENSE("GPL");
-
-module_param(debug, int, 0);
-MODULE_PARM_DESC(debug,"Enable debugging output");
-
-module_init(tuner3036_init);
-module_exit(tuner3036_exit);
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c
index d806a35..4a7735c 100644
--- a/drivers/media/video/tuner-core.c
+++ b/drivers/media/video/tuner-core.c
@@ -92,7 +92,6 @@
unsigned int type; /* chip type id */
unsigned int config;
- int (*tuner_callback) (void *dev, int command, int arg);
const char *name;
};
@@ -346,7 +345,7 @@
static void set_type(struct i2c_client *c, unsigned int type,
unsigned int new_mode_mask, unsigned int new_config,
- int (*tuner_callback) (void *dev, int command,int arg))
+ int (*tuner_callback) (void *dev, int component, int cmd, int arg))
{
struct tuner *t = i2c_get_clientdata(c);
struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
@@ -362,7 +361,7 @@
t->config = new_config;
if (tuner_callback != NULL) {
tuner_dbg("defining GPIO callback\n");
- t->tuner_callback = tuner_callback;
+ t->fe.callback = tuner_callback;
}
if (t->mode == T_UNINITIALIZED) {
@@ -385,7 +384,6 @@
{
struct tda829x_config cfg = {
.lna_cfg = t->config,
- .tuner_callback = t->tuner_callback,
};
if (!dvb_attach(tda829x_attach, &t->fe, t->i2c->adapter,
t->i2c->addr, &cfg))
@@ -433,7 +431,6 @@
struct xc2028_config cfg = {
.i2c_adap = t->i2c->adapter,
.i2c_addr = t->i2c->addr,
- .callback = t->tuner_callback,
};
if (!dvb_attach(xc2028_attach, &t->fe, &cfg))
goto attach_failed;
@@ -450,10 +447,8 @@
xc5000_cfg.i2c_address = t->i2c->addr;
xc5000_cfg.if_khz = 5380;
- xc5000_cfg.tuner_callback = t->tuner_callback;
if (!dvb_attach(xc5000_attach,
- &t->fe, t->i2c->adapter, &xc5000_cfg,
- c->adapter->algo_data))
+ &t->fe, t->i2c->adapter, &xc5000_cfg))
goto attach_failed;
xc_tuner_ops = &t->fe.ops.tuner_ops;
@@ -1225,7 +1220,7 @@
} else {
t->mode = V4L2_TUNER_DIGITAL_TV;
}
- set_type(client, t->type, t->mode_mask, t->config, t->tuner_callback);
+ set_type(client, t->type, t->mode_mask, t->config, t->fe.callback);
list_add_tail(&t->list, &tuner_list);
return 0;
}
diff --git a/drivers/media/video/usbvideo/ibmcam.c b/drivers/media/video/usbvideo/ibmcam.c
index cc27efe..28421d3 100644
--- a/drivers/media/video/usbvideo/ibmcam.c
+++ b/drivers/media/video/usbvideo/ibmcam.c
@@ -258,7 +258,9 @@
(RING_QUEUE_PEEK(&uvd->dp, 2) == 0x00))
{
#if 0 /* This code helps to detect new frame markers */
- info("Header sig: 00 FF 00 %02X", RING_QUEUE_PEEK(&uvd->dp, 3));
+ dev_info(&uvd->dev->dev,
+ "Header sig: 00 FF 00 %02X\n",
+ RING_QUEUE_PEEK(&uvd->dp, 3));
#endif
frame->header = RING_QUEUE_PEEK(&uvd->dp, 3);
if ((frame->header == HDRSIG_MODEL1_128x96) ||
@@ -266,7 +268,8 @@
(frame->header == HDRSIG_MODEL1_352x288))
{
#if 0
- info("Header found.");
+ dev_info(&uvd->dev->dev,
+ "Header found.\n");
#endif
RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, marker_len);
icam->has_hdr = 1;
@@ -295,7 +298,7 @@
(RING_QUEUE_PEEK(&uvd->dp, 1) == 0xFF))
{
#if 0
- info("Header found.");
+ dev_info(&uvd->dev->dev, "Header found.\n");
#endif
RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, marker_len);
icam->has_hdr = 1;
@@ -338,7 +341,7 @@
byte4 = RING_QUEUE_PEEK(&uvd->dp, 3);
frame->header = (byte3 << 8) | byte4;
#if 0
- info("Header found.");
+ dev_info(&uvd->dev->dev, "Header found.\n");
#endif
RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, marker_len);
icam->has_hdr = 1;
@@ -354,7 +357,8 @@
}
if (!icam->has_hdr) {
if (uvd->debug > 2)
- info("Skipping frame, no header");
+ dev_info(&uvd->dev->dev,
+ "Skipping frame, no header\n");
return scan_EndParse;
}
@@ -881,7 +885,9 @@
*/
if ((frame->curline + 1) >= data_h) {
if (uvd->debug >= 3)
- info("Reached line %d. (frame is done)", frame->curline);
+ dev_info(&uvd->dev->dev,
+ "Reached line %d. (frame is done)\n",
+ frame->curline);
return scan_NextFrame;
}
@@ -954,8 +960,9 @@
if (frame->curline >= VIDEOSIZE_Y(frame->request)) {
if (uvd->debug >= 3) {
- info("All requested lines (%ld.) done.",
- VIDEOSIZE_Y(frame->request));
+ dev_info(&uvd->dev->dev,
+ "All requested lines (%ld.) done.\n",
+ VIDEOSIZE_Y(frame->request));
}
return scan_NextFrame;
} else
@@ -1000,7 +1007,9 @@
*/
if ((frame->curline + 1) >= data_h) {
if (uvd->debug >= 3)
- info("Reached line %d. (frame is done)", frame->curline);
+ dev_info(&uvd->dev->dev,
+ "Reached line %d. (frame is done)\n",
+ frame->curline);
return scan_NextFrame;
}
@@ -1049,8 +1058,9 @@
if (frame->curline >= VIDEOSIZE_Y(frame->request)) {
if (uvd->debug >= 3) {
- info("All requested lines (%ld.) done.",
- VIDEOSIZE_Y(frame->request));
+ dev_info(&uvd->dev->dev,
+ "All requested lines (%ld.) done.\n",
+ VIDEOSIZE_Y(frame->request));
}
return scan_NextFrame;
} else
@@ -1171,10 +1181,11 @@
sizeof(cp),
1000);
#if 0
- info("USB => %02x%02x%02x%02x%02x%02x%02x%02x "
- "(req=$%02x val=$%04x ind=$%04x)",
- cp[0],cp[1],cp[2],cp[3],cp[4],cp[5],cp[6],cp[7],
- req, value, index);
+ dev_info(&uvd->dev->dev,
+ "USB => %02x%02x%02x%02x%02x%02x%02x%02x "
+ "(req=$%02x val=$%04x ind=$%04x)\n",
+ cp[0],cp[1],cp[2],cp[3],cp[4],cp[5],cp[6],cp[7],
+ req, value, index);
#endif
} else {
i = usb_control_msg(
@@ -1449,10 +1460,9 @@
*/
static void ibmcam_change_lighting_conditions(struct uvd *uvd)
{
- static const char proc[] = "ibmcam_change_lighting_conditions";
-
if (debug > 0)
- info("%s: Set lighting to %hu.", proc, lighting);
+ dev_info(&uvd->dev->dev,
+ "%s: Set lighting to %hu.\n", __func__, lighting);
switch (IBMCAM_T(uvd)->camera_model) {
case IBMCAM_MODEL_1:
@@ -1495,8 +1505,6 @@
*/
static void ibmcam_set_sharpness(struct uvd *uvd)
{
- static const char proc[] = "ibmcam_set_sharpness";
-
switch (IBMCAM_T(uvd)->camera_model) {
case IBMCAM_MODEL_1:
{
@@ -1505,7 +1513,8 @@
RESTRICT_TO_RANGE(sharpness, SHARPNESS_MIN, SHARPNESS_MAX);
if (debug > 0)
- info("%s: Set sharpness to %hu.", proc, sharpness);
+ dev_info(&uvd->dev->dev, "%s: Set sharpness to %hu.\n",
+ __func__, sharpness);
sv = sa[sharpness - SHARPNESS_MIN];
for (i=0; i < 2; i++) {
@@ -1564,11 +1573,11 @@
*/
static void ibmcam_set_brightness(struct uvd *uvd)
{
- static const char proc[] = "ibmcam_set_brightness";
static const unsigned short n = 1;
if (debug > 0)
- info("%s: Set brightness to %hu.", proc, uvd->vpic.brightness);
+ dev_info(&uvd->dev->dev, "%s: Set brightness to %hu.\n",
+ __func__, uvd->vpic.brightness);
switch (IBMCAM_T(uvd)->camera_model) {
case IBMCAM_MODEL_1:
@@ -2115,7 +2124,8 @@
break;
}
if (uvd->debug > 0)
- info("Framerate (hardware): %hd.", hw_fps);
+ dev_info(&uvd->dev->dev, "Framerate (hardware): %hd.\n",
+ hw_fps);
RESTRICT_TO_RANGE(hw_fps, 0, 31);
ibmcam_model2_Packet1(uvd, mod2_set_framerate, hw_fps);
}
@@ -3487,7 +3497,7 @@
/* 01.01.08 - Added for RCA video in support -LO */
if(init_model3_input) {
if (debug > 0)
- info("Setting input to RCA.");
+ dev_info(&uvd->dev->dev, "Setting input to RCA.\n");
for (i=0; i < ARRAY_SIZE(initData); i++) {
ibmcam_veio(uvd, initData[i].req, initData[i].value, initData[i].index);
}
@@ -3685,7 +3695,7 @@
unsigned char video_ep = 0;
if (debug >= 1)
- info("ibmcam_probe(%p,%u.)", intf, ifnum);
+ dev_info(&uvd->dev->dev, "ibmcam_probe(%p,%u.)\n", intf, ifnum);
/* We don't handle multi-config cameras */
if (dev->descriptor.bNumConfigurations != 1)
@@ -3736,14 +3746,16 @@
brand = "IBM PC Camera"; /* a.k.a. Xirlink C-It */
break;
}
- info("%s USB camera found (model %d, rev. 0x%04x)",
- brand, model, le16_to_cpu(dev->descriptor.bcdDevice));
+ dev_info(&uvd->dev->dev,
+ "%s USB camera found (model %d, rev. 0x%04x)\n",
+ brand, model, le16_to_cpu(dev->descriptor.bcdDevice));
} while (0);
/* Validate found interface: must have one ISO endpoint */
nas = intf->num_altsetting;
if (debug > 0)
- info("Number of alternate settings=%d.", nas);
+ dev_info(&uvd->dev->dev, "Number of alternate settings=%d.\n",
+ nas);
if (nas < 2) {
err("Too few alternate settings for this camera!");
return -ENODEV;
@@ -3787,7 +3799,9 @@
actInterface = i;
maxPS = le16_to_cpu(endpoint->wMaxPacketSize);
if (debug > 0)
- info("Active setting=%d. maxPS=%d.", i, maxPS);
+ dev_info(&uvd->dev->dev,
+ "Active setting=%d. "
+ "maxPS=%d.\n", i, maxPS);
} else
err("More than one active alt. setting! Ignoring #%d.", i);
}
@@ -3826,7 +3840,7 @@
RESTRICT_TO_RANGE(framerate, 0, 5);
break;
default:
- info("IBM camera: using 320x240");
+ dev_info(&uvd->dev->dev, "IBM camera: using 320x240\n");
size = SIZE_320x240;
/* No break here */
case SIZE_320x240:
@@ -3855,7 +3869,7 @@
canvasY = 120;
break;
default:
- info("IBM NetCamera: using 176x144");
+ dev_info(&uvd->dev->dev, "IBM NetCamera: using 176x144\n");
size = SIZE_176x144;
/* No break here */
case SIZE_176x144:
diff --git a/drivers/media/video/usbvideo/konicawc.c b/drivers/media/video/usbvideo/konicawc.c
index 1c18028..e986c28 100644
--- a/drivers/media/video/usbvideo/konicawc.c
+++ b/drivers/media/video/usbvideo/konicawc.c
@@ -337,7 +337,8 @@
}
if((sts > 0x01) && (sts < 0x80)) {
- info("unknown status %2.2x", sts);
+ dev_info(&uvd->dev->dev, "unknown status %2.2x\n",
+ sts);
bad++;
continue;
}
@@ -568,8 +569,12 @@
fdrops = (0x80 + curframe - cam->lastframe) & 0x7F;
fdrops--;
if(fdrops) {
- info("Dropped %d frames (%d -> %d)", fdrops,
- cam->lastframe, curframe);
+ dev_info(&uvd->dev->dev,
+ "Dropped %d frames "
+ "(%d -> %d)\n",
+ fdrops,
+ cam->lastframe,
+ curframe);
}
}
cam->lastframe = curframe;
@@ -784,7 +789,8 @@
if (dev->descriptor.bNumConfigurations != 1)
return -ENODEV;
- info("Konica Webcam (rev. 0x%04x)", le16_to_cpu(dev->descriptor.bcdDevice));
+ dev_info(&intf->dev, "Konica Webcam (rev. 0x%04x)\n",
+ le16_to_cpu(dev->descriptor.bcdDevice));
RESTRICT_TO_RANGE(speed, 0, MAX_SPEED);
/* Validate found interface: must have one ISO endpoint */
@@ -925,7 +931,8 @@
static int __init konicawc_init(void)
{
struct usbvideo_cb cbTbl;
- info(DRIVER_DESC " " DRIVER_VERSION);
+ printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
+ DRIVER_DESC "\n");
memset(&cbTbl, 0, sizeof(cbTbl));
cbTbl.probe = konicawc_probe;
cbTbl.setupOnOpen = konicawc_setup_on_open;
diff --git a/drivers/media/video/usbvideo/quickcam_messenger.c b/drivers/media/video/usbvideo/quickcam_messenger.c
index 3d26a30..05c61b5 100644
--- a/drivers/media/video/usbvideo/quickcam_messenger.c
+++ b/drivers/media/video/usbvideo/quickcam_messenger.c
@@ -1080,7 +1080,8 @@
static int __init qcm_init(void)
{
- info(DRIVER_DESC " " DRIVER_VERSION);
+ printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
+ DRIVER_DESC "\n");
return usbvideo_register(
&cams,
diff --git a/drivers/media/video/usbvideo/ultracam.c b/drivers/media/video/usbvideo/ultracam.c
index 9544e64..9714baa 100644
--- a/drivers/media/video/usbvideo/ultracam.c
+++ b/drivers/media/video/usbvideo/ultracam.c
@@ -156,10 +156,11 @@
sizeof(cp),
1000);
#if 1
- info("USB => %02x%02x%02x%02x%02x%02x%02x%02x "
- "(req=$%02x val=$%04x ind=$%04x)",
- cp[0],cp[1],cp[2],cp[3],cp[4],cp[5],cp[6],cp[7],
- req, value, index);
+ dev_info(&uvd->dev->dev,
+ "USB => %02x%02x%02x%02x%02x%02x%02x%02x "
+ "(req=$%02x val=$%04x ind=$%04x)\n",
+ cp[0],cp[1],cp[2],cp[3],cp[4],cp[5],cp[6],cp[7],
+ req, value, index);
#endif
} else {
i = usb_control_msg(
@@ -517,19 +518,20 @@
unsigned char video_ep = 0;
if (debug >= 1)
- info("ultracam_probe(%p)", intf);
+ dev_info(&intf->dev, "ultracam_probe\n");
/* We don't handle multi-config cameras */
if (dev->descriptor.bNumConfigurations != 1)
return -ENODEV;
- info("IBM Ultra camera found (rev. 0x%04x)",
- le16_to_cpu(dev->descriptor.bcdDevice));
+ dev_info(&intf->dev, "IBM Ultra camera found (rev. 0x%04x)\n",
+ le16_to_cpu(dev->descriptor.bcdDevice));
/* Validate found interface: must have one ISO endpoint */
nas = intf->num_altsetting;
if (debug > 0)
- info("Number of alternate settings=%d.", nas);
+ dev_info(&intf->dev, "Number of alternate settings=%d.\n",
+ nas);
if (nas < 8) {
err("Too few alternate settings for this camera!");
return -ENODEV;
@@ -576,7 +578,9 @@
actInterface = i;
maxPS = le16_to_cpu(endpoint->wMaxPacketSize);
if (debug > 0)
- info("Active setting=%d. maxPS=%d.", i, maxPS);
+ dev_info(&intf->dev,
+ "Active setting=%d. "
+ "maxPS=%d.\n", i, maxPS);
} else {
/* Got another active alt. setting */
if (maxPS < le16_to_cpu(endpoint->wMaxPacketSize)) {
@@ -584,8 +588,11 @@
actInterface = i;
maxPS = le16_to_cpu(endpoint->wMaxPacketSize);
if (debug > 0) {
- info("Even better ctive setting=%d. maxPS=%d.",
- i, maxPS);
+ dev_info(&intf->dev,
+ "Even better ctive "
+ "setting=%d. "
+ "maxPS=%d.\n",
+ i, maxPS);
}
}
}
diff --git a/drivers/media/video/usbvideo/usbvideo.c b/drivers/media/video/usbvideo/usbvideo.c
index bf1bc2f..07cd87d 100644
--- a/drivers/media/video/usbvideo/usbvideo.c
+++ b/drivers/media/video/usbvideo/usbvideo.c
@@ -468,8 +468,9 @@
percent = (100 * goodPackets) / allPackets;
else
percent = goodPackets / (allPackets / 100);
- info("Packet Statistics: Total=%lu. Empty=%lu. Usage=%lu%%",
- allPackets, badPackets, percent);
+ dev_info(&uvd->dev->dev,
+ "Packet Statistics: Total=%lu. Empty=%lu. Usage=%lu%%\n",
+ allPackets, badPackets, percent);
if (uvd->iso_packet_len > 0) {
unsigned long allBytes, xferBytes;
char multiplier = ' ';
@@ -497,8 +498,9 @@
}
}
}
- info("Transfer Statistics: Transferred=%lu%cB Usage=%lu%%",
- xferBytes, multiplier, percent);
+ dev_info(&uvd->dev->dev,
+ "Transfer Statistics: Transferred=%lu%cB Usage=%lu%%\n",
+ xferBytes, multiplier, percent);
}
}
}
@@ -545,7 +547,7 @@
{ /* For debugging purposes only */
char tmp[20];
usbvideo_VideosizeToString(tmp, sizeof(tmp), frame->request);
- info("testpattern: frame=%s", tmp);
+ dev_info(&uvd->dev->dev, "testpattern: frame=%s\n", tmp);
}
#endif
/* Form every scan line */
@@ -854,7 +856,7 @@
usbvideo_ClientIncModCount(uvd);
if (uvd->debug > 0)
- info("%s(%p.)", __func__, intf);
+ dev_info(&intf->dev, "%s(%p.)\n", __func__, intf);
mutex_lock(&uvd->lock);
uvd->remove_pending = 1; /* Now all ISO data will be ignored */
@@ -870,14 +872,15 @@
video_unregister_device(&uvd->vdev);
if (uvd->debug > 0)
- info("%s: Video unregistered.", __func__);
+ dev_info(&intf->dev, "%s: Video unregistered.\n", __func__);
if (uvd->user)
- info("%s: In use, disconnect pending.", __func__);
+ dev_info(&intf->dev, "%s: In use, disconnect pending.\n",
+ __func__);
else
usbvideo_CameraRelease(uvd);
mutex_unlock(&uvd->lock);
- info("USB camera disconnected.");
+ dev_info(&intf->dev, "USB camera disconnected.\n");
usbvideo_ClientDecModCount(uvd);
}
@@ -1015,14 +1018,17 @@
return -EINVAL;
}
if (uvd->video_endp == 0) {
- info("%s: No video endpoint specified; data pump disabled.", __func__);
+ dev_info(&uvd->dev->dev,
+ "%s: No video endpoint specified; data pump disabled.\n",
+ __func__);
}
if (uvd->paletteBits == 0) {
err("%s: No palettes specified!", __func__);
return -EINVAL;
}
if (uvd->defaultPalette == 0) {
- info("%s: No default palette!", __func__);
+ dev_info(&uvd->dev->dev, "%s: No default palette!\n",
+ __func__);
}
uvd->max_frame_size = VIDEOSIZE_X(uvd->canvas) *
@@ -1031,25 +1037,29 @@
usbvideo_VideosizeToString(tmp2, sizeof(tmp2), uvd->canvas);
if (uvd->debug > 0) {
- info("%s: iface=%d. endpoint=$%02x paletteBits=$%08lx",
- __func__, uvd->iface, uvd->video_endp, uvd->paletteBits);
+ dev_info(&uvd->dev->dev,
+ "%s: iface=%d. endpoint=$%02x paletteBits=$%08lx\n",
+ __func__, uvd->iface, uvd->video_endp,
+ uvd->paletteBits);
}
if (uvd->dev == NULL) {
err("%s: uvd->dev == NULL", __func__);
return -EINVAL;
}
uvd->vdev.parent = &uvd->dev->dev;
- if (video_register_device(&uvd->vdev, VFL_TYPE_GRABBER, video_nr) == -1) {
+ uvd->vdev.release = video_device_release_empty;
+ if (video_register_device(&uvd->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
err("%s: video_register_device failed", __func__);
return -EPIPE;
}
if (uvd->debug > 1) {
- info("%s: video_register_device() successful", __func__);
+ dev_info(&uvd->dev->dev,
+ "%s: video_register_device() successful\n", __func__);
}
- info("%s on /dev/video%d: canvas=%s videosize=%s",
- (uvd->handle != NULL) ? uvd->handle->drvName : "???",
- uvd->vdev.minor, tmp2, tmp1);
+ dev_info(&uvd->dev->dev, "%s on /dev/video%d: canvas=%s videosize=%s\n",
+ (uvd->handle != NULL) ? uvd->handle->drvName : "???",
+ uvd->vdev.minor, tmp2, tmp1);
usb_get_dev(uvd->dev);
return 0;
@@ -1111,7 +1121,7 @@
int i, errCode = 0;
if (uvd->debug > 1)
- info("%s($%p)", __func__, dev);
+ dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, dev);
if (0 < usbvideo_ClientIncModCount(uvd))
return -ENODEV;
@@ -1178,19 +1188,25 @@
if (errCode == 0) {
if (VALID_CALLBACK(uvd, setupOnOpen)) {
if (uvd->debug > 1)
- info("%s: setupOnOpen callback", __func__);
+ dev_info(&uvd->dev->dev,
+ "%s: setupOnOpen callback\n",
+ __func__);
errCode = GET_CALLBACK(uvd, setupOnOpen)(uvd);
if (errCode < 0) {
err("%s: setupOnOpen callback failed (%d.).",
__func__, errCode);
} else if (uvd->debug > 1) {
- info("%s: setupOnOpen callback successful", __func__);
+ dev_info(&uvd->dev->dev,
+ "%s: setupOnOpen callback successful\n",
+ __func__);
}
}
if (errCode == 0) {
uvd->settingsAdjusted = 0;
if (uvd->debug > 1)
- info("%s: Open succeeded.", __func__);
+ dev_info(&uvd->dev->dev,
+ "%s: Open succeeded.\n",
+ __func__);
uvd->user++;
file->private_data = uvd;
}
@@ -1200,7 +1216,8 @@
if (errCode != 0)
usbvideo_ClientDecModCount(uvd);
if (uvd->debug > 0)
- info("%s: Returning %d.", __func__, errCode);
+ dev_info(&uvd->dev->dev, "%s: Returning %d.\n", __func__,
+ errCode);
return errCode;
}
@@ -1223,7 +1240,7 @@
int i;
if (uvd->debug > 1)
- info("%s($%p)", __func__, dev);
+ dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, dev);
mutex_lock(&uvd->lock);
GET_CALLBACK(uvd, stopDataPump)(uvd);
@@ -1243,14 +1260,15 @@
uvd->user--;
if (uvd->remove_pending) {
if (uvd->debug > 0)
- info("usbvideo_v4l_close: Final disconnect.");
+ dev_info(&uvd->dev->dev, "%s: Final disconnect.\n",
+ __func__);
usbvideo_CameraRelease(uvd);
}
mutex_unlock(&uvd->lock);
usbvideo_ClientDecModCount(uvd);
if (uvd->debug > 1)
- info("%s: Completed.", __func__);
+ dev_info(&uvd->dev->dev, "%s: Completed.\n", __func__);
file->private_data = NULL;
return 0;
}
@@ -1364,8 +1382,9 @@
struct video_mmap *vm = arg;
if (uvd->debug >= 1) {
- info("VIDIOCMCAPTURE: frame=%d. size=%dx%d, format=%d.",
- vm->frame, vm->width, vm->height, vm->format);
+ dev_info(&uvd->dev->dev,
+ "VIDIOCMCAPTURE: frame=%d. size=%dx%d, format=%d.\n",
+ vm->frame, vm->width, vm->height, vm->format);
}
/*
* Check if the requested size is supported. If the requestor
@@ -1383,18 +1402,24 @@
if ((vm->width > VIDEOSIZE_X(uvd->canvas)) ||
(vm->height > VIDEOSIZE_Y(uvd->canvas))) {
if (uvd->debug > 0) {
- info("VIDIOCMCAPTURE: Size=%dx%d too large; "
- "allowed only up to %ldx%ld", vm->width, vm->height,
- VIDEOSIZE_X(uvd->canvas), VIDEOSIZE_Y(uvd->canvas));
+ dev_info(&uvd->dev->dev,
+ "VIDIOCMCAPTURE: Size=%dx%d "
+ "too large; allowed only up "
+ "to %ldx%ld\n", vm->width,
+ vm->height,
+ VIDEOSIZE_X(uvd->canvas),
+ VIDEOSIZE_Y(uvd->canvas));
}
return -EINVAL;
}
/* Check if the palette is supported */
if (((1L << vm->format) & uvd->paletteBits) == 0) {
if (uvd->debug > 0) {
- info("VIDIOCMCAPTURE: format=%d. not supported"
- " (paletteBits=$%08lx)",
- vm->format, uvd->paletteBits);
+ dev_info(&uvd->dev->dev,
+ "VIDIOCMCAPTURE: format=%d. "
+ "not supported "
+ "(paletteBits=$%08lx)\n",
+ vm->format, uvd->paletteBits);
}
return -EINVAL;
}
@@ -1422,7 +1447,9 @@
return -EINVAL;
if (uvd->debug >= 1)
- info("VIDIOCSYNC: syncing to frame %d.", *frameNum);
+ dev_info(&uvd->dev->dev,
+ "VIDIOCSYNC: syncing to frame %d.\n",
+ *frameNum);
if (uvd->flags & FLAGS_NO_DECODING)
ret = usbvideo_GetFrame(uvd, *frameNum);
else if (VALID_CALLBACK(uvd, getFrame)) {
@@ -1504,7 +1531,9 @@
return -EFAULT;
if (uvd->debug >= 1)
- info("%s: %Zd. bytes, noblock=%d.", __func__, count, noblock);
+ dev_info(&uvd->dev->dev,
+ "%s: %Zd. bytes, noblock=%d.\n",
+ __func__, count, noblock);
mutex_lock(&uvd->lock);
@@ -1685,18 +1714,21 @@
return;
#if 0
if (urb->actual_length > 0) {
- info("urb=$%p status=%d. errcount=%d. length=%d.",
- urb, urb->status, urb->error_count, urb->actual_length);
+ dev_info(&uvd->dev->dev,
+ "urb=$%p status=%d. errcount=%d. length=%d.\n",
+ urb, urb->status, urb->error_count,
+ urb->actual_length);
} else {
static int c = 0;
if (c++ % 100 == 0)
- info("No Isoc data");
+ dev_info(&uvd->dev->dev, "No Isoc data\n");
}
#endif
if (!uvd->streaming) {
if (uvd->debug >= 1)
- info("Not streaming, but interrupt!");
+ dev_info(&uvd->dev->dev,
+ "Not streaming, but interrupt!\n");
return;
}
@@ -1741,7 +1773,7 @@
int i, errFlag;
if (uvd->debug > 1)
- info("%s($%p)", __func__, uvd);
+ dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, uvd);
if (!CAMERA_IS_OPERATIONAL(uvd)) {
err("%s: Camera is not operational", __func__);
@@ -1789,7 +1821,9 @@
uvd->streaming = 1;
if (uvd->debug > 1)
- info("%s: streaming=1 video_endp=$%02x", __func__, uvd->video_endp);
+ dev_info(&uvd->dev->dev,
+ "%s: streaming=1 video_endp=$%02x\n", __func__,
+ uvd->video_endp);
return 0;
}
@@ -1811,14 +1845,14 @@
return;
if (uvd->debug > 1)
- info("%s($%p)", __func__, uvd);
+ dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, uvd);
/* Unschedule all of the iso td's */
for (i=0; i < USBVIDEO_NUMSBUF; i++) {
usb_kill_urb(uvd->sbuf[i].urb);
}
if (uvd->debug > 1)
- info("%s: streaming=0", __func__);
+ dev_info(&uvd->dev->dev, "%s: streaming=0\n", __func__);
uvd->streaming = 0;
if (!uvd->remove_pending) {
@@ -1850,7 +1884,8 @@
int n;
if (uvd->debug > 1)
- info("usbvideo_NewFrame($%p,%d.)", uvd, framenum);
+ dev_info(&uvd->dev->dev, "usbvideo_NewFrame($%p,%d.)\n", uvd,
+ framenum);
/* If we're not grabbing a frame right now and the other frame is */
/* ready to be grabbed into, then use it instead */
@@ -1955,12 +1990,14 @@
struct usbvideo_frame *frame = &uvd->frame[frameNum];
if (uvd->debug >= 2)
- info("%s($%p,%d.)", __func__, uvd, frameNum);
+ dev_info(&uvd->dev->dev, "%s($%p,%d.)\n", __func__, uvd,
+ frameNum);
switch (frame->frameState) {
case FrameState_Unused:
if (uvd->debug >= 2)
- info("%s: FrameState_Unused", __func__);
+ dev_info(&uvd->dev->dev, "%s: FrameState_Unused\n",
+ __func__);
return -EINVAL;
case FrameState_Ready:
case FrameState_Grabbing:
@@ -1970,7 +2007,9 @@
redo:
if (!CAMERA_IS_OPERATIONAL(uvd)) {
if (uvd->debug >= 2)
- info("%s: Camera is not operational (1)", __func__);
+ dev_info(&uvd->dev->dev,
+ "%s: Camera is not operational (1)\n",
+ __func__);
return -EIO;
}
ntries = 0;
@@ -1979,24 +2018,33 @@
signalPending = signal_pending(current);
if (!CAMERA_IS_OPERATIONAL(uvd)) {
if (uvd->debug >= 2)
- info("%s: Camera is not operational (2)", __func__);
+ dev_info(&uvd->dev->dev,
+ "%s: Camera is not "
+ "operational (2)\n", __func__);
return -EIO;
}
assert(uvd->fbuf != NULL);
if (signalPending) {
if (uvd->debug >= 2)
- info("%s: Signal=$%08x", __func__, signalPending);
+ dev_info(&uvd->dev->dev,
+ "%s: Signal=$%08x\n", __func__,
+ signalPending);
if (uvd->flags & FLAGS_RETRY_VIDIOCSYNC) {
usbvideo_TestPattern(uvd, 1, 0);
uvd->curframe = -1;
uvd->stats.frame_num++;
if (uvd->debug >= 2)
- info("%s: Forced test pattern screen", __func__);
+ dev_info(&uvd->dev->dev,
+ "%s: Forced test "
+ "pattern screen\n",
+ __func__);
return 0;
} else {
/* Standard answer: Interrupted! */
if (uvd->debug >= 2)
- info("%s: Interrupted!", __func__);
+ dev_info(&uvd->dev->dev,
+ "%s: Interrupted!\n",
+ __func__);
return -EINTR;
}
} else {
@@ -2010,8 +2058,10 @@
}
} while (frame->frameState == FrameState_Grabbing);
if (uvd->debug >= 2) {
- info("%s: Grabbing done; state=%d. (%lu. bytes)",
- __func__, frame->frameState, frame->seqRead_Length);
+ dev_info(&uvd->dev->dev,
+ "%s: Grabbing done; state=%d. (%lu. bytes)\n",
+ __func__, frame->frameState,
+ frame->seqRead_Length);
}
if (frame->frameState == FrameState_Error) {
int ret = usbvideo_NewFrame(uvd, frameNum);
@@ -2048,7 +2098,9 @@
}
frame->frameState = FrameState_Done_Hold;
if (uvd->debug >= 2)
- info("%s: Entered FrameState_Done_Hold state.", __func__);
+ dev_info(&uvd->dev->dev,
+ "%s: Entered FrameState_Done_Hold state.\n",
+ __func__);
return 0;
case FrameState_Done_Hold:
@@ -2059,7 +2111,9 @@
* it will be released back into the wild to roam freely.
*/
if (uvd->debug >= 2)
- info("%s: FrameState_Done_Hold state.", __func__);
+ dev_info(&uvd->dev->dev,
+ "%s: FrameState_Done_Hold state.\n",
+ __func__);
return 0;
}
diff --git a/drivers/media/video/usbvideo/vicam.c b/drivers/media/video/usbvideo/vicam.c
index 2eb4582..7a127d6 100644
--- a/drivers/media/video/usbvideo/vicam.c
+++ b/drivers/media/video/usbvideo/vicam.c
@@ -472,9 +472,8 @@
static int
vicam_open(struct inode *inode, struct file *file)
{
- struct video_device *dev = video_devdata(file);
- struct vicam_camera *cam =
- (struct vicam_camera *) dev->priv;
+ struct vicam_camera *cam = video_drvdata(file);
+
DBG("open\n");
if (!cam) {
@@ -488,20 +487,24 @@
* rely on this fact forever.
*/
+ lock_kernel();
if (cam->open_count > 0) {
printk(KERN_INFO
"vicam_open called on already opened camera");
+ unlock_kernel();
return -EBUSY;
}
cam->raw_image = kmalloc(VICAM_MAX_READ_SIZE, GFP_KERNEL);
if (!cam->raw_image) {
+ unlock_kernel();
return -ENOMEM;
}
cam->framebuf = rvmalloc(VICAM_MAX_FRAME_SIZE * VICAM_FRAMES);
if (!cam->framebuf) {
kfree(cam->raw_image);
+ unlock_kernel();
return -ENOMEM;
}
@@ -509,6 +512,7 @@
if (!cam->cntrlbuf) {
kfree(cam->raw_image);
rvfree(cam->framebuf, VICAM_MAX_FRAME_SIZE * VICAM_FRAMES);
+ unlock_kernel();
return -ENOMEM;
}
@@ -526,6 +530,7 @@
cam->open_count++;
file->private_data = cam;
+ unlock_kernel();
return 0;
}
@@ -795,6 +800,7 @@
.name = "ViCam-based USB Camera",
.fops = &vicam_fops,
.minor = -1,
+ .release = video_device_release_empty,
};
/* table of devices that work with this driver */
@@ -859,9 +865,8 @@
mutex_init(&cam->cam_lock);
- memcpy(&cam->vdev, &vicam_template,
- sizeof (vicam_template));
- cam->vdev.priv = cam; // sort of a reverse mapping for those functions that get vdev only
+ memcpy(&cam->vdev, &vicam_template, sizeof(vicam_template));
+ video_set_drvdata(&cam->vdev, cam);
cam->udev = dev;
cam->bulkEndpoint = bulkEndpoint;
diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c
index c317ed7..b26b563 100644
--- a/drivers/media/video/usbvision/usbvision-core.c
+++ b/drivers/media/video/usbvision/usbvision-core.c
@@ -84,7 +84,8 @@
#ifdef USBVISION_DEBUG
#define PDEBUG(level, fmt, args...) { \
if (core_debug & (level)) \
- info("[%s:%d] " fmt, __func__, __LINE__ , ## args); \
+ printk(KERN_INFO KBUILD_MODNAME ":[%s:%d] " fmt, \
+ __func__, __LINE__ , ## args); \
}
#else
#define PDEBUG(level, fmt, args...) do {} while(0)
diff --git a/drivers/media/video/usbvision/usbvision-i2c.c b/drivers/media/video/usbvision/usbvision-i2c.c
index a6d0085..92427fd 100644
--- a/drivers/media/video/usbvision/usbvision-i2c.c
+++ b/drivers/media/video/usbvision/usbvision-i2c.c
@@ -47,7 +47,8 @@
#define PDEBUG(level, fmt, args...) { \
if (i2c_debug & (level)) \
- info("[%s:%d] " fmt, __func__, __LINE__ , ## args); \
+ printk(KERN_INFO KBUILD_MODNAME ":[%s:%d] " fmt, \
+ __func__, __LINE__ , ## args); \
}
static int usbvision_i2c_write(struct usb_usbvision *usbvision, unsigned char addr, char *buf,
diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c
index b977116..e10b256 100644
--- a/drivers/media/video/usbvision/usbvision-video.c
+++ b/drivers/media/video/usbvision/usbvision-video.c
@@ -98,7 +98,8 @@
#ifdef USBVISION_DEBUG
#define PDEBUG(level, fmt, args...) { \
if (video_debug & (level)) \
- info("[%s:%d] " fmt, __func__, __LINE__ , ## args); \
+ printk(KERN_INFO KBUILD_MODNAME ":[%s:%d] " fmt, \
+ __func__, __LINE__ , ## args); \
}
#else
#define PDEBUG(level, fmt, args...) do {} while(0)
@@ -360,13 +361,12 @@
*/
static int usbvision_v4l2_open(struct inode *inode, struct file *file)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
int errCode = 0;
PDEBUG(DBG_IO, "open");
+ lock_kernel();
usbvision_reset_powerOffTimer(usbvision);
if (usbvision->user)
@@ -424,6 +424,7 @@
usbvision_empty_framequeues(usbvision);
PDEBUG(DBG_IO, "success");
+ unlock_kernel();
return errCode;
}
@@ -437,9 +438,7 @@
*/
static int usbvision_v4l2_close(struct inode *inode, struct file *file)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
PDEBUG(DBG_IO, "close");
mutex_lock(&usbvision->lock);
@@ -484,9 +483,7 @@
static int vidioc_g_register (struct file *file, void *priv,
struct v4l2_register *reg)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
int errCode;
if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
@@ -505,9 +502,7 @@
static int vidioc_s_register (struct file *file, void *priv,
struct v4l2_register *reg)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
int errCode;
if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
@@ -526,9 +521,7 @@
static int vidioc_querycap (struct file *file, void *priv,
struct v4l2_capability *vc)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
strlcpy(vc->driver, "USBVision", sizeof(vc->driver));
strlcpy(vc->card,
@@ -548,9 +541,7 @@
static int vidioc_enum_input (struct file *file, void *priv,
struct v4l2_input *vi)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
int chan;
if ((vi->index >= usbvision->video_inputs) || (vi->index < 0) )
@@ -603,9 +594,7 @@
static int vidioc_g_input (struct file *file, void *priv, unsigned int *input)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
*input = usbvision->ctl_input;
return 0;
@@ -613,9 +602,7 @@
static int vidioc_s_input (struct file *file, void *priv, unsigned int input)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
if ((input >= usbvision->video_inputs) || (input < 0) )
return -EINVAL;
@@ -632,9 +619,8 @@
static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *id)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
usbvision->tvnormId=*id;
mutex_lock(&usbvision->lock);
@@ -650,9 +636,7 @@
static int vidioc_g_tuner (struct file *file, void *priv,
struct v4l2_tuner *vt)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
if (!usbvision->have_tuner || vt->index) // Only tuner 0
return -EINVAL;
@@ -671,9 +655,7 @@
static int vidioc_s_tuner (struct file *file, void *priv,
struct v4l2_tuner *vt)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
// Only no or one tuner for now
if (!usbvision->have_tuner || vt->index)
@@ -687,9 +669,7 @@
static int vidioc_g_frequency (struct file *file, void *priv,
struct v4l2_frequency *freq)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
freq->tuner = 0; // Only one tuner
if(usbvision->radio) {
@@ -705,9 +685,7 @@
static int vidioc_s_frequency (struct file *file, void *priv,
struct v4l2_frequency *freq)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
// Only no or one tuner for now
if (!usbvision->have_tuner || freq->tuner)
@@ -721,9 +699,7 @@
static int vidioc_g_audio (struct file *file, void *priv, struct v4l2_audio *a)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
memset(a,0,sizeof(*a));
if(usbvision->radio) {
@@ -748,9 +724,7 @@
static int vidioc_queryctrl (struct file *file, void *priv,
struct v4l2_queryctrl *ctrl)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
int id=ctrl->id;
memset(ctrl,0,sizeof(*ctrl));
@@ -767,9 +741,7 @@
static int vidioc_g_ctrl (struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
call_i2c_clients(usbvision, VIDIOC_G_CTRL, ctrl);
return 0;
@@ -778,9 +750,7 @@
static int vidioc_s_ctrl (struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
call_i2c_clients(usbvision, VIDIOC_S_CTRL, ctrl);
return 0;
@@ -789,9 +759,7 @@
static int vidioc_reqbufs (struct file *file,
void *priv, struct v4l2_requestbuffers *vr)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
int ret;
RESTRICT_TO_RANGE(vr->count,1,USBVISION_NUMFRAMES);
@@ -819,9 +787,7 @@
static int vidioc_querybuf (struct file *file,
void *priv, struct v4l2_buffer *vb)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
struct usbvision_frame *frame;
/* FIXME : must control
@@ -857,9 +823,7 @@
static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *vb)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
struct usbvision_frame *frame;
unsigned long lock_flags;
@@ -896,9 +860,7 @@
static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *vb)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
int ret;
struct usbvision_frame *f;
unsigned long lock_flags;
@@ -939,9 +901,7 @@
static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
int b=V4L2_BUF_TYPE_VIDEO_CAPTURE;
usbvision->streaming = Stream_On;
@@ -953,9 +913,7 @@
static int vidioc_streamoff(struct file *file,
void *priv, enum v4l2_buf_type type)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
int b=V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
@@ -988,9 +946,7 @@
static int vidioc_g_fmt_vid_cap (struct file *file, void *priv,
struct v4l2_format *vf)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
vf->fmt.pix.width = usbvision->curwidth;
vf->fmt.pix.height = usbvision->curheight;
vf->fmt.pix.pixelformat = usbvision->palette.format;
@@ -1006,9 +962,7 @@
static int vidioc_try_fmt_vid_cap (struct file *file, void *priv,
struct v4l2_format *vf)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
int formatIdx;
/* Find requested format in available ones */
@@ -1036,9 +990,7 @@
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *vf)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
int ret;
if( 0 != (ret=vidioc_try_fmt_vid_cap (file, priv, vf)) ) {
@@ -1066,9 +1018,7 @@
static ssize_t usbvision_v4l2_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
int noblock = file->f_flags & O_NONBLOCK;
unsigned long lock_flags;
@@ -1177,10 +1127,7 @@
start = vma->vm_start;
void *pos;
u32 i;
-
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
PDEBUG(DBG_MMAP, "mmap");
@@ -1237,9 +1184,7 @@
*/
static int usbvision_radio_open(struct inode *inode, struct file *file)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
int errCode = 0;
PDEBUG(DBG_IO, "%s:", __func__);
@@ -1289,9 +1234,7 @@
static int usbvision_radio_close(struct inode *inode, struct file *file)
{
- struct video_device *dev = video_devdata(file);
- struct usb_usbvision *usbvision =
- (struct usb_usbvision *) video_get_drvdata(dev);
+ struct usb_usbvision *usbvision = video_drvdata(file);
int errCode = 0;
PDEBUG(DBG_IO, "");
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c
index feab12a..f16aafe 100644
--- a/drivers/media/video/uvc/uvc_ctrl.c
+++ b/drivers/media/video/uvc/uvc_ctrl.c
@@ -83,6 +83,22 @@
},
{
.entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_WHITE_BALANCE_TEMPERATURE_CONTROL,
+ .index = 6,
+ .size = 2,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_WHITE_BALANCE_COMPONENT_CONTROL,
+ .index = 7,
+ .size = 4,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
.selector = PU_BACKLIGHT_COMPENSATION_CONTROL,
.index = 8,
.size = 2,
@@ -114,6 +130,60 @@
| UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE,
},
{
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL,
+ .index = 12,
+ .size = 1,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
+ | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL,
+ .index = 13,
+ .size = 1,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
+ | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_DIGITAL_MULTIPLIER_CONTROL,
+ .index = 14,
+ .size = 2,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL,
+ .index = 15,
+ .size = 2,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_ANALOG_VIDEO_STANDARD_CONTROL,
+ .index = 16,
+ .size = 1,
+ .flags = UVC_CONTROL_GET_CUR,
+ },
+ {
+ .entity = UVC_GUID_UVC_PROCESSING,
+ .selector = PU_ANALOG_LOCK_STATUS_CONTROL,
+ .index = 17,
+ .size = 1,
+ .flags = UVC_CONTROL_GET_CUR,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_SCANNING_MODE_CONTROL,
+ .index = 0,
+ .size = 1,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
+ | UVC_CONTROL_RESTORE,
+ },
+ {
.entity = UVC_GUID_UVC_CAMERA,
.selector = CT_AE_MODE_CONTROL,
.index = 1,
@@ -140,6 +210,14 @@
},
{
.entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_EXPOSURE_TIME_RELATIVE_CONTROL,
+ .index = 4,
+ .size = 1,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
+ | UVC_CONTROL_RESTORE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
.selector = CT_FOCUS_ABSOLUTE_CONTROL,
.index = 5,
.size = 2,
@@ -148,6 +226,78 @@
},
{
.entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_FOCUS_RELATIVE_CONTROL,
+ .index = 6,
+ .size = 2,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_IRIS_ABSOLUTE_CONTROL,
+ .index = 7,
+ .size = 2,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_IRIS_RELATIVE_CONTROL,
+ .index = 8,
+ .size = 1,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
+ | UVC_CONTROL_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_ZOOM_ABSOLUTE_CONTROL,
+ .index = 9,
+ .size = 2,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_ZOOM_RELATIVE_CONTROL,
+ .index = 10,
+ .size = 3,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_PANTILT_ABSOLUTE_CONTROL,
+ .index = 11,
+ .size = 8,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_PANTILT_RELATIVE_CONTROL,
+ .index = 12,
+ .size = 4,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_ROLL_ABSOLUTE_CONTROL,
+ .index = 13,
+ .size = 2,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_ROLL_RELATIVE_CONTROL,
+ .index = 14,
+ .size = 2,
+ .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+ | UVC_CONTROL_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_CAMERA,
.selector = CT_FOCUS_AUTO_CONTROL,
.index = 17,
.size = 1,
@@ -155,35 +305,11 @@
| UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE,
},
{
- .entity = UVC_GUID_UVC_PROCESSING,
- .selector = PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL,
- .index = 12,
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_PRIVACY_CONTROL,
+ .index = 18,
.size = 1,
.flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
- | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE,
- },
- {
- .entity = UVC_GUID_UVC_PROCESSING,
- .selector = PU_WHITE_BALANCE_TEMPERATURE_CONTROL,
- .index = 6,
- .size = 2,
- .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
- | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE,
- },
- {
- .entity = UVC_GUID_UVC_PROCESSING,
- .selector = PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL,
- .index = 13,
- .size = 1,
- .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
- | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE,
- },
- {
- .entity = UVC_GUID_UVC_PROCESSING,
- .selector = PU_WHITE_BALANCE_COMPONENT_CONTROL,
- .index = 7,
- .size = 4,
- .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
| UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE,
},
};
@@ -711,7 +837,17 @@
for (i = 0; i < entity->ncontrols; ++i) {
ctrl = &entity->controls[i];
- if (ctrl->info == NULL || !ctrl->dirty)
+ if (ctrl->info == NULL)
+ continue;
+
+ /* Reset the loaded flag for auto-update controls that were
+ * marked as loaded in uvc_ctrl_get/uvc_ctrl_set to prevent
+ * uvc_ctrl_get from using the cached value.
+ */
+ if (ctrl->info->flags & UVC_CONTROL_AUTO_UPDATE)
+ ctrl->loaded = 0;
+
+ if (!ctrl->dirty)
continue;
if (!rollback)
@@ -727,9 +863,6 @@
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
ctrl->info->size);
- if ((ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0)
- ctrl->loaded = 0;
-
ctrl->dirty = 0;
if (ret < 0)
@@ -787,8 +920,7 @@
if (ret < 0)
return ret;
- if ((ctrl->info->flags & UVC_CONTROL_AUTO_UPDATE) == 0)
- ctrl->loaded = 1;
+ ctrl->loaded = 1;
}
xctrl->value = uvc_get_le_value(
@@ -839,8 +971,7 @@
return ret;
}
- if ((ctrl->info->flags & UVC_CONTROL_AUTO_UPDATE) == 0)
- ctrl->loaded = 1;
+ ctrl->loaded = 1;
}
if (!ctrl->dirty) {
diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c
index 7e10203..d7ad060 100644
--- a/drivers/media/video/uvc/uvc_driver.c
+++ b/drivers/media/video/uvc/uvc_driver.c
@@ -1663,7 +1663,7 @@
return uvc_video_suspend(&dev->video);
}
-static int uvc_resume(struct usb_interface *intf)
+static int __uvc_resume(struct usb_interface *intf, int reset)
{
struct uvc_device *dev = usb_get_intfdata(intf);
int ret;
@@ -1672,7 +1672,7 @@
intf->cur_altsetting->desc.bInterfaceNumber);
if (intf->cur_altsetting->desc.bInterfaceSubClass == SC_VIDEOCONTROL) {
- if ((ret = uvc_ctrl_resume_device(dev)) < 0)
+ if (reset && (ret = uvc_ctrl_resume_device(dev)) < 0)
return ret;
return uvc_status_resume(dev);
@@ -1687,6 +1687,16 @@
return uvc_video_resume(&dev->video);
}
+static int uvc_resume(struct usb_interface *intf)
+{
+ return __uvc_resume(intf, 0);
+}
+
+static int uvc_reset_resume(struct usb_interface *intf)
+{
+ return __uvc_resume(intf, 1);
+}
+
/* ------------------------------------------------------------------------
* Driver initialization and cleanup
*/
@@ -1902,6 +1912,24 @@
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Compaq Presario B1200 - Bison Electronics */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x5986,
+ .idProduct = 0x0104,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Acer Travelmate 7720 - Bison Electronics */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x5986,
+ .idProduct = 0x0105,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
/* Medion Akoya Mini E1210 - Bison Electronics */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
@@ -1920,6 +1948,24 @@
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Fujitsu Amilo SI2636 - Bison Electronics */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x5986,
+ .idProduct = 0x0202,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Advent 4211 - Bison Electronics */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x5986,
+ .idProduct = 0x0203,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
/* Bison Electronics */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
@@ -1952,6 +1998,7 @@
.disconnect = uvc_disconnect,
.suspend = uvc_suspend,
.resume = uvc_resume,
+ .reset_resume = uvc_reset_resume,
.id_table = uvc_ids,
.supports_autosuspend = 1,
},
diff --git a/drivers/media/video/uvc/uvc_status.c b/drivers/media/video/uvc/uvc_status.c
index 75e678a..5d60b26 100644
--- a/drivers/media/video/uvc/uvc_status.c
+++ b/drivers/media/video/uvc/uvc_status.c
@@ -177,10 +177,16 @@
uvc_input_init(dev);
- dev->int_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (dev->int_urb == NULL)
+ dev->status = kzalloc(UVC_MAX_STATUS_SIZE, GFP_KERNEL);
+ if (dev->status == NULL)
return -ENOMEM;
+ dev->int_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (dev->int_urb == NULL) {
+ kfree(dev->status);
+ return -ENOMEM;
+ }
+
pipe = usb_rcvintpipe(dev->udev, ep->desc.bEndpointAddress);
/* For high-speed interrupt endpoints, the bInterval value is used as
@@ -192,7 +198,7 @@
interval = fls(interval) - 1;
usb_fill_int_urb(dev->int_urb, dev->udev, pipe,
- dev->status, sizeof dev->status, uvc_status_complete,
+ dev->status, UVC_MAX_STATUS_SIZE, uvc_status_complete,
dev, interval);
return usb_submit_urb(dev->int_urb, GFP_KERNEL);
@@ -202,6 +208,7 @@
{
usb_kill_urb(dev->int_urb);
usb_free_urb(dev->int_urb);
+ kfree(dev->status);
uvc_input_cleanup(dev);
}
diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c
index d7bd71b..78e4c4e 100644
--- a/drivers/media/video/uvc/uvc_v4l2.c
+++ b/drivers/media/video/uvc/uvc_v4l2.c
@@ -400,15 +400,13 @@
static int uvc_v4l2_open(struct inode *inode, struct file *file)
{
- struct video_device *vdev;
struct uvc_video_device *video;
struct uvc_fh *handle;
int ret = 0;
uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n");
mutex_lock(&uvc_driver.open_mutex);
- vdev = video_devdata(file);
- video = video_get_drvdata(vdev);
+ video = video_drvdata(file);
if (video->dev->state & UVC_DEV_DISCONNECTED) {
ret = -ENODEV;
@@ -440,8 +438,7 @@
static int uvc_v4l2_release(struct inode *inode, struct file *file)
{
- struct video_device *vdev = video_devdata(file);
- struct uvc_video_device *video = video_get_drvdata(vdev);
+ struct uvc_video_device *video = video_drvdata(file);
struct uvc_fh *handle = (struct uvc_fh *)file->private_data;
uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n");
@@ -845,10 +842,6 @@
if (ret < 0)
return ret;
- if (!(video->streaming->cur_format->flags &
- UVC_FMT_FLAG_COMPRESSED))
- video->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE;
-
rb->count = ret;
ret = 0;
break;
@@ -1031,8 +1024,7 @@
static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
{
- struct video_device *vdev = video_devdata(file);
- struct uvc_video_device *video = video_get_drvdata(vdev);
+ struct uvc_video_device *video = video_drvdata(file);
struct uvc_buffer *uninitialized_var(buffer);
struct page *page;
unsigned long addr, start, size;
@@ -1085,8 +1077,7 @@
static unsigned int uvc_v4l2_poll(struct file *file, poll_table *wait)
{
- struct video_device *vdev = video_devdata(file);
- struct uvc_video_device *video = video_get_drvdata(vdev);
+ struct uvc_video_device *video = video_drvdata(file);
uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n");
diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c
index 6854ac7..b7bb238 100644
--- a/drivers/media/video/uvc/uvc_video.c
+++ b/drivers/media/video/uvc/uvc_video.c
@@ -455,7 +455,8 @@
urb->iso_frame_desc[i].actual_length - ret);
/* Process the header again. */
- uvc_video_decode_end(video, buf, mem, ret);
+ uvc_video_decode_end(video, buf, mem,
+ urb->iso_frame_desc[i].actual_length);
if (buf->state == UVC_BUF_STATE_DONE ||
buf->state == UVC_BUF_STATE_ERROR)
@@ -512,7 +513,7 @@
video->bulk.payload_size >= video->bulk.max_payload_size) {
if (!video->bulk.skip_payload && buf != NULL) {
uvc_video_decode_end(video, buf, video->bulk.header,
- video->bulk.header_size);
+ video->bulk.payload_size);
if (buf->state == UVC_BUF_STATE_DONE ||
buf->state == UVC_BUF_STATE_ERROR)
buf = uvc_queue_next_buffer(&video->queue, buf);
@@ -655,7 +656,7 @@
if (size > UVC_MAX_FRAME_SIZE)
return -EINVAL;
- npackets = (size + psize - 1) / psize;
+ npackets = DIV_ROUND_UP(size, psize);
if (npackets > UVC_MAX_ISO_PACKETS)
npackets = UVC_MAX_ISO_PACKETS;
@@ -970,6 +971,11 @@
return 0;
}
+ if (video->streaming->cur_format->flags & UVC_FMT_FLAG_COMPRESSED)
+ video->queue.flags &= ~UVC_QUEUE_DROP_INCOMPLETE;
+ else
+ video->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE;
+
if ((ret = uvc_queue_enable(&video->queue, 1)) < 0)
return ret;
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h
index bafe340..9a6bc1a 100644
--- a/drivers/media/video/uvc/uvcvideo.h
+++ b/drivers/media/video/uvc/uvcvideo.h
@@ -303,6 +303,8 @@
#define UVC_MAX_FRAME_SIZE (16*1024*1024)
/* Maximum number of video buffers. */
#define UVC_MAX_VIDEO_BUFFERS 32
+/* Maximum status buffer size in bytes of interrupt URB. */
+#define UVC_MAX_STATUS_SIZE 16
#define UVC_CTRL_CONTROL_TIMEOUT 300
#define UVC_CTRL_STREAMING_TIMEOUT 1000
@@ -634,7 +636,7 @@
/* Status Interrupt Endpoint */
struct usb_host_endpoint *int_ep;
struct urb *int_urb;
- __u8 status[16];
+ __u8 *status;
struct input_dev *input;
/* Video Streaming interfaces */
diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c
index 88ca131..20c3be8 100644
--- a/drivers/media/video/v4l2-common.c
+++ b/drivers/media/video/v4l2-common.c
@@ -187,9 +187,11 @@
NULL
};
static const char *mpeg_audio_encoding[] = {
- "Layer I",
- "Layer II",
- "Layer III",
+ "MPEG-1/2 Layer I",
+ "MPEG-1/2 Layer II",
+ "MPEG-1/2 Layer III",
+ "MPEG-2/4 AAC",
+ "AC-3",
NULL
};
static const char *mpeg_audio_l1_bitrate[] = {
@@ -243,6 +245,28 @@
"320 kbps",
NULL
};
+ static const char *mpeg_audio_ac3_bitrate[] = {
+ "32 kbps",
+ "40 kbps",
+ "48 kbps",
+ "56 kbps",
+ "64 kbps",
+ "80 kbps",
+ "96 kbps",
+ "112 kbps",
+ "128 kbps",
+ "160 kbps",
+ "192 kbps",
+ "224 kbps",
+ "256 kbps",
+ "320 kbps",
+ "384 kbps",
+ "448 kbps",
+ "512 kbps",
+ "576 kbps",
+ "640 kbps",
+ NULL
+ };
static const char *mpeg_audio_mode[] = {
"Stereo",
"Joint Stereo",
@@ -271,6 +295,7 @@
static const char *mpeg_video_encoding[] = {
"MPEG-1",
"MPEG-2",
+ "MPEG-4 AVC",
NULL
};
static const char *mpeg_video_aspect[] = {
@@ -311,6 +336,8 @@
return mpeg_audio_l2_bitrate;
case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
return mpeg_audio_l3_bitrate;
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+ return mpeg_audio_ac3_bitrate;
case V4L2_CID_MPEG_AUDIO_MODE:
return mpeg_audio_mode;
case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
@@ -335,62 +362,73 @@
}
EXPORT_SYMBOL(v4l2_ctrl_get_menu);
+/* Return the control name. */
+const char *v4l2_ctrl_get_name(u32 id)
+{
+ switch (id) {
+ /* USER controls */
+ case V4L2_CID_USER_CLASS: return "User Controls";
+ case V4L2_CID_AUDIO_VOLUME: return "Volume";
+ case V4L2_CID_AUDIO_MUTE: return "Mute";
+ case V4L2_CID_AUDIO_BALANCE: return "Balance";
+ case V4L2_CID_AUDIO_BASS: return "Bass";
+ case V4L2_CID_AUDIO_TREBLE: return "Treble";
+ case V4L2_CID_AUDIO_LOUDNESS: return "Loudness";
+ case V4L2_CID_BRIGHTNESS: return "Brightness";
+ case V4L2_CID_CONTRAST: return "Contrast";
+ case V4L2_CID_SATURATION: return "Saturation";
+ case V4L2_CID_HUE: return "Hue";
+
+ /* MPEG controls */
+ case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls";
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: return "Audio Sampling Frequency";
+ case V4L2_CID_MPEG_AUDIO_ENCODING: return "Audio Encoding";
+ case V4L2_CID_MPEG_AUDIO_L1_BITRATE: return "Audio Layer I Bitrate";
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE: return "Audio Layer II Bitrate";
+ case V4L2_CID_MPEG_AUDIO_L3_BITRATE: return "Audio Layer III Bitrate";
+ case V4L2_CID_MPEG_AUDIO_AAC_BITRATE: return "Audio AAC Bitrate";
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: return "Audio AC-3 Bitrate";
+ case V4L2_CID_MPEG_AUDIO_MODE: return "Audio Stereo Mode";
+ case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: return "Audio Stereo Mode Extension";
+ case V4L2_CID_MPEG_AUDIO_EMPHASIS: return "Audio Emphasis";
+ case V4L2_CID_MPEG_AUDIO_CRC: return "Audio CRC";
+ case V4L2_CID_MPEG_AUDIO_MUTE: return "Audio Mute";
+ case V4L2_CID_MPEG_VIDEO_ENCODING: return "Video Encoding";
+ case V4L2_CID_MPEG_VIDEO_ASPECT: return "Video Aspect";
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES: return "Video B Frames";
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE: return "Video GOP Size";
+ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: return "Video GOP Closure";
+ case V4L2_CID_MPEG_VIDEO_PULLDOWN: return "Video Pulldown";
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: return "Video Bitrate Mode";
+ case V4L2_CID_MPEG_VIDEO_BITRATE: return "Video Bitrate";
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: return "Video Peak Bitrate";
+ case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: return "Video Temporal Decimation";
+ case V4L2_CID_MPEG_VIDEO_MUTE: return "Video Mute";
+ case V4L2_CID_MPEG_VIDEO_MUTE_YUV: return "Video Mute YUV";
+ case V4L2_CID_MPEG_STREAM_TYPE: return "Stream Type";
+ case V4L2_CID_MPEG_STREAM_PID_PMT: return "Stream PMT Program ID";
+ case V4L2_CID_MPEG_STREAM_PID_AUDIO: return "Stream Audio Program ID";
+ case V4L2_CID_MPEG_STREAM_PID_VIDEO: return "Stream Video Program ID";
+ case V4L2_CID_MPEG_STREAM_PID_PCR: return "Stream PCR Program ID";
+ case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO: return "Stream PES Audio ID";
+ case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: return "Stream PES Video ID";
+ case V4L2_CID_MPEG_STREAM_VBI_FMT: return "Stream VBI Format";
+
+ default:
+ return NULL;
+ }
+}
+EXPORT_SYMBOL(v4l2_ctrl_get_name);
+
/* Fill in a struct v4l2_queryctrl */
int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def)
{
- const char *name;
+ const char *name = v4l2_ctrl_get_name(qctrl->id);
qctrl->flags = 0;
- switch (qctrl->id) {
- /* USER controls */
- case V4L2_CID_USER_CLASS: name = "User Controls"; break;
- case V4L2_CID_AUDIO_VOLUME: name = "Volume"; break;
- case V4L2_CID_AUDIO_MUTE: name = "Mute"; break;
- case V4L2_CID_AUDIO_BALANCE: name = "Balance"; break;
- case V4L2_CID_AUDIO_BASS: name = "Bass"; break;
- case V4L2_CID_AUDIO_TREBLE: name = "Treble"; break;
- case V4L2_CID_AUDIO_LOUDNESS: name = "Loudness"; break;
- case V4L2_CID_BRIGHTNESS: name = "Brightness"; break;
- case V4L2_CID_CONTRAST: name = "Contrast"; break;
- case V4L2_CID_SATURATION: name = "Saturation"; break;
- case V4L2_CID_HUE: name = "Hue"; break;
-
- /* MPEG controls */
- case V4L2_CID_MPEG_CLASS: name = "MPEG Encoder Controls"; break;
- case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: name = "Audio Sampling Frequency"; break;
- case V4L2_CID_MPEG_AUDIO_ENCODING: name = "Audio Encoding Layer"; break;
- case V4L2_CID_MPEG_AUDIO_L1_BITRATE: name = "Audio Layer I Bitrate"; break;
- case V4L2_CID_MPEG_AUDIO_L2_BITRATE: name = "Audio Layer II Bitrate"; break;
- case V4L2_CID_MPEG_AUDIO_L3_BITRATE: name = "Audio Layer III Bitrate"; break;
- case V4L2_CID_MPEG_AUDIO_MODE: name = "Audio Stereo Mode"; break;
- case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: name = "Audio Stereo Mode Extension"; break;
- case V4L2_CID_MPEG_AUDIO_EMPHASIS: name = "Audio Emphasis"; break;
- case V4L2_CID_MPEG_AUDIO_CRC: name = "Audio CRC"; break;
- case V4L2_CID_MPEG_AUDIO_MUTE: name = "Audio Mute"; break;
- case V4L2_CID_MPEG_VIDEO_ENCODING: name = "Video Encoding"; break;
- case V4L2_CID_MPEG_VIDEO_ASPECT: name = "Video Aspect"; break;
- case V4L2_CID_MPEG_VIDEO_B_FRAMES: name = "Video B Frames"; break;
- case V4L2_CID_MPEG_VIDEO_GOP_SIZE: name = "Video GOP Size"; break;
- case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: name = "Video GOP Closure"; break;
- case V4L2_CID_MPEG_VIDEO_PULLDOWN: name = "Video Pulldown"; break;
- case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: name = "Video Bitrate Mode"; break;
- case V4L2_CID_MPEG_VIDEO_BITRATE: name = "Video Bitrate"; break;
- case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: name = "Video Peak Bitrate"; break;
- case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: name = "Video Temporal Decimation"; break;
- case V4L2_CID_MPEG_VIDEO_MUTE: name = "Video Mute"; break;
- case V4L2_CID_MPEG_VIDEO_MUTE_YUV: name = "Video Mute YUV"; break;
- case V4L2_CID_MPEG_STREAM_TYPE: name = "Stream Type"; break;
- case V4L2_CID_MPEG_STREAM_PID_PMT: name = "Stream PMT Program ID"; break;
- case V4L2_CID_MPEG_STREAM_PID_AUDIO: name = "Stream Audio Program ID"; break;
- case V4L2_CID_MPEG_STREAM_PID_VIDEO: name = "Stream Video Program ID"; break;
- case V4L2_CID_MPEG_STREAM_PID_PCR: name = "Stream PCR Program ID"; break;
- case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO: name = "Stream PES Audio ID"; break;
- case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: name = "Stream PES Video ID"; break;
- case V4L2_CID_MPEG_STREAM_VBI_FMT: name = "Stream VBI Format"; break;
-
- default:
+ if (name == NULL)
return -EINVAL;
- }
+
switch (qctrl->id) {
case V4L2_CID_AUDIO_MUTE:
case V4L2_CID_AUDIO_LOUDNESS:
@@ -407,6 +445,7 @@
case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
case V4L2_CID_MPEG_AUDIO_MODE:
case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
case V4L2_CID_MPEG_AUDIO_EMPHASIS:
@@ -493,7 +532,7 @@
case V4L2_CID_MPEG_AUDIO_ENCODING:
return v4l2_ctrl_query_fill(qctrl,
V4L2_MPEG_AUDIO_ENCODING_LAYER_1,
- V4L2_MPEG_AUDIO_ENCODING_LAYER_3, 1,
+ V4L2_MPEG_AUDIO_ENCODING_AC3, 1,
V4L2_MPEG_AUDIO_ENCODING_LAYER_2);
case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
return v4l2_ctrl_query_fill(qctrl,
@@ -510,6 +549,13 @@
V4L2_MPEG_AUDIO_L3_BITRATE_32K,
V4L2_MPEG_AUDIO_L3_BITRATE_320K, 1,
V4L2_MPEG_AUDIO_L3_BITRATE_192K);
+ case V4L2_CID_MPEG_AUDIO_AAC_BITRATE:
+ return v4l2_ctrl_query_fill(qctrl, 0, 6400, 1, 3200000);
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_32K,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_640K, 1,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_384K);
case V4L2_CID_MPEG_AUDIO_MODE:
return v4l2_ctrl_query_fill(qctrl,
V4L2_MPEG_AUDIO_MODE_STEREO,
@@ -535,7 +581,7 @@
case V4L2_CID_MPEG_VIDEO_ENCODING:
return v4l2_ctrl_query_fill(qctrl,
V4L2_MPEG_VIDEO_ENCODING_MPEG_1,
- V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 1,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 1,
V4L2_MPEG_VIDEO_ENCODING_MPEG_2);
case V4L2_CID_MPEG_VIDEO_ASPECT:
return v4l2_ctrl_query_fill(qctrl,
@@ -594,12 +640,17 @@
EXPORT_SYMBOL(v4l2_ctrl_query_fill_std);
/* Fill in a struct v4l2_querymenu based on the struct v4l2_queryctrl and
- the menu. The qctrl pointer may be NULL, in which case it is ignored. */
+ the menu. The qctrl pointer may be NULL, in which case it is ignored.
+ If menu_items is NULL, then the menu items are retrieved using
+ v4l2_ctrl_get_menu. */
int v4l2_ctrl_query_menu(struct v4l2_querymenu *qmenu, struct v4l2_queryctrl *qctrl,
const char **menu_items)
{
int i;
+ qmenu->reserved = 0;
+ if (menu_items == NULL)
+ menu_items = v4l2_ctrl_get_menu(qmenu->id);
if (menu_items == NULL ||
(qctrl && (qmenu->index < qctrl->minimum || qmenu->index > qctrl->maximum)))
return -EINVAL;
@@ -607,11 +658,31 @@
if (menu_items[i] == NULL || menu_items[i][0] == '\0')
return -EINVAL;
snprintf(qmenu->name, sizeof(qmenu->name), menu_items[qmenu->index]);
- qmenu->reserved = 0;
return 0;
}
EXPORT_SYMBOL(v4l2_ctrl_query_menu);
+/* Fill in a struct v4l2_querymenu based on the specified array of valid
+ menu items (terminated by V4L2_CTRL_MENU_IDS_END).
+ Use this if there are 'holes' in the list of valid menu items. */
+int v4l2_ctrl_query_menu_valid_items(struct v4l2_querymenu *qmenu, const u32 *ids)
+{
+ const char **menu_items = v4l2_ctrl_get_menu(qmenu->id);
+
+ qmenu->reserved = 0;
+ if (menu_items == NULL || ids == NULL)
+ return -EINVAL;
+ while (*ids != V4L2_CTRL_MENU_IDS_END) {
+ if (*ids++ == qmenu->index) {
+ snprintf(qmenu->name, sizeof(qmenu->name),
+ menu_items[qmenu->index]);
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL(v4l2_ctrl_query_menu_valid_items);
+
/* ctrl_classes points to an array of u32 pointers, the last element is
a NULL pointer. Each u32 array is a 0-terminated array of control IDs.
Each array must be sorted low to high and belong to the same control
diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c
index 155fdec..ccd6566 100644
--- a/drivers/media/video/v4l2-dev.c
+++ b/drivers/media/video/v4l2-dev.c
@@ -42,6 +42,7 @@
struct device_attribute *attr, char *buf)
{
struct video_device *vfd = container_of(cd, struct video_device, dev);
+
return sprintf(buf, "%i\n", vfd->index);
}
@@ -49,6 +50,7 @@
struct device_attribute *attr, char *buf)
{
struct video_device *vfd = container_of(cd, struct video_device, dev);
+
return sprintf(buf, "%.*s\n", (int)sizeof(vfd->name), vfd->name);
}
@@ -58,12 +60,16 @@
__ATTR_NULL
};
+/*
+ * Active devices
+ */
+static struct video_device *video_device[VIDEO_NUM_DEVICES];
+static DEFINE_MUTEX(videodev_lock);
+static DECLARE_BITMAP(video_nums[VFL_TYPE_MAX], VIDEO_NUM_DEVICES);
+
struct video_device *video_device_alloc(void)
{
- struct video_device *vfd;
-
- vfd = kzalloc(sizeof(*vfd), GFP_KERNEL);
- return vfd;
+ return kzalloc(sizeof(struct video_device), GFP_KERNEL);
}
EXPORT_SYMBOL(video_device_alloc);
@@ -73,16 +79,52 @@
}
EXPORT_SYMBOL(video_device_release);
+void video_device_release_empty(struct video_device *vfd)
+{
+ /* Do nothing */
+ /* Only valid when the video_device struct is a static. */
+}
+EXPORT_SYMBOL(video_device_release_empty);
+
+/* Called when the last user of the character device is gone. */
+static void v4l2_chardev_release(struct kobject *kobj)
+{
+ struct video_device *vfd = container_of(kobj, struct video_device, cdev.kobj);
+
+ mutex_lock(&videodev_lock);
+ if (video_device[vfd->minor] != vfd) {
+ mutex_unlock(&videodev_lock);
+ BUG();
+ return;
+ }
+
+ /* Free up this device for reuse */
+ video_device[vfd->minor] = NULL;
+ clear_bit(vfd->num, video_nums[vfd->vfl_type]);
+ mutex_unlock(&videodev_lock);
+
+ /* Release the character device */
+ vfd->cdev_release(kobj);
+ /* Release video_device and perform other
+ cleanups as needed. */
+ if (vfd->release)
+ vfd->release(vfd);
+}
+
+/* The new kobj_type for the character device */
+static struct kobj_type v4l2_ktype_cdev_default = {
+ .release = v4l2_chardev_release,
+};
+
static void video_release(struct device *cd)
{
struct video_device *vfd = container_of(cd, struct video_device, dev);
-#if 1
- /* needed until all drivers are fixed */
- if (!vfd->release)
- return;
-#endif
- vfd->release(vfd);
+ /* It's now safe to delete the char device.
+ This will either trigger the v4l2_chardev_release immediately (if
+ the refcount goes to 0) or later when the last user of the
+ character device closes it. */
+ cdev_del(&vfd->cdev);
}
static struct class video_class = {
@@ -91,87 +133,12 @@
.dev_release = video_release,
};
-/*
- * Active devices
- */
-
-static struct video_device *video_device[VIDEO_NUM_DEVICES];
-static DEFINE_MUTEX(videodev_lock);
-
struct video_device *video_devdata(struct file *file)
{
return video_device[iminor(file->f_path.dentry->d_inode)];
}
EXPORT_SYMBOL(video_devdata);
-/*
- * Open a video device - FIXME: Obsoleted
- */
-static int video_open(struct inode *inode, struct file *file)
-{
- unsigned int minor = iminor(inode);
- int err = 0;
- struct video_device *vfl;
- const struct file_operations *old_fops;
-
- if (minor >= VIDEO_NUM_DEVICES)
- return -ENODEV;
- lock_kernel();
- mutex_lock(&videodev_lock);
- vfl = video_device[minor];
- if (vfl == NULL) {
- mutex_unlock(&videodev_lock);
- request_module("char-major-%d-%d", VIDEO_MAJOR, minor);
- mutex_lock(&videodev_lock);
- vfl = video_device[minor];
- if (vfl == NULL) {
- mutex_unlock(&videodev_lock);
- unlock_kernel();
- return -ENODEV;
- }
- }
- old_fops = file->f_op;
- file->f_op = fops_get(vfl->fops);
- if (file->f_op->open)
- err = file->f_op->open(inode, file);
- if (err) {
- fops_put(file->f_op);
- file->f_op = fops_get(old_fops);
- }
- fops_put(old_fops);
- mutex_unlock(&videodev_lock);
- unlock_kernel();
- return err;
-}
-
-/*
- * open/release helper functions -- handle exclusive opens
- * Should be removed soon
- */
-int video_exclusive_open(struct inode *inode, struct file *file)
-{
- struct video_device *vfl = video_devdata(file);
- int retval = 0;
-
- mutex_lock(&vfl->lock);
- if (vfl->users)
- retval = -EBUSY;
- else
- vfl->users++;
- mutex_unlock(&vfl->lock);
- return retval;
-}
-EXPORT_SYMBOL(video_exclusive_open);
-
-int video_exclusive_release(struct inode *inode, struct file *file)
-{
- struct video_device *vfl = video_devdata(file);
-
- vfl->users--;
- return 0;
-}
-EXPORT_SYMBOL(video_exclusive_release);
-
/**
* get_index - assign stream number based on parent device
* @vdev: video_device to assign index number to, vdev->dev should be assigned
@@ -252,33 +219,29 @@
int index)
{
int i = 0;
- int base;
- int end;
int ret;
- char *name_base;
+ int minor_offset = 0;
+ int minor_cnt = VIDEO_NUM_DEVICES;
+ const char *name_base;
+ void *priv = video_get_drvdata(vfd);
+
+ /* the release callback MUST be present */
+ BUG_ON(!vfd->release);
if (vfd == NULL)
return -EINVAL;
switch (type) {
case VFL_TYPE_GRABBER:
- base = MINOR_VFL_TYPE_GRABBER_MIN;
- end = MINOR_VFL_TYPE_GRABBER_MAX+1;
name_base = "video";
break;
case VFL_TYPE_VTX:
- base = MINOR_VFL_TYPE_VTX_MIN;
- end = MINOR_VFL_TYPE_VTX_MAX+1;
name_base = "vtx";
break;
case VFL_TYPE_VBI:
- base = MINOR_VFL_TYPE_VBI_MIN;
- end = MINOR_VFL_TYPE_VBI_MAX+1;
name_base = "vbi";
break;
case VFL_TYPE_RADIO:
- base = MINOR_VFL_TYPE_RADIO_MIN;
- end = MINOR_VFL_TYPE_RADIO_MAX+1;
name_base = "radio";
break;
default:
@@ -287,28 +250,70 @@
return -EINVAL;
}
+ vfd->vfl_type = type;
+
+#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
+ /* Keep the ranges for the first four types for historical
+ * reasons.
+ * Newer devices (not yet in place) should use the range
+ * of 128-191 and just pick the first free minor there
+ * (new style). */
+ switch (type) {
+ case VFL_TYPE_GRABBER:
+ minor_offset = 0;
+ minor_cnt = 64;
+ break;
+ case VFL_TYPE_RADIO:
+ minor_offset = 64;
+ minor_cnt = 64;
+ break;
+ case VFL_TYPE_VTX:
+ minor_offset = 192;
+ minor_cnt = 32;
+ break;
+ case VFL_TYPE_VBI:
+ minor_offset = 224;
+ minor_cnt = 32;
+ break;
+ default:
+ minor_offset = 128;
+ minor_cnt = 64;
+ break;
+ }
+#endif
+
+ /* Initialize the character device */
+ cdev_init(&vfd->cdev, vfd->fops);
+ vfd->cdev.owner = vfd->fops->owner;
/* pick a minor number */
mutex_lock(&videodev_lock);
- if (nr >= 0 && nr < end-base) {
- /* use the one the driver asked for */
- i = base + nr;
- if (NULL != video_device[i]) {
- mutex_unlock(&videodev_lock);
- return -ENFILE;
- }
- } else {
- /* use first free */
- for (i = base; i < end; i++)
- if (NULL == video_device[i])
- break;
- if (i == end) {
- mutex_unlock(&videodev_lock);
- return -ENFILE;
- }
+ nr = find_next_zero_bit(video_nums[type], minor_cnt, nr == -1 ? 0 : nr);
+ if (nr == minor_cnt)
+ nr = find_first_zero_bit(video_nums[type], minor_cnt);
+ if (nr == minor_cnt) {
+ printk(KERN_ERR "could not get a free kernel number\n");
+ mutex_unlock(&videodev_lock);
+ return -ENFILE;
}
- video_device[i] = vfd;
- vfd->vfl_type = type;
- vfd->minor = i;
+#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
+ /* 1-on-1 mapping of kernel number to minor number */
+ i = nr;
+#else
+ /* The kernel number and minor numbers are independent */
+ for (i = 0; i < VIDEO_NUM_DEVICES; i++)
+ if (video_device[i] == NULL)
+ break;
+ if (i == VIDEO_NUM_DEVICES) {
+ mutex_unlock(&videodev_lock);
+ printk(KERN_ERR "could not get a free minor\n");
+ return -ENFILE;
+ }
+#endif
+ vfd->minor = i + minor_offset;
+ vfd->num = nr;
+ set_bit(nr, video_nums[type]);
+ BUG_ON(video_device[vfd->minor]);
+ video_device[vfd->minor] = vfd;
ret = get_index(vfd, index);
vfd->index = ret;
@@ -320,35 +325,41 @@
goto fail_minor;
}
- mutex_init(&vfd->lock);
-
+ ret = cdev_add(&vfd->cdev, MKDEV(VIDEO_MAJOR, vfd->minor), 1);
+ if (ret < 0) {
+ printk(KERN_ERR "%s: cdev_add failed\n", __func__);
+ goto fail_minor;
+ }
/* sysfs class */
- memset(&vfd->dev, 0x00, sizeof(vfd->dev));
+ memset(&vfd->dev, 0, sizeof(vfd->dev));
+ /* The memset above cleared the device's drvdata, so
+ put back the copy we made earlier. */
+ video_set_drvdata(vfd, priv);
vfd->dev.class = &video_class;
vfd->dev.devt = MKDEV(VIDEO_MAJOR, vfd->minor);
if (vfd->parent)
vfd->dev.parent = vfd->parent;
- sprintf(vfd->dev.bus_id, "%s%d", name_base, i - base);
+ sprintf(vfd->dev.bus_id, "%s%d", name_base, nr);
ret = device_register(&vfd->dev);
if (ret < 0) {
printk(KERN_ERR "%s: device_register failed\n", __func__);
- goto fail_minor;
+ goto del_cdev;
}
-
-#if 1
- /* needed until all drivers are fixed */
- if (!vfd->release)
- printk(KERN_WARNING "videodev: \"%s\" has no release callback. "
- "Please fix your driver for proper sysfs support, see "
- "http://lwn.net/Articles/36850/\n", vfd->name);
-#endif
+ /* Remember the cdev's release function */
+ vfd->cdev_release = vfd->cdev.kobj.ktype->release;
+ /* Install our own */
+ vfd->cdev.kobj.ktype = &v4l2_ktype_cdev_default;
return 0;
+del_cdev:
+ cdev_del(&vfd->cdev);
+
fail_minor:
mutex_lock(&videodev_lock);
video_device[vfd->minor] = NULL;
- vfd->minor = -1;
+ clear_bit(vfd->num, video_nums[type]);
mutex_unlock(&videodev_lock);
+ vfd->minor = -1;
return ret;
}
EXPORT_SYMBOL(video_register_device_index);
@@ -363,42 +374,29 @@
void video_unregister_device(struct video_device *vfd)
{
- mutex_lock(&videodev_lock);
- if (video_device[vfd->minor] != vfd)
- panic("videodev: bad unregister");
-
- video_device[vfd->minor] = NULL;
device_unregister(&vfd->dev);
- mutex_unlock(&videodev_lock);
}
EXPORT_SYMBOL(video_unregister_device);
/*
- * Video fs operations
- */
-static const struct file_operations video_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .open = video_open,
-};
-
-/*
* Initialise video for linux
*/
-
static int __init videodev_init(void)
{
+ dev_t dev = MKDEV(VIDEO_MAJOR, 0);
int ret;
printk(KERN_INFO "Linux video capture interface: v2.00\n");
- if (register_chrdev(VIDEO_MAJOR, VIDEO_NAME, &video_fops)) {
- printk(KERN_WARNING "video_dev: unable to get major %d\n", VIDEO_MAJOR);
- return -EIO;
+ ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);
+ if (ret < 0) {
+ printk(KERN_WARNING "videodev: unable to get major %d\n",
+ VIDEO_MAJOR);
+ return ret;
}
ret = class_register(&video_class);
if (ret < 0) {
- unregister_chrdev(VIDEO_MAJOR, VIDEO_NAME);
+ unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
printk(KERN_WARNING "video_dev: class_register failed\n");
return -EIO;
}
@@ -408,8 +406,10 @@
static void __exit videodev_exit(void)
{
+ dev_t dev = MKDEV(VIDEO_MAJOR, 0);
+
class_unregister(&video_class);
- unregister_chrdev(VIDEO_MAJOR, VIDEO_NAME);
+ unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
}
module_init(videodev_init)
diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c
index 140ef92..155c9d7 100644
--- a/drivers/media/video/v4l2-ioctl.c
+++ b/drivers/media/video/v4l2-ioctl.c
@@ -746,18 +746,6 @@
ret = ops->vidioc_enum_fmt_vid_overlay(file,
fh, f);
break;
-#if 1
- /* V4L2_BUF_TYPE_VBI_CAPTURE should not support VIDIOC_ENUM_FMT
- * according to the spec. The bttv and saa7134 drivers support
- * it though, so just warn that this is deprecated and will be
- * removed in the near future. */
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- if (ops->vidioc_enum_fmt_vbi_cap) {
- printk(KERN_WARNING "vidioc_enum_fmt_vbi_cap will be removed in 2.6.28!\n");
- ret = ops->vidioc_enum_fmt_vbi_cap(file, fh, f);
- }
- break;
-#endif
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
if (ops->vidioc_enum_fmt_vid_out)
ret = ops->vidioc_enum_fmt_vid_out(file, fh, f);
diff --git a/drivers/media/video/vino.c b/drivers/media/video/vino.c
index 1edda45..8ec57df 100644
--- a/drivers/media/video/vino.c
+++ b/drivers/media/video/vino.c
@@ -39,7 +39,6 @@
#include <linux/i2c-algo-sgi.h>
#include <linux/videodev2.h>
-#include <media/v4l2-ioctl.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <linux/video_decoder.h>
@@ -810,7 +809,7 @@
dprintk("vino_free_buffer_with_count(): count = %d\n", count);
for (i = 0; i < count; i++) {
- ClearPageReserved(virt_to_page(fb->desc_table.virtual[i]));
+ ClearPageReserved(virt_to_page((void *)fb->desc_table.virtual[i]));
dma_unmap_single(NULL,
fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i],
PAGE_SIZE, DMA_FROM_DEVICE);
@@ -888,7 +887,7 @@
dma_data_addr + VINO_PAGE_SIZE * j;
}
- SetPageReserved(virt_to_page(fb->desc_table.virtual[i]));
+ SetPageReserved(virt_to_page((void *)fb->desc_table.virtual[i]));
}
/* page_count needs to be set anyway, because the descriptor table has
@@ -975,7 +974,7 @@
dma_data_addr + VINO_PAGE_SIZE * j;
}
- SetPageReserved(virt_to_page(fb->desc_table.virtual[i]));
+ SetPageReserved(virt_to_page((void *)fb->desc_table.virtual[i]));
}
/* page_count needs to be set anyway, because the descriptor table has
@@ -4025,8 +4024,7 @@
static int vino_open(struct inode *inode, struct file *file)
{
- struct video_device *dev = video_devdata(file);
- struct vino_channel_settings *vcs = video_get_drvdata(dev);
+ struct vino_channel_settings *vcs = video_drvdata(file);
int ret = 0;
dprintk("open(): channel = %c\n",
(vcs->channel == VINO_CHANNEL_A) ? 'A' : 'B');
@@ -4057,8 +4055,7 @@
static int vino_close(struct inode *inode, struct file *file)
{
- struct video_device *dev = video_devdata(file);
- struct vino_channel_settings *vcs = video_get_drvdata(dev);
+ struct vino_channel_settings *vcs = video_drvdata(file);
dprintk("close():\n");
mutex_lock(&vcs->mutex);
@@ -4101,8 +4098,7 @@
static int vino_mmap(struct file *file, struct vm_area_struct *vma)
{
- struct video_device *dev = video_devdata(file);
- struct vino_channel_settings *vcs = video_get_drvdata(dev);
+ struct vino_channel_settings *vcs = video_drvdata(file);
unsigned long start = vma->vm_start;
unsigned long size = vma->vm_end - vma->vm_start;
@@ -4207,8 +4203,7 @@
static unsigned int vino_poll(struct file *file, poll_table *pt)
{
- struct video_device *dev = video_devdata(file);
- struct vino_channel_settings *vcs = video_get_drvdata(dev);
+ struct vino_channel_settings *vcs = video_drvdata(file);
unsigned int outgoing;
unsigned int ret = 0;
@@ -4248,8 +4243,7 @@
static int vino_do_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, void *arg)
{
- struct video_device *dev = video_devdata(file);
- struct vino_channel_settings *vcs = video_get_drvdata(dev);
+ struct vino_channel_settings *vcs = video_drvdata(file);
#ifdef VINO_DEBUG
switch (_IOC_TYPE(cmd)) {
@@ -4356,8 +4350,7 @@
static int vino_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- struct video_device *dev = video_devdata(file);
- struct vino_channel_settings *vcs = video_get_drvdata(dev);
+ struct vino_channel_settings *vcs = video_drvdata(file);
int ret;
if (mutex_lock_interruptible(&vcs->mutex))
diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
index 8ba8daa..65c8af1 100644
--- a/drivers/media/video/vivi.c
+++ b/drivers/media/video/vivi.c
@@ -898,9 +898,11 @@
printk(KERN_DEBUG "vivi: open called (minor=%d)\n", minor);
+ lock_kernel();
list_for_each_entry(dev, &vivi_devlist, vivi_devlist)
if (dev->vfd->minor == minor)
goto found;
+ unlock_kernel();
return -ENODEV;
found:
@@ -925,8 +927,10 @@
}
unlock:
mutex_unlock(&dev->mutex);
- if (retval)
+ if (retval) {
+ unlock_kernel();
return retval;
+ }
file->private_data = fh;
fh->dev = dev;
@@ -955,6 +959,7 @@
sizeof(struct vivi_buffer), fh);
vivi_start_thread(fh);
+ unlock_kernel();
return 0;
}
diff --git a/drivers/media/video/vpx3220.c b/drivers/media/video/vpx3220.c
index 3529302..45be9ec 100644
--- a/drivers/media/video/vpx3220.c
+++ b/drivers/media/video/vpx3220.c
@@ -24,8 +24,6 @@
#include <linux/types.h>
#include <linux/slab.h>
-#include <linux/byteorder/swab.h>
-
#include <asm/io.h>
#include <asm/uaccess.h>
diff --git a/drivers/media/video/w9966.c b/drivers/media/video/w9966.c
index 2ff00bc..b2dbe48 100644
--- a/drivers/media/video/w9966.c
+++ b/drivers/media/video/w9966.c
@@ -113,6 +113,7 @@
signed char contrast;
signed char color;
signed char hue;
+ unsigned long in_use;
};
/*
@@ -184,10 +185,25 @@
static ssize_t w9966_v4l_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos);
+static int w9966_exclusive_open(struct inode *inode, struct file *file)
+{
+ struct w9966_dev *cam = video_drvdata(file);
+
+ return test_and_set_bit(0, &cam->in_use) ? -EBUSY : 0;
+}
+
+static int w9966_exclusive_release(struct inode *inode, struct file *file)
+{
+ struct w9966_dev *cam = video_drvdata(file);
+
+ clear_bit(0, &cam->in_use);
+ return 0;
+}
+
static const struct file_operations w9966_fops = {
.owner = THIS_MODULE,
- .open = video_exclusive_open,
- .release = video_exclusive_release,
+ .open = w9966_exclusive_open,
+ .release = w9966_exclusive_release,
.ioctl = w9966_v4l_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = v4l_compat_ioctl32,
@@ -198,6 +214,7 @@
static struct video_device w9966_template = {
.name = W9966_DRIVERNAME,
.fops = &w9966_fops,
+ .release = video_device_release_empty,
};
/*
@@ -332,7 +349,7 @@
// Fill in the video_device struct and register us to v4l
memcpy(&cam->vdev, &w9966_template, sizeof(struct video_device));
- cam->vdev.priv = cam;
+ video_set_drvdata(&cam->vdev, cam);
if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0)
return -1;
@@ -713,8 +730,7 @@
static int w9966_v4l_do_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, void *arg)
{
- struct video_device *vdev = video_devdata(file);
- struct w9966_dev *cam = vdev->priv;
+ struct w9966_dev *cam = video_drvdata(file);
switch(cmd)
{
@@ -872,8 +888,7 @@
static ssize_t w9966_v4l_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
- struct video_device *vdev = video_devdata(file);
- struct w9966_dev *cam = vdev->priv;
+ struct w9966_dev *cam = video_drvdata(file);
unsigned char addr = 0xa0; // ECP, read, CCD-transfer, 00000
unsigned char __user *dest = (unsigned char __user *)buf;
unsigned long dleft = count;
diff --git a/drivers/media/video/zc0301/zc0301_core.c b/drivers/media/video/zc0301/zc0301_core.c
index 0c32877..6a0902b 100644
--- a/drivers/media/video/zc0301/zc0301_core.c
+++ b/drivers/media/video/zc0301/zc0301_core.c
@@ -657,7 +657,7 @@
if (!down_read_trylock(&zc0301_dev_lock))
return -EAGAIN;
- cam = video_get_drvdata(video_devdata(filp));
+ cam = video_drvdata(filp);
if (wait_for_completion_interruptible(&cam->probe)) {
up_read(&zc0301_dev_lock);
@@ -739,7 +739,7 @@
down_write(&zc0301_dev_lock);
- cam = video_get_drvdata(video_devdata(filp));
+ cam = video_drvdata(filp);
zc0301_stop_transfer(cam);
zc0301_release_buffers(cam);
@@ -759,7 +759,7 @@
static ssize_t
zc0301_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
{
- struct zc0301_device* cam = video_get_drvdata(video_devdata(filp));
+ struct zc0301_device *cam = video_drvdata(filp);
struct zc0301_frame_t* f, * i;
unsigned long lock_flags;
long timeout;
@@ -866,7 +866,7 @@
static unsigned int zc0301_poll(struct file *filp, poll_table *wait)
{
- struct zc0301_device* cam = video_get_drvdata(video_devdata(filp));
+ struct zc0301_device *cam = video_drvdata(filp);
struct zc0301_frame_t* f;
unsigned long lock_flags;
unsigned int mask = 0;
@@ -941,7 +941,7 @@
static int zc0301_mmap(struct file* filp, struct vm_area_struct *vma)
{
- struct zc0301_device* cam = video_get_drvdata(video_devdata(filp));
+ struct zc0301_device *cam = video_drvdata(filp);
unsigned long size = vma->vm_end - vma->vm_start,
start = vma->vm_start;
void *pos;
@@ -1796,7 +1796,7 @@
static int zc0301_ioctl_v4l2(struct inode* inode, struct file* filp,
unsigned int cmd, void __user * arg)
{
- struct zc0301_device* cam = video_get_drvdata(video_devdata(filp));
+ struct zc0301_device *cam = video_drvdata(filp);
switch (cmd) {
@@ -1891,7 +1891,7 @@
static int zc0301_ioctl(struct inode* inode, struct file* filp,
unsigned int cmd, unsigned long arg)
{
- struct zc0301_device* cam = video_get_drvdata(video_devdata(filp));
+ struct zc0301_device *cam = video_drvdata(filp);
int err = 0;
if (mutex_lock_interruptible(&cam->fileop_mutex))
diff --git a/drivers/media/video/zoran/Kconfig b/drivers/media/video/zoran/Kconfig
new file mode 100644
index 0000000..4ea5fa7
--- /dev/null
+++ b/drivers/media/video/zoran/Kconfig
@@ -0,0 +1,73 @@
+config VIDEO_ZORAN
+ tristate "Zoran ZR36057/36067 Video For Linux"
+ depends on PCI && I2C_ALGOBIT && VIDEO_V4L1 && VIRT_TO_BUS
+ help
+ Say Y for support for MJPEG capture cards based on the Zoran
+ 36057/36067 PCI controller chipset. This includes the Iomega
+ Buz, Pinnacle DC10+ and the Linux Media Labs LML33. There is
+ a driver homepage at <http://mjpeg.sf.net/driver-zoran/>. For
+ more information, check <file:Documentation/video4linux/Zoran>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called zr36067.
+
+config VIDEO_ZORAN_DC30
+ tristate "Pinnacle/Miro DC30(+) support"
+ depends on VIDEO_ZORAN
+ select VIDEO_ADV7175 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_VPX3220 if VIDEO_HELPER_CHIPS_AUTO
+ help
+ Support for the Pinnacle/Miro DC30(+) MJPEG capture/playback
+ card. This also supports really old DC10 cards based on the
+ zr36050 MJPEG codec and zr36016 VFE.
+
+config VIDEO_ZORAN_ZR36060
+ tristate "Zoran ZR36060"
+ depends on VIDEO_ZORAN
+ help
+ Say Y to support Zoran boards based on 36060 chips.
+ This includes Iomega Buz, Pinnacle DC10, Linux media Labs 33
+ and 33 R10 and AverMedia 6 boards.
+
+config VIDEO_ZORAN_BUZ
+ tristate "Iomega Buz support"
+ depends on VIDEO_ZORAN_ZR36060
+ select VIDEO_SAA7111 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_SAA7185 if VIDEO_HELPER_CHIPS_AUTO
+ help
+ Support for the Iomega Buz MJPEG capture/playback card.
+
+config VIDEO_ZORAN_DC10
+ tristate "Pinnacle/Miro DC10(+) support"
+ depends on VIDEO_ZORAN_ZR36060
+ select VIDEO_SAA7110 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_ADV7175 if VIDEO_HELPER_CHIPS_AUTO
+ help
+ Support for the Pinnacle/Miro DC10(+) MJPEG capture/playback
+ card.
+
+config VIDEO_ZORAN_LML33
+ tristate "Linux Media Labs LML33 support"
+ depends on VIDEO_ZORAN_ZR36060
+ select VIDEO_BT819 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_BT856 if VIDEO_HELPER_CHIPS_AUTO
+ help
+ Support for the Linux Media Labs LML33 MJPEG capture/playback
+ card.
+
+config VIDEO_ZORAN_LML33R10
+ tristate "Linux Media Labs LML33R10 support"
+ depends on VIDEO_ZORAN_ZR36060
+ select VIDEO_SAA7114 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_ADV7170 if VIDEO_HELPER_CHIPS_AUTO
+ help
+ support for the Linux Media Labs LML33R10 MJPEG capture/playback
+ card.
+
+config VIDEO_ZORAN_AVS6EYES
+ tristate "AverMedia 6 Eyes support (EXPERIMENTAL)"
+ depends on VIDEO_ZORAN_ZR36060 && EXPERIMENTAL && VIDEO_V4L1
+ select VIDEO_BT856 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_KS0127 if VIDEO_HELPER_CHIPS_AUTO
+ help
+ Support for the AverMedia 6 Eyes video surveillance card.
diff --git a/drivers/media/video/zoran/Makefile b/drivers/media/video/zoran/Makefile
new file mode 100644
index 0000000..44cc133
--- /dev/null
+++ b/drivers/media/video/zoran/Makefile
@@ -0,0 +1,6 @@
+zr36067-objs := zoran_procfs.o zoran_device.o \
+ zoran_driver.o zoran_card.o
+
+obj-$(CONFIG_VIDEO_ZORAN) += zr36067.o videocodec.o
+obj-$(CONFIG_VIDEO_ZORAN_DC30) += zr36050.o zr36016.o
+obj-$(CONFIG_VIDEO_ZORAN_ZR36060) += zr36060.o
diff --git a/drivers/media/video/videocodec.c b/drivers/media/video/zoran/videocodec.c
similarity index 100%
rename from drivers/media/video/videocodec.c
rename to drivers/media/video/zoran/videocodec.c
diff --git a/drivers/media/video/videocodec.h b/drivers/media/video/zoran/videocodec.h
similarity index 100%
rename from drivers/media/video/videocodec.h
rename to drivers/media/video/zoran/videocodec.h
diff --git a/drivers/media/video/zoran.h b/drivers/media/video/zoran/zoran.h
similarity index 100%
rename from drivers/media/video/zoran.h
rename to drivers/media/video/zoran/zoran.h
diff --git a/drivers/media/video/zoran_card.c b/drivers/media/video/zoran/zoran_card.c
similarity index 100%
rename from drivers/media/video/zoran_card.c
rename to drivers/media/video/zoran/zoran_card.c
diff --git a/drivers/media/video/zoran_card.h b/drivers/media/video/zoran/zoran_card.h
similarity index 100%
rename from drivers/media/video/zoran_card.h
rename to drivers/media/video/zoran/zoran_card.h
diff --git a/drivers/media/video/zoran_device.c b/drivers/media/video/zoran/zoran_device.c
similarity index 99%
rename from drivers/media/video/zoran_device.c
rename to drivers/media/video/zoran/zoran_device.c
index 88d3697..5d948ff 100644
--- a/drivers/media/video/zoran_device.c
+++ b/drivers/media/video/zoran/zoran_device.c
@@ -58,8 +58,6 @@
ZR36057_ISR_GIRQ1 | \
ZR36057_ISR_JPEGRepIRQ )
-extern const struct zoran_format zoran_formats[];
-
static int lml33dpath; /* default = 0
* 1 will use digital path in capture
* mode instead of analog. It can be
@@ -377,7 +375,7 @@
/* horizontal */
VidWinWid = video_width;
- X = (VidWinWid * 64 + tvn->Wa - 1) / tvn->Wa;
+ X = DIV_ROUND_UP(VidWinWid * 64, tvn->Wa);
We = (VidWinWid * 64) / X;
HorDcm = 64 - X;
hcrop1 = 2 * ((tvn->Wa - We) / 4);
@@ -403,7 +401,7 @@
/* Vertical */
DispMode = !(video_height > BUZ_MAX_HEIGHT / 2);
VidWinHt = DispMode ? video_height : video_height / 2;
- Y = (VidWinHt * 64 * 2 + tvn->Ha - 1) / tvn->Ha;
+ Y = DIV_ROUND_UP(VidWinHt * 64 * 2, tvn->Ha);
He = (VidWinHt * 64) / Y;
VerDcm = 64 - Y;
vcrop1 = (tvn->Ha / 2 - He) / 2;
diff --git a/drivers/media/video/zoran_device.h b/drivers/media/video/zoran/zoran_device.h
similarity index 94%
rename from drivers/media/video/zoran_device.h
rename to drivers/media/video/zoran/zoran_device.h
index 37fa86a..74c6c8e 100644
--- a/drivers/media/video/zoran_device.h
+++ b/drivers/media/video/zoran/zoran_device.h
@@ -78,6 +78,14 @@
extern void zoran_init_hardware(struct zoran *zr);
extern void zr36057_restart(struct zoran *zr);
+extern const struct zoran_format zoran_formats[];
+
+extern int v4l_nbufs;
+extern int v4l_bufsize;
+extern int jpg_nbufs;
+extern int jpg_bufsize;
+extern int pass_through;
+
/* i2c */
extern int decoder_command(struct zoran *zr,
int cmd,
diff --git a/drivers/media/video/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c
similarity index 99%
rename from drivers/media/video/zoran_driver.c
rename to drivers/media/video/zoran/zoran_driver.c
index 2dab9ee..25de763 100644
--- a/drivers/media/video/zoran_driver.c
+++ b/drivers/media/video/zoran/zoran_driver.c
@@ -194,12 +194,6 @@
// RJ: Test only - want to test BUZ_USE_HIMEM even when CONFIG_BIGPHYS_AREA is defined
-extern int v4l_nbufs;
-extern int v4l_bufsize;
-extern int jpg_nbufs;
-extern int jpg_bufsize;
-extern int pass_through;
-
static int lock_norm; /* 0 = default 1 = Don't change TV standard (norm) */
module_param(lock_norm, int, 0644);
MODULE_PARM_DESC(lock_norm, "Prevent norm changes (1 = ignore, >1 = fail)");
@@ -1211,6 +1205,7 @@
struct zoran_fh *fh;
int i, res, first_open = 0, have_module_locks = 0;
+ lock_kernel();
/* find the device */
for (i = 0; i < zoran_num; i++) {
if (zoran[i]->video_dev->minor == minor) {
@@ -1321,6 +1316,7 @@
file->private_data = fh;
fh->zr = zr;
zoran_open_init_session(file);
+ unlock_kernel();
return 0;
@@ -1338,6 +1334,7 @@
if (zr) {
/*mutex_unlock(&zr->resource_lock);*/
}
+ unlock_kernel();
return res;
}
@@ -2920,6 +2917,8 @@
fmt->fmt.pix.bytesperline = 0;
fmt->fmt.pix.sizeimage =
fh->jpg_buffers.buffer_size;
+ fmt->fmt.pix.colorspace =
+ V4L2_COLORSPACE_SMPTE170M;
/* we hereby abuse this variable to show that
* we're gonna do mjpeg capture */
@@ -2979,6 +2978,8 @@
fmt->fmt.pix.sizeimage =
fh->v4l_settings.height *
fh->v4l_settings.bytesperline;
+ fmt->fmt.pix.colorspace =
+ fh->v4l_settings.format->colorspace;
if (BUZ_MAX_HEIGHT <
(fh->v4l_settings.height * 2))
fmt->fmt.pix.field =
diff --git a/drivers/media/video/zoran_procfs.c b/drivers/media/video/zoran/zoran_procfs.c
similarity index 100%
rename from drivers/media/video/zoran_procfs.c
rename to drivers/media/video/zoran/zoran_procfs.c
diff --git a/drivers/media/video/zoran_procfs.h b/drivers/media/video/zoran/zoran_procfs.h
similarity index 100%
rename from drivers/media/video/zoran_procfs.h
rename to drivers/media/video/zoran/zoran_procfs.h
diff --git a/drivers/media/video/zr36016.c b/drivers/media/video/zoran/zr36016.c
similarity index 100%
rename from drivers/media/video/zr36016.c
rename to drivers/media/video/zoran/zr36016.c
diff --git a/drivers/media/video/zr36016.h b/drivers/media/video/zoran/zr36016.h
similarity index 100%
rename from drivers/media/video/zr36016.h
rename to drivers/media/video/zoran/zr36016.h
diff --git a/drivers/media/video/zr36050.c b/drivers/media/video/zoran/zr36050.c
similarity index 100%
rename from drivers/media/video/zr36050.c
rename to drivers/media/video/zoran/zr36050.c
diff --git a/drivers/media/video/zr36050.h b/drivers/media/video/zoran/zr36050.h
similarity index 100%
rename from drivers/media/video/zr36050.h
rename to drivers/media/video/zoran/zr36050.h
diff --git a/drivers/media/video/zr36057.h b/drivers/media/video/zoran/zr36057.h
similarity index 100%
rename from drivers/media/video/zr36057.h
rename to drivers/media/video/zoran/zr36057.h
diff --git a/drivers/media/video/zr36060.c b/drivers/media/video/zoran/zr36060.c
similarity index 100%
rename from drivers/media/video/zr36060.c
rename to drivers/media/video/zoran/zr36060.c
diff --git a/drivers/media/video/zr36060.h b/drivers/media/video/zoran/zr36060.h
similarity index 100%
rename from drivers/media/video/zr36060.h
rename to drivers/media/video/zoran/zr36060.h
diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c
index 18d1c4b..7cdac99 100644
--- a/drivers/media/video/zr364xx.c
+++ b/drivers/media/video/zr364xx.c
@@ -52,7 +52,7 @@
/* Debug macro */
-#define DBG(x...) if (debug) info(x)
+#define DBG(x...) if (debug) printk(KERN_INFO KBUILD_MODNAME x)
/* Init methods, need to find nicer names for these
@@ -116,6 +116,7 @@
int height;
int method;
struct mutex lock;
+ int users;
};
@@ -127,7 +128,7 @@
unsigned char *transfer_buffer = kmalloc(size, GFP_KERNEL);
if (!transfer_buffer) {
- info("kmalloc(%d) failed", size);
+ dev_err(&udev->dev, "kmalloc(%d) failed\n", size);
return -ENOMEM;
}
@@ -143,7 +144,8 @@
kfree(transfer_buffer);
if (status < 0)
- info("Failed sending control message, error %d.", status);
+ dev_err(&udev->dev,
+ "Failed sending control message, error %d.\n", status);
return status;
}
@@ -303,11 +305,11 @@
DBG("buffer : %d %d", cam->buffer[0], cam->buffer[1]);
DBG("bulk : n=%d size=%d", n, actual_length);
if (n < 0) {
- info("error reading bulk msg");
+ dev_err(&cam->udev->dev, "error reading bulk msg\n");
return 0;
}
if (actual_length < 0 || actual_length > BUFFER_SIZE) {
- info("wrong number of bytes");
+ dev_err(&cam->udev->dev, "wrong number of bytes\n");
return 0;
}
@@ -641,42 +643,47 @@
DBG("zr364xx_open");
- cam->skip = 2;
+ mutex_lock(&cam->lock);
- err = video_exclusive_open(inode, file);
- if (err < 0)
- return err;
+ if (cam->users) {
+ err = -EBUSY;
+ goto out;
+ }
if (!cam->framebuf) {
cam->framebuf = vmalloc_32(MAX_FRAME_SIZE * FRAMES);
if (!cam->framebuf) {
- info("vmalloc_32 failed!");
- return -ENOMEM;
+ dev_err(&cam->udev->dev, "vmalloc_32 failed!\n");
+ err = -ENOMEM;
+ goto out;
}
}
- mutex_lock(&cam->lock);
for (i = 0; init[cam->method][i].size != -1; i++) {
err =
send_control_msg(udev, 1, init[cam->method][i].value,
0, init[cam->method][i].bytes,
init[cam->method][i].size);
if (err < 0) {
- info("error during open sequence: %d", i);
- mutex_unlock(&cam->lock);
- return err;
+ dev_err(&cam->udev->dev,
+ "error during open sequence: %d\n", i);
+ goto out;
}
}
+ cam->skip = 2;
+ cam->users++;
file->private_data = vdev;
/* Added some delay here, since opening/closing the camera quickly,
* like Ekiga does during its startup, can crash the webcam
*/
mdelay(100);
+ err = 0;
+out:
mutex_unlock(&cam->lock);
- return 0;
+ return err;
}
@@ -697,28 +704,30 @@
udev = cam->udev;
mutex_lock(&cam->lock);
+
+ cam->users--;
+ file->private_data = NULL;
+
for (i = 0; i < 2; i++) {
err =
send_control_msg(udev, 1, init[cam->method][i].value,
0, init[i][cam->method].bytes,
init[cam->method][i].size);
if (err < 0) {
- info("error during release sequence");
- mutex_unlock(&cam->lock);
- return err;
+ dev_err(&udev->dev, "error during release sequence\n");
+ goto out;
}
}
- file->private_data = NULL;
- video_exclusive_release(inode, file);
-
/* Added some delay here, since opening/closing the camera quickly,
* like Ekiga does during its startup, can crash the webcam
*/
mdelay(100);
+ err = 0;
+out:
mutex_unlock(&cam->lock);
- return 0;
+ return err;
}
@@ -801,13 +810,14 @@
DBG("probing...");
- info(DRIVER_DESC " compatible webcam plugged");
- info("model %04x:%04x detected", udev->descriptor.idVendor,
- udev->descriptor.idProduct);
+ dev_info(&intf->dev, DRIVER_DESC " compatible webcam plugged\n");
+ dev_info(&intf->dev, "model %04x:%04x detected\n",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct));
cam = kzalloc(sizeof(struct zr364xx_camera), GFP_KERNEL);
if (cam == NULL) {
- info("cam: out of memory !");
+ dev_err(&udev->dev, "cam: out of memory !\n");
return -ENOMEM;
}
/* save the init method used by this camera */
@@ -815,7 +825,7 @@
cam->vdev = video_device_alloc();
if (cam->vdev == NULL) {
- info("cam->vdev: out of memory !");
+ dev_err(&udev->dev, "cam->vdev: out of memory !\n");
kfree(cam);
return -ENOMEM;
}
@@ -827,7 +837,7 @@
cam->udev = udev;
if ((cam->buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL)) == NULL) {
- info("cam->buffer: out of memory !");
+ dev_info(&udev->dev, "cam->buffer: out of memory !\n");
video_device_release(cam->vdev);
kfree(cam);
return -ENODEV;
@@ -835,17 +845,17 @@
switch (mode) {
case 1:
- info("160x120 mode selected");
+ dev_info(&udev->dev, "160x120 mode selected\n");
cam->width = 160;
cam->height = 120;
break;
case 2:
- info("640x480 mode selected");
+ dev_info(&udev->dev, "640x480 mode selected\n");
cam->width = 640;
cam->height = 480;
break;
default:
- info("320x240 mode selected");
+ dev_info(&udev->dev, "320x240 mode selected\n");
cam->width = 320;
cam->height = 240;
break;
@@ -865,7 +875,7 @@
err = video_register_device(cam->vdev, VFL_TYPE_GRABBER, -1);
if (err) {
- info("video_register_device failed");
+ dev_err(&udev->dev, "video_register_device failed\n");
video_device_release(cam->vdev);
kfree(cam->buffer);
kfree(cam);
@@ -874,7 +884,8 @@
usb_set_intfdata(intf, cam);
- info(DRIVER_DESC " controlling video device %d", cam->vdev->minor);
+ dev_info(&udev->dev, DRIVER_DESC " controlling video device %d\n",
+ cam->vdev->minor);
return 0;
}
@@ -884,7 +895,7 @@
struct zr364xx_camera *cam = usb_get_intfdata(intf);
usb_set_intfdata(intf, NULL);
dev_set_drvdata(&intf->dev, NULL);
- info(DRIVER_DESC " webcam unplugged");
+ dev_info(&intf->dev, DRIVER_DESC " webcam unplugged\n");
if (cam->vdev)
video_unregister_device(cam->vdev);
cam->vdev = NULL;
@@ -913,16 +924,16 @@
int retval;
retval = usb_register(&zr364xx_driver);
if (retval)
- info("usb_register failed!");
+ printk(KERN_ERR KBUILD_MODNAME ": usb_register failed!\n");
else
- info(DRIVER_DESC " module loaded");
+ printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
return retval;
}
static void __exit zr364xx_exit(void)
{
- info(DRIVER_DESC " module unloaded");
+ printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC " module unloaded\n");
usb_deregister(&zr364xx_driver);
}
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index c0b41e8..f2eeb38 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -3,13 +3,14 @@
#
menuconfig MMC
- tristate "MMC/SD card support"
+ tristate "MMC/SD/SDIO card support"
depends on HAS_IOMEM
help
- MMC is the "multi-media card" bus protocol.
+ This selects MultiMediaCard, Secure Digital and Secure
+ Digital I/O support.
- If you want MMC support, you should say Y here and also
- to the specific driver for your MMC interface.
+ If you want MMC/SD/SDIO support, you should say Y here and
+ also to your specific host controller driver.
config MMC_DEBUG
bool "MMC debugging"
diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig
index dd0f398..3f2a912 100644
--- a/drivers/mmc/card/Kconfig
+++ b/drivers/mmc/card/Kconfig
@@ -2,7 +2,7 @@
# MMC/SD card drivers
#
-comment "MMC/SD Card Drivers"
+comment "MMC/SD/SDIO Card Drivers"
config MMC_BLOCK
tristate "MMC block device driver"
@@ -34,7 +34,6 @@
config SDIO_UART
tristate "SDIO UART/GPS class support"
- depends on MMC
help
SDIO function driver for SDIO cards that implements the UART
class, as well as the GPS class which appears like a UART.
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index efacee0..24c97d3 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -58,7 +58,6 @@
struct mmc_queue queue;
unsigned int usage;
- unsigned int block_bits;
unsigned int read_only;
};
@@ -216,8 +215,7 @@
struct mmc_blk_data *md = mq->data;
struct mmc_card *card = md->queue.card;
struct mmc_blk_request brq;
- int ret = 1, data_size, i;
- struct scatterlist *sg;
+ int ret = 1;
mmc_claim_host(card->host);
@@ -233,13 +231,11 @@
if (!mmc_card_blockaddr(card))
brq.cmd.arg <<= 9;
brq.cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
- brq.data.blksz = 1 << md->block_bits;
+ brq.data.blksz = 512;
brq.stop.opcode = MMC_STOP_TRANSMISSION;
brq.stop.arg = 0;
brq.stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
- brq.data.blocks = req->nr_sectors >> (md->block_bits - 9);
- if (brq.data.blocks > card->host->max_blk_count)
- brq.data.blocks = card->host->max_blk_count;
+ brq.data.blocks = req->nr_sectors;
if (brq.data.blocks > 1) {
/* SPI multiblock writes terminate using a special
@@ -271,24 +267,6 @@
mmc_queue_bounce_pre(mq);
- /*
- * Adjust the sg list so it is the same size as the
- * request.
- */
- if (brq.data.blocks !=
- (req->nr_sectors >> (md->block_bits - 9))) {
- data_size = brq.data.blocks * brq.data.blksz;
- for_each_sg(brq.data.sg, sg, brq.data.sg_len, i) {
- data_size -= sg->length;
- if (data_size <= 0) {
- sg->length += data_size;
- i++;
- break;
- }
- }
- brq.data.sg_len = i;
- }
-
mmc_wait_for_req(card->host, &brq.mrq);
mmc_queue_bounce_post(mq);
@@ -373,16 +351,11 @@
if (rq_data_dir(req) != READ) {
if (mmc_card_sd(card)) {
u32 blocks;
- unsigned int bytes;
blocks = mmc_sd_num_wr_blocks(card);
if (blocks != (u32)-1) {
- if (card->csd.write_partial)
- bytes = blocks << md->block_bits;
- else
- bytes = blocks << 9;
spin_lock_irq(&md->lock);
- ret = __blk_end_request(req, 0, bytes);
+ ret = __blk_end_request(req, 0, blocks << 9);
spin_unlock_irq(&md->lock);
}
} else {
@@ -432,13 +405,6 @@
*/
md->read_only = mmc_blk_readonly(card);
- /*
- * Both SD and MMC specifications state (although a bit
- * unclearly in the MMC case) that a block size of 512
- * bytes must always be supported by the card.
- */
- md->block_bits = 9;
-
md->disk = alloc_disk(1 << MMC_SHIFT);
if (md->disk == NULL) {
ret = -ENOMEM;
@@ -476,7 +442,7 @@
sprintf(md->disk->disk_name, "mmcblk%d", devidx);
- blk_queue_hardsect_size(md->queue.queue, 1 << md->block_bits);
+ blk_queue_hardsect_size(md->queue.queue, 512);
if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) {
/*
@@ -514,7 +480,7 @@
mmc_claim_host(card->host);
cmd.opcode = MMC_SET_BLOCKLEN;
- cmd.arg = 1 << md->block_bits;
+ cmd.arg = 512;
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, 5);
mmc_release_host(card->host);
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index 3dee97e..406989e 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -31,7 +31,7 @@
/*
* We only like normal block requests.
*/
- if (!blk_fs_request(req) && !blk_pc_request(req)) {
+ if (!blk_fs_request(req)) {
blk_dump_rq_flags(req, "MMC bad request");
return BLKPREP_KILL;
}
@@ -131,6 +131,7 @@
mq->req = NULL;
blk_queue_prep_rq(mq->queue, mmc_prep_request);
+ blk_queue_ordered(mq->queue, QUEUE_ORDERED_DRAIN, NULL);
#ifdef CONFIG_MMC_BLOCK_BOUNCE
if (host->max_hw_segs == 1) {
@@ -142,12 +143,19 @@
bouncesz = host->max_req_size;
if (bouncesz > host->max_seg_size)
bouncesz = host->max_seg_size;
+ if (bouncesz > (host->max_blk_count * 512))
+ bouncesz = host->max_blk_count * 512;
- mq->bounce_buf = kmalloc(bouncesz, GFP_KERNEL);
- if (!mq->bounce_buf) {
- printk(KERN_WARNING "%s: unable to allocate "
- "bounce buffer\n", mmc_card_name(card));
- } else {
+ if (bouncesz > 512) {
+ mq->bounce_buf = kmalloc(bouncesz, GFP_KERNEL);
+ if (!mq->bounce_buf) {
+ printk(KERN_WARNING "%s: unable to "
+ "allocate bounce buffer\n",
+ mmc_card_name(card));
+ }
+ }
+
+ if (mq->bounce_buf) {
blk_queue_bounce_limit(mq->queue, BLK_BOUNCE_ANY);
blk_queue_max_sectors(mq->queue, bouncesz / 512);
blk_queue_max_phys_segments(mq->queue, bouncesz / 512);
@@ -175,7 +183,8 @@
if (!mq->bounce_buf) {
blk_queue_bounce_limit(mq->queue, limit);
- blk_queue_max_sectors(mq->queue, host->max_req_size / 512);
+ blk_queue_max_sectors(mq->queue,
+ min(host->max_blk_count, host->max_req_size / 512));
blk_queue_max_phys_segments(mq->queue, host->max_phys_segs);
blk_queue_max_hw_segments(mq->queue, host->max_hw_segs);
blk_queue_max_segment_size(mq->queue, host->max_seg_size);
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 64b05c6..9c50e6f 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -248,8 +248,12 @@
sg_init_one(&sg, data_buf, len);
- if (card)
- mmc_set_data_timeout(&data, card);
+ /*
+ * The spec states that CSR and CID accesses have a timeout
+ * of 64 clock cycles.
+ */
+ data.timeout_ns = 0;
+ data.timeout_clks = 64;
mmc_wait_for_req(host, &mrq);
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index 4eab79e..fb99ccf 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -165,6 +165,36 @@
}
/*
+ * Test if the card supports high-speed mode and, if so, switch to it.
+ */
+static int sdio_enable_hs(struct mmc_card *card)
+{
+ int ret;
+ u8 speed;
+
+ if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED))
+ return 0;
+
+ if (!card->cccr.high_speed)
+ return 0;
+
+ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &speed);
+ if (ret)
+ return ret;
+
+ speed |= SDIO_SPEED_EHS;
+
+ ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_SPEED, speed, NULL);
+ if (ret)
+ return ret;
+
+ mmc_card_set_highspeed(card);
+ mmc_set_timing(card->host, MMC_TIMING_SD_HS);
+
+ return 0;
+}
+
+/*
* Host is being removed. Free up the current card.
*/
static void mmc_sdio_remove(struct mmc_host *host)
@@ -333,10 +363,26 @@
goto remove;
/*
- * No support for high-speed yet, so just set
- * the card's maximum speed.
+ * Switch to high-speed (if supported).
*/
- mmc_set_clock(host, card->cis.max_dtr);
+ err = sdio_enable_hs(card);
+ if (err)
+ goto remove;
+
+ /*
+ * Change to the card's maximum speed.
+ */
+ if (mmc_card_highspeed(card)) {
+ /*
+ * The SDIO specification doesn't mention how
+ * the CIS transfer speed register relates to
+ * high-speed, but it seems that 50 MHz is
+ * mandatory.
+ */
+ mmc_set_clock(host, 50000000);
+ } else {
+ mmc_set_clock(host, card->cis.max_dtr);
+ }
/*
* Switch to wider bus (if supported).
diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c
index c292e12..bb192f9 100644
--- a/drivers/mmc/core/sdio_irq.c
+++ b/drivers/mmc/core/sdio_irq.c
@@ -5,6 +5,8 @@
* Created: June 18, 2007
* Copyright: MontaVista Software Inc.
*
+ * Copyright 2008 Pierre Ossman
+ *
* 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
@@ -107,11 +109,14 @@
/*
* Give other threads a chance to run in the presence of
- * errors. FIXME: determine if due to card removal and
- * possibly exit this thread if so.
+ * errors.
*/
- if (ret < 0)
- ssleep(1);
+ if (ret < 0) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (!kthread_should_stop())
+ schedule_timeout(HZ);
+ set_current_state(TASK_RUNNING);
+ }
/*
* Adaptive polling frequency based on the assumption
@@ -154,7 +159,8 @@
if (!host->sdio_irqs++) {
atomic_set(&host->sdio_irq_thread_abort, 0);
host->sdio_irq_thread =
- kthread_run(sdio_irq_thread, host, "ksdiorqd");
+ kthread_run(sdio_irq_thread, host, "ksdioirqd/%s",
+ mmc_hostname(host));
if (IS_ERR(host->sdio_irq_thread)) {
int err = PTR_ERR(host->sdio_irq_thread);
host->sdio_irqs--;
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index ea8d7a3..dfa585f 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -2,7 +2,7 @@
# MMC/SD host controller drivers
#
-comment "MMC/SD Host Controller Drivers"
+comment "MMC/SD/SDIO Host Controller Drivers"
config MMC_ARMMMCI
tristate "ARM AMBA Multimedia Card Interface support"
@@ -114,6 +114,17 @@
If unsure, say N.
+config MMC_ATMELMCI_DMA
+ bool "Atmel MCI DMA support (EXPERIMENTAL)"
+ depends on MMC_ATMELMCI && DMA_ENGINE && EXPERIMENTAL
+ help
+ Say Y here to have the Atmel MCI driver use a DMA engine to
+ do data transfers and thus increase the throughput and
+ reduce the CPU utilization. Note that this is highly
+ experimental and may cause the driver to lock up.
+
+ If unsure, say N.
+
config MMC_IMX
tristate "Motorola i.MX Multimedia Card Interface support"
depends on ARCH_IMX
@@ -141,21 +152,22 @@
module will be called tifm_sd.
config MMC_SPI
- tristate "MMC/SD over SPI"
- depends on MMC && SPI_MASTER && !HIGHMEM && HAS_DMA
+ tristate "MMC/SD/SDIO over SPI"
+ depends on SPI_MASTER && !HIGHMEM && HAS_DMA
select CRC7
select CRC_ITU_T
help
- Some systems accss MMC/SD cards using a SPI controller instead of
- using a "native" MMC/SD controller. This has a disadvantage of
- being relatively high overhead, but a compensating advantage of
- working on many systems without dedicated MMC/SD controllers.
+ Some systems accss MMC/SD/SDIO cards using a SPI controller
+ instead of using a "native" MMC/SD/SDIO controller. This has a
+ disadvantage of being relatively high overhead, but a compensating
+ advantage of working on many systems without dedicated MMC/SD/SDIO
+ controllers.
If unsure, or if your system has no SPI master driver, say N.
config MMC_S3C
tristate "Samsung S3C SD/MMC Card Interface support"
- depends on ARCH_S3C2410 && MMC
+ depends on ARCH_S3C2410
help
This selects a driver for the MCI interface found in
Samsung's S3C2410, S3C2412, S3C2440, S3C2442 CPUs.
@@ -166,7 +178,7 @@
config MMC_SDRICOH_CS
tristate "MMC/SD driver for Ricoh Bay1Controllers (EXPERIMENTAL)"
- depends on EXPERIMENTAL && MMC && PCI && PCMCIA
+ depends on EXPERIMENTAL && PCI && PCMCIA
help
Say Y here if your Notebook reports a Ricoh Bay1Controller PCMCIA
card whenever you insert a MMC or SD card into the card slot.
diff --git a/drivers/mmc/host/atmel-mci-regs.h b/drivers/mmc/host/atmel-mci-regs.h
index 26bd80e..b58364e 100644
--- a/drivers/mmc/host/atmel-mci-regs.h
+++ b/drivers/mmc/host/atmel-mci-regs.h
@@ -25,8 +25,10 @@
#define MCI_SDCR 0x000c /* SD Card / SDIO */
# define MCI_SDCSEL_SLOT_A ( 0 << 0) /* Select SD slot A */
# define MCI_SDCSEL_SLOT_B ( 1 << 0) /* Select SD slot A */
-# define MCI_SDCBUS_1BIT ( 0 << 7) /* 1-bit data bus */
-# define MCI_SDCBUS_4BIT ( 1 << 7) /* 4-bit data bus */
+# define MCI_SDCSEL_MASK ( 3 << 0)
+# define MCI_SDCBUS_1BIT ( 0 << 6) /* 1-bit data bus */
+# define MCI_SDCBUS_4BIT ( 2 << 6) /* 4-bit data bus */
+# define MCI_SDCBUS_MASK ( 3 << 6)
#define MCI_ARGR 0x0010 /* Command Argument */
#define MCI_CMDR 0x0014 /* Command */
# define MCI_CMDR_CMDNB(x) ((x) << 0) /* Command Opcode */
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index 0000896..7a3f243 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -11,6 +11,8 @@
#include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/device.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/init.h>
@@ -33,64 +35,178 @@
#include "atmel-mci-regs.h"
#define ATMCI_DATA_ERROR_FLAGS (MCI_DCRCE | MCI_DTOE | MCI_OVRE | MCI_UNRE)
+#define ATMCI_DMA_THRESHOLD 16
enum {
EVENT_CMD_COMPLETE = 0,
- EVENT_DATA_ERROR,
- EVENT_DATA_COMPLETE,
- EVENT_STOP_SENT,
- EVENT_STOP_COMPLETE,
EVENT_XFER_COMPLETE,
+ EVENT_DATA_COMPLETE,
+ EVENT_DATA_ERROR,
};
+enum atmel_mci_state {
+ STATE_IDLE = 0,
+ STATE_SENDING_CMD,
+ STATE_SENDING_DATA,
+ STATE_DATA_BUSY,
+ STATE_SENDING_STOP,
+ STATE_DATA_ERROR,
+};
+
+struct atmel_mci_dma {
+#ifdef CONFIG_MMC_ATMELMCI_DMA
+ struct dma_client client;
+ struct dma_chan *chan;
+ struct dma_async_tx_descriptor *data_desc;
+#endif
+};
+
+/**
+ * struct atmel_mci - MMC controller state shared between all slots
+ * @lock: Spinlock protecting the queue and associated data.
+ * @regs: Pointer to MMIO registers.
+ * @sg: Scatterlist entry currently being processed by PIO code, if any.
+ * @pio_offset: Offset into the current scatterlist entry.
+ * @cur_slot: The slot which is currently using the controller.
+ * @mrq: The request currently being processed on @cur_slot,
+ * or NULL if the controller is idle.
+ * @cmd: The command currently being sent to the card, or NULL.
+ * @data: The data currently being transferred, or NULL if no data
+ * transfer is in progress.
+ * @dma: DMA client state.
+ * @data_chan: DMA channel being used for the current data transfer.
+ * @cmd_status: Snapshot of SR taken upon completion of the current
+ * command. Only valid when EVENT_CMD_COMPLETE is pending.
+ * @data_status: Snapshot of SR taken upon completion of the current
+ * data transfer. Only valid when EVENT_DATA_COMPLETE or
+ * EVENT_DATA_ERROR is pending.
+ * @stop_cmdr: Value to be loaded into CMDR when the stop command is
+ * to be sent.
+ * @tasklet: Tasklet running the request state machine.
+ * @pending_events: Bitmask of events flagged by the interrupt handler
+ * to be processed by the tasklet.
+ * @completed_events: Bitmask of events which the state machine has
+ * processed.
+ * @state: Tasklet state.
+ * @queue: List of slots waiting for access to the controller.
+ * @need_clock_update: Update the clock rate before the next request.
+ * @need_reset: Reset controller before next request.
+ * @mode_reg: Value of the MR register.
+ * @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus
+ * rate and timeout calculations.
+ * @mapbase: Physical address of the MMIO registers.
+ * @mck: The peripheral bus clock hooked up to the MMC controller.
+ * @pdev: Platform device associated with the MMC controller.
+ * @slot: Slots sharing this MMC controller.
+ *
+ * Locking
+ * =======
+ *
+ * @lock is a softirq-safe spinlock protecting @queue as well as
+ * @cur_slot, @mrq and @state. These must always be updated
+ * at the same time while holding @lock.
+ *
+ * @lock also protects mode_reg and need_clock_update since these are
+ * used to synchronize mode register updates with the queue
+ * processing.
+ *
+ * The @mrq field of struct atmel_mci_slot is also protected by @lock,
+ * and must always be written at the same time as the slot is added to
+ * @queue.
+ *
+ * @pending_events and @completed_events are accessed using atomic bit
+ * operations, so they don't need any locking.
+ *
+ * None of the fields touched by the interrupt handler need any
+ * locking. However, ordering is important: Before EVENT_DATA_ERROR or
+ * EVENT_DATA_COMPLETE is set in @pending_events, all data-related
+ * interrupts must be disabled and @data_status updated with a
+ * snapshot of SR. Similarly, before EVENT_CMD_COMPLETE is set, the
+ * CMDRDY interupt must be disabled and @cmd_status updated with a
+ * snapshot of SR, and before EVENT_XFER_COMPLETE can be set, the
+ * bytes_xfered field of @data must be written. This is ensured by
+ * using barriers.
+ */
struct atmel_mci {
- struct mmc_host *mmc;
+ spinlock_t lock;
void __iomem *regs;
struct scatterlist *sg;
unsigned int pio_offset;
+ struct atmel_mci_slot *cur_slot;
struct mmc_request *mrq;
struct mmc_command *cmd;
struct mmc_data *data;
+ struct atmel_mci_dma dma;
+ struct dma_chan *data_chan;
+
u32 cmd_status;
u32 data_status;
- u32 stop_status;
u32 stop_cmdr;
- u32 mode_reg;
- u32 sdc_reg;
-
struct tasklet_struct tasklet;
unsigned long pending_events;
unsigned long completed_events;
+ enum atmel_mci_state state;
+ struct list_head queue;
- int present;
- int detect_pin;
- int wp_pin;
-
- /* For detect pin debouncing */
- struct timer_list detect_timer;
-
+ bool need_clock_update;
+ bool need_reset;
+ u32 mode_reg;
unsigned long bus_hz;
unsigned long mapbase;
struct clk *mck;
struct platform_device *pdev;
+
+ struct atmel_mci_slot *slot[ATMEL_MCI_MAX_NR_SLOTS];
};
-#define atmci_is_completed(host, event) \
- test_bit(event, &host->completed_events)
+/**
+ * struct atmel_mci_slot - MMC slot state
+ * @mmc: The mmc_host representing this slot.
+ * @host: The MMC controller this slot is using.
+ * @sdc_reg: Value of SDCR to be written before using this slot.
+ * @mrq: mmc_request currently being processed or waiting to be
+ * processed, or NULL when the slot is idle.
+ * @queue_node: List node for placing this node in the @queue list of
+ * &struct atmel_mci.
+ * @clock: Clock rate configured by set_ios(). Protected by host->lock.
+ * @flags: Random state bits associated with the slot.
+ * @detect_pin: GPIO pin used for card detection, or negative if not
+ * available.
+ * @wp_pin: GPIO pin used for card write protect sending, or negative
+ * if not available.
+ * @detect_timer: Timer used for debouncing @detect_pin interrupts.
+ */
+struct atmel_mci_slot {
+ struct mmc_host *mmc;
+ struct atmel_mci *host;
+
+ u32 sdc_reg;
+
+ struct mmc_request *mrq;
+ struct list_head queue_node;
+
+ unsigned int clock;
+ unsigned long flags;
+#define ATMCI_CARD_PRESENT 0
+#define ATMCI_CARD_NEED_INIT 1
+#define ATMCI_SHUTDOWN 2
+
+ int detect_pin;
+ int wp_pin;
+
+ struct timer_list detect_timer;
+};
+
#define atmci_test_and_clear_pending(host, event) \
test_and_clear_bit(event, &host->pending_events)
-#define atmci_test_and_set_completed(host, event) \
- test_and_set_bit(event, &host->completed_events)
#define atmci_set_completed(host, event) \
set_bit(event, &host->completed_events)
#define atmci_set_pending(host, event) \
set_bit(event, &host->pending_events)
-#define atmci_clear_pending(host, event) \
- clear_bit(event, &host->pending_events)
/*
* The debugfs stuff below is mostly optimized away when
@@ -98,14 +214,15 @@
*/
static int atmci_req_show(struct seq_file *s, void *v)
{
- struct atmel_mci *host = s->private;
- struct mmc_request *mrq = host->mrq;
+ struct atmel_mci_slot *slot = s->private;
+ struct mmc_request *mrq;
struct mmc_command *cmd;
struct mmc_command *stop;
struct mmc_data *data;
/* Make sure we get a consistent snapshot */
- spin_lock_irq(&host->mmc->lock);
+ spin_lock_bh(&slot->host->lock);
+ mrq = slot->mrq;
if (mrq) {
cmd = mrq->cmd;
@@ -130,7 +247,7 @@
stop->resp[2], stop->error);
}
- spin_unlock_irq(&host->mmc->lock);
+ spin_unlock_bh(&slot->host->lock);
return 0;
}
@@ -193,12 +310,16 @@
if (!buf)
return -ENOMEM;
- /* Grab a more or less consistent snapshot */
- spin_lock_irq(&host->mmc->lock);
+ /*
+ * Grab a more or less consistent snapshot. Note that we're
+ * not disabling interrupts, so IMR and SR may not be
+ * consistent.
+ */
+ spin_lock_bh(&host->lock);
clk_enable(host->mck);
memcpy_fromio(buf, host->regs, MCI_REGS_SIZE);
clk_disable(host->mck);
- spin_unlock_irq(&host->mmc->lock);
+ spin_unlock_bh(&host->lock);
seq_printf(s, "MR:\t0x%08x%s%s CLKDIV=%u\n",
buf[MCI_MR / 4],
@@ -236,13 +357,13 @@
.release = single_release,
};
-static void atmci_init_debugfs(struct atmel_mci *host)
+static void atmci_init_debugfs(struct atmel_mci_slot *slot)
{
- struct mmc_host *mmc;
- struct dentry *root;
- struct dentry *node;
+ struct mmc_host *mmc = slot->mmc;
+ struct atmel_mci *host = slot->host;
+ struct dentry *root;
+ struct dentry *node;
- mmc = host->mmc;
root = mmc->debugfs_root;
if (!root)
return;
@@ -254,7 +375,11 @@
if (!node)
goto err;
- node = debugfs_create_file("req", S_IRUSR, root, host, &atmci_req_fops);
+ node = debugfs_create_file("req", S_IRUSR, root, slot, &atmci_req_fops);
+ if (!node)
+ goto err;
+
+ node = debugfs_create_u32("state", S_IRUSR, root, (u32 *)&host->state);
if (!node)
goto err;
@@ -271,25 +396,7 @@
return;
err:
- dev_err(&host->pdev->dev,
- "failed to initialize debugfs for controller\n");
-}
-
-static void atmci_enable(struct atmel_mci *host)
-{
- clk_enable(host->mck);
- mci_writel(host, CR, MCI_CR_MCIEN);
- mci_writel(host, MR, host->mode_reg);
- mci_writel(host, SDCR, host->sdc_reg);
-}
-
-static void atmci_disable(struct atmel_mci *host)
-{
- mci_writel(host, CR, MCI_CR_SWRST);
-
- /* Stall until write is complete, then disable the bus clock */
- mci_readl(host, SR);
- clk_disable(host->mck);
+ dev_err(&mmc->class_dev, "failed to initialize debugfs for slot\n");
}
static inline unsigned int ns_to_clocks(struct atmel_mci *host,
@@ -299,7 +406,7 @@
}
static void atmci_set_timeout(struct atmel_mci *host,
- struct mmc_data *data)
+ struct atmel_mci_slot *slot, struct mmc_data *data)
{
static unsigned dtomul_to_shift[] = {
0, 4, 7, 8, 10, 12, 16, 20
@@ -322,7 +429,7 @@
dtocyc = 15;
}
- dev_vdbg(&host->mmc->class_dev, "setting timeout to %u cycles\n",
+ dev_vdbg(&slot->mmc->class_dev, "setting timeout to %u cycles\n",
dtocyc << dtomul_to_shift[dtomul]);
mci_writel(host, DTOR, (MCI_DTOMUL(dtomul) | MCI_DTOCYC(dtocyc)));
}
@@ -375,15 +482,12 @@
}
static void atmci_start_command(struct atmel_mci *host,
- struct mmc_command *cmd,
- u32 cmd_flags)
+ struct mmc_command *cmd, u32 cmd_flags)
{
- /* Must read host->cmd after testing event flags */
- smp_rmb();
WARN_ON(host->cmd);
host->cmd = cmd;
- dev_vdbg(&host->mmc->class_dev,
+ dev_vdbg(&host->pdev->dev,
"start command: ARGR=0x%08x CMDR=0x%08x\n",
cmd->arg, cmd_flags);
@@ -391,34 +495,157 @@
mci_writel(host, CMDR, cmd_flags);
}
-static void send_stop_cmd(struct mmc_host *mmc, struct mmc_data *data)
+static void send_stop_cmd(struct atmel_mci *host, struct mmc_data *data)
{
- struct atmel_mci *host = mmc_priv(mmc);
-
atmci_start_command(host, data->stop, host->stop_cmdr);
mci_writel(host, IER, MCI_CMDRDY);
}
-static void atmci_request_end(struct mmc_host *mmc, struct mmc_request *mrq)
+#ifdef CONFIG_MMC_ATMELMCI_DMA
+static void atmci_dma_cleanup(struct atmel_mci *host)
{
- struct atmel_mci *host = mmc_priv(mmc);
+ struct mmc_data *data = host->data;
- WARN_ON(host->cmd || host->data);
- host->mrq = NULL;
-
- atmci_disable(host);
-
- mmc_request_done(mmc, mrq);
+ dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len,
+ ((data->flags & MMC_DATA_WRITE)
+ ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
}
+static void atmci_stop_dma(struct atmel_mci *host)
+{
+ struct dma_chan *chan = host->data_chan;
+
+ if (chan) {
+ chan->device->device_terminate_all(chan);
+ atmci_dma_cleanup(host);
+ } else {
+ /* Data transfer was stopped by the interrupt handler */
+ atmci_set_pending(host, EVENT_XFER_COMPLETE);
+ mci_writel(host, IER, MCI_NOTBUSY);
+ }
+}
+
+/* This function is called by the DMA driver from tasklet context. */
+static void atmci_dma_complete(void *arg)
+{
+ struct atmel_mci *host = arg;
+ struct mmc_data *data = host->data;
+
+ dev_vdbg(&host->pdev->dev, "DMA complete\n");
+
+ atmci_dma_cleanup(host);
+
+ /*
+ * If the card was removed, data will be NULL. No point trying
+ * to send the stop command or waiting for NBUSY in this case.
+ */
+ if (data) {
+ atmci_set_pending(host, EVENT_XFER_COMPLETE);
+ tasklet_schedule(&host->tasklet);
+
+ /*
+ * Regardless of what the documentation says, we have
+ * to wait for NOTBUSY even after block read
+ * operations.
+ *
+ * When the DMA transfer is complete, the controller
+ * may still be reading the CRC from the card, i.e.
+ * the data transfer is still in progress and we
+ * haven't seen all the potential error bits yet.
+ *
+ * The interrupt handler will schedule a different
+ * tasklet to finish things up when the data transfer
+ * is completely done.
+ *
+ * We may not complete the mmc request here anyway
+ * because the mmc layer may call back and cause us to
+ * violate the "don't submit new operations from the
+ * completion callback" rule of the dma engine
+ * framework.
+ */
+ mci_writel(host, IER, MCI_NOTBUSY);
+ }
+}
+
+static int
+atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data)
+{
+ struct dma_chan *chan;
+ struct dma_async_tx_descriptor *desc;
+ struct scatterlist *sg;
+ unsigned int i;
+ enum dma_data_direction direction;
+
+ /*
+ * We don't do DMA on "complex" transfers, i.e. with
+ * non-word-aligned buffers or lengths. Also, we don't bother
+ * with all the DMA setup overhead for short transfers.
+ */
+ if (data->blocks * data->blksz < ATMCI_DMA_THRESHOLD)
+ return -EINVAL;
+ if (data->blksz & 3)
+ return -EINVAL;
+
+ for_each_sg(data->sg, sg, data->sg_len, i) {
+ if (sg->offset & 3 || sg->length & 3)
+ return -EINVAL;
+ }
+
+ /* If we don't have a channel, we can't do DMA */
+ chan = host->dma.chan;
+ if (chan) {
+ dma_chan_get(chan);
+ host->data_chan = chan;
+ }
+
+ if (!chan)
+ return -ENODEV;
+
+ if (data->flags & MMC_DATA_READ)
+ direction = DMA_FROM_DEVICE;
+ else
+ direction = DMA_TO_DEVICE;
+
+ desc = chan->device->device_prep_slave_sg(chan,
+ data->sg, data->sg_len, direction,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc)
+ return -ENOMEM;
+
+ host->dma.data_desc = desc;
+ desc->callback = atmci_dma_complete;
+ desc->callback_param = host;
+ desc->tx_submit(desc);
+
+ /* Go! */
+ chan->device->device_issue_pending(chan);
+
+ return 0;
+}
+
+#else /* CONFIG_MMC_ATMELMCI_DMA */
+
+static int atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data)
+{
+ return -ENOSYS;
+}
+
+static void atmci_stop_dma(struct atmel_mci *host)
+{
+ /* Data transfer was stopped by the interrupt handler */
+ atmci_set_pending(host, EVENT_XFER_COMPLETE);
+ mci_writel(host, IER, MCI_NOTBUSY);
+}
+
+#endif /* CONFIG_MMC_ATMELMCI_DMA */
+
/*
* Returns a mask of interrupt flags to be enabled after the whole
* request has been prepared.
*/
-static u32 atmci_submit_data(struct mmc_host *mmc, struct mmc_data *data)
+static u32 atmci_submit_data(struct atmel_mci *host, struct mmc_data *data)
{
- struct atmel_mci *host = mmc_priv(mmc);
- u32 iflags;
+ u32 iflags;
data->error = -EINPROGRESS;
@@ -426,77 +653,89 @@
host->sg = NULL;
host->data = data;
- dev_vdbg(&mmc->class_dev, "BLKR=0x%08x\n",
- MCI_BCNT(data->blocks) | MCI_BLKLEN(data->blksz));
-
iflags = ATMCI_DATA_ERROR_FLAGS;
- host->sg = data->sg;
- host->pio_offset = 0;
- if (data->flags & MMC_DATA_READ)
- iflags |= MCI_RXRDY;
- else
- iflags |= MCI_TXRDY;
+ if (atmci_submit_data_dma(host, data)) {
+ host->data_chan = NULL;
+
+ /*
+ * Errata: MMC data write operation with less than 12
+ * bytes is impossible.
+ *
+ * Errata: MCI Transmit Data Register (TDR) FIFO
+ * corruption when length is not multiple of 4.
+ */
+ if (data->blocks * data->blksz < 12
+ || (data->blocks * data->blksz) & 3)
+ host->need_reset = true;
+
+ host->sg = data->sg;
+ host->pio_offset = 0;
+ if (data->flags & MMC_DATA_READ)
+ iflags |= MCI_RXRDY;
+ else
+ iflags |= MCI_TXRDY;
+ }
return iflags;
}
-static void atmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+static void atmci_start_request(struct atmel_mci *host,
+ struct atmel_mci_slot *slot)
{
- struct atmel_mci *host = mmc_priv(mmc);
- struct mmc_data *data;
+ struct mmc_request *mrq;
struct mmc_command *cmd;
+ struct mmc_data *data;
u32 iflags;
- u32 cmdflags = 0;
+ u32 cmdflags;
+
+ mrq = slot->mrq;
+ host->cur_slot = slot;
+ host->mrq = mrq;
+
+ host->pending_events = 0;
+ host->completed_events = 0;
+ host->data_status = 0;
+
+ if (host->need_reset) {
+ mci_writel(host, CR, MCI_CR_SWRST);
+ mci_writel(host, CR, MCI_CR_MCIEN);
+ mci_writel(host, MR, host->mode_reg);
+ host->need_reset = false;
+ }
+ mci_writel(host, SDCR, slot->sdc_reg);
iflags = mci_readl(host, IMR);
if (iflags)
- dev_warn(&mmc->class_dev, "WARNING: IMR=0x%08x\n",
- mci_readl(host, IMR));
+ dev_warn(&slot->mmc->class_dev, "WARNING: IMR=0x%08x\n",
+ iflags);
- WARN_ON(host->mrq != NULL);
-
- /*
- * We may "know" the card is gone even though there's still an
- * electrical connection. If so, we really need to communicate
- * this to the MMC core since there won't be any more
- * interrupts as the card is completely removed. Otherwise,
- * the MMC core might believe the card is still there even
- * though the card was just removed very slowly.
- */
- if (!host->present) {
- mrq->cmd->error = -ENOMEDIUM;
- mmc_request_done(mmc, mrq);
- return;
+ if (unlikely(test_and_clear_bit(ATMCI_CARD_NEED_INIT, &slot->flags))) {
+ /* Send init sequence (74 clock cycles) */
+ mci_writel(host, CMDR, MCI_CMDR_SPCMD_INIT);
+ while (!(mci_readl(host, SR) & MCI_CMDRDY))
+ cpu_relax();
}
-
- host->mrq = mrq;
- host->pending_events = 0;
- host->completed_events = 0;
-
- atmci_enable(host);
-
- /* We don't support multiple blocks of weird lengths. */
data = mrq->data;
if (data) {
- if (data->blocks > 1 && data->blksz & 3)
- goto fail;
- atmci_set_timeout(host, data);
+ atmci_set_timeout(host, slot, data);
/* Must set block count/size before sending command */
mci_writel(host, BLKR, MCI_BCNT(data->blocks)
| MCI_BLKLEN(data->blksz));
+ dev_vdbg(&slot->mmc->class_dev, "BLKR=0x%08x\n",
+ MCI_BCNT(data->blocks) | MCI_BLKLEN(data->blksz));
}
iflags = MCI_CMDRDY;
cmd = mrq->cmd;
- cmdflags = atmci_prepare_command(mmc, cmd);
+ cmdflags = atmci_prepare_command(slot->mmc, cmd);
atmci_start_command(host, cmd, cmdflags);
if (data)
- iflags |= atmci_submit_data(mmc, data);
+ iflags |= atmci_submit_data(host, data);
if (mrq->stop) {
- host->stop_cmdr = atmci_prepare_command(mmc, mrq->stop);
+ host->stop_cmdr = atmci_prepare_command(slot->mmc, mrq->stop);
host->stop_cmdr |= MCI_CMDR_STOP_XFER;
if (!(data->flags & MMC_DATA_WRITE))
host->stop_cmdr |= MCI_CMDR_TRDIR_READ;
@@ -513,59 +752,156 @@
* prepared yet.)
*/
mci_writel(host, IER, iflags);
+}
- return;
+static void atmci_queue_request(struct atmel_mci *host,
+ struct atmel_mci_slot *slot, struct mmc_request *mrq)
+{
+ dev_vdbg(&slot->mmc->class_dev, "queue request: state=%d\n",
+ host->state);
-fail:
- atmci_disable(host);
- host->mrq = NULL;
- mrq->cmd->error = -EINVAL;
- mmc_request_done(mmc, mrq);
+ spin_lock_bh(&host->lock);
+ slot->mrq = mrq;
+ if (host->state == STATE_IDLE) {
+ host->state = STATE_SENDING_CMD;
+ atmci_start_request(host, slot);
+ } else {
+ list_add_tail(&slot->queue_node, &host->queue);
+ }
+ spin_unlock_bh(&host->lock);
+}
+
+static void atmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct atmel_mci_slot *slot = mmc_priv(mmc);
+ struct atmel_mci *host = slot->host;
+ struct mmc_data *data;
+
+ WARN_ON(slot->mrq);
+
+ /*
+ * We may "know" the card is gone even though there's still an
+ * electrical connection. If so, we really need to communicate
+ * this to the MMC core since there won't be any more
+ * interrupts as the card is completely removed. Otherwise,
+ * the MMC core might believe the card is still there even
+ * though the card was just removed very slowly.
+ */
+ if (!test_bit(ATMCI_CARD_PRESENT, &slot->flags)) {
+ mrq->cmd->error = -ENOMEDIUM;
+ mmc_request_done(mmc, mrq);
+ return;
+ }
+
+ /* We don't support multiple blocks of weird lengths. */
+ data = mrq->data;
+ if (data && data->blocks > 1 && data->blksz & 3) {
+ mrq->cmd->error = -EINVAL;
+ mmc_request_done(mmc, mrq);
+ }
+
+ atmci_queue_request(host, slot, mrq);
}
static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
- struct atmel_mci *host = mmc_priv(mmc);
+ struct atmel_mci_slot *slot = mmc_priv(mmc);
+ struct atmel_mci *host = slot->host;
+ unsigned int i;
+
+ slot->sdc_reg &= ~MCI_SDCBUS_MASK;
+ switch (ios->bus_width) {
+ case MMC_BUS_WIDTH_1:
+ slot->sdc_reg |= MCI_SDCBUS_1BIT;
+ break;
+ case MMC_BUS_WIDTH_4:
+ slot->sdc_reg = MCI_SDCBUS_4BIT;
+ break;
+ }
if (ios->clock) {
+ unsigned int clock_min = ~0U;
u32 clkdiv;
- /* Set clock rate */
- clkdiv = DIV_ROUND_UP(host->bus_hz, 2 * ios->clock) - 1;
+ spin_lock_bh(&host->lock);
+ if (!host->mode_reg) {
+ clk_enable(host->mck);
+ mci_writel(host, CR, MCI_CR_SWRST);
+ mci_writel(host, CR, MCI_CR_MCIEN);
+ }
+
+ /*
+ * Use mirror of ios->clock to prevent race with mmc
+ * core ios update when finding the minimum.
+ */
+ slot->clock = ios->clock;
+ for (i = 0; i < ATMEL_MCI_MAX_NR_SLOTS; i++) {
+ if (host->slot[i] && host->slot[i]->clock
+ && host->slot[i]->clock < clock_min)
+ clock_min = host->slot[i]->clock;
+ }
+
+ /* Calculate clock divider */
+ clkdiv = DIV_ROUND_UP(host->bus_hz, 2 * clock_min) - 1;
if (clkdiv > 255) {
dev_warn(&mmc->class_dev,
"clock %u too slow; using %lu\n",
- ios->clock, host->bus_hz / (2 * 256));
+ clock_min, host->bus_hz / (2 * 256));
clkdiv = 255;
}
+ /*
+ * WRPROOF and RDPROOF prevent overruns/underruns by
+ * stopping the clock when the FIFO is full/empty.
+ * This state is not expected to last for long.
+ */
host->mode_reg = MCI_MR_CLKDIV(clkdiv) | MCI_MR_WRPROOF
| MCI_MR_RDPROOF;
- }
- switch (ios->bus_width) {
- case MMC_BUS_WIDTH_1:
- host->sdc_reg = 0;
- break;
- case MMC_BUS_WIDTH_4:
- host->sdc_reg = MCI_SDCBUS_4BIT;
- break;
+ if (list_empty(&host->queue))
+ mci_writel(host, MR, host->mode_reg);
+ else
+ host->need_clock_update = true;
+
+ spin_unlock_bh(&host->lock);
+ } else {
+ bool any_slot_active = false;
+
+ spin_lock_bh(&host->lock);
+ slot->clock = 0;
+ for (i = 0; i < ATMEL_MCI_MAX_NR_SLOTS; i++) {
+ if (host->slot[i] && host->slot[i]->clock) {
+ any_slot_active = true;
+ break;
+ }
+ }
+ if (!any_slot_active) {
+ mci_writel(host, CR, MCI_CR_MCIDIS);
+ if (host->mode_reg) {
+ mci_readl(host, MR);
+ clk_disable(host->mck);
+ }
+ host->mode_reg = 0;
+ }
+ spin_unlock_bh(&host->lock);
}
switch (ios->power_mode) {
- case MMC_POWER_ON:
- /* Send init sequence (74 clock cycles) */
- atmci_enable(host);
- mci_writel(host, CMDR, MCI_CMDR_SPCMD_INIT);
- while (!(mci_readl(host, SR) & MCI_CMDRDY))
- cpu_relax();
- atmci_disable(host);
+ case MMC_POWER_UP:
+ set_bit(ATMCI_CARD_NEED_INIT, &slot->flags);
break;
default:
/*
* TODO: None of the currently available AVR32-based
* boards allow MMC power to be turned off. Implement
* power control when this can be tested properly.
+ *
+ * We also need to hook this into the clock management
+ * somehow so that newly inserted cards aren't
+ * subjected to a fast clock before we have a chance
+ * to figure out what the maximum rate is. Currently,
+ * there's no way to avoid this, and there never will
+ * be for boards that don't support power control.
*/
break;
}
@@ -573,31 +909,82 @@
static int atmci_get_ro(struct mmc_host *mmc)
{
- int read_only = 0;
- struct atmel_mci *host = mmc_priv(mmc);
+ int read_only = -ENOSYS;
+ struct atmel_mci_slot *slot = mmc_priv(mmc);
- if (gpio_is_valid(host->wp_pin)) {
- read_only = gpio_get_value(host->wp_pin);
+ if (gpio_is_valid(slot->wp_pin)) {
+ read_only = gpio_get_value(slot->wp_pin);
dev_dbg(&mmc->class_dev, "card is %s\n",
read_only ? "read-only" : "read-write");
- } else {
- dev_dbg(&mmc->class_dev,
- "no pin for checking read-only switch."
- " Assuming write-enable.\n");
}
return read_only;
}
-static struct mmc_host_ops atmci_ops = {
+static int atmci_get_cd(struct mmc_host *mmc)
+{
+ int present = -ENOSYS;
+ struct atmel_mci_slot *slot = mmc_priv(mmc);
+
+ if (gpio_is_valid(slot->detect_pin)) {
+ present = !gpio_get_value(slot->detect_pin);
+ dev_dbg(&mmc->class_dev, "card is %spresent\n",
+ present ? "" : "not ");
+ }
+
+ return present;
+}
+
+static const struct mmc_host_ops atmci_ops = {
.request = atmci_request,
.set_ios = atmci_set_ios,
.get_ro = atmci_get_ro,
+ .get_cd = atmci_get_cd,
};
-static void atmci_command_complete(struct atmel_mci *host,
- struct mmc_command *cmd, u32 status)
+/* Called with host->lock held */
+static void atmci_request_end(struct atmel_mci *host, struct mmc_request *mrq)
+ __releases(&host->lock)
+ __acquires(&host->lock)
{
+ struct atmel_mci_slot *slot = NULL;
+ struct mmc_host *prev_mmc = host->cur_slot->mmc;
+
+ WARN_ON(host->cmd || host->data);
+
+ /*
+ * Update the MMC clock rate if necessary. This may be
+ * necessary if set_ios() is called when a different slot is
+ * busy transfering data.
+ */
+ if (host->need_clock_update)
+ mci_writel(host, MR, host->mode_reg);
+
+ host->cur_slot->mrq = NULL;
+ host->mrq = NULL;
+ if (!list_empty(&host->queue)) {
+ slot = list_entry(host->queue.next,
+ struct atmel_mci_slot, queue_node);
+ list_del(&slot->queue_node);
+ dev_vdbg(&host->pdev->dev, "list not empty: %s is next\n",
+ mmc_hostname(slot->mmc));
+ host->state = STATE_SENDING_CMD;
+ atmci_start_request(host, slot);
+ } else {
+ dev_vdbg(&host->pdev->dev, "list empty\n");
+ host->state = STATE_IDLE;
+ }
+
+ spin_unlock(&host->lock);
+ mmc_request_done(prev_mmc, mrq);
+ spin_lock(&host->lock);
+}
+
+static void atmci_command_complete(struct atmel_mci *host,
+ struct mmc_command *cmd)
+{
+ u32 status = host->cmd_status;
+
/* Read the response from the card (up to 16 bytes) */
cmd->resp[0] = mci_readl(host, RSPR);
cmd->resp[1] = mci_readl(host, RSPR);
@@ -614,11 +1001,12 @@
cmd->error = 0;
if (cmd->error) {
- dev_dbg(&host->mmc->class_dev,
+ dev_dbg(&host->pdev->dev,
"command error: status=0x%08x\n", status);
if (cmd->data) {
host->data = NULL;
+ atmci_stop_dma(host);
mci_writel(host, IDR, MCI_NOTBUSY
| MCI_TXRDY | MCI_RXRDY
| ATMCI_DATA_ERROR_FLAGS);
@@ -628,146 +1016,222 @@
static void atmci_detect_change(unsigned long data)
{
- struct atmel_mci *host = (struct atmel_mci *)data;
- struct mmc_request *mrq = host->mrq;
- int present;
+ struct atmel_mci_slot *slot = (struct atmel_mci_slot *)data;
+ bool present;
+ bool present_old;
/*
- * atmci_remove() sets detect_pin to -1 before freeing the
- * interrupt. We must not re-enable the interrupt if it has
- * been freed.
+ * atmci_cleanup_slot() sets the ATMCI_SHUTDOWN flag before
+ * freeing the interrupt. We must not re-enable the interrupt
+ * if it has been freed, and if we're shutting down, it
+ * doesn't really matter whether the card is present or not.
*/
smp_rmb();
- if (!gpio_is_valid(host->detect_pin))
+ if (test_bit(ATMCI_SHUTDOWN, &slot->flags))
return;
- enable_irq(gpio_to_irq(host->detect_pin));
- present = !gpio_get_value(host->detect_pin);
+ enable_irq(gpio_to_irq(slot->detect_pin));
+ present = !gpio_get_value(slot->detect_pin);
+ present_old = test_bit(ATMCI_CARD_PRESENT, &slot->flags);
- dev_vdbg(&host->pdev->dev, "detect change: %d (was %d)\n",
- present, host->present);
+ dev_vdbg(&slot->mmc->class_dev, "detect change: %d (was %d)\n",
+ present, present_old);
- if (present != host->present) {
- dev_dbg(&host->mmc->class_dev, "card %s\n",
+ if (present != present_old) {
+ struct atmel_mci *host = slot->host;
+ struct mmc_request *mrq;
+
+ dev_dbg(&slot->mmc->class_dev, "card %s\n",
present ? "inserted" : "removed");
- host->present = present;
- /* Reset controller if card is gone */
- if (!present) {
- mci_writel(host, CR, MCI_CR_SWRST);
- mci_writel(host, IDR, ~0UL);
- mci_writel(host, CR, MCI_CR_MCIEN);
- }
+ spin_lock(&host->lock);
+
+ if (!present)
+ clear_bit(ATMCI_CARD_PRESENT, &slot->flags);
+ else
+ set_bit(ATMCI_CARD_PRESENT, &slot->flags);
/* Clean up queue if present */
+ mrq = slot->mrq;
if (mrq) {
- /*
- * Reset controller to terminate any ongoing
- * commands or data transfers.
- */
- mci_writel(host, CR, MCI_CR_SWRST);
+ if (mrq == host->mrq) {
+ /*
+ * Reset controller to terminate any ongoing
+ * commands or data transfers.
+ */
+ mci_writel(host, CR, MCI_CR_SWRST);
+ mci_writel(host, CR, MCI_CR_MCIEN);
+ mci_writel(host, MR, host->mode_reg);
- if (!atmci_is_completed(host, EVENT_CMD_COMPLETE))
- mrq->cmd->error = -ENOMEDIUM;
-
- if (mrq->data && !atmci_is_completed(host,
- EVENT_DATA_COMPLETE)) {
host->data = NULL;
- mrq->data->error = -ENOMEDIUM;
+ host->cmd = NULL;
+
+ switch (host->state) {
+ case STATE_IDLE:
+ break;
+ case STATE_SENDING_CMD:
+ mrq->cmd->error = -ENOMEDIUM;
+ if (!mrq->data)
+ break;
+ /* fall through */
+ case STATE_SENDING_DATA:
+ mrq->data->error = -ENOMEDIUM;
+ atmci_stop_dma(host);
+ break;
+ case STATE_DATA_BUSY:
+ case STATE_DATA_ERROR:
+ if (mrq->data->error == -EINPROGRESS)
+ mrq->data->error = -ENOMEDIUM;
+ if (!mrq->stop)
+ break;
+ /* fall through */
+ case STATE_SENDING_STOP:
+ mrq->stop->error = -ENOMEDIUM;
+ break;
+ }
+
+ atmci_request_end(host, mrq);
+ } else {
+ list_del(&slot->queue_node);
+ mrq->cmd->error = -ENOMEDIUM;
+ if (mrq->data)
+ mrq->data->error = -ENOMEDIUM;
+ if (mrq->stop)
+ mrq->stop->error = -ENOMEDIUM;
+
+ spin_unlock(&host->lock);
+ mmc_request_done(slot->mmc, mrq);
+ spin_lock(&host->lock);
}
- if (mrq->stop && !atmci_is_completed(host,
- EVENT_STOP_COMPLETE))
- mrq->stop->error = -ENOMEDIUM;
-
- host->cmd = NULL;
- atmci_request_end(host->mmc, mrq);
}
+ spin_unlock(&host->lock);
- mmc_detect_change(host->mmc, 0);
+ mmc_detect_change(slot->mmc, 0);
}
}
static void atmci_tasklet_func(unsigned long priv)
{
- struct mmc_host *mmc = (struct mmc_host *)priv;
- struct atmel_mci *host = mmc_priv(mmc);
+ struct atmel_mci *host = (struct atmel_mci *)priv;
struct mmc_request *mrq = host->mrq;
struct mmc_data *data = host->data;
+ struct mmc_command *cmd = host->cmd;
+ enum atmel_mci_state state = host->state;
+ enum atmel_mci_state prev_state;
+ u32 status;
- dev_vdbg(&mmc->class_dev,
- "tasklet: pending/completed/mask %lx/%lx/%x\n",
- host->pending_events, host->completed_events,
+ spin_lock(&host->lock);
+
+ state = host->state;
+
+ dev_vdbg(&host->pdev->dev,
+ "tasklet: state %u pending/completed/mask %lx/%lx/%x\n",
+ state, host->pending_events, host->completed_events,
mci_readl(host, IMR));
- if (atmci_test_and_clear_pending(host, EVENT_CMD_COMPLETE)) {
- /*
- * host->cmd must be set to NULL before the interrupt
- * handler sees EVENT_CMD_COMPLETE
- */
- host->cmd = NULL;
- smp_wmb();
- atmci_set_completed(host, EVENT_CMD_COMPLETE);
- atmci_command_complete(host, mrq->cmd, host->cmd_status);
+ do {
+ prev_state = state;
- if (!mrq->cmd->error && mrq->stop
- && atmci_is_completed(host, EVENT_XFER_COMPLETE)
- && !atmci_test_and_set_completed(host,
- EVENT_STOP_SENT))
- send_stop_cmd(host->mmc, mrq->data);
- }
- if (atmci_test_and_clear_pending(host, EVENT_STOP_COMPLETE)) {
- /*
- * host->cmd must be set to NULL before the interrupt
- * handler sees EVENT_STOP_COMPLETE
- */
- host->cmd = NULL;
- smp_wmb();
- atmci_set_completed(host, EVENT_STOP_COMPLETE);
- atmci_command_complete(host, mrq->stop, host->stop_status);
- }
- if (atmci_test_and_clear_pending(host, EVENT_DATA_ERROR)) {
- u32 status = host->data_status;
+ switch (state) {
+ case STATE_IDLE:
+ break;
- dev_vdbg(&mmc->class_dev, "data error: status=%08x\n", status);
+ case STATE_SENDING_CMD:
+ if (!atmci_test_and_clear_pending(host,
+ EVENT_CMD_COMPLETE))
+ break;
- atmci_set_completed(host, EVENT_DATA_ERROR);
- atmci_set_completed(host, EVENT_DATA_COMPLETE);
+ host->cmd = NULL;
+ atmci_set_completed(host, EVENT_CMD_COMPLETE);
+ atmci_command_complete(host, mrq->cmd);
+ if (!mrq->data || cmd->error) {
+ atmci_request_end(host, host->mrq);
+ goto unlock;
+ }
- if (status & MCI_DTOE) {
- dev_dbg(&mmc->class_dev,
- "data timeout error\n");
- data->error = -ETIMEDOUT;
- } else if (status & MCI_DCRCE) {
- dev_dbg(&mmc->class_dev, "data CRC error\n");
- data->error = -EILSEQ;
- } else {
- dev_dbg(&mmc->class_dev,
- "data FIFO error (status=%08x)\n",
- status);
- data->error = -EIO;
+ prev_state = state = STATE_SENDING_DATA;
+ /* fall through */
+
+ case STATE_SENDING_DATA:
+ if (atmci_test_and_clear_pending(host,
+ EVENT_DATA_ERROR)) {
+ atmci_stop_dma(host);
+ if (data->stop)
+ send_stop_cmd(host, data);
+ state = STATE_DATA_ERROR;
+ break;
+ }
+
+ if (!atmci_test_and_clear_pending(host,
+ EVENT_XFER_COMPLETE))
+ break;
+
+ atmci_set_completed(host, EVENT_XFER_COMPLETE);
+ prev_state = state = STATE_DATA_BUSY;
+ /* fall through */
+
+ case STATE_DATA_BUSY:
+ if (!atmci_test_and_clear_pending(host,
+ EVENT_DATA_COMPLETE))
+ break;
+
+ host->data = NULL;
+ atmci_set_completed(host, EVENT_DATA_COMPLETE);
+ status = host->data_status;
+ if (unlikely(status & ATMCI_DATA_ERROR_FLAGS)) {
+ if (status & MCI_DTOE) {
+ dev_dbg(&host->pdev->dev,
+ "data timeout error\n");
+ data->error = -ETIMEDOUT;
+ } else if (status & MCI_DCRCE) {
+ dev_dbg(&host->pdev->dev,
+ "data CRC error\n");
+ data->error = -EILSEQ;
+ } else {
+ dev_dbg(&host->pdev->dev,
+ "data FIFO error (status=%08x)\n",
+ status);
+ data->error = -EIO;
+ }
+ } else {
+ data->bytes_xfered = data->blocks * data->blksz;
+ data->error = 0;
+ }
+
+ if (!data->stop) {
+ atmci_request_end(host, host->mrq);
+ goto unlock;
+ }
+
+ prev_state = state = STATE_SENDING_STOP;
+ if (!data->error)
+ send_stop_cmd(host, data);
+ /* fall through */
+
+ case STATE_SENDING_STOP:
+ if (!atmci_test_and_clear_pending(host,
+ EVENT_CMD_COMPLETE))
+ break;
+
+ host->cmd = NULL;
+ atmci_command_complete(host, mrq->stop);
+ atmci_request_end(host, host->mrq);
+ goto unlock;
+
+ case STATE_DATA_ERROR:
+ if (!atmci_test_and_clear_pending(host,
+ EVENT_XFER_COMPLETE))
+ break;
+
+ state = STATE_DATA_BUSY;
+ break;
}
+ } while (state != prev_state);
- if (host->present && data->stop
- && atmci_is_completed(host, EVENT_CMD_COMPLETE)
- && !atmci_test_and_set_completed(
- host, EVENT_STOP_SENT))
- send_stop_cmd(host->mmc, data);
+ host->state = state;
- host->data = NULL;
- }
- if (atmci_test_and_clear_pending(host, EVENT_DATA_COMPLETE)) {
- atmci_set_completed(host, EVENT_DATA_COMPLETE);
-
- if (!atmci_is_completed(host, EVENT_DATA_ERROR)) {
- data->bytes_xfered = data->blocks * data->blksz;
- data->error = 0;
- }
-
- host->data = NULL;
- }
-
- if (host->mrq && !host->cmd && !host->data)
- atmci_request_end(mmc, host->mrq);
+unlock:
+ spin_unlock(&host->lock);
}
static void atmci_read_data_pio(struct atmel_mci *host)
@@ -789,6 +1253,7 @@
nbytes += 4;
if (offset == sg->length) {
+ flush_dcache_page(sg_page(sg));
host->sg = sg = sg_next(sg);
if (!sg)
goto done;
@@ -817,9 +1282,11 @@
mci_writel(host, IDR, (MCI_NOTBUSY | MCI_RXRDY
| ATMCI_DATA_ERROR_FLAGS));
host->data_status = status;
+ data->bytes_xfered += nbytes;
+ smp_wmb();
atmci_set_pending(host, EVENT_DATA_ERROR);
tasklet_schedule(&host->tasklet);
- break;
+ return;
}
} while (status & MCI_RXRDY);
@@ -832,10 +1299,8 @@
mci_writel(host, IDR, MCI_RXRDY);
mci_writel(host, IER, MCI_NOTBUSY);
data->bytes_xfered += nbytes;
- atmci_set_completed(host, EVENT_XFER_COMPLETE);
- if (data->stop && atmci_is_completed(host, EVENT_CMD_COMPLETE)
- && !atmci_test_and_set_completed(host, EVENT_STOP_SENT))
- send_stop_cmd(host->mmc, data);
+ smp_wmb();
+ atmci_set_pending(host, EVENT_XFER_COMPLETE);
}
static void atmci_write_data_pio(struct atmel_mci *host)
@@ -888,9 +1353,11 @@
mci_writel(host, IDR, (MCI_NOTBUSY | MCI_TXRDY
| ATMCI_DATA_ERROR_FLAGS));
host->data_status = status;
+ data->bytes_xfered += nbytes;
+ smp_wmb();
atmci_set_pending(host, EVENT_DATA_ERROR);
tasklet_schedule(&host->tasklet);
- break;
+ return;
}
} while (status & MCI_TXRDY);
@@ -903,38 +1370,26 @@
mci_writel(host, IDR, MCI_TXRDY);
mci_writel(host, IER, MCI_NOTBUSY);
data->bytes_xfered += nbytes;
- atmci_set_completed(host, EVENT_XFER_COMPLETE);
- if (data->stop && atmci_is_completed(host, EVENT_CMD_COMPLETE)
- && !atmci_test_and_set_completed(host, EVENT_STOP_SENT))
- send_stop_cmd(host->mmc, data);
+ smp_wmb();
+ atmci_set_pending(host, EVENT_XFER_COMPLETE);
}
-static void atmci_cmd_interrupt(struct mmc_host *mmc, u32 status)
+static void atmci_cmd_interrupt(struct atmel_mci *host, u32 status)
{
- struct atmel_mci *host = mmc_priv(mmc);
-
mci_writel(host, IDR, MCI_CMDRDY);
- if (atmci_is_completed(host, EVENT_STOP_SENT)) {
- host->stop_status = status;
- atmci_set_pending(host, EVENT_STOP_COMPLETE);
- } else {
- host->cmd_status = status;
- atmci_set_pending(host, EVENT_CMD_COMPLETE);
- }
-
+ host->cmd_status = status;
+ smp_wmb();
+ atmci_set_pending(host, EVENT_CMD_COMPLETE);
tasklet_schedule(&host->tasklet);
}
static irqreturn_t atmci_interrupt(int irq, void *dev_id)
{
- struct mmc_host *mmc = dev_id;
- struct atmel_mci *host = mmc_priv(mmc);
+ struct atmel_mci *host = dev_id;
u32 status, mask, pending;
unsigned int pass_count = 0;
- spin_lock(&mmc->lock);
-
do {
status = mci_readl(host, SR);
mask = mci_readl(host, IMR);
@@ -946,13 +1401,18 @@
mci_writel(host, IDR, ATMCI_DATA_ERROR_FLAGS
| MCI_RXRDY | MCI_TXRDY);
pending &= mci_readl(host, IMR);
+
host->data_status = status;
+ smp_wmb();
atmci_set_pending(host, EVENT_DATA_ERROR);
tasklet_schedule(&host->tasklet);
}
if (pending & MCI_NOTBUSY) {
- mci_writel(host, IDR, (MCI_NOTBUSY
- | ATMCI_DATA_ERROR_FLAGS));
+ mci_writel(host, IDR,
+ ATMCI_DATA_ERROR_FLAGS | MCI_NOTBUSY);
+ if (!host->data_status)
+ host->data_status = status;
+ smp_wmb();
atmci_set_pending(host, EVENT_DATA_COMPLETE);
tasklet_schedule(&host->tasklet);
}
@@ -962,18 +1422,15 @@
atmci_write_data_pio(host);
if (pending & MCI_CMDRDY)
- atmci_cmd_interrupt(mmc, status);
+ atmci_cmd_interrupt(host, status);
} while (pass_count++ < 5);
- spin_unlock(&mmc->lock);
-
return pass_count ? IRQ_HANDLED : IRQ_NONE;
}
static irqreturn_t atmci_detect_interrupt(int irq, void *dev_id)
{
- struct mmc_host *mmc = dev_id;
- struct atmel_mci *host = mmc_priv(mmc);
+ struct atmel_mci_slot *slot = dev_id;
/*
* Disable interrupts until the pin has stabilized and check
@@ -981,19 +1438,176 @@
* middle of the timer routine when this interrupt triggers.
*/
disable_irq_nosync(irq);
- mod_timer(&host->detect_timer, jiffies + msecs_to_jiffies(20));
+ mod_timer(&slot->detect_timer, jiffies + msecs_to_jiffies(20));
return IRQ_HANDLED;
}
+#ifdef CONFIG_MMC_ATMELMCI_DMA
+
+static inline struct atmel_mci *
+dma_client_to_atmel_mci(struct dma_client *client)
+{
+ return container_of(client, struct atmel_mci, dma.client);
+}
+
+static enum dma_state_client atmci_dma_event(struct dma_client *client,
+ struct dma_chan *chan, enum dma_state state)
+{
+ struct atmel_mci *host;
+ enum dma_state_client ret = DMA_NAK;
+
+ host = dma_client_to_atmel_mci(client);
+
+ switch (state) {
+ case DMA_RESOURCE_AVAILABLE:
+ spin_lock_bh(&host->lock);
+ if (!host->dma.chan) {
+ host->dma.chan = chan;
+ ret = DMA_ACK;
+ }
+ spin_unlock_bh(&host->lock);
+
+ if (ret == DMA_ACK)
+ dev_info(&host->pdev->dev,
+ "Using %s for DMA transfers\n",
+ chan->dev.bus_id);
+ break;
+
+ case DMA_RESOURCE_REMOVED:
+ spin_lock_bh(&host->lock);
+ if (host->dma.chan == chan) {
+ host->dma.chan = NULL;
+ ret = DMA_ACK;
+ }
+ spin_unlock_bh(&host->lock);
+
+ if (ret == DMA_ACK)
+ dev_info(&host->pdev->dev,
+ "Lost %s, falling back to PIO\n",
+ chan->dev.bus_id);
+ break;
+
+ default:
+ break;
+ }
+
+
+ return ret;
+}
+#endif /* CONFIG_MMC_ATMELMCI_DMA */
+
+static int __init atmci_init_slot(struct atmel_mci *host,
+ struct mci_slot_pdata *slot_data, unsigned int id,
+ u32 sdc_reg)
+{
+ struct mmc_host *mmc;
+ struct atmel_mci_slot *slot;
+
+ mmc = mmc_alloc_host(sizeof(struct atmel_mci_slot), &host->pdev->dev);
+ if (!mmc)
+ return -ENOMEM;
+
+ slot = mmc_priv(mmc);
+ slot->mmc = mmc;
+ slot->host = host;
+ slot->detect_pin = slot_data->detect_pin;
+ slot->wp_pin = slot_data->wp_pin;
+ slot->sdc_reg = sdc_reg;
+
+ mmc->ops = &atmci_ops;
+ mmc->f_min = DIV_ROUND_UP(host->bus_hz, 512);
+ mmc->f_max = host->bus_hz / 2;
+ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+ if (slot_data->bus_width >= 4)
+ mmc->caps |= MMC_CAP_4_BIT_DATA;
+
+ mmc->max_hw_segs = 64;
+ mmc->max_phys_segs = 64;
+ mmc->max_req_size = 32768 * 512;
+ mmc->max_blk_size = 32768;
+ mmc->max_blk_count = 512;
+
+ /* Assume card is present initially */
+ set_bit(ATMCI_CARD_PRESENT, &slot->flags);
+ if (gpio_is_valid(slot->detect_pin)) {
+ if (gpio_request(slot->detect_pin, "mmc_detect")) {
+ dev_dbg(&mmc->class_dev, "no detect pin available\n");
+ slot->detect_pin = -EBUSY;
+ } else if (gpio_get_value(slot->detect_pin)) {
+ clear_bit(ATMCI_CARD_PRESENT, &slot->flags);
+ }
+ }
+
+ if (!gpio_is_valid(slot->detect_pin))
+ mmc->caps |= MMC_CAP_NEEDS_POLL;
+
+ if (gpio_is_valid(slot->wp_pin)) {
+ if (gpio_request(slot->wp_pin, "mmc_wp")) {
+ dev_dbg(&mmc->class_dev, "no WP pin available\n");
+ slot->wp_pin = -EBUSY;
+ }
+ }
+
+ host->slot[id] = slot;
+ mmc_add_host(mmc);
+
+ if (gpio_is_valid(slot->detect_pin)) {
+ int ret;
+
+ setup_timer(&slot->detect_timer, atmci_detect_change,
+ (unsigned long)slot);
+
+ ret = request_irq(gpio_to_irq(slot->detect_pin),
+ atmci_detect_interrupt,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "mmc-detect", slot);
+ if (ret) {
+ dev_dbg(&mmc->class_dev,
+ "could not request IRQ %d for detect pin\n",
+ gpio_to_irq(slot->detect_pin));
+ gpio_free(slot->detect_pin);
+ slot->detect_pin = -EBUSY;
+ }
+ }
+
+ atmci_init_debugfs(slot);
+
+ return 0;
+}
+
+static void __exit atmci_cleanup_slot(struct atmel_mci_slot *slot,
+ unsigned int id)
+{
+ /* Debugfs stuff is cleaned up by mmc core */
+
+ set_bit(ATMCI_SHUTDOWN, &slot->flags);
+ smp_wmb();
+
+ mmc_remove_host(slot->mmc);
+
+ if (gpio_is_valid(slot->detect_pin)) {
+ int pin = slot->detect_pin;
+
+ free_irq(gpio_to_irq(pin), slot);
+ del_timer_sync(&slot->detect_timer);
+ gpio_free(pin);
+ }
+ if (gpio_is_valid(slot->wp_pin))
+ gpio_free(slot->wp_pin);
+
+ slot->host->slot[id] = NULL;
+ mmc_free_host(slot->mmc);
+}
+
static int __init atmci_probe(struct platform_device *pdev)
{
struct mci_platform_data *pdata;
- struct atmel_mci *host;
- struct mmc_host *mmc;
- struct resource *regs;
- int irq;
- int ret;
+ struct atmel_mci *host;
+ struct resource *regs;
+ unsigned int nr_slots;
+ int irq;
+ int ret;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!regs)
@@ -1005,15 +1619,13 @@
if (irq < 0)
return irq;
- mmc = mmc_alloc_host(sizeof(struct atmel_mci), &pdev->dev);
- if (!mmc)
+ host = kzalloc(sizeof(struct atmel_mci), GFP_KERNEL);
+ if (!host)
return -ENOMEM;
- host = mmc_priv(mmc);
host->pdev = pdev;
- host->mmc = mmc;
- host->detect_pin = pdata->detect_pin;
- host->wp_pin = pdata->wp_pin;
+ spin_lock_init(&host->lock);
+ INIT_LIST_HEAD(&host->queue);
host->mck = clk_get(&pdev->dev, "mci_clk");
if (IS_ERR(host->mck)) {
@@ -1033,122 +1645,102 @@
host->mapbase = regs->start;
- mmc->ops = &atmci_ops;
- mmc->f_min = (host->bus_hz + 511) / 512;
- mmc->f_max = host->bus_hz / 2;
- mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
- mmc->caps |= MMC_CAP_4_BIT_DATA;
+ tasklet_init(&host->tasklet, atmci_tasklet_func, (unsigned long)host);
- mmc->max_hw_segs = 64;
- mmc->max_phys_segs = 64;
- mmc->max_req_size = 32768 * 512;
- mmc->max_blk_size = 32768;
- mmc->max_blk_count = 512;
-
- tasklet_init(&host->tasklet, atmci_tasklet_func, (unsigned long)mmc);
-
- ret = request_irq(irq, atmci_interrupt, 0, pdev->dev.bus_id, mmc);
+ ret = request_irq(irq, atmci_interrupt, 0, pdev->dev.bus_id, host);
if (ret)
goto err_request_irq;
- /* Assume card is present if we don't have a detect pin */
- host->present = 1;
- if (gpio_is_valid(host->detect_pin)) {
- if (gpio_request(host->detect_pin, "mmc_detect")) {
- dev_dbg(&mmc->class_dev, "no detect pin available\n");
- host->detect_pin = -1;
- } else {
- host->present = !gpio_get_value(host->detect_pin);
- }
- }
+#ifdef CONFIG_MMC_ATMELMCI_DMA
+ if (pdata->dma_slave) {
+ struct dma_slave *slave = pdata->dma_slave;
- if (!gpio_is_valid(host->detect_pin))
- mmc->caps |= MMC_CAP_NEEDS_POLL;
+ slave->tx_reg = regs->start + MCI_TDR;
+ slave->rx_reg = regs->start + MCI_RDR;
- if (gpio_is_valid(host->wp_pin)) {
- if (gpio_request(host->wp_pin, "mmc_wp")) {
- dev_dbg(&mmc->class_dev, "no WP pin available\n");
- host->wp_pin = -1;
- }
+ /* Try to grab a DMA channel */
+ host->dma.client.event_callback = atmci_dma_event;
+ dma_cap_set(DMA_SLAVE, host->dma.client.cap_mask);
+ host->dma.client.slave = slave;
+
+ dma_async_client_register(&host->dma.client);
+ dma_async_client_chan_request(&host->dma.client);
+ } else {
+ dev_notice(&pdev->dev, "DMA not available, using PIO\n");
}
+#endif /* CONFIG_MMC_ATMELMCI_DMA */
platform_set_drvdata(pdev, host);
- mmc_add_host(mmc);
-
- if (gpio_is_valid(host->detect_pin)) {
- setup_timer(&host->detect_timer, atmci_detect_change,
- (unsigned long)host);
-
- ret = request_irq(gpio_to_irq(host->detect_pin),
- atmci_detect_interrupt,
- IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
- "mmc-detect", mmc);
- if (ret) {
- dev_dbg(&mmc->class_dev,
- "could not request IRQ %d for detect pin\n",
- gpio_to_irq(host->detect_pin));
- gpio_free(host->detect_pin);
- host->detect_pin = -1;
- }
+ /* We need at least one slot to succeed */
+ nr_slots = 0;
+ ret = -ENODEV;
+ if (pdata->slot[0].bus_width) {
+ ret = atmci_init_slot(host, &pdata->slot[0],
+ MCI_SDCSEL_SLOT_A, 0);
+ if (!ret)
+ nr_slots++;
+ }
+ if (pdata->slot[1].bus_width) {
+ ret = atmci_init_slot(host, &pdata->slot[1],
+ MCI_SDCSEL_SLOT_B, 1);
+ if (!ret)
+ nr_slots++;
}
- dev_info(&mmc->class_dev,
- "Atmel MCI controller at 0x%08lx irq %d\n",
- host->mapbase, irq);
+ if (!nr_slots)
+ goto err_init_slot;
- atmci_init_debugfs(host);
+ dev_info(&pdev->dev,
+ "Atmel MCI controller at 0x%08lx irq %d, %u slots\n",
+ host->mapbase, irq, nr_slots);
return 0;
+err_init_slot:
+#ifdef CONFIG_MMC_ATMELMCI_DMA
+ if (pdata->dma_slave)
+ dma_async_client_unregister(&host->dma.client);
+#endif
+ free_irq(irq, host);
err_request_irq:
iounmap(host->regs);
err_ioremap:
clk_put(host->mck);
err_clk_get:
- mmc_free_host(mmc);
+ kfree(host);
return ret;
}
static int __exit atmci_remove(struct platform_device *pdev)
{
- struct atmel_mci *host = platform_get_drvdata(pdev);
+ struct atmel_mci *host = platform_get_drvdata(pdev);
+ unsigned int i;
platform_set_drvdata(pdev, NULL);
- if (host) {
- /* Debugfs stuff is cleaned up by mmc core */
-
- if (gpio_is_valid(host->detect_pin)) {
- int pin = host->detect_pin;
-
- /* Make sure the timer doesn't enable the interrupt */
- host->detect_pin = -1;
- smp_wmb();
-
- free_irq(gpio_to_irq(pin), host->mmc);
- del_timer_sync(&host->detect_timer);
- gpio_free(pin);
- }
-
- mmc_remove_host(host->mmc);
-
- clk_enable(host->mck);
- mci_writel(host, IDR, ~0UL);
- mci_writel(host, CR, MCI_CR_MCIDIS);
- mci_readl(host, SR);
- clk_disable(host->mck);
-
- if (gpio_is_valid(host->wp_pin))
- gpio_free(host->wp_pin);
-
- free_irq(platform_get_irq(pdev, 0), host->mmc);
- iounmap(host->regs);
-
- clk_put(host->mck);
-
- mmc_free_host(host->mmc);
+ for (i = 0; i < ATMEL_MCI_MAX_NR_SLOTS; i++) {
+ if (host->slot[i])
+ atmci_cleanup_slot(host->slot[i], i);
}
+
+ clk_enable(host->mck);
+ mci_writel(host, IDR, ~0UL);
+ mci_writel(host, CR, MCI_CR_MCIDIS);
+ mci_readl(host, SR);
+ clk_disable(host->mck);
+
+#ifdef CONFIG_MMC_ATMELMCI_DMA
+ if (host->dma.client.slave)
+ dma_async_client_unregister(&host->dma.client);
+#endif
+
+ free_irq(platform_get_irq(pdev, 0), host);
+ iounmap(host->regs);
+
+ clk_put(host->mck);
+ kfree(host);
+
return 0;
}
diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c
index 7503b81..07faf54 100644
--- a/drivers/mmc/host/mmc_spi.c
+++ b/drivers/mmc/host/mmc_spi.c
@@ -95,8 +95,6 @@
* reads which takes nowhere near that long. Older cards may be able to use
* shorter timeouts ... but why bother?
*/
-#define readblock_timeout ktime_set(0, 100 * 1000 * 1000)
-#define writeblock_timeout ktime_set(0, 250 * 1000 * 1000)
#define r1b_timeout ktime_set(3, 0)
@@ -220,9 +218,9 @@
return mmc_spi_skip(host, timeout, sizeof(host->data->status), 0);
}
-static int mmc_spi_readtoken(struct mmc_spi_host *host)
+static int mmc_spi_readtoken(struct mmc_spi_host *host, ktime_t timeout)
{
- return mmc_spi_skip(host, readblock_timeout, 1, 0xff);
+ return mmc_spi_skip(host, timeout, 1, 0xff);
}
@@ -605,7 +603,8 @@
* Return negative errno, else success.
*/
static int
-mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t)
+mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t,
+ ktime_t timeout)
{
struct spi_device *spi = host->spi;
int status, i;
@@ -673,7 +672,7 @@
if (scratch->status[i] != 0)
return 0;
}
- return mmc_spi_wait_unbusy(host, writeblock_timeout);
+ return mmc_spi_wait_unbusy(host, timeout);
}
/*
@@ -693,7 +692,8 @@
* STOP_TRANSMISSION command.
*/
static int
-mmc_spi_readblock(struct mmc_spi_host *host, struct spi_transfer *t)
+mmc_spi_readblock(struct mmc_spi_host *host, struct spi_transfer *t,
+ ktime_t timeout)
{
struct spi_device *spi = host->spi;
int status;
@@ -707,7 +707,7 @@
return status;
status = scratch->status[0];
if (status == 0xff || status == 0)
- status = mmc_spi_readtoken(host);
+ status = mmc_spi_readtoken(host, timeout);
if (status == SPI_TOKEN_SINGLE) {
if (host->dma_dev) {
@@ -778,6 +778,8 @@
struct scatterlist *sg;
unsigned n_sg;
int multiple = (data->blocks > 1);
+ u32 clock_rate;
+ ktime_t timeout;
if (data->flags & MMC_DATA_READ)
direction = DMA_FROM_DEVICE;
@@ -786,6 +788,14 @@
mmc_spi_setup_data_message(host, multiple, direction);
t = &host->t;
+ if (t->speed_hz)
+ clock_rate = t->speed_hz;
+ else
+ clock_rate = spi->max_speed_hz;
+
+ timeout = ktime_add_ns(ktime_set(0, 0), data->timeout_ns +
+ data->timeout_clks * 1000000 / clock_rate);
+
/* Handle scatterlist segments one at a time, with synch for
* each 512-byte block
*/
@@ -832,9 +842,9 @@
t->len);
if (direction == DMA_TO_DEVICE)
- status = mmc_spi_writeblock(host, t);
+ status = mmc_spi_writeblock(host, t, timeout);
else
- status = mmc_spi_readblock(host, t);
+ status = mmc_spi_readblock(host, t, timeout);
if (status < 0)
break;
@@ -917,7 +927,7 @@
if (scratch->status[tmp] != 0)
return;
}
- tmp = mmc_spi_wait_unbusy(host, writeblock_timeout);
+ tmp = mmc_spi_wait_unbusy(host, timeout);
if (tmp < 0 && !data->error)
data->error = tmp;
}
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
index fcb14c2..9bd7026 100644
--- a/drivers/mmc/host/sdhci-pci.c
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -144,7 +144,8 @@
SDHCI_QUIRK_32BIT_DMA_SIZE |
SDHCI_QUIRK_32BIT_ADMA_SIZE |
SDHCI_QUIRK_RESET_AFTER_REQUEST |
- SDHCI_QUIRK_BROKEN_SMALL_PIO;
+ SDHCI_QUIRK_BROKEN_SMALL_PIO |
+ SDHCI_QUIRK_FORCE_HIGHSPEED;
}
/*
@@ -326,7 +327,7 @@
{
.vendor = PCI_VENDOR_ID_MARVELL,
- .device = PCI_DEVICE_ID_MARVELL_CAFE_SD,
+ .device = PCI_DEVICE_ID_MARVELL_88ALP01_SD,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (kernel_ulong_t)&sdhci_cafe,
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index e3a8133..30f64b1 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -177,7 +177,7 @@
{
unsigned long flags;
size_t blksize, len, chunk;
- u32 scratch;
+ u32 uninitialized_var(scratch);
u8 *buf;
DBG("PIO reading\n");
@@ -1154,7 +1154,7 @@
spin_unlock_irqrestore(&host->lock, flags);
- mmc_detect_change(host->mmc, msecs_to_jiffies(500));
+ mmc_detect_change(host->mmc, msecs_to_jiffies(200));
}
static void sdhci_tasklet_finish(unsigned long param)
@@ -1266,9 +1266,31 @@
SDHCI_INT_INDEX))
host->cmd->error = -EILSEQ;
- if (host->cmd->error)
+ if (host->cmd->error) {
tasklet_schedule(&host->finish_tasklet);
- else if (intmask & SDHCI_INT_RESPONSE)
+ return;
+ }
+
+ /*
+ * The host can send and interrupt when the busy state has
+ * ended, allowing us to wait without wasting CPU cycles.
+ * Unfortunately this is overloaded on the "data complete"
+ * interrupt, so we need to take some care when handling
+ * it.
+ *
+ * Note: The 1.0 specification is a bit ambiguous about this
+ * feature so there might be some problems with older
+ * controllers.
+ */
+ if (host->cmd->flags & MMC_RSP_BUSY) {
+ if (host->cmd->data)
+ DBG("Cannot wait for busy signal when also "
+ "doing a data transfer");
+ else
+ return;
+ }
+
+ if (intmask & SDHCI_INT_RESPONSE)
sdhci_finish_command(host);
}
@@ -1278,11 +1300,16 @@
if (!host->data) {
/*
- * A data end interrupt is sent together with the response
- * for the stop command.
+ * The "data complete" interrupt is also used to
+ * indicate that a busy state has ended. See comment
+ * above in sdhci_cmd_irq().
*/
- if (intmask & SDHCI_INT_DATA_END)
- return;
+ if (host->cmd && (host->cmd->flags & MMC_RSP_BUSY)) {
+ if (intmask & SDHCI_INT_DATA_END) {
+ sdhci_finish_command(host);
+ return;
+ }
+ }
printk(KERN_ERR "%s: Got data interrupt 0x%08x even "
"though no data operation was in progress.\n",
@@ -1604,7 +1631,8 @@
mmc->f_max = host->max_clk;
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
- if (caps & SDHCI_CAN_DO_HISPD)
+ if ((caps & SDHCI_CAN_DO_HISPD) ||
+ (host->quirks & SDHCI_QUIRK_FORCE_HIGHSPEED))
mmc->caps |= MMC_CAP_SD_HIGHSPEED;
mmc->ocr_avail = 0;
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 197d4a0..31f4b15 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -208,6 +208,8 @@
#define SDHCI_QUIRK_BROKEN_TIMEOUT_VAL (1<<12)
/* Controller has an issue with buffer bits for small transfers */
#define SDHCI_QUIRK_BROKEN_SMALL_PIO (1<<13)
+/* Controller supports high speed but doesn't have the caps bit set */
+#define SDHCI_QUIRK_FORCE_HIGHSPEED (1<<14)
int irq; /* Device IRQ */
void __iomem * ioaddr; /* Mapped address */
diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c
index 90924fb..d600c2d 100644
--- a/drivers/mtd/maps/pcmciamtd.c
+++ b/drivers/mtd/maps/pcmciamtd.c
@@ -118,7 +118,8 @@
DEBUG(2, "Remapping window from 0x%8.8x to 0x%8.8x",
dev->offset, mrq.CardOffset);
mrq.Page = 0;
- if( (ret = pcmcia_map_mem_page(win, &mrq)) != CS_SUCCESS) {
+ ret = pcmcia_map_mem_page(win, &mrq);
+ if (ret != 0) {
cs_error(dev->p_dev, MapMemPage, ret);
return NULL;
}
@@ -326,9 +327,8 @@
DEBUG(2, "dev = %p on = %d vpp = %d\n", dev, on, dev->vpp);
ret = pcmcia_modify_configuration(link, &mod);
- if(ret != CS_SUCCESS) {
+ if (ret != 0)
cs_error(link, ModifyConfiguration, ret);
- }
}
@@ -368,14 +368,14 @@
tuple.DesiredTuple = RETURN_FIRST_TUPLE;
rc = pcmcia_get_first_tuple(link, &tuple);
- while(rc == CS_SUCCESS) {
+ while (rc == 0) {
rc = pcmcia_get_tuple_data(link, &tuple);
- if(rc != CS_SUCCESS) {
+ if (rc != 0) {
cs_error(link, GetTupleData, rc);
break;
}
- rc = pcmcia_parse_tuple(link, &tuple, &parse);
- if(rc != CS_SUCCESS) {
+ rc = pcmcia_parse_tuple(&tuple, &parse);
+ if (rc != 0) {
cs_error(link, ParseTuple, rc);
break;
}
@@ -493,18 +493,11 @@
int last_ret = 0, last_fn = 0;
int ret;
int i;
- config_info_t t;
static char *probes[] = { "jedec_probe", "cfi_probe" };
int new_name = 0;
DEBUG(3, "link=0x%p", link);
- DEBUG(2, "Validating CIS");
- ret = pcmcia_validate_cis(link, NULL);
- if(ret != CS_SUCCESS) {
- cs_error(link, GetTupleData, ret);
- }
-
card_settings(dev, link, &new_name);
dev->pcmcia_map.phys = NO_XIP;
@@ -571,10 +564,7 @@
dev->pcmcia_map.map_priv_1 = (unsigned long)dev;
dev->pcmcia_map.map_priv_2 = (unsigned long)link->win;
- DEBUG(2, "Getting configuration");
- CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(link, &t));
- DEBUG(2, "Vcc = %d Vpp1 = %d Vpp2 = %d", t.Vcc, t.Vpp1, t.Vpp2);
- dev->vpp = (vpp) ? vpp : t.Vpp1;
+ dev->vpp = (vpp) ? vpp : link->socket.socket.Vpp;
link->conf.Attributes = 0;
if(setvpp == 2) {
link->conf.Vpp = dev->vpp;
@@ -583,16 +573,10 @@
}
link->conf.IntType = INT_MEMORY;
- link->conf.ConfigBase = t.ConfigBase;
- link->conf.Status = t.Status;
- link->conf.Pin = t.Pin;
- link->conf.Copy = t.Copy;
- link->conf.ExtStatus = t.ExtStatus;
link->conf.ConfigIndex = 0;
- link->conf.Present = t.Present;
DEBUG(2, "Setting Configuration");
ret = pcmcia_request_configuration(link, &link->conf);
- if(ret != CS_SUCCESS) {
+ if (ret != 0) {
cs_error(link, RequestConfiguration, ret);
if (dev->win_base) {
iounmap(dev->win_base);
diff --git a/drivers/mtd/maps/sun_uflash.c b/drivers/mtd/maps/sun_uflash.c
index 0d7c883..fd7a101 100644
--- a/drivers/mtd/maps/sun_uflash.c
+++ b/drivers/mtd/maps/sun_uflash.c
@@ -1,13 +1,10 @@
-/*
- *
- * sun_uflash - Driver implementation for user-programmable flash
- * present on many Sun Microsystems SME boardsets.
+/* sun_uflash.c - Driver for user-programmable flash on
+ * Sun Microsystems SME boardsets.
*
* This driver does NOT provide access to the OBP-flash for
* safety reasons-- use <linux>/drivers/sbus/char/flash.c instead.
*
* Copyright (c) 2001 Eric Brower (ebrower@usa.net)
- *
*/
#include <linux/kernel.h>
@@ -16,8 +13,8 @@
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/ioport.h>
-#include <asm/ebus.h>
-#include <asm/oplib.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <asm/prom.h>
#include <asm/uaccess.h>
#include <asm/io.h>
@@ -26,67 +23,65 @@
#include <linux/mtd/map.h>
#define UFLASH_OBPNAME "flashprom"
-#define UFLASH_DEVNAME "userflash"
+#define DRIVER_NAME "sun_uflash"
+#define PFX DRIVER_NAME ": "
#define UFLASH_WINDOW_SIZE 0x200000
#define UFLASH_BUSWIDTH 1 /* EBus is 8-bit */
MODULE_AUTHOR("Eric Brower <ebrower@usa.net>");
MODULE_DESCRIPTION("User-programmable flash device on Sun Microsystems boardsets");
-MODULE_SUPPORTED_DEVICE("userflash");
+MODULE_SUPPORTED_DEVICE(DRIVER_NAME);
MODULE_LICENSE("GPL");
-MODULE_VERSION("2.0");
+MODULE_VERSION("2.1");
-static LIST_HEAD(device_list);
struct uflash_dev {
const char *name; /* device name */
struct map_info map; /* mtd map info */
struct mtd_info *mtd; /* mtd info */
};
-
struct map_info uflash_map_templ = {
.name = "SUNW,???-????",
.size = UFLASH_WINDOW_SIZE,
.bankwidth = UFLASH_BUSWIDTH,
};
-int uflash_devinit(struct linux_ebus_device *edev, struct device_node *dp)
+int uflash_devinit(struct of_device *op, struct device_node *dp)
{
struct uflash_dev *up;
- struct resource *res;
- res = &edev->resource[0];
-
- if (edev->num_addrs != 1) {
+ if (op->resource[1].flags) {
/* Non-CFI userflash device-- once I find one we
* can work on supporting it.
*/
- printk("%s: unsupported device at 0x%llx (%d regs): " \
- "email ebrower@usa.net\n",
- dp->full_name, (unsigned long long)res->start,
- edev->num_addrs);
+ printk(KERN_ERR PFX "Unsupported device at %s, 0x%llx\n",
+ dp->full_name, (unsigned long long)op->resource[0].start);
return -ENODEV;
}
up = kzalloc(sizeof(struct uflash_dev), GFP_KERNEL);
- if (!up)
+ if (!up) {
+ printk(KERN_ERR PFX "Cannot allocate struct uflash_dev\n");
return -ENOMEM;
+ }
/* copy defaults and tweak parameters */
memcpy(&up->map, &uflash_map_templ, sizeof(uflash_map_templ));
- up->map.size = (res->end - res->start) + 1UL;
+
+ up->map.size = resource_size(&op->resource[0]);
up->name = of_get_property(dp, "model", NULL);
if (up->name && 0 < strlen(up->name))
up->map.name = (char *)up->name;
- up->map.phys = res->start;
+ up->map.phys = op->resource[0].start;
- up->map.virt = ioremap_nocache(res->start, up->map.size);
+ up->map.virt = of_ioremap(&op->resource[0], 0, up->map.size,
+ DRIVER_NAME);
if (!up->map.virt) {
- printk("%s: Failed to map device.\n", dp->full_name);
+ printk(KERN_ERR PFX "Failed to map device.\n");
kfree(up);
return -EINVAL;
@@ -97,7 +92,7 @@
/* MTD registration */
up->mtd = do_map_probe("cfi_probe", &up->map);
if (!up->mtd) {
- iounmap(up->map.virt);
+ of_iounmap(&op->resource[0], up->map.virt, up->map.size);
kfree(up);
return -ENXIO;
@@ -107,32 +102,34 @@
add_mtd_device(up->mtd);
- dev_set_drvdata(&edev->ofdev.dev, up);
+ dev_set_drvdata(&op->dev, up);
return 0;
}
-static int __devinit uflash_probe(struct of_device *dev, const struct of_device_id *match)
+static int __devinit uflash_probe(struct of_device *op, const struct of_device_id *match)
{
- struct linux_ebus_device *edev = to_ebus_device(&dev->dev);
- struct device_node *dp = dev->node;
+ struct device_node *dp = op->node;
- if (of_find_property(dp, "user", NULL))
+ /* Flashprom must have the "user" property in order to
+ * be used by this driver.
+ */
+ if (!of_find_property(dp, "user", NULL))
return -ENODEV;
- return uflash_devinit(edev, dp);
+ return uflash_devinit(op, dp);
}
-static int __devexit uflash_remove(struct of_device *dev)
+static int __devexit uflash_remove(struct of_device *op)
{
- struct uflash_dev *up = dev_get_drvdata(&dev->dev);
+ struct uflash_dev *up = dev_get_drvdata(&op->dev);
if (up->mtd) {
del_mtd_device(up->mtd);
map_destroy(up->mtd);
}
if (up->map.virt) {
- iounmap(up->map.virt);
+ of_iounmap(&op->resource[0], up->map.virt, up->map.size);
up->map.virt = NULL;
}
@@ -141,7 +138,7 @@
return 0;
}
-static struct of_device_id uflash_match[] = {
+static const struct of_device_id uflash_match[] = {
{
.name = UFLASH_OBPNAME,
},
@@ -151,7 +148,7 @@
MODULE_DEVICE_TABLE(of, uflash_match);
static struct of_platform_driver uflash_driver = {
- .name = UFLASH_DEVNAME,
+ .name = DRIVER_NAME,
.match_table = uflash_match,
.probe = uflash_probe,
.remove = __devexit_p(uflash_remove),
@@ -159,7 +156,7 @@
static int __init uflash_init(void)
{
- return of_register_driver(&uflash_driver, &ebus_bus_type);
+ return of_register_driver(&uflash_driver, &of_bus_type);
}
static void __exit uflash_exit(void)
diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c
index 95345d0..b8064bf 100644
--- a/drivers/mtd/nand/cafe_nand.c
+++ b/drivers/mtd/nand/cafe_nand.c
@@ -1,6 +1,9 @@
/*
* Driver for One Laptop Per Child ‘CAFÉ’ controller, aka Marvell 88ALP01
*
+ * The data sheet for this device can be found at:
+ * http://www.marvell.com/products/pcconn/88ALP01.jsp
+ *
* Copyright © 2006 Red Hat, Inc.
* Copyright © 2006 David Woodhouse <dwmw2@infradead.org>
*/
@@ -842,7 +845,8 @@
}
static struct pci_device_id cafe_nand_tbl[] = {
- { 0x11ab, 0x4100, PCI_ANY_ID, PCI_ANY_ID },
+ { PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_88ALP01_NAND,
+ PCI_ANY_ID, PCI_ANY_ID },
{ }
};
diff --git a/drivers/net/myri_sbus.c b/drivers/net/myri_sbus.c
index 06e6823..3ad7589 100644
--- a/drivers/net/myri_sbus.c
+++ b/drivers/net/myri_sbus.c
@@ -1,6 +1,6 @@
/* myri_sbus.c: MyriCOM MyriNET SBUS card driver.
*
- * Copyright (C) 1996, 1999, 2006 David S. Miller (davem@davemloft.net)
+ * Copyright (C) 1996, 1999, 2006, 2008 David S. Miller (davem@davemloft.net)
*/
static char version[] =
@@ -22,6 +22,9 @@
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/bitops.h>
+#include <linux/dma-mapping.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <net/dst.h>
#include <net/arp.h>
@@ -33,7 +36,6 @@
#include <asm/dma.h>
#include <asm/byteorder.h>
#include <asm/idprom.h>
-#include <asm/sbus.h>
#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/auxio.h>
@@ -243,7 +245,8 @@
u32 dma_addr;
dma_addr = sbus_readl(&rxd->myri_scatters[0].addr);
- sbus_unmap_single(mp->myri_sdev, dma_addr, RX_ALLOC_SIZE, SBUS_DMA_FROMDEVICE);
+ dma_unmap_single(&mp->myri_op->dev, dma_addr,
+ RX_ALLOC_SIZE, DMA_FROM_DEVICE);
dev_kfree_skb(mp->rx_skbs[i]);
mp->rx_skbs[i] = NULL;
}
@@ -259,7 +262,9 @@
u32 dma_addr;
dma_addr = sbus_readl(&txd->myri_gathers[0].addr);
- sbus_unmap_single(mp->myri_sdev, dma_addr, (skb->len + 3) & ~3, SBUS_DMA_TODEVICE);
+ dma_unmap_single(&mp->myri_op->dev, dma_addr,
+ (skb->len + 3) & ~3,
+ DMA_TO_DEVICE);
dev_kfree_skb(mp->tx_skbs[i]);
mp->tx_skbs[i] = NULL;
}
@@ -288,7 +293,9 @@
skb->dev = dev;
skb_put(skb, RX_ALLOC_SIZE);
- dma_addr = sbus_map_single(mp->myri_sdev, skb->data, RX_ALLOC_SIZE, SBUS_DMA_FROMDEVICE);
+ dma_addr = dma_map_single(&mp->myri_op->dev,
+ skb->data, RX_ALLOC_SIZE,
+ DMA_FROM_DEVICE);
sbus_writel(dma_addr, &rxd[i].myri_scatters[0].addr);
sbus_writel(RX_ALLOC_SIZE, &rxd[i].myri_scatters[0].len);
sbus_writel(i, &rxd[i].ctx);
@@ -344,7 +351,8 @@
DTX(("SKB[%d] ", entry));
dma_addr = sbus_readl(&sq->myri_txd[entry].myri_gathers[0].addr);
- sbus_unmap_single(mp->myri_sdev, dma_addr, skb->len, SBUS_DMA_TODEVICE);
+ dma_unmap_single(&mp->myri_op->dev, dma_addr,
+ skb->len, DMA_TO_DEVICE);
dev_kfree_skb(skb);
mp->tx_skbs[entry] = NULL;
dev->stats.tx_packets++;
@@ -423,9 +431,9 @@
/* Check for errors. */
DRX(("rxd[%d]: %p len[%d] csum[%08x] ", entry, rxd, len, csum));
- sbus_dma_sync_single_for_cpu(mp->myri_sdev,
- sbus_readl(&rxd->myri_scatters[0].addr),
- RX_ALLOC_SIZE, SBUS_DMA_FROMDEVICE);
+ dma_sync_single_for_cpu(&mp->myri_op->dev,
+ sbus_readl(&rxd->myri_scatters[0].addr),
+ RX_ALLOC_SIZE, DMA_FROM_DEVICE);
if (len < (ETH_HLEN + MYRI_PAD_LEN) || (skb->data[0] != MYRI_PAD_LEN)) {
DRX(("ERROR["));
dev->stats.rx_errors++;
@@ -442,10 +450,10 @@
drops++;
DRX(("DROP "));
dev->stats.rx_dropped++;
- sbus_dma_sync_single_for_device(mp->myri_sdev,
- sbus_readl(&rxd->myri_scatters[0].addr),
- RX_ALLOC_SIZE,
- SBUS_DMA_FROMDEVICE);
+ dma_sync_single_for_device(&mp->myri_op->dev,
+ sbus_readl(&rxd->myri_scatters[0].addr),
+ RX_ALLOC_SIZE,
+ DMA_FROM_DEVICE);
sbus_writel(RX_ALLOC_SIZE, &rxd->myri_scatters[0].len);
sbus_writel(index, &rxd->ctx);
sbus_writel(1, &rxd->num_sg);
@@ -464,17 +472,17 @@
DRX(("skb_alloc(FAILED) "));
goto drop_it;
}
- sbus_unmap_single(mp->myri_sdev,
- sbus_readl(&rxd->myri_scatters[0].addr),
- RX_ALLOC_SIZE,
- SBUS_DMA_FROMDEVICE);
+ dma_unmap_single(&mp->myri_op->dev,
+ sbus_readl(&rxd->myri_scatters[0].addr),
+ RX_ALLOC_SIZE,
+ DMA_FROM_DEVICE);
mp->rx_skbs[index] = new_skb;
new_skb->dev = dev;
skb_put(new_skb, RX_ALLOC_SIZE);
- dma_addr = sbus_map_single(mp->myri_sdev,
- new_skb->data,
- RX_ALLOC_SIZE,
- SBUS_DMA_FROMDEVICE);
+ dma_addr = dma_map_single(&mp->myri_op->dev,
+ new_skb->data,
+ RX_ALLOC_SIZE,
+ DMA_FROM_DEVICE);
sbus_writel(dma_addr, &rxd->myri_scatters[0].addr);
sbus_writel(RX_ALLOC_SIZE, &rxd->myri_scatters[0].len);
sbus_writel(index, &rxd->ctx);
@@ -500,10 +508,10 @@
/* Reuse original ring buffer. */
DRX(("reuse "));
- sbus_dma_sync_single_for_device(mp->myri_sdev,
- sbus_readl(&rxd->myri_scatters[0].addr),
- RX_ALLOC_SIZE,
- SBUS_DMA_FROMDEVICE);
+ dma_sync_single_for_device(&mp->myri_op->dev,
+ sbus_readl(&rxd->myri_scatters[0].addr),
+ RX_ALLOC_SIZE,
+ DMA_FROM_DEVICE);
sbus_writel(RX_ALLOC_SIZE, &rxd->myri_scatters[0].len);
sbus_writel(index, &rxd->ctx);
sbus_writel(1, &rxd->num_sg);
@@ -652,7 +660,8 @@
sbus_writew((skb->data[4] << 8) | skb->data[5], &txd->addr[3]);
}
- dma_addr = sbus_map_single(mp->myri_sdev, skb->data, len, SBUS_DMA_TODEVICE);
+ dma_addr = dma_map_single(&mp->myri_op->dev, skb->data,
+ len, DMA_TO_DEVICE);
sbus_writel(dma_addr, &txd->myri_gathers[0].addr);
sbus_writel(len, &txd->myri_gathers[0].len);
sbus_writel(1, &txd->num_sg);
@@ -891,30 +900,30 @@
.cache_update = myri_header_cache_update,
};
-static int __devinit myri_ether_init(struct sbus_dev *sdev)
+static int __devinit myri_sbus_probe(struct of_device *op, const struct of_device_id *match)
{
- static int num;
+ struct device_node *dp = op->node;
static unsigned version_printed;
struct net_device *dev;
- struct myri_eth *mp;
- unsigned char prop_buf[32];
- int i;
DECLARE_MAC_BUF(mac);
+ struct myri_eth *mp;
+ const void *prop;
+ static int num;
+ int i, len;
- DET(("myri_ether_init(%p,%d):\n", sdev, num));
+ DET(("myri_ether_init(%p,%d):\n", op, num));
dev = alloc_etherdev(sizeof(struct myri_eth));
-
if (!dev)
return -ENOMEM;
if (version_printed++ == 0)
printk(version);
- SET_NETDEV_DEV(dev, &sdev->ofdev.dev);
+ SET_NETDEV_DEV(dev, &op->dev);
- mp = (struct myri_eth *) dev->priv;
+ mp = netdev_priv(dev);
spin_lock_init(&mp->irq_lock);
- mp->myri_sdev = sdev;
+ mp->myri_op = op;
/* Clean out skb arrays. */
for (i = 0; i < (RX_RING_SIZE + 1); i++)
@@ -924,55 +933,44 @@
mp->tx_skbs[i] = NULL;
/* First check for EEPROM information. */
- i = prom_getproperty(sdev->prom_node, "myrinet-eeprom-info",
- (char *)&mp->eeprom, sizeof(struct myri_eeprom));
- DET(("prom_getprop(myrinet-eeprom-info) returns %d\n", i));
- if (i == 0 || i == -1) {
+ prop = of_get_property(dp, "myrinet-eeprom-info", &len);
+
+ if (prop)
+ memcpy(&mp->eeprom, prop, sizeof(struct myri_eeprom));
+ if (!prop) {
/* No eeprom property, must cook up the values ourselves. */
DET(("No EEPROM: "));
mp->eeprom.bus_type = BUS_TYPE_SBUS;
- mp->eeprom.cpuvers = prom_getintdefault(sdev->prom_node,"cpu_version",0);
- mp->eeprom.cval = prom_getintdefault(sdev->prom_node,"clock_value",0);
- mp->eeprom.ramsz = prom_getintdefault(sdev->prom_node,"sram_size",0);
- DET(("cpuvers[%d] cval[%d] ramsz[%d]\n", mp->eeprom.cpuvers,
- mp->eeprom.cval, mp->eeprom.ramsz));
- if (mp->eeprom.cpuvers == 0) {
- DET(("EEPROM: cpuvers was zero, setting to %04x\n",CPUVERS_2_3));
+ mp->eeprom.cpuvers =
+ of_getintprop_default(dp, "cpu_version", 0);
+ mp->eeprom.cval =
+ of_getintprop_default(dp, "clock_value", 0);
+ mp->eeprom.ramsz = of_getintprop_default(dp, "sram_size", 0);
+ if (!mp->eeprom.cpuvers)
mp->eeprom.cpuvers = CPUVERS_2_3;
- }
- if (mp->eeprom.cpuvers < CPUVERS_3_0) {
- DET(("EEPROM: cpuvers < CPUVERS_3_0, clockval set to zero.\n"));
+ if (mp->eeprom.cpuvers < CPUVERS_3_0)
mp->eeprom.cval = 0;
- }
- if (mp->eeprom.ramsz == 0) {
- DET(("EEPROM: ramsz == 0, setting to 128k\n"));
+ if (!mp->eeprom.ramsz)
mp->eeprom.ramsz = (128 * 1024);
- }
- i = prom_getproperty(sdev->prom_node, "myrinet-board-id",
- &prop_buf[0], 10);
- DET(("EEPROM: prom_getprop(myrinet-board-id) returns %d\n", i));
- if ((i != 0) && (i != -1))
- memcpy(&mp->eeprom.id[0], &prop_buf[0], 6);
+
+ prop = of_get_property(dp, "myrinet-board-id", &len);
+ if (prop)
+ memcpy(&mp->eeprom.id[0], prop, 6);
else
set_boardid_from_idprom(mp, num);
- i = prom_getproperty(sdev->prom_node, "fpga_version",
- &mp->eeprom.fvers[0], 32);
- DET(("EEPROM: prom_getprop(fpga_version) returns %d\n", i));
- if (i == 0 || i == -1)
+
+ prop = of_get_property(dp, "fpga_version", &len);
+ if (prop)
+ memcpy(&mp->eeprom.fvers[0], prop, 32);
+ else
memset(&mp->eeprom.fvers[0], 0, 32);
if (mp->eeprom.cpuvers == CPUVERS_4_1) {
- DET(("EEPROM: cpuvers CPUVERS_4_1, "));
- if (mp->eeprom.ramsz == (128 * 1024)) {
- DET(("ramsize 128k, setting to 256k, "));
+ if (mp->eeprom.ramsz == (128 * 1024))
mp->eeprom.ramsz = (256 * 1024);
- }
- if ((mp->eeprom.cval==0x40414041)||(mp->eeprom.cval==0x90449044)){
- DET(("changing cval from %08x to %08x ",
- mp->eeprom.cval, 0x50e450e4));
+ if ((mp->eeprom.cval == 0x40414041) ||
+ (mp->eeprom.cval == 0x90449044))
mp->eeprom.cval = 0x50e450e4;
- }
- DET(("\n"));
}
}
#ifdef DEBUG_DETECT
@@ -991,8 +989,8 @@
* XXX only a valid version for PCI cards? Ask feldy...
*/
DET(("Mapping regs for cpuvers < CPUVERS_4_0\n"));
- mp->regs = sbus_ioremap(&sdev->resource[0], 0,
- mp->reg_size, "MyriCOM Regs");
+ mp->regs = of_ioremap(&op->resource[0], 0,
+ mp->reg_size, "MyriCOM Regs");
if (!mp->regs) {
printk("MyriCOM: Cannot map MyriCOM registers.\n");
goto err;
@@ -1001,13 +999,12 @@
mp->lregs = mp->lanai + (0x10000 * 2);
} else {
DET(("Mapping regs for cpuvers >= CPUVERS_4_0\n"));
- mp->cregs = sbus_ioremap(&sdev->resource[0], 0,
- PAGE_SIZE, "MyriCOM Control Regs");
- mp->lregs = sbus_ioremap(&sdev->resource[0], (256 * 1024),
+ mp->cregs = of_ioremap(&op->resource[0], 0,
+ PAGE_SIZE, "MyriCOM Control Regs");
+ mp->lregs = of_ioremap(&op->resource[0], (256 * 1024),
PAGE_SIZE, "MyriCOM LANAI Regs");
- mp->lanai =
- sbus_ioremap(&sdev->resource[0], (512 * 1024),
- mp->eeprom.ramsz, "MyriCOM SRAM");
+ mp->lanai = of_ioremap(&op->resource[0], (512 * 1024),
+ mp->eeprom.ramsz, "MyriCOM SRAM");
}
DET(("Registers mapped: cregs[%p] lregs[%p] lanai[%p]\n",
mp->cregs, mp->lregs, mp->lanai));
@@ -1039,16 +1036,15 @@
myri_reset_on(mp->cregs);
/* Get the supported DVMA burst sizes from our SBUS. */
- mp->myri_bursts = prom_getintdefault(mp->myri_sdev->bus->prom_node,
- "burst-sizes", 0x00);
-
- if (!sbus_can_burst64(sdev))
+ mp->myri_bursts = of_getintprop_default(dp->parent,
+ "burst-sizes", 0x00);
+ if (!sbus_can_burst64())
mp->myri_bursts &= ~(DMA_BURST64);
DET(("MYRI bursts %02x\n", mp->myri_bursts));
/* Encode SBUS interrupt level in second control register. */
- i = prom_getint(sdev->prom_node, "interrupts");
+ i = of_getintprop_default(dp, "interrupts", 0);
if (i == 0)
i = 4;
DET(("prom_getint(interrupts)==%d, irqlvl set to %04x\n",
@@ -1063,7 +1059,7 @@
dev->tx_timeout = &myri_tx_timeout;
dev->watchdog_timeo = 5*HZ;
dev->set_multicast_list = &myri_set_multicast;
- dev->irq = sdev->irqs[0];
+ dev->irq = op->irqs[0];
/* Register interrupt handler now. */
DET(("Requesting MYRIcom IRQ line.\n"));
@@ -1088,7 +1084,7 @@
goto err_free_irq;
}
- dev_set_drvdata(&sdev->ofdev.dev, mp);
+ dev_set_drvdata(&op->dev, mp);
num++;
@@ -1105,17 +1101,9 @@
return -ENODEV;
}
-
-static int __devinit myri_sbus_probe(struct of_device *dev, const struct of_device_id *match)
+static int __devexit myri_sbus_remove(struct of_device *op)
{
- struct sbus_dev *sdev = to_sbus_device(&dev->dev);
-
- return myri_ether_init(sdev);
-}
-
-static int __devexit myri_sbus_remove(struct of_device *dev)
-{
- struct myri_eth *mp = dev_get_drvdata(&dev->dev);
+ struct myri_eth *mp = dev_get_drvdata(&op->dev);
struct net_device *net_dev = mp->dev;
unregister_netdev(net_dev);
@@ -1123,21 +1111,21 @@
free_irq(net_dev->irq, net_dev);
if (mp->eeprom.cpuvers < CPUVERS_4_0) {
- sbus_iounmap(mp->regs, mp->reg_size);
+ of_iounmap(&op->resource[0], mp->regs, mp->reg_size);
} else {
- sbus_iounmap(mp->cregs, PAGE_SIZE);
- sbus_iounmap(mp->lregs, (256 * 1024));
- sbus_iounmap(mp->lanai, (512 * 1024));
+ of_iounmap(&op->resource[0], mp->cregs, PAGE_SIZE);
+ of_iounmap(&op->resource[0], mp->lregs, (256 * 1024));
+ of_iounmap(&op->resource[0], mp->lanai, (512 * 1024));
}
free_netdev(net_dev);
- dev_set_drvdata(&dev->dev, NULL);
+ dev_set_drvdata(&op->dev, NULL);
return 0;
}
-static struct of_device_id myri_sbus_match[] = {
+static const struct of_device_id myri_sbus_match[] = {
{
.name = "MYRICOM,mlanai",
},
@@ -1158,7 +1146,7 @@
static int __init myri_sbus_init(void)
{
- return of_register_driver(&myri_sbus_driver, &sbus_bus_type);
+ return of_register_driver(&myri_sbus_driver, &of_bus_type);
}
static void __exit myri_sbus_exit(void)
diff --git a/drivers/net/myri_sbus.h b/drivers/net/myri_sbus.h
index 5d93fcc..ff363e9 100644
--- a/drivers/net/myri_sbus.h
+++ b/drivers/net/myri_sbus.h
@@ -288,7 +288,7 @@
struct myri_eeprom eeprom; /* Local copy of EEPROM. */
unsigned int reg_size; /* Size of register space. */
unsigned int shmem_base; /* Offset to shared ram. */
- struct sbus_dev *myri_sdev; /* Our SBUS device struct. */
+ struct of_device *myri_op; /* Our OF device struct. */
};
/* We use this to acquire receive skb's that we can DMA directly into. */
diff --git a/drivers/net/niu.c b/drivers/net/niu.c
index e3be81e..ebc8127 100644
--- a/drivers/net/niu.c
+++ b/drivers/net/niu.c
@@ -9130,7 +9130,7 @@
return 0;
}
-static struct of_device_id niu_match[] = {
+static const struct of_device_id niu_match[] = {
{
.name = "network",
.compatible = "SUNW,niusl",
diff --git a/drivers/net/pcmcia/3c574_cs.c b/drivers/net/pcmcia/3c574_cs.c
index 7112fd5..08c4dd8 100644
--- a/drivers/net/pcmcia/3c574_cs.c
+++ b/drivers/net/pcmcia/3c574_cs.c
@@ -355,9 +355,10 @@
for (i = j = 0; j < 0x400; j += 0x20) {
link->io.BasePort1 = j ^ 0x300;
i = pcmcia_request_io(link, &link->io);
- if (i == CS_SUCCESS) break;
+ if (i == 0)
+ break;
}
- if (i != CS_SUCCESS) {
+ if (i != 0) {
cs_error(link, RequestIO, i);
goto failed;
}
@@ -377,7 +378,7 @@
tuple.TupleDataMax = 64;
tuple.TupleOffset = 0;
tuple.DesiredTuple = 0x88;
- if (pcmcia_get_first_tuple(link, &tuple) == CS_SUCCESS) {
+ if (pcmcia_get_first_tuple(link, &tuple) == 0) {
pcmcia_get_tuple_data(link, &tuple);
for (i = 0; i < 3; i++)
phys_addr[i] = htons(le16_to_cpu(buf[i]));
diff --git a/drivers/net/pcmcia/3c589_cs.c b/drivers/net/pcmcia/3c589_cs.c
index 29bd853..c235cdb 100644
--- a/drivers/net/pcmcia/3c589_cs.c
+++ b/drivers/net/pcmcia/3c589_cs.c
@@ -278,9 +278,10 @@
if (multi && (j & 0x80)) continue;
link->io.BasePort1 = j ^ 0x300;
i = pcmcia_request_io(link, &link->io);
- if (i == CS_SUCCESS) break;
+ if (i == 0)
+ break;
}
- if (i != CS_SUCCESS) {
+ if (i != 0) {
cs_error(link, RequestIO, i);
goto failed;
}
@@ -295,7 +296,7 @@
/* The 3c589 has an extra EEPROM for configuration info, including
the hardware address. The 3c562 puts the address in the CIS. */
tuple.DesiredTuple = 0x88;
- if (pcmcia_get_first_tuple(link, &tuple) == CS_SUCCESS) {
+ if (pcmcia_get_first_tuple(link, &tuple) == 0) {
pcmcia_get_tuple_data(link, &tuple);
for (i = 0; i < 3; i++)
phys_addr[i] = htons(le16_to_cpu(buf[i]));
diff --git a/drivers/net/pcmcia/axnet_cs.c b/drivers/net/pcmcia/axnet_cs.c
index 52bf11b..b37a498 100644
--- a/drivers/net/pcmcia/axnet_cs.c
+++ b/drivers/net/pcmcia/axnet_cs.c
@@ -262,7 +262,7 @@
if (link->io.NumPorts2 > 0) {
/* for master/slave multifunction cards */
link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
- link->irq.Attributes =
+ link->irq.Attributes =
IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
}
} else {
@@ -276,7 +276,8 @@
link->io.BasePort1 = j ^ 0x300;
link->io.BasePort2 = (j ^ 0x300) + 0x10;
ret = pcmcia_request_io(link, &link->io);
- if (ret == CS_SUCCESS) return ret;
+ if (ret == 0)
+ return ret;
}
return ret;
} else {
@@ -284,59 +285,50 @@
}
}
+static int axnet_configcheck(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ int i;
+ cistpl_io_t *io = &cfg->io;
+
+ if (cfg->index == 0 || cfg->io.nwin == 0)
+ return -ENODEV;
+
+ p_dev->conf.ConfigIndex = 0x05;
+ /* For multifunction cards, by convention, we configure the
+ network function with window 0, and serial with window 1 */
+ if (io->nwin > 1) {
+ i = (io->win[1].len > io->win[0].len);
+ p_dev->io.BasePort2 = io->win[1-i].base;
+ p_dev->io.NumPorts2 = io->win[1-i].len;
+ } else {
+ i = p_dev->io.NumPorts2 = 0;
+ }
+ p_dev->io.BasePort1 = io->win[i].base;
+ p_dev->io.NumPorts1 = io->win[i].len;
+ p_dev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+ if (p_dev->io.NumPorts1 + p_dev->io.NumPorts2 >= 32)
+ return try_io_port(p_dev);
+
+ return -ENODEV;
+}
+
static int axnet_config(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
axnet_dev_t *info = PRIV(dev);
- tuple_t tuple;
- cisparse_t parse;
int i, j, last_ret, last_fn;
- u_short buf[64];
DECLARE_MAC_BUF(mac);
DEBUG(0, "axnet_config(0x%p)\n", link);
- tuple.Attributes = 0;
- tuple.TupleData = (cisdata_t *)buf;
- tuple.TupleDataMax = sizeof(buf);
- tuple.TupleOffset = 0;
-
/* don't trust the CIS on this; Linksys got it wrong */
link->conf.Present = 0x63;
-
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- tuple.Attributes = 0;
- CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
- while (last_ret == CS_SUCCESS) {
- cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
- cistpl_io_t *io = &(parse.cftable_entry.io);
-
- if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
- pcmcia_parse_tuple(link, &tuple, &parse) != 0 ||
- cfg->index == 0 || cfg->io.nwin == 0)
- goto next_entry;
-
- link->conf.ConfigIndex = 0x05;
- /* For multifunction cards, by convention, we configure the
- network function with window 0, and serial with window 1 */
- if (io->nwin > 1) {
- i = (io->win[1].len > io->win[0].len);
- link->io.BasePort2 = io->win[1-i].base;
- link->io.NumPorts2 = io->win[1-i].len;
- } else {
- i = link->io.NumPorts2 = 0;
- }
- link->io.BasePort1 = io->win[i].base;
- link->io.NumPorts1 = io->win[i].len;
- link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
- if (link->io.NumPorts1 + link->io.NumPorts2 >= 32) {
- last_ret = try_io_port(link);
- if (last_ret == CS_SUCCESS) break;
- }
- next_entry:
- last_ret = pcmcia_get_next_tuple(link, &tuple);
- }
- if (last_ret != CS_SUCCESS) {
+ last_ret = pcmcia_loop_config(link, axnet_configcheck, NULL);
+ if (last_ret != 0) {
cs_error(link, RequestIO, last_ret);
goto failed;
}
diff --git a/drivers/net/pcmcia/com20020_cs.c b/drivers/net/pcmcia/com20020_cs.c
index ea9414c..831090c 100644
--- a/drivers/net/pcmcia/com20020_cs.c
+++ b/drivers/net/pcmcia/com20020_cs.c
@@ -260,21 +260,21 @@
DEBUG(0, "com20020_config(0x%p)\n", link);
DEBUG(1,"arcnet: baseport1 is %Xh\n", link->io.BasePort1);
- i = !CS_SUCCESS;
+ i = -ENODEV;
if (!link->io.BasePort1)
{
for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x10)
{
link->io.BasePort1 = ioaddr;
i = pcmcia_request_io(link, &link->io);
- if (i == CS_SUCCESS)
+ if (i == 0)
break;
}
}
else
i = pcmcia_request_io(link, &link->io);
- if (i != CS_SUCCESS)
+ if (i != 0)
{
DEBUG(1,"arcnet: requestIO failed totally!\n");
goto failed;
@@ -287,7 +287,7 @@
link->irq.AssignedIRQ,
link->irq.IRQInfo1, link->irq.IRQInfo2);
i = pcmcia_request_irq(link, &link->irq);
- if (i != CS_SUCCESS)
+ if (i != 0)
{
DEBUG(1,"arcnet: requestIRQ failed totally!\n");
goto failed;
diff --git a/drivers/net/pcmcia/fmvj18x_cs.c b/drivers/net/pcmcia/fmvj18x_cs.c
index a550c9b..69d916d 100644
--- a/drivers/net/pcmcia/fmvj18x_cs.c
+++ b/drivers/net/pcmcia/fmvj18x_cs.c
@@ -309,7 +309,8 @@
printk(KERN_NOTICE "fmvj18x_cs: out of resource for serial\n");
}
ret = pcmcia_request_io(link, &link->io);
- if (ret == CS_SUCCESS) return ret;
+ if (ret == 0)
+ return ret;
}
return ret;
}
@@ -325,7 +326,7 @@
for (ioaddr = 0x300; ioaddr < 0x3e0; ioaddr += 0x20) {
link->io.BasePort1 = ioaddr;
ret = pcmcia_request_io(link, &link->io);
- if (ret == CS_SUCCESS) {
+ if (ret == 0) {
/* calculate ConfigIndex value */
link->conf.ConfigIndex =
((link->io.BasePort1 & 0x0f0) >> 3) | 0x22;
@@ -356,12 +357,12 @@
tuple.TupleOffset = 0;
tuple.DesiredTuple = CISTPL_FUNCE;
tuple.TupleOffset = 0;
- if (pcmcia_get_first_tuple(link, &tuple) == CS_SUCCESS) {
+ if (pcmcia_get_first_tuple(link, &tuple) == 0) {
/* Yes, I have CISTPL_FUNCE. Let's check CISTPL_MANFID */
tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
- CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
+ CS_CHECK(ParseTuple, pcmcia_parse_tuple(&tuple, &parse));
link->conf.ConfigIndex = parse.cftable_entry.index;
switch (link->manf_id) {
case MANFID_TDK:
@@ -430,10 +431,10 @@
link->irq.Attributes =
IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED|IRQ_HANDLE_PRESENT;
ret = mfc_try_io_port(link);
- if (ret != CS_SUCCESS) goto cs_failed;
+ if (ret != 0) goto cs_failed;
} else if (cardtype == UNGERMANN) {
ret = ungermann_try_io_port(link);
- if (ret != CS_SUCCESS) goto cs_failed;
+ if (ret != 0) goto cs_failed;
} else {
CS_CHECK(RequestIO, pcmcia_request_io(link, &link->io));
}
@@ -565,7 +566,7 @@
req.Base = 0; req.Size = 0;
req.AccessSpeed = 0;
i = pcmcia_request_window(&link, &req, &link->win);
- if (i != CS_SUCCESS) {
+ if (i != 0) {
cs_error(link, RequestWindow, i);
return -1;
}
@@ -599,7 +600,7 @@
iounmap(base);
j = pcmcia_release_window(link->win);
- if (j != CS_SUCCESS)
+ if (j != 0)
cs_error(link, ReleaseWindow, j);
return (i != 0x200) ? 0 : -1;
@@ -620,7 +621,7 @@
req.Base = 0; req.Size = 0;
req.AccessSpeed = 0;
i = pcmcia_request_window(&link, &req, &link->win);
- if (i != CS_SUCCESS) {
+ if (i != 0) {
cs_error(link, RequestWindow, i);
return -1;
}
@@ -642,7 +643,7 @@
iounmap(base);
j = pcmcia_release_window(link->win);
- if (j != CS_SUCCESS)
+ if (j != 0)
cs_error(link, ReleaseWindow, j);
return 0;
diff --git a/drivers/net/pcmcia/ibmtr_cs.c b/drivers/net/pcmcia/ibmtr_cs.c
index 4eafa4f..cf3cca4 100644
--- a/drivers/net/pcmcia/ibmtr_cs.c
+++ b/drivers/net/pcmcia/ibmtr_cs.c
@@ -238,7 +238,7 @@
/* Try PRIMARY card at 0xA20-0xA23 */
link->io.BasePort1 = 0xA20;
i = pcmcia_request_io(link, &link->io);
- if (i != CS_SUCCESS) {
+ if (i != 0) {
/* Couldn't get 0xA20-0xA23. Try ALTERNATE at 0xA24-0xA27. */
link->io.BasePort1 = 0xA24;
CS_CHECK(RequestIO, pcmcia_request_io(link, &link->io));
diff --git a/drivers/net/pcmcia/nmclan_cs.c b/drivers/net/pcmcia/nmclan_cs.c
index ee5dcbf..448cd40 100644
--- a/drivers/net/pcmcia/nmclan_cs.c
+++ b/drivers/net/pcmcia/nmclan_cs.c
@@ -925,7 +925,7 @@
printk(KERN_NOTICE "%s: transmit timed out -- ", dev->name);
#if RESET_ON_TIMEOUT
printk("resetting card\n");
- pcmcia_reset_card(link, NULL);
+ pcmcia_reset_card(link->socket);
#else /* #if RESET_ON_TIMEOUT */
printk("NOT resetting card\n");
#endif /* #if RESET_ON_TIMEOUT */
diff --git a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c
index ebc1ae6..e40d630 100644
--- a/drivers/net/pcmcia/pcnet_cs.c
+++ b/drivers/net/pcmcia/pcnet_cs.c
@@ -310,7 +310,7 @@
req.Base = 0; req.Size = 0;
req.AccessSpeed = 0;
i = pcmcia_request_window(&link, &req, &link->win);
- if (i != CS_SUCCESS) {
+ if (i != 0) {
cs_error(link, RequestWindow, i);
return NULL;
}
@@ -333,7 +333,7 @@
iounmap(virt);
j = pcmcia_release_window(link->win);
- if (j != CS_SUCCESS)
+ if (j != 0)
cs_error(link, ReleaseWindow, j);
return (i < NR_INFO) ? hw_info+i : NULL;
} /* get_hwinfo */
@@ -504,7 +504,8 @@
link->io.BasePort1 = j ^ 0x300;
link->io.BasePort2 = (j ^ 0x300) + 0x10;
ret = pcmcia_request_io(link, &link->io);
- if (ret == CS_SUCCESS) return ret;
+ if (ret == 0)
+ return ret;
}
return ret;
} else {
@@ -512,58 +513,53 @@
}
}
+static int pcnet_confcheck(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ int *has_shmem = priv_data;
+ int i;
+ cistpl_io_t *io = &cfg->io;
+
+ if (cfg->index == 0 || cfg->io.nwin == 0)
+ return -EINVAL;
+
+ /* For multifunction cards, by convention, we configure the
+ network function with window 0, and serial with window 1 */
+ if (io->nwin > 1) {
+ i = (io->win[1].len > io->win[0].len);
+ p_dev->io.BasePort2 = io->win[1-i].base;
+ p_dev->io.NumPorts2 = io->win[1-i].len;
+ } else {
+ i = p_dev->io.NumPorts2 = 0;
+ }
+
+ *has_shmem = ((cfg->mem.nwin == 1) &&
+ (cfg->mem.win[0].len >= 0x4000));
+ p_dev->io.BasePort1 = io->win[i].base;
+ p_dev->io.NumPorts1 = io->win[i].len;
+ p_dev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+ if (p_dev->io.NumPorts1 + p_dev->io.NumPorts2 >= 32)
+ return try_io_port(p_dev);
+
+ return 0;
+}
+
static int pcnet_config(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
pcnet_dev_t *info = PRIV(dev);
- tuple_t tuple;
- cisparse_t parse;
- int i, last_ret, last_fn, start_pg, stop_pg, cm_offset;
+ int last_ret, last_fn, start_pg, stop_pg, cm_offset;
int has_shmem = 0;
- u_short buf[64];
hw_info_t *local_hw_info;
DECLARE_MAC_BUF(mac);
DEBUG(0, "pcnet_config(0x%p)\n", link);
- tuple.TupleData = (cisdata_t *)buf;
- tuple.TupleDataMax = sizeof(buf);
- tuple.TupleOffset = 0;
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- tuple.Attributes = 0;
- CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
- while (last_ret == CS_SUCCESS) {
- cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
- cistpl_io_t *io = &(parse.cftable_entry.io);
-
- if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
- pcmcia_parse_tuple(link, &tuple, &parse) != 0 ||
- cfg->index == 0 || cfg->io.nwin == 0)
- goto next_entry;
-
- link->conf.ConfigIndex = cfg->index;
- /* For multifunction cards, by convention, we configure the
- network function with window 0, and serial with window 1 */
- if (io->nwin > 1) {
- i = (io->win[1].len > io->win[0].len);
- link->io.BasePort2 = io->win[1-i].base;
- link->io.NumPorts2 = io->win[1-i].len;
- } else {
- i = link->io.NumPorts2 = 0;
- }
- has_shmem = ((cfg->mem.nwin == 1) &&
- (cfg->mem.win[0].len >= 0x4000));
- link->io.BasePort1 = io->win[i].base;
- link->io.NumPorts1 = io->win[i].len;
- link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
- if (link->io.NumPorts1 + link->io.NumPorts2 >= 32) {
- last_ret = try_io_port(link);
- if (last_ret == CS_SUCCESS) break;
- }
- next_entry:
- last_ret = pcmcia_get_next_tuple(link, &tuple);
- }
- if (last_ret != CS_SUCCESS) {
+ last_ret = pcmcia_loop_config(link, pcnet_confcheck, &has_shmem);
+ if (last_ret) {
cs_error(link, RequestIO, last_ret);
goto failed;
}
diff --git a/drivers/net/pcmcia/smc91c92_cs.c b/drivers/net/pcmcia/smc91c92_cs.c
index 250eb19..c74d665 100644
--- a/drivers/net/pcmcia/smc91c92_cs.c
+++ b/drivers/net/pcmcia/smc91c92_cs.c
@@ -409,10 +409,13 @@
{
int i;
- if ((i = pcmcia_get_first_tuple(handle, tuple)) != CS_SUCCESS ||
- (i = pcmcia_get_tuple_data(handle, tuple)) != CS_SUCCESS)
+ i = pcmcia_get_first_tuple(handle, tuple);
+ if (i != 0)
return i;
- return pcmcia_parse_tuple(handle, tuple, parse);
+ i = pcmcia_get_tuple_data(handle, tuple);
+ if (i != 0)
+ return i;
+ return pcmcia_parse_tuple(tuple, parse);
}
static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple,
@@ -420,10 +423,10 @@
{
int i;
- if ((i = pcmcia_get_next_tuple(handle, tuple)) != CS_SUCCESS ||
- (i = pcmcia_get_tuple_data(handle, tuple)) != CS_SUCCESS)
+ if ((i = pcmcia_get_next_tuple(handle, tuple)) != 0 ||
+ (i = pcmcia_get_tuple_data(handle, tuple)) != 0)
return i;
- return pcmcia_parse_tuple(handle, tuple, parse);
+ return pcmcia_parse_tuple(tuple, parse);
}
/*======================================================================
@@ -459,27 +462,36 @@
return 0;
}
+static int mhz_mfc_config_check(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ int k;
+ p_dev->io.BasePort2 = cf->io.win[0].base;
+ for (k = 0; k < 0x400; k += 0x10) {
+ if (k & 0x80)
+ continue;
+ p_dev->io.BasePort1 = k ^ 0x300;
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+ return 0;
+ }
+ return -ENODEV;
+}
+
static int mhz_mfc_config(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
struct smc_private *smc = netdev_priv(dev);
struct smc_cfg_mem *cfg_mem;
- tuple_t *tuple;
- cisparse_t *parse;
- cistpl_cftable_entry_t *cf;
- u_char *buf;
win_req_t req;
memreq_t mem;
- int i, k;
+ int i;
cfg_mem = kmalloc(sizeof(struct smc_cfg_mem), GFP_KERNEL);
if (!cfg_mem)
- return CS_OUT_OF_RESOURCE;
-
- tuple = &cfg_mem->tuple;
- parse = &cfg_mem->parse;
- cf = &parse->cftable_entry;
- buf = cfg_mem->buf;
+ return -ENOMEM;
link->conf.Attributes |= CONF_ENABLE_SPKR;
link->conf.Status = CCSR_AUDIO_ENA;
@@ -489,27 +501,9 @@
link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
link->io.NumPorts2 = 8;
- tuple->Attributes = tuple->TupleOffset = 0;
- tuple->TupleData = (cisdata_t *)buf;
- tuple->TupleDataMax = 255;
- tuple->DesiredTuple = CISTPL_CFTABLE_ENTRY;
-
- i = first_tuple(link, tuple, parse);
/* The Megahertz combo cards have modem-like CIS entries, so
we have to explicitly try a bunch of port combinations. */
- while (i == CS_SUCCESS) {
- link->conf.ConfigIndex = cf->index;
- link->io.BasePort2 = cf->io.win[0].base;
- for (k = 0; k < 0x400; k += 0x10) {
- if (k & 0x80) continue;
- link->io.BasePort1 = k ^ 0x300;
- i = pcmcia_request_io(link, &link->io);
- if (i == CS_SUCCESS) break;
- }
- if (i == CS_SUCCESS) break;
- i = next_tuple(link, tuple, parse);
- }
- if (i != CS_SUCCESS)
+ if (pcmcia_loop_config(link, mhz_mfc_config_check, NULL))
goto free_cfg_mem;
dev->base_addr = link->io.BasePort1;
@@ -518,7 +512,7 @@
req.Base = req.Size = 0;
req.AccessSpeed = 0;
i = pcmcia_request_window(&link, &req, &link->win);
- if (i != CS_SUCCESS)
+ if (i != 0)
goto free_cfg_mem;
smc->base = ioremap(req.Base, req.Size);
mem.CardOffset = mem.Page = 0;
@@ -526,14 +520,14 @@
mem.CardOffset = link->conf.ConfigBase;
i = pcmcia_map_mem_page(link->win, &mem);
- if ((i == CS_SUCCESS)
+ if ((i == 0)
&& (smc->manfid == MANFID_MEGAHERTZ)
&& (smc->cardid == PRODID_MEGAHERTZ_EM3288))
mhz_3288_power(link);
free_cfg_mem:
kfree(cfg_mem);
- return i;
+ return -ENODEV;
}
static int mhz_setup(struct pcmcia_device *link)
@@ -560,12 +554,12 @@
/* Read the station address from the CIS. It is stored as the last
(fourth) string in the Version 1 Version/ID tuple. */
tuple->DesiredTuple = CISTPL_VERS_1;
- if (first_tuple(link, tuple, parse) != CS_SUCCESS) {
+ if (first_tuple(link, tuple, parse) != 0) {
rc = -1;
goto free_cfg_mem;
}
/* Ugh -- the EM1144 card has two VERS_1 tuples!?! */
- if (next_tuple(link, tuple, parse) != CS_SUCCESS)
+ if (next_tuple(link, tuple, parse) != 0)
first_tuple(link, tuple, parse);
if (parse->version_1.ns > 3) {
station_addr = parse->version_1.str + parse->version_1.ofs[3];
@@ -577,11 +571,11 @@
/* Another possibility: for the EM3288, in a special tuple */
tuple->DesiredTuple = 0x81;
- if (pcmcia_get_first_tuple(link, tuple) != CS_SUCCESS) {
+ if (pcmcia_get_first_tuple(link, tuple) != 0) {
rc = -1;
goto free_cfg_mem;
}
- if (pcmcia_get_tuple_data(link, tuple) != CS_SUCCESS) {
+ if (pcmcia_get_tuple_data(link, tuple) != 0) {
rc = -1;
goto free_cfg_mem;
}
@@ -660,46 +654,27 @@
/*====================================================================*/
+static int smc_configcheck(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ p_dev->io.BasePort1 = cf->io.win[0].base;
+ p_dev->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK;
+ return pcmcia_request_io(p_dev, &p_dev->io);
+}
+
static int smc_config(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
- struct smc_cfg_mem *cfg_mem;
- tuple_t *tuple;
- cisparse_t *parse;
- cistpl_cftable_entry_t *cf;
- u_char *buf;
int i;
- cfg_mem = kmalloc(sizeof(struct smc_cfg_mem), GFP_KERNEL);
- if (!cfg_mem)
- return CS_OUT_OF_RESOURCE;
-
- tuple = &cfg_mem->tuple;
- parse = &cfg_mem->parse;
- cf = &parse->cftable_entry;
- buf = cfg_mem->buf;
-
- tuple->Attributes = tuple->TupleOffset = 0;
- tuple->TupleData = (cisdata_t *)buf;
- tuple->TupleDataMax = 255;
- tuple->DesiredTuple = CISTPL_CFTABLE_ENTRY;
-
link->io.NumPorts1 = 16;
- i = first_tuple(link, tuple, parse);
- while (i != CS_NO_MORE_ITEMS) {
- if (i == CS_SUCCESS) {
- link->conf.ConfigIndex = cf->index;
- link->io.BasePort1 = cf->io.win[0].base;
- link->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK;
- i = pcmcia_request_io(link, &link->io);
- if (i == CS_SUCCESS) break;
- }
- i = next_tuple(link, tuple, parse);
- }
- if (i == CS_SUCCESS)
- dev->base_addr = link->io.BasePort1;
+ i = pcmcia_loop_config(link, smc_configcheck, NULL);
+ if (!i)
+ dev->base_addr = link->io.BasePort1;
- kfree(cfg_mem);
return i;
}
@@ -715,7 +690,7 @@
cfg_mem = kmalloc(sizeof(struct smc_cfg_mem), GFP_KERNEL);
if (!cfg_mem)
- return CS_OUT_OF_RESOURCE;
+ return -ENOMEM;
tuple = &cfg_mem->tuple;
parse = &cfg_mem->parse;
@@ -728,12 +703,12 @@
/* Check for a LAN function extension tuple */
tuple->DesiredTuple = CISTPL_FUNCE;
i = first_tuple(link, tuple, parse);
- while (i == CS_SUCCESS) {
+ while (i == 0) {
if (parse->funce.type == CISTPL_FUNCE_LAN_NODE_ID)
break;
i = next_tuple(link, tuple, parse);
}
- if (i == CS_SUCCESS) {
+ if (i == 0) {
node_id = (cistpl_lan_node_id_t *)parse->funce.data;
if (node_id->nb == 6) {
for (i = 0; i < 6; i++)
@@ -780,9 +755,10 @@
for (i = j = 0; j < 4; j++) {
link->io.BasePort2 = com[j];
i = pcmcia_request_io(link, &link->io);
- if (i == CS_SUCCESS) break;
+ if (i == 0)
+ break;
}
- if (i != CS_SUCCESS) {
+ if (i != 0) {
/* Fallback: turn off hard decode */
link->conf.ConfigIndex = 0x03;
link->io.NumPorts2 = 0;
@@ -815,13 +791,13 @@
/* Read the station address from tuple 0x90, subtuple 0x04 */
tuple->DesiredTuple = 0x90;
i = pcmcia_get_first_tuple(link, tuple);
- while (i == CS_SUCCESS) {
+ while (i == 0) {
i = pcmcia_get_tuple_data(link, tuple);
- if ((i != CS_SUCCESS) || (buf[0] == 0x04))
+ if ((i != 0) || (buf[0] == 0x04))
break;
i = pcmcia_get_next_tuple(link, tuple);
}
- if (i != CS_SUCCESS) {
+ if (i != 0) {
rc = -1;
goto free_cfg_mem;
}
@@ -959,8 +935,11 @@
======================================================================*/
-#define CS_EXIT_TEST(ret, svc, label) \
-if (ret != CS_SUCCESS) { cs_error(link, svc, ret); goto label; }
+#define CS_EXIT_TEST(ret, svc, label) \
+if (ret != 0) { \
+ cs_error(link, svc, ret); \
+ goto label; \
+}
static int smc91c92_config(struct pcmcia_device *link)
{
diff --git a/drivers/net/pcmcia/xirc2ps_cs.c b/drivers/net/pcmcia/xirc2ps_cs.c
index c33a3d5..e1fd585 100644
--- a/drivers/net/pcmcia/xirc2ps_cs.c
+++ b/drivers/net/pcmcia/xirc2ps_cs.c
@@ -377,7 +377,7 @@
if ((err = pcmcia_get_first_tuple(handle, tuple)) == 0 &&
(err = pcmcia_get_tuple_data(handle, tuple)) == 0)
- err = pcmcia_parse_tuple(handle, tuple, parse);
+ err = pcmcia_parse_tuple(tuple, parse);
return err;
}
@@ -388,7 +388,7 @@
if ((err = pcmcia_get_next_tuple(handle, tuple)) == 0 &&
(err = pcmcia_get_tuple_data(handle, tuple)) == 0)
- err = pcmcia_parse_tuple(handle, tuple, parse);
+ err = pcmcia_parse_tuple(tuple, parse);
return err;
}
@@ -715,6 +715,47 @@
return 0;
}
+static int
+xirc2ps_config_modem(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ unsigned int ioaddr;
+
+ if (cf->io.nwin > 0 && (cf->io.win[0].base & 0xf) == 8) {
+ for (ioaddr = 0x300; ioaddr < 0x400; ioaddr += 0x10) {
+ p_dev->io.BasePort2 = cf->io.win[0].base;
+ p_dev->io.BasePort1 = ioaddr;
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+ return 0;
+ }
+ }
+ return -ENODEV;
+}
+
+static int
+xirc2ps_config_check(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ int *pass = priv_data;
+
+ if (cf->io.nwin > 0 && (cf->io.win[0].base & 0xf) == 8) {
+ p_dev->io.BasePort2 = cf->io.win[0].base;
+ p_dev->io.BasePort1 = p_dev->io.BasePort2
+ + (*pass ? (cf->index & 0x20 ? -24:8)
+ : (cf->index & 0x20 ? 8:-24));
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+ return 0;
+ }
+ return -ENODEV;
+
+}
+
/****************
* xirc2ps_config() is scheduled to run after a CARD_INSERTION event
* is received, to configure the PCMCIA socket, and to make the
@@ -725,13 +766,12 @@
{
struct net_device *dev = link->priv;
local_info_t *local = netdev_priv(dev);
+ unsigned int ioaddr;
tuple_t tuple;
cisparse_t parse;
- unsigned int ioaddr;
int err, i;
u_char buf[64];
cistpl_lan_node_id_t *node_id = (cistpl_lan_node_id_t*)parse.funce.data;
- cistpl_cftable_entry_t *cf = &parse.cftable_entry;
DECLARE_MAC_BUF(mac);
local->dingo_ccr = NULL;
@@ -846,19 +886,8 @@
/* Take the Modem IO port from the CIS and scan for a free
* Ethernet port */
link->io.NumPorts1 = 16; /* no Mako stuff anymore */
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- for (err = first_tuple(link, &tuple, &parse); !err;
- err = next_tuple(link, &tuple, &parse)) {
- if (cf->io.nwin > 0 && (cf->io.win[0].base & 0xf) == 8) {
- for (ioaddr = 0x300; ioaddr < 0x400; ioaddr += 0x10) {
- link->conf.ConfigIndex = cf->index ;
- link->io.BasePort2 = cf->io.win[0].base;
- link->io.BasePort1 = ioaddr;
- if (!(err=pcmcia_request_io(link, &link->io)))
- goto port_found;
- }
- }
- }
+ if (!pcmcia_loop_config(link, xirc2ps_config_modem, NULL))
+ goto port_found;
} else {
link->io.NumPorts1 = 18;
/* We do 2 passes here: The first one uses the regular mapping and
@@ -866,21 +895,9 @@
* mirrored every 32 bytes. Actually we use a mirrored port for
* the Mako if (on the first pass) the COR bit 5 is set.
*/
- for (pass=0; pass < 2; pass++) {
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- for (err = first_tuple(link, &tuple, &parse); !err;
- err = next_tuple(link, &tuple, &parse)){
- if (cf->io.nwin > 0 && (cf->io.win[0].base & 0xf) == 8){
- link->conf.ConfigIndex = cf->index ;
- link->io.BasePort2 = cf->io.win[0].base;
- link->io.BasePort1 = link->io.BasePort2
- + (pass ? (cf->index & 0x20 ? -24:8)
- : (cf->index & 0x20 ? 8:-24));
- if (!(err=pcmcia_request_io(link, &link->io)))
+ for (pass=0; pass < 2; pass++)
+ if (!pcmcia_loop_config(link, xirc2ps_config_check, &pass))
goto port_found;
- }
- }
- }
/* if special option:
* try to configure as Ethernet only.
* .... */
diff --git a/drivers/net/sunbmac.c b/drivers/net/sunbmac.c
index 31e7384..018d0fc 100644
--- a/drivers/net/sunbmac.c
+++ b/drivers/net/sunbmac.c
@@ -1,7 +1,6 @@
-/* $Id: sunbmac.c,v 1.30 2002/01/15 06:48:55 davem Exp $
- * sunbmac.c: Driver for Sparc BigMAC 100baseT ethernet adapters.
+/* sunbmac.c: Driver for Sparc BigMAC 100baseT ethernet adapters.
*
- * Copyright (C) 1997, 1998, 1999, 2003 David S. Miller (davem@redhat.com)
+ * Copyright (C) 1997, 1998, 1999, 2003, 2008 David S. Miller (davem@davemloft.net)
*/
#include <linux/module.h>
@@ -23,6 +22,9 @@
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/bitops.h>
+#include <linux/dma-mapping.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <asm/auxio.h>
#include <asm/byteorder.h>
@@ -32,15 +34,14 @@
#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/pgtable.h>
-#include <asm/sbus.h>
#include <asm/system.h>
#include "sunbmac.h"
#define DRV_NAME "sunbmac"
-#define DRV_VERSION "2.0"
-#define DRV_RELDATE "11/24/03"
-#define DRV_AUTHOR "David S. Miller (davem@redhat.com)"
+#define DRV_VERSION "2.1"
+#define DRV_RELDATE "August 26, 2008"
+#define DRV_AUTHOR "David S. Miller (davem@davemloft.net)"
static char version[] =
DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " " DRV_AUTHOR "\n";
@@ -96,8 +97,8 @@
static void qec_init(struct bigmac *bp)
{
+ struct of_device *qec_op = bp->qec_op;
void __iomem *gregs = bp->gregs;
- struct sbus_dev *qec_sdev = bp->qec_sdev;
u8 bsizes = bp->bigmac_bursts;
u32 regval;
@@ -112,13 +113,13 @@
sbus_writel(GLOB_PSIZE_2048, gregs + GLOB_PSIZE);
/* All of memsize is given to bigmac. */
- sbus_writel(qec_sdev->reg_addrs[1].reg_size,
+ sbus_writel(resource_size(&qec_op->resource[1]),
gregs + GLOB_MSIZE);
/* Half to the transmitter, half to the receiver. */
- sbus_writel(qec_sdev->reg_addrs[1].reg_size >> 1,
+ sbus_writel(resource_size(&qec_op->resource[1]) >> 1,
gregs + GLOB_TSIZE);
- sbus_writel(qec_sdev->reg_addrs[1].reg_size >> 1,
+ sbus_writel(resource_size(&qec_op->resource[1]) >> 1,
gregs + GLOB_RSIZE);
}
@@ -239,9 +240,10 @@
skb_reserve(skb, 34);
bb->be_rxd[i].rx_addr =
- sbus_map_single(bp->bigmac_sdev, skb->data,
- RX_BUF_ALLOC_SIZE - 34,
- SBUS_DMA_FROMDEVICE);
+ dma_map_single(&bp->bigmac_op->dev,
+ skb->data,
+ RX_BUF_ALLOC_SIZE - 34,
+ DMA_FROM_DEVICE);
bb->be_rxd[i].rx_flags =
(RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH));
}
@@ -776,9 +778,9 @@
skb = bp->tx_skbs[elem];
bp->enet_stats.tx_packets++;
bp->enet_stats.tx_bytes += skb->len;
- sbus_unmap_single(bp->bigmac_sdev,
- this->tx_addr, skb->len,
- SBUS_DMA_TODEVICE);
+ dma_unmap_single(&bp->bigmac_op->dev,
+ this->tx_addr, skb->len,
+ DMA_TO_DEVICE);
DTX(("skb(%p) ", skb));
bp->tx_skbs[elem] = NULL;
@@ -831,18 +833,19 @@
drops++;
goto drop_it;
}
- sbus_unmap_single(bp->bigmac_sdev,
- this->rx_addr,
- RX_BUF_ALLOC_SIZE - 34,
- SBUS_DMA_FROMDEVICE);
+ dma_unmap_single(&bp->bigmac_op->dev,
+ this->rx_addr,
+ RX_BUF_ALLOC_SIZE - 34,
+ DMA_FROM_DEVICE);
bp->rx_skbs[elem] = new_skb;
new_skb->dev = bp->dev;
skb_put(new_skb, ETH_FRAME_LEN);
skb_reserve(new_skb, 34);
- this->rx_addr = sbus_map_single(bp->bigmac_sdev,
- new_skb->data,
- RX_BUF_ALLOC_SIZE - 34,
- SBUS_DMA_FROMDEVICE);
+ this->rx_addr =
+ dma_map_single(&bp->bigmac_op->dev,
+ new_skb->data,
+ RX_BUF_ALLOC_SIZE - 34,
+ DMA_FROM_DEVICE);
this->rx_flags =
(RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH));
@@ -857,13 +860,13 @@
}
skb_reserve(copy_skb, 2);
skb_put(copy_skb, len);
- sbus_dma_sync_single_for_cpu(bp->bigmac_sdev,
- this->rx_addr, len,
- SBUS_DMA_FROMDEVICE);
+ dma_sync_single_for_cpu(&bp->bigmac_op->dev,
+ this->rx_addr, len,
+ DMA_FROM_DEVICE);
skb_copy_to_linear_data(copy_skb, (unsigned char *)skb->data, len);
- sbus_dma_sync_single_for_device(bp->bigmac_sdev,
- this->rx_addr, len,
- SBUS_DMA_FROMDEVICE);
+ dma_sync_single_for_device(&bp->bigmac_op->dev,
+ this->rx_addr, len,
+ DMA_FROM_DEVICE);
/* Reuse original ring buffer. */
this->rx_flags =
@@ -959,7 +962,8 @@
u32 mapping;
len = skb->len;
- mapping = sbus_map_single(bp->bigmac_sdev, skb->data, len, SBUS_DMA_TODEVICE);
+ mapping = dma_map_single(&bp->bigmac_op->dev, skb->data,
+ len, DMA_TO_DEVICE);
/* Avoid a race... */
spin_lock_irq(&bp->lock);
@@ -1051,12 +1055,8 @@
/* Ethtool support... */
static void bigmac_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
{
- struct bigmac *bp = dev->priv;
-
strcpy(info->driver, "sunbmac");
strcpy(info->version, "2.0");
- sprintf(info->bus_info, "SBUS:%d",
- bp->qec_sdev->slot);
}
static u32 bigmac_get_link(struct net_device *dev)
@@ -1075,14 +1075,15 @@
.get_link = bigmac_get_link,
};
-static int __devinit bigmac_ether_init(struct sbus_dev *qec_sdev)
+static int __devinit bigmac_ether_init(struct of_device *op,
+ struct of_device *qec_op)
{
- struct net_device *dev;
static int version_printed;
- struct bigmac *bp;
+ struct net_device *dev;
u8 bsizes, bsizes_more;
- int i;
DECLARE_MAC_BUF(mac);
+ struct bigmac *bp;
+ int i;
/* Get a new device struct for this interface. */
dev = alloc_etherdev(sizeof(struct bigmac));
@@ -1092,32 +1093,21 @@
if (version_printed++ == 0)
printk(KERN_INFO "%s", version);
- dev->base_addr = (long) qec_sdev;
for (i = 0; i < 6; i++)
dev->dev_addr[i] = idprom->id_ethaddr[i];
/* Setup softc, with backpointers to QEC and BigMAC SBUS device structs. */
- bp = dev->priv;
- bp->qec_sdev = qec_sdev;
- bp->bigmac_sdev = qec_sdev->child;
+ bp = netdev_priv(dev);
+ bp->qec_op = qec_op;
+ bp->bigmac_op = op;
- SET_NETDEV_DEV(dev, &bp->bigmac_sdev->ofdev.dev);
+ SET_NETDEV_DEV(dev, &op->dev);
spin_lock_init(&bp->lock);
- /* Verify the registers we expect, are actually there. */
- if ((bp->bigmac_sdev->num_registers != 3) ||
- (bp->qec_sdev->num_registers != 2)) {
- printk(KERN_ERR "BIGMAC: Device does not have 2 and 3 regs, it has %d and %d.\n",
- bp->qec_sdev->num_registers,
- bp->bigmac_sdev->num_registers);
- printk(KERN_ERR "BIGMAC: Would you like that for here or to go?\n");
- goto fail_and_cleanup;
- }
-
/* Map in QEC global control registers. */
- bp->gregs = sbus_ioremap(&bp->qec_sdev->resource[0], 0,
- GLOB_REG_SIZE, "BigMAC QEC GLobal Regs");
+ bp->gregs = of_ioremap(&qec_op->resource[0], 0,
+ GLOB_REG_SIZE, "BigMAC QEC GLobal Regs");
if (!bp->gregs) {
printk(KERN_ERR "BIGMAC: Cannot map QEC global registers.\n");
goto fail_and_cleanup;
@@ -1134,13 +1124,8 @@
goto fail_and_cleanup;
/* Get supported SBUS burst sizes. */
- bsizes = prom_getintdefault(bp->qec_sdev->prom_node,
- "burst-sizes",
- 0xff);
-
- bsizes_more = prom_getintdefault(bp->qec_sdev->bus->prom_node,
- "burst-sizes",
- 0xff);
+ bsizes = of_getintprop_default(qec_op->node, "burst-sizes", 0xff);
+ bsizes_more = of_getintprop_default(qec_op->node, "burst-sizes", 0xff);
bsizes &= 0xff;
if (bsizes_more != 0xff)
@@ -1154,16 +1139,16 @@
qec_init(bp);
/* Map in the BigMAC channel registers. */
- bp->creg = sbus_ioremap(&bp->bigmac_sdev->resource[0], 0,
- CREG_REG_SIZE, "BigMAC QEC Channel Regs");
+ bp->creg = of_ioremap(&op->resource[0], 0,
+ CREG_REG_SIZE, "BigMAC QEC Channel Regs");
if (!bp->creg) {
printk(KERN_ERR "BIGMAC: Cannot map QEC channel registers.\n");
goto fail_and_cleanup;
}
/* Map in the BigMAC control registers. */
- bp->bregs = sbus_ioremap(&bp->bigmac_sdev->resource[1], 0,
- BMAC_REG_SIZE, "BigMAC Primary Regs");
+ bp->bregs = of_ioremap(&op->resource[1], 0,
+ BMAC_REG_SIZE, "BigMAC Primary Regs");
if (!bp->bregs) {
printk(KERN_ERR "BIGMAC: Cannot map BigMAC primary registers.\n");
goto fail_and_cleanup;
@@ -1172,8 +1157,8 @@
/* Map in the BigMAC transceiver registers, this is how you poke at
* the BigMAC's PHY.
*/
- bp->tregs = sbus_ioremap(&bp->bigmac_sdev->resource[2], 0,
- TCVR_REG_SIZE, "BigMAC Transceiver Regs");
+ bp->tregs = of_ioremap(&op->resource[2], 0,
+ TCVR_REG_SIZE, "BigMAC Transceiver Regs");
if (!bp->tregs) {
printk(KERN_ERR "BIGMAC: Cannot map BigMAC transceiver registers.\n");
goto fail_and_cleanup;
@@ -1183,17 +1168,17 @@
bigmac_stop(bp);
/* Allocate transmit/receive descriptor DVMA block. */
- bp->bmac_block = sbus_alloc_consistent(bp->bigmac_sdev,
- PAGE_SIZE,
- &bp->bblock_dvma);
+ bp->bmac_block = dma_alloc_coherent(&bp->bigmac_op->dev,
+ PAGE_SIZE,
+ &bp->bblock_dvma, GFP_ATOMIC);
if (bp->bmac_block == NULL || bp->bblock_dvma == 0) {
printk(KERN_ERR "BIGMAC: Cannot allocate consistent DMA.\n");
goto fail_and_cleanup;
}
/* Get the board revision of this BigMAC. */
- bp->board_rev = prom_getintdefault(bp->bigmac_sdev->prom_node,
- "board-version", 1);
+ bp->board_rev = of_getintprop_default(bp->bigmac_op->node,
+ "board-version", 1);
/* Init auto-negotiation timer state. */
init_timer(&bp->bigmac_timer);
@@ -1217,7 +1202,7 @@
dev->watchdog_timeo = 5*HZ;
/* Finish net device registration. */
- dev->irq = bp->bigmac_sdev->irqs[0];
+ dev->irq = bp->bigmac_op->irqs[0];
dev->dma = 0;
if (register_netdev(dev)) {
@@ -1225,7 +1210,7 @@
goto fail_and_cleanup;
}
- dev_set_drvdata(&bp->bigmac_sdev->ofdev.dev, bp);
+ dev_set_drvdata(&bp->bigmac_op->dev, bp);
printk(KERN_INFO "%s: BigMAC 100baseT Ethernet %s\n",
dev->name, print_mac(mac, dev->dev_addr));
@@ -1236,66 +1221,67 @@
/* Something went wrong, undo whatever we did so far. */
/* Free register mappings if any. */
if (bp->gregs)
- sbus_iounmap(bp->gregs, GLOB_REG_SIZE);
+ of_iounmap(&qec_op->resource[0], bp->gregs, GLOB_REG_SIZE);
if (bp->creg)
- sbus_iounmap(bp->creg, CREG_REG_SIZE);
+ of_iounmap(&op->resource[0], bp->creg, CREG_REG_SIZE);
if (bp->bregs)
- sbus_iounmap(bp->bregs, BMAC_REG_SIZE);
+ of_iounmap(&op->resource[1], bp->bregs, BMAC_REG_SIZE);
if (bp->tregs)
- sbus_iounmap(bp->tregs, TCVR_REG_SIZE);
+ of_iounmap(&op->resource[2], bp->tregs, TCVR_REG_SIZE);
if (bp->bmac_block)
- sbus_free_consistent(bp->bigmac_sdev,
- PAGE_SIZE,
- bp->bmac_block,
- bp->bblock_dvma);
+ dma_free_coherent(&bp->bigmac_op->dev,
+ PAGE_SIZE,
+ bp->bmac_block,
+ bp->bblock_dvma);
/* This also frees the co-located 'dev->priv' */
free_netdev(dev);
return -ENODEV;
}
-/* QEC can be the parent of either QuadEthernet or
- * a BigMAC. We want the latter.
+/* QEC can be the parent of either QuadEthernet or a BigMAC. We want
+ * the latter.
*/
-static int __devinit bigmac_sbus_probe(struct of_device *dev, const struct of_device_id *match)
+static int __devinit bigmac_sbus_probe(struct of_device *op,
+ const struct of_device_id *match)
{
- struct sbus_dev *sdev = to_sbus_device(&dev->dev);
- struct device_node *dp = dev->node;
+ struct device *parent = op->dev.parent;
+ struct of_device *qec_op;
- if (!strcmp(dp->name, "be"))
- sdev = sdev->parent;
+ qec_op = to_of_device(parent);
- return bigmac_ether_init(sdev);
+ return bigmac_ether_init(op, qec_op);
}
-static int __devexit bigmac_sbus_remove(struct of_device *dev)
+static int __devexit bigmac_sbus_remove(struct of_device *op)
{
- struct bigmac *bp = dev_get_drvdata(&dev->dev);
+ struct bigmac *bp = dev_get_drvdata(&op->dev);
+ struct device *parent = op->dev.parent;
struct net_device *net_dev = bp->dev;
+ struct of_device *qec_op;
+
+ qec_op = to_of_device(parent);
unregister_netdev(net_dev);
- sbus_iounmap(bp->gregs, GLOB_REG_SIZE);
- sbus_iounmap(bp->creg, CREG_REG_SIZE);
- sbus_iounmap(bp->bregs, BMAC_REG_SIZE);
- sbus_iounmap(bp->tregs, TCVR_REG_SIZE);
- sbus_free_consistent(bp->bigmac_sdev,
- PAGE_SIZE,
- bp->bmac_block,
- bp->bblock_dvma);
+ of_iounmap(&qec_op->resource[0], bp->gregs, GLOB_REG_SIZE);
+ of_iounmap(&op->resource[0], bp->creg, CREG_REG_SIZE);
+ of_iounmap(&op->resource[1], bp->bregs, BMAC_REG_SIZE);
+ of_iounmap(&op->resource[2], bp->tregs, TCVR_REG_SIZE);
+ dma_free_coherent(&op->dev,
+ PAGE_SIZE,
+ bp->bmac_block,
+ bp->bblock_dvma);
free_netdev(net_dev);
- dev_set_drvdata(&dev->dev, NULL);
+ dev_set_drvdata(&op->dev, NULL);
return 0;
}
-static struct of_device_id bigmac_sbus_match[] = {
- {
- .name = "qec",
- },
+static const struct of_device_id bigmac_sbus_match[] = {
{
.name = "be",
},
@@ -1313,7 +1299,7 @@
static int __init bigmac_init(void)
{
- return of_register_driver(&bigmac_sbus_driver, &sbus_bus_type);
+ return of_register_driver(&bigmac_sbus_driver, &of_bus_type);
}
static void __exit bigmac_exit(void)
diff --git a/drivers/net/sunbmac.h b/drivers/net/sunbmac.h
index b563d3c..8840bc0 100644
--- a/drivers/net/sunbmac.h
+++ b/drivers/net/sunbmac.h
@@ -329,8 +329,8 @@
unsigned int timer_ticks;
struct net_device_stats enet_stats;
- struct sbus_dev *qec_sdev;
- struct sbus_dev *bigmac_sdev;
+ struct of_device *qec_op;
+ struct of_device *bigmac_op;
struct net_device *dev;
};
diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c
index b79d5f0..f1ebeb5 100644
--- a/drivers/net/sunhme.c
+++ b/drivers/net/sunhme.c
@@ -3,7 +3,7 @@
* "Happy Meal Ethernet" found on SunSwift SBUS cards.
*
* Copyright (C) 1996, 1998, 1999, 2002, 2003,
- 2006 David S. Miller (davem@davemloft.net)
+ * 2006, 2008 David S. Miller (davem@davemloft.net)
*
* Changes :
* 2000/11/11 Willy Tarreau <willy AT meta-x.org>
@@ -34,6 +34,7 @@
#include <linux/skbuff.h>
#include <linux/mm.h>
#include <linux/bitops.h>
+#include <linux/dma-mapping.h>
#include <asm/system.h>
#include <asm/io.h>
@@ -41,8 +42,9 @@
#include <asm/byteorder.h>
#ifdef CONFIG_SPARC
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <asm/idprom.h>
-#include <asm/sbus.h>
#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/prom.h>
@@ -60,8 +62,8 @@
#include "sunhme.h"
#define DRV_NAME "sunhme"
-#define DRV_VERSION "3.00"
-#define DRV_RELDATE "June 23, 2006"
+#define DRV_VERSION "3.10"
+#define DRV_RELDATE "August 26, 2008"
#define DRV_AUTHOR "David S. Miller (davem@davemloft.net)"
static char version[] =
@@ -251,13 +253,13 @@
#define hme_read_desc32(__hp, __p) \
((__hp)->read_desc32(__p))
#define hme_dma_map(__hp, __ptr, __size, __dir) \
- ((__hp)->dma_map((__hp)->happy_dev, (__ptr), (__size), (__dir)))
+ ((__hp)->dma_map((__hp)->dma_dev, (__ptr), (__size), (__dir)))
#define hme_dma_unmap(__hp, __addr, __size, __dir) \
- ((__hp)->dma_unmap((__hp)->happy_dev, (__addr), (__size), (__dir)))
+ ((__hp)->dma_unmap((__hp)->dma_dev, (__addr), (__size), (__dir)))
#define hme_dma_sync_for_cpu(__hp, __addr, __size, __dir) \
- ((__hp)->dma_sync_for_cpu((__hp)->happy_dev, (__addr), (__size), (__dir)))
+ ((__hp)->dma_sync_for_cpu((__hp)->dma_dev, (__addr), (__size), (__dir)))
#define hme_dma_sync_for_device(__hp, __addr, __size, __dir) \
- ((__hp)->dma_sync_for_device((__hp)->happy_dev, (__addr), (__size), (__dir)))
+ ((__hp)->dma_sync_for_device((__hp)->dma_dev, (__addr), (__size), (__dir)))
#else
#ifdef CONFIG_SBUS
/* SBUS only compilation */
@@ -277,13 +279,13 @@
} while(0)
#define hme_read_desc32(__hp, __p) ((__force u32)(hme32)*(__p))
#define hme_dma_map(__hp, __ptr, __size, __dir) \
- sbus_map_single((__hp)->happy_dev, (__ptr), (__size), (__dir))
+ dma_map_single((__hp)->dma_dev, (__ptr), (__size), (__dir))
#define hme_dma_unmap(__hp, __addr, __size, __dir) \
- sbus_unmap_single((__hp)->happy_dev, (__addr), (__size), (__dir))
+ dma_unmap_single((__hp)->dma_dev, (__addr), (__size), (__dir))
#define hme_dma_sync_for_cpu(__hp, __addr, __size, __dir) \
- sbus_dma_sync_single_for_cpu((__hp)->happy_dev, (__addr), (__size), (__dir))
+ dma_dma_sync_single_for_cpu((__hp)->dma_dev, (__addr), (__size), (__dir))
#define hme_dma_sync_for_device(__hp, __addr, __size, __dir) \
- sbus_dma_sync_single_for_device((__hp)->happy_dev, (__addr), (__size), (__dir))
+ dma_dma_sync_single_for_device((__hp)->dma_dev, (__addr), (__size), (__dir))
#else
/* PCI only compilation */
#define hme_write32(__hp, __reg, __val) \
@@ -305,36 +307,17 @@
return le32_to_cpup((__le32 *)p);
}
#define hme_dma_map(__hp, __ptr, __size, __dir) \
- pci_map_single((__hp)->happy_dev, (__ptr), (__size), (__dir))
+ pci_map_single((__hp)->dma_dev, (__ptr), (__size), (__dir))
#define hme_dma_unmap(__hp, __addr, __size, __dir) \
- pci_unmap_single((__hp)->happy_dev, (__addr), (__size), (__dir))
+ pci_unmap_single((__hp)->dma_dev, (__addr), (__size), (__dir))
#define hme_dma_sync_for_cpu(__hp, __addr, __size, __dir) \
- pci_dma_sync_single_for_cpu((__hp)->happy_dev, (__addr), (__size), (__dir))
+ pci_dma_sync_single_for_cpu((__hp)->dma_dev, (__addr), (__size), (__dir))
#define hme_dma_sync_for_device(__hp, __addr, __size, __dir) \
- pci_dma_sync_single_for_device((__hp)->happy_dev, (__addr), (__size), (__dir))
+ pci_dma_sync_single_for_device((__hp)->dma_dev, (__addr), (__size), (__dir))
#endif
#endif
-#ifdef SBUS_DMA_BIDIRECTIONAL
-# define DMA_BIDIRECTIONAL SBUS_DMA_BIDIRECTIONAL
-#else
-# define DMA_BIDIRECTIONAL 0
-#endif
-
-#ifdef SBUS_DMA_FROMDEVICE
-# define DMA_FROMDEVICE SBUS_DMA_FROMDEVICE
-#else
-# define DMA_TODEVICE 1
-#endif
-
-#ifdef SBUS_DMA_TODEVICE
-# define DMA_TODEVICE SBUS_DMA_TODEVICE
-#else
-# define DMA_FROMDEVICE 2
-#endif
-
-
/* Oh yes, the MIF BitBang is mighty fun to program. BitBucket is more like it. */
static void BB_PUT_BIT(struct happy_meal *hp, void __iomem *tregs, int bit)
{
@@ -1224,7 +1207,8 @@
rxd = &hp->happy_block->happy_meal_rxd[i];
dma_addr = hme_read_desc32(hp, &rxd->rx_addr);
- hme_dma_unmap(hp, dma_addr, RX_BUF_ALLOC_SIZE, DMA_FROMDEVICE);
+ dma_unmap_single(hp->dma_dev, dma_addr,
+ RX_BUF_ALLOC_SIZE, DMA_FROM_DEVICE);
dev_kfree_skb_any(skb);
hp->rx_skbs[i] = NULL;
}
@@ -1242,10 +1226,10 @@
for (frag = 0; frag <= skb_shinfo(skb)->nr_frags; frag++) {
txd = &hp->happy_block->happy_meal_txd[i];
dma_addr = hme_read_desc32(hp, &txd->tx_addr);
- hme_dma_unmap(hp, dma_addr,
- (hme_read_desc32(hp, &txd->tx_flags)
- & TXFLAG_SIZE),
- DMA_TODEVICE);
+ dma_unmap_single(hp->dma_dev, dma_addr,
+ (hme_read_desc32(hp, &txd->tx_flags)
+ & TXFLAG_SIZE),
+ DMA_TO_DEVICE);
if (frag != skb_shinfo(skb)->nr_frags)
i++;
@@ -1287,7 +1271,8 @@
skb_put(skb, (ETH_FRAME_LEN + RX_OFFSET + 4));
hme_write_rxd(hp, &hb->happy_meal_rxd[i],
(RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16)),
- hme_dma_map(hp, skb->data, RX_BUF_ALLOC_SIZE, DMA_FROMDEVICE));
+ dma_map_single(hp->dma_dev, skb->data, RX_BUF_ALLOC_SIZE,
+ DMA_FROM_DEVICE));
skb_reserve(skb, RX_OFFSET);
}
@@ -1593,7 +1578,7 @@
if ((hp->happy_bursts & DMA_BURST64) &&
((hp->happy_flags & HFLAG_PCI) != 0
#ifdef CONFIG_SBUS
- || sbus_can_burst64(hp->happy_dev)
+ || sbus_can_burst64()
#endif
|| 0)) {
u32 gcfg = GREG_CFG_BURST64;
@@ -1603,11 +1588,13 @@
* do not. -DaveM
*/
#ifdef CONFIG_SBUS
- if ((hp->happy_flags & HFLAG_PCI) == 0 &&
- sbus_can_dma_64bit(hp->happy_dev)) {
- sbus_set_sbus64(hp->happy_dev,
- hp->happy_bursts);
- gcfg |= GREG_CFG_64BIT;
+ if ((hp->happy_flags & HFLAG_PCI) == 0) {
+ struct of_device *op = hp->happy_dev;
+ if (sbus_can_dma_64bit()) {
+ sbus_set_sbus64(&op->dev,
+ hp->happy_bursts);
+ gcfg |= GREG_CFG_64BIT;
+ }
}
#endif
@@ -1966,7 +1953,7 @@
dma_len = hme_read_desc32(hp, &this->tx_flags);
dma_len &= TXFLAG_SIZE;
- hme_dma_unmap(hp, dma_addr, dma_len, DMA_TODEVICE);
+ dma_unmap_single(hp->dma_dev, dma_addr, dma_len, DMA_TO_DEVICE);
elem = NEXT_TX(elem);
this = &txbase[elem];
@@ -2044,13 +2031,14 @@
drops++;
goto drop_it;
}
- hme_dma_unmap(hp, dma_addr, RX_BUF_ALLOC_SIZE, DMA_FROMDEVICE);
+ dma_unmap_single(hp->dma_dev, dma_addr, RX_BUF_ALLOC_SIZE, DMA_FROM_DEVICE);
hp->rx_skbs[elem] = new_skb;
new_skb->dev = dev;
skb_put(new_skb, (ETH_FRAME_LEN + RX_OFFSET + 4));
hme_write_rxd(hp, this,
(RXFLAG_OWN|((RX_BUF_ALLOC_SIZE-RX_OFFSET)<<16)),
- hme_dma_map(hp, new_skb->data, RX_BUF_ALLOC_SIZE, DMA_FROMDEVICE));
+ dma_map_single(hp->dma_dev, new_skb->data, RX_BUF_ALLOC_SIZE,
+ DMA_FROM_DEVICE));
skb_reserve(new_skb, RX_OFFSET);
/* Trim the original skb for the netif. */
@@ -2065,10 +2053,9 @@
skb_reserve(copy_skb, 2);
skb_put(copy_skb, len);
- hme_dma_sync_for_cpu(hp, dma_addr, len, DMA_FROMDEVICE);
+ dma_sync_single_for_cpu(hp->dma_dev, dma_addr, len, DMA_FROM_DEVICE);
skb_copy_from_linear_data(skb, copy_skb->data, len);
- hme_dma_sync_for_device(hp, dma_addr, len, DMA_FROMDEVICE);
-
+ dma_sync_single_for_device(hp->dma_dev, dma_addr, len, DMA_FROM_DEVICE);
/* Reuse original ring buffer. */
hme_write_rxd(hp, this,
(RXFLAG_OWN|((RX_BUF_ALLOC_SIZE-RX_OFFSET)<<16)),
@@ -2300,7 +2287,7 @@
u32 mapping, len;
len = skb->len;
- mapping = hme_dma_map(hp, skb->data, len, DMA_TODEVICE);
+ mapping = dma_map_single(hp->dma_dev, skb->data, len, DMA_TO_DEVICE);
tx_flags |= (TXFLAG_SOP | TXFLAG_EOP);
hme_write_txd(hp, &hp->happy_block->happy_meal_txd[entry],
(tx_flags | (len & TXFLAG_SIZE)),
@@ -2314,7 +2301,8 @@
* Otherwise we could race with the device.
*/
first_len = skb_headlen(skb);
- first_mapping = hme_dma_map(hp, skb->data, first_len, DMA_TODEVICE);
+ first_mapping = dma_map_single(hp->dma_dev, skb->data, first_len,
+ DMA_TO_DEVICE);
entry = NEXT_TX(entry);
for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) {
@@ -2322,10 +2310,9 @@
u32 len, mapping, this_txflags;
len = this_frag->size;
- mapping = hme_dma_map(hp,
- ((void *) page_address(this_frag->page) +
- this_frag->page_offset),
- len, DMA_TODEVICE);
+ mapping = dma_map_page(hp->dma_dev, this_frag->page,
+ this_frag->page_offset, len,
+ DMA_TO_DEVICE);
this_txflags = tx_flags;
if (frag == skb_shinfo(skb)->nr_frags - 1)
this_txflags |= TXFLAG_EOP;
@@ -2493,9 +2480,12 @@
}
#ifdef CONFIG_SBUS
else {
- struct sbus_dev *sdev = hp->happy_dev;
- sprintf(info->bus_info, "SBUS:%d",
- sdev->slot);
+ const struct linux_prom_registers *regs;
+ struct of_device *op = hp->happy_dev;
+ regs = of_get_property(op->node, "regs", NULL);
+ if (regs)
+ sprintf(info->bus_info, "SBUS:%d",
+ regs->which_io);
}
#endif
}
@@ -2521,63 +2511,21 @@
static int hme_version_printed;
#ifdef CONFIG_SBUS
-void __devinit quattro_get_ranges(struct quattro *qp)
-{
- struct sbus_dev *sdev = qp->quattro_dev;
- int err;
-
- err = prom_getproperty(sdev->prom_node,
- "ranges",
- (char *)&qp->ranges[0],
- sizeof(qp->ranges));
- if (err == 0 || err == -1) {
- qp->nranges = 0;
- return;
- }
- qp->nranges = (err / sizeof(struct linux_prom_ranges));
-}
-
-static void __devinit quattro_apply_ranges(struct quattro *qp, struct happy_meal *hp)
-{
- struct sbus_dev *sdev = hp->happy_dev;
- int rng;
-
- for (rng = 0; rng < qp->nranges; rng++) {
- struct linux_prom_ranges *rngp = &qp->ranges[rng];
- int reg;
-
- for (reg = 0; reg < 5; reg++) {
- if (sdev->reg_addrs[reg].which_io ==
- rngp->ot_child_space)
- break;
- }
- if (reg == 5)
- continue;
-
- sdev->reg_addrs[reg].which_io = rngp->ot_parent_space;
- sdev->reg_addrs[reg].phys_addr += rngp->ot_parent_base;
- }
-}
-
/* Given a happy meal sbus device, find it's quattro parent.
* If none exist, allocate and return a new one.
*
* Return NULL on failure.
*/
-static struct quattro * __devinit quattro_sbus_find(struct sbus_dev *goal_sdev)
+static struct quattro * __devinit quattro_sbus_find(struct of_device *child)
{
- struct sbus_dev *sdev;
+ struct device *parent = child->dev.parent;
+ struct of_device *op;
struct quattro *qp;
- int i;
- for (qp = qfe_sbus_list; qp != NULL; qp = qp->next) {
- for (i = 0, sdev = qp->quattro_dev;
- (sdev != NULL) && (i < 4);
- sdev = sdev->next, i++) {
- if (sdev == goal_sdev)
- return qp;
- }
- }
+ op = to_of_device(parent);
+ qp = dev_get_drvdata(&op->dev);
+ if (qp)
+ return qp;
qp = kmalloc(sizeof(struct quattro), GFP_KERNEL);
if (qp != NULL) {
@@ -2586,10 +2534,11 @@
for (i = 0; i < 4; i++)
qp->happy_meals[i] = NULL;
- qp->quattro_dev = goal_sdev;
+ qp->quattro_dev = child;
qp->next = qfe_sbus_list;
qfe_sbus_list = qp;
- quattro_get_ranges(qp);
+
+ dev_set_drvdata(&op->dev, qp);
}
return qp;
}
@@ -2602,10 +2551,10 @@
struct quattro *qp;
for (qp = qfe_sbus_list; qp != NULL; qp = qp->next) {
- struct sbus_dev *sdev = qp->quattro_dev;
+ struct of_device *op = qp->quattro_dev;
int err;
- err = request_irq(sdev->irqs[0],
+ err = request_irq(op->irqs[0],
quattro_sbus_interrupt,
IRQF_SHARED, "Quattro",
qp);
@@ -2621,9 +2570,9 @@
struct quattro *qp;
for (qp = qfe_sbus_list; qp != NULL; qp = qp->next) {
- struct sbus_dev *sdev = qp->quattro_dev;
+ struct of_device *op = qp->quattro_dev;
- free_irq(sdev->irqs[0], qp);
+ free_irq(op->irqs[0], qp);
}
}
#endif /* CONFIG_SBUS */
@@ -2660,9 +2609,9 @@
#endif /* CONFIG_PCI */
#ifdef CONFIG_SBUS
-static int __devinit happy_meal_sbus_probe_one(struct sbus_dev *sdev, int is_qfe)
+static int __devinit happy_meal_sbus_probe_one(struct of_device *op, int is_qfe)
{
- struct device_node *dp = sdev->ofdev.node;
+ struct device_node *dp = op->node, *sbus_dp;
struct quattro *qp = NULL;
struct happy_meal *hp;
struct net_device *dev;
@@ -2671,7 +2620,7 @@
DECLARE_MAC_BUF(mac);
if (is_qfe) {
- qp = quattro_sbus_find(sdev);
+ qp = quattro_sbus_find(op);
if (qp == NULL)
goto err_out;
for (qfe_slot = 0; qfe_slot < 4; qfe_slot++)
@@ -2685,7 +2634,7 @@
dev = alloc_etherdev(sizeof(struct happy_meal));
if (!dev)
goto err_out;
- SET_NETDEV_DEV(dev, &sdev->ofdev.dev);
+ SET_NETDEV_DEV(dev, &op->dev);
if (hme_version_printed++ == 0)
printk(KERN_INFO "%s", version);
@@ -2713,56 +2662,50 @@
memcpy(dev->dev_addr, idprom->id_ethaddr, 6);
}
- hp = dev->priv;
+ hp = netdev_priv(dev);
- hp->happy_dev = sdev;
+ hp->happy_dev = op;
+ hp->dma_dev = &op->dev;
spin_lock_init(&hp->happy_lock);
err = -ENODEV;
- if (sdev->num_registers != 5) {
- printk(KERN_ERR "happymeal: Device needs 5 regs, has %d.\n",
- sdev->num_registers);
- goto err_out_free_netdev;
- }
-
if (qp != NULL) {
hp->qfe_parent = qp;
hp->qfe_ent = qfe_slot;
qp->happy_meals[qfe_slot] = dev;
- quattro_apply_ranges(qp, hp);
}
- hp->gregs = sbus_ioremap(&sdev->resource[0], 0,
- GREG_REG_SIZE, "HME Global Regs");
+ hp->gregs = of_ioremap(&op->resource[0], 0,
+ GREG_REG_SIZE, "HME Global Regs");
if (!hp->gregs) {
printk(KERN_ERR "happymeal: Cannot map global registers.\n");
goto err_out_free_netdev;
}
- hp->etxregs = sbus_ioremap(&sdev->resource[1], 0,
- ETX_REG_SIZE, "HME TX Regs");
+ hp->etxregs = of_ioremap(&op->resource[1], 0,
+ ETX_REG_SIZE, "HME TX Regs");
if (!hp->etxregs) {
printk(KERN_ERR "happymeal: Cannot map MAC TX registers.\n");
goto err_out_iounmap;
}
- hp->erxregs = sbus_ioremap(&sdev->resource[2], 0,
- ERX_REG_SIZE, "HME RX Regs");
+ hp->erxregs = of_ioremap(&op->resource[2], 0,
+ ERX_REG_SIZE, "HME RX Regs");
if (!hp->erxregs) {
printk(KERN_ERR "happymeal: Cannot map MAC RX registers.\n");
goto err_out_iounmap;
}
- hp->bigmacregs = sbus_ioremap(&sdev->resource[3], 0,
- BMAC_REG_SIZE, "HME BIGMAC Regs");
+ hp->bigmacregs = of_ioremap(&op->resource[3], 0,
+ BMAC_REG_SIZE, "HME BIGMAC Regs");
if (!hp->bigmacregs) {
printk(KERN_ERR "happymeal: Cannot map BIGMAC registers.\n");
goto err_out_iounmap;
}
- hp->tcvregs = sbus_ioremap(&sdev->resource[4], 0,
- TCVR_REG_SIZE, "HME Tranceiver Regs");
+ hp->tcvregs = of_ioremap(&op->resource[4], 0,
+ TCVR_REG_SIZE, "HME Tranceiver Regs");
if (!hp->tcvregs) {
printk(KERN_ERR "happymeal: Cannot map TCVR registers.\n");
goto err_out_iounmap;
@@ -2781,13 +2724,18 @@
if (qp != NULL)
hp->happy_flags |= HFLAG_QUATTRO;
+ sbus_dp = to_of_device(op->dev.parent)->node;
+ if (is_qfe)
+ sbus_dp = to_of_device(op->dev.parent->parent)->node;
+
/* Get the supported DVMA burst sizes from our Happy SBUS. */
- hp->happy_bursts = of_getintprop_default(sdev->bus->ofdev.node,
+ hp->happy_bursts = of_getintprop_default(sbus_dp,
"burst-sizes", 0x00);
- hp->happy_block = sbus_alloc_consistent(hp->happy_dev,
- PAGE_SIZE,
- &hp->hblock_dvma);
+ hp->happy_block = dma_alloc_coherent(hp->dma_dev,
+ PAGE_SIZE,
+ &hp->hblock_dvma,
+ GFP_ATOMIC);
err = -ENOMEM;
if (!hp->happy_block) {
printk(KERN_ERR "happymeal: Cannot allocate descriptors.\n");
@@ -2816,19 +2764,13 @@
/* Happy Meal can do it all... */
dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM;
- dev->irq = sdev->irqs[0];
+ dev->irq = op->irqs[0];
#if defined(CONFIG_SBUS) && defined(CONFIG_PCI)
- /* Hook up PCI register/dma accessors. */
+ /* Hook up SBUS register/descriptor accessors. */
hp->read_desc32 = sbus_hme_read_desc32;
hp->write_txd = sbus_hme_write_txd;
hp->write_rxd = sbus_hme_write_rxd;
- hp->dma_map = (u32 (*)(void *, void *, long, int))sbus_map_single;
- hp->dma_unmap = (void (*)(void *, u32, long, int))sbus_unmap_single;
- hp->dma_sync_for_cpu = (void (*)(void *, u32, long, int))
- sbus_dma_sync_single_for_cpu;
- hp->dma_sync_for_device = (void (*)(void *, u32, long, int))
- sbus_dma_sync_single_for_device;
hp->read32 = sbus_hme_read32;
hp->write32 = sbus_hme_write32;
#endif
@@ -2843,10 +2785,10 @@
if (register_netdev(hp->dev)) {
printk(KERN_ERR "happymeal: Cannot register net device, "
"aborting.\n");
- goto err_out_free_consistent;
+ goto err_out_free_coherent;
}
- dev_set_drvdata(&sdev->ofdev.dev, hp);
+ dev_set_drvdata(&op->dev, hp);
if (qfe_slot != -1)
printk(KERN_INFO "%s: Quattro HME slot %d (SBUS) 10/100baseT Ethernet ",
@@ -2859,23 +2801,23 @@
return 0;
-err_out_free_consistent:
- sbus_free_consistent(hp->happy_dev,
- PAGE_SIZE,
- hp->happy_block,
- hp->hblock_dvma);
+err_out_free_coherent:
+ dma_free_coherent(hp->dma_dev,
+ PAGE_SIZE,
+ hp->happy_block,
+ hp->hblock_dvma);
err_out_iounmap:
if (hp->gregs)
- sbus_iounmap(hp->gregs, GREG_REG_SIZE);
+ of_iounmap(&op->resource[0], hp->gregs, GREG_REG_SIZE);
if (hp->etxregs)
- sbus_iounmap(hp->etxregs, ETX_REG_SIZE);
+ of_iounmap(&op->resource[1], hp->etxregs, ETX_REG_SIZE);
if (hp->erxregs)
- sbus_iounmap(hp->erxregs, ERX_REG_SIZE);
+ of_iounmap(&op->resource[2], hp->erxregs, ERX_REG_SIZE);
if (hp->bigmacregs)
- sbus_iounmap(hp->bigmacregs, BMAC_REG_SIZE);
+ of_iounmap(&op->resource[3], hp->bigmacregs, BMAC_REG_SIZE);
if (hp->tcvregs)
- sbus_iounmap(hp->tcvregs, TCVR_REG_SIZE);
+ of_iounmap(&op->resource[4], hp->tcvregs, TCVR_REG_SIZE);
err_out_free_netdev:
free_netdev(dev);
@@ -3035,6 +2977,7 @@
memset(hp, 0, sizeof(*hp));
hp->happy_dev = pdev;
+ hp->dma_dev = &pdev->dev;
spin_lock_init(&hp->happy_lock);
@@ -3121,7 +3064,7 @@
#endif
hp->happy_block = (struct hmeal_init_block *)
- pci_alloc_consistent(pdev, PAGE_SIZE, &hp->hblock_dvma);
+ dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &hp->hblock_dvma, GFP_KERNEL);
err = -ENODEV;
if (!hp->happy_block) {
@@ -3151,16 +3094,10 @@
dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM;
#if defined(CONFIG_SBUS) && defined(CONFIG_PCI)
- /* Hook up PCI register/dma accessors. */
+ /* Hook up PCI register/descriptor accessors. */
hp->read_desc32 = pci_hme_read_desc32;
hp->write_txd = pci_hme_write_txd;
hp->write_rxd = pci_hme_write_rxd;
- hp->dma_map = (u32 (*)(void *, void *, long, int))pci_map_single;
- hp->dma_unmap = (void (*)(void *, u32, long, int))pci_unmap_single;
- hp->dma_sync_for_cpu = (void (*)(void *, u32, long, int))
- pci_dma_sync_single_for_cpu;
- hp->dma_sync_for_device = (void (*)(void *, u32, long, int))
- pci_dma_sync_single_for_device;
hp->read32 = pci_hme_read32;
hp->write32 = pci_hme_write32;
#endif
@@ -3231,10 +3168,8 @@
unregister_netdev(net_dev);
- pci_free_consistent(hp->happy_dev,
- PAGE_SIZE,
- hp->happy_block,
- hp->hblock_dvma);
+ dma_free_coherent(hp->dma_dev, PAGE_SIZE,
+ hp->happy_block, hp->hblock_dvma);
iounmap(hp->gregs);
pci_release_regions(hp->happy_dev);
@@ -3279,46 +3214,45 @@
#endif
#ifdef CONFIG_SBUS
-static int __devinit hme_sbus_probe(struct of_device *dev, const struct of_device_id *match)
+static int __devinit hme_sbus_probe(struct of_device *op, const struct of_device_id *match)
{
- struct sbus_dev *sdev = to_sbus_device(&dev->dev);
- struct device_node *dp = dev->node;
+ struct device_node *dp = op->node;
const char *model = of_get_property(dp, "model", NULL);
int is_qfe = (match->data != NULL);
if (!is_qfe && model && !strcmp(model, "SUNW,sbus-qfe"))
is_qfe = 1;
- return happy_meal_sbus_probe_one(sdev, is_qfe);
+ return happy_meal_sbus_probe_one(op, is_qfe);
}
-static int __devexit hme_sbus_remove(struct of_device *dev)
+static int __devexit hme_sbus_remove(struct of_device *op)
{
- struct happy_meal *hp = dev_get_drvdata(&dev->dev);
+ struct happy_meal *hp = dev_get_drvdata(&op->dev);
struct net_device *net_dev = hp->dev;
unregister_netdev(net_dev);
/* XXX qfe parent interrupt... */
- sbus_iounmap(hp->gregs, GREG_REG_SIZE);
- sbus_iounmap(hp->etxregs, ETX_REG_SIZE);
- sbus_iounmap(hp->erxregs, ERX_REG_SIZE);
- sbus_iounmap(hp->bigmacregs, BMAC_REG_SIZE);
- sbus_iounmap(hp->tcvregs, TCVR_REG_SIZE);
- sbus_free_consistent(hp->happy_dev,
- PAGE_SIZE,
- hp->happy_block,
- hp->hblock_dvma);
+ of_iounmap(&op->resource[0], hp->gregs, GREG_REG_SIZE);
+ of_iounmap(&op->resource[1], hp->etxregs, ETX_REG_SIZE);
+ of_iounmap(&op->resource[2], hp->erxregs, ERX_REG_SIZE);
+ of_iounmap(&op->resource[3], hp->bigmacregs, BMAC_REG_SIZE);
+ of_iounmap(&op->resource[4], hp->tcvregs, TCVR_REG_SIZE);
+ dma_free_coherent(hp->dma_dev,
+ PAGE_SIZE,
+ hp->happy_block,
+ hp->hblock_dvma);
free_netdev(net_dev);
- dev_set_drvdata(&dev->dev, NULL);
+ dev_set_drvdata(&op->dev, NULL);
return 0;
}
-static struct of_device_id hme_sbus_match[] = {
+static const struct of_device_id hme_sbus_match[] = {
{
.name = "SUNW,hme",
},
@@ -3346,7 +3280,7 @@
{
int err;
- err = of_register_driver(&hme_sbus_driver, &sbus_bus_type);
+ err = of_register_driver(&hme_sbus_driver, &of_bus_type);
if (!err)
quattro_sbus_register_irqs();
diff --git a/drivers/net/sunhme.h b/drivers/net/sunhme.h
index 4da5539..efd2ca0 100644
--- a/drivers/net/sunhme.h
+++ b/drivers/net/sunhme.h
@@ -405,14 +405,11 @@
u32 (*read_desc32)(hme32 *);
void (*write_txd)(struct happy_meal_txd *, u32, u32);
void (*write_rxd)(struct happy_meal_rxd *, u32, u32);
- u32 (*dma_map)(void *, void *, long, int);
- void (*dma_unmap)(void *, u32, long, int);
- void (*dma_sync_for_cpu)(void *, u32, long, int);
- void (*dma_sync_for_device)(void *, u32, long, int);
#endif
- /* This is either a sbus_dev or a pci_dev. */
+ /* This is either an of_device or a pci_dev. */
void *happy_dev;
+ struct device *dma_dev;
spinlock_t happy_lock;
diff --git a/drivers/net/sunlance.c b/drivers/net/sunlance.c
index 4e994f8..704301a 100644
--- a/drivers/net/sunlance.c
+++ b/drivers/net/sunlance.c
@@ -91,6 +91,9 @@
#include <linux/skbuff.h>
#include <linux/ethtool.h>
#include <linux/bitops.h>
+#include <linux/dma-mapping.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <asm/system.h>
#include <asm/io.h>
@@ -98,7 +101,6 @@
#include <asm/pgtable.h>
#include <asm/byteorder.h> /* Used by the checksum routines */
#include <asm/idprom.h>
-#include <asm/sbus.h>
#include <asm/prom.h>
#include <asm/auxio.h> /* For tpe-link-test? setting */
#include <asm/irq.h>
@@ -248,7 +250,7 @@
int rx_new, tx_new;
int rx_old, tx_old;
- struct sbus_dma *ledma; /* If set this points to ledma */
+ struct of_device *ledma; /* If set this points to ledma */
char tpe; /* cable-selection is TPE */
char auto_select; /* cable-selection by carrier */
char burst_sizes; /* ledma SBus burst sizes */
@@ -263,7 +265,8 @@
char *name;
dma_addr_t init_block_dvma;
struct net_device *dev; /* Backpointer */
- struct sbus_dev *sdev;
+ struct of_device *op;
+ struct of_device *lebuffer;
struct timer_list multicast_timer;
};
@@ -1272,27 +1275,29 @@
static void lance_free_hwresources(struct lance_private *lp)
{
if (lp->lregs)
- sbus_iounmap(lp->lregs, LANCE_REG_SIZE);
+ of_iounmap(&lp->op->resource[0], lp->lregs, LANCE_REG_SIZE);
+ if (lp->dregs) {
+ struct of_device *ledma = lp->ledma;
+
+ of_iounmap(&ledma->resource[0], lp->dregs,
+ resource_size(&ledma->resource[0]));
+ }
if (lp->init_block_iomem) {
- sbus_iounmap(lp->init_block_iomem,
- sizeof(struct lance_init_block));
+ of_iounmap(&lp->lebuffer->resource[0], lp->init_block_iomem,
+ sizeof(struct lance_init_block));
} else if (lp->init_block_mem) {
- sbus_free_consistent(lp->sdev,
- sizeof(struct lance_init_block),
- lp->init_block_mem,
- lp->init_block_dvma);
+ dma_free_coherent(&lp->op->dev,
+ sizeof(struct lance_init_block),
+ lp->init_block_mem,
+ lp->init_block_dvma);
}
}
/* Ethtool support... */
static void sparc_lance_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
{
- struct lance_private *lp = netdev_priv(dev);
-
strcpy(info->driver, "sunlance");
strcpy(info->version, "2.02");
- sprintf(info->bus_info, "SBUS:%d",
- lp->sdev->slot);
}
static u32 sparc_lance_get_link(struct net_device *dev)
@@ -1308,16 +1313,16 @@
.get_link = sparc_lance_get_link,
};
-static int __devinit sparc_lance_probe_one(struct sbus_dev *sdev,
- struct sbus_dma *ledma,
- struct sbus_dev *lebuffer)
+static int __devinit sparc_lance_probe_one(struct of_device *op,
+ struct of_device *ledma,
+ struct of_device *lebuffer)
{
+ struct device_node *dp = op->node;
static unsigned version_printed;
- struct device_node *dp = sdev->ofdev.node;
- struct net_device *dev;
struct lance_private *lp;
- int i;
+ struct net_device *dev;
DECLARE_MAC_BUF(mac);
+ int i;
dev = alloc_etherdev(sizeof(struct lance_private) + 8);
if (!dev)
@@ -1338,14 +1343,27 @@
dev->dev_addr[i] = idprom->id_ethaddr[i];
/* Get the IO region */
- lp->lregs = sbus_ioremap(&sdev->resource[0], 0,
- LANCE_REG_SIZE, lancestr);
+ lp->lregs = of_ioremap(&op->resource[0], 0,
+ LANCE_REG_SIZE, lancestr);
if (!lp->lregs) {
printk(KERN_ERR "SunLance: Cannot map registers.\n");
goto fail;
}
- lp->sdev = sdev;
+ lp->ledma = ledma;
+ if (lp->ledma) {
+ lp->dregs = of_ioremap(&ledma->resource[0], 0,
+ resource_size(&ledma->resource[0]),
+ "ledma");
+ if (!lp->dregs) {
+ printk(KERN_ERR "SunLance: Cannot map "
+ "ledma registers.\n");
+ goto fail;
+ }
+ }
+
+ lp->op = op;
+ lp->lebuffer = lebuffer;
if (lebuffer) {
/* sanity check */
if (lebuffer->resource[0].start & 7) {
@@ -1353,8 +1371,8 @@
goto fail;
}
lp->init_block_iomem =
- sbus_ioremap(&lebuffer->resource[0], 0,
- sizeof(struct lance_init_block), "lebuffer");
+ of_ioremap(&lebuffer->resource[0], 0,
+ sizeof(struct lance_init_block), "lebuffer");
if (!lp->init_block_iomem) {
printk(KERN_ERR "SunLance: Cannot map PIO buffer.\n");
goto fail;
@@ -1366,9 +1384,10 @@
lp->tx = lance_tx_pio;
} else {
lp->init_block_mem =
- sbus_alloc_consistent(sdev, sizeof(struct lance_init_block),
- &lp->init_block_dvma);
- if (!lp->init_block_mem || lp->init_block_dvma == 0) {
+ dma_alloc_coherent(&op->dev,
+ sizeof(struct lance_init_block),
+ &lp->init_block_dvma, GFP_ATOMIC);
+ if (!lp->init_block_mem) {
printk(KERN_ERR "SunLance: Cannot allocate consistent DMA memory.\n");
goto fail;
}
@@ -1383,13 +1402,13 @@
LE_C3_BCON));
lp->name = lancestr;
- lp->ledma = ledma;
lp->burst_sizes = 0;
if (lp->ledma) {
- struct device_node *ledma_dp = ledma->sdev->ofdev.node;
- const char *prop;
+ struct device_node *ledma_dp = ledma->node;
+ struct device_node *sbus_dp;
unsigned int sbmask;
+ const char *prop;
u32 csr;
/* Find burst-size property for ledma */
@@ -1397,7 +1416,8 @@
"burst-sizes", 0);
/* ledma may be capable of fast bursts, but sbus may not. */
- sbmask = of_getintprop_default(ledma_dp, "burst-sizes",
+ sbus_dp = ledma_dp->parent;
+ sbmask = of_getintprop_default(sbus_dp, "burst-sizes",
DMA_BURSTBITS);
lp->burst_sizes &= sbmask;
@@ -1435,8 +1455,6 @@
lp->tpe = 1;
}
- lp->dregs = ledma->regs;
-
/* Reset ledma */
csr = sbus_readl(lp->dregs + DMA_CSR);
sbus_writel(csr | DMA_RST_ENET, lp->dregs + DMA_CSR);
@@ -1446,7 +1464,7 @@
lp->dregs = NULL;
lp->dev = dev;
- SET_NETDEV_DEV(dev, &sdev->ofdev.dev);
+ SET_NETDEV_DEV(dev, &op->dev);
dev->open = &lance_open;
dev->stop = &lance_close;
dev->hard_start_xmit = &lance_start_xmit;
@@ -1455,9 +1473,7 @@
dev->set_multicast_list = &lance_set_multicast;
dev->ethtool_ops = &sparc_lance_ethtool_ops;
- dev->irq = sdev->irqs[0];
-
- dev->dma = 0;
+ dev->irq = op->irqs[0];
/* We cannot sleep if the chip is busy during a
* multicast list update event, because such events
@@ -1473,7 +1489,7 @@
goto fail;
}
- dev_set_drvdata(&sdev->ofdev.dev, lp);
+ dev_set_drvdata(&op->dev, lp);
printk(KERN_INFO "%s: LANCE %s\n",
dev->name, print_mac(mac, dev->dev_addr));
@@ -1486,80 +1502,25 @@
return -ENODEV;
}
-/* On 4m, find the associated dma for the lance chip */
-static struct sbus_dma * __devinit find_ledma(struct sbus_dev *sdev)
+static int __devinit sunlance_sbus_probe(struct of_device *op, const struct of_device_id *match)
{
- struct sbus_dma *p;
-
- for_each_dvma(p) {
- if (p->sdev == sdev)
- return p;
- }
- return NULL;
-}
-
-#ifdef CONFIG_SUN4
-
-#include <asm/sun4paddr.h>
-#include <asm/machines.h>
-
-/* Find all the lance cards on the system and initialize them */
-static struct sbus_dev sun4_sdev;
-static int __devinit sparc_lance_init(void)
-{
- if ((idprom->id_machtype == (SM_SUN4|SM_4_330)) ||
- (idprom->id_machtype == (SM_SUN4|SM_4_470))) {
- memset(&sun4_sdev, 0, sizeof(struct sbus_dev));
- sun4_sdev.reg_addrs[0].phys_addr = sun4_eth_physaddr;
- sun4_sdev.irqs[0] = 6;
- return sparc_lance_probe_one(&sun4_sdev, NULL, NULL);
- }
- return -ENODEV;
-}
-
-static int __exit sunlance_sun4_remove(void)
-{
- struct lance_private *lp = dev_get_drvdata(&sun4_sdev.ofdev.dev);
- struct net_device *net_dev = lp->dev;
-
- unregister_netdev(net_dev);
-
- lance_free_hwresources(lp);
-
- free_netdev(net_dev);
-
- dev_set_drvdata(&sun4_sdev.ofdev.dev, NULL);
-
- return 0;
-}
-
-#else /* !CONFIG_SUN4 */
-
-static int __devinit sunlance_sbus_probe(struct of_device *dev, const struct of_device_id *match)
-{
- struct sbus_dev *sdev = to_sbus_device(&dev->dev);
+ struct of_device *parent = to_of_device(op->dev.parent);
+ struct device_node *parent_dp = parent->node;
int err;
- if (sdev->parent) {
- struct of_device *parent = &sdev->parent->ofdev;
-
- if (!strcmp(parent->node->name, "ledma")) {
- struct sbus_dma *ledma = find_ledma(to_sbus_device(&parent->dev));
-
- err = sparc_lance_probe_one(sdev, ledma, NULL);
- } else if (!strcmp(parent->node->name, "lebuffer")) {
- err = sparc_lance_probe_one(sdev, NULL, to_sbus_device(&parent->dev));
- } else
- err = sparc_lance_probe_one(sdev, NULL, NULL);
+ if (!strcmp(parent_dp->name, "ledma")) {
+ err = sparc_lance_probe_one(op, parent, NULL);
+ } else if (!strcmp(parent_dp->name, "lebuffer")) {
+ err = sparc_lance_probe_one(op, NULL, parent);
} else
- err = sparc_lance_probe_one(sdev, NULL, NULL);
+ err = sparc_lance_probe_one(op, NULL, NULL);
return err;
}
-static int __devexit sunlance_sbus_remove(struct of_device *dev)
+static int __devexit sunlance_sbus_remove(struct of_device *op)
{
- struct lance_private *lp = dev_get_drvdata(&dev->dev);
+ struct lance_private *lp = dev_get_drvdata(&op->dev);
struct net_device *net_dev = lp->dev;
unregister_netdev(net_dev);
@@ -1568,12 +1529,12 @@
free_netdev(net_dev);
- dev_set_drvdata(&dev->dev, NULL);
+ dev_set_drvdata(&op->dev, NULL);
return 0;
}
-static struct of_device_id sunlance_sbus_match[] = {
+static const struct of_device_id sunlance_sbus_match[] = {
{
.name = "le",
},
@@ -1593,17 +1554,12 @@
/* Find all the lance cards on the system and initialize them */
static int __init sparc_lance_init(void)
{
- return of_register_driver(&sunlance_sbus_driver, &sbus_bus_type);
+ return of_register_driver(&sunlance_sbus_driver, &of_bus_type);
}
-#endif /* !CONFIG_SUN4 */
static void __exit sparc_lance_exit(void)
{
-#ifdef CONFIG_SUN4
- sunlance_sun4_remove();
-#else
of_unregister_driver(&sunlance_sbus_driver);
-#endif
}
module_init(sparc_lance_init);
diff --git a/drivers/net/sunqe.c b/drivers/net/sunqe.c
index e811331..f636447 100644
--- a/drivers/net/sunqe.c
+++ b/drivers/net/sunqe.c
@@ -3,7 +3,7 @@
* controller out there can be most efficiently programmed
* if you make it look like a LANCE.
*
- * Copyright (C) 1996, 1999, 2003, 2006 David S. Miller (davem@davemloft.net)
+ * Copyright (C) 1996, 1999, 2003, 2006, 2008 David S. Miller (davem@davemloft.net)
*/
#include <linux/module.h>
@@ -24,13 +24,15 @@
#include <linux/skbuff.h>
#include <linux/ethtool.h>
#include <linux/bitops.h>
+#include <linux/dma-mapping.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/byteorder.h>
#include <asm/idprom.h>
-#include <asm/sbus.h>
#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/auxio.h>
@@ -40,8 +42,8 @@
#include "sunqe.h"
#define DRV_NAME "sunqe"
-#define DRV_VERSION "4.0"
-#define DRV_RELDATE "June 23, 2006"
+#define DRV_VERSION "4.1"
+#define DRV_RELDATE "August 27, 2008"
#define DRV_AUTHOR "David S. Miller (davem@davemloft.net)"
static char version[] =
@@ -690,12 +692,18 @@
/* Ethtool support... */
static void qe_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
{
+ const struct linux_prom_registers *regs;
struct sunqe *qep = dev->priv;
+ struct of_device *op;
strcpy(info->driver, "sunqe");
strcpy(info->version, "3.0");
- sprintf(info->bus_info, "SBUS:%d",
- qep->qe_sdev->slot);
+
+ op = qep->op;
+ regs = of_get_property(op->node, "reg", NULL);
+ if (regs)
+ sprintf(info->bus_info, "SBUS:%d", regs->which_io);
+
}
static u32 qe_get_link(struct net_device *dev)
@@ -717,11 +725,11 @@
};
/* This is only called once at boot time for each card probed. */
-static inline void qec_init_once(struct sunqec *qecp, struct sbus_dev *qsdev)
+static void qec_init_once(struct sunqec *qecp, struct of_device *op)
{
u8 bsizes = qecp->qec_bursts;
- if (sbus_can_burst64(qsdev) && (bsizes & DMA_BURST64)) {
+ if (sbus_can_burst64() && (bsizes & DMA_BURST64)) {
sbus_writel(GLOB_CTRL_B64, qecp->gregs + GLOB_CTRL);
} else if (bsizes & DMA_BURST32) {
sbus_writel(GLOB_CTRL_B32, qecp->gregs + GLOB_CTRL);
@@ -735,15 +743,15 @@
sbus_writel(GLOB_PSIZE_2048, qecp->gregs + GLOB_PSIZE);
/* Set the local memsize register, divided up to one piece per QE channel. */
- sbus_writel((qsdev->reg_addrs[1].reg_size >> 2),
+ sbus_writel((resource_size(&op->resource[1]) >> 2),
qecp->gregs + GLOB_MSIZE);
/* Divide up the local QEC memory amongst the 4 QE receiver and
* transmitter FIFOs. Basically it is (total / 2 / num_channels).
*/
- sbus_writel((qsdev->reg_addrs[1].reg_size >> 2) >> 1,
+ sbus_writel((resource_size(&op->resource[1]) >> 2) >> 1,
qecp->gregs + GLOB_TSIZE);
- sbus_writel((qsdev->reg_addrs[1].reg_size >> 2) >> 1,
+ sbus_writel((resource_size(&op->resource[1]) >> 2) >> 1,
qecp->gregs + GLOB_RSIZE);
}
@@ -767,24 +775,21 @@
return bsizes;
}
-static struct sunqec * __devinit get_qec(struct sbus_dev *child_sdev)
+static struct sunqec * __devinit get_qec(struct of_device *child)
{
- struct sbus_dev *qec_sdev = child_sdev->parent;
+ struct of_device *op = to_of_device(child->dev.parent);
struct sunqec *qecp;
- for (qecp = root_qec_dev; qecp; qecp = qecp->next_module) {
- if (qecp->qec_sdev == qec_sdev)
- break;
- }
+ qecp = dev_get_drvdata(&op->dev);
if (!qecp) {
qecp = kzalloc(sizeof(struct sunqec), GFP_KERNEL);
if (qecp) {
u32 ctrl;
- qecp->qec_sdev = qec_sdev;
- qecp->gregs = sbus_ioremap(&qec_sdev->resource[0], 0,
- GLOB_REG_SIZE,
- "QEC Global Registers");
+ qecp->op = op;
+ qecp->gregs = of_ioremap(&op->resource[0], 0,
+ GLOB_REG_SIZE,
+ "QEC Global Registers");
if (!qecp->gregs)
goto fail;
@@ -799,16 +804,18 @@
if (qec_global_reset(qecp->gregs))
goto fail;
- qecp->qec_bursts = qec_get_burst(qec_sdev->ofdev.node);
+ qecp->qec_bursts = qec_get_burst(op->node);
- qec_init_once(qecp, qec_sdev);
+ qec_init_once(qecp, op);
- if (request_irq(qec_sdev->irqs[0], &qec_interrupt,
+ if (request_irq(op->irqs[0], &qec_interrupt,
IRQF_SHARED, "qec", (void *) qecp)) {
printk(KERN_ERR "qec: Can't register irq.\n");
goto fail;
}
+ dev_set_drvdata(&op->dev, qecp);
+
qecp->next_module = root_qec_dev;
root_qec_dev = qecp;
}
@@ -818,17 +825,17 @@
fail:
if (qecp->gregs)
- sbus_iounmap(qecp->gregs, GLOB_REG_SIZE);
+ of_iounmap(&op->resource[0], qecp->gregs, GLOB_REG_SIZE);
kfree(qecp);
return NULL;
}
-static int __devinit qec_ether_init(struct sbus_dev *sdev)
+static int __devinit qec_ether_init(struct of_device *op)
{
static unsigned version_printed;
struct net_device *dev;
- struct sunqe *qe;
struct sunqec *qecp;
+ struct sunqe *qe;
int i, res;
if (version_printed++ == 0)
@@ -842,49 +849,42 @@
qe = netdev_priv(dev);
- i = of_getintprop_default(sdev->ofdev.node, "channel#", -1);
- if (i == -1) {
- struct sbus_dev *td = sdev->parent->child;
- i = 0;
- while (td != sdev) {
- td = td->next;
- i++;
- }
- }
+ res = -ENODEV;
+
+ i = of_getintprop_default(op->node, "channel#", -1);
+ if (i == -1)
+ goto fail;
qe->channel = i;
spin_lock_init(&qe->lock);
- res = -ENODEV;
- qecp = get_qec(sdev);
+ qecp = get_qec(op);
if (!qecp)
goto fail;
qecp->qes[qe->channel] = qe;
qe->dev = dev;
qe->parent = qecp;
- qe->qe_sdev = sdev;
+ qe->op = op;
res = -ENOMEM;
- qe->qcregs = sbus_ioremap(&qe->qe_sdev->resource[0], 0,
- CREG_REG_SIZE, "QEC Channel Registers");
+ qe->qcregs = of_ioremap(&op->resource[0], 0,
+ CREG_REG_SIZE, "QEC Channel Registers");
if (!qe->qcregs) {
printk(KERN_ERR "qe: Cannot map channel registers.\n");
goto fail;
}
- qe->mregs = sbus_ioremap(&qe->qe_sdev->resource[1], 0,
- MREGS_REG_SIZE, "QE MACE Registers");
+ qe->mregs = of_ioremap(&op->resource[1], 0,
+ MREGS_REG_SIZE, "QE MACE Registers");
if (!qe->mregs) {
printk(KERN_ERR "qe: Cannot map MACE registers.\n");
goto fail;
}
- qe->qe_block = sbus_alloc_consistent(qe->qe_sdev,
- PAGE_SIZE,
- &qe->qblock_dvma);
- qe->buffers = sbus_alloc_consistent(qe->qe_sdev,
- sizeof(struct sunqe_buffers),
- &qe->buffers_dvma);
+ qe->qe_block = dma_alloc_coherent(&op->dev, PAGE_SIZE,
+ &qe->qblock_dvma, GFP_ATOMIC);
+ qe->buffers = dma_alloc_coherent(&op->dev, sizeof(struct sunqe_buffers),
+ &qe->buffers_dvma, GFP_ATOMIC);
if (qe->qe_block == NULL || qe->qblock_dvma == 0 ||
qe->buffers == NULL || qe->buffers_dvma == 0)
goto fail;
@@ -892,7 +892,7 @@
/* Stop this QE. */
qe_stop(qe);
- SET_NETDEV_DEV(dev, &sdev->ofdev.dev);
+ SET_NETDEV_DEV(dev, &op->dev);
dev->open = qe_open;
dev->stop = qe_close;
@@ -900,7 +900,7 @@
dev->set_multicast_list = qe_set_multicast;
dev->tx_timeout = qe_tx_timeout;
dev->watchdog_timeo = 5*HZ;
- dev->irq = sdev->irqs[0];
+ dev->irq = op->irqs[0];
dev->dma = 0;
dev->ethtool_ops = &qe_ethtool_ops;
@@ -908,7 +908,7 @@
if (res)
goto fail;
- dev_set_drvdata(&sdev->ofdev.dev, qe);
+ dev_set_drvdata(&op->dev, qe);
printk(KERN_INFO "%s: qe channel[%d] ", dev->name, qe->channel);
for (i = 0; i < 6; i++)
@@ -922,58 +922,50 @@
fail:
if (qe->qcregs)
- sbus_iounmap(qe->qcregs, CREG_REG_SIZE);
+ of_iounmap(&op->resource[0], qe->qcregs, CREG_REG_SIZE);
if (qe->mregs)
- sbus_iounmap(qe->mregs, MREGS_REG_SIZE);
+ of_iounmap(&op->resource[1], qe->mregs, MREGS_REG_SIZE);
if (qe->qe_block)
- sbus_free_consistent(qe->qe_sdev,
- PAGE_SIZE,
- qe->qe_block,
- qe->qblock_dvma);
+ dma_free_coherent(&op->dev, PAGE_SIZE,
+ qe->qe_block, qe->qblock_dvma);
if (qe->buffers)
- sbus_free_consistent(qe->qe_sdev,
- sizeof(struct sunqe_buffers),
- qe->buffers,
- qe->buffers_dvma);
+ dma_free_coherent(&op->dev,
+ sizeof(struct sunqe_buffers),
+ qe->buffers,
+ qe->buffers_dvma);
free_netdev(dev);
return res;
}
-static int __devinit qec_sbus_probe(struct of_device *dev, const struct of_device_id *match)
+static int __devinit qec_sbus_probe(struct of_device *op, const struct of_device_id *match)
{
- struct sbus_dev *sdev = to_sbus_device(&dev->dev);
-
- return qec_ether_init(sdev);
+ return qec_ether_init(op);
}
-static int __devexit qec_sbus_remove(struct of_device *dev)
+static int __devexit qec_sbus_remove(struct of_device *op)
{
- struct sunqe *qp = dev_get_drvdata(&dev->dev);
+ struct sunqe *qp = dev_get_drvdata(&op->dev);
struct net_device *net_dev = qp->dev;
unregister_netdev(net_dev);
- sbus_iounmap(qp->qcregs, CREG_REG_SIZE);
- sbus_iounmap(qp->mregs, MREGS_REG_SIZE);
- sbus_free_consistent(qp->qe_sdev,
- PAGE_SIZE,
- qp->qe_block,
- qp->qblock_dvma);
- sbus_free_consistent(qp->qe_sdev,
- sizeof(struct sunqe_buffers),
- qp->buffers,
- qp->buffers_dvma);
+ of_iounmap(&op->resource[0], qp->qcregs, CREG_REG_SIZE);
+ of_iounmap(&op->resource[1], qp->mregs, MREGS_REG_SIZE);
+ dma_free_coherent(&op->dev, PAGE_SIZE,
+ qp->qe_block, qp->qblock_dvma);
+ dma_free_coherent(&op->dev, sizeof(struct sunqe_buffers),
+ qp->buffers, qp->buffers_dvma);
free_netdev(net_dev);
- dev_set_drvdata(&dev->dev, NULL);
+ dev_set_drvdata(&op->dev, NULL);
return 0;
}
-static struct of_device_id qec_sbus_match[] = {
+static const struct of_device_id qec_sbus_match[] = {
{
.name = "qe",
},
@@ -991,7 +983,7 @@
static int __init qec_init(void)
{
- return of_register_driver(&qec_sbus_driver, &sbus_bus_type);
+ return of_register_driver(&qec_sbus_driver, &of_bus_type);
}
static void __exit qec_exit(void)
@@ -1000,11 +992,11 @@
while (root_qec_dev) {
struct sunqec *next = root_qec_dev->next_module;
+ struct of_device *op = root_qec_dev->op;
- free_irq(root_qec_dev->qec_sdev->irqs[0],
- (void *) root_qec_dev);
- sbus_iounmap(root_qec_dev->gregs, GLOB_REG_SIZE);
-
+ free_irq(op->irqs[0], (void *) root_qec_dev);
+ of_iounmap(&op->resource[0], root_qec_dev->gregs,
+ GLOB_REG_SIZE);
kfree(root_qec_dev);
root_qec_dev = next;
diff --git a/drivers/net/sunqe.h b/drivers/net/sunqe.h
index 347c8dd..5813a7b 100644
--- a/drivers/net/sunqe.h
+++ b/drivers/net/sunqe.h
@@ -314,7 +314,7 @@
void __iomem *gregs; /* QEC Global Registers */
struct sunqe *qes[4]; /* Each child MACE */
unsigned int qec_bursts; /* Support burst sizes */
- struct sbus_dev *qec_sdev; /* QEC's SBUS device */
+ struct of_device *op; /* QEC's OF device */
struct sunqec *next_module; /* List of all QECs in system */
};
@@ -342,7 +342,7 @@
__u32 buffers_dvma; /* DVMA visible address. */
struct sunqec *parent;
u8 mconfig; /* Base MACE mconfig value */
- struct sbus_dev *qe_sdev; /* QE's SBUS device struct */
+ struct of_device *op; /* QE's OF device struct */
struct net_device *dev; /* QE's netdevice struct */
int channel; /* Who am I? */
};
diff --git a/drivers/net/sunvnet.c b/drivers/net/sunvnet.c
index 6415ce1..a720065 100644
--- a/drivers/net/sunvnet.c
+++ b/drivers/net/sunvnet.c
@@ -1,6 +1,6 @@
/* sunvnet.c: Sun LDOM Virtual Network Driver.
*
- * Copyright (C) 2007 David S. Miller <davem@davemloft.net>
+ * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net>
*/
#include <linux/module.h>
@@ -1260,7 +1260,7 @@
return 0;
}
-static struct vio_device_id vnet_port_match[] = {
+static const struct vio_device_id vnet_port_match[] = {
{
.type = "vnet-port",
},
diff --git a/drivers/net/wan/Kconfig b/drivers/net/wan/Kconfig
index 2ae2ec4..21efd99 100644
--- a/drivers/net/wan/Kconfig
+++ b/drivers/net/wan/Kconfig
@@ -205,7 +205,7 @@
config PC300
tristate "Cyclades-PC300 support (RS-232/V.35, X.21, T1/E1 boards)"
- depends on HDLC && PCI
+ depends on HDLC && PCI && BROKEN
---help---
Driver for the Cyclades-PC300 synchronous communication boards.
diff --git a/drivers/net/wireless/airo_cs.c b/drivers/net/wireless/airo_cs.c
index fd72e42..27696c2 100644
--- a/drivers/net/wireless/airo_cs.c
+++ b/drivers/net/wireless/airo_cs.c
@@ -206,126 +206,123 @@
#define CS_CHECK(fn, ret) \
do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+static int airo_cs_config_check(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ win_req_t *req = priv_data;
+
+ if (cfg->index == 0)
+ return -ENODEV;
+
+ /* Does this card need audio output? */
+ if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
+ p_dev->conf.Attributes |= CONF_ENABLE_SPKR;
+ p_dev->conf.Status = CCSR_AUDIO_ENA;
+ }
+
+ /* Use power settings for Vcc and Vpp if present */
+ /* Note that the CIS values need to be rescaled */
+ if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM))
+ p_dev->conf.Vpp = cfg->vpp1.param[CISTPL_POWER_VNOM]/10000;
+ else if (dflt->vpp1.present & (1<<CISTPL_POWER_VNOM))
+ p_dev->conf.Vpp = dflt->vpp1.param[CISTPL_POWER_VNOM]/10000;
+
+ /* Do we need to allocate an interrupt? */
+ if (cfg->irq.IRQInfo1 || dflt->irq.IRQInfo1)
+ p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
+
+ /* IO window settings */
+ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0;
+ if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(io->flags & CISTPL_IO_8BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(io->flags & CISTPL_IO_16BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ p_dev->io.BasePort1 = io->win[0].base;
+ p_dev->io.NumPorts1 = io->win[0].len;
+ if (io->nwin > 1) {
+ p_dev->io.Attributes2 = p_dev->io.Attributes1;
+ p_dev->io.BasePort2 = io->win[1].base;
+ p_dev->io.NumPorts2 = io->win[1].len;
+ }
+ }
+
+ /* This reserves IO space but doesn't actually enable it */
+ if (pcmcia_request_io(p_dev, &p_dev->io) != 0)
+ return -ENODEV;
+
+ /*
+ Now set up a common memory window, if needed. There is room
+ in the struct pcmcia_device structure for one memory window handle,
+ but if the base addresses need to be saved, or if multiple
+ windows are needed, the info should go in the private data
+ structure for this device.
+
+ Note that the memory window base is a physical address, and
+ needs to be mapped to virtual space with ioremap() before it
+ is used.
+ */
+ if ((cfg->mem.nwin > 0) || (dflt->mem.nwin > 0)) {
+ cistpl_mem_t *mem = (cfg->mem.nwin) ? &cfg->mem : &dflt->mem;
+ memreq_t map;
+ req->Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM;
+ req->Base = mem->win[0].host_addr;
+ req->Size = mem->win[0].len;
+ req->AccessSpeed = 0;
+ if (pcmcia_request_window(&p_dev, req, &p_dev->win) != 0)
+ return -ENODEV;
+ map.Page = 0;
+ map.CardOffset = mem->win[0].card_addr;
+ if (pcmcia_map_mem_page(p_dev->win, &map) != 0)
+ return -ENODEV;
+ }
+ /* If we got this far, we're cool! */
+ return 0;
+}
+
+
static int airo_config(struct pcmcia_device *link)
{
- tuple_t tuple;
- cisparse_t parse;
local_info_t *dev;
+ win_req_t *req;
int last_fn, last_ret;
- u_char buf[64];
- win_req_t req;
- memreq_t map;
dev = link->priv;
DEBUG(0, "airo_config(0x%p)\n", link);
+ req = kzalloc(sizeof(win_req_t), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
/*
- In this loop, we scan the CIS for configuration table entries,
- each of which describes a valid card configuration, including
- voltage, IO window, memory window, and interrupt settings.
-
- We make no assumptions about the card to be configured: we use
- just the information available in the CIS. In an ideal world,
- this would work for any PCMCIA card, but it requires a complete
- and accurate CIS. In practice, a driver usually "knows" most of
- these things without consulting the CIS, and most client drivers
- will only use the CIS to fill in implementation-defined details.
+ * In this loop, we scan the CIS for configuration table
+ * entries, each of which describes a valid card
+ * configuration, including voltage, IO window, memory window,
+ * and interrupt settings.
+ *
+ * We make no assumptions about the card to be configured: we
+ * use just the information available in the CIS. In an ideal
+ * world, this would work for any PCMCIA card, but it requires
+ * a complete and accurate CIS. In practice, a driver usually
+ * "knows" most of these things without consulting the CIS,
+ * and most client drivers will only use the CIS to fill in
+ * implementation-defined details.
+ */
+ last_ret = pcmcia_loop_config(link, airo_cs_config_check, req);
+ if (last_ret)
+ goto failed;
+
+ /*
+ Allocate an interrupt line. Note that this does not assign a
+ handler to the interrupt, unless the 'Handler' member of the
+ irq structure is initialized.
*/
- tuple.Attributes = 0;
- tuple.TupleData = buf;
- tuple.TupleDataMax = sizeof(buf);
- tuple.TupleOffset = 0;
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
- while (1) {
- cistpl_cftable_entry_t dflt = { 0 };
- cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
- if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
- pcmcia_parse_tuple(link, &tuple, &parse) != 0)
- goto next_entry;
-
- if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg;
- if (cfg->index == 0) goto next_entry;
- link->conf.ConfigIndex = cfg->index;
-
- /* Does this card need audio output? */
- if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
- link->conf.Attributes |= CONF_ENABLE_SPKR;
- link->conf.Status = CCSR_AUDIO_ENA;
- }
-
- /* Use power settings for Vcc and Vpp if present */
- /* Note that the CIS values need to be rescaled */
- if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM))
- link->conf.Vpp =
- cfg->vpp1.param[CISTPL_POWER_VNOM]/10000;
- else if (dflt.vpp1.present & (1<<CISTPL_POWER_VNOM))
- link->conf.Vpp =
- dflt.vpp1.param[CISTPL_POWER_VNOM]/10000;
-
- /* Do we need to allocate an interrupt? */
- if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
- link->conf.Attributes |= CONF_ENABLE_IRQ;
-
- /* IO window settings */
- link->io.NumPorts1 = link->io.NumPorts2 = 0;
- if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
- cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
- if (!(io->flags & CISTPL_IO_8BIT))
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
- if (!(io->flags & CISTPL_IO_16BIT))
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
- link->io.BasePort1 = io->win[0].base;
- link->io.NumPorts1 = io->win[0].len;
- if (io->nwin > 1) {
- link->io.Attributes2 = link->io.Attributes1;
- link->io.BasePort2 = io->win[1].base;
- link->io.NumPorts2 = io->win[1].len;
- }
- }
-
- /* This reserves IO space but doesn't actually enable it */
- if (pcmcia_request_io(link, &link->io) != 0)
- goto next_entry;
-
- /*
- Now set up a common memory window, if needed. There is room
- in the struct pcmcia_device structure for one memory window handle,
- but if the base addresses need to be saved, or if multiple
- windows are needed, the info should go in the private data
- structure for this device.
-
- Note that the memory window base is a physical address, and
- needs to be mapped to virtual space with ioremap() before it
- is used.
- */
- if ((cfg->mem.nwin > 0) || (dflt.mem.nwin > 0)) {
- cistpl_mem_t *mem =
- (cfg->mem.nwin) ? &cfg->mem : &dflt.mem;
- req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM;
- req.Base = mem->win[0].host_addr;
- req.Size = mem->win[0].len;
- req.AccessSpeed = 0;
- if (pcmcia_request_window(&link, &req, &link->win) != 0)
- goto next_entry;
- map.Page = 0; map.CardOffset = mem->win[0].card_addr;
- if (pcmcia_map_mem_page(link->win, &map) != 0)
- goto next_entry;
- }
- /* If we got this far, we're cool! */
- break;
-
- next_entry:
- CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple));
- }
-
- /*
- Allocate an interrupt line. Note that this does not assign a
- handler to the interrupt, unless the 'Handler' member of the
- irq structure is initialized.
- */
if (link->conf.Attributes & CONF_ENABLE_IRQ)
CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
@@ -362,14 +359,17 @@
printk(" & 0x%04x-0x%04x", link->io.BasePort2,
link->io.BasePort2+link->io.NumPorts2-1);
if (link->win)
- printk(", mem 0x%06lx-0x%06lx", req.Base,
- req.Base+req.Size-1);
+ printk(", mem 0x%06lx-0x%06lx", req->Base,
+ req->Base+req->Size-1);
printk("\n");
+ kfree(req);
return 0;
cs_failed:
cs_error(link, last_fn, last_ret);
+ failed:
airo_release(link);
+ kfree(req);
return -ENODEV;
} /* airo_config */
diff --git a/drivers/net/wireless/atmel_cs.c b/drivers/net/wireless/atmel_cs.c
index d2388e8..7740624 100644
--- a/drivers/net/wireless/atmel_cs.c
+++ b/drivers/net/wireless/atmel_cs.c
@@ -224,13 +224,58 @@
return 0;
}
+static int atmel_config_check(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ if (cfg->index == 0)
+ return -ENODEV;
+
+ /* Does this card need audio output? */
+ if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
+ p_dev->conf.Attributes |= CONF_ENABLE_SPKR;
+ p_dev->conf.Status = CCSR_AUDIO_ENA;
+ }
+
+ /* Use power settings for Vcc and Vpp if present */
+ /* Note that the CIS values need to be rescaled */
+ if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM))
+ p_dev->conf.Vpp = cfg->vpp1.param[CISTPL_POWER_VNOM]/10000;
+ else if (dflt->vpp1.present & (1<<CISTPL_POWER_VNOM))
+ p_dev->conf.Vpp = dflt->vpp1.param[CISTPL_POWER_VNOM]/10000;
+
+ /* Do we need to allocate an interrupt? */
+ if (cfg->irq.IRQInfo1 || dflt->irq.IRQInfo1)
+ p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
+
+ /* IO window settings */
+ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0;
+ if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(io->flags & CISTPL_IO_8BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(io->flags & CISTPL_IO_16BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ p_dev->io.BasePort1 = io->win[0].base;
+ p_dev->io.NumPorts1 = io->win[0].len;
+ if (io->nwin > 1) {
+ p_dev->io.Attributes2 = p_dev->io.Attributes1;
+ p_dev->io.BasePort2 = io->win[1].base;
+ p_dev->io.NumPorts2 = io->win[1].len;
+ }
+ }
+
+ /* This reserves IO space but doesn't actually enable it */
+ return pcmcia_request_io(p_dev, &p_dev->io);
+}
+
static int atmel_config(struct pcmcia_device *link)
{
- tuple_t tuple;
- cisparse_t parse;
local_info_t *dev;
int last_fn, last_ret;
- u_char buf[64];
struct pcmcia_device_id *did;
dev = link->priv;
@@ -238,11 +283,6 @@
DEBUG(0, "atmel_config(0x%p)\n", link);
- tuple.Attributes = 0;
- tuple.TupleData = buf;
- tuple.TupleDataMax = sizeof(buf);
- tuple.TupleOffset = 0;
-
/*
In this loop, we scan the CIS for configuration table entries,
each of which describes a valid card configuration, including
@@ -255,66 +295,8 @@
these things without consulting the CIS, and most client drivers
will only use the CIS to fill in implementation-defined details.
*/
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
- while (1) {
- cistpl_cftable_entry_t dflt = { 0 };
- cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
- if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
- pcmcia_parse_tuple(link, &tuple, &parse) != 0)
- goto next_entry;
-
- if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg;
- if (cfg->index == 0) goto next_entry;
- link->conf.ConfigIndex = cfg->index;
-
- /* Does this card need audio output? */
- if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
- link->conf.Attributes |= CONF_ENABLE_SPKR;
- link->conf.Status = CCSR_AUDIO_ENA;
- }
-
- /* Use power settings for Vcc and Vpp if present */
- /* Note that the CIS values need to be rescaled */
- if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM))
- link->conf.Vpp =
- cfg->vpp1.param[CISTPL_POWER_VNOM]/10000;
- else if (dflt.vpp1.present & (1<<CISTPL_POWER_VNOM))
- link->conf.Vpp =
- dflt.vpp1.param[CISTPL_POWER_VNOM]/10000;
-
- /* Do we need to allocate an interrupt? */
- if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
- link->conf.Attributes |= CONF_ENABLE_IRQ;
-
- /* IO window settings */
- link->io.NumPorts1 = link->io.NumPorts2 = 0;
- if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
- cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
- if (!(io->flags & CISTPL_IO_8BIT))
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
- if (!(io->flags & CISTPL_IO_16BIT))
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
- link->io.BasePort1 = io->win[0].base;
- link->io.NumPorts1 = io->win[0].len;
- if (io->nwin > 1) {
- link->io.Attributes2 = link->io.Attributes1;
- link->io.BasePort2 = io->win[1].base;
- link->io.NumPorts2 = io->win[1].len;
- }
- }
-
- /* This reserves IO space but doesn't actually enable it */
- if (pcmcia_request_io(link, &link->io) != 0)
- goto next_entry;
-
- /* If we got this far, we're cool! */
- break;
-
- next_entry:
- CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple));
- }
+ if (pcmcia_loop_config(link, atmel_config_check, NULL))
+ goto failed;
/*
Allocate an interrupt line. Note that this does not assign a
@@ -360,6 +342,7 @@
cs_failed:
cs_error(link, last_fn, last_ret);
+ failed:
atmel_release(link);
return -ENODEV;
}
diff --git a/drivers/net/wireless/b43/pcmcia.c b/drivers/net/wireless/b43/pcmcia.c
index b8aa163..3cfc303 100644
--- a/drivers/net/wireless/b43/pcmcia.c
+++ b/drivers/net/wireless/b43/pcmcia.c
@@ -82,13 +82,13 @@
tuple.TupleOffset = 0;
res = pcmcia_get_first_tuple(dev, &tuple);
- if (res != CS_SUCCESS)
+ if (res != 0)
goto err_kfree_ssb;
res = pcmcia_get_tuple_data(dev, &tuple);
- if (res != CS_SUCCESS)
+ if (res != 0)
goto err_kfree_ssb;
- res = pcmcia_parse_tuple(dev, &tuple, &parse);
- if (res != CS_SUCCESS)
+ res = pcmcia_parse_tuple(&tuple, &parse);
+ if (res != 0)
goto err_kfree_ssb;
dev->conf.ConfigBase = parse.config.base;
@@ -107,13 +107,13 @@
win.Size = SSB_CORE_SIZE;
win.AccessSpeed = 250;
res = pcmcia_request_window(&dev, &win, &dev->win);
- if (res != CS_SUCCESS)
+ if (res != 0)
goto err_kfree_ssb;
mem.CardOffset = 0;
mem.Page = 0;
res = pcmcia_map_mem_page(dev->win, &mem);
- if (res != CS_SUCCESS)
+ if (res != 0)
goto err_disable;
dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
@@ -121,11 +121,11 @@
dev->irq.Handler = NULL; /* The handler is registered later. */
dev->irq.Instance = NULL;
res = pcmcia_request_irq(dev, &dev->irq);
- if (res != CS_SUCCESS)
+ if (res != 0)
goto err_disable;
res = pcmcia_request_configuration(dev, &dev->conf);
- if (res != CS_SUCCESS)
+ if (res != 0)
goto err_disable;
err = ssb_bus_pcmciabus_register(ssb, dev, win.Base);
diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c
index 3b4e55c..6337402 100644
--- a/drivers/net/wireless/hostap/hostap_cs.c
+++ b/drivers/net/wireless/hostap/hostap_cs.c
@@ -234,7 +234,7 @@
reg.Value = hw_priv->link->io.BasePort1 & 0x00ff;
res = pcmcia_access_configuration_register(hw_priv->link,
®);
- if (res != CS_SUCCESS) {
+ if (res != 0) {
printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 0 -"
" res=%d\n", res);
}
@@ -246,7 +246,7 @@
reg.Value = (hw_priv->link->io.BasePort1 & 0xff00) >> 8;
res = pcmcia_access_configuration_register(hw_priv->link,
®);
- if (res != CS_SUCCESS) {
+ if (res != 0) {
printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 1 -"
" res=%d\n", res);
}
@@ -305,7 +305,7 @@
tuple.DesiredTuple = CISTPL_LONGLINK_MFC;
if (pcmcia_get_first_tuple(hw_priv->link, &tuple) ||
pcmcia_get_tuple_data(hw_priv->link, &tuple) ||
- pcmcia_parse_tuple(hw_priv->link, &tuple, parse) ||
+ pcmcia_parse_tuple(&tuple, parse) ||
parse->longlink_mfc.nfn < 2) {
/* No multi-function links found */
ret = -ENODEV;
@@ -322,7 +322,7 @@
reg.Value = COR_SOFT_RESET;
res = pcmcia_access_configuration_register(hw_priv->link,
®);
- if (res != CS_SUCCESS) {
+ if (res != 0) {
printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n",
dev->name, res);
goto done;
@@ -339,7 +339,7 @@
reg.Value = COR_LEVEL_REQ | 0x8 | COR_ADDR_DECODE | COR_FUNC_ENA;
res = pcmcia_access_configuration_register(hw_priv->link,
®);
- if (res != CS_SUCCESS) {
+ if (res != 0) {
printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n",
dev->name, res);
goto done;
@@ -374,7 +374,7 @@
reg.Value = 0;
res = pcmcia_access_configuration_register(hw_priv->link,
®);
- if (res != CS_SUCCESS) {
+ if (res != 0) {
printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 1 (%d)\n",
res);
return;
@@ -386,7 +386,7 @@
reg.Value |= COR_SOFT_RESET;
res = pcmcia_access_configuration_register(hw_priv->link,
®);
- if (res != CS_SUCCESS) {
+ if (res != 0) {
printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 2 (%d)\n",
res);
return;
@@ -399,7 +399,7 @@
reg.Value |= COR_IREQ_ENA;
res = pcmcia_access_configuration_register(hw_priv->link,
®);
- if (res != CS_SUCCESS) {
+ if (res != 0) {
printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 3 (%d)\n",
res);
return;
@@ -433,7 +433,7 @@
reg.Value = 0;
res = pcmcia_access_configuration_register(hw_priv->link,
®);
- if (res != CS_SUCCESS) {
+ if (res != 0) {
printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 1 "
"(%d)\n", res);
return;
@@ -446,7 +446,7 @@
reg.Value |= COR_SOFT_RESET;
res = pcmcia_access_configuration_register(hw_priv->link,
®);
- if (res != CS_SUCCESS) {
+ if (res != 0) {
printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 2 "
"(%d)\n", res);
return;
@@ -460,7 +460,7 @@
reg.Offset = CISREG_CCSR;
res = pcmcia_access_configuration_register(hw_priv->link,
®);
- if (res != CS_SUCCESS) {
+ if (res != 0) {
printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 3 "
"(%d)\n", res);
return;
@@ -472,7 +472,7 @@
reg.Value = old_cor & ~COR_SOFT_RESET;
res = pcmcia_access_configuration_register(hw_priv->link,
®);
- if (res != CS_SUCCESS) {
+ if (res != 0) {
printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 4 "
"(%d)\n", res);
return;
@@ -532,145 +532,118 @@
#define CS_CHECK(fn, ret) \
do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-#define CFG_CHECK2(fn, retf) \
-do { int _ret = (retf); \
-if (_ret != 0) { \
- PDEBUG(DEBUG_EXTRA, "CardServices(" #fn ") returned %d\n", _ret); \
- cs_error(link, fn, _ret); \
- goto next_entry; \
-} \
-} while (0)
-
/* run after a CARD_INSERTION event is received to configure the PCMCIA
* socket and make the device available to the system */
+
+static int prism2_config_check(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ if (cfg->index == 0)
+ return -ENODEV;
+
+ PDEBUG(DEBUG_EXTRA, "Checking CFTABLE_ENTRY 0x%02X "
+ "(default 0x%02X)\n", cfg->index, dflt->index);
+
+ /* Does this card need audio output? */
+ if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
+ p_dev->conf.Attributes |= CONF_ENABLE_SPKR;
+ p_dev->conf.Status = CCSR_AUDIO_ENA;
+ }
+
+ /* Use power settings for Vcc and Vpp if present */
+ /* Note that the CIS values need to be rescaled */
+ if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
+ if (vcc != cfg->vcc.param[CISTPL_POWER_VNOM] /
+ 10000 && !ignore_cis_vcc) {
+ PDEBUG(DEBUG_EXTRA, " Vcc mismatch - skipping"
+ " this entry\n");
+ return -ENODEV;
+ }
+ } else if (dflt->vcc.present & (1 << CISTPL_POWER_VNOM)) {
+ if (vcc != dflt->vcc.param[CISTPL_POWER_VNOM] /
+ 10000 && !ignore_cis_vcc) {
+ PDEBUG(DEBUG_EXTRA, " Vcc (default) mismatch "
+ "- skipping this entry\n");
+ return -ENODEV;
+ }
+ }
+
+ if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ p_dev->conf.Vpp = cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+ else if (dflt->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ p_dev->conf.Vpp = dflt->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+
+ /* Do we need to allocate an interrupt? */
+ if (cfg->irq.IRQInfo1 || dflt->irq.IRQInfo1)
+ p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
+ else if (!(p_dev->conf.Attributes & CONF_ENABLE_IRQ)) {
+ /* At least Compaq WL200 does not have IRQInfo1 set,
+ * but it does not work without interrupts.. */
+ printk(KERN_WARNING "Config has no IRQ info, but trying to "
+ "enable IRQ anyway..\n");
+ p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
+ }
+
+ /* IO window settings */
+ PDEBUG(DEBUG_EXTRA, "IO window settings: cfg->io.nwin=%d "
+ "dflt->io.nwin=%d\n",
+ cfg->io.nwin, dflt->io.nwin);
+ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0;
+ if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ PDEBUG(DEBUG_EXTRA, "io->flags = 0x%04X, "
+ "io.base=0x%04x, len=%d\n", io->flags,
+ io->win[0].base, io->win[0].len);
+ if (!(io->flags & CISTPL_IO_8BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(io->flags & CISTPL_IO_16BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ p_dev->io.IOAddrLines = io->flags &
+ CISTPL_IO_LINES_MASK;
+ p_dev->io.BasePort1 = io->win[0].base;
+ p_dev->io.NumPorts1 = io->win[0].len;
+ if (io->nwin > 1) {
+ p_dev->io.Attributes2 = p_dev->io.Attributes1;
+ p_dev->io.BasePort2 = io->win[1].base;
+ p_dev->io.NumPorts2 = io->win[1].len;
+ }
+ }
+
+ /* This reserves IO space but doesn't actually enable it */
+ return pcmcia_request_io(p_dev, &p_dev->io);
+}
+
static int prism2_config(struct pcmcia_device *link)
{
struct net_device *dev;
struct hostap_interface *iface;
local_info_t *local;
int ret = 1;
- tuple_t tuple;
- cisparse_t *parse;
int last_fn, last_ret;
- u_char buf[64];
- config_info_t conf;
- cistpl_cftable_entry_t dflt = { 0 };
struct hostap_cs_priv *hw_priv;
PDEBUG(DEBUG_FLOW, "prism2_config()\n");
- parse = kmalloc(sizeof(cisparse_t), GFP_KERNEL);
hw_priv = kzalloc(sizeof(*hw_priv), GFP_KERNEL);
- if (parse == NULL || hw_priv == NULL) {
+ if (hw_priv == NULL) {
ret = -ENOMEM;
goto failed;
}
- tuple.Attributes = 0;
- tuple.TupleData = buf;
- tuple.TupleDataMax = sizeof(buf);
- tuple.TupleOffset = 0;
-
- CS_CHECK(GetConfigurationInfo,
- pcmcia_get_configuration_info(link, &conf));
-
/* Look for an appropriate configuration table entry in the CIS */
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
- for (;;) {
- cistpl_cftable_entry_t *cfg = &(parse->cftable_entry);
- CFG_CHECK2(GetTupleData,
- pcmcia_get_tuple_data(link, &tuple));
- CFG_CHECK2(ParseTuple,
- pcmcia_parse_tuple(link, &tuple, parse));
-
- if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
- dflt = *cfg;
- if (cfg->index == 0)
- goto next_entry;
- link->conf.ConfigIndex = cfg->index;
- PDEBUG(DEBUG_EXTRA, "Checking CFTABLE_ENTRY 0x%02X "
- "(default 0x%02X)\n", cfg->index, dflt.index);
-
- /* Does this card need audio output? */
- if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
- link->conf.Attributes |= CONF_ENABLE_SPKR;
- link->conf.Status = CCSR_AUDIO_ENA;
- }
-
- /* Use power settings for Vcc and Vpp if present */
- /* Note that the CIS values need to be rescaled */
- if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
- if (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM] /
- 10000 && !ignore_cis_vcc) {
- PDEBUG(DEBUG_EXTRA, " Vcc mismatch - skipping"
- " this entry\n");
- goto next_entry;
- }
- } else if (dflt.vcc.present & (1 << CISTPL_POWER_VNOM)) {
- if (conf.Vcc != dflt.vcc.param[CISTPL_POWER_VNOM] /
- 10000 && !ignore_cis_vcc) {
- PDEBUG(DEBUG_EXTRA, " Vcc (default) mismatch "
- "- skipping this entry\n");
- goto next_entry;
- }
- }
-
- if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
- link->conf.Vpp =
- cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
- else if (dflt.vpp1.present & (1 << CISTPL_POWER_VNOM))
- link->conf.Vpp =
- dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000;
-
- /* Do we need to allocate an interrupt? */
- if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
- link->conf.Attributes |= CONF_ENABLE_IRQ;
- else if (!(link->conf.Attributes & CONF_ENABLE_IRQ)) {
- /* At least Compaq WL200 does not have IRQInfo1 set,
- * but it does not work without interrupts.. */
- printk("Config has no IRQ info, but trying to enable "
- "IRQ anyway..\n");
- link->conf.Attributes |= CONF_ENABLE_IRQ;
- }
-
- /* IO window settings */
- PDEBUG(DEBUG_EXTRA, "IO window settings: cfg->io.nwin=%d "
- "dflt.io.nwin=%d\n",
- cfg->io.nwin, dflt.io.nwin);
- link->io.NumPorts1 = link->io.NumPorts2 = 0;
- if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
- cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
- PDEBUG(DEBUG_EXTRA, "io->flags = 0x%04X, "
- "io.base=0x%04x, len=%d\n", io->flags,
- io->win[0].base, io->win[0].len);
- if (!(io->flags & CISTPL_IO_8BIT))
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
- if (!(io->flags & CISTPL_IO_16BIT))
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
- link->io.IOAddrLines = io->flags &
- CISTPL_IO_LINES_MASK;
- link->io.BasePort1 = io->win[0].base;
- link->io.NumPorts1 = io->win[0].len;
- if (io->nwin > 1) {
- link->io.Attributes2 = link->io.Attributes1;
- link->io.BasePort2 = io->win[1].base;
- link->io.NumPorts2 = io->win[1].len;
- }
- }
-
- /* This reserves IO space but doesn't actually enable it */
- CFG_CHECK2(RequestIO,
- pcmcia_request_io(link, &link->io));
-
- /* This configuration table entry is OK */
- break;
-
- next_entry:
- CS_CHECK(GetNextTuple,
- pcmcia_get_next_tuple(link, &tuple));
+ last_ret = pcmcia_loop_config(link, prism2_config_check, NULL);
+ if (last_ret) {
+ if (!ignore_cis_vcc)
+ printk(KERN_ERR "GetNextTuple(): No matching "
+ "CIS configuration. Maybe you need the "
+ "ignore_cis_vcc=1 parameter.\n");
+ cs_error(link, RequestIO, last_ret);
+ goto failed;
}
/* Need to allocate net_device before requesting IRQ handler */
@@ -738,14 +711,12 @@
if (ret == 0 && local->ddev)
strcpy(hw_priv->node.dev_name, local->ddev->name);
}
- kfree(parse);
return ret;
cs_failed:
cs_error(link, last_fn, last_ret);
failed:
- kfree(parse);
kfree(hw_priv);
prism2_release((u_long)link);
return ret;
diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/libertas/if_cs.c
index e3505c1..842a08d 100644
--- a/drivers/net/wireless/libertas/if_cs.c
+++ b/drivers/net/wireless/libertas/if_cs.c
@@ -791,7 +791,7 @@
tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
if ((ret = pcmcia_get_first_tuple(p_dev, &tuple)) != 0 ||
(ret = pcmcia_get_tuple_data(p_dev, &tuple)) != 0 ||
- (ret = pcmcia_parse_tuple(p_dev, &tuple, &parse)) != 0)
+ (ret = pcmcia_parse_tuple(&tuple, &parse)) != 0)
{
lbs_pr_err("error in pcmcia_get_first_tuple etc\n");
goto out1;
diff --git a/drivers/net/wireless/netwave_cs.c b/drivers/net/wireless/netwave_cs.c
index 25bae79..a670f36b 100644
--- a/drivers/net/wireless/netwave_cs.c
+++ b/drivers/net/wireless/netwave_cs.c
@@ -749,9 +749,10 @@
for (i = j = 0x0; j < 0x400; j += 0x20) {
link->io.BasePort1 = j ^ 0x300;
i = pcmcia_request_io(link, &link->io);
- if (i == CS_SUCCESS) break;
+ if (i == 0)
+ break;
}
- if (i != CS_SUCCESS) {
+ if (i != 0) {
cs_error(link, RequestIO, i);
goto failed;
}
diff --git a/drivers/net/wireless/orinoco_cs.c b/drivers/net/wireless/orinoco_cs.c
index 9eaa252..e585684 100644
--- a/drivers/net/wireless/orinoco_cs.c
+++ b/drivers/net/wireless/orinoco_cs.c
@@ -80,7 +80,7 @@
/* We need atomic ops here, because we're not holding the lock */
set_bit(0, &card->hard_reset_in_progress);
- err = pcmcia_reset_card(link, NULL);
+ err = pcmcia_reset_card(link->socket);
if (err)
return err;
@@ -165,6 +165,70 @@
last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; \
} while (0)
+static int orinoco_cs_config_check(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ if (cfg->index == 0)
+ goto next_entry;
+
+ /* Use power settings for Vcc and Vpp if present */
+ /* Note that the CIS values need to be rescaled */
+ if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
+ if (vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / 10000) {
+ DEBUG(2, "spectrum_cs_config: Vcc mismatch (vcc = %d, CIS = %d)\n", vcc, cfg->vcc.param[CISTPL_POWER_VNOM] / 10000);
+ if (!ignore_cis_vcc)
+ goto next_entry;
+ }
+ } else if (dflt->vcc.present & (1 << CISTPL_POWER_VNOM)) {
+ if (vcc != dflt->vcc.param[CISTPL_POWER_VNOM] / 10000) {
+ DEBUG(2, "spectrum_cs_config: Vcc mismatch (vcc = %d, CIS = %d)\n", vcc, dflt->vcc.param[CISTPL_POWER_VNOM] / 10000);
+ if (!ignore_cis_vcc)
+ goto next_entry;
+ }
+ }
+
+ if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ p_dev->conf.Vpp =
+ cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+ else if (dflt->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ p_dev->conf.Vpp =
+ dflt->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+
+ /* Do we need to allocate an interrupt? */
+ p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
+
+ /* IO window settings */
+ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0;
+ if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(io->flags & CISTPL_IO_8BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(io->flags & CISTPL_IO_16BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ p_dev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+ p_dev->io.BasePort1 = io->win[0].base;
+ p_dev->io.NumPorts1 = io->win[0].len;
+ if (io->nwin > 1) {
+ p_dev->io.Attributes2 = p_dev->io.Attributes1;
+ p_dev->io.BasePort2 = io->win[1].base;
+ p_dev->io.NumPorts2 = io->win[1].len;
+ }
+
+ /* This reserves IO space but doesn't actually enable it */
+ if (pcmcia_request_io(p_dev, &p_dev->io) != 0)
+ goto next_entry;
+ }
+ return 0;
+
+next_entry:
+ pcmcia_disable_device(p_dev);
+ return -ENODEV;
+};
+
static int
orinoco_cs_config(struct pcmcia_device *link)
{
@@ -173,16 +237,8 @@
struct orinoco_pccard *card = priv->card;
hermes_t *hw = &priv->hw;
int last_fn, last_ret;
- u_char buf[64];
- config_info_t conf;
- tuple_t tuple;
- cisparse_t parse;
void __iomem *mem;
- /* Look up the current Vcc */
- CS_CHECK(GetConfigurationInfo,
- pcmcia_get_configuration_info(link, &conf));
-
/*
* In this loop, we scan the CIS for configuration table
* entries, each of which describes a valid card
@@ -197,94 +253,14 @@
* and most client drivers will only use the CIS to fill in
* implementation-defined details.
*/
- tuple.Attributes = 0;
- tuple.TupleData = buf;
- tuple.TupleDataMax = sizeof(buf);
- tuple.TupleOffset = 0;
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
- while (1) {
- cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
- cistpl_cftable_entry_t dflt = { .index = 0 };
-
- if ( (pcmcia_get_tuple_data(link, &tuple) != 0)
- || (pcmcia_parse_tuple(link, &tuple, &parse) != 0))
- goto next_entry;
-
- if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
- dflt = *cfg;
- if (cfg->index == 0)
- goto next_entry;
- link->conf.ConfigIndex = cfg->index;
-
- /* Use power settings for Vcc and Vpp if present */
- /* Note that the CIS values need to be rescaled */
- if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
- if (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / 10000) {
- DEBUG(2, "orinoco_cs_config: Vcc mismatch (conf.Vcc = %d, cfg CIS = %d)\n", conf.Vcc, cfg->vcc.param[CISTPL_POWER_VNOM] / 10000);
- if (!ignore_cis_vcc)
- goto next_entry;
- }
- } else if (dflt.vcc.present & (1 << CISTPL_POWER_VNOM)) {
- if (conf.Vcc != dflt.vcc.param[CISTPL_POWER_VNOM] / 10000) {
- DEBUG(2, "orinoco_cs_config: Vcc mismatch (conf.Vcc = %d, dflt CIS = %d)\n", conf.Vcc, dflt.vcc.param[CISTPL_POWER_VNOM] / 10000);
- if(!ignore_cis_vcc)
- goto next_entry;
- }
- }
-
- if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
- link->conf.Vpp =
- cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
- else if (dflt.vpp1.present & (1 << CISTPL_POWER_VNOM))
- link->conf.Vpp =
- dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000;
-
- /* Do we need to allocate an interrupt? */
- link->conf.Attributes |= CONF_ENABLE_IRQ;
-
- /* IO window settings */
- link->io.NumPorts1 = link->io.NumPorts2 = 0;
- if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
- cistpl_io_t *io =
- (cfg->io.nwin) ? &cfg->io : &dflt.io;
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
- if (!(io->flags & CISTPL_IO_8BIT))
- link->io.Attributes1 =
- IO_DATA_PATH_WIDTH_16;
- if (!(io->flags & CISTPL_IO_16BIT))
- link->io.Attributes1 =
- IO_DATA_PATH_WIDTH_8;
- link->io.IOAddrLines =
- io->flags & CISTPL_IO_LINES_MASK;
- link->io.BasePort1 = io->win[0].base;
- link->io.NumPorts1 = io->win[0].len;
- if (io->nwin > 1) {
- link->io.Attributes2 =
- link->io.Attributes1;
- link->io.BasePort2 = io->win[1].base;
- link->io.NumPorts2 = io->win[1].len;
- }
-
- /* This reserves IO space but doesn't actually enable it */
- if (pcmcia_request_io(link, &link->io) != 0)
- goto next_entry;
- }
-
-
- /* If we got this far, we're cool! */
-
- break;
-
- next_entry:
- pcmcia_disable_device(link);
- last_ret = pcmcia_get_next_tuple(link, &tuple);
- if (last_ret == CS_NO_MORE_ITEMS) {
+ last_ret = pcmcia_loop_config(link, orinoco_cs_config_check, NULL);
+ if (last_ret) {
+ if (!ignore_cis_vcc)
printk(KERN_ERR PFX "GetNextTuple(): No matching "
"CIS configuration. Maybe you need the "
"ignore_cis_vcc=1 parameter.\n");
- goto cs_failed;
- }
+ cs_error(link, RequestIO, last_ret);
+ goto failed;
}
/*
@@ -335,7 +311,6 @@
"0x%04x-0x%04x\n", dev->name, dev->dev.parent->bus_id,
link->irq.AssignedIRQ, link->io.BasePort1,
link->io.BasePort1 + link->io.NumPorts1 - 1);
-
return 0;
cs_failed:
diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c
index 44da0d1..1404a57 100644
--- a/drivers/net/wireless/ray_cs.c
+++ b/drivers/net/wireless/ray_cs.c
@@ -798,9 +798,9 @@
iounmap(local->amem);
/* Do bother checking to see if these succeed or not */
i = pcmcia_release_window(local->amem_handle);
- if ( i != CS_SUCCESS ) DEBUG(0,"ReleaseWindow(local->amem) ret = %x\n",i);
+ if ( i != 0 ) DEBUG(0,"ReleaseWindow(local->amem) ret = %x\n",i);
i = pcmcia_release_window(local->rmem_handle);
- if ( i != CS_SUCCESS ) DEBUG(0,"ReleaseWindow(local->rmem) ret = %x\n",i);
+ if ( i != 0 ) DEBUG(0,"ReleaseWindow(local->rmem) ret = %x\n",i);
pcmcia_disable_device(link);
DEBUG(2,"ray_release ending\n");
diff --git a/drivers/net/wireless/spectrum_cs.c b/drivers/net/wireless/spectrum_cs.c
index 67b26d3..b0c71c3 100644
--- a/drivers/net/wireless/spectrum_cs.c
+++ b/drivers/net/wireless/spectrum_cs.c
@@ -235,6 +235,70 @@
* device available to the system.
*/
+static int spectrum_cs_config_check(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ if (cfg->index == 0)
+ goto next_entry;
+
+ /* Use power settings for Vcc and Vpp if present */
+ /* Note that the CIS values need to be rescaled */
+ if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
+ if (vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / 10000) {
+ DEBUG(2, "spectrum_cs_config: Vcc mismatch (vcc = %d, CIS = %d)\n", vcc, cfg->vcc.param[CISTPL_POWER_VNOM] / 10000);
+ if (!ignore_cis_vcc)
+ goto next_entry;
+ }
+ } else if (dflt->vcc.present & (1 << CISTPL_POWER_VNOM)) {
+ if (vcc != dflt->vcc.param[CISTPL_POWER_VNOM] / 10000) {
+ DEBUG(2, "spectrum_cs_config: Vcc mismatch (vcc = %d, CIS = %d)\n", vcc, dflt->vcc.param[CISTPL_POWER_VNOM] / 10000);
+ if (!ignore_cis_vcc)
+ goto next_entry;
+ }
+ }
+
+ if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ p_dev->conf.Vpp =
+ cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+ else if (dflt->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ p_dev->conf.Vpp =
+ dflt->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+
+ /* Do we need to allocate an interrupt? */
+ p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
+
+ /* IO window settings */
+ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0;
+ if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(io->flags & CISTPL_IO_8BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(io->flags & CISTPL_IO_16BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ p_dev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+ p_dev->io.BasePort1 = io->win[0].base;
+ p_dev->io.NumPorts1 = io->win[0].len;
+ if (io->nwin > 1) {
+ p_dev->io.Attributes2 = p_dev->io.Attributes1;
+ p_dev->io.BasePort2 = io->win[1].base;
+ p_dev->io.NumPorts2 = io->win[1].len;
+ }
+
+ /* This reserves IO space but doesn't actually enable it */
+ if (pcmcia_request_io(p_dev, &p_dev->io) != 0)
+ goto next_entry;
+ }
+ return 0;
+
+next_entry:
+ pcmcia_disable_device(p_dev);
+ return -ENODEV;
+};
+
static int
spectrum_cs_config(struct pcmcia_device *link)
{
@@ -243,16 +307,8 @@
struct orinoco_pccard *card = priv->card;
hermes_t *hw = &priv->hw;
int last_fn, last_ret;
- u_char buf[64];
- config_info_t conf;
- tuple_t tuple;
- cisparse_t parse;
void __iomem *mem;
- /* Look up the current Vcc */
- CS_CHECK(GetConfigurationInfo,
- pcmcia_get_configuration_info(link, &conf));
-
/*
* In this loop, we scan the CIS for configuration table
* entries, each of which describes a valid card
@@ -267,94 +323,14 @@
* and most client drivers will only use the CIS to fill in
* implementation-defined details.
*/
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- tuple.Attributes = 0;
- tuple.TupleData = buf;
- tuple.TupleDataMax = sizeof(buf);
- tuple.TupleOffset = 0;
- CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
- while (1) {
- cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
- cistpl_cftable_entry_t dflt = { .index = 0 };
-
- if ( (pcmcia_get_tuple_data(link, &tuple) != 0)
- || (pcmcia_parse_tuple(link, &tuple, &parse) != 0))
- goto next_entry;
-
- if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
- dflt = *cfg;
- if (cfg->index == 0)
- goto next_entry;
- link->conf.ConfigIndex = cfg->index;
-
- /* Use power settings for Vcc and Vpp if present */
- /* Note that the CIS values need to be rescaled */
- if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
- if (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / 10000) {
- DEBUG(2, "spectrum_cs_config: Vcc mismatch (conf.Vcc = %d, CIS = %d)\n", conf.Vcc, cfg->vcc.param[CISTPL_POWER_VNOM] / 10000);
- if (!ignore_cis_vcc)
- goto next_entry;
- }
- } else if (dflt.vcc.present & (1 << CISTPL_POWER_VNOM)) {
- if (conf.Vcc != dflt.vcc.param[CISTPL_POWER_VNOM] / 10000) {
- DEBUG(2, "spectrum_cs_config: Vcc mismatch (conf.Vcc = %d, CIS = %d)\n", conf.Vcc, dflt.vcc.param[CISTPL_POWER_VNOM] / 10000);
- if(!ignore_cis_vcc)
- goto next_entry;
- }
- }
-
- if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
- link->conf.Vpp =
- cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
- else if (dflt.vpp1.present & (1 << CISTPL_POWER_VNOM))
- link->conf.Vpp =
- dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000;
-
- /* Do we need to allocate an interrupt? */
- link->conf.Attributes |= CONF_ENABLE_IRQ;
-
- /* IO window settings */
- link->io.NumPorts1 = link->io.NumPorts2 = 0;
- if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
- cistpl_io_t *io =
- (cfg->io.nwin) ? &cfg->io : &dflt.io;
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
- if (!(io->flags & CISTPL_IO_8BIT))
- link->io.Attributes1 =
- IO_DATA_PATH_WIDTH_16;
- if (!(io->flags & CISTPL_IO_16BIT))
- link->io.Attributes1 =
- IO_DATA_PATH_WIDTH_8;
- link->io.IOAddrLines =
- io->flags & CISTPL_IO_LINES_MASK;
- link->io.BasePort1 = io->win[0].base;
- link->io.NumPorts1 = io->win[0].len;
- if (io->nwin > 1) {
- link->io.Attributes2 =
- link->io.Attributes1;
- link->io.BasePort2 = io->win[1].base;
- link->io.NumPorts2 = io->win[1].len;
- }
-
- /* This reserves IO space but doesn't actually enable it */
- if (pcmcia_request_io(link, &link->io) != 0)
- goto next_entry;
- }
-
-
- /* If we got this far, we're cool! */
-
- break;
-
- next_entry:
- pcmcia_disable_device(link);
- last_ret = pcmcia_get_next_tuple(link, &tuple);
- if (last_ret == CS_NO_MORE_ITEMS) {
+ last_ret = pcmcia_loop_config(link, spectrum_cs_config_check, NULL);
+ if (last_ret) {
+ if (!ignore_cis_vcc)
printk(KERN_ERR PFX "GetNextTuple(): No matching "
"CIS configuration. Maybe you need the "
"ignore_cis_vcc=1 parameter.\n");
- goto cs_failed;
- }
+ cs_error(link, RequestIO, last_ret);
+ goto failed;
}
/*
diff --git a/drivers/net/wireless/wavelan_cs.c b/drivers/net/wireless/wavelan_cs.c
index b5de38a..e124b1d 100644
--- a/drivers/net/wireless/wavelan_cs.c
+++ b/drivers/net/wireless/wavelan_cs.c
@@ -3702,7 +3702,7 @@
#endif
i = pcmcia_access_configuration_register(link, ®);
- if(i != CS_SUCCESS)
+ if (i != 0)
{
cs_error(link, AccessConfigurationRegister, i);
return FALSE;
@@ -3716,7 +3716,7 @@
reg.Action = CS_WRITE;
reg.Value = reg.Value | COR_SW_RESET;
i = pcmcia_access_configuration_register(link, ®);
- if(i != CS_SUCCESS)
+ if (i != 0)
{
cs_error(link, AccessConfigurationRegister, i);
return FALSE;
@@ -3725,7 +3725,7 @@
reg.Action = CS_WRITE;
reg.Value = COR_LEVEL_IRQ | COR_CONFIG;
i = pcmcia_access_configuration_register(link, ®);
- if(i != CS_SUCCESS)
+ if (i != 0)
{
cs_error(link, AccessConfigurationRegister, i);
return FALSE;
@@ -3903,7 +3903,7 @@
do
{
i = pcmcia_request_io(link, &link->io);
- if(i != CS_SUCCESS)
+ if (i != 0)
{
cs_error(link, RequestIO, i);
break;
@@ -3914,7 +3914,7 @@
* actually assign a handler to the interrupt.
*/
i = pcmcia_request_irq(link, &link->irq);
- if(i != CS_SUCCESS)
+ if (i != 0)
{
cs_error(link, RequestIRQ, i);
break;
@@ -3926,7 +3926,7 @@
*/
link->conf.ConfigIndex = 1;
i = pcmcia_request_configuration(link, &link->conf);
- if(i != CS_SUCCESS)
+ if (i != 0)
{
cs_error(link, RequestConfiguration, i);
break;
@@ -3942,7 +3942,7 @@
req.Base = req.Size = 0;
req.AccessSpeed = mem_speed;
i = pcmcia_request_window(&link, &req, &link->win);
- if(i != CS_SUCCESS)
+ if (i != 0)
{
cs_error(link, RequestWindow, i);
break;
@@ -3954,7 +3954,7 @@
mem.CardOffset = 0; mem.Page = 0;
i = pcmcia_map_mem_page(link->win, &mem);
- if(i != CS_SUCCESS)
+ if (i != 0)
{
cs_error(link, MapMemPage, i);
break;
diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c
index 74a5ad2..68789c6 100644
--- a/drivers/net/wireless/wl3501_cs.c
+++ b/drivers/net/wireless/wl3501_cs.c
@@ -1977,10 +1977,10 @@
link->io.BasePort1 = j;
link->io.BasePort2 = link->io.BasePort1 + 0x10;
i = pcmcia_request_io(link, &link->io);
- if (i == CS_SUCCESS)
+ if (i == 0)
break;
}
- if (i != CS_SUCCESS) {
+ if (i != 0) {
cs_error(link, RequestIO, i);
goto failed;
}
diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c
index c749bdb..3c3dd40 100644
--- a/drivers/net/xen-netfront.c
+++ b/drivers/net/xen-netfront.c
@@ -1794,10 +1794,10 @@
static int __init netif_init(void)
{
- if (!is_running_on_xen())
+ if (!xen_domain())
return -ENODEV;
- if (is_initial_xendomain())
+ if (xen_initial_domain())
return 0;
printk(KERN_INFO "Initialising Xen virtual ethernet driver.\n");
@@ -1809,7 +1809,7 @@
static void __exit netif_exit(void)
{
- if (is_initial_xendomain())
+ if (xen_initial_domain())
return;
xenbus_unregister_driver(&netfront);
diff --git a/drivers/oprofile/buffer_sync.c b/drivers/oprofile/buffer_sync.c
index 9304c45..ed98227 100644
--- a/drivers/oprofile/buffer_sync.c
+++ b/drivers/oprofile/buffer_sync.c
@@ -5,6 +5,7 @@
* @remark Read the file COPYING
*
* @author John Levon <levon@movementarian.org>
+ * @author Barry Kasindorf
*
* This is the core of the buffer management. Each
* CPU buffer is processed and entered into the
@@ -33,7 +34,7 @@
#include "event_buffer.h"
#include "cpu_buffer.h"
#include "buffer_sync.h"
-
+
static LIST_HEAD(dying_tasks);
static LIST_HEAD(dead_tasks);
static cpumask_t marked_cpus = CPU_MASK_NONE;
@@ -48,10 +49,11 @@
* Can be invoked from softirq via RCU callback due to
* call_rcu() of the task struct, hence the _irqsave.
*/
-static int task_free_notify(struct notifier_block * self, unsigned long val, void * data)
+static int
+task_free_notify(struct notifier_block *self, unsigned long val, void *data)
{
unsigned long flags;
- struct task_struct * task = data;
+ struct task_struct *task = data;
spin_lock_irqsave(&task_mortuary, flags);
list_add(&task->tasks, &dying_tasks);
spin_unlock_irqrestore(&task_mortuary, flags);
@@ -62,13 +64,14 @@
/* The task is on its way out. A sync of the buffer means we can catch
* any remaining samples for this task.
*/
-static int task_exit_notify(struct notifier_block * self, unsigned long val, void * data)
+static int
+task_exit_notify(struct notifier_block *self, unsigned long val, void *data)
{
/* To avoid latency problems, we only process the current CPU,
* hoping that most samples for the task are on this CPU
*/
sync_buffer(raw_smp_processor_id());
- return 0;
+ return 0;
}
@@ -77,11 +80,12 @@
* we don't lose any. This does not have to be exact, it's a QoI issue
* only.
*/
-static int munmap_notify(struct notifier_block * self, unsigned long val, void * data)
+static int
+munmap_notify(struct notifier_block *self, unsigned long val, void *data)
{
unsigned long addr = (unsigned long)data;
- struct mm_struct * mm = current->mm;
- struct vm_area_struct * mpnt;
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *mpnt;
down_read(&mm->mmap_sem);
@@ -99,11 +103,12 @@
return 0;
}
-
+
/* We need to be told about new modules so we don't attribute to a previously
* loaded module, or drop the samples on the floor.
*/
-static int module_load_notify(struct notifier_block * self, unsigned long val, void * data)
+static int
+module_load_notify(struct notifier_block *self, unsigned long val, void *data)
{
#ifdef CONFIG_MODULES
if (val != MODULE_STATE_COMING)
@@ -118,7 +123,7 @@
return 0;
}
-
+
static struct notifier_block task_free_nb = {
.notifier_call = task_free_notify,
};
@@ -135,7 +140,7 @@
.notifier_call = module_load_notify,
};
-
+
static void end_sync(void)
{
end_cpu_work();
@@ -208,14 +213,14 @@
* not strictly necessary but allows oprofile to associate
* shared-library samples with particular applications
*/
-static unsigned long get_exec_dcookie(struct mm_struct * mm)
+static unsigned long get_exec_dcookie(struct mm_struct *mm)
{
unsigned long cookie = NO_COOKIE;
- struct vm_area_struct * vma;
-
+ struct vm_area_struct *vma;
+
if (!mm)
goto out;
-
+
for (vma = mm->mmap; vma; vma = vma->vm_next) {
if (!vma->vm_file)
continue;
@@ -235,13 +240,14 @@
* sure to do this lookup before a mm->mmap modification happens so
* we don't lose track.
*/
-static unsigned long lookup_dcookie(struct mm_struct * mm, unsigned long addr, off_t * offset)
+static unsigned long
+lookup_dcookie(struct mm_struct *mm, unsigned long addr, off_t *offset)
{
unsigned long cookie = NO_COOKIE;
- struct vm_area_struct * vma;
+ struct vm_area_struct *vma;
for (vma = find_vma(mm, addr); vma; vma = vma->vm_next) {
-
+
if (addr < vma->vm_start || addr >= vma->vm_end)
continue;
@@ -263,9 +269,20 @@
return cookie;
}
+static void increment_tail(struct oprofile_cpu_buffer *b)
+{
+ unsigned long new_tail = b->tail_pos + 1;
+
+ rmb(); /* be sure fifo pointers are synchromized */
+
+ if (new_tail < b->buffer_size)
+ b->tail_pos = new_tail;
+ else
+ b->tail_pos = 0;
+}
static unsigned long last_cookie = INVALID_COOKIE;
-
+
static void add_cpu_switch(int i)
{
add_event_entry(ESCAPE_CODE);
@@ -278,16 +295,16 @@
{
add_event_entry(ESCAPE_CODE);
if (in_kernel)
- add_event_entry(KERNEL_ENTER_SWITCH_CODE);
+ add_event_entry(KERNEL_ENTER_SWITCH_CODE);
else
- add_event_entry(KERNEL_EXIT_SWITCH_CODE);
+ add_event_entry(KERNEL_EXIT_SWITCH_CODE);
}
-
+
static void
-add_user_ctx_switch(struct task_struct const * task, unsigned long cookie)
+add_user_ctx_switch(struct task_struct const *task, unsigned long cookie)
{
add_event_entry(ESCAPE_CODE);
- add_event_entry(CTX_SWITCH_CODE);
+ add_event_entry(CTX_SWITCH_CODE);
add_event_entry(task->pid);
add_event_entry(cookie);
/* Another code for daemon back-compat */
@@ -296,7 +313,7 @@
add_event_entry(task->tgid);
}
-
+
static void add_cookie_switch(unsigned long cookie)
{
add_event_entry(ESCAPE_CODE);
@@ -304,13 +321,78 @@
add_event_entry(cookie);
}
-
+
static void add_trace_begin(void)
{
add_event_entry(ESCAPE_CODE);
add_event_entry(TRACE_BEGIN_CODE);
}
+#ifdef CONFIG_OPROFILE_IBS
+
+#define IBS_FETCH_CODE_SIZE 2
+#define IBS_OP_CODE_SIZE 5
+#define IBS_EIP(offset) \
+ (((struct op_sample *)&cpu_buf->buffer[(offset)])->eip)
+#define IBS_EVENT(offset) \
+ (((struct op_sample *)&cpu_buf->buffer[(offset)])->event)
+
+/*
+ * Add IBS fetch and op entries to event buffer
+ */
+static void add_ibs_begin(struct oprofile_cpu_buffer *cpu_buf, int code,
+ int in_kernel, struct mm_struct *mm)
+{
+ unsigned long rip;
+ int i, count;
+ unsigned long ibs_cookie = 0;
+ off_t offset;
+
+ increment_tail(cpu_buf); /* move to RIP entry */
+
+ rip = IBS_EIP(cpu_buf->tail_pos);
+
+#ifdef __LP64__
+ rip += IBS_EVENT(cpu_buf->tail_pos) << 32;
+#endif
+
+ if (mm) {
+ ibs_cookie = lookup_dcookie(mm, rip, &offset);
+
+ if (ibs_cookie == NO_COOKIE)
+ offset = rip;
+ if (ibs_cookie == INVALID_COOKIE) {
+ atomic_inc(&oprofile_stats.sample_lost_no_mapping);
+ offset = rip;
+ }
+ if (ibs_cookie != last_cookie) {
+ add_cookie_switch(ibs_cookie);
+ last_cookie = ibs_cookie;
+ }
+ } else
+ offset = rip;
+
+ add_event_entry(ESCAPE_CODE);
+ add_event_entry(code);
+ add_event_entry(offset); /* Offset from Dcookie */
+
+ /* we send the Dcookie offset, but send the raw Linear Add also*/
+ add_event_entry(IBS_EIP(cpu_buf->tail_pos));
+ add_event_entry(IBS_EVENT(cpu_buf->tail_pos));
+
+ if (code == IBS_FETCH_CODE)
+ count = IBS_FETCH_CODE_SIZE; /*IBS FETCH is 2 int64s*/
+ else
+ count = IBS_OP_CODE_SIZE; /*IBS OP is 5 int64s*/
+
+ for (i = 0; i < count; i++) {
+ increment_tail(cpu_buf);
+ add_event_entry(IBS_EIP(cpu_buf->tail_pos));
+ add_event_entry(IBS_EVENT(cpu_buf->tail_pos));
+ }
+}
+
+#endif
static void add_sample_entry(unsigned long offset, unsigned long event)
{
@@ -319,13 +401,13 @@
}
-static int add_us_sample(struct mm_struct * mm, struct op_sample * s)
+static int add_us_sample(struct mm_struct *mm, struct op_sample *s)
{
unsigned long cookie;
off_t offset;
-
- cookie = lookup_dcookie(mm, s->eip, &offset);
-
+
+ cookie = lookup_dcookie(mm, s->eip, &offset);
+
if (cookie == INVALID_COOKIE) {
atomic_inc(&oprofile_stats.sample_lost_no_mapping);
return 0;
@@ -341,13 +423,13 @@
return 1;
}
-
+
/* Add a sample to the global event buffer. If possible the
* sample is converted into a persistent dentry/offset pair
* for later lookup from userspace.
*/
static int
-add_sample(struct mm_struct * mm, struct op_sample * s, int in_kernel)
+add_sample(struct mm_struct *mm, struct op_sample *s, int in_kernel)
{
if (in_kernel) {
add_sample_entry(s->eip, s->event);
@@ -359,9 +441,9 @@
}
return 0;
}
-
-static void release_mm(struct mm_struct * mm)
+
+static void release_mm(struct mm_struct *mm)
{
if (!mm)
return;
@@ -370,9 +452,9 @@
}
-static struct mm_struct * take_tasks_mm(struct task_struct * task)
+static struct mm_struct *take_tasks_mm(struct task_struct *task)
{
- struct mm_struct * mm = get_task_mm(task);
+ struct mm_struct *mm = get_task_mm(task);
if (mm)
down_read(&mm->mmap_sem);
return mm;
@@ -383,10 +465,10 @@
{
return val == ESCAPE_CODE;
}
-
+
/* "acquire" as many cpu buffer slots as we can */
-static unsigned long get_slots(struct oprofile_cpu_buffer * b)
+static unsigned long get_slots(struct oprofile_cpu_buffer *b)
{
unsigned long head = b->head_pos;
unsigned long tail = b->tail_pos;
@@ -412,19 +494,6 @@
}
-static void increment_tail(struct oprofile_cpu_buffer * b)
-{
- unsigned long new_tail = b->tail_pos + 1;
-
- rmb();
-
- if (new_tail < b->buffer_size)
- b->tail_pos = new_tail;
- else
- b->tail_pos = 0;
-}
-
-
/* Move tasks along towards death. Any tasks on dead_tasks
* will definitely have no remaining references in any
* CPU buffers at this point, because we use two lists,
@@ -435,8 +504,8 @@
{
unsigned long flags;
LIST_HEAD(local_dead_tasks);
- struct task_struct * task;
- struct task_struct * ttask;
+ struct task_struct *task;
+ struct task_struct *ttask;
spin_lock_irqsave(&task_mortuary, flags);
@@ -493,7 +562,7 @@
{
struct oprofile_cpu_buffer *cpu_buf = &per_cpu(cpu_buffer, cpu);
struct mm_struct *mm = NULL;
- struct task_struct * new;
+ struct task_struct *new;
unsigned long cookie = 0;
int in_kernel = 1;
unsigned int i;
@@ -501,7 +570,7 @@
unsigned long available;
mutex_lock(&buffer_mutex);
-
+
add_cpu_switch(cpu);
/* Remember, only we can modify tail_pos */
@@ -509,8 +578,8 @@
available = get_slots(cpu_buf);
for (i = 0; i < available; ++i) {
- struct op_sample * s = &cpu_buf->buffer[cpu_buf->tail_pos];
-
+ struct op_sample *s = &cpu_buf->buffer[cpu_buf->tail_pos];
+
if (is_code(s->eip)) {
if (s->event <= CPU_IS_KERNEL) {
/* kernel/userspace switch */
@@ -521,8 +590,18 @@
} else if (s->event == CPU_TRACE_BEGIN) {
state = sb_bt_start;
add_trace_begin();
+#ifdef CONFIG_OPROFILE_IBS
+ } else if (s->event == IBS_FETCH_BEGIN) {
+ state = sb_bt_start;
+ add_ibs_begin(cpu_buf,
+ IBS_FETCH_CODE, in_kernel, mm);
+ } else if (s->event == IBS_OP_BEGIN) {
+ state = sb_bt_start;
+ add_ibs_begin(cpu_buf,
+ IBS_OP_CODE, in_kernel, mm);
+#endif
} else {
- struct mm_struct * oldmm = mm;
+ struct mm_struct *oldmm = mm;
/* userspace context switch */
new = (struct task_struct *)s->event;
@@ -533,13 +612,11 @@
cookie = get_exec_dcookie(mm);
add_user_ctx_switch(new, cookie);
}
- } else {
- if (state >= sb_bt_start &&
- !add_sample(mm, s, in_kernel)) {
- if (state == sb_bt_start) {
- state = sb_bt_ignore;
- atomic_inc(&oprofile_stats.bt_lost_no_mapping);
- }
+ } else if (state >= sb_bt_start &&
+ !add_sample(mm, s, in_kernel)) {
+ if (state == sb_bt_start) {
+ state = sb_bt_ignore;
+ atomic_inc(&oprofile_stats.bt_lost_no_mapping);
}
}
diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c
index 7ba78e6..e1bd5a9 100644
--- a/drivers/oprofile/cpu_buffer.c
+++ b/drivers/oprofile/cpu_buffer.c
@@ -5,6 +5,7 @@
* @remark Read the file COPYING
*
* @author John Levon <levon@movementarian.org>
+ * @author Barry Kasindorf <barry.kasindorf@amd.com>
*
* Each CPU has a local buffer that stores PC value/event
* pairs. We also log context switches when we notice them.
@@ -209,7 +210,7 @@
return 1;
}
-static int oprofile_begin_trace(struct oprofile_cpu_buffer * cpu_buf)
+static int oprofile_begin_trace(struct oprofile_cpu_buffer *cpu_buf)
{
if (nr_available_slots(cpu_buf) < 4) {
cpu_buf->sample_lost_overflow++;
@@ -254,6 +255,75 @@
oprofile_add_ext_sample(pc, regs, event, is_kernel);
}
+#ifdef CONFIG_OPROFILE_IBS
+
+#define MAX_IBS_SAMPLE_SIZE 14
+static int log_ibs_sample(struct oprofile_cpu_buffer *cpu_buf,
+ unsigned long pc, int is_kernel, unsigned int *ibs, int ibs_code)
+{
+ struct task_struct *task;
+
+ cpu_buf->sample_received++;
+
+ if (nr_available_slots(cpu_buf) < MAX_IBS_SAMPLE_SIZE) {
+ cpu_buf->sample_lost_overflow++;
+ return 0;
+ }
+
+ is_kernel = !!is_kernel;
+
+ /* notice a switch from user->kernel or vice versa */
+ if (cpu_buf->last_is_kernel != is_kernel) {
+ cpu_buf->last_is_kernel = is_kernel;
+ add_code(cpu_buf, is_kernel);
+ }
+
+ /* notice a task switch */
+ if (!is_kernel) {
+ task = current;
+
+ if (cpu_buf->last_task != task) {
+ cpu_buf->last_task = task;
+ add_code(cpu_buf, (unsigned long)task);
+ }
+ }
+
+ add_code(cpu_buf, ibs_code);
+ add_sample(cpu_buf, ibs[0], ibs[1]);
+ add_sample(cpu_buf, ibs[2], ibs[3]);
+ add_sample(cpu_buf, ibs[4], ibs[5]);
+
+ if (ibs_code == IBS_OP_BEGIN) {
+ add_sample(cpu_buf, ibs[6], ibs[7]);
+ add_sample(cpu_buf, ibs[8], ibs[9]);
+ add_sample(cpu_buf, ibs[10], ibs[11]);
+ }
+
+ return 1;
+}
+
+void oprofile_add_ibs_sample(struct pt_regs *const regs,
+ unsigned int * const ibs_sample, u8 code)
+{
+ int is_kernel = !user_mode(regs);
+ unsigned long pc = profile_pc(regs);
+
+ struct oprofile_cpu_buffer *cpu_buf =
+ &per_cpu(cpu_buffer, smp_processor_id());
+
+ if (!backtrace_depth) {
+ log_ibs_sample(cpu_buf, pc, is_kernel, ibs_sample, code);
+ return;
+ }
+
+ /* if log_sample() fails we can't backtrace since we lost the source
+ * of this event */
+ if (log_ibs_sample(cpu_buf, pc, is_kernel, ibs_sample, code))
+ oprofile_ops.backtrace(regs, backtrace_depth);
+}
+
+#endif
+
void oprofile_add_pc(unsigned long pc, int is_kernel, unsigned long event)
{
struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer);
@@ -296,7 +366,7 @@
struct oprofile_cpu_buffer * b =
container_of(work, struct oprofile_cpu_buffer, work.work);
if (b->cpu != smp_processor_id()) {
- printk("WQ on CPU%d, prefer CPU%d\n",
+ printk(KERN_DEBUG "WQ on CPU%d, prefer CPU%d\n",
smp_processor_id(), b->cpu);
}
sync_buffer(b->cpu);
diff --git a/drivers/oprofile/cpu_buffer.h b/drivers/oprofile/cpu_buffer.h
index c3e366b..9c44d00 100644
--- a/drivers/oprofile/cpu_buffer.h
+++ b/drivers/oprofile/cpu_buffer.h
@@ -55,5 +55,7 @@
/* transient events for the CPU buffer -> event buffer */
#define CPU_IS_KERNEL 1
#define CPU_TRACE_BEGIN 2
+#define IBS_FETCH_BEGIN 3
+#define IBS_OP_BEGIN 4
#endif /* OPROFILE_CPU_BUFFER_H */
diff --git a/drivers/parport/parport_cs.c b/drivers/parport/parport_cs.c
index 00e1d96..b1899e9 100644
--- a/drivers/parport/parport_cs.c
+++ b/drivers/parport/parport_cs.c
@@ -149,52 +149,44 @@
#define CS_CHECK(fn, ret) \
do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+static int parport_config_check(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
+ if (epp_mode)
+ p_dev->conf.ConfigIndex |= FORCE_EPP_MODE;
+ p_dev->io.BasePort1 = io->win[0].base;
+ p_dev->io.NumPorts1 = io->win[0].len;
+ p_dev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+ if (io->nwin == 2) {
+ p_dev->io.BasePort2 = io->win[1].base;
+ p_dev->io.NumPorts2 = io->win[1].len;
+ }
+ if (pcmcia_request_io(p_dev, &p_dev->io) != 0)
+ return -ENODEV;
+ return 0;
+ }
+ return -ENODEV;
+}
+
static int parport_config(struct pcmcia_device *link)
{
parport_info_t *info = link->priv;
- tuple_t tuple;
- u_short buf[128];
- cisparse_t parse;
- cistpl_cftable_entry_t *cfg = &parse.cftable_entry;
- cistpl_cftable_entry_t dflt = { 0 };
struct parport *p;
int last_ret, last_fn;
-
- DEBUG(0, "parport_config(0x%p)\n", link);
-
- tuple.TupleData = (cisdata_t *)buf;
- tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- tuple.Attributes = 0;
- CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
- while (1) {
- if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
- pcmcia_parse_tuple(link, &tuple, &parse) != 0)
- goto next_entry;
- if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
- cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
- link->conf.ConfigIndex = cfg->index;
- if (epp_mode)
- link->conf.ConfigIndex |= FORCE_EPP_MODE;
- link->io.BasePort1 = io->win[0].base;
- link->io.NumPorts1 = io->win[0].len;
- link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
- if (io->nwin == 2) {
- link->io.BasePort2 = io->win[1].base;
- link->io.NumPorts2 = io->win[1].len;
- }
- if (pcmcia_request_io(link, &link->io) != 0)
- goto next_entry;
- /* If we've got this far, we're done */
- break;
- }
-
- next_entry:
- if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg;
- CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple));
+ DEBUG(0, "parport_config(0x%p)\n", link);
+
+ last_ret = pcmcia_loop_config(link, parport_config_check, NULL);
+ if (last_ret) {
+ cs_error(link, RequestIO, last_ret);
+ goto failed;
}
-
+
CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
diff --git a/drivers/parport/parport_sunbpp.c b/drivers/parport/parport_sunbpp.c
index 9d595aa..065f229 100644
--- a/drivers/parport/parport_sunbpp.c
+++ b/drivers/parport/parport_sunbpp.c
@@ -26,6 +26,8 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/parport.h>
@@ -34,7 +36,6 @@
#include <asm/io.h>
#include <asm/oplib.h> /* OpenProm Library */
-#include <asm/sbus.h>
#include <asm/dma.h> /* BPP uses LSI 64854 for DMA */
#include <asm/irq.h>
#include <asm/sunbpp.h>
@@ -285,38 +286,37 @@
.owner = THIS_MODULE,
};
-static int __devinit init_one_port(struct sbus_dev *sdev)
+static int __devinit bpp_probe(struct of_device *op, const struct of_device_id *match)
{
- struct parport *p;
- /* at least in theory there may be a "we don't dma" case */
struct parport_operations *ops;
- void __iomem *base;
- int irq, dma, err = 0, size;
struct bpp_regs __iomem *regs;
+ int irq, dma, err = 0, size;
unsigned char value_tcr;
+ void __iomem *base;
+ struct parport *p;
- irq = sdev->irqs[0];
- base = sbus_ioremap(&sdev->resource[0], 0,
- sdev->reg_addrs[0].reg_size,
- "sunbpp");
+ irq = op->irqs[0];
+ base = of_ioremap(&op->resource[0], 0,
+ resource_size(&op->resource[0]),
+ "sunbpp");
if (!base)
return -ENODEV;
- size = sdev->reg_addrs[0].reg_size;
+ size = resource_size(&op->resource[0]);
dma = PARPORT_DMA_NONE;
ops = kmalloc(sizeof(struct parport_operations), GFP_KERNEL);
if (!ops)
goto out_unmap;
- memcpy (ops, &parport_sunbpp_ops, sizeof (struct parport_operations));
+ memcpy (ops, &parport_sunbpp_ops, sizeof(struct parport_operations));
dprintk(("register_port\n"));
if (!(p = parport_register_port((unsigned long)base, irq, dma, ops)))
goto out_free_ops;
p->size = size;
- p->dev = &sdev->ofdev.dev;
+ p->dev = &op->dev;
if ((err = request_irq(p->irq, parport_irq_handler,
IRQF_SHARED, p->name, p)) != 0) {
@@ -333,7 +333,7 @@
printk(KERN_INFO "%s: sunbpp at 0x%lx\n", p->name, p->base);
- dev_set_drvdata(&sdev->ofdev.dev, p);
+ dev_set_drvdata(&op->dev, p);
parport_announce_port(p);
@@ -346,21 +346,14 @@
kfree(ops);
out_unmap:
- sbus_iounmap(base, size);
+ of_iounmap(&op->resource[0], base, size);
return err;
}
-static int __devinit bpp_probe(struct of_device *dev, const struct of_device_id *match)
+static int __devexit bpp_remove(struct of_device *op)
{
- struct sbus_dev *sdev = to_sbus_device(&dev->dev);
-
- return init_one_port(sdev);
-}
-
-static int __devexit bpp_remove(struct of_device *dev)
-{
- struct parport *p = dev_get_drvdata(&dev->dev);
+ struct parport *p = dev_get_drvdata(&op->dev);
struct parport_operations *ops = p->ops;
parport_remove_port(p);
@@ -370,16 +363,16 @@
free_irq(p->irq, p);
}
- sbus_iounmap((void __iomem *) p->base, p->size);
+ of_iounmap(&op->resource[0], (void __iomem *) p->base, p->size);
parport_put_port(p);
kfree(ops);
- dev_set_drvdata(&dev->dev, NULL);
+ dev_set_drvdata(&op->dev, NULL);
return 0;
}
-static struct of_device_id bpp_match[] = {
+static const struct of_device_id bpp_match[] = {
{
.name = "SUNW,bpp",
},
@@ -397,7 +390,7 @@
static int __init parport_sunbpp_init(void)
{
- return of_register_driver(&bpp_sbus_driver, &sbus_bus_type);
+ return of_register_driver(&bpp_sbus_driver, &of_bus_type);
}
static void __exit parport_sunbpp_exit(void)
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 7d63f8c..4b47f4e 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -26,6 +26,8 @@
# Build Intel IOMMU support
obj-$(CONFIG_DMAR) += dmar.o iova.o intel-iommu.o
+obj-$(CONFIG_INTR_REMAP) += dmar.o intr_remapping.o
+
#
# Some architectures use the generic PCI setup functions
#
diff --git a/drivers/pci/dma_remapping.h b/drivers/pci/dma_remapping.h
new file mode 100644
index 0000000..bff5c65
--- /dev/null
+++ b/drivers/pci/dma_remapping.h
@@ -0,0 +1,157 @@
+#ifndef _DMA_REMAPPING_H
+#define _DMA_REMAPPING_H
+
+/*
+ * We need a fixed PAGE_SIZE of 4K irrespective of
+ * arch PAGE_SIZE for IOMMU page tables.
+ */
+#define PAGE_SHIFT_4K (12)
+#define PAGE_SIZE_4K (1UL << PAGE_SHIFT_4K)
+#define PAGE_MASK_4K (((u64)-1) << PAGE_SHIFT_4K)
+#define PAGE_ALIGN_4K(addr) (((addr) + PAGE_SIZE_4K - 1) & PAGE_MASK_4K)
+
+#define IOVA_PFN(addr) ((addr) >> PAGE_SHIFT_4K)
+#define DMA_32BIT_PFN IOVA_PFN(DMA_32BIT_MASK)
+#define DMA_64BIT_PFN IOVA_PFN(DMA_64BIT_MASK)
+
+
+/*
+ * 0: Present
+ * 1-11: Reserved
+ * 12-63: Context Ptr (12 - (haw-1))
+ * 64-127: Reserved
+ */
+struct root_entry {
+ u64 val;
+ u64 rsvd1;
+};
+#define ROOT_ENTRY_NR (PAGE_SIZE_4K/sizeof(struct root_entry))
+static inline bool root_present(struct root_entry *root)
+{
+ return (root->val & 1);
+}
+static inline void set_root_present(struct root_entry *root)
+{
+ root->val |= 1;
+}
+static inline void set_root_value(struct root_entry *root, unsigned long value)
+{
+ root->val |= value & PAGE_MASK_4K;
+}
+
+struct context_entry;
+static inline struct context_entry *
+get_context_addr_from_root(struct root_entry *root)
+{
+ return (struct context_entry *)
+ (root_present(root)?phys_to_virt(
+ root->val & PAGE_MASK_4K):
+ NULL);
+}
+
+/*
+ * low 64 bits:
+ * 0: present
+ * 1: fault processing disable
+ * 2-3: translation type
+ * 12-63: address space root
+ * high 64 bits:
+ * 0-2: address width
+ * 3-6: aval
+ * 8-23: domain id
+ */
+struct context_entry {
+ u64 lo;
+ u64 hi;
+};
+#define context_present(c) ((c).lo & 1)
+#define context_fault_disable(c) (((c).lo >> 1) & 1)
+#define context_translation_type(c) (((c).lo >> 2) & 3)
+#define context_address_root(c) ((c).lo & PAGE_MASK_4K)
+#define context_address_width(c) ((c).hi & 7)
+#define context_domain_id(c) (((c).hi >> 8) & ((1 << 16) - 1))
+
+#define context_set_present(c) do {(c).lo |= 1;} while (0)
+#define context_set_fault_enable(c) \
+ do {(c).lo &= (((u64)-1) << 2) | 1;} while (0)
+#define context_set_translation_type(c, val) \
+ do { \
+ (c).lo &= (((u64)-1) << 4) | 3; \
+ (c).lo |= ((val) & 3) << 2; \
+ } while (0)
+#define CONTEXT_TT_MULTI_LEVEL 0
+#define context_set_address_root(c, val) \
+ do {(c).lo |= (val) & PAGE_MASK_4K;} while (0)
+#define context_set_address_width(c, val) do {(c).hi |= (val) & 7;} while (0)
+#define context_set_domain_id(c, val) \
+ do {(c).hi |= ((val) & ((1 << 16) - 1)) << 8;} while (0)
+#define context_clear_entry(c) do {(c).lo = 0; (c).hi = 0;} while (0)
+
+/*
+ * 0: readable
+ * 1: writable
+ * 2-6: reserved
+ * 7: super page
+ * 8-11: available
+ * 12-63: Host physcial address
+ */
+struct dma_pte {
+ u64 val;
+};
+#define dma_clear_pte(p) do {(p).val = 0;} while (0)
+
+#define DMA_PTE_READ (1)
+#define DMA_PTE_WRITE (2)
+
+#define dma_set_pte_readable(p) do {(p).val |= DMA_PTE_READ;} while (0)
+#define dma_set_pte_writable(p) do {(p).val |= DMA_PTE_WRITE;} while (0)
+#define dma_set_pte_prot(p, prot) \
+ do {(p).val = ((p).val & ~3) | ((prot) & 3); } while (0)
+#define dma_pte_addr(p) ((p).val & PAGE_MASK_4K)
+#define dma_set_pte_addr(p, addr) do {\
+ (p).val |= ((addr) & PAGE_MASK_4K); } while (0)
+#define dma_pte_present(p) (((p).val & 3) != 0)
+
+struct intel_iommu;
+
+struct dmar_domain {
+ int id; /* domain id */
+ struct intel_iommu *iommu; /* back pointer to owning iommu */
+
+ struct list_head devices; /* all devices' list */
+ struct iova_domain iovad; /* iova's that belong to this domain */
+
+ struct dma_pte *pgd; /* virtual address */
+ spinlock_t mapping_lock; /* page table lock */
+ int gaw; /* max guest address width */
+
+ /* adjusted guest address width, 0 is level 2 30-bit */
+ int agaw;
+
+#define DOMAIN_FLAG_MULTIPLE_DEVICES 1
+ int flags;
+};
+
+/* PCI domain-device relationship */
+struct device_domain_info {
+ struct list_head link; /* link to domain siblings */
+ struct list_head global; /* link to global list */
+ u8 bus; /* PCI bus numer */
+ u8 devfn; /* PCI devfn number */
+ struct pci_dev *dev; /* it's NULL for PCIE-to-PCI bridge */
+ struct dmar_domain *domain; /* pointer to domain */
+};
+
+extern int init_dmars(void);
+extern void free_dmar_iommu(struct intel_iommu *iommu);
+
+extern int dmar_disabled;
+
+#ifndef CONFIG_DMAR_GFX_WA
+static inline void iommu_prepare_gfx_mapping(void)
+{
+ return;
+}
+#endif /* !CONFIG_DMAR_GFX_WA */
+
+#endif
diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c
index 8bf86ae..bd2c016 100644
--- a/drivers/pci/dmar.c
+++ b/drivers/pci/dmar.c
@@ -19,13 +19,16 @@
* Author: Shaohua Li <shaohua.li@intel.com>
* Author: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
*
- * This file implements early detection/parsing of DMA Remapping Devices
+ * This file implements early detection/parsing of Remapping Devices
* reported to OS through BIOS via DMA remapping reporting (DMAR) ACPI
* tables.
+ *
+ * These routines are used by both DMA-remapping and Interrupt-remapping
*/
#include <linux/pci.h>
#include <linux/dmar.h>
+#include <linux/timer.h>
#include "iova.h"
#include "intel-iommu.h"
@@ -37,7 +40,6 @@
* these units are not supported by the architecture.
*/
LIST_HEAD(dmar_drhd_units);
-LIST_HEAD(dmar_rmrr_units);
static struct acpi_table_header * __initdata dmar_tbl;
@@ -53,11 +55,6 @@
list_add(&drhd->list, &dmar_drhd_units);
}
-static void __init dmar_register_rmrr_unit(struct dmar_rmrr_unit *rmrr)
-{
- list_add(&rmrr->list, &dmar_rmrr_units);
-}
-
static int __init dmar_parse_one_dev_scope(struct acpi_dmar_device_scope *scope,
struct pci_dev **dev, u16 segment)
{
@@ -172,19 +169,37 @@
struct acpi_dmar_hardware_unit *drhd;
struct dmar_drhd_unit *dmaru;
int ret = 0;
- static int include_all;
dmaru = kzalloc(sizeof(*dmaru), GFP_KERNEL);
if (!dmaru)
return -ENOMEM;
+ dmaru->hdr = header;
drhd = (struct acpi_dmar_hardware_unit *)header;
dmaru->reg_base_addr = drhd->address;
dmaru->include_all = drhd->flags & 0x1; /* BIT0: INCLUDE_ALL */
+ ret = alloc_iommu(dmaru);
+ if (ret) {
+ kfree(dmaru);
+ return ret;
+ }
+ dmar_register_drhd_unit(dmaru);
+ return 0;
+}
+
+static int __init
+dmar_parse_dev(struct dmar_drhd_unit *dmaru)
+{
+ struct acpi_dmar_hardware_unit *drhd;
+ static int include_all;
+ int ret;
+
+ drhd = (struct acpi_dmar_hardware_unit *) dmaru->hdr;
+
if (!dmaru->include_all)
ret = dmar_parse_dev_scope((void *)(drhd + 1),
- ((void *)drhd) + header->length,
+ ((void *)drhd) + drhd->header.length,
&dmaru->devices_cnt, &dmaru->devices,
drhd->segment);
else {
@@ -197,37 +212,59 @@
include_all = 1;
}
- if (ret || (dmaru->devices_cnt == 0 && !dmaru->include_all))
+ if (ret || (dmaru->devices_cnt == 0 && !dmaru->include_all)) {
+ list_del(&dmaru->list);
kfree(dmaru);
- else
- dmar_register_drhd_unit(dmaru);
+ }
return ret;
}
+#ifdef CONFIG_DMAR
+LIST_HEAD(dmar_rmrr_units);
+
+static void __init dmar_register_rmrr_unit(struct dmar_rmrr_unit *rmrr)
+{
+ list_add(&rmrr->list, &dmar_rmrr_units);
+}
+
+
static int __init
dmar_parse_one_rmrr(struct acpi_dmar_header *header)
{
struct acpi_dmar_reserved_memory *rmrr;
struct dmar_rmrr_unit *rmrru;
- int ret = 0;
rmrru = kzalloc(sizeof(*rmrru), GFP_KERNEL);
if (!rmrru)
return -ENOMEM;
+ rmrru->hdr = header;
rmrr = (struct acpi_dmar_reserved_memory *)header;
rmrru->base_address = rmrr->base_address;
rmrru->end_address = rmrr->end_address;
+
+ dmar_register_rmrr_unit(rmrru);
+ return 0;
+}
+
+static int __init
+rmrr_parse_dev(struct dmar_rmrr_unit *rmrru)
+{
+ struct acpi_dmar_reserved_memory *rmrr;
+ int ret;
+
+ rmrr = (struct acpi_dmar_reserved_memory *) rmrru->hdr;
ret = dmar_parse_dev_scope((void *)(rmrr + 1),
- ((void *)rmrr) + header->length,
+ ((void *)rmrr) + rmrr->header.length,
&rmrru->devices_cnt, &rmrru->devices, rmrr->segment);
- if (ret || (rmrru->devices_cnt == 0))
+ if (ret || (rmrru->devices_cnt == 0)) {
+ list_del(&rmrru->list);
kfree(rmrru);
- else
- dmar_register_rmrr_unit(rmrru);
+ }
return ret;
}
+#endif
static void __init
dmar_table_print_dmar_entry(struct acpi_dmar_header *header)
@@ -252,6 +289,7 @@
}
}
+
/**
* parse_dmar_table - parses the DMA reporting table
*/
@@ -284,7 +322,9 @@
ret = dmar_parse_one_drhd(entry_header);
break;
case ACPI_DMAR_TYPE_RESERVED_MEMORY:
+#ifdef CONFIG_DMAR
ret = dmar_parse_one_rmrr(entry_header);
+#endif
break;
default:
printk(KERN_WARNING PREFIX
@@ -300,15 +340,77 @@
return ret;
}
+int dmar_pci_device_match(struct pci_dev *devices[], int cnt,
+ struct pci_dev *dev)
+{
+ int index;
+
+ while (dev) {
+ for (index = 0; index < cnt; index++)
+ if (dev == devices[index])
+ return 1;
+
+ /* Check our parent */
+ dev = dev->bus->self;
+ }
+
+ return 0;
+}
+
+struct dmar_drhd_unit *
+dmar_find_matched_drhd_unit(struct pci_dev *dev)
+{
+ struct dmar_drhd_unit *drhd = NULL;
+
+ list_for_each_entry(drhd, &dmar_drhd_units, list) {
+ if (drhd->include_all || dmar_pci_device_match(drhd->devices,
+ drhd->devices_cnt, dev))
+ return drhd;
+ }
+
+ return NULL;
+}
+
+int __init dmar_dev_scope_init(void)
+{
+ struct dmar_drhd_unit *drhd;
+ int ret = -ENODEV;
+
+ for_each_drhd_unit(drhd) {
+ ret = dmar_parse_dev(drhd);
+ if (ret)
+ return ret;
+ }
+
+#ifdef CONFIG_DMAR
+ {
+ struct dmar_rmrr_unit *rmrr;
+ for_each_rmrr_units(rmrr) {
+ ret = rmrr_parse_dev(rmrr);
+ if (ret)
+ return ret;
+ }
+ }
+#endif
+
+ return ret;
+}
+
int __init dmar_table_init(void)
{
-
+ static int dmar_table_initialized;
int ret;
+ if (dmar_table_initialized)
+ return 0;
+
+ dmar_table_initialized = 1;
+
ret = parse_dmar_table();
if (ret) {
- printk(KERN_INFO PREFIX "parse DMAR table failure.\n");
+ if (ret != -ENODEV)
+ printk(KERN_INFO PREFIX "parse DMAR table failure.\n");
return ret;
}
@@ -317,9 +419,14 @@
return -ENODEV;
}
+#ifdef CONFIG_DMAR
if (list_empty(&dmar_rmrr_units))
printk(KERN_INFO PREFIX "No RMRR found\n");
+#endif
+#ifdef CONFIG_INTR_REMAP
+ parse_ioapics_under_ir();
+#endif
return 0;
}
@@ -341,3 +448,255 @@
return (ACPI_SUCCESS(status) ? 1 : 0);
}
+
+void __init detect_intel_iommu(void)
+{
+ int ret;
+
+ ret = early_dmar_detect();
+
+#ifdef CONFIG_DMAR
+ {
+ struct acpi_table_dmar *dmar;
+ /*
+ * for now we will disable dma-remapping when interrupt
+ * remapping is enabled.
+ * When support for queued invalidation for IOTLB invalidation
+ * is added, we will not need this any more.
+ */
+ dmar = (struct acpi_table_dmar *) dmar_tbl;
+ if (ret && cpu_has_x2apic && dmar->flags & 0x1) {
+ printk(KERN_INFO
+ "Queued invalidation will be enabled to support "
+ "x2apic and Intr-remapping.\n");
+ printk(KERN_INFO
+ "Disabling IOMMU detection, because of missing "
+ "queued invalidation support for IOTLB "
+ "invalidation\n");
+ printk(KERN_INFO
+ "Use \"nox2apic\", if you want to use Intel "
+ " IOMMU for DMA-remapping and don't care about "
+ " x2apic support\n");
+
+ dmar_disabled = 1;
+ return;
+ }
+
+ if (ret && !no_iommu && !iommu_detected && !swiotlb &&
+ !dmar_disabled)
+ iommu_detected = 1;
+ }
+#endif
+}
+
+
+int alloc_iommu(struct dmar_drhd_unit *drhd)
+{
+ struct intel_iommu *iommu;
+ int map_size;
+ u32 ver;
+ static int iommu_allocated = 0;
+
+ iommu = kzalloc(sizeof(*iommu), GFP_KERNEL);
+ if (!iommu)
+ return -ENOMEM;
+
+ iommu->seq_id = iommu_allocated++;
+
+ iommu->reg = ioremap(drhd->reg_base_addr, PAGE_SIZE_4K);
+ if (!iommu->reg) {
+ printk(KERN_ERR "IOMMU: can't map the region\n");
+ goto error;
+ }
+ iommu->cap = dmar_readq(iommu->reg + DMAR_CAP_REG);
+ iommu->ecap = dmar_readq(iommu->reg + DMAR_ECAP_REG);
+
+ /* the registers might be more than one page */
+ map_size = max_t(int, ecap_max_iotlb_offset(iommu->ecap),
+ cap_max_fault_reg_offset(iommu->cap));
+ map_size = PAGE_ALIGN_4K(map_size);
+ if (map_size > PAGE_SIZE_4K) {
+ iounmap(iommu->reg);
+ iommu->reg = ioremap(drhd->reg_base_addr, map_size);
+ if (!iommu->reg) {
+ printk(KERN_ERR "IOMMU: can't map the region\n");
+ goto error;
+ }
+ }
+
+ ver = readl(iommu->reg + DMAR_VER_REG);
+ pr_debug("IOMMU %llx: ver %d:%d cap %llx ecap %llx\n",
+ drhd->reg_base_addr, DMAR_VER_MAJOR(ver), DMAR_VER_MINOR(ver),
+ iommu->cap, iommu->ecap);
+
+ spin_lock_init(&iommu->register_lock);
+
+ drhd->iommu = iommu;
+ return 0;
+error:
+ kfree(iommu);
+ return -1;
+}
+
+void free_iommu(struct intel_iommu *iommu)
+{
+ if (!iommu)
+ return;
+
+#ifdef CONFIG_DMAR
+ free_dmar_iommu(iommu);
+#endif
+
+ if (iommu->reg)
+ iounmap(iommu->reg);
+ kfree(iommu);
+}
+
+/*
+ * Reclaim all the submitted descriptors which have completed its work.
+ */
+static inline void reclaim_free_desc(struct q_inval *qi)
+{
+ while (qi->desc_status[qi->free_tail] == QI_DONE) {
+ qi->desc_status[qi->free_tail] = QI_FREE;
+ qi->free_tail = (qi->free_tail + 1) % QI_LENGTH;
+ qi->free_cnt++;
+ }
+}
+
+/*
+ * Submit the queued invalidation descriptor to the remapping
+ * hardware unit and wait for its completion.
+ */
+void qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu)
+{
+ struct q_inval *qi = iommu->qi;
+ struct qi_desc *hw, wait_desc;
+ int wait_index, index;
+ unsigned long flags;
+
+ if (!qi)
+ return;
+
+ hw = qi->desc;
+
+ spin_lock(&qi->q_lock);
+ while (qi->free_cnt < 3) {
+ spin_unlock(&qi->q_lock);
+ cpu_relax();
+ spin_lock(&qi->q_lock);
+ }
+
+ index = qi->free_head;
+ wait_index = (index + 1) % QI_LENGTH;
+
+ qi->desc_status[index] = qi->desc_status[wait_index] = QI_IN_USE;
+
+ hw[index] = *desc;
+
+ wait_desc.low = QI_IWD_STATUS_DATA(2) | QI_IWD_STATUS_WRITE | QI_IWD_TYPE;
+ wait_desc.high = virt_to_phys(&qi->desc_status[wait_index]);
+
+ hw[wait_index] = wait_desc;
+
+ __iommu_flush_cache(iommu, &hw[index], sizeof(struct qi_desc));
+ __iommu_flush_cache(iommu, &hw[wait_index], sizeof(struct qi_desc));
+
+ qi->free_head = (qi->free_head + 2) % QI_LENGTH;
+ qi->free_cnt -= 2;
+
+ spin_lock_irqsave(&iommu->register_lock, flags);
+ /*
+ * update the HW tail register indicating the presence of
+ * new descriptors.
+ */
+ writel(qi->free_head << 4, iommu->reg + DMAR_IQT_REG);
+ spin_unlock_irqrestore(&iommu->register_lock, flags);
+
+ while (qi->desc_status[wait_index] != QI_DONE) {
+ spin_unlock(&qi->q_lock);
+ cpu_relax();
+ spin_lock(&qi->q_lock);
+ }
+
+ qi->desc_status[index] = QI_DONE;
+
+ reclaim_free_desc(qi);
+ spin_unlock(&qi->q_lock);
+}
+
+/*
+ * Flush the global interrupt entry cache.
+ */
+void qi_global_iec(struct intel_iommu *iommu)
+{
+ struct qi_desc desc;
+
+ desc.low = QI_IEC_TYPE;
+ desc.high = 0;
+
+ qi_submit_sync(&desc, iommu);
+}
+
+/*
+ * Enable Queued Invalidation interface. This is a must to support
+ * interrupt-remapping. Also used by DMA-remapping, which replaces
+ * register based IOTLB invalidation.
+ */
+int dmar_enable_qi(struct intel_iommu *iommu)
+{
+ u32 cmd, sts;
+ unsigned long flags;
+ struct q_inval *qi;
+
+ if (!ecap_qis(iommu->ecap))
+ return -ENOENT;
+
+ /*
+ * queued invalidation is already setup and enabled.
+ */
+ if (iommu->qi)
+ return 0;
+
+ iommu->qi = kmalloc(sizeof(*qi), GFP_KERNEL);
+ if (!iommu->qi)
+ return -ENOMEM;
+
+ qi = iommu->qi;
+
+ qi->desc = (void *)(get_zeroed_page(GFP_KERNEL));
+ if (!qi->desc) {
+ kfree(qi);
+ iommu->qi = 0;
+ return -ENOMEM;
+ }
+
+ qi->desc_status = kmalloc(QI_LENGTH * sizeof(int), GFP_KERNEL);
+ if (!qi->desc_status) {
+ free_page((unsigned long) qi->desc);
+ kfree(qi);
+ iommu->qi = 0;
+ return -ENOMEM;
+ }
+
+ qi->free_head = qi->free_tail = 0;
+ qi->free_cnt = QI_LENGTH;
+
+ spin_lock_init(&qi->q_lock);
+
+ spin_lock_irqsave(&iommu->register_lock, flags);
+ /* write zero to the tail reg */
+ writel(0, iommu->reg + DMAR_IQT_REG);
+
+ dmar_writeq(iommu->reg + DMAR_IQA_REG, virt_to_phys(qi->desc));
+
+ cmd = iommu->gcmd | DMA_GCMD_QIE;
+ iommu->gcmd |= DMA_GCMD_QIE;
+ writel(cmd, iommu->reg + DMAR_GCMD_REG);
+
+ /* Make sure hardware complete it */
+ IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, readl, (sts & DMA_GSTS_QIES), sts);
+ spin_unlock_irqrestore(&iommu->register_lock, flags);
+
+ return 0;
+}
diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c
index 6c4c1c3..389fdd6 100644
--- a/drivers/pci/intel-iommu.c
+++ b/drivers/pci/intel-iommu.c
@@ -49,8 +49,6 @@
#define DEFAULT_DOMAIN_ADDRESS_WIDTH 48
-#define DMAR_OPERATION_TIMEOUT ((cycles_t) tsc_khz*10*1000) /* 10sec */
-
#define DOMAIN_MAX_ADDR(gaw) ((((u64)1) << gaw) - 1)
@@ -58,8 +56,6 @@
DEFINE_TIMER(unmap_timer, flush_unmaps_timeout, 0, 0);
-static struct intel_iommu *g_iommus;
-
#define HIGH_WATER_MARK 250
struct deferred_flush_tables {
int next;
@@ -185,13 +181,6 @@
kmem_cache_free(iommu_iova_cache, iova);
}
-static inline void __iommu_flush_cache(
- struct intel_iommu *iommu, void *addr, int size)
-{
- if (!ecap_coherent(iommu->ecap))
- clflush_cache_range(addr, size);
-}
-
/* Gets context entry for a given bus and devfn */
static struct context_entry * device_to_context_entry(struct intel_iommu *iommu,
u8 bus, u8 devfn)
@@ -488,19 +477,6 @@
return 0;
}
-#define IOMMU_WAIT_OP(iommu, offset, op, cond, sts) \
-{\
- cycles_t start_time = get_cycles();\
- while (1) {\
- sts = op (iommu->reg + offset);\
- if (cond)\
- break;\
- if (DMAR_OPERATION_TIMEOUT < (get_cycles() - start_time))\
- panic("DMAR hardware is malfunctioning\n");\
- cpu_relax();\
- }\
-}
-
static void iommu_set_root_entry(struct intel_iommu *iommu)
{
void *addr;
@@ -990,6 +966,8 @@
return -ENOMEM;
}
+ spin_lock_init(&iommu->lock);
+
/*
* if Caching mode is set, then invalid translations are tagged
* with domainid 0. Hence we need to pre-allocate it.
@@ -998,62 +976,15 @@
set_bit(0, iommu->domain_ids);
return 0;
}
-static struct intel_iommu *alloc_iommu(struct intel_iommu *iommu,
- struct dmar_drhd_unit *drhd)
-{
- int ret;
- int map_size;
- u32 ver;
- iommu->reg = ioremap(drhd->reg_base_addr, PAGE_SIZE_4K);
- if (!iommu->reg) {
- printk(KERN_ERR "IOMMU: can't map the region\n");
- goto error;
- }
- iommu->cap = dmar_readq(iommu->reg + DMAR_CAP_REG);
- iommu->ecap = dmar_readq(iommu->reg + DMAR_ECAP_REG);
-
- /* the registers might be more than one page */
- map_size = max_t(int, ecap_max_iotlb_offset(iommu->ecap),
- cap_max_fault_reg_offset(iommu->cap));
- map_size = PAGE_ALIGN_4K(map_size);
- if (map_size > PAGE_SIZE_4K) {
- iounmap(iommu->reg);
- iommu->reg = ioremap(drhd->reg_base_addr, map_size);
- if (!iommu->reg) {
- printk(KERN_ERR "IOMMU: can't map the region\n");
- goto error;
- }
- }
-
- ver = readl(iommu->reg + DMAR_VER_REG);
- pr_debug("IOMMU %llx: ver %d:%d cap %llx ecap %llx\n",
- drhd->reg_base_addr, DMAR_VER_MAJOR(ver), DMAR_VER_MINOR(ver),
- iommu->cap, iommu->ecap);
- ret = iommu_init_domains(iommu);
- if (ret)
- goto error_unmap;
- spin_lock_init(&iommu->lock);
- spin_lock_init(&iommu->register_lock);
-
- drhd->iommu = iommu;
- return iommu;
-error_unmap:
- iounmap(iommu->reg);
-error:
- kfree(iommu);
- return NULL;
-}
static void domain_exit(struct dmar_domain *domain);
-static void free_iommu(struct intel_iommu *iommu)
+
+void free_dmar_iommu(struct intel_iommu *iommu)
{
struct dmar_domain *domain;
int i;
- if (!iommu)
- return;
-
i = find_first_bit(iommu->domain_ids, cap_ndoms(iommu->cap));
for (; i < cap_ndoms(iommu->cap); ) {
domain = iommu->domains[i];
@@ -1078,10 +1009,6 @@
/* free context mapping */
free_context_table(iommu);
-
- if (iommu->reg)
- iounmap(iommu->reg);
- kfree(iommu);
}
static struct dmar_domain * iommu_alloc_domain(struct intel_iommu *iommu)
@@ -1426,37 +1353,6 @@
return NULL;
}
-static int dmar_pci_device_match(struct pci_dev *devices[], int cnt,
- struct pci_dev *dev)
-{
- int index;
-
- while (dev) {
- for (index = 0; index < cnt; index++)
- if (dev == devices[index])
- return 1;
-
- /* Check our parent */
- dev = dev->bus->self;
- }
-
- return 0;
-}
-
-static struct dmar_drhd_unit *
-dmar_find_matched_drhd_unit(struct pci_dev *dev)
-{
- struct dmar_drhd_unit *drhd = NULL;
-
- list_for_each_entry(drhd, &dmar_drhd_units, list) {
- if (drhd->include_all || dmar_pci_device_match(drhd->devices,
- drhd->devices_cnt, dev))
- return drhd;
- }
-
- return NULL;
-}
-
/* domain is initialized */
static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw)
{
@@ -1729,8 +1625,6 @@
* endfor
*/
for_each_drhd_unit(drhd) {
- if (drhd->ignored)
- continue;
g_num_of_iommus++;
/*
* lock not needed as this is only incremented in the single
@@ -1739,12 +1633,6 @@
*/
}
- g_iommus = kzalloc(g_num_of_iommus * sizeof(*iommu), GFP_KERNEL);
- if (!g_iommus) {
- ret = -ENOMEM;
- goto error;
- }
-
deferred_flush = kzalloc(g_num_of_iommus *
sizeof(struct deferred_flush_tables), GFP_KERNEL);
if (!deferred_flush) {
@@ -1752,16 +1640,15 @@
goto error;
}
- i = 0;
for_each_drhd_unit(drhd) {
if (drhd->ignored)
continue;
- iommu = alloc_iommu(&g_iommus[i], drhd);
- i++;
- if (!iommu) {
- ret = -ENOMEM;
+
+ iommu = drhd->iommu;
+
+ ret = iommu_init_domains(iommu);
+ if (ret)
goto error;
- }
/*
* TBD:
@@ -1845,7 +1732,6 @@
iommu = drhd->iommu;
free_iommu(iommu);
}
- kfree(g_iommus);
return ret;
}
@@ -2002,7 +1888,10 @@
/* just flush them all */
for (i = 0; i < g_num_of_iommus; i++) {
if (deferred_flush[i].next) {
- iommu_flush_iotlb_global(&g_iommus[i], 0);
+ struct intel_iommu *iommu =
+ deferred_flush[i].domain[0]->iommu;
+
+ iommu_flush_iotlb_global(iommu, 0);
for (j = 0; j < deferred_flush[i].next; j++) {
__free_iova(&deferred_flush[i].domain[j]->iovad,
deferred_flush[i].iova[j]);
@@ -2032,7 +1921,8 @@
if (list_size == HIGH_WATER_MARK)
flush_unmaps();
- iommu_id = dom->iommu - g_iommus;
+ iommu_id = dom->iommu->seq_id;
+
next = deferred_flush[iommu_id].next;
deferred_flush[iommu_id].domain[next] = dom;
deferred_flush[iommu_id].iova[next] = iova;
@@ -2348,38 +2238,6 @@
}
-static int blacklist_iommu(const struct dmi_system_id *id)
-{
- printk(KERN_INFO "%s detected; disabling IOMMU\n",
- id->ident);
- dmar_disabled = 1;
- return 0;
-}
-
-static struct dmi_system_id __initdata intel_iommu_dmi_table[] = {
- { /* Some DG33BU BIOS revisions advertised non-existent VT-d */
- .callback = blacklist_iommu,
- .ident = "Intel DG33BU",
- { DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"),
- DMI_MATCH(DMI_BOARD_NAME, "DG33BU"),
- }
- },
- { }
-};
-
-
-void __init detect_intel_iommu(void)
-{
- if (swiotlb || no_iommu || iommu_detected || dmar_disabled)
- return;
- if (early_dmar_detect()) {
- dmi_check_system(intel_iommu_dmi_table);
- if (dmar_disabled)
- return;
- iommu_detected = 1;
- }
-}
-
static void __init init_no_remapping_devices(void)
{
struct dmar_drhd_unit *drhd;
@@ -2426,12 +2284,19 @@
{
int ret = 0;
- if (no_iommu || swiotlb || dmar_disabled)
- return -ENODEV;
-
if (dmar_table_init())
return -ENODEV;
+ if (dmar_dev_scope_init())
+ return -ENODEV;
+
+ /*
+ * Check the need for DMA-remapping initialization now.
+ * Above initialization will also be used by Interrupt-remapping.
+ */
+ if (no_iommu || swiotlb || dmar_disabled)
+ return -ENODEV;
+
iommu_init_mempool();
dmar_init_reserved_ranges();
diff --git a/drivers/pci/intel-iommu.h b/drivers/pci/intel-iommu.h
index afc0ad9..2142c01 100644
--- a/drivers/pci/intel-iommu.h
+++ b/drivers/pci/intel-iommu.h
@@ -27,19 +27,8 @@
#include <linux/sysdev.h>
#include "iova.h"
#include <linux/io.h>
-
-/*
- * We need a fixed PAGE_SIZE of 4K irrespective of
- * arch PAGE_SIZE for IOMMU page tables.
- */
-#define PAGE_SHIFT_4K (12)
-#define PAGE_SIZE_4K (1UL << PAGE_SHIFT_4K)
-#define PAGE_MASK_4K (((u64)-1) << PAGE_SHIFT_4K)
-#define PAGE_ALIGN_4K(addr) (((addr) + PAGE_SIZE_4K - 1) & PAGE_MASK_4K)
-
-#define IOVA_PFN(addr) ((addr) >> PAGE_SHIFT_4K)
-#define DMA_32BIT_PFN IOVA_PFN(DMA_32BIT_MASK)
-#define DMA_64BIT_PFN IOVA_PFN(DMA_64BIT_MASK)
+#include <asm/cacheflush.h>
+#include "dma_remapping.h"
/*
* Intel IOMMU register specification per version 1.0 public spec.
@@ -63,6 +52,11 @@
#define DMAR_PLMLIMIT_REG 0x6c /* PMRR low limit */
#define DMAR_PHMBASE_REG 0x70 /* pmrr high base addr */
#define DMAR_PHMLIMIT_REG 0x78 /* pmrr high limit */
+#define DMAR_IQH_REG 0x80 /* Invalidation queue head register */
+#define DMAR_IQT_REG 0x88 /* Invalidation queue tail register */
+#define DMAR_IQA_REG 0x90 /* Invalidation queue addr register */
+#define DMAR_ICS_REG 0x98 /* Invalidation complete status register */
+#define DMAR_IRTA_REG 0xb8 /* Interrupt remapping table addr register */
#define OFFSET_STRIDE (9)
/*
@@ -126,6 +120,10 @@
#define ecap_max_iotlb_offset(e) \
(ecap_iotlb_offset(e) + ecap_niotlb_iunits(e) * 16)
#define ecap_coherent(e) ((e) & 0x1)
+#define ecap_qis(e) ((e) & 0x2)
+#define ecap_eim_support(e) ((e >> 4) & 0x1)
+#define ecap_ir_support(e) ((e >> 3) & 0x1)
+#define ecap_max_handle_mask(e) ((e >> 20) & 0xf)
/* IOTLB_REG */
@@ -141,6 +139,17 @@
#define DMA_TLB_IH_NONLEAF (((u64)1) << 6)
#define DMA_TLB_MAX_SIZE (0x3f)
+/* INVALID_DESC */
+#define DMA_ID_TLB_GLOBAL_FLUSH (((u64)1) << 3)
+#define DMA_ID_TLB_DSI_FLUSH (((u64)2) << 3)
+#define DMA_ID_TLB_PSI_FLUSH (((u64)3) << 3)
+#define DMA_ID_TLB_READ_DRAIN (((u64)1) << 7)
+#define DMA_ID_TLB_WRITE_DRAIN (((u64)1) << 6)
+#define DMA_ID_TLB_DID(id) (((u64)((id & 0xffff) << 16)))
+#define DMA_ID_TLB_IH_NONLEAF (((u64)1) << 6)
+#define DMA_ID_TLB_ADDR(addr) (addr)
+#define DMA_ID_TLB_ADDR_MASK(mask) (mask)
+
/* PMEN_REG */
#define DMA_PMEN_EPM (((u32)1)<<31)
#define DMA_PMEN_PRS (((u32)1)<<0)
@@ -151,6 +160,9 @@
#define DMA_GCMD_SFL (((u32)1) << 29)
#define DMA_GCMD_EAFL (((u32)1) << 28)
#define DMA_GCMD_WBF (((u32)1) << 27)
+#define DMA_GCMD_QIE (((u32)1) << 26)
+#define DMA_GCMD_SIRTP (((u32)1) << 24)
+#define DMA_GCMD_IRE (((u32) 1) << 25)
/* GSTS_REG */
#define DMA_GSTS_TES (((u32)1) << 31)
@@ -158,6 +170,9 @@
#define DMA_GSTS_FLS (((u32)1) << 29)
#define DMA_GSTS_AFLS (((u32)1) << 28)
#define DMA_GSTS_WBFS (((u32)1) << 27)
+#define DMA_GSTS_QIES (((u32)1) << 26)
+#define DMA_GSTS_IRTPS (((u32)1) << 24)
+#define DMA_GSTS_IRES (((u32)1) << 25)
/* CCMD_REG */
#define DMA_CCMD_ICC (((u64)1) << 63)
@@ -187,158 +202,106 @@
#define dma_frcd_source_id(c) (c & 0xffff)
#define dma_frcd_page_addr(d) (d & (((u64)-1) << 12)) /* low 64 bit */
-/*
- * 0: Present
- * 1-11: Reserved
- * 12-63: Context Ptr (12 - (haw-1))
- * 64-127: Reserved
- */
-struct root_entry {
- u64 val;
- u64 rsvd1;
-};
-#define ROOT_ENTRY_NR (PAGE_SIZE_4K/sizeof(struct root_entry))
-static inline bool root_present(struct root_entry *root)
-{
- return (root->val & 1);
-}
-static inline void set_root_present(struct root_entry *root)
-{
- root->val |= 1;
-}
-static inline void set_root_value(struct root_entry *root, unsigned long value)
-{
- root->val |= value & PAGE_MASK_4K;
+#define DMAR_OPERATION_TIMEOUT ((cycles_t) tsc_khz*10*1000) /* 10sec */
+
+#define IOMMU_WAIT_OP(iommu, offset, op, cond, sts) \
+{\
+ cycles_t start_time = get_cycles();\
+ while (1) {\
+ sts = op (iommu->reg + offset);\
+ if (cond)\
+ break;\
+ if (DMAR_OPERATION_TIMEOUT < (get_cycles() - start_time))\
+ panic("DMAR hardware is malfunctioning\n");\
+ cpu_relax();\
+ }\
}
-struct context_entry;
-static inline struct context_entry *
-get_context_addr_from_root(struct root_entry *root)
-{
- return (struct context_entry *)
- (root_present(root)?phys_to_virt(
- root->val & PAGE_MASK_4K):
- NULL);
-}
+#define QI_LENGTH 256 /* queue length */
-/*
- * low 64 bits:
- * 0: present
- * 1: fault processing disable
- * 2-3: translation type
- * 12-63: address space root
- * high 64 bits:
- * 0-2: address width
- * 3-6: aval
- * 8-23: domain id
- */
-struct context_entry {
- u64 lo;
- u64 hi;
-};
-#define context_present(c) ((c).lo & 1)
-#define context_fault_disable(c) (((c).lo >> 1) & 1)
-#define context_translation_type(c) (((c).lo >> 2) & 3)
-#define context_address_root(c) ((c).lo & PAGE_MASK_4K)
-#define context_address_width(c) ((c).hi & 7)
-#define context_domain_id(c) (((c).hi >> 8) & ((1 << 16) - 1))
-
-#define context_set_present(c) do {(c).lo |= 1;} while (0)
-#define context_set_fault_enable(c) \
- do {(c).lo &= (((u64)-1) << 2) | 1;} while (0)
-#define context_set_translation_type(c, val) \
- do { \
- (c).lo &= (((u64)-1) << 4) | 3; \
- (c).lo |= ((val) & 3) << 2; \
- } while (0)
-#define CONTEXT_TT_MULTI_LEVEL 0
-#define context_set_address_root(c, val) \
- do {(c).lo |= (val) & PAGE_MASK_4K;} while (0)
-#define context_set_address_width(c, val) do {(c).hi |= (val) & 7;} while (0)
-#define context_set_domain_id(c, val) \
- do {(c).hi |= ((val) & ((1 << 16) - 1)) << 8;} while (0)
-#define context_clear_entry(c) do {(c).lo = 0; (c).hi = 0;} while (0)
-
-/*
- * 0: readable
- * 1: writable
- * 2-6: reserved
- * 7: super page
- * 8-11: available
- * 12-63: Host physcial address
- */
-struct dma_pte {
- u64 val;
-};
-#define dma_clear_pte(p) do {(p).val = 0;} while (0)
-
-#define DMA_PTE_READ (1)
-#define DMA_PTE_WRITE (2)
-
-#define dma_set_pte_readable(p) do {(p).val |= DMA_PTE_READ;} while (0)
-#define dma_set_pte_writable(p) do {(p).val |= DMA_PTE_WRITE;} while (0)
-#define dma_set_pte_prot(p, prot) \
- do {(p).val = ((p).val & ~3) | ((prot) & 3); } while (0)
-#define dma_pte_addr(p) ((p).val & PAGE_MASK_4K)
-#define dma_set_pte_addr(p, addr) do {\
- (p).val |= ((addr) & PAGE_MASK_4K); } while (0)
-#define dma_pte_present(p) (((p).val & 3) != 0)
-
-struct intel_iommu;
-
-struct dmar_domain {
- int id; /* domain id */
- struct intel_iommu *iommu; /* back pointer to owning iommu */
-
- struct list_head devices; /* all devices' list */
- struct iova_domain iovad; /* iova's that belong to this domain */
-
- struct dma_pte *pgd; /* virtual address */
- spinlock_t mapping_lock; /* page table lock */
- int gaw; /* max guest address width */
-
- /* adjusted guest address width, 0 is level 2 30-bit */
- int agaw;
-
-#define DOMAIN_FLAG_MULTIPLE_DEVICES 1
- int flags;
+enum {
+ QI_FREE,
+ QI_IN_USE,
+ QI_DONE
};
-/* PCI domain-device relationship */
-struct device_domain_info {
- struct list_head link; /* link to domain siblings */
- struct list_head global; /* link to global list */
- u8 bus; /* PCI bus numer */
- u8 devfn; /* PCI devfn number */
- struct pci_dev *dev; /* it's NULL for PCIE-to-PCI bridge */
- struct dmar_domain *domain; /* pointer to domain */
+#define QI_CC_TYPE 0x1
+#define QI_IOTLB_TYPE 0x2
+#define QI_DIOTLB_TYPE 0x3
+#define QI_IEC_TYPE 0x4
+#define QI_IWD_TYPE 0x5
+
+#define QI_IEC_SELECTIVE (((u64)1) << 4)
+#define QI_IEC_IIDEX(idx) (((u64)(idx & 0xffff) << 32))
+#define QI_IEC_IM(m) (((u64)(m & 0x1f) << 27))
+
+#define QI_IWD_STATUS_DATA(d) (((u64)d) << 32)
+#define QI_IWD_STATUS_WRITE (((u64)1) << 5)
+
+struct qi_desc {
+ u64 low, high;
};
-extern int init_dmars(void);
+struct q_inval {
+ spinlock_t q_lock;
+ struct qi_desc *desc; /* invalidation queue */
+ int *desc_status; /* desc status */
+ int free_head; /* first free entry */
+ int free_tail; /* last free entry */
+ int free_cnt;
+};
+
+#ifdef CONFIG_INTR_REMAP
+/* 1MB - maximum possible interrupt remapping table size */
+#define INTR_REMAP_PAGE_ORDER 8
+#define INTR_REMAP_TABLE_REG_SIZE 0xf
+
+#define INTR_REMAP_TABLE_ENTRIES 65536
+
+struct ir_table {
+ struct irte *base;
+};
+#endif
struct intel_iommu {
void __iomem *reg; /* Pointer to hardware regs, virtual addr */
u64 cap;
u64 ecap;
- unsigned long *domain_ids; /* bitmap of domains */
- struct dmar_domain **domains; /* ptr to domains */
int seg;
u32 gcmd; /* Holds TE, EAFL. Don't need SRTP, SFL, WBF */
- spinlock_t lock; /* protect context, domain ids */
spinlock_t register_lock; /* protect register handling */
+ int seq_id; /* sequence id of the iommu */
+
+#ifdef CONFIG_DMAR
+ unsigned long *domain_ids; /* bitmap of domains */
+ struct dmar_domain **domains; /* ptr to domains */
+ spinlock_t lock; /* protect context, domain ids */
struct root_entry *root_entry; /* virtual address */
unsigned int irq;
unsigned char name[7]; /* Device Name */
struct msi_msg saved_msg;
struct sys_device sysdev;
+#endif
+ struct q_inval *qi; /* Queued invalidation info */
+#ifdef CONFIG_INTR_REMAP
+ struct ir_table *ir_table; /* Interrupt remapping info */
+#endif
};
-#ifndef CONFIG_DMAR_GFX_WA
-static inline void iommu_prepare_gfx_mapping(void)
+static inline void __iommu_flush_cache(
+ struct intel_iommu *iommu, void *addr, int size)
{
- return;
+ if (!ecap_coherent(iommu->ecap))
+ clflush_cache_range(addr, size);
}
-#endif /* !CONFIG_DMAR_GFX_WA */
+extern struct dmar_drhd_unit * dmar_find_matched_drhd_unit(struct pci_dev *dev);
+
+extern int alloc_iommu(struct dmar_drhd_unit *drhd);
+extern void free_iommu(struct intel_iommu *iommu);
+extern int dmar_enable_qi(struct intel_iommu *iommu);
+extern void qi_global_iec(struct intel_iommu *iommu);
+
+extern void qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu);
#endif
diff --git a/drivers/pci/intr_remapping.c b/drivers/pci/intr_remapping.c
new file mode 100644
index 0000000..bb642cc
--- /dev/null
+++ b/drivers/pci/intr_remapping.c
@@ -0,0 +1,471 @@
+#include <linux/dmar.h>
+#include <linux/spinlock.h>
+#include <linux/jiffies.h>
+#include <linux/pci.h>
+#include <linux/irq.h>
+#include <asm/io_apic.h>
+#include "intel-iommu.h"
+#include "intr_remapping.h"
+
+static struct ioapic_scope ir_ioapic[MAX_IO_APICS];
+static int ir_ioapic_num;
+int intr_remapping_enabled;
+
+static struct {
+ struct intel_iommu *iommu;
+ u16 irte_index;
+ u16 sub_handle;
+ u8 irte_mask;
+} irq_2_iommu[NR_IRQS];
+
+static DEFINE_SPINLOCK(irq_2_ir_lock);
+
+int irq_remapped(int irq)
+{
+ if (irq > NR_IRQS)
+ return 0;
+
+ if (!irq_2_iommu[irq].iommu)
+ return 0;
+
+ return 1;
+}
+
+int get_irte(int irq, struct irte *entry)
+{
+ int index;
+
+ if (!entry || irq > NR_IRQS)
+ return -1;
+
+ spin_lock(&irq_2_ir_lock);
+ if (!irq_2_iommu[irq].iommu) {
+ spin_unlock(&irq_2_ir_lock);
+ return -1;
+ }
+
+ index = irq_2_iommu[irq].irte_index + irq_2_iommu[irq].sub_handle;
+ *entry = *(irq_2_iommu[irq].iommu->ir_table->base + index);
+
+ spin_unlock(&irq_2_ir_lock);
+ return 0;
+}
+
+int alloc_irte(struct intel_iommu *iommu, int irq, u16 count)
+{
+ struct ir_table *table = iommu->ir_table;
+ u16 index, start_index;
+ unsigned int mask = 0;
+ int i;
+
+ if (!count)
+ return -1;
+
+ /*
+ * start the IRTE search from index 0.
+ */
+ index = start_index = 0;
+
+ if (count > 1) {
+ count = __roundup_pow_of_two(count);
+ mask = ilog2(count);
+ }
+
+ if (mask > ecap_max_handle_mask(iommu->ecap)) {
+ printk(KERN_ERR
+ "Requested mask %x exceeds the max invalidation handle"
+ " mask value %Lx\n", mask,
+ ecap_max_handle_mask(iommu->ecap));
+ return -1;
+ }
+
+ spin_lock(&irq_2_ir_lock);
+ do {
+ for (i = index; i < index + count; i++)
+ if (table->base[i].present)
+ break;
+ /* empty index found */
+ if (i == index + count)
+ break;
+
+ index = (index + count) % INTR_REMAP_TABLE_ENTRIES;
+
+ if (index == start_index) {
+ spin_unlock(&irq_2_ir_lock);
+ printk(KERN_ERR "can't allocate an IRTE\n");
+ return -1;
+ }
+ } while (1);
+
+ for (i = index; i < index + count; i++)
+ table->base[i].present = 1;
+
+ irq_2_iommu[irq].iommu = iommu;
+ irq_2_iommu[irq].irte_index = index;
+ irq_2_iommu[irq].sub_handle = 0;
+ irq_2_iommu[irq].irte_mask = mask;
+
+ spin_unlock(&irq_2_ir_lock);
+
+ return index;
+}
+
+static void qi_flush_iec(struct intel_iommu *iommu, int index, int mask)
+{
+ struct qi_desc desc;
+
+ desc.low = QI_IEC_IIDEX(index) | QI_IEC_TYPE | QI_IEC_IM(mask)
+ | QI_IEC_SELECTIVE;
+ desc.high = 0;
+
+ qi_submit_sync(&desc, iommu);
+}
+
+int map_irq_to_irte_handle(int irq, u16 *sub_handle)
+{
+ int index;
+
+ spin_lock(&irq_2_ir_lock);
+ if (irq >= NR_IRQS || !irq_2_iommu[irq].iommu) {
+ spin_unlock(&irq_2_ir_lock);
+ return -1;
+ }
+
+ *sub_handle = irq_2_iommu[irq].sub_handle;
+ index = irq_2_iommu[irq].irte_index;
+ spin_unlock(&irq_2_ir_lock);
+ return index;
+}
+
+int set_irte_irq(int irq, struct intel_iommu *iommu, u16 index, u16 subhandle)
+{
+ spin_lock(&irq_2_ir_lock);
+ if (irq >= NR_IRQS || irq_2_iommu[irq].iommu) {
+ spin_unlock(&irq_2_ir_lock);
+ return -1;
+ }
+
+ irq_2_iommu[irq].iommu = iommu;
+ irq_2_iommu[irq].irte_index = index;
+ irq_2_iommu[irq].sub_handle = subhandle;
+ irq_2_iommu[irq].irte_mask = 0;
+
+ spin_unlock(&irq_2_ir_lock);
+
+ return 0;
+}
+
+int clear_irte_irq(int irq, struct intel_iommu *iommu, u16 index)
+{
+ spin_lock(&irq_2_ir_lock);
+ if (irq >= NR_IRQS || !irq_2_iommu[irq].iommu) {
+ spin_unlock(&irq_2_ir_lock);
+ return -1;
+ }
+
+ irq_2_iommu[irq].iommu = NULL;
+ irq_2_iommu[irq].irte_index = 0;
+ irq_2_iommu[irq].sub_handle = 0;
+ irq_2_iommu[irq].irte_mask = 0;
+
+ spin_unlock(&irq_2_ir_lock);
+
+ return 0;
+}
+
+int modify_irte(int irq, struct irte *irte_modified)
+{
+ int index;
+ struct irte *irte;
+ struct intel_iommu *iommu;
+
+ spin_lock(&irq_2_ir_lock);
+ if (irq >= NR_IRQS || !irq_2_iommu[irq].iommu) {
+ spin_unlock(&irq_2_ir_lock);
+ return -1;
+ }
+
+ iommu = irq_2_iommu[irq].iommu;
+
+ index = irq_2_iommu[irq].irte_index + irq_2_iommu[irq].sub_handle;
+ irte = &iommu->ir_table->base[index];
+
+ set_64bit((unsigned long *)irte, irte_modified->low | (1 << 1));
+ __iommu_flush_cache(iommu, irte, sizeof(*irte));
+
+ qi_flush_iec(iommu, index, 0);
+
+ spin_unlock(&irq_2_ir_lock);
+ return 0;
+}
+
+int flush_irte(int irq)
+{
+ int index;
+ struct intel_iommu *iommu;
+
+ spin_lock(&irq_2_ir_lock);
+ if (irq >= NR_IRQS || !irq_2_iommu[irq].iommu) {
+ spin_unlock(&irq_2_ir_lock);
+ return -1;
+ }
+
+ iommu = irq_2_iommu[irq].iommu;
+
+ index = irq_2_iommu[irq].irte_index + irq_2_iommu[irq].sub_handle;
+
+ qi_flush_iec(iommu, index, irq_2_iommu[irq].irte_mask);
+ spin_unlock(&irq_2_ir_lock);
+
+ return 0;
+}
+
+struct intel_iommu *map_ioapic_to_ir(int apic)
+{
+ int i;
+
+ for (i = 0; i < MAX_IO_APICS; i++)
+ if (ir_ioapic[i].id == apic)
+ return ir_ioapic[i].iommu;
+ return NULL;
+}
+
+struct intel_iommu *map_dev_to_ir(struct pci_dev *dev)
+{
+ struct dmar_drhd_unit *drhd;
+
+ drhd = dmar_find_matched_drhd_unit(dev);
+ if (!drhd)
+ return NULL;
+
+ return drhd->iommu;
+}
+
+int free_irte(int irq)
+{
+ int index, i;
+ struct irte *irte;
+ struct intel_iommu *iommu;
+
+ spin_lock(&irq_2_ir_lock);
+ if (irq >= NR_IRQS || !irq_2_iommu[irq].iommu) {
+ spin_unlock(&irq_2_ir_lock);
+ return -1;
+ }
+
+ iommu = irq_2_iommu[irq].iommu;
+
+ index = irq_2_iommu[irq].irte_index + irq_2_iommu[irq].sub_handle;
+ irte = &iommu->ir_table->base[index];
+
+ if (!irq_2_iommu[irq].sub_handle) {
+ for (i = 0; i < (1 << irq_2_iommu[irq].irte_mask); i++)
+ set_64bit((unsigned long *)irte, 0);
+ qi_flush_iec(iommu, index, irq_2_iommu[irq].irte_mask);
+ }
+
+ irq_2_iommu[irq].iommu = NULL;
+ irq_2_iommu[irq].irte_index = 0;
+ irq_2_iommu[irq].sub_handle = 0;
+ irq_2_iommu[irq].irte_mask = 0;
+
+ spin_unlock(&irq_2_ir_lock);
+
+ return 0;
+}
+
+static void iommu_set_intr_remapping(struct intel_iommu *iommu, int mode)
+{
+ u64 addr;
+ u32 cmd, sts;
+ unsigned long flags;
+
+ addr = virt_to_phys((void *)iommu->ir_table->base);
+
+ spin_lock_irqsave(&iommu->register_lock, flags);
+
+ dmar_writeq(iommu->reg + DMAR_IRTA_REG,
+ (addr) | IR_X2APIC_MODE(mode) | INTR_REMAP_TABLE_REG_SIZE);
+
+ /* Set interrupt-remapping table pointer */
+ cmd = iommu->gcmd | DMA_GCMD_SIRTP;
+ writel(cmd, iommu->reg + DMAR_GCMD_REG);
+
+ IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
+ readl, (sts & DMA_GSTS_IRTPS), sts);
+ spin_unlock_irqrestore(&iommu->register_lock, flags);
+
+ /*
+ * global invalidation of interrupt entry cache before enabling
+ * interrupt-remapping.
+ */
+ qi_global_iec(iommu);
+
+ spin_lock_irqsave(&iommu->register_lock, flags);
+
+ /* Enable interrupt-remapping */
+ cmd = iommu->gcmd | DMA_GCMD_IRE;
+ iommu->gcmd |= DMA_GCMD_IRE;
+ writel(cmd, iommu->reg + DMAR_GCMD_REG);
+
+ IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG,
+ readl, (sts & DMA_GSTS_IRES), sts);
+
+ spin_unlock_irqrestore(&iommu->register_lock, flags);
+}
+
+
+static int setup_intr_remapping(struct intel_iommu *iommu, int mode)
+{
+ struct ir_table *ir_table;
+ struct page *pages;
+
+ ir_table = iommu->ir_table = kzalloc(sizeof(struct ir_table),
+ GFP_KERNEL);
+
+ if (!iommu->ir_table)
+ return -ENOMEM;
+
+ pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, INTR_REMAP_PAGE_ORDER);
+
+ if (!pages) {
+ printk(KERN_ERR "failed to allocate pages of order %d\n",
+ INTR_REMAP_PAGE_ORDER);
+ kfree(iommu->ir_table);
+ return -ENOMEM;
+ }
+
+ ir_table->base = page_address(pages);
+
+ iommu_set_intr_remapping(iommu, mode);
+ return 0;
+}
+
+int __init enable_intr_remapping(int eim)
+{
+ struct dmar_drhd_unit *drhd;
+ int setup = 0;
+
+ /*
+ * check for the Interrupt-remapping support
+ */
+ for_each_drhd_unit(drhd) {
+ struct intel_iommu *iommu = drhd->iommu;
+
+ if (!ecap_ir_support(iommu->ecap))
+ continue;
+
+ if (eim && !ecap_eim_support(iommu->ecap)) {
+ printk(KERN_INFO "DRHD %Lx: EIM not supported by DRHD, "
+ " ecap %Lx\n", drhd->reg_base_addr, iommu->ecap);
+ return -1;
+ }
+ }
+
+ /*
+ * Enable queued invalidation for all the DRHD's.
+ */
+ for_each_drhd_unit(drhd) {
+ int ret;
+ struct intel_iommu *iommu = drhd->iommu;
+ ret = dmar_enable_qi(iommu);
+
+ if (ret) {
+ printk(KERN_ERR "DRHD %Lx: failed to enable queued, "
+ " invalidation, ecap %Lx, ret %d\n",
+ drhd->reg_base_addr, iommu->ecap, ret);
+ return -1;
+ }
+ }
+
+ /*
+ * Setup Interrupt-remapping for all the DRHD's now.
+ */
+ for_each_drhd_unit(drhd) {
+ struct intel_iommu *iommu = drhd->iommu;
+
+ if (!ecap_ir_support(iommu->ecap))
+ continue;
+
+ if (setup_intr_remapping(iommu, eim))
+ goto error;
+
+ setup = 1;
+ }
+
+ if (!setup)
+ goto error;
+
+ intr_remapping_enabled = 1;
+
+ return 0;
+
+error:
+ /*
+ * handle error condition gracefully here!
+ */
+ return -1;
+}
+
+static int ir_parse_ioapic_scope(struct acpi_dmar_header *header,
+ struct intel_iommu *iommu)
+{
+ struct acpi_dmar_hardware_unit *drhd;
+ struct acpi_dmar_device_scope *scope;
+ void *start, *end;
+
+ drhd = (struct acpi_dmar_hardware_unit *)header;
+
+ start = (void *)(drhd + 1);
+ end = ((void *)drhd) + header->length;
+
+ while (start < end) {
+ scope = start;
+ if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_IOAPIC) {
+ if (ir_ioapic_num == MAX_IO_APICS) {
+ printk(KERN_WARNING "Exceeded Max IO APICS\n");
+ return -1;
+ }
+
+ printk(KERN_INFO "IOAPIC id %d under DRHD base"
+ " 0x%Lx\n", scope->enumeration_id,
+ drhd->address);
+
+ ir_ioapic[ir_ioapic_num].iommu = iommu;
+ ir_ioapic[ir_ioapic_num].id = scope->enumeration_id;
+ ir_ioapic_num++;
+ }
+ start += scope->length;
+ }
+
+ return 0;
+}
+
+/*
+ * Finds the assocaition between IOAPIC's and its Interrupt-remapping
+ * hardware unit.
+ */
+int __init parse_ioapics_under_ir(void)
+{
+ struct dmar_drhd_unit *drhd;
+ int ir_supported = 0;
+
+ for_each_drhd_unit(drhd) {
+ struct intel_iommu *iommu = drhd->iommu;
+
+ if (ecap_ir_support(iommu->ecap)) {
+ if (ir_parse_ioapic_scope(drhd->hdr, iommu))
+ return -1;
+
+ ir_supported = 1;
+ }
+ }
+
+ if (ir_supported && ir_ioapic_num != nr_ioapics) {
+ printk(KERN_WARNING
+ "Not all IO-APIC's listed under remapping hardware\n");
+ return -1;
+ }
+
+ return ir_supported;
+}
diff --git a/drivers/pci/intr_remapping.h b/drivers/pci/intr_remapping.h
new file mode 100644
index 0000000..05f2635
--- /dev/null
+++ b/drivers/pci/intr_remapping.h
@@ -0,0 +1,8 @@
+#include "intel-iommu.h"
+
+struct ioapic_scope {
+ struct intel_iommu *iommu;
+ unsigned int id;
+};
+
+#define IR_X2APIC_MODE(mode) (mode ? (1 << 11) : 0)
diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile
index a87902d..74d1c90 100644
--- a/drivers/pcmcia/Makefile
+++ b/drivers/pcmcia/Makefile
@@ -2,10 +2,6 @@
# Makefile for the kernel pcmcia subsystem (c/o David Hinds)
#
-ifeq ($(CONFIG_PCMCIA_DEBUG),y)
-EXTRA_CFLAGS += -DDEBUG
-endif
-
pcmcia_core-y += cs.o cistpl.o rsrc_mgr.o socket_sysfs.o
pcmcia_core-$(CONFIG_CARDBUS) += cardbus.o
obj-$(CONFIG_PCCARD) += pcmcia_core.o
diff --git a/drivers/pcmcia/au1000_generic.c b/drivers/pcmcia/au1000_generic.c
index 75e8f85..fc1de46 100644
--- a/drivers/pcmcia/au1000_generic.c
+++ b/drivers/pcmcia/au1000_generic.c
@@ -292,7 +292,7 @@
skt->spd_io[map->map] = speed;
}
- map->start=(ioaddr_t)(u32)skt->virt_io;
+ map->start=(unsigned int)(u32)skt->virt_io;
map->stop=map->start+MAP_SIZE;
return 0;
diff --git a/drivers/pcmcia/au1000_generic.h b/drivers/pcmcia/au1000_generic.h
index a53ef59..13a4fbc 100644
--- a/drivers/pcmcia/au1000_generic.h
+++ b/drivers/pcmcia/au1000_generic.h
@@ -116,7 +116,7 @@
struct resource res_attr;
void * virt_io;
- ioaddr_t phys_io;
+ unsigned int phys_io;
unsigned int phys_attr;
unsigned int phys_mem;
unsigned short speed_io, speed_attr, speed_mem;
diff --git a/drivers/pcmcia/au1000_pb1x00.c b/drivers/pcmcia/au1000_pb1x00.c
index aa1cd4d..d6b4bd1 100644
--- a/drivers/pcmcia/au1000_pb1x00.c
+++ b/drivers/pcmcia/au1000_pb1x00.c
@@ -37,7 +37,6 @@
#include <pcmcia/ss.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/bus_ops.h>
-#include "cs_internal.h"
#include <asm/io.h>
#include <asm/irq.h>
diff --git a/drivers/pcmcia/au1000_xxs1500.c b/drivers/pcmcia/au1000_xxs1500.c
index 8a9b18c..9627390 100644
--- a/drivers/pcmcia/au1000_xxs1500.c
+++ b/drivers/pcmcia/au1000_xxs1500.c
@@ -41,7 +41,6 @@
#include <pcmcia/ss.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/bus_ops.h>
-#include "cs_internal.h"
#include <asm/io.h>
#include <asm/irq.h>
diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c
index 911ca0e..db77e1f 100644
--- a/drivers/pcmcia/cardbus.c
+++ b/drivers/pcmcia/cardbus.c
@@ -238,7 +238,7 @@
pci_bus_add_devices(bus);
s->irq.AssignedIRQ = s->pci_irq;
- return CS_SUCCESS;
+ return 0;
}
void cb_free(struct pcmcia_socket * s)
diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c
index 65129b5..dcce9f5 100644
--- a/drivers/pcmcia/cistpl.c
+++ b/drivers/pcmcia/cistpl.c
@@ -92,7 +92,8 @@
if (!(s->features & SS_CAP_STATIC_MAP) && (mem->res == NULL)) {
mem->res = pcmcia_find_mem_region(0, s->map_size, s->map_size, 0, s);
if (mem->res == NULL) {
- printk(KERN_NOTICE "cs: unable to map card memory!\n");
+ dev_printk(KERN_NOTICE, &s->dev,
+ "cs: unable to map card memory!\n");
return NULL;
}
s->cis_virt = NULL;
@@ -265,13 +266,13 @@
======================================================================*/
static void read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr,
- u_int len, void *ptr)
+ size_t len, void *ptr)
{
struct cis_cache_entry *cis;
int ret;
if (s->fake_cis) {
- if (s->fake_cis_len > addr+len)
+ if (s->fake_cis_len >= addr+len)
memcpy(ptr, s->fake_cis+addr, len);
else
memset(ptr, 0xff, len);
@@ -351,7 +352,9 @@
buf = kmalloc(256, GFP_KERNEL);
if (buf == NULL)
- return -1;
+ dev_printk(KERN_WARNING, &s->dev,
+ "no memory for verifying CIS\n");
+ return -ENOMEM;
list_for_each_entry(cis, &s->cis_cache, node) {
int len = cis->len;
@@ -380,18 +383,22 @@
======================================================================*/
-int pcmcia_replace_cis(struct pcmcia_socket *s, cisdump_t *cis)
+int pcmcia_replace_cis(struct pcmcia_socket *s,
+ const u8 *data, const size_t len)
{
- kfree(s->fake_cis);
- s->fake_cis = NULL;
- if (cis->Length > CISTPL_MAX_CIS_SIZE)
- return CS_BAD_SIZE;
- s->fake_cis = kmalloc(cis->Length, GFP_KERNEL);
- if (s->fake_cis == NULL)
- return CS_OUT_OF_RESOURCE;
- s->fake_cis_len = cis->Length;
- memcpy(s->fake_cis, cis->Data, cis->Length);
- return CS_SUCCESS;
+ if (len > CISTPL_MAX_CIS_SIZE) {
+ dev_printk(KERN_WARNING, &s->dev, "replacement CIS too big\n");
+ return -EINVAL;
+ }
+ kfree(s->fake_cis);
+ s->fake_cis = kmalloc(len, GFP_KERNEL);
+ if (s->fake_cis == NULL) {
+ dev_printk(KERN_WARNING, &s->dev, "no memory to replace CIS\n");
+ return -ENOMEM;
+ }
+ s->fake_cis_len = len;
+ memcpy(s->fake_cis, data, len);
+ return 0;
}
EXPORT_SYMBOL(pcmcia_replace_cis);
@@ -418,9 +425,9 @@
int pccard_get_first_tuple(struct pcmcia_socket *s, unsigned int function, tuple_t *tuple)
{
if (!s)
- return CS_BAD_HANDLE;
+ return -EINVAL;
if (!(s->state & SOCKET_PRESENT))
- return CS_NO_CARD;
+ return -ENODEV;
tuple->TupleLink = tuple->Flags = 0;
#ifdef CONFIG_CARDBUS
if (s->state & SOCKET_CARDBUS) {
@@ -440,10 +447,10 @@
!(tuple->Attributes & TUPLE_RETURN_COMMON)) {
cisdata_t req = tuple->DesiredTuple;
tuple->DesiredTuple = CISTPL_LONGLINK_MFC;
- if (pccard_get_next_tuple(s, function, tuple) == CS_SUCCESS) {
+ if (pccard_get_next_tuple(s, function, tuple) == 0) {
tuple->DesiredTuple = CISTPL_LINKTARGET;
- if (pccard_get_next_tuple(s, function, tuple) != CS_SUCCESS)
- return CS_NO_MORE_ITEMS;
+ if (pccard_get_next_tuple(s, function, tuple) != 0)
+ return -ENOSPC;
} else
tuple->CISOffset = tuple->TupleLink = 0;
tuple->DesiredTuple = req;
@@ -498,9 +505,9 @@
int ofs, i, attr;
if (!s)
- return CS_BAD_HANDLE;
+ return -EINVAL;
if (!(s->state & SOCKET_PRESENT))
- return CS_NO_CARD;
+ return -ENODEV;
link[1] = tuple->TupleLink;
ofs = tuple->CISOffset + tuple->TupleLink;
@@ -519,7 +526,7 @@
/* End of chain? Follow long link if possible */
if (link[0] == CISTPL_END) {
if ((ofs = follow_link(s, tuple)) < 0)
- return CS_NO_MORE_ITEMS;
+ return -ENOSPC;
attr = SPACE(tuple->Flags);
read_cis_cache(s, attr, ofs, 2, link);
}
@@ -577,13 +584,13 @@
}
if (i == MAX_TUPLES) {
cs_dbg(s, 1, "cs: overrun in pcmcia_get_next_tuple\n");
- return CS_NO_MORE_ITEMS;
+ return -ENOSPC;
}
tuple->TupleCode = link[0];
tuple->TupleLink = link[1];
tuple->CISOffset = ofs + 2;
- return CS_SUCCESS;
+ return 0;
}
EXPORT_SYMBOL(pccard_get_next_tuple);
@@ -596,18 +603,18 @@
u_int len;
if (!s)
- return CS_BAD_HANDLE;
+ return -EINVAL;
if (tuple->TupleLink < tuple->TupleOffset)
- return CS_NO_MORE_ITEMS;
+ return -ENOSPC;
len = tuple->TupleLink - tuple->TupleOffset;
tuple->TupleDataLen = tuple->TupleLink;
if (len == 0)
- return CS_SUCCESS;
+ return 0;
read_cis_cache(s, SPACE(tuple->Flags),
tuple->CISOffset + tuple->TupleOffset,
_MIN(len, tuple->TupleDataMax), tuple->TupleData);
- return CS_SUCCESS;
+ return 0;
}
EXPORT_SYMBOL(pccard_get_tuple_data);
@@ -640,25 +647,31 @@
case 3: device->dev[i].speed = 150; break;
case 4: device->dev[i].speed = 100; break;
case 7:
- if (++p == q) return CS_BAD_TUPLE;
+ if (++p == q)
+ return -EINVAL;
device->dev[i].speed = SPEED_CVT(*p);
while (*p & 0x80)
- if (++p == q) return CS_BAD_TUPLE;
+ if (++p == q)
+ return -EINVAL;
break;
default:
- return CS_BAD_TUPLE;
+ return -EINVAL;
}
- if (++p == q) return CS_BAD_TUPLE;
- if (*p == 0xff) break;
+ if (++p == q)
+ return -EINVAL;
+ if (*p == 0xff)
+ break;
scale = *p & 7;
- if (scale == 7) return CS_BAD_TUPLE;
+ if (scale == 7)
+ return -EINVAL;
device->dev[i].size = ((*p >> 3) + 1) * (512 << (scale*2));
device->ndev++;
- if (++p == q) break;
+ if (++p == q)
+ break;
}
- return CS_SUCCESS;
+ return 0;
}
/*====================================================================*/
@@ -667,12 +680,12 @@
{
u_char *p;
if (tuple->TupleDataLen < 5)
- return CS_BAD_TUPLE;
+ return -EINVAL;
p = (u_char *) tuple->TupleData;
csum->addr = tuple->CISOffset + get_unaligned_le16(p) - 2;
csum->len = get_unaligned_le16(p + 2);
csum->sum = *(p + 4);
- return CS_SUCCESS;
+ return 0;
}
/*====================================================================*/
@@ -680,9 +693,9 @@
static int parse_longlink(tuple_t *tuple, cistpl_longlink_t *link)
{
if (tuple->TupleDataLen < 4)
- return CS_BAD_TUPLE;
+ return -EINVAL;
link->addr = get_unaligned_le32(tuple->TupleData);
- return CS_SUCCESS;
+ return 0;
}
/*====================================================================*/
@@ -697,13 +710,13 @@
link->nfn = *p; p++;
if (tuple->TupleDataLen <= link->nfn*5)
- return CS_BAD_TUPLE;
+ return -EINVAL;
for (i = 0; i < link->nfn; i++) {
link->fn[i].space = *p; p++;
link->fn[i].addr = get_unaligned_le32(p);
p += 4;
}
- return CS_SUCCESS;
+ return 0;
}
/*====================================================================*/
@@ -713,24 +726,27 @@
{
int i, j, ns;
- if (p == q) return CS_BAD_TUPLE;
+ if (p == q)
+ return -EINVAL;
ns = 0; j = 0;
for (i = 0; i < max; i++) {
- if (*p == 0xff) break;
+ if (*p == 0xff)
+ break;
ofs[i] = j;
ns++;
for (;;) {
s[j++] = (*p == 0xff) ? '\0' : *p;
if ((*p == '\0') || (*p == 0xff)) break;
- if (++p == q) return CS_BAD_TUPLE;
+ if (++p == q)
+ return -EINVAL;
}
if ((*p == 0xff) || (++p == q)) break;
}
if (found) {
*found = ns;
- return CS_SUCCESS;
+ return 0;
} else {
- return (ns == max) ? CS_SUCCESS : CS_BAD_TUPLE;
+ return (ns == max) ? 0 : -EINVAL;
}
}
@@ -745,7 +761,8 @@
vers_1->major = *p; p++;
vers_1->minor = *p; p++;
- if (p >= q) return CS_BAD_TUPLE;
+ if (p >= q)
+ return -EINVAL;
return parse_strings(p, q, CISTPL_VERS_1_MAX_PROD_STRINGS,
vers_1->str, vers_1->ofs, &vers_1->ns);
@@ -781,7 +798,7 @@
p += 2;
}
jedec->nid = nid;
- return CS_SUCCESS;
+ return 0;
}
/*====================================================================*/
@@ -789,10 +806,10 @@
static int parse_manfid(tuple_t *tuple, cistpl_manfid_t *m)
{
if (tuple->TupleDataLen < 4)
- return CS_BAD_TUPLE;
+ return -EINVAL;
m->manf = get_unaligned_le16(tuple->TupleData);
m->card = get_unaligned_le16(tuple->TupleData + 2);
- return CS_SUCCESS;
+ return 0;
}
/*====================================================================*/
@@ -801,11 +818,11 @@
{
u_char *p;
if (tuple->TupleDataLen < 2)
- return CS_BAD_TUPLE;
+ return -EINVAL;
p = (u_char *)tuple->TupleData;
f->func = p[0];
f->sysinit = p[1];
- return CS_SUCCESS;
+ return 0;
}
/*====================================================================*/
@@ -815,12 +832,12 @@
u_char *p;
int i;
if (tuple->TupleDataLen < 1)
- return CS_BAD_TUPLE;
+ return -EINVAL;
p = (u_char *)tuple->TupleData;
f->type = p[0];
for (i = 1; i < tuple->TupleDataLen; i++)
f->data[i-1] = p[i];
- return CS_SUCCESS;
+ return 0;
}
/*====================================================================*/
@@ -834,7 +851,7 @@
rasz = *p & 0x03;
rmsz = (*p & 0x3c) >> 2;
if (tuple->TupleDataLen < rasz+rmsz+4)
- return CS_BAD_TUPLE;
+ return -EINVAL;
config->last_idx = *(++p);
p++;
config->base = 0;
@@ -846,7 +863,7 @@
for (i = 0; i <= rmsz; i++)
config->rmask[i>>2] += p[i] << (8*(i%4));
config->subtuples = tuple->TupleDataLen - (rasz+rmsz+4);
- return CS_SUCCESS;
+ return 0;
}
/*======================================================================
@@ -1002,10 +1019,12 @@
static u_char *parse_irq(u_char *p, u_char *q, cistpl_irq_t *irq)
{
- if (p == q) return NULL;
+ if (p == q)
+ return NULL;
irq->IRQInfo1 = *p; p++;
if (irq->IRQInfo1 & IRQ_INFO2_VALID) {
- if (p+2 > q) return NULL;
+ if (p+2 > q)
+ return NULL;
irq->IRQInfo2 = (p[1]<<8) + p[0];
p += 2;
}
@@ -1026,7 +1045,8 @@
if (*p & 0x40)
entry->flags |= CISTPL_CFTABLE_DEFAULT;
if (*p & 0x80) {
- if (++p == q) return CS_BAD_TUPLE;
+ if (++p == q)
+ return -EINVAL;
if (*p & 0x10)
entry->flags |= CISTPL_CFTABLE_BVDS;
if (*p & 0x20)
@@ -1040,30 +1060,35 @@
entry->interface = 0;
/* Process optional features */
- if (++p == q) return CS_BAD_TUPLE;
+ if (++p == q)
+ return -EINVAL;
features = *p; p++;
/* Power options */
if ((features & 3) > 0) {
p = parse_power(p, q, &entry->vcc);
- if (p == NULL) return CS_BAD_TUPLE;
+ if (p == NULL)
+ return -EINVAL;
} else
entry->vcc.present = 0;
if ((features & 3) > 1) {
p = parse_power(p, q, &entry->vpp1);
- if (p == NULL) return CS_BAD_TUPLE;
+ if (p == NULL)
+ return -EINVAL;
} else
entry->vpp1.present = 0;
if ((features & 3) > 2) {
p = parse_power(p, q, &entry->vpp2);
- if (p == NULL) return CS_BAD_TUPLE;
+ if (p == NULL)
+ return -EINVAL;
} else
entry->vpp2.present = 0;
/* Timing options */
if (features & 0x04) {
p = parse_timing(p, q, &entry->timing);
- if (p == NULL) return CS_BAD_TUPLE;
+ if (p == NULL)
+ return -EINVAL;
} else {
entry->timing.wait = 0;
entry->timing.ready = 0;
@@ -1073,14 +1098,16 @@
/* I/O window options */
if (features & 0x08) {
p = parse_io(p, q, &entry->io);
- if (p == NULL) return CS_BAD_TUPLE;
+ if (p == NULL)
+ return -EINVAL;
} else
entry->io.nwin = 0;
/* Interrupt options */
if (features & 0x10) {
p = parse_irq(p, q, &entry->irq);
- if (p == NULL) return CS_BAD_TUPLE;
+ if (p == NULL)
+ return -EINVAL;
} else
entry->irq.IRQInfo1 = 0;
@@ -1094,7 +1121,8 @@
entry->mem.win[0].card_addr = 0;
entry->mem.win[0].host_addr = 0;
p += 2;
- if (p > q) return CS_BAD_TUPLE;
+ if (p > q)
+ return -EINVAL;
break;
case 0x40:
entry->mem.nwin = 1;
@@ -1102,26 +1130,30 @@
entry->mem.win[0].card_addr = get_unaligned_le16(p + 2) << 8;
entry->mem.win[0].host_addr = 0;
p += 4;
- if (p > q) return CS_BAD_TUPLE;
+ if (p > q)
+ return -EINVAL;
break;
case 0x60:
p = parse_mem(p, q, &entry->mem);
- if (p == NULL) return CS_BAD_TUPLE;
+ if (p == NULL)
+ return -EINVAL;
break;
}
/* Misc features */
if (features & 0x80) {
- if (p == q) return CS_BAD_TUPLE;
+ if (p == q)
+ return -EINVAL;
entry->flags |= (*p << 8);
while (*p & 0x80)
- if (++p == q) return CS_BAD_TUPLE;
+ if (++p == q)
+ return -EINVAL;
p++;
}
entry->subtuples = q-p;
- return CS_SUCCESS;
+ return 0;
}
/*====================================================================*/
@@ -1132,12 +1164,12 @@
{
u_char *p;
if (tuple->TupleDataLen < 6)
- return CS_BAD_TUPLE;
+ return -EINVAL;
p = (u_char *)tuple->TupleData;
bar->attr = *p;
p += 2;
bar->size = get_unaligned_le32(p);
- return CS_SUCCESS;
+ return 0;
}
static int parse_config_cb(tuple_t *tuple, cistpl_config_t *config)
@@ -1146,12 +1178,12 @@
p = (u_char *)tuple->TupleData;
if ((*p != 3) || (tuple->TupleDataLen < 6))
- return CS_BAD_TUPLE;
+ return -EINVAL;
config->last_idx = *(++p);
p++;
config->base = get_unaligned_le32(p);
config->subtuples = tuple->TupleDataLen - 6;
- return CS_SUCCESS;
+ return 0;
}
static int parse_cftable_entry_cb(tuple_t *tuple,
@@ -1167,29 +1199,34 @@
entry->flags |= CISTPL_CFTABLE_DEFAULT;
/* Process optional features */
- if (++p == q) return CS_BAD_TUPLE;
+ if (++p == q)
+ return -EINVAL;
features = *p; p++;
/* Power options */
if ((features & 3) > 0) {
p = parse_power(p, q, &entry->vcc);
- if (p == NULL) return CS_BAD_TUPLE;
+ if (p == NULL)
+ return -EINVAL;
} else
entry->vcc.present = 0;
if ((features & 3) > 1) {
p = parse_power(p, q, &entry->vpp1);
- if (p == NULL) return CS_BAD_TUPLE;
+ if (p == NULL)
+ return -EINVAL;
} else
entry->vpp1.present = 0;
if ((features & 3) > 2) {
p = parse_power(p, q, &entry->vpp2);
- if (p == NULL) return CS_BAD_TUPLE;
+ if (p == NULL)
+ return -EINVAL;
} else
entry->vpp2.present = 0;
/* I/O window options */
if (features & 0x08) {
- if (p == q) return CS_BAD_TUPLE;
+ if (p == q)
+ return -EINVAL;
entry->io = *p; p++;
} else
entry->io = 0;
@@ -1197,32 +1234,37 @@
/* Interrupt options */
if (features & 0x10) {
p = parse_irq(p, q, &entry->irq);
- if (p == NULL) return CS_BAD_TUPLE;
+ if (p == NULL)
+ return -EINVAL;
} else
entry->irq.IRQInfo1 = 0;
if (features & 0x20) {
- if (p == q) return CS_BAD_TUPLE;
+ if (p == q)
+ return -EINVAL;
entry->mem = *p; p++;
} else
entry->mem = 0;
/* Misc features */
if (features & 0x80) {
- if (p == q) return CS_BAD_TUPLE;
+ if (p == q)
+ return -EINVAL;
entry->flags |= (*p << 8);
if (*p & 0x80) {
- if (++p == q) return CS_BAD_TUPLE;
+ if (++p == q)
+ return -EINVAL;
entry->flags |= (*p << 16);
}
while (*p & 0x80)
- if (++p == q) return CS_BAD_TUPLE;
+ if (++p == q)
+ return -EINVAL;
p++;
}
entry->subtuples = q-p;
- return CS_SUCCESS;
+ return 0;
}
#endif
@@ -1248,7 +1290,7 @@
p += 6;
}
geo->ngeo = n;
- return CS_SUCCESS;
+ return 0;
}
/*====================================================================*/
@@ -1258,7 +1300,7 @@
u_char *p, *q;
if (tuple->TupleDataLen < 10)
- return CS_BAD_TUPLE;
+ return -EINVAL;
p = tuple->TupleData;
q = p + tuple->TupleDataLen;
@@ -1282,15 +1324,18 @@
p = tuple->TupleData;
q = p + tuple->TupleDataLen;
- if (p == q) return CS_BAD_TUPLE;
+ if (p == q)
+ return -EINVAL;
org->data_org = *p;
- if (++p == q) return CS_BAD_TUPLE;
+ if (++p == q)
+ return -EINVAL;
for (i = 0; i < 30; i++) {
org->desc[i] = *p;
if (*p == '\0') break;
- if (++p == q) return CS_BAD_TUPLE;
+ if (++p == q)
+ return -EINVAL;
}
- return CS_SUCCESS;
+ return 0;
}
/*====================================================================*/
@@ -1300,7 +1345,7 @@
u_char *p;
if (tuple->TupleDataLen < 10)
- return CS_BAD_TUPLE;
+ return -EINVAL;
p = tuple->TupleData;
@@ -1309,17 +1354,17 @@
fmt->offset = get_unaligned_le32(p + 2);
fmt->length = get_unaligned_le32(p + 6);
- return CS_SUCCESS;
+ return 0;
}
/*====================================================================*/
-int pccard_parse_tuple(tuple_t *tuple, cisparse_t *parse)
+int pcmcia_parse_tuple(tuple_t *tuple, cisparse_t *parse)
{
- int ret = CS_SUCCESS;
+ int ret = 0;
if (tuple->TupleDataLen > tuple->TupleDataMax)
- return CS_BAD_TUPLE;
+ return -EINVAL;
switch (tuple->TupleCode) {
case CISTPL_DEVICE:
case CISTPL_DEVICE_A:
@@ -1387,15 +1432,17 @@
break;
case CISTPL_NO_LINK:
case CISTPL_LINKTARGET:
- ret = CS_SUCCESS;
+ ret = 0;
break;
default:
- ret = CS_UNSUPPORTED_FUNCTION;
+ ret = -EINVAL;
break;
}
+ if (ret)
+ __cs_dbg(0, "parse_tuple failed %d\n", ret);
return ret;
}
-EXPORT_SYMBOL(pccard_parse_tuple);
+EXPORT_SYMBOL(pcmcia_parse_tuple);
/*======================================================================
@@ -1410,18 +1457,22 @@
int ret;
buf = kmalloc(256, GFP_KERNEL);
- if (buf == NULL)
- return CS_OUT_OF_RESOURCE;
+ if (buf == NULL) {
+ dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n");
+ return -ENOMEM;
+ }
tuple.DesiredTuple = code;
tuple.Attributes = TUPLE_RETURN_COMMON;
ret = pccard_get_first_tuple(s, function, &tuple);
- if (ret != CS_SUCCESS) goto done;
+ if (ret != 0)
+ goto done;
tuple.TupleData = buf;
tuple.TupleOffset = 0;
tuple.TupleDataMax = 255;
ret = pccard_get_tuple_data(s, &tuple);
- if (ret != CS_SUCCESS) goto done;
- ret = pccard_parse_tuple(&tuple, parse);
+ if (ret != 0)
+ goto done;
+ ret = pcmcia_parse_tuple(&tuple, parse);
done:
kfree(buf);
return ret;
@@ -1446,37 +1497,40 @@
int ret, reserved, dev_ok = 0, ident_ok = 0;
if (!s)
- return CS_BAD_HANDLE;
+ return -EINVAL;
tuple = kmalloc(sizeof(*tuple), GFP_KERNEL);
- if (tuple == NULL)
- return CS_OUT_OF_RESOURCE;
+ if (tuple == NULL) {
+ dev_printk(KERN_WARNING, &s->dev, "no memory to validate CIS\n");
+ return -ENOMEM;
+ }
p = kmalloc(sizeof(*p), GFP_KERNEL);
if (p == NULL) {
- kfree(tuple);
- return CS_OUT_OF_RESOURCE;
+ kfree(tuple);
+ dev_printk(KERN_WARNING, &s->dev, "no memory to validate CIS\n");
+ return -ENOMEM;
}
count = reserved = 0;
tuple->DesiredTuple = RETURN_FIRST_TUPLE;
tuple->Attributes = TUPLE_RETURN_COMMON;
ret = pccard_get_first_tuple(s, function, tuple);
- if (ret != CS_SUCCESS)
+ if (ret != 0)
goto done;
/* First tuple should be DEVICE; we should really have either that
or a CFTABLE_ENTRY of some sort */
if ((tuple->TupleCode == CISTPL_DEVICE) ||
- (pccard_read_tuple(s, function, CISTPL_CFTABLE_ENTRY, p) == CS_SUCCESS) ||
- (pccard_read_tuple(s, function, CISTPL_CFTABLE_ENTRY_CB, p) == CS_SUCCESS))
+ (pccard_read_tuple(s, function, CISTPL_CFTABLE_ENTRY, p) == 0) ||
+ (pccard_read_tuple(s, function, CISTPL_CFTABLE_ENTRY_CB, p) == 0))
dev_ok++;
/* All cards should have a MANFID tuple, and/or a VERS_1 or VERS_2
tuple, for card identification. Certain old D-Link and Linksys
cards have only a broken VERS_2 tuple; hence the bogus test. */
- if ((pccard_read_tuple(s, function, CISTPL_MANFID, p) == CS_SUCCESS) ||
- (pccard_read_tuple(s, function, CISTPL_VERS_1, p) == CS_SUCCESS) ||
- (pccard_read_tuple(s, function, CISTPL_VERS_2, p) != CS_NO_MORE_ITEMS))
+ if ((pccard_read_tuple(s, function, CISTPL_MANFID, p) == 0) ||
+ (pccard_read_tuple(s, function, CISTPL_VERS_1, p) == 0) ||
+ (pccard_read_tuple(s, function, CISTPL_VERS_2, p) != -ENOSPC))
ident_ok++;
if (!dev_ok && !ident_ok)
@@ -1484,7 +1538,8 @@
for (count = 1; count < MAX_TUPLES; count++) {
ret = pccard_get_next_tuple(s, function, tuple);
- if (ret != CS_SUCCESS) break;
+ if (ret != 0)
+ break;
if (((tuple->TupleCode > 0x23) && (tuple->TupleCode < 0x40)) ||
((tuple->TupleCode > 0x47) && (tuple->TupleCode < 0x80)) ||
((tuple->TupleCode > 0x90) && (tuple->TupleCode < 0xff)))
@@ -1499,6 +1554,6 @@
*info = count;
kfree(tuple);
kfree(p);
- return CS_SUCCESS;
+ return 0;
}
EXPORT_SYMBOL(pccard_validate_cis);
diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c
index d120739..c68c5d3 100644
--- a/drivers/pcmcia/cs.c
+++ b/drivers/pcmcia/cs.c
@@ -61,7 +61,7 @@
/* Access speed for attribute memory windows */
INT_MODULE_PARM(cis_speed, 300); /* ns */
-#ifdef DEBUG
+#ifdef CONFIG_PCMCIA_DEBUG
static int pc_debug;
module_param(pc_debug, int, 0644);
@@ -247,7 +247,8 @@
wait_for_completion(&socket->thread_done);
if (!socket->thread) {
- printk(KERN_WARNING "PCMCIA: warning: socket thread for socket %p did not start\n", socket);
+ dev_printk(KERN_WARNING, &socket->dev,
+ "PCMCIA: warning: socket thread did not start\n");
return -EIO;
}
@@ -366,16 +367,16 @@
skt->ops->get_status(skt, &status);
if (!(status & SS_DETECT))
- return CS_NO_CARD;
+ return -ENODEV;
if (status & SS_READY)
- return CS_SUCCESS;
+ return 0;
msleep(unreset_check * 10);
}
cs_err(skt, "time out after reset.\n");
- return CS_GENERAL_FAILURE;
+ return -ETIMEDOUT;
}
/*
@@ -412,7 +413,8 @@
s->ops->get_status(s, &status);
if (status & SS_POWERON) {
- printk(KERN_ERR "PCMCIA: socket %p: *** DANGER *** unable to remove socket power\n", s);
+ dev_printk(KERN_ERR, &s->dev,
+ "*** DANGER *** unable to remove socket power\n");
}
cs_socket_put(s);
@@ -426,14 +428,14 @@
skt->ops->get_status(skt, &status);
if (!(status & SS_DETECT))
- return CS_NO_CARD;
+ return -ENODEV;
msleep(initial_delay * 10);
for (i = 0; i < 100; i++) {
skt->ops->get_status(skt, &status);
if (!(status & SS_DETECT))
- return CS_NO_CARD;
+ return -ENODEV;
if (!(status & SS_PENDING))
break;
@@ -443,13 +445,13 @@
if (status & SS_PENDING) {
cs_err(skt, "voltage interrogation timed out.\n");
- return CS_GENERAL_FAILURE;
+ return -ETIMEDOUT;
}
if (status & SS_CARDBUS) {
if (!(skt->features & SS_CAP_CARDBUS)) {
cs_err(skt, "cardbus cards are not supported.\n");
- return CS_BAD_TYPE;
+ return -EINVAL;
}
skt->state |= SOCKET_CARDBUS;
}
@@ -463,7 +465,7 @@
skt->socket.Vcc = skt->socket.Vpp = 50;
else {
cs_err(skt, "unsupported voltage key.\n");
- return CS_BAD_TYPE;
+ return -EIO;
}
if (skt->power_hook)
@@ -480,7 +482,7 @@
skt->ops->get_status(skt, &status);
if (!(status & SS_POWERON)) {
cs_err(skt, "unable to apply power.\n");
- return CS_BAD_TYPE;
+ return -EIO;
}
status = socket_reset(skt);
@@ -502,15 +504,16 @@
cs_dbg(skt, 4, "insert\n");
if (!cs_socket_get(skt))
- return CS_NO_CARD;
+ return -ENODEV;
ret = socket_setup(skt, setup_delay);
- if (ret == CS_SUCCESS) {
+ if (ret == 0) {
skt->state |= SOCKET_PRESENT;
- printk(KERN_NOTICE "pccard: %s card inserted into slot %d\n",
- (skt->state & SOCKET_CARDBUS) ? "CardBus" : "PCMCIA",
- skt->sock);
+ dev_printk(KERN_NOTICE, &skt->dev,
+ "pccard: %s card inserted into slot %d\n",
+ (skt->state & SOCKET_CARDBUS) ? "CardBus" : "PCMCIA",
+ skt->sock);
#ifdef CONFIG_CARDBUS
if (skt->state & SOCKET_CARDBUS) {
@@ -531,7 +534,7 @@
static int socket_suspend(struct pcmcia_socket *skt)
{
if (skt->state & SOCKET_SUSPEND)
- return CS_IN_USE;
+ return -EBUSY;
send_event(skt, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW);
skt->socket = dead_socket;
@@ -540,7 +543,7 @@
skt->ops->suspend(skt);
skt->state |= SOCKET_SUSPEND;
- return CS_SUCCESS;
+ return 0;
}
/*
@@ -553,7 +556,7 @@
int ret;
if (!(skt->state & SOCKET_SUSPEND))
- return CS_IN_USE;
+ return -EBUSY;
skt->socket = dead_socket;
skt->ops->init(skt);
@@ -565,7 +568,7 @@
}
ret = socket_setup(skt, resume_delay);
- if (ret == CS_SUCCESS) {
+ if (ret == 0) {
/*
* FIXME: need a better check here for cardbus cards.
*/
@@ -590,12 +593,13 @@
skt->state &= ~SOCKET_SUSPEND;
- return CS_SUCCESS;
+ return 0;
}
static void socket_remove(struct pcmcia_socket *skt)
{
- printk(KERN_NOTICE "pccard: card ejected from slot %d\n", skt->sock);
+ dev_printk(KERN_NOTICE, &skt->dev,
+ "pccard: card ejected from slot %d\n", skt->sock);
socket_shutdown(skt);
}
@@ -641,8 +645,8 @@
/* register with the device core */
ret = device_register(&skt->dev);
if (ret) {
- printk(KERN_WARNING "PCMCIA: unable to register socket 0x%p\n",
- skt);
+ dev_printk(KERN_WARNING, &skt->dev,
+ "PCMCIA: unable to register socket\n");
skt->thread = NULL;
complete(&skt->thread_done);
return 0;
@@ -748,7 +752,7 @@
* CIS register.
*/
-int pccard_reset_card(struct pcmcia_socket *skt)
+int pcmcia_reset_card(struct pcmcia_socket *skt)
{
int ret;
@@ -757,15 +761,15 @@
mutex_lock(&skt->skt_mutex);
do {
if (!(skt->state & SOCKET_PRESENT)) {
- ret = CS_NO_CARD;
+ ret = -ENODEV;
break;
}
if (skt->state & SOCKET_SUSPEND) {
- ret = CS_IN_USE;
+ ret = -EBUSY;
break;
}
if (skt->state & SOCKET_CARDBUS) {
- ret = CS_UNSUPPORTED_FUNCTION;
+ ret = -EPERM;
break;
}
@@ -774,20 +778,20 @@
send_event(skt, CS_EVENT_RESET_PHYSICAL, CS_EVENT_PRI_LOW);
if (skt->callback)
skt->callback->suspend(skt);
- if (socket_reset(skt) == CS_SUCCESS) {
+ if (socket_reset(skt) == 0) {
send_event(skt, CS_EVENT_CARD_RESET, CS_EVENT_PRI_LOW);
if (skt->callback)
skt->callback->resume(skt);
}
}
- ret = CS_SUCCESS;
+ ret = 0;
} while (0);
mutex_unlock(&skt->skt_mutex);
return ret;
} /* reset_card */
-EXPORT_SYMBOL(pccard_reset_card);
+EXPORT_SYMBOL(pcmcia_reset_card);
/* These shut down or wake up a socket. They are sort of user
@@ -802,11 +806,11 @@
mutex_lock(&skt->skt_mutex);
do {
if (!(skt->state & SOCKET_PRESENT)) {
- ret = CS_NO_CARD;
+ ret = -ENODEV;
break;
}
if (skt->state & SOCKET_CARDBUS) {
- ret = CS_UNSUPPORTED_FUNCTION;
+ ret = -EPERM;
break;
}
if (skt->callback) {
@@ -832,11 +836,11 @@
mutex_lock(&skt->skt_mutex);
do {
if (!(skt->state & SOCKET_PRESENT)) {
- ret = CS_NO_CARD;
+ ret = -ENODEV;
break;
}
if (skt->state & SOCKET_CARDBUS) {
- ret = CS_UNSUPPORTED_FUNCTION;
+ ret = -EPERM;
break;
}
ret = socket_resume(skt);
@@ -892,7 +896,7 @@
ret = -EBUSY;
break;
}
- if (socket_insert(skt) == CS_NO_CARD) {
+ if (socket_insert(skt) == -ENODEV) {
ret = -ENODEV;
break;
}
diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h
index 63dc1a2..79615e6 100644
--- a/drivers/pcmcia/cs_internal.h
+++ b/drivers/pcmcia/cs_internal.h
@@ -1,5 +1,5 @@
/*
- * cs_internal.h
+ * cs_internal.h -- definitions internal to the PCMCIA core modules
*
* 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
@@ -10,6 +10,12 @@
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
*
* (C) 1999 David A. Hinds
+ * (C) 2003 - 2008 Dominik Brodowski
+ *
+ *
+ * This file contains definitions _only_ needed by the PCMCIA core modules.
+ * It must not be included by PCMCIA socket drivers or by PCMCIA device
+ * drivers.
*/
#ifndef _LINUX_CS_INTERNAL_H
@@ -18,29 +24,24 @@
#include <linux/kref.h>
/* Flags in client state */
-#define CLIENT_CONFIG_LOCKED 0x0001
-#define CLIENT_IRQ_REQ 0x0002
-#define CLIENT_IO_REQ 0x0004
-#define CLIENT_UNBOUND 0x0008
-#define CLIENT_STALE 0x0010
#define CLIENT_WIN_REQ(i) (0x1<<(i))
-#define CLIENT_CARDBUS 0x8000
/* Each card function gets one of these guys */
typedef struct config_t {
struct kref ref;
- u_int state;
- u_int Attributes;
- u_int IntType;
- u_int ConfigBase;
- u_char Status, Pin, Copy, Option, ExtStatus;
- u_int CardValues;
- io_req_t io;
- struct {
- u_int Attributes;
- } irq;
+ unsigned int state;
+ unsigned int Attributes;
+ unsigned int IntType;
+ unsigned int ConfigBase;
+ unsigned char Status, Pin, Copy, Option, ExtStatus;
+ unsigned int CardValues;
+ io_req_t io;
+ struct {
+ u_int Attributes;
+ } irq;
} config_t;
+
struct cis_cache_entry {
struct list_head node;
unsigned int addr;
@@ -49,6 +50,30 @@
unsigned char cache[0];
};
+struct pccard_resource_ops {
+ int (*validate_mem) (struct pcmcia_socket *s);
+ int (*adjust_io_region) (struct resource *res,
+ unsigned long r_start,
+ unsigned long r_end,
+ struct pcmcia_socket *s);
+ struct resource* (*find_io) (unsigned long base, int num,
+ unsigned long align,
+ struct pcmcia_socket *s);
+ struct resource* (*find_mem) (unsigned long base, unsigned long num,
+ unsigned long align, int low,
+ struct pcmcia_socket *s);
+ int (*add_io) (struct pcmcia_socket *s,
+ unsigned int action,
+ unsigned long r_start,
+ unsigned long r_end);
+ int (*add_mem) (struct pcmcia_socket *s,
+ unsigned int action,
+ unsigned long r_start,
+ unsigned long r_end);
+ int (*init) (struct pcmcia_socket *s);
+ void (*exit) (struct pcmcia_socket *s);
+};
+
/* Flags in config state */
#define CONFIG_LOCKED 0x01
#define CONFIG_IRQ_REQ 0x02
@@ -59,7 +84,6 @@
#define SOCKET_INUSE 0x0010
#define SOCKET_SUSPEND 0x0080
#define SOCKET_WIN_REQ(i) (0x0100<<(i))
-#define SOCKET_REGION_INFO 0x4000
#define SOCKET_CARDBUS 0x8000
#define SOCKET_CARDBUS_CONFIG 0x10000
@@ -83,69 +107,153 @@
}
}
-/* In cardbus.c */
-int cb_alloc(struct pcmcia_socket *s);
-void cb_free(struct pcmcia_socket *s);
-int read_cb_mem(struct pcmcia_socket *s, int space, u_int addr, u_int len, void *ptr);
+#ifdef CONFIG_PCMCIA_DEBUG
+extern int cs_debug_level(int);
-/* In cistpl.c */
-int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr,
- u_int addr, u_int len, void *ptr);
-void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr,
- u_int addr, u_int len, void *ptr);
-void release_cis_mem(struct pcmcia_socket *s);
-void destroy_cis_cache(struct pcmcia_socket *s);
+#define cs_dbg(skt, lvl, fmt, arg...) do { \
+ if (cs_debug_level(lvl)) \
+ dev_printk(KERN_DEBUG, &skt->dev, \
+ "cs: " fmt, ## arg); \
+} while (0)
+#define __cs_dbg(lvl, fmt, arg...) do { \
+ if (cs_debug_level(lvl)) \
+ printk(KERN_DEBUG \
+ "cs: " fmt, ## arg); \
+} while (0)
+
+#else
+#define cs_dbg(skt, lvl, fmt, arg...) do { } while (0)
+#define __cs_dbg(lvl, fmt, arg...) do { } while (0)
+#endif
+
+#define cs_err(skt, fmt, arg...) \
+ dev_printk(KERN_ERR, &skt->dev, "cs: " fmt, ## arg)
+
+
+/*
+ * Stuff internal to module "pcmcia_core":
+ */
+
+/* cistpl.c */
int verify_cis_cache(struct pcmcia_socket *s);
-int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function, cisdata_t code, void *parse);
-/* In rsrc_mgr */
-int pcmcia_validate_mem(struct pcmcia_socket *s);
-struct resource *pcmcia_find_io_region(unsigned long base, int num, unsigned long align,
- struct pcmcia_socket *s);
-int pcmcia_adjust_io_region(struct resource *res, unsigned long r_start,
- unsigned long r_end, struct pcmcia_socket *s);
-struct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align,
- int low, struct pcmcia_socket *s);
+/* rsrc_mgr.c */
void release_resource_db(struct pcmcia_socket *s);
-/* In socket_sysfs.c */
+/* socket_sysfs.c */
extern int pccard_sysfs_add_socket(struct device *dev);
extern void pccard_sysfs_remove_socket(struct device *dev);
-/* In cs.c */
-extern struct rw_semaphore pcmcia_socket_list_rwsem;
-extern struct list_head pcmcia_socket_list;
-int pcmcia_get_window(struct pcmcia_socket *s, window_handle_t *handle, int idx, win_req_t *req);
-int pccard_get_configuration_info(struct pcmcia_socket *s, struct pcmcia_device *p_dev, config_info_t *config);
-int pccard_reset_card(struct pcmcia_socket *skt);
+/* cardbus.c */
+int cb_alloc(struct pcmcia_socket *s);
+void cb_free(struct pcmcia_socket *s);
+int read_cb_mem(struct pcmcia_socket *s, int space, u_int addr, u_int len,
+ void *ptr);
+
+/*
+ * Stuff exported by module "pcmcia_core" to module "pcmcia"
+ */
+
struct pcmcia_callback{
struct module *owner;
- int (*event) (struct pcmcia_socket *s, event_t event, int priority);
+ int (*event) (struct pcmcia_socket *s,
+ event_t event, int priority);
void (*requery) (struct pcmcia_socket *s, int new_cis);
int (*suspend) (struct pcmcia_socket *s);
int (*resume) (struct pcmcia_socket *s);
};
+/* cs.c */
+extern struct rw_semaphore pcmcia_socket_list_rwsem;
+extern struct list_head pcmcia_socket_list;
+extern struct class pcmcia_socket_class;
+
+int pcmcia_get_window(struct pcmcia_socket *s,
+ window_handle_t *handle,
+ int idx,
+ win_req_t *req);
int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c);
+struct pcmcia_socket *pcmcia_get_socket_by_nr(unsigned int nr);
-#define cs_socket_name(skt) ((skt)->dev.bus_id)
+int pcmcia_suspend_card(struct pcmcia_socket *skt);
+int pcmcia_resume_card(struct pcmcia_socket *skt);
-#ifdef DEBUG
-extern int cs_debug_level(int);
+int pcmcia_eject_card(struct pcmcia_socket *skt);
+int pcmcia_insert_card(struct pcmcia_socket *skt);
-#define cs_dbg(skt, lvl, fmt, arg...) do { \
- if (cs_debug_level(lvl)) \
- printk(KERN_DEBUG "cs: %s: " fmt, \
- cs_socket_name(skt) , ## arg); \
-} while (0)
+struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt);
+void pcmcia_put_socket(struct pcmcia_socket *skt);
-#else
-#define cs_dbg(skt, lvl, fmt, arg...) do { } while (0)
-#endif
+/* cistpl.c */
+int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr,
+ u_int addr, u_int len, void *ptr);
+void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr,
+ u_int addr, u_int len, void *ptr);
+void release_cis_mem(struct pcmcia_socket *s);
+void destroy_cis_cache(struct pcmcia_socket *s);
+int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function,
+ cisdata_t code, void *parse);
+int pcmcia_replace_cis(struct pcmcia_socket *s,
+ const u8 *data, const size_t len);
+int pccard_validate_cis(struct pcmcia_socket *s, unsigned int function,
+ unsigned int *count);
-#define cs_err(skt, fmt, arg...) \
- printk(KERN_ERR "cs: %s: " fmt, (skt)->dev.bus_id , ## arg)
+/* rsrc_mgr.c */
+int pcmcia_validate_mem(struct pcmcia_socket *s);
+struct resource *pcmcia_find_io_region(unsigned long base,
+ int num,
+ unsigned long align,
+ struct pcmcia_socket *s);
+int pcmcia_adjust_io_region(struct resource *res,
+ unsigned long r_start,
+ unsigned long r_end,
+ struct pcmcia_socket *s);
+struct resource *pcmcia_find_mem_region(u_long base,
+ u_long num,
+ u_long align,
+ int low,
+ struct pcmcia_socket *s);
+
+/*
+ * Stuff internal to module "pcmcia".
+ */
+/* ds.c */
+extern struct bus_type pcmcia_bus_type;
+
+/* pcmcia_resource.c */
+extern int pcmcia_release_configuration(struct pcmcia_device *p_dev);
+
+#ifdef CONFIG_PCMCIA_IOCTL
+/* ds.c */
+extern spinlock_t pcmcia_dev_list_lock;
+
+extern struct pcmcia_device *pcmcia_get_dev(struct pcmcia_device *p_dev);
+extern void pcmcia_put_dev(struct pcmcia_device *p_dev);
+
+struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s,
+ unsigned int function);
+
+/* pcmcia_ioctl.c */
+extern void __init pcmcia_setup_ioctl(void);
+extern void __exit pcmcia_cleanup_ioctl(void);
+extern void handle_event(struct pcmcia_socket *s, event_t event);
+extern int handle_request(struct pcmcia_socket *s, event_t event);
+
+#else /* CONFIG_PCMCIA_IOCTL */
+
+static inline void __init pcmcia_setup_ioctl(void) { return; }
+static inline void __exit pcmcia_cleanup_ioctl(void) { return; }
+static inline void handle_event(struct pcmcia_socket *s, event_t event)
+{
+ return;
+}
+static inline int handle_request(struct pcmcia_socket *s, event_t event)
+{
+ return 0;
+}
+
+#endif /* CONFIG_PCMCIA_IOCTL */
#endif /* _LINUX_CS_INTERNAL_H */
diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c
index 34c83d3..7956602 100644
--- a/drivers/pcmcia/ds.c
+++ b/drivers/pcmcia/ds.c
@@ -32,7 +32,6 @@
#include <pcmcia/ss.h>
#include "cs_internal.h"
-#include "ds_internal.h"
/*====================================================================*/
@@ -42,17 +41,22 @@
MODULE_DESCRIPTION("PCMCIA Driver Services");
MODULE_LICENSE("GPL");
-#ifdef DEBUG
+#ifdef CONFIG_PCMCIA_DEBUG
int ds_pc_debug;
module_param_named(pc_debug, ds_pc_debug, int, 0644);
#define ds_dbg(lvl, fmt, arg...) do { \
- if (ds_pc_debug > (lvl)) \
+ if (ds_pc_debug > (lvl)) \
printk(KERN_DEBUG "ds: " fmt , ## arg); \
} while (0)
+#define ds_dev_dbg(lvl, dev, fmt, arg...) do { \
+ if (ds_pc_debug > (lvl)) \
+ dev_printk(KERN_DEBUG, dev, "ds: " fmt , ## arg); \
+} while (0)
#else
#define ds_dbg(lvl, fmt, arg...) do { } while (0)
+#define ds_dev_dbg(lvl, dev, fmt, arg...) do { } while (0)
#endif
spinlock_t pcmcia_dev_list_lock;
@@ -64,42 +68,19 @@
/* String tables for error messages */
typedef struct lookup_t {
- int key;
- char *msg;
+ const int key;
+ const char *msg;
} lookup_t;
static const lookup_t error_table[] = {
- { CS_SUCCESS, "Operation succeeded" },
- { CS_BAD_ADAPTER, "Bad adapter" },
- { CS_BAD_ATTRIBUTE, "Bad attribute", },
- { CS_BAD_BASE, "Bad base address" },
- { CS_BAD_EDC, "Bad EDC" },
- { CS_BAD_IRQ, "Bad IRQ" },
- { CS_BAD_OFFSET, "Bad offset" },
- { CS_BAD_PAGE, "Bad page number" },
- { CS_READ_FAILURE, "Read failure" },
- { CS_BAD_SIZE, "Bad size" },
- { CS_BAD_SOCKET, "Bad socket" },
- { CS_BAD_TYPE, "Bad type" },
- { CS_BAD_VCC, "Bad Vcc" },
- { CS_BAD_VPP, "Bad Vpp" },
- { CS_BAD_WINDOW, "Bad window" },
- { CS_WRITE_FAILURE, "Write failure" },
- { CS_NO_CARD, "No card present" },
- { CS_UNSUPPORTED_FUNCTION, "Usupported function" },
- { CS_UNSUPPORTED_MODE, "Unsupported mode" },
- { CS_BAD_SPEED, "Bad speed" },
- { CS_BUSY, "Resource busy" },
- { CS_GENERAL_FAILURE, "General failure" },
- { CS_WRITE_PROTECTED, "Write protected" },
- { CS_BAD_ARG_LENGTH, "Bad argument length" },
- { CS_BAD_ARGS, "Bad arguments" },
- { CS_CONFIGURATION_LOCKED, "Configuration locked" },
- { CS_IN_USE, "Resource in use" },
- { CS_NO_MORE_ITEMS, "No more items" },
- { CS_OUT_OF_RESOURCE, "Out of resource" },
- { CS_BAD_HANDLE, "Bad handle" },
- { CS_BAD_TUPLE, "Bad CIS tuple" }
+ { 0, "Operation succeeded" },
+ { -EIO, "Input/Output error" },
+ { -ENODEV, "No card present" },
+ { -EINVAL, "Bad parameter" },
+ { -EACCES, "Configuration locked" },
+ { -EBUSY, "Resource in use" },
+ { -ENOSPC, "No more items" },
+ { -ENOMEM, "Out of resource" },
};
@@ -155,46 +136,32 @@
{ ReplaceCIS, "ReplaceCIS" }
};
-
-static int pcmcia_report_error(struct pcmcia_device *p_dev, error_info_t *err)
+const char *pcmcia_error_func(int func)
{
int i;
- char *serv;
-
- if (!p_dev)
- printk(KERN_NOTICE);
- else
- printk(KERN_NOTICE "%s: ", p_dev->dev.bus_id);
for (i = 0; i < ARRAY_SIZE(service_table); i++)
- if (service_table[i].key == err->func)
- break;
- if (i < ARRAY_SIZE(service_table))
- serv = service_table[i].msg;
- else
- serv = "Unknown service number";
+ if (service_table[i].key == func)
+ return service_table[i].msg;
+
+ return "Unknown service number";
+}
+EXPORT_SYMBOL(pcmcia_error_func);
+
+const char *pcmcia_error_ret(int ret)
+{
+ int i;
for (i = 0; i < ARRAY_SIZE(error_table); i++)
- if (error_table[i].key == err->retcode)
- break;
- if (i < ARRAY_SIZE(error_table))
- printk("%s: %s\n", serv, error_table[i].msg);
- else
- printk("%s: Unknown error code %#x\n", serv, err->retcode);
+ if (error_table[i].key == ret)
+ return error_table[i].msg;
- return CS_SUCCESS;
-} /* report_error */
-
-/* end of code which was in cs.c before */
+ return "unknown";
+}
+EXPORT_SYMBOL(pcmcia_error_ret);
/*======================================================================*/
-void cs_error(struct pcmcia_device *p_dev, int func, int ret)
-{
- error_info_t err = { func, ret };
- pcmcia_report_error(p_dev, &err);
-}
-EXPORT_SYMBOL(cs_error);
static void pcmcia_check_driver(struct pcmcia_driver *p_drv)
@@ -391,7 +358,7 @@
static void pcmcia_release_dev(struct device *dev)
{
struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
- ds_dbg(1, "releasing device %s\n", p_dev->dev.bus_id);
+ ds_dev_dbg(1, dev, "releasing device\n");
pcmcia_put_socket(p_dev->socket);
kfree(p_dev->devname);
kref_put(&p_dev->function_config->ref, pcmcia_release_function);
@@ -401,7 +368,7 @@
static void pcmcia_add_device_later(struct pcmcia_socket *s, int mfc)
{
if (!s->pcmcia_state.device_add_pending) {
- ds_dbg(1, "scheduling to add %s secondary"
+ ds_dev_dbg(1, &s->dev, "scheduling to add %s secondary"
" device to %d\n", mfc ? "mfc" : "pfc", s->sock);
s->pcmcia_state.device_add_pending = 1;
s->pcmcia_state.mfc_pfc = mfc;
@@ -439,8 +406,7 @@
*/
did = p_dev->dev.driver_data;
- ds_dbg(1, "trying to bind %s to %s\n", p_dev->dev.bus_id,
- p_drv->drv.name);
+ ds_dev_dbg(1, dev, "trying to bind to %s\n", p_drv->drv.name);
if ((!p_drv->probe) || (!p_dev->function_config) ||
(!try_module_get(p_drv->owner))) {
@@ -455,15 +421,16 @@
p_dev->conf.ConfigBase = cis_config.base;
p_dev->conf.Present = cis_config.rmask[0];
} else {
- printk(KERN_INFO "pcmcia: could not parse base and rmask0 of CIS\n");
+ dev_printk(KERN_INFO, dev,
+ "pcmcia: could not parse base and rmask0 of CIS\n");
p_dev->conf.ConfigBase = 0;
p_dev->conf.Present = 0;
}
ret = p_drv->probe(p_dev);
if (ret) {
- ds_dbg(1, "binding %s to %s failed with %d\n",
- p_dev->dev.bus_id, p_drv->drv.name, ret);
+ ds_dev_dbg(1, dev, "binding to %s failed with %d\n",
+ p_drv->drv.name, ret);
goto put_module;
}
@@ -490,8 +457,9 @@
struct pcmcia_device *tmp;
unsigned long flags;
- ds_dbg(2, "pcmcia_card_remove(%d) %s\n", s->sock,
- leftover ? leftover->devname : "");
+ ds_dev_dbg(2, leftover ? &leftover->dev : &s->dev,
+ "pcmcia_card_remove(%d) %s\n", s->sock,
+ leftover ? leftover->devname : "");
if (!leftover)
s->device_count = 0;
@@ -508,7 +476,7 @@
p_dev->_removed=1;
spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
- ds_dbg(2, "unregistering device %s\n", p_dev->dev.bus_id);
+ ds_dev_dbg(2, &p_dev->dev, "unregistering device\n");
device_unregister(&p_dev->dev);
}
@@ -525,7 +493,7 @@
p_dev = to_pcmcia_dev(dev);
p_drv = to_pcmcia_drv(dev->driver);
- ds_dbg(1, "removing device %s\n", p_dev->dev.bus_id);
+ ds_dev_dbg(1, dev, "removing device\n");
/* If we're removing the primary module driving a
* pseudo multi-function card, we need to unbind
@@ -548,13 +516,15 @@
/* check for proper unloading */
if (p_dev->_irq || p_dev->_io || p_dev->_locked)
- printk(KERN_INFO "pcmcia: driver %s did not release config properly\n",
- p_drv->drv.name);
+ dev_printk(KERN_INFO, dev,
+ "pcmcia: driver %s did not release config properly\n",
+ p_drv->drv.name);
for (i = 0; i < MAX_WIN; i++)
if (p_dev->_win & CLIENT_WIN_REQ(i))
- printk(KERN_INFO "pcmcia: driver %s did not release windows properly\n",
- p_drv->drv.name);
+ dev_printk(KERN_INFO, dev,
+ "pcmcia: driver %s did not release window properly\n",
+ p_drv->drv.name);
/* references from pcmcia_probe_device */
pcmcia_put_dev(p_dev);
@@ -603,8 +573,9 @@
}
if (!pccard_read_tuple(p_dev->socket, p_dev->func,
CISTPL_DEVICE_GEO, devgeo)) {
- ds_dbg(0, "mem device geometry probably means "
- "FUNCID_MEMORY\n");
+ ds_dev_dbg(0, &p_dev->dev,
+ "mem device geometry probably means "
+ "FUNCID_MEMORY\n");
p_dev->func_id = CISTPL_FUNCID_MEMORY;
p_dev->has_func_id = 1;
}
@@ -685,7 +656,7 @@
if (!p_dev->devname)
goto err_free;
sprintf (p_dev->devname, "pcmcia%s", p_dev->dev.bus_id);
- ds_dbg(3, "devname is %s\n", p_dev->devname);
+ ds_dev_dbg(3, &p_dev->dev, "devname is %s\n", p_dev->devname);
spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
@@ -706,7 +677,7 @@
spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
if (!p_dev->function_config) {
- ds_dbg(3, "creating config_t for %s\n", p_dev->dev.bus_id);
+ ds_dev_dbg(3, &p_dev->dev, "creating config_t\n");
p_dev->function_config = kzalloc(sizeof(struct config_t),
GFP_KERNEL);
if (!p_dev->function_config)
@@ -714,8 +685,9 @@
kref_init(&p_dev->function_config->ref);
}
- printk(KERN_NOTICE "pcmcia: registering new device %s\n",
- p_dev->devname);
+ dev_printk(KERN_NOTICE, &p_dev->dev,
+ "pcmcia: registering new device %s\n",
+ p_dev->devname);
pcmcia_device_query(p_dev);
@@ -750,19 +722,20 @@
int ret = 0;
if (!(s->resource_setup_done)) {
- ds_dbg(3, "no resources available, delaying card_add\n");
+ ds_dev_dbg(3, &s->dev,
+ "no resources available, delaying card_add\n");
return -EAGAIN; /* try again, but later... */
}
if (pcmcia_validate_mem(s)) {
- ds_dbg(3, "validating mem resources failed, "
+ ds_dev_dbg(3, &s->dev, "validating mem resources failed, "
"delaying card_add\n");
return -EAGAIN; /* try again, but later... */
}
ret = pccard_validate_cis(s, BIND_FN_ALL, &no_chains);
if (ret || !no_chains) {
- ds_dbg(0, "invalid CIS or invalid resources\n");
+ ds_dev_dbg(0, &s->dev, "invalid CIS or invalid resources\n");
return -ENODEV;
}
@@ -783,7 +756,7 @@
{
struct pcmcia_socket *s =
container_of(work, struct pcmcia_socket, device_add);
- ds_dbg(1, "adding additional device to %d\n", s->sock);
+ ds_dev_dbg(1, &s->dev, "adding additional device to %d\n", s->sock);
pcmcia_device_add(s, s->pcmcia_state.mfc_pfc);
s->pcmcia_state.device_add_pending = 0;
s->pcmcia_state.mfc_pfc = 0;
@@ -793,8 +766,7 @@
{
struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
if (!p_dev->dev.driver) {
- ds_dbg(1, "update device information for %s\n",
- p_dev->dev.bus_id);
+ ds_dev_dbg(1, dev, "update device information\n");
pcmcia_device_query(p_dev);
}
@@ -808,7 +780,7 @@
unsigned long flags;
/* must be called with skt_mutex held */
- ds_dbg(0, "re-scanning socket %d\n", skt->sock);
+ ds_dev_dbg(0, &skt->dev, "re-scanning socket %d\n", skt->sock);
spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
if (list_empty(&skt->devices_list))
@@ -859,17 +831,17 @@
int ret = -ENOMEM;
int no_funcs;
int old_funcs;
- cisdump_t *cis;
cistpl_longlink_mfc_t mfc;
if (!filename)
return -EINVAL;
- ds_dbg(1, "trying to load CIS file %s\n", filename);
+ ds_dev_dbg(1, &dev->dev, "trying to load CIS file %s\n", filename);
if (strlen(filename) > (FIRMWARE_NAME_MAX - 1)) {
- printk(KERN_WARNING "pcmcia: CIS filename is too long [%s]\n",
- filename);
+ dev_printk(KERN_WARNING, &dev->dev,
+ "pcmcia: CIS filename is too long [%s]\n",
+ filename);
return -EINVAL;
}
@@ -878,23 +850,16 @@
if (request_firmware(&fw, path, &dev->dev) == 0) {
if (fw->size >= CISTPL_MAX_CIS_SIZE) {
ret = -EINVAL;
- printk(KERN_ERR "pcmcia: CIS override is too big\n");
+ dev_printk(KERN_ERR, &dev->dev,
+ "pcmcia: CIS override is too big\n");
goto release;
}
- cis = kzalloc(sizeof(cisdump_t), GFP_KERNEL);
- if (!cis) {
- ret = -ENOMEM;
- goto release;
- }
-
- cis->Length = fw->size + 1;
- memcpy(cis->Data, fw->data, fw->size);
-
- if (!pcmcia_replace_cis(s, cis))
+ if (!pcmcia_replace_cis(s, fw->data, fw->size))
ret = 0;
else {
- printk(KERN_ERR "pcmcia: CIS override failed\n");
+ dev_printk(KERN_ERR, &dev->dev,
+ "pcmcia: CIS override failed\n");
goto release;
}
@@ -998,14 +963,14 @@
* after it has re-checked that there is no possible module
* with a prod_id/manf_id/card_id match.
*/
- ds_dbg(0, "skipping FUNC_ID match for %s until userspace "
- "interaction\n", dev->dev.bus_id);
+ ds_dev_dbg(0, &dev->dev,
+ "skipping FUNC_ID match until userspace interaction\n");
if (!dev->allow_func_id_match)
return 0;
}
if (did->match_flags & PCMCIA_DEV_ID_MATCH_FAKE_CIS) {
- ds_dbg(0, "device %s needs a fake CIS\n", dev->dev.bus_id);
+ ds_dev_dbg(0, &dev->dev, "device needs a fake CIS\n");
if (!dev->socket->fake_cis)
pcmcia_load_firmware(dev, did->cisfile);
@@ -1037,11 +1002,9 @@
/* match dynamic devices first */
spin_lock(&p_drv->dynids.lock);
list_for_each_entry(dynid, &p_drv->dynids.list, node) {
- ds_dbg(3, "trying to match %s to %s\n", dev->bus_id,
- drv->name);
+ ds_dev_dbg(3, dev, "trying to match to %s\n", drv->name);
if (pcmcia_devmatch(p_dev, &dynid->id)) {
- ds_dbg(0, "matched %s to %s\n", dev->bus_id,
- drv->name);
+ ds_dev_dbg(0, dev, "matched to %s\n", drv->name);
spin_unlock(&p_drv->dynids.lock);
return 1;
}
@@ -1051,18 +1014,15 @@
#ifdef CONFIG_PCMCIA_IOCTL
/* matching by cardmgr */
if (p_dev->cardmgr == p_drv) {
- ds_dbg(0, "cardmgr matched %s to %s\n", dev->bus_id,
- drv->name);
+ ds_dev_dbg(0, dev, "cardmgr matched to %s\n", drv->name);
return 1;
}
#endif
while (did && did->match_flags) {
- ds_dbg(3, "trying to match %s to %s\n", dev->bus_id,
- drv->name);
+ ds_dev_dbg(3, dev, "trying to match to %s\n", drv->name);
if (pcmcia_devmatch(p_dev, did)) {
- ds_dbg(0, "matched %s to %s\n", dev->bus_id,
- drv->name);
+ ds_dev_dbg(0, dev, "matched to %s\n", drv->name);
return 1;
}
did++;
@@ -1268,7 +1228,7 @@
if (p_dev->suspended)
return 0;
- ds_dbg(2, "suspending %s\n", dev->bus_id);
+ ds_dev_dbg(2, dev, "suspending\n");
if (dev->driver)
p_drv = to_pcmcia_drv(dev->driver);
@@ -1279,15 +1239,16 @@
if (p_drv->suspend) {
ret = p_drv->suspend(p_dev);
if (ret) {
- printk(KERN_ERR "pcmcia: device %s (driver %s) did "
- "not want to go to sleep (%d)\n",
- p_dev->devname, p_drv->drv.name, ret);
+ dev_printk(KERN_ERR, dev,
+ "pcmcia: device %s (driver %s) did "
+ "not want to go to sleep (%d)\n",
+ p_dev->devname, p_drv->drv.name, ret);
goto out;
}
}
if (p_dev->device_no == p_dev->func) {
- ds_dbg(2, "releasing configuration for %s\n", dev->bus_id);
+ ds_dev_dbg(2, dev, "releasing configuration\n");
pcmcia_release_configuration(p_dev);
}
@@ -1307,7 +1268,7 @@
if (!p_dev->suspended)
return 0;
- ds_dbg(2, "resuming %s\n", dev->bus_id);
+ ds_dev_dbg(2, dev, "resuming\n");
if (dev->driver)
p_drv = to_pcmcia_drv(dev->driver);
@@ -1316,7 +1277,7 @@
goto out;
if (p_dev->device_no == p_dev->func) {
- ds_dbg(2, "requesting configuration for %s\n", dev->bus_id);
+ ds_dev_dbg(2, dev, "requesting configuration\n");
ret = pcmcia_request_configuration(p_dev, &p_dev->conf);
if (ret)
goto out;
@@ -1358,14 +1319,14 @@
static int pcmcia_bus_resume(struct pcmcia_socket *skt)
{
- ds_dbg(2, "resuming socket %d\n", skt->sock);
+ ds_dev_dbg(2, &skt->dev, "resuming socket %d\n", skt->sock);
bus_for_each_dev(&pcmcia_bus_type, NULL, skt, pcmcia_bus_resume_callback);
return 0;
}
static int pcmcia_bus_suspend(struct pcmcia_socket *skt)
{
- ds_dbg(2, "suspending socket %d\n", skt->sock);
+ ds_dev_dbg(2, &skt->dev, "suspending socket %d\n", skt->sock);
if (bus_for_each_dev(&pcmcia_bus_type, NULL, skt,
pcmcia_bus_suspend_callback)) {
pcmcia_bus_resume(skt);
@@ -1391,13 +1352,14 @@
struct pcmcia_socket *s = pcmcia_get_socket(skt);
if (!s) {
- printk(KERN_ERR "PCMCIA obtaining reference to socket %p " \
- "failed, event 0x%x lost!\n", skt, event);
+ dev_printk(KERN_ERR, &skt->dev,
+ "PCMCIA obtaining reference to socket " \
+ "failed, event 0x%x lost!\n", event);
return -ENODEV;
}
- ds_dbg(1, "ds_event(0x%06x, %d, 0x%p)\n",
- event, priority, skt);
+ ds_dev_dbg(1, &skt->dev, "ds_event(0x%06x, %d, 0x%p)\n",
+ event, priority, skt);
switch (event) {
case CS_EVENT_CARD_REMOVAL:
@@ -1472,7 +1434,8 @@
socket = pcmcia_get_socket(socket);
if (!socket) {
- printk(KERN_ERR "PCMCIA obtaining reference to socket %p failed\n", socket);
+ dev_printk(KERN_ERR, dev,
+ "PCMCIA obtaining reference to socket failed\n");
return -ENODEV;
}
@@ -1492,7 +1455,7 @@
ret = pccard_register_pcmcia(socket, &pcmcia_bus_callback);
if (ret) {
- printk(KERN_ERR "PCMCIA registration PCCard core failed for socket %p\n", socket);
+ dev_printk(KERN_ERR, dev, "PCMCIA registration failed\n");
pcmcia_put_socket(socket);
return (ret);
}
diff --git a/drivers/pcmcia/ds_internal.h b/drivers/pcmcia/ds_internal.h
deleted file mode 100644
index 3a2b25e..0000000
--- a/drivers/pcmcia/ds_internal.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/* ds_internal.h - internal header for 16-bit PCMCIA devices management */
-
-extern spinlock_t pcmcia_dev_list_lock;
-extern struct bus_type pcmcia_bus_type;
-
-extern struct pcmcia_device * pcmcia_get_dev(struct pcmcia_device *p_dev);
-extern void pcmcia_put_dev(struct pcmcia_device *p_dev);
-
-struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int function);
-
-extern int pcmcia_release_configuration(struct pcmcia_device *p_dev);
-
-#ifdef CONFIG_PCMCIA_IOCTL
-extern void __init pcmcia_setup_ioctl(void);
-extern void __exit pcmcia_cleanup_ioctl(void);
-extern void handle_event(struct pcmcia_socket *s, event_t event);
-extern int handle_request(struct pcmcia_socket *s, event_t event);
-#else
-static inline void __init pcmcia_setup_ioctl(void) { return; }
-static inline void __exit pcmcia_cleanup_ioctl(void) { return; }
-static inline void handle_event(struct pcmcia_socket *s, event_t event) { return; }
-static inline int handle_request(struct pcmcia_socket *s, event_t event) { return CS_SUCCESS; }
-#endif
diff --git a/drivers/pcmcia/hd64465_ss.c b/drivers/pcmcia/hd64465_ss.c
index fb2bc1f..117dc12 100644
--- a/drivers/pcmcia/hd64465_ss.c
+++ b/drivers/pcmcia/hd64465_ss.c
@@ -46,7 +46,6 @@
#include <pcmcia/cistpl.h>
#include <pcmcia/ds.h>
#include <pcmcia/ss.h>
-#include "cs_internal.h"
#define MODNAME "hd64465_ss"
diff --git a/drivers/pcmcia/i82365.c b/drivers/pcmcia/i82365.c
index 68f6b27..71653ab 100644
--- a/drivers/pcmcia/i82365.c
+++ b/drivers/pcmcia/i82365.c
@@ -63,7 +63,7 @@
#include "vg468.h"
#include "ricoh.h"
-#ifdef DEBUG
+#ifdef CONFIG_PCMCIA_DEBUG
static const char version[] =
"i82365.c 1.265 1999/11/10 18:36:21 (David Hinds)";
diff --git a/drivers/pcmcia/m32r_cfc.c b/drivers/pcmcia/m32r_cfc.c
index 3616da2..2ab4f22 100644
--- a/drivers/pcmcia/m32r_cfc.c
+++ b/drivers/pcmcia/m32r_cfc.c
@@ -38,7 +38,7 @@
#include "m32r_cfc.h"
-#ifdef DEBUG
+#ifdef CONFIG_PCMCIA_DEBUG
static int m32r_cfc_debug;
module_param(m32r_cfc_debug, int, 0644);
#define debug(lvl, fmt, arg...) do { \
@@ -505,7 +505,7 @@
pcc_set(sock,(unsigned int)PLD_CFBUFCR,1);
}
-#ifdef DEBUG
+#ifdef CONFIG_PCMCIA_DEBUG
if(state->flags & SS_IOCARD){
debug(3, ":IOCARD");
}
diff --git a/drivers/pcmcia/m32r_pcc.c b/drivers/pcmcia/m32r_pcc.c
index 2b42b71..2f108c2 100644
--- a/drivers/pcmcia/m32r_pcc.c
+++ b/drivers/pcmcia/m32r_pcc.c
@@ -45,7 +45,7 @@
#define PCC_DEBUG_DBEX
-#ifdef DEBUG
+#ifdef CONFIG_PCMCIA_DEBUG
static int m32r_pcc_debug;
module_param(m32r_pcc_debug, int, 0644);
#define debug(lvl, fmt, arg...) do { \
@@ -460,7 +460,7 @@
pcc_set(sock,PCCSIGCR,reg);
-#ifdef DEBUG
+#ifdef CONFIG_PCMCIA_DEBUG
if(state->flags & SS_IOCARD){
debug(3, ":IOCARD");
}
diff --git a/drivers/pcmcia/m8xx_pcmcia.c b/drivers/pcmcia/m8xx_pcmcia.c
index ff66604..d1ad096 100644
--- a/drivers/pcmcia/m8xx_pcmcia.c
+++ b/drivers/pcmcia/m8xx_pcmcia.c
@@ -64,8 +64,8 @@
#include <pcmcia/cs.h>
#include <pcmcia/ss.h>
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
+#ifdef CONFIG_PCMCIA_DEBUG
+static int pc_debug;
module_param(pc_debug, int, 0);
#define dprintk(args...) printk(KERN_DEBUG "m8xx_pcmcia: " args);
#else
diff --git a/drivers/pcmcia/o2micro.h b/drivers/pcmcia/o2micro.h
index a234ce1..5554015 100644
--- a/drivers/pcmcia/o2micro.h
+++ b/drivers/pcmcia/o2micro.h
@@ -140,7 +140,8 @@
a = config_readb(socket, O2_RESERVED1);
b = config_readb(socket, O2_RESERVED2);
- printk(KERN_INFO "Yenta O2: res at 0x94/0xD4: %02x/%02x\n", a, b);
+ dev_printk(KERN_INFO, &socket->dev->dev,
+ "O2: res at 0x94/0xD4: %02x/%02x\n", a, b);
switch (socket->dev->device) {
/*
@@ -153,7 +154,9 @@
case PCI_DEVICE_ID_O2_6812:
case PCI_DEVICE_ID_O2_6832:
case PCI_DEVICE_ID_O2_6836:
- printk(KERN_INFO "Yenta O2: old bridge, disabling read prefetch/write burst\n");
+ dev_printk(KERN_INFO, &socket->dev->dev,
+ "Yenta O2: old bridge, disabling read "
+ "prefetch/write burst\n");
config_writeb(socket, O2_RESERVED1,
a & ~(O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST));
config_writeb(socket, O2_RESERVED2,
@@ -161,7 +164,8 @@
break;
default:
- printk(KERN_INFO "Yenta O2: enabling read prefetch/write burst\n");
+ dev_printk(KERN_INFO , &socket->dev->dev,
+ "O2: enabling read prefetch/write burst\n");
config_writeb(socket, O2_RESERVED1,
a | O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST);
config_writeb(socket, O2_RESERVED2,
diff --git a/drivers/pcmcia/pcmcia_ioctl.c b/drivers/pcmcia/pcmcia_ioctl.c
index 419f97f..1703b20 100644
--- a/drivers/pcmcia/pcmcia_ioctl.c
+++ b/drivers/pcmcia/pcmcia_ioctl.c
@@ -38,7 +38,6 @@
#include <pcmcia/ss.h>
#include "cs_internal.h"
-#include "ds_internal.h"
static int major_dev = -1;
@@ -58,7 +57,7 @@
} user_info_t;
-#ifdef DEBUG
+#ifdef CONFIG_PCMCIA_DEBUG
extern int ds_pc_debug;
#define ds_dbg(lvl, fmt, arg...) do { \
@@ -149,7 +148,7 @@
irq = adj->resource.irq.IRQ;
if ((irq < 0) || (irq > 15))
- return CS_BAD_IRQ;
+ return -EINVAL;
if (adj->Action != REMOVE_MANAGED_RESOURCE)
return 0;
@@ -167,7 +166,7 @@
#else
static inline int adjust_irq(struct pcmcia_socket *s, adjust_t *adj) {
- return CS_SUCCESS;
+ return 0;
}
#endif
@@ -175,7 +174,7 @@
static int pcmcia_adjust_resource_info(adjust_t *adj)
{
struct pcmcia_socket *s;
- int ret = CS_UNSUPPORTED_FUNCTION;
+ int ret = -ENOSYS;
unsigned long flags;
down_read(&pcmcia_socket_list_rwsem);
@@ -248,7 +247,7 @@
if (s->state & SOCKET_SUSPEND)
status->CardState |= CS_EVENT_PM_SUSPEND;
if (!(s->state & SOCKET_PRESENT))
- return CS_NO_CARD;
+ return -ENODEV;
c = (p_dev) ? p_dev->function_config : NULL;
@@ -274,7 +273,7 @@
status->CardState |=
(reg & ESR_REQ_ATTN) ? CS_EVENT_REQUEST_ATTENTION : 0;
}
- return CS_SUCCESS;
+ return 0;
}
status->CardState |=
(val & SS_WRPROT) ? CS_EVENT_WRITE_PROTECT : 0;
@@ -284,9 +283,81 @@
(val & SS_BATWARN) ? CS_EVENT_BATTERY_LOW : 0;
status->CardState |=
(val & SS_READY) ? CS_EVENT_READY_CHANGE : 0;
- return CS_SUCCESS;
+ return 0;
} /* pccard_get_status */
+int pccard_get_configuration_info(struct pcmcia_socket *s,
+ struct pcmcia_device *p_dev,
+ config_info_t *config)
+{
+ config_t *c;
+
+ if (!(s->state & SOCKET_PRESENT))
+ return -ENODEV;
+
+
+#ifdef CONFIG_CARDBUS
+ if (s->state & SOCKET_CARDBUS) {
+ memset(config, 0, sizeof(config_info_t));
+ config->Vcc = s->socket.Vcc;
+ config->Vpp1 = config->Vpp2 = s->socket.Vpp;
+ config->Option = s->cb_dev->subordinate->number;
+ if (s->state & SOCKET_CARDBUS_CONFIG) {
+ config->Attributes = CONF_VALID_CLIENT;
+ config->IntType = INT_CARDBUS;
+ config->AssignedIRQ = s->irq.AssignedIRQ;
+ if (config->AssignedIRQ)
+ config->Attributes |= CONF_ENABLE_IRQ;
+ if (s->io[0].res) {
+ config->BasePort1 = s->io[0].res->start;
+ config->NumPorts1 = s->io[0].res->end -
+ config->BasePort1 + 1;
+ }
+ }
+ return 0;
+ }
+#endif
+
+ if (p_dev) {
+ c = p_dev->function_config;
+ config->Function = p_dev->func;
+ } else {
+ c = NULL;
+ config->Function = 0;
+ }
+
+ if ((c == NULL) || !(c->state & CONFIG_LOCKED)) {
+ config->Attributes = 0;
+ config->Vcc = s->socket.Vcc;
+ config->Vpp1 = config->Vpp2 = s->socket.Vpp;
+ return 0;
+ }
+
+ config->Attributes = c->Attributes | CONF_VALID_CLIENT;
+ config->Vcc = s->socket.Vcc;
+ config->Vpp1 = config->Vpp2 = s->socket.Vpp;
+ config->IntType = c->IntType;
+ config->ConfigBase = c->ConfigBase;
+ config->Status = c->Status;
+ config->Pin = c->Pin;
+ config->Copy = c->Copy;
+ config->Option = c->Option;
+ config->ExtStatus = c->ExtStatus;
+ config->Present = config->CardValues = c->CardValues;
+ config->IRQAttributes = c->irq.Attributes;
+ config->AssignedIRQ = s->irq.AssignedIRQ;
+ config->BasePort1 = c->io.BasePort1;
+ config->NumPorts1 = c->io.NumPorts1;
+ config->Attributes1 = c->io.Attributes1;
+ config->BasePort2 = c->io.BasePort2;
+ config->NumPorts2 = c->io.NumPorts2;
+ config->Attributes2 = c->io.Attributes2;
+ config->IOAddrLines = c->io.IOAddrLines;
+
+ return 0;
+} /* pccard_get_configuration_info */
+
+
/*======================================================================
These manage a ring buffer of events pending for one user process
@@ -764,7 +835,7 @@
case DS_GET_CONFIGURATION_INFO:
if (buf->config.Function &&
(buf->config.Function >= s->functions))
- ret = CS_BAD_ARGS;
+ ret = -EINVAL;
else {
struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->config.Function);
ret = pccard_get_configuration_info(s, p_dev, &buf->config);
@@ -787,15 +858,15 @@
break;
case DS_PARSE_TUPLE:
buf->tuple.TupleData = buf->tuple_parse.data;
- ret = pccard_parse_tuple(&buf->tuple, &buf->tuple_parse.parse);
+ ret = pcmcia_parse_tuple(&buf->tuple, &buf->tuple_parse.parse);
break;
case DS_RESET_CARD:
- ret = pccard_reset_card(s);
+ ret = pcmcia_reset_card(s);
break;
case DS_GET_STATUS:
if (buf->status.Function &&
(buf->status.Function >= s->functions))
- ret = CS_BAD_ARGS;
+ ret = -EINVAL;
else {
struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->status.Function);
ret = pccard_get_status(s, p_dev, &buf->status);
@@ -826,7 +897,7 @@
goto free_out;
}
- ret = CS_BAD_ARGS;
+ ret = -EINVAL;
if (!(buf->conf_reg.Function &&
(buf->conf_reg.Function >= s->functions))) {
@@ -867,7 +938,7 @@
&buf->win_info.map);
break;
case DS_REPLACE_CIS:
- ret = pcmcia_replace_cis(s, &buf->cisdump);
+ ret = pcmcia_replace_cis(s, buf->cisdump.Data, buf->cisdump.Length);
break;
case DS_BIND_REQUEST:
if (!capable(CAP_SYS_ADMIN)) {
@@ -889,22 +960,19 @@
err = -EINVAL;
}
- if ((err == 0) && (ret != CS_SUCCESS)) {
+ if ((err == 0) && (ret != 0)) {
ds_dbg(2, "ds_ioctl: ret = %d\n", ret);
switch (ret) {
- case CS_BAD_SOCKET: case CS_NO_CARD:
- err = -ENODEV; break;
- case CS_BAD_ARGS: case CS_BAD_ATTRIBUTE: case CS_BAD_IRQ:
- case CS_BAD_TUPLE:
- err = -EINVAL; break;
- case CS_IN_USE:
- err = -EBUSY; break;
- case CS_OUT_OF_RESOURCE:
+ case -ENODEV:
+ case -EINVAL:
+ case -EBUSY:
+ case -ENOSYS:
+ err = ret;
+ break;
+ case -ENOMEM:
err = -ENOSPC; break;
- case CS_NO_MORE_ITEMS:
+ case -ENOSPC:
err = -ENODATA; break;
- case CS_UNSUPPORTED_FUNCTION:
- err = -ENOSYS; break;
default:
err = -EIO; break;
}
diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c
index 4884a18..afea2b2 100644
--- a/drivers/pcmcia/pcmcia_resource.c
+++ b/drivers/pcmcia/pcmcia_resource.c
@@ -29,7 +29,6 @@
#include <pcmcia/ds.h>
#include "cs_internal.h"
-#include "ds_internal.h"
/* Access speed for IO windows */
@@ -44,16 +43,17 @@
#endif
-#ifdef DEBUG
+#ifdef CONFIG_PCMCIA_DEBUG
extern int ds_pc_debug;
#define ds_dbg(skt, lvl, fmt, arg...) do { \
if (ds_pc_debug >= lvl) \
- printk(KERN_DEBUG "pcmcia_resource: %s: " fmt, \
- cs_socket_name(skt) , ## arg); \
+ dev_printk(KERN_DEBUG, &skt->dev, \
+ "pcmcia_resource: " fmt, \
+ ## arg); \
} while (0)
#else
-#define ds_dbg(lvl, fmt, arg...) do { } while (0)
+#define ds_dbg(skt, lvl, fmt, arg...) do { } while (0)
#endif
@@ -168,13 +168,13 @@
u_char val;
if (!p_dev || !p_dev->function_config)
- return CS_NO_CARD;
+ return -EINVAL;
s = p_dev->socket;
c = p_dev->function_config;
if (!(c->state & CONFIG_LOCKED))
- return CS_CONFIGURATION_LOCKED;
+ return -EACCES;
addr = (c->ConfigBase + reg->Offset) >> 1;
@@ -188,93 +188,14 @@
pcmcia_write_cis_mem(s, 1, addr, 1, &val);
break;
default:
- return CS_BAD_ARGS;
+ return -EINVAL;
break;
}
- return CS_SUCCESS;
+ return 0;
} /* pcmcia_access_configuration_register */
EXPORT_SYMBOL(pcmcia_access_configuration_register);
-int pccard_get_configuration_info(struct pcmcia_socket *s,
- struct pcmcia_device *p_dev,
- config_info_t *config)
-{
- config_t *c;
-
- if (!(s->state & SOCKET_PRESENT))
- return CS_NO_CARD;
-
-
-#ifdef CONFIG_CARDBUS
- if (s->state & SOCKET_CARDBUS) {
- memset(config, 0, sizeof(config_info_t));
- config->Vcc = s->socket.Vcc;
- config->Vpp1 = config->Vpp2 = s->socket.Vpp;
- config->Option = s->cb_dev->subordinate->number;
- if (s->state & SOCKET_CARDBUS_CONFIG) {
- config->Attributes = CONF_VALID_CLIENT;
- config->IntType = INT_CARDBUS;
- config->AssignedIRQ = s->irq.AssignedIRQ;
- if (config->AssignedIRQ)
- config->Attributes |= CONF_ENABLE_IRQ;
- if (s->io[0].res) {
- config->BasePort1 = s->io[0].res->start;
- config->NumPorts1 = s->io[0].res->end - config->BasePort1 + 1;
- }
- }
- return CS_SUCCESS;
- }
-#endif
-
- if (p_dev) {
- c = p_dev->function_config;
- config->Function = p_dev->func;
- } else {
- c = NULL;
- config->Function = 0;
- }
-
- if ((c == NULL) || !(c->state & CONFIG_LOCKED)) {
- config->Attributes = 0;
- config->Vcc = s->socket.Vcc;
- config->Vpp1 = config->Vpp2 = s->socket.Vpp;
- return CS_SUCCESS;
- }
-
- config->Attributes = c->Attributes | CONF_VALID_CLIENT;
- config->Vcc = s->socket.Vcc;
- config->Vpp1 = config->Vpp2 = s->socket.Vpp;
- config->IntType = c->IntType;
- config->ConfigBase = c->ConfigBase;
- config->Status = c->Status;
- config->Pin = c->Pin;
- config->Copy = c->Copy;
- config->Option = c->Option;
- config->ExtStatus = c->ExtStatus;
- config->Present = config->CardValues = c->CardValues;
- config->IRQAttributes = c->irq.Attributes;
- config->AssignedIRQ = s->irq.AssignedIRQ;
- config->BasePort1 = c->io.BasePort1;
- config->NumPorts1 = c->io.NumPorts1;
- config->Attributes1 = c->io.Attributes1;
- config->BasePort2 = c->io.BasePort2;
- config->NumPorts2 = c->io.NumPorts2;
- config->Attributes2 = c->io.Attributes2;
- config->IOAddrLines = c->io.IOAddrLines;
-
- return CS_SUCCESS;
-} /* pccard_get_configuration_info */
-
-int pcmcia_get_configuration_info(struct pcmcia_device *p_dev,
- config_info_t *config)
-{
- return pccard_get_configuration_info(p_dev->socket, p_dev,
- config);
-}
-EXPORT_SYMBOL(pcmcia_get_configuration_info);
-
-
/** pcmcia_get_window
*/
int pcmcia_get_window(struct pcmcia_socket *s, window_handle_t *handle,
@@ -284,12 +205,12 @@
int w;
if (!s || !(s->state & SOCKET_PRESENT))
- return CS_NO_CARD;
+ return -ENODEV;
for (w = idx; w < MAX_WIN; w++)
if (s->state & SOCKET_WIN_REQ(w))
break;
if (w == MAX_WIN)
- return CS_NO_MORE_ITEMS;
+ return -EINVAL;
win = &s->win[w];
req->Base = win->ctl.res->start;
req->Size = win->ctl.res->end - win->ctl.res->start + 1;
@@ -304,7 +225,7 @@
if (win->ctl.flags & MAP_USE_WAIT)
req->Attributes |= WIN_USE_WAIT;
*handle = win;
- return CS_SUCCESS;
+ return 0;
} /* pcmcia_get_window */
EXPORT_SYMBOL(pcmcia_get_window);
@@ -316,10 +237,10 @@
int pcmcia_get_mem_page(window_handle_t win, memreq_t *req)
{
if ((win == NULL) || (win->magic != WINDOW_MAGIC))
- return CS_BAD_HANDLE;
+ return -EINVAL;
req->Page = 0;
req->CardOffset = win->ctl.card_start;
- return CS_SUCCESS;
+ return 0;
} /* pcmcia_get_mem_page */
EXPORT_SYMBOL(pcmcia_get_mem_page);
@@ -328,14 +249,18 @@
{
struct pcmcia_socket *s;
if ((win == NULL) || (win->magic != WINDOW_MAGIC))
- return CS_BAD_HANDLE;
- if (req->Page != 0)
- return CS_BAD_PAGE;
+ return -EINVAL;
s = win->sock;
+ if (req->Page != 0) {
+ ds_dbg(s, 0, "failure: requested page is zero\n");
+ return -EINVAL;
+ }
win->ctl.card_start = req->CardOffset;
- if (s->ops->set_mem_map(s, &win->ctl) != 0)
- return CS_BAD_OFFSET;
- return CS_SUCCESS;
+ if (s->ops->set_mem_map(s, &win->ctl) != 0) {
+ ds_dbg(s, 0, "failed to set_mem_map\n");
+ return -EIO;
+ }
+ return 0;
} /* pcmcia_map_mem_page */
EXPORT_SYMBOL(pcmcia_map_mem_page);
@@ -354,9 +279,9 @@
c = p_dev->function_config;
if (!(s->state & SOCKET_PRESENT))
- return CS_NO_CARD;
+ return -ENODEV;
if (!(c->state & CONFIG_LOCKED))
- return CS_CONFIGURATION_LOCKED;
+ return -EACCES;
if (mod->Attributes & CONF_IRQ_CHANGE_VALID) {
if (mod->Attributes & CONF_ENABLE_IRQ) {
@@ -369,20 +294,28 @@
s->ops->set_socket(s, &s->socket);
}
- if (mod->Attributes & CONF_VCC_CHANGE_VALID)
- return CS_BAD_VCC;
+ if (mod->Attributes & CONF_VCC_CHANGE_VALID) {
+ ds_dbg(s, 0, "changing Vcc is not allowed at this time\n");
+ return -EINVAL;
+ }
/* We only allow changing Vpp1 and Vpp2 to the same value */
if ((mod->Attributes & CONF_VPP1_CHANGE_VALID) &&
(mod->Attributes & CONF_VPP2_CHANGE_VALID)) {
if (mod->Vpp1 != mod->Vpp2)
- return CS_BAD_VPP;
+ ds_dbg(s, 0, "Vpp1 and Vpp2 must be the same\n");
+ return -EINVAL;
s->socket.Vpp = mod->Vpp1;
- if (s->ops->set_socket(s, &s->socket))
- return CS_BAD_VPP;
+ if (s->ops->set_socket(s, &s->socket)) {
+ dev_printk(KERN_WARNING, &s->dev,
+ "Unable to set VPP\n");
+ return -EIO;
+ }
} else if ((mod->Attributes & CONF_VPP1_CHANGE_VALID) ||
- (mod->Attributes & CONF_VPP2_CHANGE_VALID))
- return CS_BAD_VPP;
+ (mod->Attributes & CONF_VPP2_CHANGE_VALID)) {
+ ds_dbg(s, 0, "changing Vcc is not allowed at this time\n");
+ return -EINVAL;
+ }
if (mod->Attributes & CONF_IO_CHANGE_WIDTH) {
pccard_io_map io_off = { 0, 0, 0, 0, 1 };
@@ -406,7 +339,7 @@
}
}
- return CS_SUCCESS;
+ return 0;
} /* modify_configuration */
EXPORT_SYMBOL(pcmcia_modify_configuration);
@@ -441,7 +374,7 @@
}
}
- return CS_SUCCESS;
+ return 0;
} /* pcmcia_release_configuration */
@@ -459,7 +392,7 @@
config_t *c = p_dev->function_config;
if (!p_dev->_io )
- return CS_BAD_HANDLE;
+ return -EINVAL;
p_dev->_io = 0;
@@ -467,7 +400,7 @@
(c->io.NumPorts1 != req->NumPorts1) ||
(c->io.BasePort2 != req->BasePort2) ||
(c->io.NumPorts2 != req->NumPorts2))
- return CS_BAD_ARGS;
+ return -EINVAL;
c->state &= ~CONFIG_IO_REQ;
@@ -475,7 +408,7 @@
if (req->NumPorts2)
release_io_space(s, req->BasePort2, req->NumPorts2);
- return CS_SUCCESS;
+ return 0;
} /* pcmcia_release_io */
@@ -485,15 +418,19 @@
config_t *c= p_dev->function_config;
if (!p_dev->_irq)
- return CS_BAD_HANDLE;
+ return -EINVAL;
p_dev->_irq = 0;
if (c->state & CONFIG_LOCKED)
- return CS_CONFIGURATION_LOCKED;
- if (c->irq.Attributes != req->Attributes)
- return CS_BAD_ATTRIBUTE;
- if (s->irq.AssignedIRQ != req->AssignedIRQ)
- return CS_BAD_IRQ;
+ return -EACCES;
+ if (c->irq.Attributes != req->Attributes) {
+ ds_dbg(s, 0, "IRQ attributes must match assigned ones\n");
+ return -EINVAL;
+ }
+ if (s->irq.AssignedIRQ != req->AssignedIRQ) {
+ ds_dbg(s, 0, "IRQ must match assigned one\n");
+ return -EINVAL;
+ }
if (--s->irq.Config == 0) {
c->state &= ~CONFIG_IRQ_REQ;
s->irq.AssignedIRQ = 0;
@@ -507,7 +444,7 @@
pcmcia_used_irq[req->AssignedIRQ]--;
#endif
- return CS_SUCCESS;
+ return 0;
} /* pcmcia_release_irq */
@@ -516,10 +453,10 @@
struct pcmcia_socket *s;
if ((win == NULL) || (win->magic != WINDOW_MAGIC))
- return CS_BAD_HANDLE;
+ return -EINVAL;
s = win->sock;
if (!(win->handle->_win & CLIENT_WIN_REQ(win->index)))
- return CS_BAD_HANDLE;
+ return -EINVAL;
/* Shut down memory window */
win->ctl.flags &= ~MAP_ACTIVE;
@@ -536,7 +473,7 @@
win->magic = 0;
- return CS_SUCCESS;
+ return 0;
} /* pcmcia_release_window */
EXPORT_SYMBOL(pcmcia_release_window);
@@ -551,18 +488,23 @@
pccard_io_map iomap;
if (!(s->state & SOCKET_PRESENT))
- return CS_NO_CARD;
+ return -ENODEV;;
- if (req->IntType & INT_CARDBUS)
- return CS_UNSUPPORTED_MODE;
+ if (req->IntType & INT_CARDBUS) {
+ ds_dbg(p_dev->socket, 0, "IntType may not be INT_CARDBUS\n");
+ return -EINVAL;
+ }
c = p_dev->function_config;
if (c->state & CONFIG_LOCKED)
- return CS_CONFIGURATION_LOCKED;
+ return -EACCES;
/* Do power control. We don't allow changes in Vcc. */
s->socket.Vpp = req->Vpp;
- if (s->ops->set_socket(s, &s->socket))
- return CS_BAD_VPP;
+ if (s->ops->set_socket(s, &s->socket)) {
+ dev_printk(KERN_WARNING, &s->dev,
+ "Unable to set socket state\n");
+ return -EINVAL;
+ }
/* Pick memory or I/O card, DMA mode, interrupt */
c->IntType = req->IntType;
@@ -651,7 +593,7 @@
c->state |= CONFIG_LOCKED;
p_dev->_locked = 1;
- return CS_SUCCESS;
+ return 0;
} /* pcmcia_request_configuration */
EXPORT_SYMBOL(pcmcia_request_configuration);
@@ -667,37 +609,48 @@
config_t *c;
if (!(s->state & SOCKET_PRESENT))
- return CS_NO_CARD;
+ return -ENODEV;
if (!req)
- return CS_UNSUPPORTED_MODE;
+ return -EINVAL;
c = p_dev->function_config;
if (c->state & CONFIG_LOCKED)
- return CS_CONFIGURATION_LOCKED;
- if (c->state & CONFIG_IO_REQ)
- return CS_IN_USE;
- if (req->Attributes1 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS))
- return CS_BAD_ATTRIBUTE;
+ return -EACCES;
+ if (c->state & CONFIG_IO_REQ) {
+ ds_dbg(s, 0, "IO already configured\n");
+ return -EBUSY;
+ }
+ if (req->Attributes1 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS)) {
+ ds_dbg(s, 0, "bad attribute setting for IO region 1\n");
+ return -EINVAL;
+ }
if ((req->NumPorts2 > 0) &&
- (req->Attributes2 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS)))
- return CS_BAD_ATTRIBUTE;
+ (req->Attributes2 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS))) {
+ ds_dbg(s, 0, "bad attribute setting for IO region 2\n");
+ return -EINVAL;
+ }
+ ds_dbg(s, 1, "trying to allocate resource 1\n");
if (alloc_io_space(s, req->Attributes1, &req->BasePort1,
- req->NumPorts1, req->IOAddrLines))
- return CS_IN_USE;
+ req->NumPorts1, req->IOAddrLines)) {
+ ds_dbg(s, 0, "allocation of resource 1 failed\n");
+ return -EBUSY;
+ }
if (req->NumPorts2) {
+ ds_dbg(s, 1, "trying to allocate resource 2\n");
if (alloc_io_space(s, req->Attributes2, &req->BasePort2,
req->NumPorts2, req->IOAddrLines)) {
+ ds_dbg(s, 0, "allocation of resource 2 failed\n");
release_io_space(s, req->BasePort1, req->NumPorts1);
- return CS_IN_USE;
+ return -EBUSY;
}
}
c->io = *req;
c->state |= CONFIG_IO_REQ;
p_dev->_io = 1;
- return CS_SUCCESS;
+ return 0;
} /* pcmcia_request_io */
EXPORT_SYMBOL(pcmcia_request_io);
@@ -723,16 +676,18 @@
{
struct pcmcia_socket *s = p_dev->socket;
config_t *c;
- int ret = CS_IN_USE, irq = 0;
+ int ret = -EINVAL, irq = 0;
int type;
if (!(s->state & SOCKET_PRESENT))
- return CS_NO_CARD;
+ return -ENODEV;
c = p_dev->function_config;
if (c->state & CONFIG_LOCKED)
- return CS_CONFIGURATION_LOCKED;
- if (c->state & CONFIG_IRQ_REQ)
- return CS_IN_USE;
+ return -EACCES;
+ if (c->state & CONFIG_IRQ_REQ) {
+ ds_dbg(s, 0, "IRQ already configured\n");
+ return -EBUSY;
+ }
/* Decide what type of interrupt we are registering */
type = 0;
@@ -795,15 +750,19 @@
}
if (ret && (req->Attributes & IRQ_HANDLE_PRESENT)) {
- if (request_irq(irq, req->Handler, type, p_dev->devname, req->Instance))
- return CS_IN_USE;
+ ret = request_irq(irq, req->Handler, type,
+ p_dev->devname, req->Instance);
+ if (ret)
+ return ret;
}
/* Make sure the fact the request type was overridden is passed back */
if (type == IRQF_SHARED && !(req->Attributes & IRQ_TYPE_DYNAMIC_SHARING)) {
req->Attributes |= IRQ_TYPE_DYNAMIC_SHARING;
- printk(KERN_WARNING "pcmcia: request for exclusive IRQ could not be fulfilled.\n");
- printk(KERN_WARNING "pcmcia: the driver needs updating to supported shared IRQ lines.\n");
+ dev_printk(KERN_WARNING, &p_dev->dev, "pcmcia: "
+ "request for exclusive IRQ could not be fulfilled.\n");
+ dev_printk(KERN_WARNING, &p_dev->dev, "pcmcia: the driver "
+ "needs updating to supported shared IRQ lines.\n");
}
c->irq.Attributes = req->Attributes;
s->irq.AssignedIRQ = req->AssignedIRQ = irq;
@@ -816,7 +775,7 @@
pcmcia_used_irq[irq]++;
#endif
- return CS_SUCCESS;
+ return 0;
} /* pcmcia_request_irq */
EXPORT_SYMBOL(pcmcia_request_irq);
@@ -834,9 +793,11 @@
int w;
if (!(s->state & SOCKET_PRESENT))
- return CS_NO_CARD;
- if (req->Attributes & (WIN_PAGED | WIN_SHARED))
- return CS_BAD_ATTRIBUTE;
+ return -ENODEV;
+ if (req->Attributes & (WIN_PAGED | WIN_SHARED)) {
+ ds_dbg(s, 0, "bad attribute setting for iomem region\n");
+ return -EINVAL;
+ }
/* Window size defaults to smallest available */
if (req->Size == 0)
@@ -844,19 +805,25 @@
align = (((s->features & SS_CAP_MEM_ALIGN) ||
(req->Attributes & WIN_STRICT_ALIGN)) ?
req->Size : s->map_size);
- if (req->Size & (s->map_size-1))
- return CS_BAD_SIZE;
+ if (req->Size & (s->map_size-1)) {
+ ds_dbg(s, 0, "invalid map size\n");
+ return -EINVAL;
+ }
if ((req->Base && (s->features & SS_CAP_STATIC_MAP)) ||
- (req->Base & (align-1)))
- return CS_BAD_BASE;
+ (req->Base & (align-1))) {
+ ds_dbg(s, 0, "invalid base address\n");
+ return -EINVAL;
+ }
if (req->Base)
align = 0;
/* Allocate system memory window */
for (w = 0; w < MAX_WIN; w++)
if (!(s->state & SOCKET_WIN_REQ(w))) break;
- if (w == MAX_WIN)
- return CS_OUT_OF_RESOURCE;
+ if (w == MAX_WIN) {
+ ds_dbg(s, 0, "all windows are used already\n");
+ return -EINVAL;
+ }
win = &s->win[w];
win->magic = WINDOW_MAGIC;
@@ -867,8 +834,10 @@
if (!(s->features & SS_CAP_STATIC_MAP)) {
win->ctl.res = pcmcia_find_mem_region(req->Base, req->Size, align,
(req->Attributes & WIN_MAP_BELOW_1MB), s);
- if (!win->ctl.res)
- return CS_IN_USE;
+ if (!win->ctl.res) {
+ ds_dbg(s, 0, "allocating mem region failed\n");
+ return -EINVAL;
+ }
}
(*p_dev)->_win |= CLIENT_WIN_REQ(w);
@@ -885,8 +854,10 @@
if (req->Attributes & WIN_USE_WAIT)
win->ctl.flags |= MAP_USE_WAIT;
win->ctl.card_start = 0;
- if (s->ops->set_mem_map(s, &win->ctl) != 0)
- return CS_BAD_ARGS;
+ if (s->ops->set_mem_map(s, &win->ctl) != 0) {
+ ds_dbg(s, 0, "failed to set memory mapping\n");
+ return -EIO;
+ }
s->state |= SOCKET_WIN_REQ(w);
/* Return window handle */
@@ -897,7 +868,7 @@
}
*wh = win;
- return CS_SUCCESS;
+ return 0;
} /* pcmcia_request_window */
EXPORT_SYMBOL(pcmcia_request_window);
@@ -909,3 +880,79 @@
pcmcia_release_window(p_dev->win);
}
EXPORT_SYMBOL(pcmcia_disable_device);
+
+
+struct pcmcia_cfg_mem {
+ tuple_t tuple;
+ cisparse_t parse;
+ u8 buf[256];
+ cistpl_cftable_entry_t dflt;
+};
+
+/**
+ * pcmcia_loop_config() - loop over configuration options
+ * @p_dev: the struct pcmcia_device which we need to loop for.
+ * @conf_check: function to call for each configuration option.
+ * It gets passed the struct pcmcia_device, the CIS data
+ * describing the configuration option, and private data
+ * being passed to pcmcia_loop_config()
+ * @priv_data: private data to be passed to the conf_check function.
+ *
+ * pcmcia_loop_config() loops over all configuration options, and calls
+ * the driver-specific conf_check() for each one, checking whether
+ * it is a valid one.
+ */
+int pcmcia_loop_config(struct pcmcia_device *p_dev,
+ int (*conf_check) (struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data),
+ void *priv_data)
+{
+ struct pcmcia_cfg_mem *cfg_mem;
+
+ tuple_t *tuple;
+ int ret = -ENODEV;
+ unsigned int vcc;
+
+ cfg_mem = kzalloc(sizeof(struct pcmcia_cfg_mem), GFP_KERNEL);
+ if (cfg_mem == NULL)
+ return -ENOMEM;
+
+ /* get the current Vcc setting */
+ vcc = p_dev->socket->socket.Vcc;
+
+ tuple = &cfg_mem->tuple;
+ tuple->TupleData = cfg_mem->buf;
+ tuple->TupleDataMax = 255;
+ tuple->TupleOffset = 0;
+ tuple->DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ tuple->Attributes = 0;
+
+ ret = pcmcia_get_first_tuple(p_dev, tuple);
+ while (!ret) {
+ cistpl_cftable_entry_t *cfg = &cfg_mem->parse.cftable_entry;
+
+ if (pcmcia_get_tuple_data(p_dev, tuple))
+ goto next_entry;
+
+ if (pcmcia_parse_tuple(tuple, &cfg_mem->parse))
+ goto next_entry;
+
+ /* default values */
+ p_dev->conf.ConfigIndex = cfg->index;
+ if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
+ cfg_mem->dflt = *cfg;
+
+ ret = conf_check(p_dev, cfg, &cfg_mem->dflt, vcc, priv_data);
+ if (!ret)
+ break;
+
+next_entry:
+ ret = pcmcia_get_next_tuple(p_dev, tuple);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(pcmcia_loop_config);
diff --git a/drivers/pcmcia/pxa2xx_base.c b/drivers/pcmcia/pxa2xx_base.c
index 13f1e0f..bb9ddb9 100644
--- a/drivers/pcmcia/pxa2xx_base.c
+++ b/drivers/pcmcia/pxa2xx_base.c
@@ -36,7 +36,6 @@
#include <pcmcia/ss.h>
#include <pcmcia/cistpl.h>
-#include "cs_internal.h"
#include "soc_common.h"
#include "pxa2xx_base.h"
diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c
index 203e579..17f4ecf 100644
--- a/drivers/pcmcia/rsrc_nonstatic.c
+++ b/drivers/pcmcia/rsrc_nonstatic.c
@@ -122,19 +122,22 @@
static int add_interval(struct resource_map *map, u_long base, u_long num)
{
- struct resource_map *p, *q;
+ struct resource_map *p, *q;
- for (p = map; ; p = p->next) {
- if ((p != map) && (p->base+p->num-1 >= base))
- return -1;
- if ((p->next == map) || (p->next->base > base+num-1))
- break;
- }
- q = kmalloc(sizeof(struct resource_map), GFP_KERNEL);
- if (!q) return CS_OUT_OF_RESOURCE;
- q->base = base; q->num = num;
- q->next = p->next; p->next = q;
- return CS_SUCCESS;
+ for (p = map; ; p = p->next) {
+ if ((p != map) && (p->base+p->num-1 >= base))
+ return -1;
+ if ((p->next == map) || (p->next->base > base+num-1))
+ break;
+ }
+ q = kmalloc(sizeof(struct resource_map), GFP_KERNEL);
+ if (!q) {
+ printk(KERN_WARNING "out of memory to update resources\n");
+ return -ENOMEM;
+ }
+ q->base = base; q->num = num;
+ q->next = p->next; p->next = q;
+ return 0;
}
/*====================================================================*/
@@ -166,7 +169,10 @@
} else {
/* Split the block into two pieces */
p = kmalloc(sizeof(struct resource_map), GFP_KERNEL);
- if (!p) return CS_OUT_OF_RESOURCE;
+ if (!p) {
+ printk(KERN_WARNING "out of memory to update resources\n");
+ return -ENOMEM;
+ }
p->base = base+num;
p->num = q->base+q->num - p->base;
q->num = base - q->base;
@@ -174,7 +180,7 @@
}
}
}
- return CS_SUCCESS;
+ return 0;
}
/*======================================================================
@@ -194,13 +200,14 @@
int any;
u_char *b, hole, most;
- printk(KERN_INFO "cs: IO port probe %#x-%#x:",
- base, base+num-1);
+ dev_printk(KERN_INFO, &s->dev, "cs: IO port probe %#x-%#x:",
+ base, base+num-1);
/* First, what does a floating port look like? */
b = kzalloc(256, GFP_KERNEL);
if (!b) {
- printk(KERN_ERR "do_io_probe: unable to kmalloc 256 bytes");
+ dev_printk(KERN_ERR, &s->dev,
+ "do_io_probe: unable to kmalloc 256 bytes");
return;
}
for (i = base, most = 0; i < base+num; i += 8) {
@@ -366,8 +373,8 @@
struct socket_data *s_data = s->resource_data;
u_long i, j, bad, fail, step;
- printk(KERN_INFO "cs: memory probe 0x%06lx-0x%06lx:",
- base, base+num-1);
+ dev_printk(KERN_INFO, &s->dev, "cs: memory probe 0x%06lx-0x%06lx:",
+ base, base+num-1);
bad = fail = 0;
step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff);
/* don't allow too large steps */
@@ -431,8 +438,8 @@
if (probe_mask & MEM_PROBE_HIGH) {
if (inv_probe(s_data->mem_db.next, s) > 0)
return 0;
- printk(KERN_NOTICE "cs: warning: no high memory space "
- "available!\n");
+ dev_printk(KERN_NOTICE, &s->dev,
+ "cs: warning: no high memory space available!\n");
return -ENODEV;
}
@@ -794,10 +801,11 @@
if (res->flags & IORESOURCE_IO) {
if (res == &ioport_resource)
continue;
- printk(KERN_INFO "pcmcia: parent PCI bridge I/O "
- "window: 0x%llx - 0x%llx\n",
- (unsigned long long)res->start,
- (unsigned long long)res->end);
+ dev_printk(KERN_INFO, &s->cb_dev->dev,
+ "pcmcia: parent PCI bridge I/O "
+ "window: 0x%llx - 0x%llx\n",
+ (unsigned long long)res->start,
+ (unsigned long long)res->end);
if (!adjust_io(s, ADD_MANAGED_RESOURCE, res->start, res->end))
done |= IORESOURCE_IO;
@@ -806,10 +814,11 @@
if (res->flags & IORESOURCE_MEM) {
if (res == &iomem_resource)
continue;
- printk(KERN_INFO "pcmcia: parent PCI bridge Memory "
- "window: 0x%llx - 0x%llx\n",
- (unsigned long long)res->start,
- (unsigned long long)res->end);
+ dev_printk(KERN_INFO, &s->cb_dev->dev,
+ "pcmcia: parent PCI bridge Memory "
+ "window: 0x%llx - 0x%llx\n",
+ (unsigned long long)res->start,
+ (unsigned long long)res->end);
if (!adjust_memory(s, ADD_MANAGED_RESOURCE, res->start, res->end))
done |= IORESOURCE_MEM;
}
diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c
index da39721..f49ac66 100644
--- a/drivers/pcmcia/soc_common.c
+++ b/drivers/pcmcia/soc_common.c
@@ -54,7 +54,7 @@
#include <mach/pxa-regs.h>
#endif
-#ifdef DEBUG
+#ifdef CONFIG_PCMCIA_DEBUG
static int pc_debug;
module_param(pc_debug, int, 0644);
diff --git a/drivers/pcmcia/soc_common.h b/drivers/pcmcia/soc_common.h
index 91ef6a0..38c6737 100644
--- a/drivers/pcmcia/soc_common.h
+++ b/drivers/pcmcia/soc_common.h
@@ -15,7 +15,6 @@
#include <pcmcia/cs.h>
#include <pcmcia/ss.h>
#include <pcmcia/cistpl.h>
-#include "cs_internal.h"
struct device;
@@ -137,7 +136,7 @@
extern int soc_common_drv_pcmcia_remove(struct device *dev);
-#ifdef DEBUG
+#ifdef CONFIG_PCMCIA_DEBUG
extern void soc_pcmcia_debug(struct soc_pcmcia_socket *skt, const char *func,
int lvl, const char *fmt, ...);
diff --git a/drivers/pcmcia/socket_sysfs.c b/drivers/pcmcia/socket_sysfs.c
index 006a29e..ff9a3bb 100644
--- a/drivers/pcmcia/socket_sysfs.c
+++ b/drivers/pcmcia/socket_sysfs.c
@@ -316,27 +316,18 @@
char *buf, loff_t off, size_t count)
{
struct pcmcia_socket *s = to_socket(container_of(kobj, struct device, kobj));
- cisdump_t *cis;
int error;
if (off)
return -EINVAL;
- if (count >= 0x200)
+ if (count >= CISTPL_MAX_CIS_SIZE)
return -EINVAL;
if (!(s->state & SOCKET_PRESENT))
return -ENODEV;
- cis = kzalloc(sizeof(cisdump_t), GFP_KERNEL);
- if (!cis)
- return -ENOMEM;
-
- cis->Length = count + 1;
- memcpy(cis->Data, buf, count);
-
- error = pcmcia_replace_cis(s, cis);
- kfree(cis);
+ error = pcmcia_replace_cis(s, buf, count);
if (error)
return -EIO;
diff --git a/drivers/pcmcia/tcic.c b/drivers/pcmcia/tcic.c
index 5792bd5..2a613e9 100644
--- a/drivers/pcmcia/tcic.c
+++ b/drivers/pcmcia/tcic.c
@@ -55,7 +55,7 @@
#include <pcmcia/ss.h>
#include "tcic.h"
-#ifdef DEBUG
+#ifdef CONFIG_PCMCIA_DEBUG
static int pc_debug;
module_param(pc_debug, int, 0644);
diff --git a/drivers/pcmcia/ti113x.h b/drivers/pcmcia/ti113x.h
index 129db7b..aaa7022 100644
--- a/drivers/pcmcia/ti113x.h
+++ b/drivers/pcmcia/ti113x.h
@@ -339,8 +339,8 @@
mfunc = mfunc_old = config_readl(socket, TI122X_MFUNC);
devctl = config_readb(socket, TI113X_DEVICE_CONTROL);
- printk(KERN_INFO "Yenta TI: socket %s, mfunc 0x%08x, devctl 0x%02x\n",
- pci_name(socket->dev), mfunc, devctl);
+ dev_printk(KERN_INFO, &socket->dev->dev,
+ "TI: mfunc 0x%08x, devctl 0x%02x\n", mfunc, devctl);
/* make sure PCI interrupts are enabled before probing */
ti_init(socket);
@@ -354,8 +354,8 @@
* We're here which means PCI interrupts are _not_ delivered. try to
* find the right setting (all serial or parallel)
*/
- printk(KERN_INFO "Yenta TI: socket %s probing PCI interrupt failed, trying to fix\n",
- pci_name(socket->dev));
+ dev_printk(KERN_INFO, &socket->dev->dev,
+ "TI: probing PCI interrupt failed, trying to fix\n");
/* for serial PCI make sure MFUNC3 is set to IRQSER */
if ((devctl & TI113X_DCR_IMODE_MASK) == TI12XX_DCR_IMODE_ALL_SERIAL) {
@@ -379,8 +379,8 @@
pci_irq_status = yenta_probe_cb_irq(socket);
if (pci_irq_status == 1) {
- printk(KERN_INFO "Yenta TI: socket %s all-serial interrupts ok\n",
- pci_name(socket->dev));
+ dev_printk(KERN_INFO, &socket->dev->dev,
+ "TI: all-serial interrupts ok\n");
mfunc_old = mfunc;
goto out;
}
@@ -395,8 +395,8 @@
}
/* serial PCI interrupts not working fall back to parallel */
- printk(KERN_INFO "Yenta TI: socket %s falling back to parallel PCI interrupts\n",
- pci_name(socket->dev));
+ dev_printk(KERN_INFO, &socket->dev->dev,
+ "TI: falling back to parallel PCI interrupts\n");
devctl &= ~TI113X_DCR_IMODE_MASK;
devctl |= TI113X_DCR_IMODE_SERIAL; /* serial ISA could be right */
config_writeb(socket, TI113X_DEVICE_CONTROL, devctl);
@@ -427,8 +427,8 @@
pci_irq_status = yenta_probe_cb_irq(socket);
if (pci_irq_status == 1) {
mfunc_old = mfunc;
- printk(KERN_INFO "Yenta TI: socket %s parallel PCI interrupts ok\n",
- pci_name(socket->dev));
+ dev_printk(KERN_INFO, &socket->dev->dev,
+ "TI: parallel PCI interrupts ok\n");
} else {
/* not working, back to old value */
mfunc = mfunc_old;
@@ -440,8 +440,9 @@
out:
if (pci_irq_status < 1) {
socket->cb_irq = 0;
- printk(KERN_INFO "Yenta TI: socket %s no PCI interrupts. Fish. Please report.\n",
- pci_name(socket->dev));
+ dev_printk(KERN_INFO, &socket->dev->dev,
+ "Yenta TI: no PCI interrupts. Fish. "
+ "Please report.\n");
}
}
@@ -513,8 +514,9 @@
mfunc = mfunc_old = config_readl(socket, TI122X_MFUNC);
devctl = config_readb(socket, TI113X_DEVICE_CONTROL);
- printk(KERN_INFO "Yenta TI: socket %s, mfunc 0x%08x, devctl 0x%02x\n",
- pci_name(socket->dev), mfunc, devctl);
+ dev_printk(KERN_INFO, &socket->dev->dev,
+ "TI: mfunc 0x%08x, devctl 0x%02x\n",
+ mfunc, devctl);
/* if IRQs are configured as tied, align irq of func1 with func0 */
sysctl = config_readl(socket, TI113X_SYSTEM_CONTROL);
@@ -533,9 +535,8 @@
* We're here which means PCI interrupts are _not_ delivered. try to
* find the right setting
*/
- printk(KERN_INFO "Yenta TI: socket %s probing PCI interrupt failed, trying to fix\n",
- pci_name(socket->dev));
-
+ dev_printk(KERN_INFO, &socket->dev->dev,
+ "TI: probing PCI interrupt failed, trying to fix\n");
/* if all serial: set INTRTIE, probe again */
if ((devctl & TI113X_DCR_IMODE_MASK) == TI12XX_DCR_IMODE_ALL_SERIAL) {
@@ -544,8 +545,8 @@
if (ti12xx_tie_interrupts(socket, &old_irq)) {
pci_irq_status = yenta_probe_cb_irq(socket);
if (pci_irq_status == 1) {
- printk(KERN_INFO "Yenta TI: socket %s all-serial interrupts, tied ok\n",
- pci_name(socket->dev));
+ dev_printk(KERN_INFO, &socket->dev->dev,
+ "TI: all-serial interrupts, tied ok\n");
goto out;
}
@@ -582,8 +583,8 @@
pci_irq_status = yenta_probe_cb_irq(socket);
if (pci_irq_status == 1) {
- printk(KERN_INFO "Yenta TI: socket %s parallel PCI interrupts ok\n",
- pci_name(socket->dev));
+ dev_printk(KERN_INFO, &socket->dev->dev,
+ "TI: parallel PCI interrupts ok\n");
goto out;
}
@@ -593,13 +594,13 @@
if (pci_irq_status == -1)
goto out;
}
-
+
/* still nothing: set INTRTIE */
if (ti12xx_tie_interrupts(socket, &old_irq)) {
pci_irq_status = yenta_probe_cb_irq(socket);
if (pci_irq_status == 1) {
- printk(KERN_INFO "Yenta TI: socket %s parallel PCI interrupts, tied ok\n",
- pci_name(socket->dev));
+ dev_printk(KERN_INFO, &socket->dev->dev,
+ "TI: parallel PCI interrupts, tied ok\n");
goto out;
}
@@ -610,8 +611,8 @@
out:
if (pci_irq_status < 1) {
socket->cb_irq = 0;
- printk(KERN_INFO "Yenta TI: socket %s no PCI interrupts. Fish. Please report.\n",
- pci_name(socket->dev));
+ dev_printk(KERN_INFO, &socket->dev->dev,
+ "TI: no PCI interrupts. Fish. Please report.\n");
}
}
@@ -815,11 +816,13 @@
/* make sure that memory burst is active */
val_orig = val = config_readl(socket, TI113X_SYSTEM_CONTROL);
if (disable_clkrun && PCI_FUNC(socket->dev->devfn) == 0) {
- printk(KERN_INFO "Yenta: Disabling CLKRUN feature\n");
+ dev_printk(KERN_INFO, &socket->dev->dev,
+ "Disabling CLKRUN feature\n");
val |= TI113X_SCR_KEEPCLK;
}
if (!(val & TI122X_SCR_MRBURSTUP)) {
- printk(KERN_INFO "Yenta: Enabling burst memory read transactions\n");
+ dev_printk(KERN_INFO, &socket->dev->dev,
+ "Enabling burst memory read transactions\n");
val |= TI122X_SCR_MRBURSTUP;
}
if (val_orig != val)
@@ -830,10 +833,12 @@
* CSC interrupts to PCI rather than INTVAL.
*/
val = config_readb(socket, TI1250_DIAGNOSTIC);
- printk(KERN_INFO "Yenta: Using %s to route CSC interrupts to PCI\n",
- (val & TI1250_DIAG_PCI_CSC) ? "CSCINT" : "INTVAL");
- printk(KERN_INFO "Yenta: Routing CardBus interrupts to %s\n",
- (val & TI1250_DIAG_PCI_IREQ) ? "PCI" : "ISA");
+ dev_printk(KERN_INFO, &socket->dev->dev,
+ "Using %s to route CSC interrupts to PCI\n",
+ (val & TI1250_DIAG_PCI_CSC) ? "CSCINT" : "INTVAL");
+ dev_printk(KERN_INFO, &socket->dev->dev,
+ "Routing CardBus interrupts to %s\n",
+ (val & TI1250_DIAG_PCI_IREQ) ? "PCI" : "ISA");
/* do irqrouting, depending on function */
if (PCI_FUNC(socket->dev->devfn) == 0)
@@ -858,8 +863,9 @@
diag |= TI1250_DIAG_PCI_CSC | TI1250_DIAG_PCI_IREQ;
if (diag != old) {
- printk(KERN_INFO "Yenta: adjusting diagnostic: %02x -> %02x\n",
- old, diag);
+ dev_printk(KERN_INFO, &socket->dev->dev,
+ "adjusting diagnostic: %02x -> %02x\n",
+ old, diag);
config_writeb(socket, TI1250_DIAGNOSTIC, diag);
}
@@ -924,7 +930,9 @@
/* default to clear TLTEnable bit, old behaviour */
test_c9 &= ~ENE_TEST_C9_TLTENABLE;
- printk(KERN_INFO "yenta EnE: chaning testregister 0xC9, %02x -> %02x\n", old_c9, test_c9);
+ dev_printk(KERN_INFO, &socket->dev->dev,
+ "EnE: chaning testregister 0xC9, %02x -> %02x\n",
+ old_c9, test_c9);
config_writeb(socket, ENE_TEST_C9, test_c9);
}
diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c
index 0ab1fb6..3ecd7c9 100644
--- a/drivers/pcmcia/yenta_socket.c
+++ b/drivers/pcmcia/yenta_socket.c
@@ -38,11 +38,7 @@
module_param(pwr_irqs_off, bool, 0644);
MODULE_PARM_DESC(pwr_irqs_off, "Force IRQs off during power-on of slot. Use only when seeing IRQ storms!");
-#if 0
-#define debug(x,args...) printk(KERN_DEBUG "%s: " x, __func__ , ##args)
-#else
-#define debug(x,args...)
-#endif
+#define debug(x, s, args...) dev_dbg(&s->dev->dev, x, ##args)
/* Don't ask.. */
#define to_cycles(ns) ((ns)/120)
@@ -69,13 +65,13 @@
static inline u32 cb_readl(struct yenta_socket *socket, unsigned reg)
{
u32 val = readl(socket->base + reg);
- debug("%p %04x %08x\n", socket, reg, val);
+ debug("%04x %08x\n", socket, reg, val);
return val;
}
static inline void cb_writel(struct yenta_socket *socket, unsigned reg, u32 val)
{
- debug("%p %04x %08x\n", socket, reg, val);
+ debug("%04x %08x\n", socket, reg, val);
writel(val, socket->base + reg);
readl(socket->base + reg); /* avoid problems with PCI write posting */
}
@@ -84,7 +80,7 @@
{
u8 val;
pci_read_config_byte(socket->dev, offset, &val);
- debug("%p %04x %02x\n", socket, offset, val);
+ debug("%04x %02x\n", socket, offset, val);
return val;
}
@@ -92,7 +88,7 @@
{
u16 val;
pci_read_config_word(socket->dev, offset, &val);
- debug("%p %04x %04x\n", socket, offset, val);
+ debug("%04x %04x\n", socket, offset, val);
return val;
}
@@ -100,32 +96,32 @@
{
u32 val;
pci_read_config_dword(socket->dev, offset, &val);
- debug("%p %04x %08x\n", socket, offset, val);
+ debug("%04x %08x\n", socket, offset, val);
return val;
}
static inline void config_writeb(struct yenta_socket *socket, unsigned offset, u8 val)
{
- debug("%p %04x %02x\n", socket, offset, val);
+ debug("%04x %02x\n", socket, offset, val);
pci_write_config_byte(socket->dev, offset, val);
}
static inline void config_writew(struct yenta_socket *socket, unsigned offset, u16 val)
{
- debug("%p %04x %04x\n", socket, offset, val);
+ debug("%04x %04x\n", socket, offset, val);
pci_write_config_word(socket->dev, offset, val);
}
static inline void config_writel(struct yenta_socket *socket, unsigned offset, u32 val)
{
- debug("%p %04x %08x\n", socket, offset, val);
+ debug("%04x %08x\n", socket, offset, val);
pci_write_config_dword(socket->dev, offset, val);
}
static inline u8 exca_readb(struct yenta_socket *socket, unsigned reg)
{
u8 val = readb(socket->base + 0x800 + reg);
- debug("%p %04x %02x\n", socket, reg, val);
+ debug("%04x %02x\n", socket, reg, val);
return val;
}
@@ -134,20 +130,20 @@
u16 val;
val = readb(socket->base + 0x800 + reg);
val |= readb(socket->base + 0x800 + reg + 1) << 8;
- debug("%p %04x %04x\n", socket, reg, val);
+ debug("%04x %04x\n", socket, reg, val);
return val;
}
static inline void exca_writeb(struct yenta_socket *socket, unsigned reg, u8 val)
{
- debug("%p %04x %02x\n", socket, reg, val);
+ debug("%04x %02x\n", socket, reg, val);
writeb(val, socket->base + 0x800 + reg);
readb(socket->base + 0x800 + reg); /* PCI write posting... */
}
static void exca_writew(struct yenta_socket *socket, unsigned reg, u16 val)
{
- debug("%p %04x %04x\n", socket, reg, val);
+ debug("%04x %04x\n", socket, reg, val);
writeb(val, socket->base + 0x800 + reg);
writeb(val >> 8, socket->base + 0x800 + reg + 1);
@@ -207,7 +203,7 @@
if (state & CB_CBCARD) {
- val |= SS_CARDBUS;
+ val |= SS_CARDBUS;
val |= (state & CB_CARDSTS) ? SS_STSCHG : 0;
val |= (state & (CB_CDETECT1 | CB_CDETECT2)) ? 0 : SS_DETECT;
val |= (state & CB_PWRCYCLE) ? SS_POWERON | SS_READY : 0;
@@ -650,8 +646,10 @@
root = pci_find_parent_resource(socket->dev, res);
if (root && (request_resource(root, res) == 0))
return 0;
- printk(KERN_INFO "yenta %s: Preassigned resource %d busy or not available, reconfiguring...\n",
- pci_name(socket->dev), nr);
+ dev_printk(KERN_INFO, &socket->dev->dev,
+ "Preassigned resource %d busy or not available, "
+ "reconfiguring...\n",
+ nr);
}
if (type & IORESOURCE_IO) {
@@ -674,8 +672,9 @@
return 1;
}
- printk(KERN_INFO "yenta %s: no resource of type %x available, trying to continue...\n",
- pci_name(socket->dev), type);
+ dev_printk(KERN_INFO, &socket->dev->dev,
+ "no resource of type %x available, trying to continue...\n",
+ type);
res->start = res->end = res->flags = 0;
return 0;
}
@@ -923,7 +922,8 @@
socket->probe_status = 0;
if (request_irq(socket->cb_irq, yenta_probe_handler, IRQF_SHARED, "yenta", socket)) {
- printk(KERN_WARNING "Yenta: request_irq() in yenta_probe_cb_irq() failed!\n");
+ dev_printk(KERN_WARNING, &socket->dev->dev,
+ "request_irq() in yenta_probe_cb_irq() failed!\n");
return -1;
}
@@ -960,8 +960,9 @@
else
socket->socket.irq_mask = 0;
- printk(KERN_INFO "Yenta: ISA IRQ mask 0x%04x, PCI irq %d\n",
- socket->socket.irq_mask, socket->cb_irq);
+ dev_printk(KERN_INFO, &socket->dev->dev,
+ "ISA IRQ mask 0x%04x, PCI irq %d\n",
+ socket->socket.irq_mask, socket->cb_irq);
}
/*
@@ -1051,8 +1052,9 @@
/* Show that the wanted subordinate number is not possible: */
if (cardbus_bridge->subordinate > upper_limit)
- printk(KERN_WARNING "Yenta: Upper limit for fixing this "
- "bridge's parent bridge: #%02x\n", upper_limit);
+ dev_printk(KERN_WARNING, &cardbus_bridge->dev,
+ "Upper limit for fixing this "
+ "bridge's parent bridge: #%02x\n", upper_limit);
/* If we have room to increase the bridge's subordinate number, */
if (bridge_to_fix->subordinate < upper_limit) {
@@ -1061,10 +1063,11 @@
unsigned char subordinate_to_assign =
min(cardbus_bridge->subordinate, upper_limit);
- printk(KERN_INFO "Yenta: Raising subordinate bus# of parent "
- "bus (#%02x) from #%02x to #%02x\n",
- bridge_to_fix->number,
- bridge_to_fix->subordinate, subordinate_to_assign);
+ dev_printk(KERN_INFO, &bridge_to_fix->dev,
+ "Raising subordinate bus# of parent "
+ "bus (#%02x) from #%02x to #%02x\n",
+ bridge_to_fix->number,
+ bridge_to_fix->subordinate, subordinate_to_assign);
/* Save the new subordinate in the bus struct of the bridge */
bridge_to_fix->subordinate = subordinate_to_assign;
@@ -1091,8 +1094,8 @@
* Bail out if so.
*/
if (!dev->subordinate) {
- printk(KERN_ERR "Yenta: no bus associated with %s! "
- "(try 'pci=assign-busses')\n", pci_name(dev));
+ dev_printk(KERN_ERR, &dev->dev, "no bus associated! "
+ "(try 'pci=assign-busses')\n");
return -ENODEV;
}
@@ -1127,7 +1130,7 @@
goto disable;
if (!pci_resource_start(dev, 0)) {
- printk(KERN_ERR "No cardbus resource!\n");
+ dev_printk(KERN_ERR, &dev->dev, "No cardbus resource!\n");
ret = -ENODEV;
goto release;
}
@@ -1146,8 +1149,8 @@
* report the subsystem vendor and device for help debugging
* the irq stuff...
*/
- printk(KERN_INFO "Yenta: CardBus bridge found at %s [%04x:%04x]\n",
- pci_name(dev), dev->subsystem_vendor, dev->subsystem_device);
+ dev_printk(KERN_INFO, &dev->dev, "CardBus bridge found [%04x:%04x]\n",
+ dev->subsystem_vendor, dev->subsystem_device);
yenta_config_init(socket);
@@ -1179,8 +1182,12 @@
socket->poll_timer.data = (unsigned long)socket;
socket->poll_timer.expires = jiffies + HZ;
add_timer(&socket->poll_timer);
- printk(KERN_INFO "Yenta: no PCI IRQ, CardBus support disabled for this socket.\n"
- KERN_INFO "Yenta: check your BIOS CardBus, BIOS IRQ or ACPI settings.\n");
+ dev_printk(KERN_INFO, &dev->dev,
+ "no PCI IRQ, CardBus support disabled for this "
+ "socket.\n");
+ dev_printk(KERN_INFO, &dev->dev,
+ "check your BIOS CardBus, BIOS IRQ or ACPI "
+ "settings.\n");
} else {
socket->socket.features |= SS_CAP_CARDBUS;
}
@@ -1188,7 +1195,8 @@
/* Figure out what the dang thing can do for the PCMCIA layer... */
yenta_interrogate(socket);
yenta_get_socket_capabilities(socket, isa_interrupts);
- printk(KERN_INFO "Socket status: %08x\n", cb_readl(socket, CB_SOCKET_STATE));
+ dev_printk(KERN_INFO, &dev->dev,
+ "Socket status: %08x\n", cb_readl(socket, CB_SOCKET_STATE));
yenta_fixup_parent_bridge(dev->subordinate);
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 1982f8b..63bb579 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -58,7 +58,7 @@
config BATTERY_WM97XX
bool "WM97xx generic battery driver"
- depends on TOUCHSCREEN_WM97XX
+ depends on TOUCHSCREEN_WM97XX=y
help
Say Y to enable support for battery measured by WM97xx aux port.
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 9a9755c..b57fba5 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -329,7 +329,7 @@
config RTC_DRV_CMOS
tristate "PC-style 'CMOS'"
- depends on X86 || ALPHA || ARM || M32R || ATARI || PPC || MIPS
+ depends on X86 || ALPHA || ARM || M32R || ATARI || PPC || MIPS || SPARC64
default y if X86
help
Say "yes" here to get direct support for the real time clock
@@ -406,14 +406,26 @@
will be called rtc-m48t86.
config RTC_DRV_M48T59
- tristate "ST M48T59"
+ tristate "ST M48T59/M48T08/M48T02"
help
If you say Y here you will get support for the
- ST M48T59 RTC chip.
+ ST M48T59 RTC chip and compatible ST M48T08 and M48T02.
+
+ These chips are usually found in Sun SPARC and UltraSPARC
+ workstations.
This driver can also be built as a module, if so, the module
will be called "rtc-m48t59".
+config RTC_DRV_BQ4802
+ tristate "TI BQ4802"
+ help
+ If you say Y here you will get support for the TI
+ BQ4802 RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-bq4802.
+
config RTC_DRV_V3020
tristate "EM Microelectronic V3020"
help
@@ -583,4 +595,18 @@
the RTC. This exposes that functionality through the generic RTC
class.
+config RTC_DRV_SUN4V
+ bool "SUN4V Hypervisor RTC"
+ depends on SPARC64
+ help
+ If you say Y here you will get support for the Hypervisor
+ based RTC on SUN4V systems.
+
+config RTC_DRV_STARFIRE
+ bool "Starfire RTC"
+ depends on SPARC64
+ help
+ If you say Y here you will get support for the RTC found on
+ Starfire systems.
+
endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 18622ef..10f41f8 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -38,6 +38,9 @@
obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o
obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o
obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o
+obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o
+obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o
+obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o
obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o
obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
diff --git a/drivers/rtc/rtc-bq4802.c b/drivers/rtc/rtc-bq4802.c
new file mode 100644
index 0000000..189a018
--- /dev/null
+++ b/drivers/rtc/rtc-bq4802.c
@@ -0,0 +1,230 @@
+/* rtc-bq4802.c: TI BQ4802 RTC driver.
+ *
+ * Copyright (C) 2008 David S. Miller <davem@davemloft.net>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+
+MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
+MODULE_DESCRIPTION("TI BQ4802 RTC driver");
+MODULE_LICENSE("GPL");
+
+struct bq4802 {
+ void __iomem *regs;
+ unsigned long ioport;
+ struct rtc_device *rtc;
+ spinlock_t lock;
+ struct resource *r;
+ u8 (*read)(struct bq4802 *, int);
+ void (*write)(struct bq4802 *, int, u8);
+};
+
+static u8 bq4802_read_io(struct bq4802 *p, int off)
+{
+ return inb(p->ioport + off);
+}
+
+static void bq4802_write_io(struct bq4802 *p, int off, u8 val)
+{
+ outb(val, p->ioport + off);
+}
+
+static u8 bq4802_read_mem(struct bq4802 *p, int off)
+{
+ return readb(p->regs + off);
+}
+
+static void bq4802_write_mem(struct bq4802 *p, int off, u8 val)
+{
+ writeb(val, p->regs + off);
+}
+
+static int bq4802_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct bq4802 *p = platform_get_drvdata(pdev);
+ unsigned long flags;
+ unsigned int century;
+ u8 val;
+
+ spin_lock_irqsave(&p->lock, flags);
+
+ val = p->read(p, 0x0e);
+ p->write(p, 0xe, val | 0x08);
+
+ tm->tm_sec = p->read(p, 0x00);
+ tm->tm_min = p->read(p, 0x02);
+ tm->tm_hour = p->read(p, 0x04);
+ tm->tm_mday = p->read(p, 0x06);
+ tm->tm_mon = p->read(p, 0x09);
+ tm->tm_year = p->read(p, 0x0a);
+ tm->tm_wday = p->read(p, 0x08);
+ century = p->read(p, 0x0f);
+
+ p->write(p, 0x0e, val);
+
+ spin_unlock_irqrestore(&p->lock, flags);
+
+ BCD_TO_BIN(tm->tm_sec);
+ BCD_TO_BIN(tm->tm_min);
+ BCD_TO_BIN(tm->tm_hour);
+ BCD_TO_BIN(tm->tm_mday);
+ BCD_TO_BIN(tm->tm_mon);
+ BCD_TO_BIN(tm->tm_year);
+ BCD_TO_BIN(tm->tm_wday);
+ BCD_TO_BIN(century);
+
+ tm->tm_year += (century * 100);
+ tm->tm_year -= 1900;
+
+ tm->tm_mon--;
+
+ return 0;
+}
+
+static int bq4802_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct bq4802 *p = platform_get_drvdata(pdev);
+ u8 sec, min, hrs, day, mon, yrs, century, val;
+ unsigned long flags;
+ unsigned int year;
+
+ year = tm->tm_year + 1900;
+ century = year / 100;
+ yrs = year % 100;
+
+ mon = tm->tm_mon + 1; /* tm_mon starts at zero */
+ day = tm->tm_mday;
+ hrs = tm->tm_hour;
+ min = tm->tm_min;
+ sec = tm->tm_sec;
+
+ BIN_TO_BCD(sec);
+ BIN_TO_BCD(min);
+ BIN_TO_BCD(hrs);
+ BIN_TO_BCD(day);
+ BIN_TO_BCD(mon);
+ BIN_TO_BCD(yrs);
+ BIN_TO_BCD(century);
+
+ spin_lock_irqsave(&p->lock, flags);
+
+ val = p->read(p, 0x0e);
+ p->write(p, 0x0e, val | 0x08);
+
+ p->write(p, 0x00, sec);
+ p->write(p, 0x02, min);
+ p->write(p, 0x04, hrs);
+ p->write(p, 0x06, day);
+ p->write(p, 0x09, mon);
+ p->write(p, 0x0a, yrs);
+ p->write(p, 0x0f, century);
+
+ p->write(p, 0x0e, val);
+
+ spin_unlock_irqrestore(&p->lock, flags);
+
+ return 0;
+}
+
+static const struct rtc_class_ops bq4802_ops = {
+ .read_time = bq4802_read_time,
+ .set_time = bq4802_set_time,
+};
+
+static int __devinit bq4802_probe(struct platform_device *pdev)
+{
+ struct bq4802 *p = kzalloc(sizeof(*p), GFP_KERNEL);
+ int err = -ENOMEM;
+
+ if (!p)
+ goto out;
+
+ spin_lock_init(&p->lock);
+
+ p->r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!p->r) {
+ p->r = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ err = -EINVAL;
+ if (!p->r)
+ goto out_free;
+ }
+ if (p->r->flags & IORESOURCE_IO) {
+ p->ioport = p->r->start;
+ p->read = bq4802_read_io;
+ p->write = bq4802_write_io;
+ } else if (p->r->flags & IORESOURCE_MEM) {
+ p->regs = ioremap(p->r->start, resource_size(p->r));
+ p->read = bq4802_read_mem;
+ p->write = bq4802_write_mem;
+ } else {
+ err = -EINVAL;
+ goto out_free;
+ }
+
+ p->rtc = rtc_device_register("bq4802", &pdev->dev,
+ &bq4802_ops, THIS_MODULE);
+ if (IS_ERR(p->rtc)) {
+ err = PTR_ERR(p->rtc);
+ goto out_iounmap;
+ }
+
+ platform_set_drvdata(pdev, p);
+ err = 0;
+out:
+ return err;
+
+out_iounmap:
+ if (p->r->flags & IORESOURCE_MEM)
+ iounmap(p->regs);
+out_free:
+ kfree(p);
+ goto out;
+}
+
+static int __devexit bq4802_remove(struct platform_device *pdev)
+{
+ struct bq4802 *p = platform_get_drvdata(pdev);
+
+ rtc_device_unregister(p->rtc);
+ if (p->r->flags & IORESOURCE_MEM)
+ iounmap(p->regs);
+
+ platform_set_drvdata(pdev, NULL);
+
+ kfree(p);
+
+ return 0;
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:rtc-bq4802");
+
+static struct platform_driver bq4802_driver = {
+ .driver = {
+ .name = "rtc-bq4802",
+ .owner = THIS_MODULE,
+ },
+ .probe = bq4802_probe,
+ .remove = __devexit_p(bq4802_remove),
+};
+
+static int __init bq4802_init(void)
+{
+ return platform_driver_register(&bq4802_driver);
+}
+
+static void __exit bq4802_exit(void)
+{
+ platform_driver_unregister(&bq4802_driver);
+}
+
+module_init(bq4802_init);
+module_exit(bq4802_exit);
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index b184367..b23af0c 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -636,7 +636,7 @@
*/
#if defined(CONFIG_ATARI)
address_space = 64;
-#elif defined(__i386__) || defined(__x86_64__) || defined(__arm__)
+#elif defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__sparc__)
address_space = 128;
#else
#warning Assuming 128 bytes of RTC+NVRAM address space, not 64 bytes.
@@ -699,7 +699,8 @@
/* FIXME teach the alarm code how to handle binary mode;
* <asm-generic/rtc.h> doesn't know 12-hour mode either.
*/
- if (!(rtc_control & RTC_24H) || (rtc_control & (RTC_DM_BINARY))) {
+ if (is_valid_irq(rtc_irq) &&
+ (!(rtc_control & RTC_24H) || (rtc_control & (RTC_DM_BINARY)))) {
dev_dbg(dev, "only 24-hr BCD mode supported\n");
retval = -ENXIO;
goto cleanup1;
diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c
index 013e6c1..ce4eff6 100644
--- a/drivers/rtc/rtc-m48t59.c
+++ b/drivers/rtc/rtc-m48t59.c
@@ -24,8 +24,9 @@
#define NO_IRQ (-1)
#endif
-#define M48T59_READ(reg) pdata->read_byte(dev, reg)
-#define M48T59_WRITE(val, reg) pdata->write_byte(dev, reg, val)
+#define M48T59_READ(reg) (pdata->read_byte(dev, pdata->offset + reg))
+#define M48T59_WRITE(val, reg) \
+ (pdata->write_byte(dev, pdata->offset + reg, val))
#define M48T59_SET_BITS(mask, reg) \
M48T59_WRITE((M48T59_READ(reg) | (mask)), (reg))
@@ -34,7 +35,6 @@
struct m48t59_private {
void __iomem *ioaddr;
- unsigned int size; /* iomem size */
int irq;
struct rtc_device *rtc;
spinlock_t lock; /* serialize the NVRAM and RTC access */
@@ -82,7 +82,8 @@
tm->tm_mday = BCD2BIN(M48T59_READ(M48T59_MDAY));
val = M48T59_READ(M48T59_WDAY);
- if ((val & M48T59_WDAY_CEB) && (val & M48T59_WDAY_CB)) {
+ if ((pdata->type == M48T59RTC_TYPE_M48T59) &&
+ (val & M48T59_WDAY_CEB) && (val & M48T59_WDAY_CB)) {
dev_dbg(dev, "Century bit is enabled\n");
tm->tm_year += 100; /* one century */
}
@@ -126,7 +127,7 @@
M48T59_WRITE((BIN2BCD(tm->tm_mon + 1) & 0x1F), M48T59_MONTH);
M48T59_WRITE(BIN2BCD(tm->tm_year % 100), M48T59_YEAR);
- if (tm->tm_year/100)
+ if (pdata->type == M48T59RTC_TYPE_M48T59 && (tm->tm_year / 100))
val = (M48T59_WDAY_CEB | M48T59_WDAY_CB);
val |= (BIN2BCD(tm->tm_wday) & 0x07);
M48T59_WRITE(val, M48T59_WDAY);
@@ -310,6 +311,11 @@
.proc = m48t59_rtc_proc,
};
+static const struct rtc_class_ops m48t02_rtc_ops = {
+ .read_time = m48t59_rtc_read_time,
+ .set_time = m48t59_rtc_set_time,
+};
+
static ssize_t m48t59_nvram_read(struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t pos, size_t size)
@@ -321,7 +327,7 @@
ssize_t cnt = 0;
unsigned long flags;
- for (; size > 0 && pos < M48T59_NVRAM_SIZE; cnt++, size--) {
+ for (; size > 0 && pos < pdata->offset; cnt++, size--) {
spin_lock_irqsave(&m48t59->lock, flags);
*buf++ = M48T59_READ(cnt);
spin_unlock_irqrestore(&m48t59->lock, flags);
@@ -341,7 +347,7 @@
ssize_t cnt = 0;
unsigned long flags;
- for (; size > 0 && pos < M48T59_NVRAM_SIZE; cnt++, size--) {
+ for (; size > 0 && pos < pdata->offset; cnt++, size--) {
spin_lock_irqsave(&m48t59->lock, flags);
M48T59_WRITE(*buf++, cnt);
spin_unlock_irqrestore(&m48t59->lock, flags);
@@ -358,7 +364,6 @@
},
.read = m48t59_nvram_read,
.write = m48t59_nvram_write,
- .size = M48T59_NVRAM_SIZE,
};
static int __devinit m48t59_rtc_probe(struct platform_device *pdev)
@@ -367,6 +372,8 @@
struct m48t59_private *m48t59 = NULL;
struct resource *res;
int ret = -ENOMEM;
+ char *name;
+ const struct rtc_class_ops *ops;
/* This chip could be memory-mapped or I/O-mapped */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -391,6 +398,8 @@
/* Ensure we only kmalloc platform data once */
pdev->dev.platform_data = pdata;
}
+ if (!pdata->type)
+ pdata->type = M48T59RTC_TYPE_M48T59;
/* Try to use the generic memory read/write ops */
if (!pdata->write_byte)
@@ -403,10 +412,14 @@
if (!m48t59)
return -ENOMEM;
- m48t59->size = res->end - res->start + 1;
- m48t59->ioaddr = ioremap(res->start, m48t59->size);
- if (!m48t59->ioaddr)
- goto out;
+ m48t59->ioaddr = pdata->ioaddr;
+
+ if (!m48t59->ioaddr) {
+ /* ioaddr not mapped externally */
+ m48t59->ioaddr = ioremap(res->start, res->end - res->start + 1);
+ if (!m48t59->ioaddr)
+ goto out;
+ }
/* Try to get irq number. We also can work in
* the mode without IRQ.
@@ -421,14 +434,36 @@
if (ret)
goto out;
}
+ switch (pdata->type) {
+ case M48T59RTC_TYPE_M48T59:
+ name = "m48t59";
+ ops = &m48t59_rtc_ops;
+ pdata->offset = 0x1ff0;
+ break;
+ case M48T59RTC_TYPE_M48T02:
+ name = "m48t02";
+ ops = &m48t02_rtc_ops;
+ pdata->offset = 0x7f0;
+ break;
+ case M48T59RTC_TYPE_M48T08:
+ name = "m48t08";
+ ops = &m48t02_rtc_ops;
+ pdata->offset = 0x1ff0;
+ break;
+ default:
+ dev_err(&pdev->dev, "Unknown RTC type\n");
+ ret = -ENODEV;
+ goto out;
+ }
- m48t59->rtc = rtc_device_register("m48t59", &pdev->dev,
- &m48t59_rtc_ops, THIS_MODULE);
+ m48t59->rtc = rtc_device_register(name, &pdev->dev, ops, THIS_MODULE);
if (IS_ERR(m48t59->rtc)) {
ret = PTR_ERR(m48t59->rtc);
goto out;
}
+ m48t59_nvram_attr.size = pdata->offset;
+
ret = sysfs_create_bin_file(&pdev->dev.kobj, &m48t59_nvram_attr);
if (ret)
goto out;
@@ -452,11 +487,12 @@
static int __devexit m48t59_rtc_remove(struct platform_device *pdev)
{
struct m48t59_private *m48t59 = platform_get_drvdata(pdev);
+ struct m48t59_plat_data *pdata = pdev->dev.platform_data;
sysfs_remove_bin_file(&pdev->dev.kobj, &m48t59_nvram_attr);
if (!IS_ERR(m48t59->rtc))
rtc_device_unregister(m48t59->rtc);
- if (m48t59->ioaddr)
+ if (m48t59->ioaddr && !pdata->ioaddr)
iounmap(m48t59->ioaddr);
if (m48t59->irq != NO_IRQ)
free_irq(m48t59->irq, &pdev->dev);
@@ -491,5 +527,5 @@
module_exit(m48t59_rtc_exit);
MODULE_AUTHOR("Mark Zhan <rongkai.zhan@windriver.com>");
-MODULE_DESCRIPTION("M48T59 RTC driver");
+MODULE_DESCRIPTION("M48T59/M48T02/M48T08 RTC driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-starfire.c b/drivers/rtc/rtc-starfire.c
new file mode 100644
index 0000000..7ccb0dd
--- /dev/null
+++ b/drivers/rtc/rtc-starfire.c
@@ -0,0 +1,120 @@
+/* rtc-starfire.c: Starfire platform RTC driver.
+ *
+ * Copyright (C) 2008 David S. Miller <davem@davemloft.net>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+
+#include <asm/oplib.h>
+
+MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
+MODULE_DESCRIPTION("Starfire RTC driver");
+MODULE_LICENSE("GPL");
+
+struct starfire_rtc {
+ struct rtc_device *rtc;
+ spinlock_t lock;
+};
+
+static u32 starfire_get_time(void)
+{
+ static char obp_gettod[32];
+ static u32 unix_tod;
+
+ sprintf(obp_gettod, "h# %08x unix-gettod",
+ (unsigned int) (long) &unix_tod);
+ prom_feval(obp_gettod);
+
+ return unix_tod;
+}
+
+static int starfire_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct starfire_rtc *p = dev_get_drvdata(dev);
+ unsigned long flags, secs;
+
+ spin_lock_irqsave(&p->lock, flags);
+ secs = starfire_get_time();
+ spin_unlock_irqrestore(&p->lock, flags);
+
+ rtc_time_to_tm(secs, tm);
+
+ return 0;
+}
+
+static int starfire_set_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned long secs;
+ int err;
+
+ err = rtc_tm_to_time(tm, &secs);
+ if (err)
+ return err;
+
+ /* Do nothing, time is set using the service processor
+ * console on this platform.
+ */
+ return 0;
+}
+
+static const struct rtc_class_ops starfire_rtc_ops = {
+ .read_time = starfire_read_time,
+ .set_time = starfire_set_time,
+};
+
+static int __devinit starfire_rtc_probe(struct platform_device *pdev)
+{
+ struct starfire_rtc *p = kzalloc(sizeof(*p), GFP_KERNEL);
+
+ if (!p)
+ return -ENOMEM;
+
+ spin_lock_init(&p->lock);
+
+ p->rtc = rtc_device_register("starfire", &pdev->dev,
+ &starfire_rtc_ops, THIS_MODULE);
+ if (IS_ERR(p->rtc)) {
+ int err = PTR_ERR(p->rtc);
+ kfree(p);
+ return err;
+ }
+ platform_set_drvdata(pdev, p);
+ return 0;
+}
+
+static int __devexit starfire_rtc_remove(struct platform_device *pdev)
+{
+ struct starfire_rtc *p = platform_get_drvdata(pdev);
+
+ rtc_device_unregister(p->rtc);
+ kfree(p);
+
+ return 0;
+}
+
+static struct platform_driver starfire_rtc_driver = {
+ .driver = {
+ .name = "rtc-starfire",
+ .owner = THIS_MODULE,
+ },
+ .probe = starfire_rtc_probe,
+ .remove = __devexit_p(starfire_rtc_remove),
+};
+
+static int __init starfire_rtc_init(void)
+{
+ return platform_driver_register(&starfire_rtc_driver);
+}
+
+static void __exit starfire_rtc_exit(void)
+{
+ platform_driver_unregister(&starfire_rtc_driver);
+}
+
+module_init(starfire_rtc_init);
+module_exit(starfire_rtc_exit);
diff --git a/drivers/rtc/rtc-sun4v.c b/drivers/rtc/rtc-sun4v.c
new file mode 100644
index 0000000..2012ccb
--- /dev/null
+++ b/drivers/rtc/rtc-sun4v.c
@@ -0,0 +1,153 @@
+/* rtc-sun4c.c: Hypervisor based RTC for SUN4V systems.
+ *
+ * Copyright (C) 2008 David S. Miller <davem@davemloft.net>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+
+#include <asm/hypervisor.h>
+
+MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
+MODULE_DESCRIPTION("SUN4V RTC driver");
+MODULE_LICENSE("GPL");
+
+struct sun4v_rtc {
+ struct rtc_device *rtc;
+ spinlock_t lock;
+};
+
+static unsigned long hypervisor_get_time(void)
+{
+ unsigned long ret, time;
+ int retries = 10000;
+
+retry:
+ ret = sun4v_tod_get(&time);
+ if (ret == HV_EOK)
+ return time;
+ if (ret == HV_EWOULDBLOCK) {
+ if (--retries > 0) {
+ udelay(100);
+ goto retry;
+ }
+ printk(KERN_WARNING "SUN4V: tod_get() timed out.\n");
+ return 0;
+ }
+ printk(KERN_WARNING "SUN4V: tod_get() not supported.\n");
+ return 0;
+}
+
+static int sun4v_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct sun4v_rtc *p = dev_get_drvdata(dev);
+ unsigned long flags, secs;
+
+ spin_lock_irqsave(&p->lock, flags);
+ secs = hypervisor_get_time();
+ spin_unlock_irqrestore(&p->lock, flags);
+
+ rtc_time_to_tm(secs, tm);
+
+ return 0;
+}
+
+static int hypervisor_set_time(unsigned long secs)
+{
+ unsigned long ret;
+ int retries = 10000;
+
+retry:
+ ret = sun4v_tod_set(secs);
+ if (ret == HV_EOK)
+ return 0;
+ if (ret == HV_EWOULDBLOCK) {
+ if (--retries > 0) {
+ udelay(100);
+ goto retry;
+ }
+ printk(KERN_WARNING "SUN4V: tod_set() timed out.\n");
+ return -EAGAIN;
+ }
+ printk(KERN_WARNING "SUN4V: tod_set() not supported.\n");
+ return -EOPNOTSUPP;
+}
+
+static int sun4v_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct sun4v_rtc *p = dev_get_drvdata(dev);
+ unsigned long flags, secs;
+ int err;
+
+ err = rtc_tm_to_time(tm, &secs);
+ if (err)
+ return err;
+
+ spin_lock_irqsave(&p->lock, flags);
+ err = hypervisor_set_time(secs);
+ spin_unlock_irqrestore(&p->lock, flags);
+
+ return err;
+}
+
+static const struct rtc_class_ops sun4v_rtc_ops = {
+ .read_time = sun4v_read_time,
+ .set_time = sun4v_set_time,
+};
+
+static int __devinit sun4v_rtc_probe(struct platform_device *pdev)
+{
+ struct sun4v_rtc *p = kzalloc(sizeof(*p), GFP_KERNEL);
+
+ if (!p)
+ return -ENOMEM;
+
+ spin_lock_init(&p->lock);
+
+ p->rtc = rtc_device_register("sun4v", &pdev->dev,
+ &sun4v_rtc_ops, THIS_MODULE);
+ if (IS_ERR(p->rtc)) {
+ int err = PTR_ERR(p->rtc);
+ kfree(p);
+ return err;
+ }
+ platform_set_drvdata(pdev, p);
+ return 0;
+}
+
+static int __devexit sun4v_rtc_remove(struct platform_device *pdev)
+{
+ struct sun4v_rtc *p = platform_get_drvdata(pdev);
+
+ rtc_device_unregister(p->rtc);
+ kfree(p);
+
+ return 0;
+}
+
+static struct platform_driver sun4v_rtc_driver = {
+ .driver = {
+ .name = "rtc-sun4v",
+ .owner = THIS_MODULE,
+ },
+ .probe = sun4v_rtc_probe,
+ .remove = __devexit_p(sun4v_rtc_remove),
+};
+
+static int __init sun4v_rtc_init(void)
+{
+ return platform_driver_register(&sun4v_rtc_driver);
+}
+
+static void __exit sun4v_rtc_exit(void)
+{
+ platform_driver_unregister(&sun4v_rtc_driver);
+}
+
+module_init(sun4v_rtc_init);
+module_exit(sun4v_rtc_exit);
diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c
index d18e6d2..40759c3 100644
--- a/drivers/s390/char/fs3270.c
+++ b/drivers/s390/char/fs3270.c
@@ -418,25 +418,22 @@
{
struct fs3270 *fp;
struct idal_buffer *ib;
- int minor, rc;
+ int minor, rc = 0;
if (imajor(filp->f_path.dentry->d_inode) != IBM_FS3270_MAJOR)
return -ENODEV;
- lock_kernel();
minor = iminor(filp->f_path.dentry->d_inode);
/* Check for minor 0 multiplexer. */
if (minor == 0) {
- struct tty_struct *tty;
- mutex_lock(&tty_mutex);
- tty = get_current_tty();
+ struct tty_struct *tty = get_current_tty();
if (!tty || tty->driver->major != IBM_TTY3270_MAJOR) {
- mutex_unlock(&tty_mutex);
- rc = -ENODEV;
- goto out;
+ tty_kref_put(tty);
+ return -ENODEV;
}
minor = tty->index + RAW3270_FIRSTMINOR;
- mutex_unlock(&tty_mutex);
+ tty_kref_put(tty);
}
+ lock_kernel();
/* Check if some other program is already using fullscreen mode. */
fp = (struct fs3270 *) raw3270_find_view(&fs3270_fn, minor);
if (!IS_ERR(fp)) {
@@ -478,7 +475,7 @@
filp->private_data = fp;
out:
unlock_kernel();
- return 0;
+ return rc;
}
/*
diff --git a/drivers/sbus/Makefile b/drivers/sbus/Makefile
index 7b1d24d..e94dc25 100644
--- a/drivers/sbus/Makefile
+++ b/drivers/sbus/Makefile
@@ -2,8 +2,4 @@
# Makefile for the linux kernel.
#
-ifneq ($(ARCH),m68k)
-obj-y := sbus.o dvma.o
-endif
-
obj-$(CONFIG_SBUSCHAR) += char/
diff --git a/drivers/sbus/char/Kconfig b/drivers/sbus/char/Kconfig
index 400c65b..73cde85 100644
--- a/drivers/sbus/char/Kconfig
+++ b/drivers/sbus/char/Kconfig
@@ -13,16 +13,6 @@
If unsure, say Y.
-config SUN_MOSTEK_RTC
- tristate "Mostek real time clock support"
- depends on SPARC32
- help
- The Mostek RTC chip is used on all known Sun computers except
- some JavaStations. For a JavaStation you need to say Y both here
- and to "Enhanced Real Time Clock Support".
-
- Say Y here unless you are building a special purpose kernel.
-
config OBP_FLASH
tristate "OBP Flash Device support"
depends on SPARC64
@@ -30,26 +20,9 @@
The OpenBoot PROM on Ultra systems is flashable. If you want to be
able to upgrade the OBP firmware, say Y here.
-config SUN_BPP
- tristate "Bidirectional parallel port support (OBSOLETE)"
- depends on EXPERIMENTAL
- help
- Say Y here to support Sun's obsolete variant of IEEE1284
- bidirectional parallel port protocol as /dev/bppX. Can be built on
- x86 machines.
-
-config SUN_VIDEOPIX
- tristate "Videopix Frame Grabber (EXPERIMENTAL)"
- depends on EXPERIMENTAL && (BROKEN || !64BIT)
- help
- Say Y here to support the Videopix Frame Grabber from Sun
- Microsystems, commonly found on SPARCstations. This card, which is
- based on the Phillips SAA9051, can handle NTSC and PAL/SECAM and
- SVIDEO signals.
-
config TADPOLE_TS102_UCTRL
tristate "Tadpole TS102 Microcontroller support (EXPERIMENTAL)"
- depends on EXPERIMENTAL && SPARC32
+ depends on EXPERIMENTAL
help
Say Y here to directly support the TS102 Microcontroller interface
on the Tadpole Sparcbook 3. This device handles power-management
diff --git a/drivers/sbus/char/Makefile b/drivers/sbus/char/Makefile
index 7ab060e..78b6183 100644
--- a/drivers/sbus/char/Makefile
+++ b/drivers/sbus/char/Makefile
@@ -7,18 +7,12 @@
# Rewritten to use lists instead of if-statements.
#
-vfc-objs := vfc_dev.o vfc_i2c.o
bbc-objs := bbc_i2c.o bbc_envctrl.o
obj-$(CONFIG_ENVCTRL) += envctrl.o
obj-$(CONFIG_DISPLAY7SEG) += display7seg.o
-obj-$(CONFIG_WATCHDOG_CP1XXX) += cpwatchdog.o
-obj-$(CONFIG_WATCHDOG_RIO) += riowatchdog.o
obj-$(CONFIG_OBP_FLASH) += flash.o
obj-$(CONFIG_SUN_OPENPROMIO) += openprom.o
-obj-$(CONFIG_SUN_MOSTEK_RTC) += rtc.o
-obj-$(CONFIG_SUN_BPP) += bpp.o
-obj-$(CONFIG_SUN_VIDEOPIX) += vfc.o
obj-$(CONFIG_TADPOLE_TS102_UCTRL) += uctrl.o
obj-$(CONFIG_SUN_JSFLASH) += jsflash.o
obj-$(CONFIG_BBC_I2C) += bbc.o
diff --git a/drivers/sbus/char/bbc_envctrl.c b/drivers/sbus/char/bbc_envctrl.c
index 0bde269..15dab96 100644
--- a/drivers/sbus/char/bbc_envctrl.c
+++ b/drivers/sbus/char/bbc_envctrl.c
@@ -1,15 +1,15 @@
-/* $Id: bbc_envctrl.c,v 1.4 2001/04/06 16:48:08 davem Exp $
- * bbc_envctrl.c: UltraSPARC-III environment control driver.
+/* bbc_envctrl.c: UltraSPARC-III environment control driver.
*
- * Copyright (C) 2001 David S. Miller (davem@redhat.com)
+ * Copyright (C) 2001, 2008 David S. Miller (davem@davemloft.net)
*/
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/kmod.h>
#include <linux/reboot.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <asm/oplib.h>
-#include <asm/ebus.h>
#include "bbc_i2c.h"
#include "max1617.h"
@@ -75,43 +75,8 @@
{ 65, 55, 40, 5, -5, -10 },
};
-enum fan_action { FAN_SLOWER, FAN_SAME, FAN_FASTER, FAN_FULLBLAST, FAN_STATE_MAX };
-
-struct bbc_cpu_temperature {
- struct bbc_cpu_temperature *next;
-
- struct bbc_i2c_client *client;
- int index;
-
- /* Current readings, and history. */
- s8 curr_cpu_temp;
- s8 curr_amb_temp;
- s8 prev_cpu_temp;
- s8 prev_amb_temp;
- s8 avg_cpu_temp;
- s8 avg_amb_temp;
-
- int sample_tick;
-
- enum fan_action fan_todo[2];
-#define FAN_AMBIENT 0
-#define FAN_CPU 1
-};
-
-struct bbc_cpu_temperature *all_bbc_temps;
-
-struct bbc_fan_control {
- struct bbc_fan_control *next;
-
- struct bbc_i2c_client *client;
- int index;
-
- int psupply_fan_on;
- int cpu_fan_speed;
- int system_fan_speed;
-};
-
-struct bbc_fan_control *all_bbc_fans;
+static LIST_HEAD(all_temps);
+static LIST_HEAD(all_fans);
#define CPU_FAN_REG 0xf0
#define SYS_FAN_REG 0xf2
@@ -330,7 +295,7 @@
* recommend we do, and perform that action on all the
* fans.
*/
- for (tp = all_bbc_temps; tp; tp = tp->next) {
+ list_for_each_entry(tp, &all_temps, glob_list) {
if (tp->fan_todo[which_fan] == FAN_FULLBLAST) {
decision = FAN_FULLBLAST;
break;
@@ -439,7 +404,7 @@
/* Since we will not be monitoring things anymore, put
* the fans on full blast.
*/
- for (fp = all_bbc_fans; fp; fp = fp->next) {
+ list_for_each_entry(fp, &all_fans, glob_list) {
fp->cpu_fan_speed = FAN_SPEED_MAX;
fp->system_fan_speed = FAN_SPEED_MAX;
fp->psupply_fan_on = 1;
@@ -463,11 +428,11 @@
if (kthread_should_stop())
break;
- for (tp = all_bbc_temps; tp; tp = tp->next) {
+ list_for_each_entry(tp, &all_temps, glob_list) {
get_current_temps(tp);
analyze_temps(tp, &last_warning_jiffies);
}
- for (fp = all_bbc_fans; fp; fp = fp->next)
+ list_for_each_entry(fp, &all_fans, glob_list)
maybe_new_fan_speeds(fp);
}
printk(KERN_INFO "bbc_envctrl: kenvctrld exiting...\n");
@@ -477,7 +442,8 @@
return 0;
}
-static void attach_one_temp(struct linux_ebus_child *echild, int temp_idx)
+static void attach_one_temp(struct bbc_i2c_bus *bp, struct of_device *op,
+ int temp_idx)
{
struct bbc_cpu_temperature *tp;
@@ -485,20 +451,17 @@
if (!tp)
return;
- tp->client = bbc_i2c_attach(echild);
+ tp->client = bbc_i2c_attach(bp, op);
if (!tp->client) {
kfree(tp);
return;
}
+
tp->index = temp_idx;
- {
- struct bbc_cpu_temperature **tpp = &all_bbc_temps;
- while (*tpp)
- tpp = &((*tpp)->next);
- tp->next = NULL;
- *tpp = tp;
- }
+
+ list_add(&tp->glob_list, &all_temps);
+ list_add(&tp->bp_list, &bp->temps);
/* Tell it to convert once every 5 seconds, clear all cfg
* bits.
@@ -524,7 +487,8 @@
tp->fan_todo[FAN_CPU] = FAN_SAME;
}
-static void attach_one_fan(struct linux_ebus_child *echild, int fan_idx)
+static void attach_one_fan(struct bbc_i2c_bus *bp, struct of_device *op,
+ int fan_idx)
{
struct bbc_fan_control *fp;
@@ -532,7 +496,7 @@
if (!fp)
return;
- fp->client = bbc_i2c_attach(echild);
+ fp->client = bbc_i2c_attach(bp, op);
if (!fp->client) {
kfree(fp);
return;
@@ -540,13 +504,8 @@
fp->index = fan_idx;
- {
- struct bbc_fan_control **fpp = &all_bbc_fans;
- while (*fpp)
- fpp = &((*fpp)->next);
- fp->next = NULL;
- *fpp = fp;
- }
+ list_add(&fp->glob_list, &all_fans);
+ list_add(&fp->bp_list, &bp->fans);
/* The i2c device controlling the fans is write-only.
* So the only way to keep track of the current power
@@ -563,18 +522,18 @@
set_fan_speeds(fp);
}
-int bbc_envctrl_init(void)
+int bbc_envctrl_init(struct bbc_i2c_bus *bp)
{
- struct linux_ebus_child *echild;
+ struct of_device *op;
int temp_index = 0;
int fan_index = 0;
int devidx = 0;
- while ((echild = bbc_i2c_getdev(devidx++)) != NULL) {
- if (!strcmp(echild->prom_node->name, "temperature"))
- attach_one_temp(echild, temp_index++);
- if (!strcmp(echild->prom_node->name, "fan-control"))
- attach_one_fan(echild, fan_index++);
+ while ((op = bbc_i2c_getdev(bp, devidx++)) != NULL) {
+ if (!strcmp(op->node->name, "temperature"))
+ attach_one_temp(bp, op, temp_index++);
+ if (!strcmp(op->node->name, "fan-control"))
+ attach_one_fan(bp, op, fan_index++);
}
if (temp_index != 0 && fan_index != 0) {
kenvctrld_task = kthread_run(kenvctrld, NULL, "kenvctrld");
@@ -597,26 +556,22 @@
kfree(fp);
}
-void bbc_envctrl_cleanup(void)
+void bbc_envctrl_cleanup(struct bbc_i2c_bus *bp)
{
- struct bbc_cpu_temperature *tp;
- struct bbc_fan_control *fp;
+ struct bbc_cpu_temperature *tp, *tpos;
+ struct bbc_fan_control *fp, *fpos;
kthread_stop(kenvctrld_task);
- tp = all_bbc_temps;
- while (tp != NULL) {
- struct bbc_cpu_temperature *next = tp->next;
+ list_for_each_entry_safe(tp, tpos, &bp->temps, bp_list) {
+ list_del(&tp->bp_list);
+ list_del(&tp->glob_list);
destroy_one_temp(tp);
- tp = next;
}
- all_bbc_temps = NULL;
- fp = all_bbc_fans;
- while (fp != NULL) {
- struct bbc_fan_control *next = fp->next;
+ list_for_each_entry_safe(fp, fpos, &bp->fans, bp_list) {
+ list_del(&fp->bp_list);
+ list_del(&fp->glob_list);
destroy_one_fan(fp);
- fp = next;
}
- all_bbc_fans = NULL;
}
diff --git a/drivers/sbus/char/bbc_i2c.c b/drivers/sbus/char/bbc_i2c.c
index ac8ef2c..f08e169 100644
--- a/drivers/sbus/char/bbc_i2c.c
+++ b/drivers/sbus/char/bbc_i2c.c
@@ -1,8 +1,7 @@
-/* $Id: bbc_i2c.c,v 1.2 2001/04/02 09:59:08 davem Exp $
- * bbc_i2c.c: I2C low-level driver for BBC device on UltraSPARC-III
+/* bbc_i2c.c: I2C low-level driver for BBC device on UltraSPARC-III
* platforms.
*
- * Copyright (C) 2001 David S. Miller (davem@redhat.com)
+ * Copyright (C) 2001, 2008 David S. Miller (davem@davemloft.net)
*/
#include <linux/module.h>
@@ -14,9 +13,8 @@
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
-#include <asm/oplib.h>
-#include <asm/ebus.h>
-#include <asm/spitfire.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <asm/bbc.h>
#include <asm/io.h>
@@ -53,54 +51,12 @@
* The second controller also connects to the smartcard reader, if present.
*/
-#define NUM_CHILDREN 8
-struct bbc_i2c_bus {
- struct bbc_i2c_bus *next;
- int index;
- spinlock_t lock;
- void __iomem *i2c_bussel_reg;
- void __iomem *i2c_control_regs;
- unsigned char own, clock;
-
- wait_queue_head_t wq;
- volatile int waiting;
-
- struct linux_ebus_device *bus_edev;
- struct {
- struct linux_ebus_child *device;
- int client_claimed;
- } devs[NUM_CHILDREN];
-};
-
-static struct bbc_i2c_bus *all_bbc_i2c;
-
-struct bbc_i2c_client {
- struct bbc_i2c_bus *bp;
- struct linux_ebus_child *echild;
- int bus;
- int address;
-};
-
-static int find_device(struct bbc_i2c_bus *bp, struct linux_ebus_child *echild)
+static void set_device_claimage(struct bbc_i2c_bus *bp, struct of_device *op, int val)
{
int i;
for (i = 0; i < NUM_CHILDREN; i++) {
- if (bp->devs[i].device == echild) {
- if (bp->devs[i].client_claimed)
- return 0;
- return 1;
- }
- }
- return 0;
-}
-
-static void set_device_claimage(struct bbc_i2c_bus *bp, struct linux_ebus_child *echild, int val)
-{
- int i;
-
- for (i = 0; i < NUM_CHILDREN; i++) {
- if (bp->devs[i].device == echild) {
+ if (bp->devs[i].device == op) {
bp->devs[i].client_claimed = val;
return;
}
@@ -110,61 +66,47 @@
#define claim_device(BP,ECHILD) set_device_claimage(BP,ECHILD,1)
#define release_device(BP,ECHILD) set_device_claimage(BP,ECHILD,0)
-static struct bbc_i2c_bus *find_bus_for_device(struct linux_ebus_child *echild)
+struct of_device *bbc_i2c_getdev(struct bbc_i2c_bus *bp, int index)
{
- struct bbc_i2c_bus *bp = all_bbc_i2c;
+ struct of_device *op = NULL;
+ int curidx = 0, i;
- while (bp != NULL) {
- if (find_device(bp, echild) != 0)
+ for (i = 0; i < NUM_CHILDREN; i++) {
+ if (!(op = bp->devs[i].device))
break;
- bp = bp->next;
+ if (curidx == index)
+ goto out;
+ op = NULL;
+ curidx++;
}
- return bp;
-}
-
-struct linux_ebus_child *bbc_i2c_getdev(int index)
-{
- struct bbc_i2c_bus *bp = all_bbc_i2c;
- struct linux_ebus_child *echild = NULL;
- int curidx = 0;
-
- while (bp != NULL) {
- struct bbc_i2c_bus *next = bp->next;
- int i;
-
- for (i = 0; i < NUM_CHILDREN; i++) {
- if (!(echild = bp->devs[i].device))
- break;
- if (curidx == index)
- goto out;
- echild = NULL;
- curidx++;
- }
- bp = next;
- }
out:
if (curidx == index)
- return echild;
+ return op;
return NULL;
}
-struct bbc_i2c_client *bbc_i2c_attach(struct linux_ebus_child *echild)
+struct bbc_i2c_client *bbc_i2c_attach(struct bbc_i2c_bus *bp, struct of_device *op)
{
- struct bbc_i2c_bus *bp = find_bus_for_device(echild);
struct bbc_i2c_client *client;
+ const u32 *reg;
- if (!bp)
- return NULL;
client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client)
return NULL;
client->bp = bp;
- client->echild = echild;
- client->bus = echild->resource[0].start;
- client->address = echild->resource[1].start;
+ client->op = op;
- claim_device(bp, echild);
+ reg = of_get_property(op->node, "reg", NULL);
+ if (!reg) {
+ kfree(client);
+ return NULL;
+ }
+
+ client->bus = reg[0];
+ client->address = reg[1];
+
+ claim_device(bp, op);
return client;
}
@@ -172,9 +114,9 @@
void bbc_i2c_detach(struct bbc_i2c_client *client)
{
struct bbc_i2c_bus *bp = client->bp;
- struct linux_ebus_child *echild = client->echild;
+ struct of_device *op = client->op;
- release_device(bp, echild);
+ release_device(bp, op);
kfree(client);
}
@@ -355,44 +297,43 @@
writeb(I2C_PCF_IDLE, bp->i2c_control_regs + 0x0);
}
-static int __init attach_one_i2c(struct linux_ebus_device *edev, int index)
+static struct bbc_i2c_bus * __init attach_one_i2c(struct of_device *op, int index)
{
struct bbc_i2c_bus *bp;
- struct linux_ebus_child *echild;
+ struct device_node *dp;
int entry;
bp = kzalloc(sizeof(*bp), GFP_KERNEL);
if (!bp)
- return -ENOMEM;
+ return NULL;
- bp->i2c_control_regs = ioremap(edev->resource[0].start, 0x2);
+ bp->i2c_control_regs = of_ioremap(&op->resource[0], 0, 0x2, "bbc_i2c_regs");
if (!bp->i2c_control_regs)
goto fail;
- if (edev->num_addrs == 2) {
- bp->i2c_bussel_reg = ioremap(edev->resource[1].start, 0x1);
- if (!bp->i2c_bussel_reg)
- goto fail;
- }
+ bp->i2c_bussel_reg = of_ioremap(&op->resource[1], 0, 0x1, "bbc_i2c_bussel");
+ if (!bp->i2c_bussel_reg)
+ goto fail;
bp->waiting = 0;
init_waitqueue_head(&bp->wq);
- if (request_irq(edev->irqs[0], bbc_i2c_interrupt,
+ if (request_irq(op->irqs[0], bbc_i2c_interrupt,
IRQF_SHARED, "bbc_i2c", bp))
goto fail;
bp->index = index;
- bp->bus_edev = edev;
+ bp->op = op;
spin_lock_init(&bp->lock);
- bp->next = all_bbc_i2c;
- all_bbc_i2c = bp;
entry = 0;
- for (echild = edev->children;
- echild && entry < 8;
- echild = echild->next, entry++) {
- bp->devs[entry].device = echild;
+ for (dp = op->node->child;
+ dp && entry < 8;
+ dp = dp->sibling, entry++) {
+ struct of_device *child_op;
+
+ child_op = of_find_device_by_node(dp);
+ bp->devs[entry].device = child_op;
bp->devs[entry].client_claimed = 0;
}
@@ -406,86 +347,90 @@
reset_one_i2c(bp);
- return 0;
+ return bp;
fail:
if (bp->i2c_bussel_reg)
- iounmap(bp->i2c_bussel_reg);
+ of_iounmap(&op->resource[1], bp->i2c_bussel_reg, 1);
if (bp->i2c_control_regs)
- iounmap(bp->i2c_control_regs);
+ of_iounmap(&op->resource[0], bp->i2c_control_regs, 2);
kfree(bp);
- return -EINVAL;
+ return NULL;
}
-static int __init bbc_present(void)
+extern int bbc_envctrl_init(struct bbc_i2c_bus *bp);
+extern void bbc_envctrl_cleanup(struct bbc_i2c_bus *bp);
+
+static int __devinit bbc_i2c_probe(struct of_device *op,
+ const struct of_device_id *match)
{
- struct linux_ebus *ebus = NULL;
- struct linux_ebus_device *edev = NULL;
-
- for_each_ebus(ebus) {
- for_each_ebusdev(edev, ebus) {
- if (!strcmp(edev->prom_node->name, "bbc"))
- return 1;
- }
- }
- return 0;
-}
-
-extern int bbc_envctrl_init(void);
-extern void bbc_envctrl_cleanup(void);
-static void bbc_i2c_cleanup(void);
-
-static int __init bbc_i2c_init(void)
-{
- struct linux_ebus *ebus = NULL;
- struct linux_ebus_device *edev = NULL;
+ struct bbc_i2c_bus *bp;
int err, index = 0;
- if ((tlb_type != cheetah && tlb_type != cheetah_plus) ||
- !bbc_present())
- return -ENODEV;
+ bp = attach_one_i2c(op, index);
+ if (!bp)
+ return -EINVAL;
- for_each_ebus(ebus) {
- for_each_ebusdev(edev, ebus) {
- if (!strcmp(edev->prom_node->name, "i2c")) {
- if (!attach_one_i2c(edev, index))
- index++;
- }
- }
+ err = bbc_envctrl_init(bp);
+ if (err) {
+ free_irq(op->irqs[0], bp);
+ if (bp->i2c_bussel_reg)
+ of_iounmap(&op->resource[0], bp->i2c_bussel_reg, 1);
+ if (bp->i2c_control_regs)
+ of_iounmap(&op->resource[1], bp->i2c_control_regs, 2);
+ kfree(bp);
+ } else {
+ dev_set_drvdata(&op->dev, bp);
}
- if (!index)
- return -ENODEV;
-
- err = bbc_envctrl_init();
- if (err)
- bbc_i2c_cleanup();
return err;
}
-static void bbc_i2c_cleanup(void)
+static int __devexit bbc_i2c_remove(struct of_device *op)
{
- struct bbc_i2c_bus *bp = all_bbc_i2c;
+ struct bbc_i2c_bus *bp = dev_get_drvdata(&op->dev);
- bbc_envctrl_cleanup();
+ bbc_envctrl_cleanup(bp);
- while (bp != NULL) {
- struct bbc_i2c_bus *next = bp->next;
+ free_irq(op->irqs[0], bp);
- free_irq(bp->bus_edev->irqs[0], bp);
+ if (bp->i2c_bussel_reg)
+ of_iounmap(&op->resource[0], bp->i2c_bussel_reg, 1);
+ if (bp->i2c_control_regs)
+ of_iounmap(&op->resource[1], bp->i2c_control_regs, 2);
- if (bp->i2c_bussel_reg)
- iounmap(bp->i2c_bussel_reg);
- if (bp->i2c_control_regs)
- iounmap(bp->i2c_control_regs);
+ kfree(bp);
- kfree(bp);
+ return 0;
+}
- bp = next;
- }
- all_bbc_i2c = NULL;
+static const struct of_device_id bbc_i2c_match[] = {
+ {
+ .name = "i2c",
+ .compatible = "SUNW,bbc-i2c",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, bbc_i2c_match);
+
+static struct of_platform_driver bbc_i2c_driver = {
+ .name = "bbc_i2c",
+ .match_table = bbc_i2c_match,
+ .probe = bbc_i2c_probe,
+ .remove = __devexit_p(bbc_i2c_remove),
+};
+
+static int __init bbc_i2c_init(void)
+{
+ return of_register_driver(&bbc_i2c_driver, &of_bus_type);
+}
+
+static void __exit bbc_i2c_exit(void)
+{
+ of_unregister_driver(&bbc_i2c_driver);
}
module_init(bbc_i2c_init);
-module_exit(bbc_i2c_cleanup);
+module_exit(bbc_i2c_exit);
+
MODULE_LICENSE("GPL");
diff --git a/drivers/sbus/char/bbc_i2c.h b/drivers/sbus/char/bbc_i2c.h
index fb01bd1..83c4811 100644
--- a/drivers/sbus/char/bbc_i2c.h
+++ b/drivers/sbus/char/bbc_i2c.h
@@ -1,14 +1,79 @@
-/* $Id: bbc_i2c.h,v 1.2 2001/04/02 09:59:25 davem Exp $ */
#ifndef _BBC_I2C_H
#define _BBC_I2C_H
-#include <asm/ebus.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/list.h>
-struct bbc_i2c_client;
+struct bbc_i2c_client {
+ struct bbc_i2c_bus *bp;
+ struct of_device *op;
+ int bus;
+ int address;
+};
+
+enum fan_action { FAN_SLOWER, FAN_SAME, FAN_FASTER, FAN_FULLBLAST, FAN_STATE_MAX };
+
+struct bbc_cpu_temperature {
+ struct list_head bp_list;
+ struct list_head glob_list;
+
+ struct bbc_i2c_client *client;
+ int index;
+
+ /* Current readings, and history. */
+ s8 curr_cpu_temp;
+ s8 curr_amb_temp;
+ s8 prev_cpu_temp;
+ s8 prev_amb_temp;
+ s8 avg_cpu_temp;
+ s8 avg_amb_temp;
+
+ int sample_tick;
+
+ enum fan_action fan_todo[2];
+#define FAN_AMBIENT 0
+#define FAN_CPU 1
+};
+
+struct bbc_fan_control {
+ struct list_head bp_list;
+ struct list_head glob_list;
+
+ struct bbc_i2c_client *client;
+ int index;
+
+ int psupply_fan_on;
+ int cpu_fan_speed;
+ int system_fan_speed;
+};
+
+#define NUM_CHILDREN 8
+
+struct bbc_i2c_bus {
+ struct bbc_i2c_bus *next;
+ int index;
+ spinlock_t lock;
+ void __iomem *i2c_bussel_reg;
+ void __iomem *i2c_control_regs;
+ unsigned char own, clock;
+
+ wait_queue_head_t wq;
+ volatile int waiting;
+
+ struct list_head temps;
+ struct list_head fans;
+
+ struct of_device *op;
+ struct {
+ struct of_device *device;
+ int client_claimed;
+ } devs[NUM_CHILDREN];
+};
/* Probing and attachment. */
-extern struct linux_ebus_child *bbc_i2c_getdev(int);
-extern struct bbc_i2c_client *bbc_i2c_attach(struct linux_ebus_child *);
+extern struct of_device *bbc_i2c_getdev(struct bbc_i2c_bus *, int);
+extern struct bbc_i2c_client *bbc_i2c_attach(struct bbc_i2c_bus *bp, struct of_device *);
extern void bbc_i2c_detach(struct bbc_i2c_client *);
/* Register read/write. NOTE: Blocking! */
diff --git a/drivers/sbus/char/bpp.c b/drivers/sbus/char/bpp.c
deleted file mode 100644
index bba21e0..0000000
--- a/drivers/sbus/char/bpp.c
+++ /dev/null
@@ -1,1055 +0,0 @@
-/*
- * drivers/sbus/char/bpp.c
- *
- * Copyright (c) 1995 Picture Elements
- * Stephen Williams (steve@icarus.com)
- * Gus Baldauf (gbaldauf@ix.netcom.com)
- *
- * Linux/SPARC port by Peter Zaitcev.
- * Integration into SPARC tree by Tom Dyas.
- */
-
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/fs.h>
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/spinlock.h>
-#include <linux/timer.h>
-#include <linux/ioport.h>
-#include <linux/major.h>
-#include <linux/smp_lock.h>
-
-#include <asm/uaccess.h>
-#include <asm/io.h>
-
-#if defined(__i386__)
-# include <asm/system.h>
-#endif
-
-#if defined(__sparc__)
-# include <linux/init.h>
-# include <linux/delay.h> /* udelay() */
-
-# include <asm/oplib.h> /* OpenProm Library */
-# include <asm/sbus.h>
-#endif
-
-#include <asm/bpp.h>
-
-#define BPP_PROBE_CODE 0x55
-#define BPP_DELAY 100
-
-static const unsigned BPP_MAJOR = LP_MAJOR;
-static const char *bpp_dev_name = "bpp";
-
-/* When switching from compatibility to a mode where I can read, try
- the following mode first. */
-
-/* const unsigned char DEFAULT_ECP = 0x10; */
-static const unsigned char DEFAULT_ECP = 0x30;
-static const unsigned char DEFAULT_NIBBLE = 0x00;
-
-/*
- * These are 1284 time constraints, in units of jiffies.
- */
-
-static const unsigned long TIME_PSetup = 1;
-static const unsigned long TIME_PResponse = 6;
-static const unsigned long TIME_IDLE_LIMIT = 2000;
-
-/*
- * One instance per supported subdevice...
- */
-# define BPP_NO 3
-
-enum IEEE_Mode { COMPATIBILITY, NIBBLE, ECP, ECP_RLE, EPP };
-
-struct inst {
- unsigned present : 1; /* True if the hardware exists */
- unsigned enhanced : 1; /* True if the hardware in "enhanced" */
- unsigned opened : 1; /* True if the device is opened already */
- unsigned run_flag : 1; /* True if waiting for a repeate byte */
-
- unsigned char direction; /* 0 --> out, 0x20 --> IN */
- unsigned char pp_state; /* State of host controlled pins. */
- enum IEEE_Mode mode;
-
- unsigned char run_length;
- unsigned char repeat_byte;
-};
-
-static struct inst instances[BPP_NO];
-
-#if defined(__i386__)
-
-static const unsigned short base_addrs[BPP_NO] = { 0x278, 0x378, 0x3bc };
-
-/*
- * These are for data access.
- * Control lines accesses are hidden in set_bits() and get_bits().
- * The exception is the probe procedure, which is system-dependent.
- */
-#define bpp_outb_p(data, base) outb_p((data), (base))
-#define bpp_inb(base) inb(base)
-#define bpp_inb_p(base) inb_p(base)
-
-/*
- * This method takes the pin values mask and sets the hardware pins to
- * the requested value: 1 == high voltage, 0 == low voltage. This
- * burries the annoying PC bit inversion and preserves the direction
- * flag.
- */
-static void set_pins(unsigned short pins, unsigned minor)
-{
- unsigned char bits = instances[minor].direction; /* == 0x20 */
-
- if (! (pins & BPP_PP_nStrobe)) bits |= 1;
- if (! (pins & BPP_PP_nAutoFd)) bits |= 2;
- if ( pins & BPP_PP_nInit) bits |= 4;
- if (! (pins & BPP_PP_nSelectIn)) bits |= 8;
-
- instances[minor].pp_state = bits;
-
- outb_p(bits, base_addrs[minor]+2);
-}
-
-static unsigned short get_pins(unsigned minor)
-{
- unsigned short bits = 0;
-
- unsigned value = instances[minor].pp_state;
- if (! (value & 0x01)) bits |= BPP_PP_nStrobe;
- if (! (value & 0x02)) bits |= BPP_PP_nAutoFd;
- if (value & 0x04) bits |= BPP_PP_nInit;
- if (! (value & 0x08)) bits |= BPP_PP_nSelectIn;
-
- value = inb_p(base_addrs[minor]+1);
- if (value & 0x08) bits |= BPP_GP_nFault;
- if (value & 0x10) bits |= BPP_GP_Select;
- if (value & 0x20) bits |= BPP_GP_PError;
- if (value & 0x40) bits |= BPP_GP_nAck;
- if (! (value & 0x80)) bits |= BPP_GP_Busy;
-
- return bits;
-}
-
-#endif /* __i386__ */
-
-#if defined(__sparc__)
-
-/*
- * Register block
- */
- /* DMA registers */
-#define BPP_CSR 0x00
-#define BPP_ADDR 0x04
-#define BPP_BCNT 0x08
-#define BPP_TST_CSR 0x0C
- /* Parallel Port registers */
-#define BPP_HCR 0x10
-#define BPP_OCR 0x12
-#define BPP_DR 0x14
-#define BPP_TCR 0x15
-#define BPP_OR 0x16
-#define BPP_IR 0x17
-#define BPP_ICR 0x18
-#define BPP_SIZE 0x1A
-
-/* BPP_CSR. Bits of type RW1 are cleared with writing '1'. */
-#define P_DEV_ID_MASK 0xf0000000 /* R */
-#define P_DEV_ID_ZEBRA 0x40000000
-#define P_DEV_ID_L64854 0xa0000000 /* == NCR 89C100+89C105. Pity. */
-#define P_NA_LOADED 0x08000000 /* R NA wirtten but was not used */
-#define P_A_LOADED 0x04000000 /* R */
-#define P_DMA_ON 0x02000000 /* R DMA is not disabled */
-#define P_EN_NEXT 0x01000000 /* RW */
-#define P_TCI_DIS 0x00800000 /* RW TCI forbidden from interrupts */
-#define P_DIAG 0x00100000 /* RW Disables draining and resetting
- of P-FIFO on loading of P_ADDR*/
-#define P_BURST_SIZE 0x000c0000 /* RW SBus burst size */
-#define P_BURST_8 0x00000000
-#define P_BURST_4 0x00040000
-#define P_BURST_1 0x00080000 /* "No burst" write */
-#define P_TC 0x00004000 /* RW1 Term Count, can be cleared when
- P_EN_NEXT=1 */
-#define P_EN_CNT 0x00002000 /* RW */
-#define P_EN_DMA 0x00000200 /* RW */
-#define P_WRITE 0x00000100 /* R DMA dir, 1=to ram, 0=to port */
-#define P_RESET 0x00000080 /* RW */
-#define P_SLAVE_ERR 0x00000040 /* RW1 Access size error */
-#define P_INVALIDATE 0x00000020 /* W Drop P-FIFO */
-#define P_INT_EN 0x00000010 /* RW OK to P_INT_PEND||P_ERR_PEND */
-#define P_DRAINING 0x0000000c /* R P-FIFO is draining to memory */
-#define P_ERR_PEND 0x00000002 /* R */
-#define P_INT_PEND 0x00000001 /* R */
-
-/* BPP_HCR. Time is in increments of SBus clock. */
-#define P_HCR_TEST 0x8000 /* Allows buried counters to be read */
-#define P_HCR_DSW 0x7f00 /* Data strobe width (in ticks) */
-#define P_HCR_DDS 0x007f /* Data setup before strobe (in ticks) */
-
-/* BPP_OCR. */
-#define P_OCR_MEM_CLR 0x8000
-#define P_OCR_DATA_SRC 0x4000 /* ) */
-#define P_OCR_DS_DSEL 0x2000 /* ) Bidirectional */
-#define P_OCR_BUSY_DSEL 0x1000 /* ) selects */
-#define P_OCR_ACK_DSEL 0x0800 /* ) */
-#define P_OCR_EN_DIAG 0x0400
-#define P_OCR_BUSY_OP 0x0200 /* Busy operation */
-#define P_OCR_ACK_OP 0x0100 /* Ack operation */
-#define P_OCR_SRST 0x0080 /* Reset state machines. Not selfcleaning. */
-#define P_OCR_IDLE 0x0008 /* PP data transfer state machine is idle */
-#define P_OCR_V_ILCK 0x0002 /* Versatec faded. Zebra only. */
-#define P_OCR_EN_VER 0x0001 /* Enable Versatec (0 - enable). Zebra only. */
-
-/* BPP_TCR */
-#define P_TCR_DIR 0x08
-#define P_TCR_BUSY 0x04
-#define P_TCR_ACK 0x02
-#define P_TCR_DS 0x01 /* Strobe */
-
-/* BPP_OR */
-#define P_OR_V3 0x20 /* ) */
-#define P_OR_V2 0x10 /* ) on Zebra only */
-#define P_OR_V1 0x08 /* ) */
-#define P_OR_INIT 0x04
-#define P_OR_AFXN 0x02 /* Auto Feed */
-#define P_OR_SLCT_IN 0x01
-
-/* BPP_IR */
-#define P_IR_PE 0x04
-#define P_IR_SLCT 0x02
-#define P_IR_ERR 0x01
-
-/* BPP_ICR */
-#define P_DS_IRQ 0x8000 /* RW1 */
-#define P_ACK_IRQ 0x4000 /* RW1 */
-#define P_BUSY_IRQ 0x2000 /* RW1 */
-#define P_PE_IRQ 0x1000 /* RW1 */
-#define P_SLCT_IRQ 0x0800 /* RW1 */
-#define P_ERR_IRQ 0x0400 /* RW1 */
-#define P_DS_IRQ_EN 0x0200 /* RW Always on rising edge */
-#define P_ACK_IRQ_EN 0x0100 /* RW Always on rising edge */
-#define P_BUSY_IRP 0x0080 /* RW 1= rising edge */
-#define P_BUSY_IRQ_EN 0x0040 /* RW */
-#define P_PE_IRP 0x0020 /* RW 1= rising edge */
-#define P_PE_IRQ_EN 0x0010 /* RW */
-#define P_SLCT_IRP 0x0008 /* RW 1= rising edge */
-#define P_SLCT_IRQ_EN 0x0004 /* RW */
-#define P_ERR_IRP 0x0002 /* RW1 1= rising edge */
-#define P_ERR_IRQ_EN 0x0001 /* RW */
-
-static void __iomem *base_addrs[BPP_NO];
-
-#define bpp_outb_p(data, base) sbus_writeb(data, (base) + BPP_DR)
-#define bpp_inb_p(base) sbus_readb((base) + BPP_DR)
-#define bpp_inb(base) sbus_readb((base) + BPP_DR)
-
-static void set_pins(unsigned short pins, unsigned minor)
-{
- void __iomem *base = base_addrs[minor];
- unsigned char bits_tcr = 0, bits_or = 0;
-
- if (instances[minor].direction & 0x20) bits_tcr |= P_TCR_DIR;
- if ( pins & BPP_PP_nStrobe) bits_tcr |= P_TCR_DS;
-
- if ( pins & BPP_PP_nAutoFd) bits_or |= P_OR_AFXN;
- if (! (pins & BPP_PP_nInit)) bits_or |= P_OR_INIT;
- if (! (pins & BPP_PP_nSelectIn)) bits_or |= P_OR_SLCT_IN;
-
- sbus_writeb(bits_or, base + BPP_OR);
- sbus_writeb(bits_tcr, base + BPP_TCR);
-}
-
-/*
- * i386 people read output pins from a software image.
- * We may get them back from hardware.
- * Again, inversion of pins must he buried here.
- */
-static unsigned short get_pins(unsigned minor)
-{
- void __iomem *base = base_addrs[minor];
- unsigned short bits = 0;
- unsigned value_tcr = sbus_readb(base + BPP_TCR);
- unsigned value_ir = sbus_readb(base + BPP_IR);
- unsigned value_or = sbus_readb(base + BPP_OR);
-
- if (value_tcr & P_TCR_DS) bits |= BPP_PP_nStrobe;
- if (value_or & P_OR_AFXN) bits |= BPP_PP_nAutoFd;
- if (! (value_or & P_OR_INIT)) bits |= BPP_PP_nInit;
- if (! (value_or & P_OR_SLCT_IN)) bits |= BPP_PP_nSelectIn;
-
- if (value_ir & P_IR_ERR) bits |= BPP_GP_nFault;
- if (! (value_ir & P_IR_SLCT)) bits |= BPP_GP_Select;
- if (! (value_ir & P_IR_PE)) bits |= BPP_GP_PError;
- if (! (value_tcr & P_TCR_ACK)) bits |= BPP_GP_nAck;
- if (value_tcr & P_TCR_BUSY) bits |= BPP_GP_Busy;
-
- return bits;
-}
-
-#endif /* __sparc__ */
-
-static void snooze(unsigned long snooze_time, unsigned minor)
-{
- schedule_timeout_uninterruptible(snooze_time + 1);
-}
-
-static int wait_for(unsigned short set, unsigned short clr,
- unsigned long delay, unsigned minor)
-{
- unsigned short pins = get_pins(minor);
-
- unsigned long extime = 0;
-
- /*
- * Try a real fast scan for the first jiffy, in case the device
- * responds real good. The first while loop guesses an expire
- * time accounting for possible wraparound of jiffies.
- */
- while (time_after_eq(jiffies, extime)) extime = jiffies + 1;
- while ( (time_before(jiffies, extime))
- && (((pins & set) != set) || ((pins & clr) != 0)) ) {
- pins = get_pins(minor);
- }
-
- delay -= 1;
-
- /*
- * If my delay expired or the pins are still not where I want
- * them, then resort to using the timer and greatly reduce my
- * sample rate. If the peripheral is going to be slow, this will
- * give the CPU up to some more worthy process.
- */
- while ( delay && (((pins & set) != set) || ((pins & clr) != 0)) ) {
-
- snooze(1, minor);
- pins = get_pins(minor);
- delay -= 1;
- }
-
- if (delay == 0) return -1;
- else return pins;
-}
-
-/*
- * Return ZERO(0) If the negotiation succeeds, an errno otherwise. An
- * errno means something broke, and I do not yet know how to fix it.
- */
-static int negotiate(unsigned char mode, unsigned minor)
-{
- int rc;
- unsigned short pins = get_pins(minor);
- if (pins & BPP_PP_nSelectIn) return -EIO;
-
-
- /* Event 0: Write the mode to the data lines */
- bpp_outb_p(mode, base_addrs[minor]);
-
- snooze(TIME_PSetup, minor);
-
- /* Event 1: Strobe the mode code into the peripheral */
- set_pins(BPP_PP_nSelectIn|BPP_PP_nStrobe|BPP_PP_nInit, minor);
-
- /* Wait for Event 2: Peripheral responds as a 1284 device. */
- rc = wait_for(BPP_GP_PError|BPP_GP_Select|BPP_GP_nFault,
- BPP_GP_nAck,
- TIME_PResponse,
- minor);
-
- if (rc == -1) return -ETIMEDOUT;
-
- /* Event 3: latch extensibility request */
- set_pins(BPP_PP_nSelectIn|BPP_PP_nInit, minor);
-
- /* ... quick nap while peripheral ponders the byte i'm sending...*/
- snooze(1, minor);
-
- /* Event 4: restore strobe, to ACK peripheral's response. */
- set_pins(BPP_PP_nSelectIn|BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, minor);
-
- /* Wait for Event 6: Peripheral latches response bits */
- rc = wait_for(BPP_GP_nAck, 0, TIME_PSetup+TIME_PResponse, minor);
- if (rc == -1) return -EIO;
-
- /* A 1284 device cannot refuse nibble mode */
- if (mode == DEFAULT_NIBBLE) return 0;
-
- if (pins & BPP_GP_Select) return 0;
-
- return -EPROTONOSUPPORT;
-}
-
-static int terminate(unsigned minor)
-{
- int rc;
-
- /* Event 22: Request termination of 1284 mode */
- set_pins(BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, minor);
-
- /* Wait for Events 23 and 24: ACK termination request. */
- rc = wait_for(BPP_GP_Busy|BPP_GP_nFault,
- BPP_GP_nAck,
- TIME_PSetup+TIME_PResponse,
- minor);
-
- instances[minor].direction = 0;
- instances[minor].mode = COMPATIBILITY;
-
- if (rc == -1) {
- return -EIO;
- }
-
- /* Event 25: Handshake by lowering nAutoFd */
- set_pins(BPP_PP_nStrobe|BPP_PP_nInit, minor);
-
- /* Event 26: Peripheral wiggles lines... */
-
- /* Event 27: Peripheral sets nAck HIGH to ack handshake */
- rc = wait_for(BPP_GP_nAck, 0, TIME_PResponse, minor);
- if (rc == -1) {
- set_pins(BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, minor);
- return -EIO;
- }
-
- /* Event 28: Finish phase by raising nAutoFd */
- set_pins(BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, minor);
-
- return 0;
-}
-
-static DEFINE_SPINLOCK(bpp_open_lock);
-
-/*
- * Allow only one process to open the device at a time.
- */
-static int bpp_open(struct inode *inode, struct file *f)
-{
- unsigned minor = iminor(inode);
- int ret;
-
- lock_kernel();
- spin_lock(&bpp_open_lock);
- ret = 0;
- if (minor >= BPP_NO) {
- ret = -ENODEV;
- } else {
- if (! instances[minor].present) {
- ret = -ENODEV;
- } else {
- if (instances[minor].opened)
- ret = -EBUSY;
- else
- instances[minor].opened = 1;
- }
- }
- spin_unlock(&bpp_open_lock);
- unlock_kernel();
-
- return ret;
-}
-
-/*
- * When the process closes the device, this method is called to clean
- * up and reset the hardware. Always leave the device in compatibility
- * mode as this is a reasonable place to clean up from messes made by
- * ioctls, or other mayhem.
- */
-static int bpp_release(struct inode *inode, struct file *f)
-{
- unsigned minor = iminor(inode);
-
- spin_lock(&bpp_open_lock);
- instances[minor].opened = 0;
-
- if (instances[minor].mode != COMPATIBILITY)
- terminate(minor);
-
- spin_unlock(&bpp_open_lock);
-
- return 0;
-}
-
-static long read_nibble(unsigned minor, char __user *c, unsigned long cnt)
-{
- unsigned long remaining = cnt;
- long rc;
-
- while (remaining > 0) {
- unsigned char byte = 0;
- int pins;
-
- /* Event 7: request nibble */
- set_pins(BPP_PP_nSelectIn|BPP_PP_nStrobe, minor);
-
- /* Wait for event 9: Peripher strobes first nibble */
- pins = wait_for(0, BPP_GP_nAck, TIME_IDLE_LIMIT, minor);
- if (pins == -1) return -ETIMEDOUT;
-
- /* Event 10: I handshake nibble */
- set_pins(BPP_PP_nSelectIn|BPP_PP_nStrobe|BPP_PP_nAutoFd, minor);
- if (pins & BPP_GP_nFault) byte |= 0x01;
- if (pins & BPP_GP_Select) byte |= 0x02;
- if (pins & BPP_GP_PError) byte |= 0x04;
- if (pins & BPP_GP_Busy) byte |= 0x08;
-
- /* Wait for event 11: Peripheral handshakes nibble */
- rc = wait_for(BPP_GP_nAck, 0, TIME_PResponse, minor);
-
- /* Event 7: request nibble */
- set_pins(BPP_PP_nSelectIn|BPP_PP_nStrobe, minor);
-
- /* Wait for event 9: Peripher strobes first nibble */
- pins = wait_for(0, BPP_GP_nAck, TIME_PResponse, minor);
- if (rc == -1) return -ETIMEDOUT;
-
- /* Event 10: I handshake nibble */
- set_pins(BPP_PP_nSelectIn|BPP_PP_nStrobe|BPP_PP_nAutoFd, minor);
- if (pins & BPP_GP_nFault) byte |= 0x10;
- if (pins & BPP_GP_Select) byte |= 0x20;
- if (pins & BPP_GP_PError) byte |= 0x40;
- if (pins & BPP_GP_Busy) byte |= 0x80;
-
- if (put_user(byte, c))
- return -EFAULT;
- c += 1;
- remaining -= 1;
-
- /* Wait for event 11: Peripheral handshakes nibble */
- rc = wait_for(BPP_GP_nAck, 0, TIME_PResponse, minor);
- if (rc == -1) return -EIO;
- }
-
- return cnt - remaining;
-}
-
-static long read_ecp(unsigned minor, char __user *c, unsigned long cnt)
-{
- unsigned long remaining;
- long rc;
-
- /* Turn ECP mode from forward to reverse if needed. */
- if (! instances[minor].direction) {
- unsigned short pins = get_pins(minor);
-
- /* Event 38: Turn the bus around */
- instances[minor].direction = 0x20;
- pins &= ~BPP_PP_nAutoFd;
- set_pins(pins, minor);
-
- /* Event 39: Set pins for reverse mode. */
- snooze(TIME_PSetup, minor);
- set_pins(BPP_PP_nStrobe|BPP_PP_nSelectIn, minor);
-
- /* Wait for event 40: Peripheral ready to be strobed */
- rc = wait_for(0, BPP_GP_PError, TIME_PResponse, minor);
- if (rc == -1) return -ETIMEDOUT;
- }
-
- remaining = cnt;
-
- while (remaining > 0) {
-
- /* If there is a run length for a repeated byte, repeat */
- /* that byte a few times. */
- if (instances[minor].run_length && !instances[minor].run_flag) {
-
- char buffer[128];
- unsigned idx;
- unsigned repeat = remaining < instances[minor].run_length
- ? remaining
- : instances[minor].run_length;
-
- for (idx = 0 ; idx < repeat ; idx += 1)
- buffer[idx] = instances[minor].repeat_byte;
-
- if (copy_to_user(c, buffer, repeat))
- return -EFAULT;
- remaining -= repeat;
- c += repeat;
- instances[minor].run_length -= repeat;
- }
-
- if (remaining == 0) break;
-
-
- /* Wait for Event 43: Data active on the bus. */
- rc = wait_for(0, BPP_GP_nAck, TIME_IDLE_LIMIT, minor);
- if (rc == -1) break;
-
- if (rc & BPP_GP_Busy) {
- /* OK, this is data. read it in. */
- unsigned char byte = bpp_inb(base_addrs[minor]);
- if (put_user(byte, c))
- return -EFAULT;
- c += 1;
- remaining -= 1;
-
- if (instances[minor].run_flag) {
- instances[minor].repeat_byte = byte;
- instances[minor].run_flag = 0;
- }
-
- } else {
- unsigned char byte = bpp_inb(base_addrs[minor]);
- if (byte & 0x80) {
- printk("bpp%d: "
- "Ignoring ECP channel %u from device.\n",
- minor, byte & 0x7f);
- } else {
- instances[minor].run_length = byte;
- instances[minor].run_flag = 1;
- }
- }
-
- /* Event 44: I got it. */
- set_pins(BPP_PP_nStrobe|BPP_PP_nAutoFd|BPP_PP_nSelectIn, minor);
-
- /* Wait for event 45: peripheral handshake */
- rc = wait_for(BPP_GP_nAck, 0, TIME_PResponse, minor);
- if (rc == -1) return -ETIMEDOUT;
-
- /* Event 46: Finish handshake */
- set_pins(BPP_PP_nStrobe|BPP_PP_nSelectIn, minor);
-
- }
-
-
- return cnt - remaining;
-}
-
-static ssize_t bpp_read(struct file *f, char __user *c, size_t cnt, loff_t * ppos)
-{
- long rc;
- unsigned minor = iminor(f->f_path.dentry->d_inode);
- if (minor >= BPP_NO) return -ENODEV;
- if (!instances[minor].present) return -ENODEV;
-
- switch (instances[minor].mode) {
-
- default:
- if (instances[minor].mode != COMPATIBILITY)
- terminate(minor);
-
- if (instances[minor].enhanced) {
- /* For now, do all reads with ECP-RLE mode */
- unsigned short pins;
-
- rc = negotiate(DEFAULT_ECP, minor);
- if (rc < 0) break;
-
- instances[minor].mode = ECP_RLE;
-
- /* Event 30: set nAutoFd low to setup for ECP mode */
- pins = get_pins(minor);
- pins &= ~BPP_PP_nAutoFd;
- set_pins(pins, minor);
-
- /* Wait for Event 31: peripheral ready */
- rc = wait_for(BPP_GP_PError, 0, TIME_PResponse, minor);
- if (rc == -1) return -ETIMEDOUT;
-
- rc = read_ecp(minor, c, cnt);
-
- } else {
- rc = negotiate(DEFAULT_NIBBLE, minor);
- if (rc < 0) break;
-
- instances[minor].mode = NIBBLE;
-
- rc = read_nibble(minor, c, cnt);
- }
- break;
-
- case NIBBLE:
- rc = read_nibble(minor, c, cnt);
- break;
-
- case ECP:
- case ECP_RLE:
- rc = read_ecp(minor, c, cnt);
- break;
-
- }
-
-
- return rc;
-}
-
-/*
- * Compatibility mode handshaking is a matter of writing data,
- * strobing it, and waiting for the printer to stop being busy.
- */
-static long write_compat(unsigned minor, const char __user *c, unsigned long cnt)
-{
- long rc;
- unsigned short pins = get_pins(minor);
-
- unsigned long remaining = cnt;
-
-
- while (remaining > 0) {
- unsigned char byte;
-
- if (get_user(byte, c))
- return -EFAULT;
- c += 1;
-
- rc = wait_for(BPP_GP_nAck, BPP_GP_Busy, TIME_IDLE_LIMIT, minor);
- if (rc == -1) return -ETIMEDOUT;
-
- bpp_outb_p(byte, base_addrs[minor]);
- remaining -= 1;
- /* snooze(1, minor); */
-
- pins &= ~BPP_PP_nStrobe;
- set_pins(pins, minor);
-
- rc = wait_for(BPP_GP_Busy, 0, TIME_PResponse, minor);
-
- pins |= BPP_PP_nStrobe;
- set_pins(pins, minor);
- }
-
- return cnt - remaining;
-}
-
-/*
- * Write data using ECP mode. Watch out that the port may be set up
- * for reading. If so, turn the port around.
- */
-static long write_ecp(unsigned minor, const char __user *c, unsigned long cnt)
-{
- unsigned short pins = get_pins(minor);
- unsigned long remaining = cnt;
-
- if (instances[minor].direction) {
- int rc;
-
- /* Event 47 Request bus be turned around */
- pins |= BPP_PP_nInit;
- set_pins(pins, minor);
-
- /* Wait for Event 49: Peripheral relinquished bus */
- rc = wait_for(BPP_GP_PError, 0, TIME_PResponse, minor);
-
- pins |= BPP_PP_nAutoFd;
- instances[minor].direction = 0;
- set_pins(pins, minor);
- }
-
- while (remaining > 0) {
- unsigned char byte;
- int rc;
-
- if (get_user(byte, c))
- return -EFAULT;
-
- rc = wait_for(0, BPP_GP_Busy, TIME_PResponse, minor);
- if (rc == -1) return -ETIMEDOUT;
-
- c += 1;
-
- bpp_outb_p(byte, base_addrs[minor]);
-
- pins &= ~BPP_PP_nStrobe;
- set_pins(pins, minor);
-
- pins |= BPP_PP_nStrobe;
- rc = wait_for(BPP_GP_Busy, 0, TIME_PResponse, minor);
- if (rc == -1) return -EIO;
-
- set_pins(pins, minor);
- }
-
- return cnt - remaining;
-}
-
-/*
- * Write to the peripheral. Be sensitive of the current mode. If I'm
- * in a mode that can be turned around (ECP) then just do
- * that. Otherwise, terminate and do my writing in compat mode. This
- * is the safest course as any device can handle it.
- */
-static ssize_t bpp_write(struct file *f, const char __user *c, size_t cnt, loff_t * ppos)
-{
- long errno = 0;
- unsigned minor = iminor(f->f_path.dentry->d_inode);
- if (minor >= BPP_NO) return -ENODEV;
- if (!instances[minor].present) return -ENODEV;
-
- switch (instances[minor].mode) {
-
- case ECP:
- case ECP_RLE:
- errno = write_ecp(minor, c, cnt);
- break;
- case COMPATIBILITY:
- errno = write_compat(minor, c, cnt);
- break;
- default:
- terminate(minor);
- errno = write_compat(minor, c, cnt);
- }
-
- return errno;
-}
-
-static int bpp_ioctl(struct inode *inode, struct file *f, unsigned int cmd,
- unsigned long arg)
-{
- int errno = 0;
-
- unsigned minor = iminor(inode);
- if (minor >= BPP_NO) return -ENODEV;
- if (!instances[minor].present) return -ENODEV;
-
-
- switch (cmd) {
-
- case BPP_PUT_PINS:
- set_pins(arg, minor);
- break;
-
- case BPP_GET_PINS:
- errno = get_pins(minor);
- break;
-
- case BPP_PUT_DATA:
- bpp_outb_p(arg, base_addrs[minor]);
- break;
-
- case BPP_GET_DATA:
- errno = bpp_inb_p(base_addrs[minor]);
- break;
-
- case BPP_SET_INPUT:
- if (arg)
- if (instances[minor].enhanced) {
- unsigned short bits = get_pins(minor);
- instances[minor].direction = 0x20;
- set_pins(bits, minor);
- } else {
- errno = -ENOTTY;
- }
- else {
- unsigned short bits = get_pins(minor);
- instances[minor].direction = 0x00;
- set_pins(bits, minor);
- }
- break;
-
- default:
- errno = -EINVAL;
- }
-
- return errno;
-}
-
-static const struct file_operations bpp_fops = {
- .owner = THIS_MODULE,
- .read = bpp_read,
- .write = bpp_write,
- .ioctl = bpp_ioctl,
- .open = bpp_open,
- .release = bpp_release,
-};
-
-#if defined(__i386__)
-
-#define collectLptPorts() {}
-
-static void probeLptPort(unsigned idx)
-{
- unsigned int testvalue;
- const unsigned short lpAddr = base_addrs[idx];
-
- instances[idx].present = 0;
- instances[idx].enhanced = 0;
- instances[idx].direction = 0;
- instances[idx].mode = COMPATIBILITY;
- instances[idx].run_length = 0;
- instances[idx].run_flag = 0;
- if (!request_region(lpAddr,3, bpp_dev_name)) return;
-
- /*
- * First, make sure the instance exists. Do this by writing to
- * the data latch and reading the value back. If the port *is*
- * present, test to see if it supports extended-mode
- * operation. This will be required for IEEE1284 reverse
- * transfers.
- */
-
- outb_p(BPP_PROBE_CODE, lpAddr);
- for (testvalue=0; testvalue<BPP_DELAY; testvalue++)
- ;
- testvalue = inb_p(lpAddr);
- if (testvalue == BPP_PROBE_CODE) {
- unsigned save;
- instances[idx].present = 1;
-
- save = inb_p(lpAddr+2);
- for (testvalue=0; testvalue<BPP_DELAY; testvalue++)
- ;
- outb_p(save|0x20, lpAddr+2);
- for (testvalue=0; testvalue<BPP_DELAY; testvalue++)
- ;
- outb_p(~BPP_PROBE_CODE, lpAddr);
- for (testvalue=0; testvalue<BPP_DELAY; testvalue++)
- ;
- testvalue = inb_p(lpAddr);
- if ((testvalue&0xff) == (0xff&~BPP_PROBE_CODE))
- instances[idx].enhanced = 0;
- else
- instances[idx].enhanced = 1;
- outb_p(save, lpAddr+2);
- }
- else {
- release_region(lpAddr,3);
- }
- /*
- * Leave the port in compat idle mode.
- */
- set_pins(BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, idx);
-
- printk("bpp%d: Port at 0x%03x: Enhanced mode %s\n", idx, base_addrs[idx],
- instances[idx].enhanced? "SUPPORTED" : "UNAVAILABLE");
-}
-
-static inline void freeLptPort(int idx)
-{
- release_region(base_addrs[idx], 3);
-}
-
-#endif
-
-#if defined(__sparc__)
-
-static void __iomem *map_bpp(struct sbus_dev *dev, int idx)
-{
- return sbus_ioremap(&dev->resource[0], 0, BPP_SIZE, "bpp");
-}
-
-static int collectLptPorts(void)
-{
- struct sbus_bus *bus;
- struct sbus_dev *dev;
- int count;
-
- count = 0;
- for_all_sbusdev(dev, bus) {
- if (strcmp(dev->prom_name, "SUNW,bpp") == 0) {
- if (count >= BPP_NO) {
- printk(KERN_NOTICE
- "bpp: More than %d bpp ports,"
- " rest is ignored\n", BPP_NO);
- return count;
- }
- base_addrs[count] = map_bpp(dev, count);
- count++;
- }
- }
- return count;
-}
-
-static void probeLptPort(unsigned idx)
-{
- void __iomem *rp = base_addrs[idx];
- __u32 csr;
- char *brand;
-
- instances[idx].present = 0;
- instances[idx].enhanced = 0;
- instances[idx].direction = 0;
- instances[idx].mode = COMPATIBILITY;
- instances[idx].run_length = 0;
- instances[idx].run_flag = 0;
-
- if (!rp) return;
-
- instances[idx].present = 1;
- instances[idx].enhanced = 1; /* Sure */
-
- csr = sbus_readl(rp + BPP_CSR);
- if ((csr & P_DRAINING) != 0 && (csr & P_ERR_PEND) == 0) {
- udelay(20);
- csr = sbus_readl(rp + BPP_CSR);
- if ((csr & P_DRAINING) != 0 && (csr & P_ERR_PEND) == 0) {
- printk("bpp%d: DRAINING still active (0x%08x)\n", idx, csr);
- }
- }
- printk("bpp%d: reset with 0x%08x ..", idx, csr);
- sbus_writel((csr | P_RESET) & ~P_INT_EN, rp + BPP_CSR);
- udelay(500);
- sbus_writel(sbus_readl(rp + BPP_CSR) & ~P_RESET, rp + BPP_CSR);
- csr = sbus_readl(rp + BPP_CSR);
- printk(" done with csr=0x%08x ocr=0x%04x\n",
- csr, sbus_readw(rp + BPP_OCR));
-
- switch (csr & P_DEV_ID_MASK) {
- case P_DEV_ID_ZEBRA:
- brand = "Zebra";
- break;
- case P_DEV_ID_L64854:
- brand = "DMA2";
- break;
- default:
- brand = "Unknown";
- }
- printk("bpp%d: %s at %p\n", idx, brand, rp);
-
- /*
- * Leave the port in compat idle mode.
- */
- set_pins(BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, idx);
-
- return;
-}
-
-static inline void freeLptPort(int idx)
-{
- sbus_iounmap(base_addrs[idx], BPP_SIZE);
-}
-
-#endif
-
-static int __init bpp_init(void)
-{
- int rc;
- unsigned idx;
-
- rc = collectLptPorts();
- if (rc == 0)
- return -ENODEV;
-
- rc = register_chrdev(BPP_MAJOR, bpp_dev_name, &bpp_fops);
- if (rc < 0)
- return rc;
-
- for (idx = 0; idx < BPP_NO; idx++) {
- instances[idx].opened = 0;
- probeLptPort(idx);
- }
-
- return 0;
-}
-
-static void __exit bpp_cleanup(void)
-{
- unsigned idx;
-
- unregister_chrdev(BPP_MAJOR, bpp_dev_name);
-
- for (idx = 0; idx < BPP_NO; idx++) {
- if (instances[idx].present)
- freeLptPort(idx);
- }
-}
-
-module_init(bpp_init);
-module_exit(bpp_cleanup);
-
-MODULE_LICENSE("GPL");
-
diff --git a/drivers/sbus/char/cpwatchdog.c b/drivers/sbus/char/cpwatchdog.c
deleted file mode 100644
index 23abfdf..0000000
--- a/drivers/sbus/char/cpwatchdog.c
+++ /dev/null
@@ -1,858 +0,0 @@
-/* cpwatchdog.c - driver implementation for hardware watchdog
- * timers found on Sun Microsystems CP1400 and CP1500 boards.
- *
- * This device supports both the generic Linux watchdog
- * interface and Solaris-compatible ioctls as best it is
- * able.
- *
- * NOTE: CP1400 systems appear to have a defective intr_mask
- * register on the PLD, preventing the disabling of
- * timer interrupts. We use a timer to periodically
- * reset 'stopped' watchdogs on affected platforms.
- *
- * Copyright (c) 2000 Eric Brower (ebrower@usa.net)
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/fs.h>
-#include <linux/errno.h>
-#include <linux/major.h>
-#include <linux/init.h>
-#include <linux/miscdevice.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/timer.h>
-#include <linux/smp_lock.h>
-#include <linux/io.h>
-#include <asm/irq.h>
-#include <asm/ebus.h>
-#include <asm/oplib.h>
-#include <asm/uaccess.h>
-
-#include <asm/watchdog.h>
-
-#define WD_OBPNAME "watchdog"
-#define WD_BADMODEL "SUNW,501-5336"
-#define WD_BTIMEOUT (jiffies + (HZ * 1000))
-#define WD_BLIMIT 0xFFFF
-
-#define WD0_DEVNAME "watchdog0"
-#define WD1_DEVNAME "watchdog1"
-#define WD2_DEVNAME "watchdog2"
-
-#define WD0_MINOR 212
-#define WD1_MINOR 213
-#define WD2_MINOR 214
-
-
-/* Internal driver definitions
- */
-#define WD0_ID 0 /* Watchdog0 */
-#define WD1_ID 1 /* Watchdog1 */
-#define WD2_ID 2 /* Watchdog2 */
-#define WD_NUMDEVS 3 /* Device contains 3 timers */
-
-#define WD_INTR_OFF 0 /* Interrupt disable value */
-#define WD_INTR_ON 1 /* Interrupt enable value */
-
-#define WD_STAT_INIT 0x01 /* Watchdog timer is initialized */
-#define WD_STAT_BSTOP 0x02 /* Watchdog timer is brokenstopped */
-#define WD_STAT_SVCD 0x04 /* Watchdog interrupt occurred */
-
-/* Register value definitions
- */
-#define WD0_INTR_MASK 0x01 /* Watchdog device interrupt masks */
-#define WD1_INTR_MASK 0x02
-#define WD2_INTR_MASK 0x04
-
-#define WD_S_RUNNING 0x01 /* Watchdog device status running */
-#define WD_S_EXPIRED 0x02 /* Watchdog device status expired */
-
-/* Sun uses Altera PLD EPF8820ATC144-4
- * providing three hardware watchdogs:
- *
- * 1) RIC - sends an interrupt when triggered
- * 2) XIR - asserts XIR_B_RESET when triggered, resets CPU
- * 3) POR - asserts POR_B_RESET when triggered, resets CPU, backplane, board
- *
- *** Timer register block definition (struct wd_timer_regblk)
- *
- * dcntr and limit registers (halfword access):
- * -------------------
- * | 15 | ...| 1 | 0 |
- * -------------------
- * |- counter val -|
- * -------------------
- * dcntr - Current 16-bit downcounter value.
- * When downcounter reaches '0' watchdog expires.
- * Reading this register resets downcounter with 'limit' value.
- * limit - 16-bit countdown value in 1/10th second increments.
- * Writing this register begins countdown with input value.
- * Reading from this register does not affect counter.
- * NOTES: After watchdog reset, dcntr and limit contain '1'
- *
- * status register (byte access):
- * ---------------------------
- * | 7 | ... | 2 | 1 | 0 |
- * --------------+------------
- * |- UNUSED -| EXP | RUN |
- * ---------------------------
- * status- Bit 0 - Watchdog is running
- * Bit 1 - Watchdog has expired
- *
- *** PLD register block definition (struct wd_pld_regblk)
- *
- * intr_mask register (byte access):
- * ---------------------------------
- * | 7 | ... | 3 | 2 | 1 | 0 |
- * +-------------+------------------
- * |- UNUSED -| WD3 | WD2 | WD1 |
- * ---------------------------------
- * WD3 - 1 == Interrupt disabled for watchdog 3
- * WD2 - 1 == Interrupt disabled for watchdog 2
- * WD1 - 1 == Interrupt disabled for watchdog 1
- *
- * pld_status register (byte access):
- * UNKNOWN, MAGICAL MYSTERY REGISTER
- *
- */
-#define WD_TIMER_REGSZ 16
-#define WD0_OFF 0
-#define WD1_OFF (WD_TIMER_REGSZ * 1)
-#define WD2_OFF (WD_TIMER_REGSZ * 2)
-#define PLD_OFF (WD_TIMER_REGSZ * 3)
-
-#define WD_DCNTR 0x00
-#define WD_LIMIT 0x04
-#define WD_STATUS 0x08
-
-#define PLD_IMASK (PLD_OFF + 0x00)
-#define PLD_STATUS (PLD_OFF + 0x04)
-
-/* Individual timer structure
- */
-struct wd_timer {
- __u16 timeout;
- __u8 intr_mask;
- unsigned char runstatus;
- void __iomem *regs;
-};
-
-/* Device structure
- */
-struct wd_device {
- int irq;
- spinlock_t lock;
- unsigned char isbaddoggie; /* defective PLD */
- unsigned char opt_enable;
- unsigned char opt_reboot;
- unsigned short opt_timeout;
- unsigned char initialized;
- struct wd_timer watchdog[WD_NUMDEVS];
- void __iomem *regs;
-};
-
-static struct wd_device wd_dev = {
- 0, __SPIN_LOCK_UNLOCKED(wd_dev.lock), 0, 0, 0, 0,
-};
-
-static struct timer_list wd_timer;
-
-static int wd0_timeout = 0;
-static int wd1_timeout = 0;
-static int wd2_timeout = 0;
-
-#ifdef MODULE
-module_param (wd0_timeout, int, 0);
-MODULE_PARM_DESC(wd0_timeout, "Default watchdog0 timeout in 1/10secs");
-module_param (wd1_timeout, int, 0);
-MODULE_PARM_DESC(wd1_timeout, "Default watchdog1 timeout in 1/10secs");
-module_param (wd2_timeout, int, 0);
-MODULE_PARM_DESC(wd2_timeout, "Default watchdog2 timeout in 1/10secs");
-
-MODULE_AUTHOR
- ("Eric Brower <ebrower@usa.net>");
-MODULE_DESCRIPTION
- ("Hardware watchdog driver for Sun Microsystems CP1400/1500");
-MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE
- ("watchdog");
-#endif /* ifdef MODULE */
-
-/* Forward declarations of internal methods
- */
-#ifdef WD_DEBUG
-static void wd_dumpregs(void);
-#endif
-static irqreturn_t wd_interrupt(int irq, void *dev_id);
-static void wd_toggleintr(struct wd_timer* pTimer, int enable);
-static void wd_pingtimer(struct wd_timer* pTimer);
-static void wd_starttimer(struct wd_timer* pTimer);
-static void wd_resetbrokentimer(struct wd_timer* pTimer);
-static void wd_stoptimer(struct wd_timer* pTimer);
-static void wd_brokentimer(unsigned long data);
-static int wd_getstatus(struct wd_timer* pTimer);
-
-/* PLD expects words to be written in LSB format,
- * so we must flip all words prior to writing them to regs
- */
-static inline unsigned short flip_word(unsigned short word)
-{
- return ((word & 0xff) << 8) | ((word >> 8) & 0xff);
-}
-
-#define wd_writew(val, addr) (writew(flip_word(val), addr))
-#define wd_readw(addr) (flip_word(readw(addr)))
-#define wd_writeb(val, addr) (writeb(val, addr))
-#define wd_readb(addr) (readb(addr))
-
-
-/* CP1400s seem to have broken PLD implementations--
- * the interrupt_mask register cannot be written, so
- * no timer interrupts can be masked within the PLD.
- */
-static inline int wd_isbroken(void)
-{
- /* we could test this by read/write/read/restore
- * on the interrupt mask register only if OBP
- * 'watchdog-enable?' == FALSE, but it seems
- * ubiquitous on CP1400s
- */
- char val[32];
- prom_getproperty(prom_root_node, "model", val, sizeof(val));
- return((!strcmp(val, WD_BADMODEL)) ? 1 : 0);
-}
-
-/* Retrieve watchdog-enable? option from OBP
- * Returns 0 if false, 1 if true
- */
-static inline int wd_opt_enable(void)
-{
- int opt_node;
-
- opt_node = prom_getchild(prom_root_node);
- opt_node = prom_searchsiblings(opt_node, "options");
- return((-1 == prom_getint(opt_node, "watchdog-enable?")) ? 0 : 1);
-}
-
-/* Retrieve watchdog-reboot? option from OBP
- * Returns 0 if false, 1 if true
- */
-static inline int wd_opt_reboot(void)
-{
- int opt_node;
-
- opt_node = prom_getchild(prom_root_node);
- opt_node = prom_searchsiblings(opt_node, "options");
- return((-1 == prom_getint(opt_node, "watchdog-reboot?")) ? 0 : 1);
-}
-
-/* Retrieve watchdog-timeout option from OBP
- * Returns OBP value, or 0 if not located
- */
-static inline int wd_opt_timeout(void)
-{
- int opt_node;
- char value[32];
- char *p = value;
-
- opt_node = prom_getchild(prom_root_node);
- opt_node = prom_searchsiblings(opt_node, "options");
- opt_node = prom_getproperty(opt_node,
- "watchdog-timeout",
- value,
- sizeof(value));
- if(-1 != opt_node) {
- /* atoi implementation */
- for(opt_node = 0; /* nop */; p++) {
- if(*p >= '0' && *p <= '9') {
- opt_node = (10*opt_node)+(*p-'0');
- }
- else {
- break;
- }
- }
- }
- return((-1 == opt_node) ? (0) : (opt_node));
-}
-
-static int wd_open(struct inode *inode, struct file *f)
-{
- lock_kernel();
- switch(iminor(inode))
- {
- case WD0_MINOR:
- f->private_data = &wd_dev.watchdog[WD0_ID];
- break;
- case WD1_MINOR:
- f->private_data = &wd_dev.watchdog[WD1_ID];
- break;
- case WD2_MINOR:
- f->private_data = &wd_dev.watchdog[WD2_ID];
- break;
- default:
- unlock_kernel();
- return(-ENODEV);
- }
-
- /* Register IRQ on first open of device */
- if(0 == wd_dev.initialized)
- {
- if (request_irq(wd_dev.irq,
- &wd_interrupt,
- IRQF_SHARED,
- WD_OBPNAME,
- (void *)wd_dev.regs)) {
- printk("%s: Cannot register IRQ %d\n",
- WD_OBPNAME, wd_dev.irq);
- unlock_kernel();
- return(-EBUSY);
- }
- wd_dev.initialized = 1;
- }
-
- unlock_kernel();
- return(nonseekable_open(inode, f));
-}
-
-static int wd_release(struct inode *inode, struct file *file)
-{
- return 0;
-}
-
-static int wd_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- int setopt = 0;
- struct wd_timer* pTimer = (struct wd_timer*)file->private_data;
- void __user *argp = (void __user *)arg;
- struct watchdog_info info = {
- 0,
- 0,
- "Altera EPF8820ATC144-4"
- };
-
- if(NULL == pTimer) {
- return(-EINVAL);
- }
-
- switch(cmd)
- {
- /* Generic Linux IOCTLs */
- case WDIOC_GETSUPPORT:
- if(copy_to_user(argp, &info, sizeof(struct watchdog_info))) {
- return(-EFAULT);
- }
- break;
- case WDIOC_GETSTATUS:
- case WDIOC_GETBOOTSTATUS:
- if (put_user(0, (int __user *)argp))
- return -EFAULT;
- break;
- case WDIOC_KEEPALIVE:
- wd_pingtimer(pTimer);
- break;
- case WDIOC_SETOPTIONS:
- if(copy_from_user(&setopt, argp, sizeof(unsigned int))) {
- return -EFAULT;
- }
- if(setopt & WDIOS_DISABLECARD) {
- if(wd_dev.opt_enable) {
- printk(
- "%s: cannot disable watchdog in ENABLED mode\n",
- WD_OBPNAME);
- return(-EINVAL);
- }
- wd_stoptimer(pTimer);
- }
- else if(setopt & WDIOS_ENABLECARD) {
- wd_starttimer(pTimer);
- }
- else {
- return(-EINVAL);
- }
- break;
- /* Solaris-compatible IOCTLs */
- case WIOCGSTAT:
- setopt = wd_getstatus(pTimer);
- if(copy_to_user(argp, &setopt, sizeof(unsigned int))) {
- return(-EFAULT);
- }
- break;
- case WIOCSTART:
- wd_starttimer(pTimer);
- break;
- case WIOCSTOP:
- if(wd_dev.opt_enable) {
- printk("%s: cannot disable watchdog in ENABLED mode\n",
- WD_OBPNAME);
- return(-EINVAL);
- }
- wd_stoptimer(pTimer);
- break;
- default:
- return(-EINVAL);
- }
- return(0);
-}
-
-static long wd_compat_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- int rval = -ENOIOCTLCMD;
-
- switch (cmd) {
- /* solaris ioctls are specific to this driver */
- case WIOCSTART:
- case WIOCSTOP:
- case WIOCGSTAT:
- lock_kernel();
- rval = wd_ioctl(file->f_path.dentry->d_inode, file, cmd, arg);
- unlock_kernel();
- break;
- /* everything else is handled by the generic compat layer */
- default:
- break;
- }
-
- return rval;
-}
-
-static ssize_t wd_write(struct file *file,
- const char __user *buf,
- size_t count,
- loff_t *ppos)
-{
- struct wd_timer* pTimer = (struct wd_timer*)file->private_data;
-
- if(NULL == pTimer) {
- return(-EINVAL);
- }
-
- if (count) {
- wd_pingtimer(pTimer);
- return 1;
- }
- return 0;
-}
-
-static ssize_t wd_read(struct file * file, char __user *buffer,
- size_t count, loff_t *ppos)
-{
-#ifdef WD_DEBUG
- wd_dumpregs();
- return(0);
-#else
- return(-EINVAL);
-#endif /* ifdef WD_DEBUG */
-}
-
-static irqreturn_t wd_interrupt(int irq, void *dev_id)
-{
- /* Only WD0 will interrupt-- others are NMI and we won't
- * see them here....
- */
- spin_lock_irq(&wd_dev.lock);
- if((unsigned long)wd_dev.regs == (unsigned long)dev_id)
- {
- wd_stoptimer(&wd_dev.watchdog[WD0_ID]);
- wd_dev.watchdog[WD0_ID].runstatus |= WD_STAT_SVCD;
- }
- spin_unlock_irq(&wd_dev.lock);
- return IRQ_HANDLED;
-}
-
-static const struct file_operations wd_fops = {
- .owner = THIS_MODULE,
- .ioctl = wd_ioctl,
- .compat_ioctl = wd_compat_ioctl,
- .open = wd_open,
- .write = wd_write,
- .read = wd_read,
- .release = wd_release,
-};
-
-static struct miscdevice wd0_miscdev = { WD0_MINOR, WD0_DEVNAME, &wd_fops };
-static struct miscdevice wd1_miscdev = { WD1_MINOR, WD1_DEVNAME, &wd_fops };
-static struct miscdevice wd2_miscdev = { WD2_MINOR, WD2_DEVNAME, &wd_fops };
-
-#ifdef WD_DEBUG
-static void wd_dumpregs(void)
-{
- /* Reading from downcounters initiates watchdog countdown--
- * Example is included below for illustration purposes.
- */
- int i;
- printk("%s: dumping register values\n", WD_OBPNAME);
- for(i = WD0_ID; i < WD_NUMDEVS; ++i) {
- /* printk("\t%s%i: dcntr at 0x%lx: 0x%x\n",
- * WD_OBPNAME,
- * i,
- * (unsigned long)(&wd_dev.watchdog[i].regs->dcntr),
- * readw(&wd_dev.watchdog[i].regs->dcntr));
- */
- printk("\t%s%i: limit at 0x%lx: 0x%x\n",
- WD_OBPNAME,
- i,
- (unsigned long)(&wd_dev.watchdog[i].regs->limit),
- readw(&wd_dev.watchdog[i].regs->limit));
- printk("\t%s%i: status at 0x%lx: 0x%x\n",
- WD_OBPNAME,
- i,
- (unsigned long)(&wd_dev.watchdog[i].regs->status),
- readb(&wd_dev.watchdog[i].regs->status));
- printk("\t%s%i: driver status: 0x%x\n",
- WD_OBPNAME,
- i,
- wd_getstatus(&wd_dev.watchdog[i]));
- }
- printk("\tintr_mask at %p: 0x%x\n",
- wd_dev.regs + PLD_IMASK,
- readb(wd_dev.regs + PLD_IMASK));
- printk("\tpld_status at %p: 0x%x\n",
- wd_dev.regs + PLD_STATUS,
- readb(wd_dev.regs + PLD_STATUS));
-}
-#endif
-
-/* Enable or disable watchdog interrupts
- * Because of the CP1400 defect this should only be
- * called during initialzation or by wd_[start|stop]timer()
- *
- * pTimer - pointer to timer device, or NULL to indicate all timers
- * enable - non-zero to enable interrupts, zero to disable
- */
-static void wd_toggleintr(struct wd_timer* pTimer, int enable)
-{
- unsigned char curregs = wd_readb(wd_dev.regs + PLD_IMASK);
- unsigned char setregs =
- (NULL == pTimer) ?
- (WD0_INTR_MASK | WD1_INTR_MASK | WD2_INTR_MASK) :
- (pTimer->intr_mask);
-
- (WD_INTR_ON == enable) ?
- (curregs &= ~setregs):
- (curregs |= setregs);
-
- wd_writeb(curregs, wd_dev.regs + PLD_IMASK);
- return;
-}
-
-/* Reset countdown timer with 'limit' value and continue countdown.
- * This will not start a stopped timer.
- *
- * pTimer - pointer to timer device
- */
-static void wd_pingtimer(struct wd_timer* pTimer)
-{
- if (wd_readb(pTimer->regs + WD_STATUS) & WD_S_RUNNING) {
- wd_readw(pTimer->regs + WD_DCNTR);
- }
-}
-
-/* Stop a running watchdog timer-- the timer actually keeps
- * running, but the interrupt is masked so that no action is
- * taken upon expiration.
- *
- * pTimer - pointer to timer device
- */
-static void wd_stoptimer(struct wd_timer* pTimer)
-{
- if(wd_readb(pTimer->regs + WD_STATUS) & WD_S_RUNNING) {
- wd_toggleintr(pTimer, WD_INTR_OFF);
-
- if(wd_dev.isbaddoggie) {
- pTimer->runstatus |= WD_STAT_BSTOP;
- wd_brokentimer((unsigned long)&wd_dev);
- }
- }
-}
-
-/* Start a watchdog timer with the specified limit value
- * If the watchdog is running, it will be restarted with
- * the provided limit value.
- *
- * This function will enable interrupts on the specified
- * watchdog.
- *
- * pTimer - pointer to timer device
- * limit - limit (countdown) value in 1/10th seconds
- */
-static void wd_starttimer(struct wd_timer* pTimer)
-{
- if(wd_dev.isbaddoggie) {
- pTimer->runstatus &= ~WD_STAT_BSTOP;
- }
- pTimer->runstatus &= ~WD_STAT_SVCD;
-
- wd_writew(pTimer->timeout, pTimer->regs + WD_LIMIT);
- wd_toggleintr(pTimer, WD_INTR_ON);
-}
-
-/* Restarts timer with maximum limit value and
- * does not unset 'brokenstop' value.
- */
-static void wd_resetbrokentimer(struct wd_timer* pTimer)
-{
- wd_toggleintr(pTimer, WD_INTR_ON);
- wd_writew(WD_BLIMIT, pTimer->regs + WD_LIMIT);
-}
-
-/* Timer device initialization helper.
- * Returns 0 on success, other on failure
- */
-static int wd_inittimer(int whichdog)
-{
- struct miscdevice *whichmisc;
- void __iomem *whichregs;
- char whichident[8];
- int whichmask;
- __u16 whichlimit;
-
- switch(whichdog)
- {
- case WD0_ID:
- whichmisc = &wd0_miscdev;
- strcpy(whichident, "RIC");
- whichregs = wd_dev.regs + WD0_OFF;
- whichmask = WD0_INTR_MASK;
- whichlimit= (0 == wd0_timeout) ?
- (wd_dev.opt_timeout):
- (wd0_timeout);
- break;
- case WD1_ID:
- whichmisc = &wd1_miscdev;
- strcpy(whichident, "XIR");
- whichregs = wd_dev.regs + WD1_OFF;
- whichmask = WD1_INTR_MASK;
- whichlimit= (0 == wd1_timeout) ?
- (wd_dev.opt_timeout):
- (wd1_timeout);
- break;
- case WD2_ID:
- whichmisc = &wd2_miscdev;
- strcpy(whichident, "POR");
- whichregs = wd_dev.regs + WD2_OFF;
- whichmask = WD2_INTR_MASK;
- whichlimit= (0 == wd2_timeout) ?
- (wd_dev.opt_timeout):
- (wd2_timeout);
- break;
- default:
- printk("%s: %s: invalid watchdog id: %i\n",
- WD_OBPNAME, __func__, whichdog);
- return(1);
- }
- if(0 != misc_register(whichmisc))
- {
- return(1);
- }
- wd_dev.watchdog[whichdog].regs = whichregs;
- wd_dev.watchdog[whichdog].timeout = whichlimit;
- wd_dev.watchdog[whichdog].intr_mask = whichmask;
- wd_dev.watchdog[whichdog].runstatus &= ~WD_STAT_BSTOP;
- wd_dev.watchdog[whichdog].runstatus |= WD_STAT_INIT;
-
- printk("%s%i: %s hardware watchdog [%01i.%i sec] %s\n",
- WD_OBPNAME,
- whichdog,
- whichident,
- wd_dev.watchdog[whichdog].timeout / 10,
- wd_dev.watchdog[whichdog].timeout % 10,
- (0 != wd_dev.opt_enable) ? "in ENABLED mode" : "");
- return(0);
-}
-
-/* Timer method called to reset stopped watchdogs--
- * because of the PLD bug on CP1400, we cannot mask
- * interrupts within the PLD so me must continually
- * reset the timers ad infinitum.
- */
-static void wd_brokentimer(unsigned long data)
-{
- struct wd_device* pDev = (struct wd_device*)data;
- int id, tripped = 0;
-
- /* kill a running timer instance, in case we
- * were called directly instead of by kernel timer
- */
- if(timer_pending(&wd_timer)) {
- del_timer(&wd_timer);
- }
-
- for(id = WD0_ID; id < WD_NUMDEVS; ++id) {
- if(pDev->watchdog[id].runstatus & WD_STAT_BSTOP) {
- ++tripped;
- wd_resetbrokentimer(&pDev->watchdog[id]);
- }
- }
-
- if(tripped) {
- /* there is at least one timer brokenstopped-- reschedule */
- init_timer(&wd_timer);
- wd_timer.expires = WD_BTIMEOUT;
- add_timer(&wd_timer);
- }
-}
-
-static int wd_getstatus(struct wd_timer* pTimer)
-{
- unsigned char stat = wd_readb(pTimer->regs + WD_STATUS);
- unsigned char intr = wd_readb(wd_dev.regs + PLD_IMASK);
- unsigned char ret = WD_STOPPED;
-
- /* determine STOPPED */
- if(0 == stat ) {
- return(ret);
- }
- /* determine EXPIRED vs FREERUN vs RUNNING */
- else if(WD_S_EXPIRED & stat) {
- ret = WD_EXPIRED;
- }
- else if(WD_S_RUNNING & stat) {
- if(intr & pTimer->intr_mask) {
- ret = WD_FREERUN;
- }
- else {
- /* Fudge WD_EXPIRED status for defective CP1400--
- * IF timer is running
- * AND brokenstop is set
- * AND an interrupt has been serviced
- * we are WD_EXPIRED.
- *
- * IF timer is running
- * AND brokenstop is set
- * AND no interrupt has been serviced
- * we are WD_FREERUN.
- */
- if(wd_dev.isbaddoggie && (pTimer->runstatus & WD_STAT_BSTOP)) {
- if(pTimer->runstatus & WD_STAT_SVCD) {
- ret = WD_EXPIRED;
- }
- else {
- /* we could as well pretend we are expired */
- ret = WD_FREERUN;
- }
- }
- else {
- ret = WD_RUNNING;
- }
- }
- }
-
- /* determine SERVICED */
- if(pTimer->runstatus & WD_STAT_SVCD) {
- ret |= WD_SERVICED;
- }
-
- return(ret);
-}
-
-static int __init wd_init(void)
-{
- int id;
- struct linux_ebus *ebus = NULL;
- struct linux_ebus_device *edev = NULL;
-
- for_each_ebus(ebus) {
- for_each_ebusdev(edev, ebus) {
- if (!strcmp(edev->ofdev.node->name, WD_OBPNAME))
- goto ebus_done;
- }
- }
-
-ebus_done:
- if(!edev) {
- printk("%s: unable to locate device\n", WD_OBPNAME);
- return -ENODEV;
- }
-
- wd_dev.regs =
- ioremap(edev->resource[0].start, 4 * WD_TIMER_REGSZ); /* ? */
-
- if(NULL == wd_dev.regs) {
- printk("%s: unable to map registers\n", WD_OBPNAME);
- return(-ENODEV);
- }
-
- /* initialize device structure from OBP parameters */
- wd_dev.irq = edev->irqs[0];
- wd_dev.opt_enable = wd_opt_enable();
- wd_dev.opt_reboot = wd_opt_reboot();
- wd_dev.opt_timeout = wd_opt_timeout();
- wd_dev.isbaddoggie = wd_isbroken();
-
- /* disable all interrupts unless watchdog-enabled? == true */
- if(! wd_dev.opt_enable) {
- wd_toggleintr(NULL, WD_INTR_OFF);
- }
-
- /* register miscellaneous devices */
- for(id = WD0_ID; id < WD_NUMDEVS; ++id) {
- if(0 != wd_inittimer(id)) {
- printk("%s%i: unable to initialize\n", WD_OBPNAME, id);
- }
- }
-
- /* warn about possible defective PLD */
- if(wd_dev.isbaddoggie) {
- init_timer(&wd_timer);
- wd_timer.function = wd_brokentimer;
- wd_timer.data = (unsigned long)&wd_dev;
- wd_timer.expires = WD_BTIMEOUT;
-
- printk("%s: PLD defect workaround enabled for model %s\n",
- WD_OBPNAME, WD_BADMODEL);
- }
- return(0);
-}
-
-static void __exit wd_cleanup(void)
-{
- int id;
-
- /* if 'watchdog-enable?' == TRUE, timers are not stopped
- * when module is unloaded. All brokenstopped timers will
- * also now eventually trip.
- */
- for(id = WD0_ID; id < WD_NUMDEVS; ++id) {
- if(WD_S_RUNNING == wd_readb(wd_dev.watchdog[id].regs + WD_STATUS)) {
- if(wd_dev.opt_enable) {
- printk(KERN_WARNING "%s%i: timer not stopped at release\n",
- WD_OBPNAME, id);
- }
- else {
- wd_stoptimer(&wd_dev.watchdog[id]);
- if(wd_dev.watchdog[id].runstatus & WD_STAT_BSTOP) {
- wd_resetbrokentimer(&wd_dev.watchdog[id]);
- printk(KERN_WARNING
- "%s%i: defect workaround disabled at release, "\
- "timer expires in ~%01i sec\n",
- WD_OBPNAME, id,
- wd_readw(wd_dev.watchdog[id].regs + WD_LIMIT) / 10);
- }
- }
- }
- }
-
- if(wd_dev.isbaddoggie && timer_pending(&wd_timer)) {
- del_timer(&wd_timer);
- }
- if(0 != (wd_dev.watchdog[WD0_ID].runstatus & WD_STAT_INIT)) {
- misc_deregister(&wd0_miscdev);
- }
- if(0 != (wd_dev.watchdog[WD1_ID].runstatus & WD_STAT_INIT)) {
- misc_deregister(&wd1_miscdev);
- }
- if(0 != (wd_dev.watchdog[WD2_ID].runstatus & WD_STAT_INIT)) {
- misc_deregister(&wd2_miscdev);
- }
- if(0 != wd_dev.initialized) {
- free_irq(wd_dev.irq, (void *)wd_dev.regs);
- }
- iounmap(wd_dev.regs);
-}
-
-module_init(wd_init);
-module_exit(wd_cleanup);
diff --git a/drivers/sbus/char/display7seg.c b/drivers/sbus/char/display7seg.c
index d8f5c0c..2550af4 100644
--- a/drivers/sbus/char/display7seg.c
+++ b/drivers/sbus/char/display7seg.c
@@ -1,10 +1,7 @@
-/* $Id: display7seg.c,v 1.6 2002/01/08 16:00:16 davem Exp $
- *
- * display7seg - Driver implementation for the 7-segment display
- * present on Sun Microsystems CP1400 and CP1500
+/* display7seg.c - Driver implementation for the 7-segment display
+ * present on Sun Microsystems CP1400 and CP1500
*
* Copyright (c) 2000 Eric Brower (ebrower@usa.net)
- *
*/
#include <linux/kernel.h>
@@ -16,22 +13,20 @@
#include <linux/miscdevice.h>
#include <linux/ioport.h> /* request_region */
#include <linux/smp_lock.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <asm/atomic.h>
-#include <asm/ebus.h> /* EBus device */
-#include <asm/oplib.h> /* OpenProm Library */
#include <asm/uaccess.h> /* put_/get_user */
#include <asm/io.h>
#include <asm/display7seg.h>
#define D7S_MINOR 193
-#define D7S_OBPNAME "display7seg"
-#define D7S_DEVNAME "d7s"
+#define DRIVER_NAME "d7s"
+#define PFX DRIVER_NAME ": "
static int sol_compat = 0; /* Solaris compatibility mode */
-#ifdef MODULE
-
/* Solaris compatibility flag -
* The Solaris implementation omits support for several
* documented driver features (ref Sun doc 806-0180-03).
@@ -46,20 +41,20 @@
* If you wish the device to operate as under Solaris,
* omitting above features, set this parameter to non-zero.
*/
-module_param
- (sol_compat, int, 0);
-MODULE_PARM_DESC
- (sol_compat,
- "Disables documented functionality omitted from Solaris driver");
+module_param(sol_compat, int, 0);
+MODULE_PARM_DESC(sol_compat,
+ "Disables documented functionality omitted from Solaris driver");
-MODULE_AUTHOR
- ("Eric Brower <ebrower@usa.net>");
-MODULE_DESCRIPTION
- ("7-Segment Display driver for Sun Microsystems CP1400/1500");
+MODULE_AUTHOR("Eric Brower <ebrower@usa.net>");
+MODULE_DESCRIPTION("7-Segment Display driver for Sun Microsystems CP1400/1500");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE
- ("d7s");
-#endif /* ifdef MODULE */
+MODULE_SUPPORTED_DEVICE("d7s");
+
+struct d7s {
+ void __iomem *regs;
+ bool flipped;
+};
+struct d7s *d7s_device;
/*
* Register block address- see header for details
@@ -72,22 +67,6 @@
* FLIP - Inverts display for upside-down mounted board
* bits 0-4 - 7-segment display contents
*/
-static void __iomem* d7s_regs;
-
-static inline void d7s_free(void)
-{
- iounmap(d7s_regs);
-}
-
-static inline int d7s_obpflipped(void)
-{
- int opt_node;
-
- opt_node = prom_getchild(prom_root_node);
- opt_node = prom_searchsiblings(opt_node, "options");
- return ((-1 != prom_getintdefault(opt_node, "d7s-flipped?", -1)) ? 0 : 1);
-}
-
static atomic_t d7s_users = ATOMIC_INIT(0);
static int d7s_open(struct inode *inode, struct file *f)
@@ -106,12 +85,15 @@
* are not operating in solaris-compat mode
*/
if (atomic_dec_and_test(&d7s_users) && !sol_compat) {
- int regval = 0;
+ struct d7s *p = d7s_device;
+ u8 regval = 0;
- regval = readb(d7s_regs);
- (0 == d7s_obpflipped()) ?
- writeb(regval |= D7S_FLIP, d7s_regs):
- writeb(regval &= ~D7S_FLIP, d7s_regs);
+ regval = readb(p->regs);
+ if (p->flipped)
+ regval |= D7S_FLIP;
+ else
+ regval &= ~D7S_FLIP;
+ writeb(regval, p->regs);
}
return 0;
@@ -119,9 +101,10 @@
static long d7s_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- __u8 regs = readb(d7s_regs);
- __u8 ireg = 0;
+ struct d7s *p = d7s_device;
+ u8 regs = readb(p->regs);
int error = 0;
+ u8 ireg = 0;
if (D7S_MINOR != iminor(file->f_path.dentry->d_inode))
return -ENODEV;
@@ -129,18 +112,20 @@
lock_kernel();
switch (cmd) {
case D7SIOCWR:
- /* assign device register values
- * we mask-out D7S_FLIP if in sol_compat mode
+ /* assign device register values we mask-out D7S_FLIP
+ * if in sol_compat mode
*/
if (get_user(ireg, (int __user *) arg)) {
error = -EFAULT;
break;
}
- if (0 != sol_compat) {
- (regs & D7S_FLIP) ?
- (ireg |= D7S_FLIP) : (ireg &= ~D7S_FLIP);
+ if (sol_compat) {
+ if (regs & D7S_FLIP)
+ ireg |= D7S_FLIP;
+ else
+ ireg &= ~D7S_FLIP;
}
- writeb(ireg, d7s_regs);
+ writeb(ireg, p->regs);
break;
case D7SIOCRD:
@@ -158,9 +143,11 @@
case D7SIOCTM:
/* toggle device mode-- flip display orientation */
- (regs & D7S_FLIP) ?
- (regs &= ~D7S_FLIP) : (regs |= D7S_FLIP);
- writeb(regs, d7s_regs);
+ if (regs & D7S_FLIP)
+ regs &= ~D7S_FLIP;
+ else
+ regs |= D7S_FLIP;
+ writeb(regs, p->regs);
break;
};
unlock_kernel();
@@ -176,69 +163,123 @@
.release = d7s_release,
};
-static struct miscdevice d7s_miscdev = { D7S_MINOR, D7S_DEVNAME, &d7s_fops };
+static struct miscdevice d7s_miscdev = {
+ .minor = D7S_MINOR,
+ .name = DRIVER_NAME,
+ .fops = &d7s_fops
+};
-static int __init d7s_init(void)
+static int __devinit d7s_probe(struct of_device *op,
+ const struct of_device_id *match)
{
- struct linux_ebus *ebus = NULL;
- struct linux_ebus_device *edev = NULL;
- int iTmp = 0, regs = 0;
+ struct device_node *opts;
+ int err = -EINVAL;
+ struct d7s *p;
+ u8 regs;
- for_each_ebus(ebus) {
- for_each_ebusdev(edev, ebus) {
- if (!strcmp(edev->prom_node->name, D7S_OBPNAME))
- goto ebus_done;
- }
+ if (d7s_device)
+ goto out;
+
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ err = -ENOMEM;
+ if (!p)
+ goto out;
+
+ p->regs = of_ioremap(&op->resource[0], 0, sizeof(u8), "d7s");
+ if (!p->regs) {
+ printk(KERN_ERR PFX "Cannot map chip registers\n");
+ goto out_free;
}
-ebus_done:
- if(!edev) {
- printk("%s: unable to locate device\n", D7S_DEVNAME);
- return -ENODEV;
+ err = misc_register(&d7s_miscdev);
+ if (err) {
+ printk(KERN_ERR PFX "Unable to acquire miscdevice minor %i\n",
+ D7S_MINOR);
+ goto out_iounmap;
}
- d7s_regs = ioremap(edev->resource[0].start, sizeof(__u8));
-
- iTmp = misc_register(&d7s_miscdev);
- if (0 != iTmp) {
- printk("%s: unable to acquire miscdevice minor %i\n",
- D7S_DEVNAME, D7S_MINOR);
- iounmap(d7s_regs);
- return iTmp;
- }
-
- /* OBP option "d7s-flipped?" is honored as default
- * for the device, and reset default when detached
+ /* OBP option "d7s-flipped?" is honored as default for the
+ * device, and reset default when detached
*/
- regs = readb(d7s_regs);
- iTmp = d7s_obpflipped();
- (0 == iTmp) ?
- writeb(regs |= D7S_FLIP, d7s_regs):
- writeb(regs &= ~D7S_FLIP, d7s_regs);
+ regs = readb(p->regs);
+ opts = of_find_node_by_path("/options");
+ if (opts &&
+ of_get_property(opts, "d7s-flipped?", NULL))
+ p->flipped = true;
- printk("%s: 7-Segment Display%s at 0x%lx %s\n",
- D7S_DEVNAME,
- (0 == iTmp) ? (" (FLIPPED)") : (""),
- edev->resource[0].start,
- (0 != sol_compat) ? ("in sol_compat mode") : (""));
+ if (p->flipped)
+ regs |= D7S_FLIP;
+ else
+ regs &= ~D7S_FLIP;
+
+ writeb(regs, p->regs);
+
+ printk(KERN_INFO PFX "7-Segment Display%s at [%s:0x%lx] %s\n",
+ op->node->full_name,
+ (regs & D7S_FLIP) ? " (FLIPPED)" : "",
+ op->resource[0].start,
+ sol_compat ? "in sol_compat mode" : "");
+
+ dev_set_drvdata(&op->dev, p);
+ d7s_device = p;
+ err = 0;
+
+out:
+ return err;
+
+out_iounmap:
+ of_iounmap(&op->resource[0], p->regs, sizeof(u8));
+
+out_free:
+ kfree(p);
+ goto out;
+}
+
+static int __devexit d7s_remove(struct of_device *op)
+{
+ struct d7s *p = dev_get_drvdata(&op->dev);
+ u8 regs = readb(p->regs);
+
+ /* Honor OBP d7s-flipped? unless operating in solaris-compat mode */
+ if (sol_compat) {
+ if (p->flipped)
+ regs |= D7S_FLIP;
+ else
+ regs &= ~D7S_FLIP;
+ writeb(regs, p->regs);
+ }
+
+ misc_deregister(&d7s_miscdev);
+ of_iounmap(&op->resource[0], p->regs, sizeof(u8));
+ kfree(p);
return 0;
}
-static void __exit d7s_cleanup(void)
+static const struct of_device_id d7s_match[] = {
+ {
+ .name = "display7seg",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, d7s_match);
+
+static struct of_platform_driver d7s_driver = {
+ .name = DRIVER_NAME,
+ .match_table = d7s_match,
+ .probe = d7s_probe,
+ .remove = __devexit_p(d7s_remove),
+};
+
+static int __init d7s_init(void)
{
- int regs = readb(d7s_regs);
+ return of_register_driver(&d7s_driver, &of_bus_type);
+}
- /* Honor OBP d7s-flipped? unless operating in solaris-compat mode */
- if (0 == sol_compat) {
- (0 == d7s_obpflipped()) ?
- writeb(regs |= D7S_FLIP, d7s_regs):
- writeb(regs &= ~D7S_FLIP, d7s_regs);
- }
-
- misc_deregister(&d7s_miscdev);
- d7s_free();
+static void __exit d7s_exit(void)
+{
+ of_unregister_driver(&d7s_driver);
}
module_init(d7s_init);
-module_exit(d7s_cleanup);
+module_exit(d7s_exit);
diff --git a/drivers/sbus/char/envctrl.c b/drivers/sbus/char/envctrl.c
index a408402..58e583b 100644
--- a/drivers/sbus/char/envctrl.c
+++ b/drivers/sbus/char/envctrl.c
@@ -1,5 +1,4 @@
-/* $Id: envctrl.c,v 1.25 2002/01/15 09:01:26 davem Exp $
- * envctrl.c: Temperature and Fan monitoring on Machines providing it.
+/* envctrl.c: Temperature and Fan monitoring on Machines providing it.
*
* Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
* Copyright (C) 2000 Vinh Truong (vinh.truong@eng.sun.com)
@@ -28,12 +27,16 @@
#include <linux/kmod.h>
#include <linux/reboot.h>
#include <linux/smp_lock.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
-#include <asm/ebus.h>
#include <asm/uaccess.h>
#include <asm/envctrl.h>
#include <asm/io.h>
+#define DRIVER_NAME "envctrl"
+#define PFX DRIVER_NAME ": "
+
#define ENVCTRL_MINOR 162
#define PCF8584_ADDRESS 0x55
@@ -193,7 +196,7 @@
}
if (limit <= 0)
- printk(KERN_INFO "envctrl: Pin status will not clear.\n");
+ printk(KERN_INFO PFX "Pin status will not clear.\n");
}
/* Function Description: Test busy bit.
@@ -211,7 +214,7 @@
}
if (limit <= 0)
- printk(KERN_INFO "envctrl: Busy bit will not clear.\n");
+ printk(KERN_INFO PFX "Busy bit will not clear.\n");
}
/* Function Description: Send the address for a read access.
@@ -858,11 +861,10 @@
/* Function Description: Initialize i2c child device.
* Return: None.
*/
-static void envctrl_init_i2c_child(struct linux_ebus_child *edev_child,
+static void envctrl_init_i2c_child(struct device_node *dp,
struct i2c_child_t *pchild)
{
int len, i, tbls_size = 0;
- struct device_node *dp = edev_child->prom_node;
const void *pval;
/* Get device address. */
@@ -882,12 +884,12 @@
pchild->tables = kmalloc(tbls_size, GFP_KERNEL);
if (pchild->tables == NULL){
- printk("envctrl: Failed to allocate table.\n");
+ printk(KERN_ERR PFX "Failed to allocate table.\n");
return;
}
pval = of_get_property(dp, "tables", &len);
if (!pval || len <= 0) {
- printk("envctrl: Failed to get table.\n");
+ printk(KERN_ERR PFX "Failed to get table.\n");
return;
}
memcpy(pchild->tables, pval, len);
@@ -993,14 +995,14 @@
struct i2c_child_t *cputemp;
if (NULL == (cputemp = envctrl_get_i2c_child(ENVCTRL_CPUTEMP_MON))) {
- printk(KERN_ERR
- "envctrl: kenvctrld unable to monitor CPU temp-- exiting\n");
+ printk(KERN_ERR PFX
+ "kenvctrld unable to monitor CPU temp-- exiting\n");
return -ENODEV;
}
poll_interval = 5000; /* TODO env_mon_interval */
- printk(KERN_INFO "envctrl: %s starting...\n", current->comm);
+ printk(KERN_INFO PFX "%s starting...\n", current->comm);
for (;;) {
msleep_interruptible(poll_interval);
@@ -1022,54 +1024,35 @@
}
}
}
- printk(KERN_INFO "envctrl: %s exiting...\n", current->comm);
+ printk(KERN_INFO PFX "%s exiting...\n", current->comm);
return 0;
}
-static int __init envctrl_init(void)
+static int __devinit envctrl_probe(struct of_device *op,
+ const struct of_device_id *match)
{
- struct linux_ebus *ebus = NULL;
- struct linux_ebus_device *edev = NULL;
- struct linux_ebus_child *edev_child = NULL;
- int err, i = 0;
+ struct device_node *dp;
+ int index, err;
- for_each_ebus(ebus) {
- for_each_ebusdev(edev, ebus) {
- if (!strcmp(edev->prom_node->name, "bbc")) {
- /* If we find a boot-bus controller node,
- * then this envctrl driver is not for us.
- */
- return -ENODEV;
- }
+ if (i2c)
+ return -EINVAL;
+
+ i2c = of_ioremap(&op->resource[0], 0, 0x2, DRIVER_NAME);
+ if (!i2c)
+ return -ENOMEM;
+
+ index = 0;
+ dp = op->node->child;
+ while (dp) {
+ if (!strcmp(dp->name, "gpio")) {
+ i2c_childlist[index].i2ctype = I2C_GPIO;
+ envctrl_init_i2c_child(dp, &(i2c_childlist[index++]));
+ } else if (!strcmp(dp->name, "adc")) {
+ i2c_childlist[index].i2ctype = I2C_ADC;
+ envctrl_init_i2c_child(dp, &(i2c_childlist[index++]));
}
- }
- /* Traverse through ebus and ebus device list for i2c device and
- * adc and gpio nodes.
- */
- for_each_ebus(ebus) {
- for_each_ebusdev(edev, ebus) {
- if (!strcmp(edev->prom_node->name, "i2c")) {
- i2c = ioremap(edev->resource[0].start, 0x2);
- for_each_edevchild(edev, edev_child) {
- if (!strcmp("gpio", edev_child->prom_node->name)) {
- i2c_childlist[i].i2ctype = I2C_GPIO;
- envctrl_init_i2c_child(edev_child, &(i2c_childlist[i++]));
- }
- if (!strcmp("adc", edev_child->prom_node->name)) {
- i2c_childlist[i].i2ctype = I2C_ADC;
- envctrl_init_i2c_child(edev_child, &(i2c_childlist[i++]));
- }
- }
- goto done;
- }
- }
- }
-
-done:
- if (!edev) {
- printk("envctrl: I2C device not found.\n");
- return -ENODEV;
+ dp = dp->sibling;
}
/* Set device address. */
@@ -1087,7 +1070,7 @@
/* Register the device as a minor miscellaneous device. */
err = misc_register(&envctrl_dev);
if (err) {
- printk("envctrl: Unable to get misc minor %d\n",
+ printk(KERN_ERR PFX "Unable to get misc minor %d\n",
envctrl_dev.minor);
goto out_iounmap;
}
@@ -1096,12 +1079,12 @@
* a next child device, so we decrement before reverse-traversal of
* child devices.
*/
- printk("envctrl: initialized ");
- for (--i; i >= 0; --i) {
+ printk(KERN_INFO PFX "Initialized ");
+ for (--index; index >= 0; --index) {
printk("[%s 0x%lx]%s",
- (I2C_ADC == i2c_childlist[i].i2ctype) ? ("adc") :
- ((I2C_GPIO == i2c_childlist[i].i2ctype) ? ("gpio") : ("unknown")),
- i2c_childlist[i].addr, (0 == i) ? ("\n") : (" "));
+ (I2C_ADC == i2c_childlist[index].i2ctype) ? "adc" :
+ ((I2C_GPIO == i2c_childlist[index].i2ctype) ? "gpio" : "unknown"),
+ i2c_childlist[index].addr, (0 == index) ? "\n" : " ");
}
kenvctrld_task = kthread_run(kenvctrld, NULL, "kenvctrld");
@@ -1115,26 +1098,54 @@
out_deregister:
misc_deregister(&envctrl_dev);
out_iounmap:
- iounmap(i2c);
- for (i = 0; i < ENVCTRL_MAX_CPU * 2; i++)
- kfree(i2c_childlist[i].tables);
+ of_iounmap(&op->resource[0], i2c, 0x2);
+ for (index = 0; index < ENVCTRL_MAX_CPU * 2; index++)
+ kfree(i2c_childlist[index].tables);
return err;
}
-static void __exit envctrl_cleanup(void)
+static int __devexit envctrl_remove(struct of_device *op)
{
- int i;
+ int index;
kthread_stop(kenvctrld_task);
- iounmap(i2c);
+ of_iounmap(&op->resource[0], i2c, 0x2);
misc_deregister(&envctrl_dev);
- for (i = 0; i < ENVCTRL_MAX_CPU * 2; i++)
- kfree(i2c_childlist[i].tables);
+ for (index = 0; index < ENVCTRL_MAX_CPU * 2; index++)
+ kfree(i2c_childlist[index].tables);
+
+ return 0;
+}
+
+static const struct of_device_id envctrl_match[] = {
+ {
+ .name = "i2c",
+ .compatible = "i2cpcf,8584",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, envctrl_match);
+
+static struct of_platform_driver envctrl_driver = {
+ .name = DRIVER_NAME,
+ .match_table = envctrl_match,
+ .probe = envctrl_probe,
+ .remove = __devexit_p(envctrl_remove),
+};
+
+static int __init envctrl_init(void)
+{
+ return of_register_driver(&envctrl_driver, &of_bus_type);
+}
+
+static void __exit envctrl_exit(void)
+{
+ of_unregister_driver(&envctrl_driver);
}
module_init(envctrl_init);
-module_exit(envctrl_cleanup);
+module_exit(envctrl_exit);
MODULE_LICENSE("GPL");
diff --git a/drivers/sbus/char/flash.c b/drivers/sbus/char/flash.c
index 7d95e15..4108347 100644
--- a/drivers/sbus/char/flash.c
+++ b/drivers/sbus/char/flash.c
@@ -1,5 +1,4 @@
-/* $Id: flash.c,v 1.25 2001/12/21 04:56:16 davem Exp $
- * flash.c: Allow mmap access to the OBP Flash, for OBP updates.
+/* flash.c: Allow mmap access to the OBP Flash, for OBP updates.
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
*/
@@ -15,13 +14,13 @@
#include <linux/smp_lock.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/io.h>
-#include <asm/sbus.h>
-#include <asm/ebus.h>
#include <asm/upa.h>
static DEFINE_SPINLOCK(flash_lock);
@@ -161,97 +160,68 @@
static struct miscdevice flash_dev = { FLASH_MINOR, "flash", &flash_fops };
-static int __init flash_init(void)
+static int __devinit flash_probe(struct of_device *op,
+ const struct of_device_id *match)
{
- struct sbus_bus *sbus;
- struct sbus_dev *sdev = NULL;
-#ifdef CONFIG_PCI
- struct linux_ebus *ebus;
- struct linux_ebus_device *edev = NULL;
- struct linux_prom_registers regs[2];
- int len, nregs;
-#endif
- int err;
+ struct device_node *dp = op->node;
+ struct device_node *parent;
- for_all_sbusdev(sdev, sbus) {
- if (!strcmp(sdev->prom_name, "flashprom")) {
- if (sdev->reg_addrs[0].phys_addr == sdev->reg_addrs[1].phys_addr) {
- flash.read_base = ((unsigned long)sdev->reg_addrs[0].phys_addr) |
- (((unsigned long)sdev->reg_addrs[0].which_io)<<32UL);
- flash.read_size = sdev->reg_addrs[0].reg_size;
- flash.write_base = flash.read_base;
- flash.write_size = flash.read_size;
- } else {
- flash.read_base = ((unsigned long)sdev->reg_addrs[0].phys_addr) |
- (((unsigned long)sdev->reg_addrs[0].which_io)<<32UL);
- flash.read_size = sdev->reg_addrs[0].reg_size;
- flash.write_base = ((unsigned long)sdev->reg_addrs[1].phys_addr) |
- (((unsigned long)sdev->reg_addrs[1].which_io)<<32UL);
- flash.write_size = sdev->reg_addrs[1].reg_size;
- }
- flash.busy = 0;
- break;
- }
- }
- if (!sdev) {
-#ifdef CONFIG_PCI
- const struct linux_prom_registers *ebus_regs;
+ parent = dp->parent;
- for_each_ebus(ebus) {
- for_each_ebusdev(edev, ebus) {
- if (!strcmp(edev->prom_node->name, "flashprom"))
- goto ebus_done;
- }
- }
- ebus_done:
- if (!edev)
- return -ENODEV;
-
- ebus_regs = of_get_property(edev->prom_node, "reg", &len);
- if (!ebus_regs || (len % sizeof(regs[0])) != 0) {
- printk("flash: Strange reg property size %d\n", len);
- return -ENODEV;
- }
-
- nregs = len / sizeof(ebus_regs[0]);
-
- flash.read_base = edev->resource[0].start;
- flash.read_size = ebus_regs[0].reg_size;
-
- if (nregs == 1) {
- flash.write_base = edev->resource[0].start;
- flash.write_size = ebus_regs[0].reg_size;
- } else if (nregs == 2) {
- flash.write_base = edev->resource[1].start;
- flash.write_size = ebus_regs[1].reg_size;
- } else {
- printk("flash: Strange number of regs %d\n", nregs);
- return -ENODEV;
- }
-
- flash.busy = 0;
-
-#else
+ if (strcmp(parent->name, "sbus") &&
+ strcmp(parent->name, "sbi") &&
+ strcmp(parent->name, "ebus"))
return -ENODEV;
-#endif
- }
- printk("OBP Flash: RD %lx[%lx] WR %lx[%lx]\n",
+ flash.read_base = op->resource[0].start;
+ flash.read_size = resource_size(&op->resource[0]);
+ if (op->resource[1].flags) {
+ flash.write_base = op->resource[1].start;
+ flash.write_size = resource_size(&op->resource[1]);
+ } else {
+ flash.write_base = op->resource[0].start;
+ flash.write_size = resource_size(&op->resource[0]);
+ }
+ flash.busy = 0;
+
+ printk(KERN_INFO "%s: OBP Flash, RD %lx[%lx] WR %lx[%lx]\n",
+ op->node->full_name,
flash.read_base, flash.read_size,
flash.write_base, flash.write_size);
- err = misc_register(&flash_dev);
- if (err) {
- printk(KERN_ERR "flash: unable to get misc minor\n");
- return err;
- }
+ return misc_register(&flash_dev);
+}
+
+static int __devexit flash_remove(struct of_device *op)
+{
+ misc_deregister(&flash_dev);
return 0;
}
+static const struct of_device_id flash_match[] = {
+ {
+ .name = "flashprom",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, flash_match);
+
+static struct of_platform_driver flash_driver = {
+ .name = "flash",
+ .match_table = flash_match,
+ .probe = flash_probe,
+ .remove = __devexit_p(flash_remove),
+};
+
+static int __init flash_init(void)
+{
+ return of_register_driver(&flash_driver, &of_bus_type);
+}
+
static void __exit flash_cleanup(void)
{
- misc_deregister(&flash_dev);
+ of_unregister_driver(&flash_driver);
}
module_init(flash_init);
diff --git a/drivers/sbus/char/riowatchdog.c b/drivers/sbus/char/riowatchdog.c
deleted file mode 100644
index 88c0fc6..0000000
--- a/drivers/sbus/char/riowatchdog.c
+++ /dev/null
@@ -1,295 +0,0 @@
-/* $Id: riowatchdog.c,v 1.3.2.2 2002/01/23 18:48:02 davem Exp $
- * riowatchdog.c - driver for hw watchdog inside Super I/O of RIO
- *
- * Copyright (C) 2001 David S. Miller (davem@redhat.com)
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/fs.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/miscdevice.h>
-#include <linux/smp_lock.h>
-
-#include <asm/io.h>
-#include <asm/ebus.h>
-#include <asm/bbc.h>
-#include <asm/oplib.h>
-#include <asm/uaccess.h>
-
-#include <asm/watchdog.h>
-
-/* RIO uses the NatSemi Super I/O power management logical device
- * as its' watchdog.
- *
- * When the watchdog triggers, it asserts a line to the BBC (Boot Bus
- * Controller) of the machine. The BBC can only be configured to
- * trigger a power-on reset when the signal is asserted. The BBC
- * can be configured to ignore the signal entirely as well.
- *
- * The only Super I/O device register we care about is at index
- * 0x05 (WDTO_INDEX) which is the watchdog time-out in minutes (1-255).
- * If set to zero, this disables the watchdog. When set, the system
- * must periodically (before watchdog expires) clear (set to zero) and
- * re-set the watchdog else it will trigger.
- *
- * There are two other indexed watchdog registers inside this Super I/O
- * logical device, but they are unused. The first, at index 0x06 is
- * the watchdog control and can be used to make the watchdog timer re-set
- * when the PS/2 mouse or serial lines show activity. The second, at
- * index 0x07 is merely a sampling of the line from the watchdog to the
- * BBC.
- *
- * The watchdog device generates no interrupts.
- */
-
-MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
-MODULE_DESCRIPTION("Hardware watchdog driver for Sun RIO");
-MODULE_SUPPORTED_DEVICE("watchdog");
-MODULE_LICENSE("GPL");
-
-#define RIOWD_NAME "pmc"
-#define RIOWD_MINOR 215
-
-static DEFINE_SPINLOCK(riowd_lock);
-
-static void __iomem *bbc_regs;
-static void __iomem *riowd_regs;
-#define WDTO_INDEX 0x05
-
-static int riowd_timeout = 1; /* in minutes */
-module_param(riowd_timeout, int, 0);
-MODULE_PARM_DESC(riowd_timeout, "Watchdog timeout in minutes");
-
-#if 0 /* Currently unused. */
-static u8 riowd_readreg(int index)
-{
- unsigned long flags;
- u8 ret;
-
- spin_lock_irqsave(&riowd_lock, flags);
- writeb(index, riowd_regs + 0);
- ret = readb(riowd_regs + 1);
- spin_unlock_irqrestore(&riowd_lock, flags);
-
- return ret;
-}
-#endif
-
-static void riowd_writereg(u8 val, int index)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&riowd_lock, flags);
- writeb(index, riowd_regs + 0);
- writeb(val, riowd_regs + 1);
- spin_unlock_irqrestore(&riowd_lock, flags);
-}
-
-static void riowd_pingtimer(void)
-{
- riowd_writereg(riowd_timeout, WDTO_INDEX);
-}
-
-static void riowd_stoptimer(void)
-{
- u8 val;
-
- riowd_writereg(0, WDTO_INDEX);
-
- val = readb(bbc_regs + BBC_WDACTION);
- val &= ~BBC_WDACTION_RST;
- writeb(val, bbc_regs + BBC_WDACTION);
-}
-
-static void riowd_starttimer(void)
-{
- u8 val;
-
- riowd_writereg(riowd_timeout, WDTO_INDEX);
-
- val = readb(bbc_regs + BBC_WDACTION);
- val |= BBC_WDACTION_RST;
- writeb(val, bbc_regs + BBC_WDACTION);
-}
-
-static int riowd_open(struct inode *inode, struct file *filp)
-{
- cycle_kernel_lock();
- nonseekable_open(inode, filp);
- return 0;
-}
-
-static int riowd_release(struct inode *inode, struct file *filp)
-{
- return 0;
-}
-
-static int riowd_ioctl(struct inode *inode, struct file *filp,
- unsigned int cmd, unsigned long arg)
-{
- static struct watchdog_info info = {
- WDIOF_SETTIMEOUT, 0, "Natl. Semiconductor PC97317"
- };
- void __user *argp = (void __user *)arg;
- unsigned int options;
- int new_margin;
-
- switch (cmd) {
- case WDIOC_GETSUPPORT:
- if (copy_to_user(argp, &info, sizeof(info)))
- return -EFAULT;
- break;
-
- case WDIOC_GETSTATUS:
- case WDIOC_GETBOOTSTATUS:
- if (put_user(0, (int __user *)argp))
- return -EFAULT;
- break;
-
- case WDIOC_KEEPALIVE:
- riowd_pingtimer();
- break;
-
- case WDIOC_SETOPTIONS:
- if (copy_from_user(&options, argp, sizeof(options)))
- return -EFAULT;
-
- if (options & WDIOS_DISABLECARD)
- riowd_stoptimer();
- else if (options & WDIOS_ENABLECARD)
- riowd_starttimer();
- else
- return -EINVAL;
-
- break;
-
- case WDIOC_SETTIMEOUT:
- if (get_user(new_margin, (int __user *)argp))
- return -EFAULT;
- if ((new_margin < 60) || (new_margin > (255 * 60)))
- return -EINVAL;
- riowd_timeout = (new_margin + 59) / 60;
- riowd_pingtimer();
- /* Fall */
-
- case WDIOC_GETTIMEOUT:
- return put_user(riowd_timeout * 60, (int __user *)argp);
-
- default:
- return -EINVAL;
- };
-
- return 0;
-}
-
-static ssize_t riowd_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
-{
- if (count) {
- riowd_pingtimer();
- return 1;
- }
-
- return 0;
-}
-
-static const struct file_operations riowd_fops = {
- .owner = THIS_MODULE,
- .ioctl = riowd_ioctl,
- .open = riowd_open,
- .write = riowd_write,
- .release = riowd_release,
-};
-
-static struct miscdevice riowd_miscdev = { RIOWD_MINOR, RIOWD_NAME, &riowd_fops };
-
-static int __init riowd_bbc_init(void)
-{
- struct linux_ebus *ebus = NULL;
- struct linux_ebus_device *edev = NULL;
- u8 val;
-
- for_each_ebus(ebus) {
- for_each_ebusdev(edev, ebus) {
- if (!strcmp(edev->ofdev.node->name, "bbc"))
- goto found_bbc;
- }
- }
-
-found_bbc:
- if (!edev)
- return -ENODEV;
- bbc_regs = ioremap(edev->resource[0].start, BBC_REGS_SIZE);
- if (!bbc_regs)
- return -ENODEV;
-
- /* Turn it off. */
- val = readb(bbc_regs + BBC_WDACTION);
- val &= ~BBC_WDACTION_RST;
- writeb(val, bbc_regs + BBC_WDACTION);
-
- return 0;
-}
-
-static int __init riowd_init(void)
-{
- struct linux_ebus *ebus = NULL;
- struct linux_ebus_device *edev = NULL;
-
- for_each_ebus(ebus) {
- for_each_ebusdev(edev, ebus) {
- if (!strcmp(edev->ofdev.node->name, RIOWD_NAME))
- goto ebus_done;
- }
- }
-
-ebus_done:
- if (!edev)
- goto fail;
-
- riowd_regs = ioremap(edev->resource[0].start, 2);
- if (riowd_regs == NULL) {
- printk(KERN_ERR "pmc: Cannot map registers.\n");
- return -ENODEV;
- }
-
- if (riowd_bbc_init()) {
- printk(KERN_ERR "pmc: Failure initializing BBC config.\n");
- goto fail;
- }
-
- if (misc_register(&riowd_miscdev)) {
- printk(KERN_ERR "pmc: Cannot register watchdog misc device.\n");
- goto fail;
- }
-
- printk(KERN_INFO "pmc: Hardware watchdog [%i minutes], "
- "regs at %p\n", riowd_timeout, riowd_regs);
-
- return 0;
-
-fail:
- if (riowd_regs) {
- iounmap(riowd_regs);
- riowd_regs = NULL;
- }
- if (bbc_regs) {
- iounmap(bbc_regs);
- bbc_regs = NULL;
- }
- return -ENODEV;
-}
-
-static void __exit riowd_cleanup(void)
-{
- misc_deregister(&riowd_miscdev);
- iounmap(riowd_regs);
- riowd_regs = NULL;
- iounmap(bbc_regs);
- bbc_regs = NULL;
-}
-
-module_init(riowd_init);
-module_exit(riowd_cleanup);
diff --git a/drivers/sbus/char/rtc.c b/drivers/sbus/char/rtc.c
deleted file mode 100644
index b042991..0000000
--- a/drivers/sbus/char/rtc.c
+++ /dev/null
@@ -1,275 +0,0 @@
-/* $Id: rtc.c,v 1.28 2001/10/08 22:19:51 davem Exp $
- *
- * Linux/SPARC Real Time Clock Driver
- * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu)
- *
- * This is a little driver that lets a user-level program access
- * the SPARC Mostek real time clock chip. It is no use unless you
- * use the modified clock utility.
- *
- * Get the modified clock utility from:
- * ftp://vger.kernel.org/pub/linux/Sparc/userland/clock.c
- */
-
-#include <linux/module.h>
-#include <linux/smp_lock.h>
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/miscdevice.h>
-#include <linux/slab.h>
-#include <linux/fcntl.h>
-#include <linux/poll.h>
-#include <linux/init.h>
-#include <asm/io.h>
-#include <asm/mostek.h>
-#include <asm/system.h>
-#include <asm/uaccess.h>
-#include <asm/rtc.h>
-
-static int rtc_busy = 0;
-
-/* This is the structure layout used by drivers/char/rtc.c, we
- * support that driver's ioctls so that things are less messy in
- * userspace.
- */
-struct rtc_time_generic {
- int tm_sec;
- int tm_min;
- int tm_hour;
- int tm_mday;
- int tm_mon;
- int tm_year;
- int tm_wday;
- int tm_yday;
- int tm_isdst;
-};
-#define RTC_AIE_ON _IO('p', 0x01) /* Alarm int. enable on */
-#define RTC_AIE_OFF _IO('p', 0x02) /* ... off */
-#define RTC_UIE_ON _IO('p', 0x03) /* Update int. enable on */
-#define RTC_UIE_OFF _IO('p', 0x04) /* ... off */
-#define RTC_PIE_ON _IO('p', 0x05) /* Periodic int. enable on */
-#define RTC_PIE_OFF _IO('p', 0x06) /* ... off */
-#define RTC_WIE_ON _IO('p', 0x0f) /* Watchdog int. enable on */
-#define RTC_WIE_OFF _IO('p', 0x10) /* ... off */
-#define RTC_RD_TIME _IOR('p', 0x09, struct rtc_time_generic) /* Read RTC time */
-#define RTC_SET_TIME _IOW('p', 0x0a, struct rtc_time_generic) /* Set RTC time */
-#define RTC_ALM_SET _IOW('p', 0x07, struct rtc_time) /* Set alarm time */
-#define RTC_ALM_READ _IOR('p', 0x08, struct rtc_time) /* Read alarm time */
-#define RTC_IRQP_READ _IOR('p', 0x0b, unsigned long) /* Read IRQ rate */
-#define RTC_IRQP_SET _IOW('p', 0x0c, unsigned long) /* Set IRQ rate */
-#define RTC_EPOCH_READ _IOR('p', 0x0d, unsigned long) /* Read epoch */
-#define RTC_EPOCH_SET _IOW('p', 0x0e, unsigned long) /* Set epoch */
-#define RTC_WKALM_SET _IOW('p', 0x0f, struct rtc_wkalrm)/* Set wakeup alarm*/
-#define RTC_WKALM_RD _IOR('p', 0x10, struct rtc_wkalrm)/* Get wakeup alarm*/
-#define RTC_PLL_GET _IOR('p', 0x11, struct rtc_pll_info) /* Get PLL correction */
-#define RTC_PLL_SET _IOW('p', 0x12, struct rtc_pll_info) /* Set PLL correction */
-
-/* Retrieve the current date and time from the real time clock. */
-static void get_rtc_time(struct rtc_time *t)
-{
- void __iomem *regs = mstk48t02_regs;
- u8 tmp;
-
- spin_lock_irq(&mostek_lock);
-
- tmp = mostek_read(regs + MOSTEK_CREG);
- tmp |= MSTK_CREG_READ;
- mostek_write(regs + MOSTEK_CREG, tmp);
-
- t->sec = MSTK_REG_SEC(regs);
- t->min = MSTK_REG_MIN(regs);
- t->hour = MSTK_REG_HOUR(regs);
- t->dow = MSTK_REG_DOW(regs);
- t->dom = MSTK_REG_DOM(regs);
- t->month = MSTK_REG_MONTH(regs);
- t->year = MSTK_CVT_YEAR( MSTK_REG_YEAR(regs) );
-
- tmp = mostek_read(regs + MOSTEK_CREG);
- tmp &= ~MSTK_CREG_READ;
- mostek_write(regs + MOSTEK_CREG, tmp);
-
- spin_unlock_irq(&mostek_lock);
-}
-
-/* Set the current date and time inthe real time clock. */
-void set_rtc_time(struct rtc_time *t)
-{
- void __iomem *regs = mstk48t02_regs;
- u8 tmp;
-
- spin_lock_irq(&mostek_lock);
-
- tmp = mostek_read(regs + MOSTEK_CREG);
- tmp |= MSTK_CREG_WRITE;
- mostek_write(regs + MOSTEK_CREG, tmp);
-
- MSTK_SET_REG_SEC(regs,t->sec);
- MSTK_SET_REG_MIN(regs,t->min);
- MSTK_SET_REG_HOUR(regs,t->hour);
- MSTK_SET_REG_DOW(regs,t->dow);
- MSTK_SET_REG_DOM(regs,t->dom);
- MSTK_SET_REG_MONTH(regs,t->month);
- MSTK_SET_REG_YEAR(regs,t->year - MSTK_YEAR_ZERO);
-
- tmp = mostek_read(regs + MOSTEK_CREG);
- tmp &= ~MSTK_CREG_WRITE;
- mostek_write(regs + MOSTEK_CREG, tmp);
-
- spin_unlock_irq(&mostek_lock);
-}
-
-static int put_rtc_time_generic(void __user *argp, struct rtc_time *tm)
-{
- struct rtc_time_generic __user *utm = argp;
-
- if (__put_user(tm->sec, &utm->tm_sec) ||
- __put_user(tm->min, &utm->tm_min) ||
- __put_user(tm->hour, &utm->tm_hour) ||
- __put_user(tm->dom, &utm->tm_mday) ||
- __put_user(tm->month, &utm->tm_mon) ||
- __put_user(tm->year, &utm->tm_year) ||
- __put_user(tm->dow, &utm->tm_wday) ||
- __put_user(0, &utm->tm_yday) ||
- __put_user(0, &utm->tm_isdst))
- return -EFAULT;
-
- return 0;
-}
-
-static int get_rtc_time_generic(struct rtc_time *tm, void __user *argp)
-{
- struct rtc_time_generic __user *utm = argp;
-
- if (__get_user(tm->sec, &utm->tm_sec) ||
- __get_user(tm->min, &utm->tm_min) ||
- __get_user(tm->hour, &utm->tm_hour) ||
- __get_user(tm->dom, &utm->tm_mday) ||
- __get_user(tm->month, &utm->tm_mon) ||
- __get_user(tm->year, &utm->tm_year) ||
- __get_user(tm->dow, &utm->tm_wday))
- return -EFAULT;
-
- return 0;
-}
-
-static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- struct rtc_time rtc_tm;
- void __user *argp = (void __user *)arg;
-
- switch (cmd) {
- /* No interrupt support, return an error
- * compatible with drivers/char/rtc.c
- */
- case RTC_AIE_OFF:
- case RTC_AIE_ON:
- case RTC_PIE_OFF:
- case RTC_PIE_ON:
- case RTC_UIE_OFF:
- case RTC_UIE_ON:
- case RTC_IRQP_READ:
- case RTC_IRQP_SET:
- case RTC_EPOCH_SET:
- case RTC_EPOCH_READ:
- return -EINVAL;
-
- case RTCGET:
- case RTC_RD_TIME:
- memset(&rtc_tm, 0, sizeof(struct rtc_time));
- get_rtc_time(&rtc_tm);
-
- if (cmd == RTCGET) {
- if (copy_to_user(argp, &rtc_tm,
- sizeof(struct rtc_time)))
- return -EFAULT;
- } else if (put_rtc_time_generic(argp, &rtc_tm))
- return -EFAULT;
-
- return 0;
-
-
- case RTCSET:
- case RTC_SET_TIME:
- if (!capable(CAP_SYS_TIME))
- return -EPERM;
-
- if (cmd == RTCSET) {
- if (copy_from_user(&rtc_tm, argp,
- sizeof(struct rtc_time)))
- return -EFAULT;
- } else if (get_rtc_time_generic(&rtc_tm, argp))
- return -EFAULT;
-
- set_rtc_time(&rtc_tm);
-
- return 0;
-
- default:
- return -EINVAL;
- }
-}
-
-static int rtc_open(struct inode *inode, struct file *file)
-{
- int ret;
-
- lock_kernel();
- spin_lock_irq(&mostek_lock);
- if (rtc_busy) {
- ret = -EBUSY;
- } else {
- rtc_busy = 1;
- ret = 0;
- }
- spin_unlock_irq(&mostek_lock);
- unlock_kernel();
-
- return ret;
-}
-
-static int rtc_release(struct inode *inode, struct file *file)
-{
- rtc_busy = 0;
-
- return 0;
-}
-
-static const struct file_operations rtc_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .ioctl = rtc_ioctl,
- .open = rtc_open,
- .release = rtc_release,
-};
-
-static struct miscdevice rtc_dev = { RTC_MINOR, "rtc", &rtc_fops };
-
-static int __init rtc_sun_init(void)
-{
- int error;
-
- /* It is possible we are being driven by some other RTC chip
- * and thus another RTC driver is handling things.
- */
- if (!mstk48t02_regs)
- return -ENODEV;
-
- error = misc_register(&rtc_dev);
- if (error) {
- printk(KERN_ERR "rtc: unable to get misc minor for Mostek\n");
- return error;
- }
- printk("rtc_sun_init: Registered Mostek RTC driver.\n");
-
- return 0;
-}
-
-static void __exit rtc_sun_cleanup(void)
-{
- misc_deregister(&rtc_dev);
-}
-
-module_init(rtc_sun_init);
-module_exit(rtc_sun_cleanup);
-MODULE_LICENSE("GPL");
diff --git a/drivers/sbus/char/uctrl.c b/drivers/sbus/char/uctrl.c
index 7776375..27993c3 100644
--- a/drivers/sbus/char/uctrl.c
+++ b/drivers/sbus/char/uctrl.c
@@ -1,7 +1,7 @@
-/* $Id: uctrl.c,v 1.12 2001/10/08 22:19:51 davem Exp $
- * uctrl.c: TS102 Microcontroller interface on Tadpole Sparcbook 3
+/* uctrl.c: TS102 Microcontroller interface on Tadpole Sparcbook 3
*
* Copyright 1999 Derrick J Brashear (shadow@dementia.org)
+ * Copyright 2008 David S. Miller (davem@davemloft.net)
*/
#include <linux/module.h>
@@ -14,6 +14,8 @@
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <asm/openprom.h>
#include <asm/oplib.h>
@@ -21,7 +23,6 @@
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/pgtable.h>
-#include <asm/sbus.h>
#define UCTRL_MINOR 174
@@ -33,26 +34,26 @@
#endif
struct uctrl_regs {
- volatile u32 uctrl_intr;
- volatile u32 uctrl_data;
- volatile u32 uctrl_stat;
- volatile u32 uctrl_xxx[5];
+ u32 uctrl_intr;
+ u32 uctrl_data;
+ u32 uctrl_stat;
+ u32 uctrl_xxx[5];
};
struct ts102_regs {
- volatile u32 card_a_intr;
- volatile u32 card_a_stat;
- volatile u32 card_a_ctrl;
- volatile u32 card_a_xxx;
- volatile u32 card_b_intr;
- volatile u32 card_b_stat;
- volatile u32 card_b_ctrl;
- volatile u32 card_b_xxx;
- volatile u32 uctrl_intr;
- volatile u32 uctrl_data;
- volatile u32 uctrl_stat;
- volatile u32 uctrl_xxx;
- volatile u32 ts102_xxx[4];
+ u32 card_a_intr;
+ u32 card_a_stat;
+ u32 card_a_ctrl;
+ u32 card_a_xxx;
+ u32 card_b_intr;
+ u32 card_b_stat;
+ u32 card_b_ctrl;
+ u32 card_b_xxx;
+ u32 uctrl_intr;
+ u32 uctrl_data;
+ u32 uctrl_stat;
+ u32 uctrl_xxx;
+ u32 ts102_xxx[4];
};
/* Bits for uctrl_intr register */
@@ -186,17 +187,15 @@
POWER_RESTART=0x83,
};
-struct uctrl_driver {
- struct uctrl_regs *regs;
+static struct uctrl_driver {
+ struct uctrl_regs __iomem *regs;
int irq;
int pending;
struct uctrl_status status;
-};
+} *global_driver;
-static struct uctrl_driver drv;
-
-static void uctrl_get_event_status(void);
-static void uctrl_get_external_status(void);
+static void uctrl_get_event_status(struct uctrl_driver *);
+static void uctrl_get_external_status(struct uctrl_driver *);
static int
uctrl_ioctl(struct inode *inode, struct file *file,
@@ -213,16 +212,14 @@
uctrl_open(struct inode *inode, struct file *file)
{
lock_kernel();
- uctrl_get_event_status();
- uctrl_get_external_status();
+ uctrl_get_event_status(global_driver);
+ uctrl_get_external_status(global_driver);
unlock_kernel();
return 0;
}
static irqreturn_t uctrl_interrupt(int irq, void *dev_id)
{
- struct uctrl_driver *driver = (struct uctrl_driver *)dev_id;
- printk("in uctrl_interrupt\n");
return IRQ_HANDLED;
}
@@ -244,11 +241,11 @@
{ \
unsigned int i; \
for (i = 0; i < 10000; i++) { \
- if (UCTRL_STAT_TXNF_STA & driver->regs->uctrl_stat) \
+ if (UCTRL_STAT_TXNF_STA & sbus_readl(&driver->regs->uctrl_stat)) \
break; \
} \
dprintk(("write data 0x%02x\n", value)); \
- driver->regs->uctrl_data = value; \
+ sbus_writel(value, &driver->regs->uctrl_data); \
}
/* Wait for something to read, read it, then clear the bit */
@@ -257,24 +254,23 @@
unsigned int i; \
value = 0; \
for (i = 0; i < 10000; i++) { \
- if ((UCTRL_STAT_RXNE_STA & driver->regs->uctrl_stat) == 0) \
+ if ((UCTRL_STAT_RXNE_STA & sbus_readl(&driver->regs->uctrl_stat)) == 0) \
break; \
udelay(1); \
} \
- value = driver->regs->uctrl_data; \
+ value = sbus_readl(&driver->regs->uctrl_data); \
dprintk(("read data 0x%02x\n", value)); \
- driver->regs->uctrl_stat = UCTRL_STAT_RXNE_STA; \
+ sbus_writel(UCTRL_STAT_RXNE_STA, &driver->regs->uctrl_stat); \
}
-static void uctrl_do_txn(struct uctrl_txn *txn)
+static void uctrl_do_txn(struct uctrl_driver *driver, struct uctrl_txn *txn)
{
- struct uctrl_driver *driver = &drv;
int stat, incnt, outcnt, bytecnt, intr;
u32 byte;
- stat = driver->regs->uctrl_stat;
- intr = driver->regs->uctrl_intr;
- driver->regs->uctrl_stat = stat;
+ stat = sbus_readl(&driver->regs->uctrl_stat);
+ intr = sbus_readl(&driver->regs->uctrl_intr);
+ sbus_writel(stat, &driver->regs->uctrl_stat);
dprintk(("interrupt stat 0x%x int 0x%x\n", stat, intr));
@@ -305,9 +301,8 @@
}
}
-static void uctrl_get_event_status(void)
+static void uctrl_get_event_status(struct uctrl_driver *driver)
{
- struct uctrl_driver *driver = &drv;
struct uctrl_txn txn;
u8 outbits[2];
@@ -317,7 +312,7 @@
txn.inbuf = NULL;
txn.outbuf = outbits;
- uctrl_do_txn(&txn);
+ uctrl_do_txn(driver, &txn);
dprintk(("bytes %x %x\n", (outbits[0] & 0xff), (outbits[1] & 0xff)));
driver->status.event_status =
@@ -325,9 +320,8 @@
dprintk(("ev is %x\n", driver->status.event_status));
}
-static void uctrl_get_external_status(void)
+static void uctrl_get_external_status(struct uctrl_driver *driver)
{
- struct uctrl_driver *driver = &drv;
struct uctrl_txn txn;
u8 outbits[2];
int i, v;
@@ -338,7 +332,7 @@
txn.inbuf = NULL;
txn.outbuf = outbits;
- uctrl_do_txn(&txn);
+ uctrl_do_txn(driver, &txn);
dprintk(("bytes %x %x\n", (outbits[0] & 0xff), (outbits[1] & 0xff)));
driver->status.external_status =
@@ -354,71 +348,101 @@
}
-static int __init ts102_uctrl_init(void)
+static int __devinit uctrl_probe(struct of_device *op,
+ const struct of_device_id *match)
{
- struct uctrl_driver *driver = &drv;
- int len;
- struct linux_prom_irqs tmp_irq[2];
- unsigned int vaddr[2] = { 0, 0 };
- int tmpnode, uctrlnode = prom_getchild(prom_root_node);
- int err;
+ struct uctrl_driver *p;
+ int err = -ENOMEM;
- tmpnode = prom_searchsiblings(uctrlnode, "obio");
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p) {
+ printk(KERN_ERR "uctrl: Unable to allocate device struct.\n");
+ goto out;
+ }
- if (tmpnode)
- uctrlnode = prom_getchild(tmpnode);
+ p->regs = of_ioremap(&op->resource[0], 0,
+ resource_size(&op->resource[0]),
+ "uctrl");
+ if (!p->regs) {
+ printk(KERN_ERR "uctrl: Unable to map registers.\n");
+ goto out_free;
+ }
- uctrlnode = prom_searchsiblings(uctrlnode, "uctrl");
-
- if (!uctrlnode)
- return -ENODEV;
-
- /* the prom mapped it for us */
- len = prom_getproperty(uctrlnode, "address", (void *) vaddr,
- sizeof(vaddr));
- driver->regs = (struct uctrl_regs *)vaddr[0];
-
- len = prom_getproperty(uctrlnode, "intr", (char *) tmp_irq,
- sizeof(tmp_irq));
-
- /* Flush device */
- READUCTLDATA(len);
-
- if(!driver->irq)
- driver->irq = tmp_irq[0].pri;
-
- err = request_irq(driver->irq, uctrl_interrupt, 0, "uctrl", driver);
+ p->irq = op->irqs[0];
+ err = request_irq(p->irq, uctrl_interrupt, 0, "uctrl", p);
if (err) {
- printk("%s: unable to register irq %d\n",
- __func__, driver->irq);
- return err;
+ printk(KERN_ERR "uctrl: Unable to register irq.\n");
+ goto out_iounmap;
}
- if (misc_register(&uctrl_dev)) {
- printk("%s: unable to get misc minor %d\n",
- __func__, uctrl_dev.minor);
- free_irq(driver->irq, driver);
- return -ENODEV;
+ err = misc_register(&uctrl_dev);
+ if (err) {
+ printk(KERN_ERR "uctrl: Unable to register misc device.\n");
+ goto out_free_irq;
}
- driver->regs->uctrl_intr = UCTRL_INTR_RXNE_REQ|UCTRL_INTR_RXNE_MSK;
- printk("uctrl: 0x%p (irq %d)\n", driver->regs, driver->irq);
- uctrl_get_event_status();
- uctrl_get_external_status();
- return 0;
+ sbus_writel(UCTRL_INTR_RXNE_REQ|UCTRL_INTR_RXNE_MSK, &p->regs->uctrl_intr);
+ printk(KERN_INFO "%s: uctrl regs[0x%p] (irq %d)\n",
+ op->node->full_name, p->regs, p->irq);
+ uctrl_get_event_status(p);
+ uctrl_get_external_status(p);
+
+ dev_set_drvdata(&op->dev, p);
+ global_driver = p;
+
+out:
+ return err;
+
+out_free_irq:
+ free_irq(p->irq, p);
+
+out_iounmap:
+ of_iounmap(&op->resource[0], p->regs, resource_size(&op->resource[0]));
+
+out_free:
+ kfree(p);
+ goto out;
}
-static void __exit ts102_uctrl_cleanup(void)
+static int __devexit uctrl_remove(struct of_device *op)
{
- struct uctrl_driver *driver = &drv;
+ struct uctrl_driver *p = dev_get_drvdata(&op->dev);
- misc_deregister(&uctrl_dev);
- if (driver->irq)
- free_irq(driver->irq, driver);
- if (driver->regs)
- driver->regs = NULL;
+ if (p) {
+ misc_deregister(&uctrl_dev);
+ free_irq(p->irq, p);
+ of_iounmap(&op->resource[0], p->regs, resource_size(&op->resource[0]));
+ kfree(p);
+ }
+ return 0;
}
-module_init(ts102_uctrl_init);
-module_exit(ts102_uctrl_cleanup);
+static const struct of_device_id uctrl_match[] = {
+ {
+ .name = "uctrl",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, uctrl_match);
+
+static struct of_platform_driver uctrl_driver = {
+ .name = "uctrl",
+ .match_table = uctrl_match,
+ .probe = uctrl_probe,
+ .remove = __devexit_p(uctrl_remove),
+};
+
+
+static int __init uctrl_init(void)
+{
+ return of_register_driver(&uctrl_driver, &of_bus_type);
+}
+
+static void __exit uctrl_exit(void)
+{
+ of_unregister_driver(&uctrl_driver);
+}
+
+module_init(uctrl_init);
+module_exit(uctrl_exit);
MODULE_LICENSE("GPL");
diff --git a/drivers/sbus/char/vfc.h b/drivers/sbus/char/vfc.h
deleted file mode 100644
index a5240c5..0000000
--- a/drivers/sbus/char/vfc.h
+++ /dev/null
@@ -1,171 +0,0 @@
-#ifndef _LINUX_VFC_H_
-#define _LINUX_VFC_H_
-
-/*
- * The control register for the vfc is at offset 0x4000
- * The first field ram bank is located at offset 0x5000
- * The second field ram bank is at offset 0x7000
- * i2c_reg address the Phillips PCF8584(see notes in vfc_i2c.c)
- * data and transmit register.
- * i2c_s1 controls register s1 of the PCF8584
- * i2c_write seems to be similar to i2c_write but I am not
- * quite sure why sun uses it
- *
- * I am also not sure whether or not you can read the fram bank as a
- * whole or whether you must read each word individually from offset
- * 0x5000 as soon as I figure it out I will update this file */
-
-struct vfc_regs {
- char pad1[0x4000];
- unsigned int control; /* Offset 0x4000 */
- char pad2[0xffb]; /* from offset 0x4004 to 0x5000 */
- unsigned int fram_bank1; /* Offset 0x5000 */
- char pad3[0xffb]; /* from offset 0x5004 to 0x6000 */
- unsigned int i2c_reg; /* Offset 0x6000 */
- unsigned int i2c_magic2; /* Offset 0x6004 */
- unsigned int i2c_s1; /* Offset 0x6008 */
- unsigned int i2c_write; /* Offset 0x600c */
- char pad4[0xff0]; /* from offset 0x6010 to 0x7000 */
- unsigned int fram_bank2; /* Offset 0x7000 */
- char pad5[0x1000];
-};
-
-#define VFC_SAA9051_NR (13)
-#define VFC_SAA9051_ADDR (0x8a)
- /* The saa9051 returns the following for its status
- * bit 0 - 0
- * bit 1 - SECAM color detected (1=found,0=not found)
- * bit 2 - COLOR detected (1=found,0=not found)
- * bit 3 - 0
- * bit 4 - Field frequency bit (1=60Hz (NTSC), 0=50Hz (PAL))
- * bit 5 - 1
- * bit 6 - horizontal frequency lock (1=transmitter found,
- * 0=no transmitter)
- * bit 7 - Power on reset bit (1=reset,0=at least one successful
- * read of the status byte)
- */
-
-#define VFC_SAA9051_PONRES (0x80)
-#define VFC_SAA9051_HLOCK (0x40)
-#define VFC_SAA9051_FD (0x10)
-#define VFC_SAA9051_CD (0x04)
-#define VFC_SAA9051_CS (0x02)
-
-
-/* The various saa9051 sub addresses */
-
-#define VFC_SAA9051_IDEL (0)
-#define VFC_SAA9051_HSY_START (1)
-#define VFC_SAA9051_HSY_STOP (2)
-#define VFC_SAA9051_HC_START (3)
-#define VFC_SAA9051_HC_STOP (4)
-#define VFC_SAA9051_HS_START (5)
-#define VFC_SAA9051_HORIZ_PEAK (6)
-#define VFC_SAA9051_HUE (7)
-#define VFC_SAA9051_C1 (8)
-#define VFC_SAA9051_C2 (9)
-#define VFC_SAA9051_C3 (0xa)
-#define VFC_SAA9051_SECAM_DELAY (0xb)
-
-
-/* Bit settings for saa9051 sub address 0x06 */
-
-#define VFC_SAA9051_AP1 (0x01)
-#define VFC_SAA9051_AP2 (0x02)
-#define VFC_SAA9051_COR1 (0x04)
-#define VFC_SAA9051_COR2 (0x08)
-#define VFC_SAA9051_BP1 (0x10)
-#define VFC_SAA9051_BP2 (0x20)
-#define VFC_SAA9051_PF (0x40)
-#define VFC_SAA9051_BY (0x80)
-
-
-/* Bit settings for saa9051 sub address 0x08 */
-
-#define VFC_SAA9051_CCFR0 (0x01)
-#define VFC_SAA9051_CCFR1 (0x02)
-#define VFC_SAA9051_YPN (0x04)
-#define VFC_SAA9051_ALT (0x08)
-#define VFC_SAA9051_CO (0x10)
-#define VFC_SAA9051_VTR (0x20)
-#define VFC_SAA9051_FS (0x40)
-#define VFC_SAA9051_HPLL (0x80)
-
-
-/* Bit settings for saa9051 sub address 9 */
-
-#define VFC_SAA9051_SS0 (0x01)
-#define VFC_SAA9051_SS1 (0x02)
-#define VFC_SAA9051_AFCC (0x04)
-#define VFC_SAA9051_CI (0x08)
-#define VFC_SAA9051_SA9D4 (0x10) /* Don't care bit */
-#define VFC_SAA9051_OEC (0x20)
-#define VFC_SAA9051_OEY (0x40)
-#define VFC_SAA9051_VNL (0x80)
-
-
-/* Bit settings for saa9051 sub address 0x0A */
-
-#define VFC_SAA9051_YDL0 (0x01)
-#define VFC_SAA9051_YDL1 (0x02)
-#define VFC_SAA9051_YDL2 (0x04)
-#define VFC_SAA9051_SS2 (0x08)
-#define VFC_SAA9051_SS3 (0x10)
-#define VFC_SAA9051_YC (0x20)
-#define VFC_SAA9051_CT (0x40)
-#define VFC_SAA9051_SYC (0x80)
-
-
-#define VFC_SAA9051_SA(a,b) ((a)->saa9051_state_array[(b)+1])
-#define vfc_update_saa9051(a) (vfc_i2c_sendbuf((a),VFC_SAA9051_ADDR,\
- (a)->saa9051_state_array,\
- VFC_SAA9051_NR))
-
-
-struct vfc_dev {
- volatile struct vfc_regs __iomem *regs;
- struct vfc_regs *phys_regs;
- unsigned int control_reg;
- struct mutex device_lock_mtx;
- int instance;
- int busy;
- unsigned long which_io;
- unsigned char saa9051_state_array[VFC_SAA9051_NR];
-};
-
-void captstat_reset(struct vfc_dev *);
-void memptr_reset(struct vfc_dev *);
-
-int vfc_pcf8584_init(struct vfc_dev *);
-void vfc_i2c_delay_no_busy(struct vfc_dev *, unsigned long);
-void vfc_i2c_delay(struct vfc_dev *);
-int vfc_i2c_sendbuf(struct vfc_dev *, unsigned char, char *, int) ;
-int vfc_i2c_recvbuf(struct vfc_dev *, unsigned char, char *, int) ;
-int vfc_i2c_reset_bus(struct vfc_dev *);
-int vfc_init_i2c_bus(struct vfc_dev *);
-
-#define VFC_CONTROL_DIAGMODE 0x10000000
-#define VFC_CONTROL_MEMPTR 0x20000000
-#define VFC_CONTROL_CAPTURE 0x02000000
-#define VFC_CONTROL_CAPTRESET 0x04000000
-
-#define VFC_STATUS_CAPTURE 0x08000000
-
-#ifdef VFC_IOCTL_DEBUG
-#define VFC_IOCTL_DEBUG_PRINTK(a) printk a
-#else
-#define VFC_IOCTL_DEBUG_PRINTK(a)
-#endif
-
-#ifdef VFC_I2C_DEBUG
-#define VFC_I2C_DEBUG_PRINTK(a) printk a
-#else
-#define VFC_I2C_DEBUG_PRINTK(a)
-#endif
-
-#endif /* _LINUX_VFC_H_ */
-
-
-
-
-
diff --git a/drivers/sbus/char/vfc_dev.c b/drivers/sbus/char/vfc_dev.c
deleted file mode 100644
index 25181bb..0000000
--- a/drivers/sbus/char/vfc_dev.c
+++ /dev/null
@@ -1,736 +0,0 @@
-/*
- * drivers/sbus/char/vfc_dev.c
- *
- * Driver for the Videopix Frame Grabber.
- *
- * In order to use the VFC you need to program the video controller
- * chip. This chip is the Phillips SAA9051. You need to call their
- * documentation ordering line to get the docs.
- *
- * There is very little documentation on the VFC itself. There is
- * some useful info that can be found in the manuals that come with
- * the card. I will hopefully write some better docs at a later date.
- *
- * Copyright (C) 1996 Manish Vachharajani (mvachhar@noc.rutgers.edu)
- * */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/delay.h>
-#include <linux/spinlock.h>
-#include <linux/mutex.h>
-#include <linux/mm.h>
-#include <linux/smp_lock.h>
-
-#include <asm/openprom.h>
-#include <asm/oplib.h>
-#include <asm/io.h>
-#include <asm/system.h>
-#include <asm/sbus.h>
-#include <asm/page.h>
-#include <asm/pgtable.h>
-#include <asm/uaccess.h>
-
-#define VFC_MAJOR (60)
-
-#if 0
-#define VFC_IOCTL_DEBUG
-#endif
-
-#include "vfc.h"
-#include <asm/vfc_ioctls.h>
-
-static const struct file_operations vfc_fops;
-static struct vfc_dev **vfc_dev_lst;
-static char vfcstr[]="vfc";
-static unsigned char saa9051_init_array[VFC_SAA9051_NR] = {
- 0x00, 0x64, 0x72, 0x52,
- 0x36, 0x18, 0xff, 0x20,
- 0xfc, 0x77, 0xe3, 0x50,
- 0x3e
-};
-
-static void vfc_lock_device(struct vfc_dev *dev)
-{
- mutex_lock(&dev->device_lock_mtx);
-}
-
-static void vfc_unlock_device(struct vfc_dev *dev)
-{
- mutex_unlock(&dev->device_lock_mtx);
-}
-
-
-static void vfc_captstat_reset(struct vfc_dev *dev)
-{
- dev->control_reg |= VFC_CONTROL_CAPTRESET;
- sbus_writel(dev->control_reg, &dev->regs->control);
- dev->control_reg &= ~VFC_CONTROL_CAPTRESET;
- sbus_writel(dev->control_reg, &dev->regs->control);
- dev->control_reg |= VFC_CONTROL_CAPTRESET;
- sbus_writel(dev->control_reg, &dev->regs->control);
-}
-
-static void vfc_memptr_reset(struct vfc_dev *dev)
-{
- dev->control_reg |= VFC_CONTROL_MEMPTR;
- sbus_writel(dev->control_reg, &dev->regs->control);
- dev->control_reg &= ~VFC_CONTROL_MEMPTR;
- sbus_writel(dev->control_reg, &dev->regs->control);
- dev->control_reg |= VFC_CONTROL_MEMPTR;
- sbus_writel(dev->control_reg, &dev->regs->control);
-}
-
-static int vfc_csr_init(struct vfc_dev *dev)
-{
- dev->control_reg = 0x80000000;
- sbus_writel(dev->control_reg, &dev->regs->control);
- udelay(200);
- dev->control_reg &= ~0x80000000;
- sbus_writel(dev->control_reg, &dev->regs->control);
- udelay(100);
- sbus_writel(0x0f000000, &dev->regs->i2c_magic2);
-
- vfc_memptr_reset(dev);
-
- dev->control_reg &= ~VFC_CONTROL_DIAGMODE;
- dev->control_reg &= ~VFC_CONTROL_CAPTURE;
- dev->control_reg |= 0x40000000;
- sbus_writel(dev->control_reg, &dev->regs->control);
-
- vfc_captstat_reset(dev);
-
- return 0;
-}
-
-static int vfc_saa9051_init(struct vfc_dev *dev)
-{
- int i;
-
- for (i = 0; i < VFC_SAA9051_NR; i++)
- dev->saa9051_state_array[i] = saa9051_init_array[i];
-
- vfc_i2c_sendbuf(dev,VFC_SAA9051_ADDR,
- dev->saa9051_state_array, VFC_SAA9051_NR);
- return 0;
-}
-
-static int init_vfc_hw(struct vfc_dev *dev)
-{
- vfc_lock_device(dev);
- vfc_csr_init(dev);
-
- vfc_pcf8584_init(dev);
- vfc_init_i2c_bus(dev); /* hopefully this doesn't undo the magic
- sun code above*/
- vfc_saa9051_init(dev);
- vfc_unlock_device(dev);
- return 0;
-}
-
-static int init_vfc_devstruct(struct vfc_dev *dev, int instance)
-{
- dev->instance=instance;
- mutex_init(&dev->device_lock_mtx);
- dev->control_reg=0;
- dev->busy=0;
- return 0;
-}
-
-static int init_vfc_device(struct sbus_dev *sdev,struct vfc_dev *dev,
- int instance)
-{
- if(dev == NULL) {
- printk(KERN_ERR "VFC: Bogus pointer passed\n");
- return -ENOMEM;
- }
- printk("Initializing vfc%d\n",instance);
- dev->regs = NULL;
- dev->regs = (volatile struct vfc_regs __iomem *)
- sbus_ioremap(&sdev->resource[0], 0,
- sizeof(struct vfc_regs), vfcstr);
- dev->which_io = sdev->reg_addrs[0].which_io;
- dev->phys_regs = (struct vfc_regs *) sdev->reg_addrs[0].phys_addr;
- if (dev->regs == NULL)
- return -EIO;
-
- printk("vfc%d: registers mapped at phys_addr: 0x%lx\n virt_addr: 0x%lx\n",
- instance,(unsigned long)sdev->reg_addrs[0].phys_addr,(unsigned long)dev->regs);
-
- if (init_vfc_devstruct(dev, instance))
- return -EINVAL;
- if (init_vfc_hw(dev))
- return -EIO;
- return 0;
-}
-
-
-static struct vfc_dev *vfc_get_dev_ptr(int instance)
-{
- return vfc_dev_lst[instance];
-}
-
-static DEFINE_SPINLOCK(vfc_dev_lock);
-
-static int vfc_open(struct inode *inode, struct file *file)
-{
- struct vfc_dev *dev;
-
- lock_kernel();
- spin_lock(&vfc_dev_lock);
- dev = vfc_get_dev_ptr(iminor(inode));
- if (dev == NULL) {
- spin_unlock(&vfc_dev_lock);
- unlock_kernel();
- return -ENODEV;
- }
- if (dev->busy) {
- spin_unlock(&vfc_dev_lock);
- unlock_kernel();
- return -EBUSY;
- }
-
- dev->busy = 1;
- spin_unlock(&vfc_dev_lock);
-
- vfc_lock_device(dev);
-
- vfc_csr_init(dev);
- vfc_pcf8584_init(dev);
- vfc_init_i2c_bus(dev);
- vfc_saa9051_init(dev);
- vfc_memptr_reset(dev);
- vfc_captstat_reset(dev);
-
- vfc_unlock_device(dev);
- unlock_kernel();
- return 0;
-}
-
-static int vfc_release(struct inode *inode,struct file *file)
-{
- struct vfc_dev *dev;
-
- spin_lock(&vfc_dev_lock);
- dev = vfc_get_dev_ptr(iminor(inode));
- if (!dev || !dev->busy) {
- spin_unlock(&vfc_dev_lock);
- return -EINVAL;
- }
- dev->busy = 0;
- spin_unlock(&vfc_dev_lock);
- return 0;
-}
-
-static int vfc_debug(struct vfc_dev *dev, int cmd, void __user *argp)
-{
- struct vfc_debug_inout inout;
- unsigned char *buffer;
-
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
- switch(cmd) {
- case VFC_I2C_SEND:
- if(copy_from_user(&inout, argp, sizeof(inout)))
- return -EFAULT;
-
- buffer = kmalloc(inout.len, GFP_KERNEL);
- if (buffer == NULL)
- return -ENOMEM;
-
- if(copy_from_user(buffer, inout.buffer, inout.len)) {
- kfree(buffer);
- return -EFAULT;
- }
-
-
- vfc_lock_device(dev);
- inout.ret=
- vfc_i2c_sendbuf(dev,inout.addr & 0xff,
- buffer,inout.len);
-
- if (copy_to_user(argp,&inout,sizeof(inout))) {
- vfc_unlock_device(dev);
- kfree(buffer);
- return -EFAULT;
- }
- vfc_unlock_device(dev);
-
- break;
- case VFC_I2C_RECV:
- if (copy_from_user(&inout, argp, sizeof(inout)))
- return -EFAULT;
-
- buffer = kzalloc(inout.len, GFP_KERNEL);
- if (buffer == NULL)
- return -ENOMEM;
-
- vfc_lock_device(dev);
- inout.ret=
- vfc_i2c_recvbuf(dev,inout.addr & 0xff
- ,buffer,inout.len);
- vfc_unlock_device(dev);
-
- if (copy_to_user(inout.buffer, buffer, inout.len)) {
- kfree(buffer);
- return -EFAULT;
- }
- if (copy_to_user(argp,&inout,sizeof(inout))) {
- kfree(buffer);
- return -EFAULT;
- }
- kfree(buffer);
- break;
- default:
- return -EINVAL;
- };
-
- return 0;
-}
-
-static int vfc_capture_start(struct vfc_dev *dev)
-{
- vfc_captstat_reset(dev);
- dev->control_reg = sbus_readl(&dev->regs->control);
- if((dev->control_reg & VFC_STATUS_CAPTURE)) {
- printk(KERN_ERR "vfc%d: vfc capture status not reset\n",
- dev->instance);
- return -EIO;
- }
-
- vfc_lock_device(dev);
- dev->control_reg &= ~VFC_CONTROL_CAPTURE;
- sbus_writel(dev->control_reg, &dev->regs->control);
- dev->control_reg |= VFC_CONTROL_CAPTURE;
- sbus_writel(dev->control_reg, &dev->regs->control);
- dev->control_reg &= ~VFC_CONTROL_CAPTURE;
- sbus_writel(dev->control_reg, &dev->regs->control);
- vfc_unlock_device(dev);
-
- return 0;
-}
-
-static int vfc_capture_poll(struct vfc_dev *dev)
-{
- int timeout = 1000;
-
- while (!timeout--) {
- if (sbus_readl(&dev->regs->control) & VFC_STATUS_CAPTURE)
- break;
- vfc_i2c_delay_no_busy(dev, 100);
- }
- if(!timeout) {
- printk(KERN_WARNING "vfc%d: capture timed out\n",
- dev->instance);
- return -ETIMEDOUT;
- }
- return 0;
-}
-
-
-
-static int vfc_set_control_ioctl(struct inode *inode, struct file *file,
- struct vfc_dev *dev, unsigned long arg)
-{
- int setcmd, ret = 0;
-
- if (copy_from_user(&setcmd,(void __user *)arg,sizeof(unsigned int)))
- return -EFAULT;
-
- VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCSCTRL) arg=0x%x\n",
- dev->instance,setcmd));
-
- switch(setcmd) {
- case MEMPRST:
- vfc_lock_device(dev);
- vfc_memptr_reset(dev);
- vfc_unlock_device(dev);
- ret=0;
- break;
- case CAPTRCMD:
- vfc_capture_start(dev);
- vfc_capture_poll(dev);
- break;
- case DIAGMODE:
- if(capable(CAP_SYS_ADMIN)) {
- vfc_lock_device(dev);
- dev->control_reg |= VFC_CONTROL_DIAGMODE;
- sbus_writel(dev->control_reg, &dev->regs->control);
- vfc_unlock_device(dev);
- ret = 0;
- } else {
- ret = -EPERM;
- }
- break;
- case NORMMODE:
- vfc_lock_device(dev);
- dev->control_reg &= ~VFC_CONTROL_DIAGMODE;
- sbus_writel(dev->control_reg, &dev->regs->control);
- vfc_unlock_device(dev);
- ret = 0;
- break;
- case CAPTRSTR:
- vfc_capture_start(dev);
- ret = 0;
- break;
- case CAPTRWAIT:
- vfc_capture_poll(dev);
- ret = 0;
- break;
- default:
- ret = -EINVAL;
- break;
- };
-
- return ret;
-}
-
-
-static int vfc_port_change_ioctl(struct inode *inode, struct file *file,
- struct vfc_dev *dev, unsigned long arg)
-{
- int ret = 0;
- int cmd;
-
- if(copy_from_user(&cmd, (void __user *)arg, sizeof(unsigned int))) {
- VFC_IOCTL_DEBUG_PRINTK(("vfc%d: User passed bogus pointer to "
- "vfc_port_change_ioctl\n",
- dev->instance));
- return -EFAULT;
- }
-
- VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCPORTCHG) arg=0x%x\n",
- dev->instance, cmd));
-
- switch(cmd) {
- case 1:
- case 2:
- VFC_SAA9051_SA(dev,VFC_SAA9051_HSY_START) = 0x72;
- VFC_SAA9051_SA(dev,VFC_SAA9051_HSY_STOP) = 0x52;
- VFC_SAA9051_SA(dev,VFC_SAA9051_HC_START) = 0x36;
- VFC_SAA9051_SA(dev,VFC_SAA9051_HC_STOP) = 0x18;
- VFC_SAA9051_SA(dev,VFC_SAA9051_HORIZ_PEAK) = VFC_SAA9051_BP2;
- VFC_SAA9051_SA(dev,VFC_SAA9051_C3) = VFC_SAA9051_CT | VFC_SAA9051_SS3;
- VFC_SAA9051_SA(dev,VFC_SAA9051_SECAM_DELAY) = 0x3e;
- break;
- case 3:
- VFC_SAA9051_SA(dev,VFC_SAA9051_HSY_START) = 0x3a;
- VFC_SAA9051_SA(dev,VFC_SAA9051_HSY_STOP) = 0x17;
- VFC_SAA9051_SA(dev,VFC_SAA9051_HC_START) = 0xfa;
- VFC_SAA9051_SA(dev,VFC_SAA9051_HC_STOP) = 0xde;
- VFC_SAA9051_SA(dev,VFC_SAA9051_HORIZ_PEAK) =
- VFC_SAA9051_BY | VFC_SAA9051_PF | VFC_SAA9051_BP2;
- VFC_SAA9051_SA(dev,VFC_SAA9051_C3) = VFC_SAA9051_YC;
- VFC_SAA9051_SA(dev,VFC_SAA9051_SECAM_DELAY) = 0;
- VFC_SAA9051_SA(dev,VFC_SAA9051_C2) &=
- ~(VFC_SAA9051_SS0 | VFC_SAA9051_SS1);
- break;
- default:
- ret = -EINVAL;
- return ret;
- break;
- }
-
- switch(cmd) {
- case 1:
- VFC_SAA9051_SA(dev,VFC_SAA9051_C2) |=
- (VFC_SAA9051_SS0 | VFC_SAA9051_SS1);
- break;
- case 2:
- VFC_SAA9051_SA(dev,VFC_SAA9051_C2) &=
- ~(VFC_SAA9051_SS0 | VFC_SAA9051_SS1);
- VFC_SAA9051_SA(dev,VFC_SAA9051_C2) |= VFC_SAA9051_SS0;
- break;
- case 3:
- break;
- default:
- ret = -EINVAL;
- return ret;
- break;
- }
- VFC_SAA9051_SA(dev,VFC_SAA9051_C3) &= ~(VFC_SAA9051_SS2);
- ret=vfc_update_saa9051(dev);
- udelay(500);
- VFC_SAA9051_SA(dev,VFC_SAA9051_C3) |= (VFC_SAA9051_SS2);
- ret=vfc_update_saa9051(dev);
- return ret;
-}
-
-static int vfc_set_video_ioctl(struct inode *inode, struct file *file,
- struct vfc_dev *dev, unsigned long arg)
-{
- int ret = 0;
- int cmd;
-
- if(copy_from_user(&cmd, (void __user *)arg, sizeof(unsigned int))) {
- VFC_IOCTL_DEBUG_PRINTK(("vfc%d: User passed bogus pointer to "
- "vfc_set_video_ioctl\n",
- dev->instance));
- return ret;
- }
-
- VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCSVID) arg=0x%x\n",
- dev->instance, cmd));
- switch(cmd) {
- case STD_NTSC:
- VFC_SAA9051_SA(dev,VFC_SAA9051_C1) &= ~VFC_SAA9051_ALT;
- VFC_SAA9051_SA(dev,VFC_SAA9051_C1) |= VFC_SAA9051_YPN |
- VFC_SAA9051_CCFR0 | VFC_SAA9051_CCFR1 | VFC_SAA9051_FS;
- ret = vfc_update_saa9051(dev);
- break;
- case STD_PAL:
- VFC_SAA9051_SA(dev,VFC_SAA9051_C1) &= ~(VFC_SAA9051_YPN |
- VFC_SAA9051_CCFR1 |
- VFC_SAA9051_CCFR0 |
- VFC_SAA9051_FS);
- VFC_SAA9051_SA(dev,VFC_SAA9051_C1) |= VFC_SAA9051_ALT;
- ret = vfc_update_saa9051(dev);
- break;
-
- case COLOR_ON:
- VFC_SAA9051_SA(dev,VFC_SAA9051_C1) |= VFC_SAA9051_CO;
- VFC_SAA9051_SA(dev,VFC_SAA9051_HORIZ_PEAK) &=
- ~(VFC_SAA9051_BY | VFC_SAA9051_PF);
- ret = vfc_update_saa9051(dev);
- break;
- case MONO:
- VFC_SAA9051_SA(dev,VFC_SAA9051_C1) &= ~(VFC_SAA9051_CO);
- VFC_SAA9051_SA(dev,VFC_SAA9051_HORIZ_PEAK) |=
- (VFC_SAA9051_BY | VFC_SAA9051_PF);
- ret = vfc_update_saa9051(dev);
- break;
- default:
- ret = -EINVAL;
- break;
- };
-
- return ret;
-}
-
-static int vfc_get_video_ioctl(struct inode *inode, struct file *file,
- struct vfc_dev *dev, unsigned long arg)
-{
- int ret = 0;
- unsigned int status = NO_LOCK;
- unsigned char buf[1];
-
- if(vfc_i2c_recvbuf(dev, VFC_SAA9051_ADDR, buf, 1)) {
- printk(KERN_ERR "vfc%d: Unable to get status\n",
- dev->instance);
- return -EIO;
- }
-
- if(buf[0] & VFC_SAA9051_HLOCK) {
- status = NO_LOCK;
- } else if(buf[0] & VFC_SAA9051_FD) {
- if(buf[0] & VFC_SAA9051_CD)
- status = NTSC_COLOR;
- else
- status = NTSC_NOCOLOR;
- } else {
- if(buf[0] & VFC_SAA9051_CD)
- status = PAL_COLOR;
- else
- status = PAL_NOCOLOR;
- }
- VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCGVID) returning status 0x%x; "
- "buf[0]=%x\n", dev->instance, status, buf[0]));
-
- if (copy_to_user((void __user *)arg,&status,sizeof(unsigned int))) {
- VFC_IOCTL_DEBUG_PRINTK(("vfc%d: User passed bogus pointer to "
- "vfc_get_video_ioctl\n",
- dev->instance));
- return ret;
- }
- return ret;
-}
-
-static int vfc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- int ret = 0;
- unsigned int tmp;
- struct vfc_dev *dev;
- void __user *argp = (void __user *)arg;
-
- dev = vfc_get_dev_ptr(iminor(inode));
- if(dev == NULL)
- return -ENODEV;
-
- switch(cmd & 0x0000ffff) {
- case VFCGCTRL:
-#if 0
- VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCGCTRL)\n", dev->instance));
-#endif
- tmp = sbus_readl(&dev->regs->control);
- if(copy_to_user(argp, &tmp, sizeof(unsigned int))) {
- ret = -EFAULT;
- break;
- }
- ret = 0;
- break;
- case VFCSCTRL:
- ret = vfc_set_control_ioctl(inode, file, dev, arg);
- break;
- case VFCGVID:
- ret = vfc_get_video_ioctl(inode, file, dev, arg);
- break;
- case VFCSVID:
- ret = vfc_set_video_ioctl(inode, file, dev, arg);
- break;
- case VFCHUE:
- VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCHUE)\n", dev->instance));
- if(copy_from_user(&tmp,argp,sizeof(unsigned int))) {
- VFC_IOCTL_DEBUG_PRINTK(("vfc%d: User passed bogus pointer "
- "to IOCTL(VFCHUE)", dev->instance));
- ret = -EFAULT;
- } else {
- VFC_SAA9051_SA(dev,VFC_SAA9051_HUE) = tmp;
- vfc_update_saa9051(dev);
- ret = 0;
- }
- break;
- case VFCPORTCHG:
- ret = vfc_port_change_ioctl(inode, file, dev, arg);
- break;
- case VFCRDINFO:
- ret = -EINVAL;
- VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCRDINFO)\n", dev->instance));
- break;
- default:
- ret = vfc_debug(vfc_get_dev_ptr(iminor(inode)), cmd, argp);
- break;
- };
-
- return ret;
-}
-
-static int vfc_mmap(struct file *file, struct vm_area_struct *vma)
-{
- unsigned int map_size, ret, map_offset;
- struct vfc_dev *dev;
-
- dev = vfc_get_dev_ptr(iminor(file->f_path.dentry->d_inode));
- if(dev == NULL)
- return -ENODEV;
-
- map_size = vma->vm_end - vma->vm_start;
- if(map_size > sizeof(struct vfc_regs))
- map_size = sizeof(struct vfc_regs);
-
- vma->vm_flags |=
- (VM_MAYREAD | VM_MAYWRITE | VM_MAYSHARE);
- map_offset = (unsigned int) (long)dev->phys_regs;
- ret = io_remap_pfn_range(vma, vma->vm_start,
- MK_IOSPACE_PFN(dev->which_io,
- map_offset >> PAGE_SHIFT),
- map_size, vma->vm_page_prot);
-
- if(ret)
- return -EAGAIN;
-
- return 0;
-}
-
-
-static const struct file_operations vfc_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .ioctl = vfc_ioctl,
- .mmap = vfc_mmap,
- .open = vfc_open,
- .release = vfc_release,
-};
-
-static int vfc_probe(void)
-{
- struct sbus_bus *sbus;
- struct sbus_dev *sdev = NULL;
- int ret;
- int instance = 0, cards = 0;
-
- for_all_sbusdev(sdev, sbus) {
- if (strcmp(sdev->prom_name, "vfc") == 0) {
- cards++;
- continue;
- }
- }
-
- if (!cards)
- return -ENODEV;
-
- vfc_dev_lst = kcalloc(cards + 1, sizeof(struct vfc_dev*), GFP_KERNEL);
- if (vfc_dev_lst == NULL)
- return -ENOMEM;
- vfc_dev_lst[cards] = NULL;
-
- ret = register_chrdev(VFC_MAJOR, vfcstr, &vfc_fops);
- if(ret) {
- printk(KERN_ERR "Unable to get major number %d\n", VFC_MAJOR);
- kfree(vfc_dev_lst);
- return -EIO;
- }
- instance = 0;
- for_all_sbusdev(sdev, sbus) {
- if (strcmp(sdev->prom_name, "vfc") == 0) {
- vfc_dev_lst[instance]=(struct vfc_dev *)
- kmalloc(sizeof(struct vfc_dev), GFP_KERNEL);
- if (vfc_dev_lst[instance] == NULL)
- return -ENOMEM;
- ret = init_vfc_device(sdev,
- vfc_dev_lst[instance],
- instance);
- if(ret) {
- printk(KERN_ERR "Unable to initialize"
- " vfc%d device\n",
- instance);
- } else {
- }
-
- instance++;
- continue;
- }
- }
-
- return 0;
-}
-
-#ifdef MODULE
-int init_module(void)
-#else
-int vfc_init(void)
-#endif
-{
- return vfc_probe();
-}
-
-#ifdef MODULE
-static void deinit_vfc_device(struct vfc_dev *dev)
-{
- if(dev == NULL)
- return;
- sbus_iounmap(dev->regs, sizeof(struct vfc_regs));
- kfree(dev);
-}
-
-void cleanup_module(void)
-{
- struct vfc_dev **devp;
-
- unregister_chrdev(VFC_MAJOR,vfcstr);
-
- for (devp = vfc_dev_lst; *devp; devp++)
- deinit_vfc_device(*devp);
-
- kfree(vfc_dev_lst);
- return;
-}
-#endif
-
-MODULE_LICENSE("GPL");
-
diff --git a/drivers/sbus/char/vfc_i2c.c b/drivers/sbus/char/vfc_i2c.c
deleted file mode 100644
index 32b986e..0000000
--- a/drivers/sbus/char/vfc_i2c.c
+++ /dev/null
@@ -1,335 +0,0 @@
-/*
- * drivers/sbus/char/vfc_i2c.c
- *
- * Driver for the Videopix Frame Grabber.
- *
- * Functions that support the Phillips i2c(I squared C) bus on the vfc
- * Documentation for the Phillips I2C bus can be found on the
- * phillips home page
- *
- * Copyright (C) 1996 Manish Vachharajani (mvachhar@noc.rutgers.edu)
- *
- */
-
-/* NOTE: It seems to me that the documentation regarding the
-pcd8584t/pcf8584 does not show the correct way to address the i2c bus.
-Based on the information on the I2C bus itself and the remainder of
-the Phillips docs the following algorithms appear to be correct. I am
-fairly certain that the flowcharts in the phillips docs are wrong. */
-
-
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/wait.h>
-#include <linux/delay.h>
-#include <asm/openprom.h>
-#include <asm/oplib.h>
-#include <asm/io.h>
-#include <asm/system.h>
-#include <asm/sbus.h>
-
-#if 0
-#define VFC_I2C_DEBUG
-#endif
-
-#include "vfc.h"
-#include "vfc_i2c.h"
-
-#define WRITE_S1(__val) \
- sbus_writel(__val, &dev->regs->i2c_s1)
-#define WRITE_REG(__val) \
- sbus_writel(__val, &dev->regs->i2c_reg)
-
-#define VFC_I2C_READ (0x1)
-#define VFC_I2C_WRITE (0x0)
-
-/******
- The i2c bus controller chip on the VFC is a pcd8584t, but
- phillips claims it doesn't exist. As far as I can tell it is
- identical to the PCF8584 so I treat it like it is the pcf8584.
-
- NOTE: The pcf8584 only cares
- about the msb of the word you feed it
-*****/
-
-int vfc_pcf8584_init(struct vfc_dev *dev)
-{
- /* This will also choose register S0_OWN so we can set it. */
- WRITE_S1(RESET);
-
- /* The pcf8584 shifts this value left one bit and uses
- * it as its i2c bus address.
- */
- WRITE_REG(0x55000000);
-
- /* This will set the i2c bus at the same speed sun uses,
- * and set another magic bit.
- */
- WRITE_S1(SELECT(S2));
- WRITE_REG(0x14000000);
-
- /* Enable the serial port, idle the i2c bus and set
- * the data reg to s0.
- */
- WRITE_S1(CLEAR_I2C_BUS);
- udelay(100);
- return 0;
-}
-
-void vfc_i2c_delay_no_busy(struct vfc_dev *dev, unsigned long usecs)
-{
- schedule_timeout_uninterruptible(usecs_to_jiffies(usecs));
-}
-
-void inline vfc_i2c_delay(struct vfc_dev *dev)
-{
- vfc_i2c_delay_no_busy(dev, 100);
-}
-
-int vfc_init_i2c_bus(struct vfc_dev *dev)
-{
- WRITE_S1(ENABLE_SERIAL | SELECT(S0) | ACK);
- vfc_i2c_reset_bus(dev);
- return 0;
-}
-
-int vfc_i2c_reset_bus(struct vfc_dev *dev)
-{
- VFC_I2C_DEBUG_PRINTK((KERN_DEBUG "vfc%d: Resetting the i2c bus\n",
- dev->instance));
- if(dev == NULL)
- return -EINVAL;
- if(dev->regs == NULL)
- return -EINVAL;
- WRITE_S1(SEND_I2C_STOP);
- WRITE_S1(SEND_I2C_STOP | ACK);
- vfc_i2c_delay(dev);
- WRITE_S1(CLEAR_I2C_BUS);
- VFC_I2C_DEBUG_PRINTK((KERN_DEBUG "vfc%d: I2C status %x\n",
- dev->instance,
- sbus_readl(&dev->regs->i2c_s1)));
- return 0;
-}
-
-static int vfc_i2c_wait_for_bus(struct vfc_dev *dev)
-{
- int timeout = 1000;
-
- while(!(sbus_readl(&dev->regs->i2c_s1) & BB)) {
- if(!(timeout--))
- return -ETIMEDOUT;
- vfc_i2c_delay(dev);
- }
- return 0;
-}
-
-static int vfc_i2c_wait_for_pin(struct vfc_dev *dev, int ack)
-{
- int timeout = 1000;
- int s1;
-
- while ((s1 = sbus_readl(&dev->regs->i2c_s1)) & PIN) {
- if (!(timeout--))
- return -ETIMEDOUT;
- vfc_i2c_delay(dev);
- }
- if (ack == VFC_I2C_ACK_CHECK) {
- if(s1 & LRB)
- return -EIO;
- }
- return 0;
-}
-
-#define SHIFT(a) ((a) << 24)
-static int vfc_i2c_xmit_addr(struct vfc_dev *dev, unsigned char addr,
- char mode)
-{
- int ret, raddr;
-#if 1
- WRITE_S1(SEND_I2C_STOP | ACK);
- WRITE_S1(SELECT(S0) | ENABLE_SERIAL);
- vfc_i2c_delay(dev);
-#endif
-
- switch(mode) {
- case VFC_I2C_READ:
- raddr = SHIFT(((unsigned int)addr | 0x1));
- WRITE_REG(raddr);
- VFC_I2C_DEBUG_PRINTK(("vfc%d: receiving from i2c addr 0x%x\n",
- dev->instance, addr | 0x1));
- break;
- case VFC_I2C_WRITE:
- raddr = SHIFT((unsigned int)addr & ~0x1);
- WRITE_REG(raddr);
- VFC_I2C_DEBUG_PRINTK(("vfc%d: sending to i2c addr 0x%x\n",
- dev->instance, addr & ~0x1));
- break;
- default:
- return -EINVAL;
- };
-
- WRITE_S1(SEND_I2C_START);
- vfc_i2c_delay(dev);
- ret = vfc_i2c_wait_for_pin(dev,VFC_I2C_ACK_CHECK); /* We wait
- for the
- i2c send
- to finish
- here but
- Sun
- doesn't,
- hmm */
- if (ret) {
- printk(KERN_ERR "vfc%d: VFC xmit addr timed out or no ack\n",
- dev->instance);
- return ret;
- } else if (mode == VFC_I2C_READ) {
- if ((ret = sbus_readl(&dev->regs->i2c_reg) & 0xff000000) != raddr) {
- printk(KERN_WARNING
- "vfc%d: returned slave address "
- "mismatch(%x,%x)\n",
- dev->instance, raddr, ret);
- }
- }
- return 0;
-}
-
-static int vfc_i2c_xmit_byte(struct vfc_dev *dev,unsigned char *byte)
-{
- int ret;
- u32 val = SHIFT((unsigned int)*byte);
-
- WRITE_REG(val);
-
- ret = vfc_i2c_wait_for_pin(dev, VFC_I2C_ACK_CHECK);
- switch(ret) {
- case -ETIMEDOUT:
- printk(KERN_ERR "vfc%d: VFC xmit byte timed out or no ack\n",
- dev->instance);
- break;
- case -EIO:
- ret = XMIT_LAST_BYTE;
- break;
- default:
- break;
- };
-
- return ret;
-}
-
-static int vfc_i2c_recv_byte(struct vfc_dev *dev, unsigned char *byte,
- int last)
-{
- int ret;
-
- if (last) {
- WRITE_REG(NEGATIVE_ACK);
- VFC_I2C_DEBUG_PRINTK(("vfc%d: sending negative ack\n",
- dev->instance));
- } else {
- WRITE_S1(ACK);
- }
-
- ret = vfc_i2c_wait_for_pin(dev, VFC_I2C_NO_ACK_CHECK);
- if(ret) {
- printk(KERN_ERR "vfc%d: "
- "VFC recv byte timed out\n",
- dev->instance);
- }
- *byte = (sbus_readl(&dev->regs->i2c_reg)) >> 24;
- return ret;
-}
-
-int vfc_i2c_recvbuf(struct vfc_dev *dev, unsigned char addr,
- char *buf, int count)
-{
- int ret, last;
-
- if(!(count && buf && dev && dev->regs) )
- return -EINVAL;
-
- if ((ret = vfc_i2c_wait_for_bus(dev))) {
- printk(KERN_ERR "vfc%d: VFC I2C bus busy\n", dev->instance);
- return ret;
- }
-
- if ((ret = vfc_i2c_xmit_addr(dev, addr, VFC_I2C_READ))) {
- WRITE_S1(SEND_I2C_STOP);
- vfc_i2c_delay(dev);
- return ret;
- }
-
- last = 0;
- while (count--) {
- if (!count)
- last = 1;
- if ((ret = vfc_i2c_recv_byte(dev, buf, last))) {
- printk(KERN_ERR "vfc%d: "
- "VFC error while receiving byte\n",
- dev->instance);
- WRITE_S1(SEND_I2C_STOP);
- ret = -EINVAL;
- }
- buf++;
- }
- WRITE_S1(SEND_I2C_STOP | ACK);
- vfc_i2c_delay(dev);
- return ret;
-}
-
-int vfc_i2c_sendbuf(struct vfc_dev *dev, unsigned char addr,
- char *buf, int count)
-{
- int ret;
-
- if (!(buf && dev && dev->regs))
- return -EINVAL;
-
- if ((ret = vfc_i2c_wait_for_bus(dev))) {
- printk(KERN_ERR "vfc%d: VFC I2C bus busy\n", dev->instance);
- return ret;
- }
-
- if ((ret = vfc_i2c_xmit_addr(dev, addr, VFC_I2C_WRITE))) {
- WRITE_S1(SEND_I2C_STOP);
- vfc_i2c_delay(dev);
- return ret;
- }
-
- while(count--) {
- ret = vfc_i2c_xmit_byte(dev, buf);
- switch(ret) {
- case XMIT_LAST_BYTE:
- VFC_I2C_DEBUG_PRINTK(("vfc%d: "
- "Receiver ended transmission with "
- " %d bytes remaining\n",
- dev->instance, count));
- ret = 0;
- goto done;
- break;
- case 0:
- break;
- default:
- printk(KERN_ERR "vfc%d: "
- "VFC error while sending byte\n", dev->instance);
- break;
- };
-
- buf++;
- }
-done:
- WRITE_S1(SEND_I2C_STOP | ACK);
- vfc_i2c_delay(dev);
- return ret;
-}
-
-
-
-
-
-
-
-
-
diff --git a/drivers/sbus/char/vfc_i2c.h b/drivers/sbus/char/vfc_i2c.h
deleted file mode 100644
index a2e6973..0000000
--- a/drivers/sbus/char/vfc_i2c.h
+++ /dev/null
@@ -1,44 +0,0 @@
-#ifndef _LINUX_VFC_I2C_H_
-#define _LINUX_VFC_I2C_H_
-
-/* control bits */
-#define PIN (0x80000000)
-#define ESO (0x40000000)
-#define ES1 (0x20000000)
-#define ES2 (0x10000000)
-#define ENI (0x08000000)
-#define STA (0x04000000)
-#define STO (0x02000000)
-#define ACK (0x01000000)
-
-/* status bits */
-#define STS (0x20000000)
-#define BER (0x10000000)
-#define LRB (0x08000000)
-#define AAS (0x04000000)
-#define LAB (0x02000000)
-#define BB (0x01000000)
-
-#define SEND_I2C_START (PIN | ESO | STA)
-#define SEND_I2C_STOP (PIN | ESO | STO)
-#define CLEAR_I2C_BUS (PIN | ESO | ACK)
-#define NEGATIVE_ACK ((ESO) & ~ACK)
-
-#define SELECT(a) (a)
-#define S0 (PIN | ESO | ES1)
-#define S0_OWN (PIN)
-#define S2 (PIN | ES1)
-#define S3 (PIN | ES2)
-
-#define ENABLE_SERIAL (PIN | ESO)
-#define DISABLE_SERIAL (PIN)
-#define RESET (PIN)
-
-#define XMIT_LAST_BYTE (1)
-#define VFC_I2C_ACK_CHECK (1)
-#define VFC_I2C_NO_ACK_CHECK (0)
-
-#endif /* _LINUX_VFC_I2C_H_ */
-
-
-
diff --git a/drivers/sbus/dvma.c b/drivers/sbus/dvma.c
deleted file mode 100644
index ab0d2de..0000000
--- a/drivers/sbus/dvma.c
+++ /dev/null
@@ -1,136 +0,0 @@
-/* dvma.c: Routines that are used to access DMA on the Sparc SBus.
- *
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- */
-
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-
-#include <asm/oplib.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <asm/sbus.h>
-
-struct sbus_dma *dma_chain;
-
-static void __init init_one_dvma(struct sbus_dma *dma, int num_dma)
-{
- printk("dma%d: ", num_dma);
-
- dma->next = NULL;
- dma->running = 0; /* No transfers going on as of yet */
- dma->allocated = 0; /* No one has allocated us yet */
- switch(sbus_readl(dma->regs + DMA_CSR)&DMA_DEVICE_ID) {
- case DMA_VERS0:
- dma->revision = dvmarev0;
- printk("Revision 0 ");
- break;
- case DMA_ESCV1:
- dma->revision = dvmaesc1;
- printk("ESC Revision 1 ");
- break;
- case DMA_VERS1:
- dma->revision = dvmarev1;
- printk("Revision 1 ");
- break;
- case DMA_VERS2:
- dma->revision = dvmarev2;
- printk("Revision 2 ");
- break;
- case DMA_VERHME:
- dma->revision = dvmahme;
- printk("HME DVMA gate array ");
- break;
- case DMA_VERSPLUS:
- dma->revision = dvmarevplus;
- printk("Revision 1 PLUS ");
- break;
- default:
- printk("unknown dma version %08x",
- sbus_readl(dma->regs + DMA_CSR) & DMA_DEVICE_ID);
- dma->allocated = 1;
- break;
- }
- printk("\n");
-}
-
-/* Probe this SBus DMA module(s) */
-void __init dvma_init(struct sbus_bus *sbus)
-{
- struct sbus_dev *this_dev;
- struct sbus_dma *dma;
- struct sbus_dma *dchain;
- static int num_dma = 0;
-
- for_each_sbusdev(this_dev, sbus) {
- char *name = this_dev->prom_name;
- int hme = 0;
-
- if(!strcmp(name, "SUNW,fas"))
- hme = 1;
- else if(strcmp(name, "dma") &&
- strcmp(name, "ledma") &&
- strcmp(name, "espdma"))
- continue;
-
- /* Found one... */
- dma = kmalloc(sizeof(struct sbus_dma), GFP_ATOMIC);
-
- dma->sdev = this_dev;
-
- /* Put at end of dma chain */
- dchain = dma_chain;
- if(dchain) {
- while(dchain->next)
- dchain = dchain->next;
- dchain->next = dma;
- } else {
- /* We're the first in line */
- dma_chain = dma;
- }
-
- dma->regs = sbus_ioremap(&dma->sdev->resource[0], 0,
- dma->sdev->resource[0].end - dma->sdev->resource[0].start + 1,
- "dma");
-
- dma->node = dma->sdev->prom_node;
-
- init_one_dvma(dma, num_dma++);
- }
-}
-
-#ifdef CONFIG_SUN4
-
-#include <asm/sun4paddr.h>
-
-void __init sun4_dvma_init(void)
-{
- struct sbus_dma *dma;
- struct resource r;
-
- if(sun4_dma_physaddr) {
- dma = kmalloc(sizeof(struct sbus_dma), GFP_ATOMIC);
-
- /* No SBUS */
- dma->sdev = NULL;
-
- /* Only one DMA device */
- dma_chain = dma;
-
- memset(&r, 0, sizeof(r));
- r.start = sun4_dma_physaddr;
- dma->regs = sbus_ioremap(&r, 0, PAGE_SIZE, "dma");
-
- /* No prom node */
- dma->node = 0x0;
-
- init_one_dvma(dma, 0);
- } else {
- dma_chain = NULL;
- }
-}
-
-#endif
diff --git a/drivers/sbus/sbus.c b/drivers/sbus/sbus.c
deleted file mode 100644
index 9c12924..0000000
--- a/drivers/sbus/sbus.c
+++ /dev/null
@@ -1,316 +0,0 @@
-/* sbus.c: SBus support routines.
- *
- * Copyright (C) 1995, 2006 David S. Miller (davem@davemloft.net)
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/of_device.h>
-
-#include <asm/system.h>
-#include <asm/sbus.h>
-#include <asm/dma.h>
-#include <asm/oplib.h>
-#include <asm/prom.h>
-#include <asm/bpp.h>
-#include <asm/irq.h>
-
-static ssize_t
-show_sbusobppath_attr(struct device * dev, struct device_attribute * attr, char * buf)
-{
- struct sbus_dev *sbus;
-
- sbus = to_sbus_device(dev);
-
- return snprintf (buf, PAGE_SIZE, "%s\n", sbus->ofdev.node->full_name);
-}
-
-static DEVICE_ATTR(obppath, S_IRUSR | S_IRGRP | S_IROTH, show_sbusobppath_attr, NULL);
-
-struct sbus_bus *sbus_root;
-
-static void __init fill_sbus_device(struct device_node *dp, struct sbus_dev *sdev)
-{
- struct dev_archdata *sd;
- unsigned long base;
- const void *pval;
- int len, err;
-
- sdev->prom_node = dp->node;
- strcpy(sdev->prom_name, dp->name);
-
- pval = of_get_property(dp, "reg", &len);
- sdev->num_registers = 0;
- if (pval) {
- memcpy(sdev->reg_addrs, pval, len);
-
- sdev->num_registers =
- len / sizeof(struct linux_prom_registers);
-
- base = (unsigned long) sdev->reg_addrs[0].phys_addr;
-
- /* Compute the slot number. */
- if (base >= SUN_SBUS_BVADDR && sparc_cpu_model == sun4m)
- sdev->slot = sbus_dev_slot(base);
- else
- sdev->slot = sdev->reg_addrs[0].which_io;
- }
-
- pval = of_get_property(dp, "ranges", &len);
- sdev->num_device_ranges = 0;
- if (pval) {
- memcpy(sdev->device_ranges, pval, len);
- sdev->num_device_ranges =
- len / sizeof(struct linux_prom_ranges);
- }
-
- sbus_fill_device_irq(sdev);
-
- sd = &sdev->ofdev.dev.archdata;
- sd->prom_node = dp;
- sd->op = &sdev->ofdev;
-
- sdev->ofdev.node = dp;
- if (sdev->parent)
- sdev->ofdev.dev.parent = &sdev->parent->ofdev.dev;
- else
- sdev->ofdev.dev.parent = &sdev->bus->ofdev.dev;
- sdev->ofdev.dev.bus = &sbus_bus_type;
- dev_set_name(&sdev->ofdev.dev, "sbus[%08x]", dp->node);
-
- if (of_device_register(&sdev->ofdev) != 0)
- printk(KERN_DEBUG "sbus: device registration error for %s!\n",
- dp->path_component_name);
-
- /* WE HAVE BEEN INVADED BY ALIENS! */
- err = sysfs_create_file(&sdev->ofdev.dev.kobj, &dev_attr_obppath.attr);
-}
-
-static void __init sbus_bus_ranges_init(struct device_node *dp, struct sbus_bus *sbus)
-{
- const void *pval;
- int len;
-
- pval = of_get_property(dp, "ranges", &len);
- sbus->num_sbus_ranges = 0;
- if (pval) {
- memcpy(sbus->sbus_ranges, pval, len);
- sbus->num_sbus_ranges =
- len / sizeof(struct linux_prom_ranges);
-
- sbus_arch_bus_ranges_init(dp->parent, sbus);
- }
-}
-
-static void __init __apply_ranges_to_regs(struct linux_prom_ranges *ranges,
- int num_ranges,
- struct linux_prom_registers *regs,
- int num_regs)
-{
- if (num_ranges) {
- int regnum;
-
- for (regnum = 0; regnum < num_regs; regnum++) {
- int rngnum;
-
- for (rngnum = 0; rngnum < num_ranges; rngnum++) {
- if (regs[regnum].which_io == ranges[rngnum].ot_child_space)
- break;
- }
- if (rngnum == num_ranges) {
- /* We used to flag this as an error. Actually
- * some devices do not report the regs as we expect.
- * For example, see SUNW,pln device. In that case
- * the reg property is in a format internal to that
- * node, ie. it is not in the SBUS register space
- * per se. -DaveM
- */
- return;
- }
- regs[regnum].which_io = ranges[rngnum].ot_parent_space;
- regs[regnum].phys_addr -= ranges[rngnum].ot_child_base;
- regs[regnum].phys_addr += ranges[rngnum].ot_parent_base;
- }
- }
-}
-
-static void __init __fixup_regs_sdev(struct sbus_dev *sdev)
-{
- if (sdev->num_registers != 0) {
- struct sbus_dev *parent = sdev->parent;
- int i;
-
- while (parent != NULL) {
- __apply_ranges_to_regs(parent->device_ranges,
- parent->num_device_ranges,
- sdev->reg_addrs,
- sdev->num_registers);
-
- parent = parent->parent;
- }
-
- __apply_ranges_to_regs(sdev->bus->sbus_ranges,
- sdev->bus->num_sbus_ranges,
- sdev->reg_addrs,
- sdev->num_registers);
-
- for (i = 0; i < sdev->num_registers; i++) {
- struct resource *res = &sdev->resource[i];
-
- res->start = sdev->reg_addrs[i].phys_addr;
- res->end = (res->start +
- (unsigned long)sdev->reg_addrs[i].reg_size - 1UL);
- res->flags = IORESOURCE_IO |
- (sdev->reg_addrs[i].which_io & 0xff);
- }
- }
-}
-
-static void __init sbus_fixup_all_regs(struct sbus_dev *first_sdev)
-{
- struct sbus_dev *sdev;
-
- for (sdev = first_sdev; sdev; sdev = sdev->next) {
- if (sdev->child)
- sbus_fixup_all_regs(sdev->child);
- __fixup_regs_sdev(sdev);
- }
-}
-
-/* We preserve the "probe order" of these bus and device lists to give
- * the same ordering as the old code.
- */
-static void __init sbus_insert(struct sbus_bus *sbus, struct sbus_bus **root)
-{
- while (*root)
- root = &(*root)->next;
- *root = sbus;
- sbus->next = NULL;
-}
-
-static void __init sdev_insert(struct sbus_dev *sdev, struct sbus_dev **root)
-{
- while (*root)
- root = &(*root)->next;
- *root = sdev;
- sdev->next = NULL;
-}
-
-static void __init walk_children(struct device_node *dp, struct sbus_dev *parent, struct sbus_bus *sbus)
-{
- dp = dp->child;
- while (dp) {
- struct sbus_dev *sdev;
-
- sdev = kzalloc(sizeof(struct sbus_dev), GFP_ATOMIC);
- if (sdev) {
- sdev_insert(sdev, &parent->child);
-
- sdev->bus = sbus;
- sdev->parent = parent;
- sdev->ofdev.dev.archdata.iommu =
- sbus->ofdev.dev.archdata.iommu;
- sdev->ofdev.dev.archdata.stc =
- sbus->ofdev.dev.archdata.stc;
-
- fill_sbus_device(dp, sdev);
-
- walk_children(dp, sdev, sbus);
- }
- dp = dp->sibling;
- }
-}
-
-static void __init build_one_sbus(struct device_node *dp, int num_sbus)
-{
- struct sbus_bus *sbus;
- unsigned int sbus_clock;
- struct device_node *dev_dp;
-
- sbus = kzalloc(sizeof(struct sbus_bus), GFP_ATOMIC);
- if (!sbus)
- return;
-
- sbus_insert(sbus, &sbus_root);
- sbus->prom_node = dp->node;
-
- sbus_setup_iommu(sbus, dp);
-
- printk("sbus%d: ", num_sbus);
-
- sbus_clock = of_getintprop_default(dp, "clock-frequency",
- (25*1000*1000));
- sbus->clock_freq = sbus_clock;
-
- printk("Clock %d.%d MHz\n", (int) ((sbus_clock/1000)/1000),
- (int) (((sbus_clock/1000)%1000 != 0) ?
- (((sbus_clock/1000)%1000) + 1000) : 0));
-
- strcpy(sbus->prom_name, dp->name);
-
- sbus_setup_arch_props(sbus, dp);
-
- sbus_bus_ranges_init(dp, sbus);
-
- sbus->ofdev.node = dp;
- sbus->ofdev.dev.parent = NULL;
- sbus->ofdev.dev.bus = &sbus_bus_type;
- dev_set_name(&sbus->ofdev.dev, "sbus%d", num_sbus);
-
- if (of_device_register(&sbus->ofdev) != 0)
- printk(KERN_DEBUG "sbus: device registration error for %s!\n",
- dev_name(&sbus->ofdev.dev));
-
- dev_dp = dp->child;
- while (dev_dp) {
- struct sbus_dev *sdev;
-
- sdev = kzalloc(sizeof(struct sbus_dev), GFP_ATOMIC);
- if (sdev) {
- sdev_insert(sdev, &sbus->devices);
-
- sdev->bus = sbus;
- sdev->parent = NULL;
- sdev->ofdev.dev.archdata.iommu =
- sbus->ofdev.dev.archdata.iommu;
- sdev->ofdev.dev.archdata.stc =
- sbus->ofdev.dev.archdata.stc;
-
- fill_sbus_device(dev_dp, sdev);
-
- walk_children(dev_dp, sdev, sbus);
- }
- dev_dp = dev_dp->sibling;
- }
-
- sbus_fixup_all_regs(sbus->devices);
-
- dvma_init(sbus);
-}
-
-static int __init sbus_init(void)
-{
- struct device_node *dp;
- const char *sbus_name = "sbus";
- int num_sbus = 0;
-
- if (sbus_arch_preinit())
- return 0;
-
- if (sparc_cpu_model == sun4d)
- sbus_name = "sbi";
-
- for_each_node_by_name(dp, sbus_name) {
- build_one_sbus(dp, num_sbus);
- num_sbus++;
-
- }
-
- sbus_arch_postinit();
-
- return 0;
-}
-
-subsys_initcall(sbus_init);
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index d3b211a..403ecad 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -1640,6 +1640,7 @@
tristate "Atari native SCSI support"
depends on ATARI && SCSI
select SCSI_SPI_ATTRS
+ select NVRAM
---help---
If you have an Atari with built-in NCR5380 SCSI controller (TT,
Falcon, ...) say Y to get it supported. Of course also, if you have
@@ -1670,14 +1671,6 @@
boot process fractionally longer but may assist recovery from errors
that leave the devices with SCSI operations partway completed.
-config TT_DMA_EMUL
- bool "Hades SCSI DMA emulator"
- depends on ATARI_SCSI && HADES
- help
- This option enables code which emulates the TT SCSI DMA chip on the
- Hades. This increases the SCSI transfer rates at least ten times
- compared to PIO transfers.
-
config MAC_SCSI
bool "Macintosh NCR5380 SCSI"
depends on MAC && SCSI=y
diff --git a/drivers/scsi/atari_dma_emul.c b/drivers/scsi/atari_dma_emul.c
deleted file mode 100644
index cdc710e..0000000
--- a/drivers/scsi/atari_dma_emul.c
+++ /dev/null
@@ -1,468 +0,0 @@
-/*
- * atari_dma_emul.c -- TT SCSI DMA emulator for the Hades.
- *
- * Copyright 1997 Wout Klaren <W.Klaren@inter.nl.net>
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file COPYING in the main directory of this archive
- * for more details.
- *
- * This code was written using the Hades TOS source code as a
- * reference. This source code can be found on the home page
- * of Medusa Computer Systems.
- *
- * Version 0.1, 1997-09-24.
- *
- * This code should be considered experimental. It has only been
- * tested on a Hades with a 68060. It might not work on a Hades
- * with a 68040. Make backups of your hard drives before using
- * this code.
- */
-
-#include <linux/compiler.h>
-#include <asm/thread_info.h>
-#include <asm/uaccess.h>
-
-#define hades_dma_ctrl (*(unsigned char *) 0xffff8717)
-#define hades_psdm_reg (*(unsigned char *) 0xffff8741)
-
-#define TRANSFER_SIZE 16
-
-struct m68040_frame {
- unsigned long effaddr; /* effective address */
- unsigned short ssw; /* special status word */
- unsigned short wb3s; /* write back 3 status */
- unsigned short wb2s; /* write back 2 status */
- unsigned short wb1s; /* write back 1 status */
- unsigned long faddr; /* fault address */
- unsigned long wb3a; /* write back 3 address */
- unsigned long wb3d; /* write back 3 data */
- unsigned long wb2a; /* write back 2 address */
- unsigned long wb2d; /* write back 2 data */
- unsigned long wb1a; /* write back 1 address */
- unsigned long wb1dpd0; /* write back 1 data/push data 0*/
- unsigned long pd1; /* push data 1*/
- unsigned long pd2; /* push data 2*/
- unsigned long pd3; /* push data 3*/
-};
-
-static void writeback (unsigned short wbs, unsigned long wba,
- unsigned long wbd, void *old_buserr)
-{
- mm_segment_t fs = get_fs();
- static void *save_buserr;
-
- __asm__ __volatile__ ("movec.l %%vbr,%%a0\n\t"
- "move.l %0,8(%%a0)\n\t"
- :
- : "r" (&&bus_error)
- : "a0" );
-
- save_buserr = old_buserr;
-
- set_fs (MAKE_MM_SEG(wbs & WBTM_040));
-
- switch (wbs & WBSIZ_040) {
- case BA_SIZE_BYTE:
- put_user (wbd & 0xff, (char *)wba);
- break;
- case BA_SIZE_WORD:
- put_user (wbd & 0xffff, (short *)wba);
- break;
- case BA_SIZE_LONG:
- put_user (wbd, (int *)wba);
- break;
- }
-
- set_fs (fs);
- return;
-
-bus_error:
- __asm__ __volatile__ ("cmp.l %0,2(%%sp)\n\t"
- "bcs.s .jump_old\n\t"
- "cmp.l %1,2(%%sp)\n\t"
- "bls.s .restore_old\n"
- ".jump_old:\n\t"
- "move.l %2,-(%%sp)\n\t"
- "rts\n"
- ".restore_old:\n\t"
- "move.l %%a0,-(%%sp)\n\t"
- "movec.l %%vbr,%%a0\n\t"
- "move.l %2,8(%%a0)\n\t"
- "move.l (%%sp)+,%%a0\n\t"
- "rte\n\t"
- :
- : "i" (writeback), "i" (&&bus_error),
- "m" (save_buserr) );
-}
-
-/*
- * static inline void set_restdata_reg(unsigned char *cur_addr)
- *
- * Set the rest data register if necessary.
- */
-
-static inline void set_restdata_reg(unsigned char *cur_addr)
-{
- if (((long) cur_addr & ~3) != 0)
- tt_scsi_dma.dma_restdata =
- *((unsigned long *) ((long) cur_addr & ~3));
-}
-
-/*
- * void hades_dma_emulator(int irq, void *dummy)
- *
- * This code emulates TT SCSI DMA on the Hades.
- *
- * Note the following:
- *
- * 1. When there is no byte available to read from the SCSI bus, or
- * when a byte cannot yet bet written to the SCSI bus, a bus
- * error occurs when reading or writing the pseudo DMA data
- * register (hades_psdm_reg). We have to catch this bus error
- * and try again to read or write the byte. If after several tries
- * we still get a bus error, the interrupt handler is left. When
- * the byte can be read or written, the interrupt handler is
- * called again.
- *
- * 2. The SCSI interrupt must be disabled in this interrupt handler.
- *
- * 3. If we set the EOP signal, the SCSI controller still expects one
- * byte to be read or written. Therefore the last byte is transferred
- * separately, after setting the EOP signal.
- *
- * 4. When this function is left, the address pointer (start_addr) is
- * converted to a physical address. Because it points one byte
- * further than the last transferred byte, it can point outside the
- * current page. If virt_to_phys() is called with this address we
- * might get an access error. Therefore virt_to_phys() is called with
- * start_addr - 1 if the count has reached zero. The result is
- * increased with one.
- */
-
-static irqreturn_t hades_dma_emulator(int irq, void *dummy)
-{
- unsigned long dma_base;
- register unsigned long dma_cnt asm ("d3");
- static long save_buserr;
- register unsigned long save_sp asm ("d4");
- register int tries asm ("d5");
- register unsigned char *start_addr asm ("a3"), *end_addr asm ("a4");
- register unsigned char *eff_addr;
- register unsigned char *psdm_reg;
- unsigned long rem;
-
- atari_disable_irq(IRQ_TT_MFP_SCSI);
-
- /*
- * Read the dma address and count registers.
- */
-
- dma_base = SCSI_DMA_READ_P(dma_addr);
- dma_cnt = SCSI_DMA_READ_P(dma_cnt);
-
- /*
- * Check if DMA is still enabled.
- */
-
- if ((tt_scsi_dma.dma_ctrl & 2) == 0)
- {
- atari_enable_irq(IRQ_TT_MFP_SCSI);
- return IRQ_HANDLED;
- }
-
- if (dma_cnt == 0)
- {
- printk(KERN_NOTICE "DMA emulation: count is zero.\n");
- tt_scsi_dma.dma_ctrl &= 0xfd; /* DMA ready. */
- atari_enable_irq(IRQ_TT_MFP_SCSI);
- return IRQ_HANDLED;
- }
-
- /*
- * Install new bus error routine.
- */
-
- __asm__ __volatile__ ("movec.l %%vbr,%%a0\n\t"
- "move.l 8(%%a0),%0\n\t"
- "move.l %1,8(%%a0)\n\t"
- : "=&r" (save_buserr)
- : "r" (&&scsi_bus_error)
- : "a0" );
-
- hades_dma_ctrl &= 0xfc; /* Bus error and EOP off. */
-
- /*
- * Save the stack pointer.
- */
-
- __asm__ __volatile__ ("move.l %%sp,%0\n\t"
- : "=&r" (save_sp) );
-
- tries = 100; /* Maximum number of bus errors. */
- start_addr = phys_to_virt(dma_base);
- end_addr = start_addr + dma_cnt;
-
-scsi_loop:
- dma_cnt--;
- rem = dma_cnt & (TRANSFER_SIZE - 1);
- dma_cnt &= ~(TRANSFER_SIZE - 1);
- psdm_reg = &hades_psdm_reg;
-
- if (tt_scsi_dma.dma_ctrl & 1) /* Read or write? */
- {
- /*
- * SCSI write. Abort when count is zero.
- */
-
- switch (rem)
- {
- case 0:
- while (dma_cnt > 0)
- {
- dma_cnt -= TRANSFER_SIZE;
-
- *psdm_reg = *start_addr++;
- case 15:
- *psdm_reg = *start_addr++;
- case 14:
- *psdm_reg = *start_addr++;
- case 13:
- *psdm_reg = *start_addr++;
- case 12:
- *psdm_reg = *start_addr++;
- case 11:
- *psdm_reg = *start_addr++;
- case 10:
- *psdm_reg = *start_addr++;
- case 9:
- *psdm_reg = *start_addr++;
- case 8:
- *psdm_reg = *start_addr++;
- case 7:
- *psdm_reg = *start_addr++;
- case 6:
- *psdm_reg = *start_addr++;
- case 5:
- *psdm_reg = *start_addr++;
- case 4:
- *psdm_reg = *start_addr++;
- case 3:
- *psdm_reg = *start_addr++;
- case 2:
- *psdm_reg = *start_addr++;
- case 1:
- *psdm_reg = *start_addr++;
- }
- }
-
- hades_dma_ctrl |= 1; /* Set EOP. */
- udelay(10);
- *psdm_reg = *start_addr++; /* Dummy byte. */
- tt_scsi_dma.dma_ctrl &= 0xfd; /* DMA ready. */
- }
- else
- {
- /*
- * SCSI read. Abort when count is zero.
- */
-
- switch (rem)
- {
- case 0:
- while (dma_cnt > 0)
- {
- dma_cnt -= TRANSFER_SIZE;
-
- *start_addr++ = *psdm_reg;
- case 15:
- *start_addr++ = *psdm_reg;
- case 14:
- *start_addr++ = *psdm_reg;
- case 13:
- *start_addr++ = *psdm_reg;
- case 12:
- *start_addr++ = *psdm_reg;
- case 11:
- *start_addr++ = *psdm_reg;
- case 10:
- *start_addr++ = *psdm_reg;
- case 9:
- *start_addr++ = *psdm_reg;
- case 8:
- *start_addr++ = *psdm_reg;
- case 7:
- *start_addr++ = *psdm_reg;
- case 6:
- *start_addr++ = *psdm_reg;
- case 5:
- *start_addr++ = *psdm_reg;
- case 4:
- *start_addr++ = *psdm_reg;
- case 3:
- *start_addr++ = *psdm_reg;
- case 2:
- *start_addr++ = *psdm_reg;
- case 1:
- *start_addr++ = *psdm_reg;
- }
- }
-
- hades_dma_ctrl |= 1; /* Set EOP. */
- udelay(10);
- *start_addr++ = *psdm_reg;
- tt_scsi_dma.dma_ctrl &= 0xfd; /* DMA ready. */
-
- set_restdata_reg(start_addr);
- }
-
- if (start_addr != end_addr)
- printk(KERN_CRIT "DMA emulation: FATAL: Count is not zero at end of transfer.\n");
-
- dma_cnt = end_addr - start_addr;
-
-scsi_end:
- dma_base = (dma_cnt == 0) ? virt_to_phys(start_addr - 1) + 1 :
- virt_to_phys(start_addr);
-
- SCSI_DMA_WRITE_P(dma_addr, dma_base);
- SCSI_DMA_WRITE_P(dma_cnt, dma_cnt);
-
- /*
- * Restore old bus error routine.
- */
-
- __asm__ __volatile__ ("movec.l %%vbr,%%a0\n\t"
- "move.l %0,8(%%a0)\n\t"
- :
- : "r" (save_buserr)
- : "a0" );
-
- atari_enable_irq(IRQ_TT_MFP_SCSI);
-
- return IRQ_HANDLED;
-
-scsi_bus_error:
- /*
- * First check if the bus error is caused by our code.
- * If not, call the original handler.
- */
-
- __asm__ __volatile__ ("cmp.l %0,2(%%sp)\n\t"
- "bcs.s .old_vector\n\t"
- "cmp.l %1,2(%%sp)\n\t"
- "bls.s .scsi_buserr\n"
- ".old_vector:\n\t"
- "move.l %2,-(%%sp)\n\t"
- "rts\n"
- ".scsi_buserr:\n\t"
- :
- : "i" (&&scsi_loop), "i" (&&scsi_end),
- "m" (save_buserr) );
-
- if (CPU_IS_060)
- {
- /*
- * Get effective address and restore the stack.
- */
-
- __asm__ __volatile__ ("move.l 8(%%sp),%0\n\t"
- "move.l %1,%%sp\n\t"
- : "=a&" (eff_addr)
- : "r" (save_sp) );
- }
- else
- {
- register struct m68040_frame *frame;
-
- __asm__ __volatile__ ("lea 8(%%sp),%0\n\t"
- : "=a&" (frame) );
-
- if (tt_scsi_dma.dma_ctrl & 1)
- {
- /*
- * Bus error while writing.
- */
-
- if (frame->wb3s & WBV_040)
- {
- if (frame->wb3a == (long) &hades_psdm_reg)
- start_addr--;
- else
- writeback(frame->wb3s, frame->wb3a,
- frame->wb3d, &&scsi_bus_error);
- }
-
- if (frame->wb2s & WBV_040)
- {
- if (frame->wb2a == (long) &hades_psdm_reg)
- start_addr--;
- else
- writeback(frame->wb2s, frame->wb2a,
- frame->wb2d, &&scsi_bus_error);
- }
-
- if (frame->wb1s & WBV_040)
- {
- if (frame->wb1a == (long) &hades_psdm_reg)
- start_addr--;
- }
- }
- else
- {
- /*
- * Bus error while reading.
- */
-
- if (frame->wb3s & WBV_040)
- writeback(frame->wb3s, frame->wb3a,
- frame->wb3d, &&scsi_bus_error);
- }
-
- eff_addr = (unsigned char *) frame->faddr;
-
- __asm__ __volatile__ ("move.l %0,%%sp\n\t"
- :
- : "r" (save_sp) );
- }
-
- dma_cnt = end_addr - start_addr;
-
- if (eff_addr == &hades_psdm_reg)
- {
- /*
- * Bus error occurred while reading the pseudo
- * DMA register. Time out.
- */
-
- tries--;
-
- if (tries <= 0)
- {
- if ((tt_scsi_dma.dma_ctrl & 1) == 0) /* Read or write? */
- set_restdata_reg(start_addr);
-
- if (dma_cnt <= 1)
- printk(KERN_CRIT "DMA emulation: Fatal "
- "error while %s the last byte.\n",
- (tt_scsi_dma.dma_ctrl & 1)
- ? "writing" : "reading");
-
- goto scsi_end;
- }
- else
- goto scsi_loop;
- }
- else
- {
- /*
- * Bus error during pseudo DMA transfer.
- * Terminate the DMA transfer.
- */
-
- hades_dma_ctrl |= 3; /* Set EOP and bus error. */
- if ((tt_scsi_dma.dma_ctrl & 1) == 0) /* Read or write? */
- set_restdata_reg(start_addr);
- goto scsi_end;
- }
-}
diff --git a/drivers/scsi/atari_scsi.c b/drivers/scsi/atari_scsi.c
index f5732d8..21fe07f 100644
--- a/drivers/scsi/atari_scsi.c
+++ b/drivers/scsi/atari_scsi.c
@@ -249,10 +249,6 @@
module_param(setup_hostid, int, 0);
-#if defined(CONFIG_TT_DMA_EMUL)
-#include "atari_dma_emul.c"
-#endif
-
#if defined(REAL_DMA)
static int scsi_dma_is_ignored_buserr(unsigned char dma_stat)
@@ -695,21 +691,8 @@
#ifdef REAL_DMA
tt_scsi_dma.dma_ctrl = 0;
atari_dma_residual = 0;
-#ifdef CONFIG_TT_DMA_EMUL
- if (MACH_IS_HADES) {
- if (request_irq(IRQ_AUTO_2, hades_dma_emulator,
- IRQ_TYPE_PRIO, "Hades DMA emulator",
- hades_dma_emulator)) {
- printk(KERN_ERR "atari_scsi_detect: cannot allocate irq %d, aborting (MACH_IS_HADES)",IRQ_AUTO_2);
- free_irq(IRQ_TT_MFP_SCSI, instance);
- scsi_unregister(atari_scsi_host);
- atari_stram_free(atari_dma_buffer);
- atari_dma_buffer = 0;
- return 0;
- }
- }
-#endif
- if (MACH_IS_MEDUSA || MACH_IS_HADES) {
+
+ if (MACH_IS_MEDUSA) {
/* While the read overruns (described by Drew Eckhardt in
* NCR5380.c) never happened on TTs, they do in fact on the Medusa
* (This was the cause why SCSI didn't work right for so long
@@ -1007,11 +990,7 @@
Scsi_Cmnd *cmd, int write_flag)
{
unsigned long possible_len, limit;
-#ifndef CONFIG_TT_DMA_EMUL
- if (MACH_IS_HADES)
- /* Hades has no SCSI DMA at all :-( Always force use of PIO */
- return 0;
-#endif
+
if (IS_A_TT())
/* TT SCSI DMA can transfer arbitrary #bytes */
return wanted_len;
diff --git a/drivers/scsi/esp_scsi.h b/drivers/scsi/esp_scsi.h
index bb43a13..28e22ac 100644
--- a/drivers/scsi/esp_scsi.h
+++ b/drivers/scsi/esp_scsi.h
@@ -521,7 +521,8 @@
struct completion *eh_reset;
- struct sbus_dma *dma;
+ void *dma;
+ int dmarev;
};
/* A front-end driver for the ESP chip should do the following in
diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c
index 81c16cb..740bad4 100644
--- a/drivers/scsi/ide-scsi.c
+++ b/drivers/scsi/ide-scsi.c
@@ -40,7 +40,6 @@
#include <linux/ioport.h>
#include <linux/blkdev.h>
#include <linux/errno.h>
-#include <linux/hdreg.h>
#include <linux/slab.h>
#include <linux/ide.h>
#include <linux/scatterlist.h>
@@ -83,7 +82,6 @@
struct gendisk *disk;
struct Scsi_Host *host;
- struct ide_atapi_pc *pc; /* Current packet command */
unsigned long transform; /* SCSI cmd translation layer */
unsigned long log; /* log flags */
} idescsi_scsi_t;
@@ -131,50 +129,6 @@
return scsihost_to_idescsi(ide_drive->driver_data);
}
-/*
- * PIO data transfer routine using the scatter gather table.
- */
-static void ide_scsi_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc,
- unsigned int bcount, int write)
-{
- ide_hwif_t *hwif = drive->hwif;
- const struct ide_tp_ops *tp_ops = hwif->tp_ops;
- xfer_func_t *xf = write ? tp_ops->output_data : tp_ops->input_data;
- char *buf;
- int count;
-
- while (bcount) {
- count = min(pc->sg->length - pc->b_count, bcount);
- if (PageHighMem(sg_page(pc->sg))) {
- unsigned long flags;
-
- local_irq_save(flags);
- buf = kmap_atomic(sg_page(pc->sg), KM_IRQ0) +
- pc->sg->offset;
- xf(drive, NULL, buf + pc->b_count, count);
- kunmap_atomic(buf - pc->sg->offset, KM_IRQ0);
- local_irq_restore(flags);
- } else {
- buf = sg_virt(pc->sg);
- xf(drive, NULL, buf + pc->b_count, count);
- }
- bcount -= count; pc->b_count += count;
- if (pc->b_count == pc->sg->length) {
- if (!--pc->sg_cnt)
- break;
- pc->sg = sg_next(pc->sg);
- pc->b_count = 0;
- }
- }
-
- if (bcount) {
- printk(KERN_ERR "%s: scatter gather table too small, %s\n",
- drive->name, write ? "padding with zeros"
- : "discarding data");
- ide_pad_transfer(drive, write, bcount);
- }
-}
-
static void ide_scsi_hex_dump(u8 *data, int len)
{
print_hex_dump(KERN_CONT, "", DUMP_PREFIX_NONE, 16, 1, data, len, 0);
@@ -182,10 +136,10 @@
static int idescsi_end_request(ide_drive_t *, int, int);
-static void ide_scsi_callback(ide_drive_t *drive)
+static void ide_scsi_callback(ide_drive_t *drive, int dsc)
{
idescsi_scsi_t *scsi = drive_to_idescsi(drive);
- struct ide_atapi_pc *pc = scsi->pc;
+ struct ide_atapi_pc *pc = drive->pc;
if (pc->flags & PC_FLAG_TIMEDOUT)
debug_log("%s: got timed out packet %lu at %lu\n", __func__,
@@ -244,9 +198,9 @@
{
ide_hwif_t *hwif = drive->hwif;
- if (hwif->tp_ops->read_status(hwif) & (BUSY_STAT | DRQ_STAT))
+ if (hwif->tp_ops->read_status(hwif) & (ATA_BUSY | ATA_DRQ))
/* force an abort */
- hwif->tp_ops->exec_command(hwif, WIN_IDLEIMMEDIATE);
+ hwif->tp_ops->exec_command(hwif, ATA_CMD_IDLEIMMEDIATE);
rq->errors++;
@@ -312,49 +266,10 @@
spin_unlock_irqrestore(host->host_lock, flags);
kfree(pc);
blk_put_request(rq);
- scsi->pc = NULL;
+ drive->pc = NULL;
return 0;
}
-static inline unsigned long get_timeout(struct ide_atapi_pc *pc)
-{
- return max_t(unsigned long, WAIT_CMD, pc->timeout - jiffies);
-}
-
-static int idescsi_expiry(ide_drive_t *drive)
-{
- idescsi_scsi_t *scsi = drive_to_idescsi(drive);
- struct ide_atapi_pc *pc = scsi->pc;
-
- debug_log("%s called for %lu at %lu\n", __func__,
- pc->scsi_cmd->serial_number, jiffies);
-
- pc->flags |= PC_FLAG_TIMEDOUT;
-
- return 0; /* we do not want the ide subsystem to retry */
-}
-
-/*
- * Our interrupt handler.
- */
-static ide_startstop_t idescsi_pc_intr (ide_drive_t *drive)
-{
- idescsi_scsi_t *scsi = drive_to_idescsi(drive);
- struct ide_atapi_pc *pc = scsi->pc;
-
- return ide_pc_intr(drive, pc, idescsi_pc_intr, get_timeout(pc),
- idescsi_expiry, NULL, NULL, NULL,
- ide_scsi_io_buffers);
-}
-
-static ide_startstop_t idescsi_transfer_pc(ide_drive_t *drive)
-{
- idescsi_scsi_t *scsi = drive_to_idescsi(drive);
-
- return ide_transfer_pc(drive, scsi->pc, idescsi_pc_intr,
- get_timeout(scsi->pc), idescsi_expiry);
-}
-
static inline int idescsi_set_direction(struct ide_atapi_pc *pc)
{
switch (pc->c[0]) {
@@ -397,13 +312,10 @@
static ide_startstop_t idescsi_issue_pc(ide_drive_t *drive,
struct ide_atapi_pc *pc)
{
- idescsi_scsi_t *scsi = drive_to_idescsi(drive);
-
/* Set the current packet command */
- scsi->pc = pc;
+ drive->pc = pc;
- return ide_issue_pc(drive, pc, idescsi_transfer_pc,
- get_timeout(pc), idescsi_expiry);
+ return ide_issue_pc(drive, ide_scsi_get_timeout(pc), ide_scsi_expiry);
}
/*
@@ -419,7 +331,8 @@
if (blk_sense_request(rq) || blk_special_request(rq)) {
struct ide_atapi_pc *pc = (struct ide_atapi_pc *)rq->special;
- if (drive->using_dma && !idescsi_map_sg(drive, pc))
+ if ((drive->dev_flags & IDE_DFLAG_USING_DMA) &&
+ idescsi_map_sg(drive, pc) == 0)
pc->flags |= PC_FLAG_DMA_OK;
return idescsi_issue_pc(drive, pc);
@@ -430,21 +343,41 @@
}
#ifdef CONFIG_IDE_PROC_FS
-static void idescsi_add_settings(ide_drive_t *drive)
-{
- idescsi_scsi_t *scsi = drive_to_idescsi(drive);
-
-/*
- * drive setting name read/write data type min max mul_factor div_factor data pointer set function
- */
- ide_add_setting(drive, "bios_cyl", SETTING_RW, TYPE_INT, 0, 1023, 1, 1, &drive->bios_cyl, NULL);
- ide_add_setting(drive, "bios_head", SETTING_RW, TYPE_BYTE, 0, 255, 1, 1, &drive->bios_head, NULL);
- ide_add_setting(drive, "bios_sect", SETTING_RW, TYPE_BYTE, 0, 63, 1, 1, &drive->bios_sect, NULL);
- ide_add_setting(drive, "transform", SETTING_RW, TYPE_INT, 0, 3, 1, 1, &scsi->transform, NULL);
- ide_add_setting(drive, "log", SETTING_RW, TYPE_INT, 0, 1, 1, 1, &scsi->log, NULL);
+#define ide_scsi_devset_get(name, field) \
+static int get_##name(ide_drive_t *drive) \
+{ \
+ idescsi_scsi_t *scsi = drive_to_idescsi(drive); \
+ return scsi->field; \
}
-#else
-static inline void idescsi_add_settings(ide_drive_t *drive) { ; }
+
+#define ide_scsi_devset_set(name, field) \
+static int set_##name(ide_drive_t *drive, int arg) \
+{ \
+ idescsi_scsi_t *scsi = drive_to_idescsi(drive); \
+ scsi->field = arg; \
+ return 0; \
+}
+
+#define ide_scsi_devset_rw_field(_name, _field) \
+ide_scsi_devset_get(_name, _field); \
+ide_scsi_devset_set(_name, _field); \
+IDE_DEVSET(_name, DS_SYNC, get_##_name, set_##_name);
+
+ide_devset_rw_field(bios_cyl, bios_cyl);
+ide_devset_rw_field(bios_head, bios_head);
+ide_devset_rw_field(bios_sect, bios_sect);
+
+ide_scsi_devset_rw_field(transform, transform);
+ide_scsi_devset_rw_field(log, log);
+
+static const struct ide_proc_devset idescsi_settings[] = {
+ IDE_PROC_DEVSET(bios_cyl, 0, 1023),
+ IDE_PROC_DEVSET(bios_head, 0, 255),
+ IDE_PROC_DEVSET(bios_sect, 0, 63),
+ IDE_PROC_DEVSET(log, 0, 1),
+ IDE_PROC_DEVSET(transform, 0, 3),
+ { 0 },
+};
#endif
/*
@@ -452,16 +385,16 @@
*/
static void idescsi_setup (ide_drive_t *drive, idescsi_scsi_t *scsi)
{
- if (drive->id && (drive->id->config & 0x0060) == 0x20)
- set_bit(IDE_AFLAG_DRQ_INTERRUPT, &drive->atapi_flags);
clear_bit(IDESCSI_SG_TRANSFORM, &scsi->transform);
#if IDESCSI_DEBUG_LOG
set_bit(IDESCSI_LOG_CMD, &scsi->log);
#endif /* IDESCSI_DEBUG_LOG */
- drive->pc_callback = ide_scsi_callback;
+ drive->pc_callback = ide_scsi_callback;
+ drive->pc_update_buffers = NULL;
+ drive->pc_io_buffers = ide_io_buffers;
- idescsi_add_settings(drive);
+ ide_proc_register_driver(drive, scsi->driver);
}
static void ide_scsi_remove(ide_drive_t *drive)
@@ -481,7 +414,7 @@
ide_scsi_put(scsi);
- drive->scsi = 0;
+ drive->dev_flags &= ~IDE_DFLAG_SCSI;
}
static int ide_scsi_probe(ide_drive_t *);
@@ -502,13 +435,12 @@
.probe = ide_scsi_probe,
.remove = ide_scsi_remove,
.version = IDESCSI_VERSION,
- .media = ide_scsi,
- .supports_dsc_overlap = 0,
.do_request = idescsi_do_request,
.end_request = idescsi_end_request,
.error = idescsi_atapi_error,
#ifdef CONFIG_IDE_PROC_FS
.proc = idescsi_proc,
+ .settings = idescsi_settings,
#endif
};
@@ -647,6 +579,8 @@
int busy;
int ret = FAILED;
+ struct ide_atapi_pc *pc;
+
/* In idescsi_eh_abort we try to gently pry our command from the ide subsystem */
if (test_bit(IDESCSI_LOG_CMD, &scsi->log))
@@ -667,26 +601,27 @@
spin_lock_irq(&ide_lock);
/* If there is no pc running we're done (our interrupt took care of it) */
- if (!scsi->pc) {
+ pc = drive->pc;
+ if (pc == NULL) {
ret = SUCCESS;
goto ide_unlock;
}
/* It's somewhere in flight. Does ide subsystem agree? */
- if (scsi->pc->scsi_cmd->serial_number == cmd->serial_number && !busy &&
- elv_queue_empty(drive->queue) && HWGROUP(drive)->rq != scsi->pc->rq) {
+ if (pc->scsi_cmd->serial_number == cmd->serial_number && !busy &&
+ elv_queue_empty(drive->queue) && HWGROUP(drive)->rq != pc->rq) {
/*
* FIXME - not sure this condition can ever occur
*/
printk (KERN_ERR "ide-scsi: cmd aborted!\n");
- if (blk_sense_request(scsi->pc->rq))
- kfree(scsi->pc->buf);
+ if (blk_sense_request(pc->rq))
+ kfree(pc->buf);
/* we need to call blk_put_request twice. */
- blk_put_request(scsi->pc->rq);
- blk_put_request(scsi->pc->rq);
- kfree(scsi->pc);
- scsi->pc = NULL;
+ blk_put_request(pc->rq);
+ blk_put_request(pc->rq);
+ kfree(pc);
+ drive->pc = NULL;
ret = SUCCESS;
}
@@ -708,6 +643,8 @@
int ready = 0;
int ret = SUCCESS;
+ struct ide_atapi_pc *pc;
+
/* In idescsi_eh_reset we forcefully remove the command from the ide subsystem and reset the device. */
if (test_bit(IDESCSI_LOG_CMD, &scsi->log))
@@ -722,7 +659,9 @@
spin_lock_irq(cmd->device->host->host_lock);
spin_lock(&ide_lock);
- if (!scsi->pc || (req = scsi->pc->rq) != HWGROUP(drive)->rq || !HWGROUP(drive)->handler) {
+ pc = drive->pc;
+
+ if (pc == NULL || (req = pc->rq) != HWGROUP(drive)->rq || !HWGROUP(drive)->handler) {
printk (KERN_WARNING "ide-scsi: No active request in idescsi_eh_reset\n");
spin_unlock(&ide_lock);
spin_unlock_irq(cmd->device->host->host_lock);
@@ -733,9 +672,9 @@
if (__blk_end_request(req, -EIO, 0))
BUG();
if (blk_sense_request(req))
- kfree(scsi->pc->buf);
- kfree(scsi->pc);
- scsi->pc = NULL;
+ kfree(pc->buf);
+ kfree(pc);
+ drive->pc = NULL;
blk_put_request(req);
/* now nuke the drive queue */
@@ -811,6 +750,7 @@
struct gendisk *g;
static int warned;
int err = -ENOMEM;
+ u16 last_lun;
if (!warned && drive->media == ide_cdrom) {
printk(KERN_WARNING "ide-scsi is deprecated for cd burning! Use ide-cd and give dev=/dev/hdX as device\n");
@@ -821,12 +761,11 @@
return -ENODEV;
if (!strstr("ide-scsi", drive->driver_req) ||
- !drive->present ||
drive->media == ide_disk ||
!(host = scsi_host_alloc(&idescsi_template,sizeof(idescsi_scsi_t))))
return -ENODEV;
- drive->scsi = 1;
+ drive->dev_flags |= IDE_DFLAG_SCSI;
g = alloc_disk(1 << PARTN_BITS);
if (!g)
@@ -836,12 +775,12 @@
host->max_id = 1;
- if (drive->id->last_lun)
- debug_log("%s: id->last_lun=%u\n", drive->name,
- drive->id->last_lun);
+ last_lun = drive->id[ATA_ID_LAST_LUN];
+ if (last_lun)
+ debug_log("%s: last_lun=%u\n", drive->name, last_lun);
- if ((drive->id->last_lun & 0x7) != 7)
- host->max_lun = (drive->id->last_lun & 0x7) + 1;
+ if ((last_lun & 7) != 7)
+ host->max_lun = (last_lun & 7) + 1;
else
host->max_lun = 1;
@@ -852,7 +791,6 @@
idescsi->host = host;
idescsi->disk = g;
g->private_data = &idescsi->driver;
- ide_proc_register_driver(drive, &idescsi_driver);
err = 0;
idescsi_setup(drive, idescsi);
g->fops = &idescsi_ops;
@@ -868,7 +806,7 @@
put_disk(g);
out_host_put:
- drive->scsi = 0;
+ drive->dev_flags &= ~IDE_DFLAG_SCSI;
scsi_host_put(host);
return err;
}
diff --git a/drivers/scsi/pcmcia/aha152x_stub.c b/drivers/scsi/pcmcia/aha152x_stub.c
index 2dd0dc9..165ff88 100644
--- a/drivers/scsi/pcmcia/aha152x_stub.c
+++ b/drivers/scsi/pcmcia/aha152x_stub.c
@@ -140,44 +140,41 @@
#define CS_CHECK(fn, ret) \
do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+static int aha152x_config_check(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ /* For New Media T&J, look for a SCSI window */
+ if (cfg->io.win[0].len >= 0x20)
+ p_dev->io.BasePort1 = cfg->io.win[0].base;
+ else if ((cfg->io.nwin > 1) &&
+ (cfg->io.win[1].len >= 0x20))
+ p_dev->io.BasePort1 = cfg->io.win[1].base;
+ if ((cfg->io.nwin > 0) &&
+ (p_dev->io.BasePort1 < 0xffff)) {
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+ return 0;
+ }
+ return -EINVAL;
+}
+
static int aha152x_config_cs(struct pcmcia_device *link)
{
scsi_info_t *info = link->priv;
struct aha152x_setup s;
- tuple_t tuple;
- cisparse_t parse;
- int i, last_ret, last_fn;
- u_char tuple_data[64];
+ int last_ret, last_fn;
struct Scsi_Host *host;
-
+
DEBUG(0, "aha152x_config(0x%p)\n", link);
- tuple.TupleData = tuple_data;
- tuple.TupleDataMax = 64;
- tuple.TupleOffset = 0;
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- tuple.Attributes = 0;
- CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
- while (1) {
- if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
- pcmcia_parse_tuple(link, &tuple, &parse) != 0)
- goto next_entry;
- /* For New Media T&J, look for a SCSI window */
- if (parse.cftable_entry.io.win[0].len >= 0x20)
- link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
- else if ((parse.cftable_entry.io.nwin > 1) &&
- (parse.cftable_entry.io.win[1].len >= 0x20))
- link->io.BasePort1 = parse.cftable_entry.io.win[1].base;
- if ((parse.cftable_entry.io.nwin > 0) &&
- (link->io.BasePort1 < 0xffff)) {
- link->conf.ConfigIndex = parse.cftable_entry.index;
- i = pcmcia_request_io(link, &link->io);
- if (i == CS_SUCCESS) break;
- }
- next_entry:
- CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple));
+ last_ret = pcmcia_loop_config(link, aha152x_config_check, NULL);
+ if (last_ret) {
+ cs_error(link, RequestIO, last_ret);
+ goto failed;
}
-
+
CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
@@ -208,6 +205,7 @@
cs_failed:
cs_error(link, last_fn, last_ret);
+failed:
aha152x_release_cs(link);
return -ENODEV;
}
diff --git a/drivers/scsi/pcmcia/fdomain_stub.c b/drivers/scsi/pcmcia/fdomain_stub.c
index d8b9935..06254f4 100644
--- a/drivers/scsi/pcmcia/fdomain_stub.c
+++ b/drivers/scsi/pcmcia/fdomain_stub.c
@@ -123,34 +123,30 @@
#define CS_CHECK(fn, ret) \
do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+static int fdomain_config_check(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ p_dev->io.BasePort1 = cfg->io.win[0].base;
+ return pcmcia_request_io(p_dev, &p_dev->io);
+}
+
+
static int fdomain_config(struct pcmcia_device *link)
{
scsi_info_t *info = link->priv;
- tuple_t tuple;
- cisparse_t parse;
- int i, last_ret, last_fn;
- u_char tuple_data[64];
+ int last_ret, last_fn;
char str[22];
struct Scsi_Host *host;
DEBUG(0, "fdomain_config(0x%p)\n", link);
- tuple.TupleData = tuple_data;
- tuple.TupleDataMax = 64;
- tuple.TupleOffset = 0;
-
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
- while (1) {
- if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
- pcmcia_parse_tuple(link, &tuple, &parse) != 0)
- goto next_entry;
- link->conf.ConfigIndex = parse.cftable_entry.index;
- link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
- i = pcmcia_request_io(link, &link->io);
- if (i == CS_SUCCESS) break;
- next_entry:
- CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple));
+ last_ret = pcmcia_loop_config(link, fdomain_config_check, NULL);
+ if (last_ret) {
+ cs_error(link, RequestIO, last_ret);
+ goto failed;
}
CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
@@ -181,6 +177,7 @@
cs_failed:
cs_error(link, last_fn, last_ret);
+failed:
fdomain_release(link);
return -ENODEV;
} /* fdomain_config */
diff --git a/drivers/scsi/pcmcia/nsp_cs.c b/drivers/scsi/pcmcia/nsp_cs.c
index 24e6cb8..11a61ea 100644
--- a/drivers/scsi/pcmcia/nsp_cs.c
+++ b/drivers/scsi/pcmcia/nsp_cs.c
@@ -1606,133 +1606,129 @@
is received, to configure the PCMCIA socket, and to make the
ethernet device available to the system.
======================================================================*/
-#define CS_CHECK(fn, ret) \
-do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
-/*====================================================================*/
+
+struct nsp_cs_configdata {
+ nsp_hw_data *data;
+ win_req_t req;
+};
+
+static int nsp_cs_config_check(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ struct nsp_cs_configdata *cfg_mem = priv_data;
+
+ if (cfg->index == 0)
+ return -ENODEV;
+
+ /* Does this card need audio output? */
+ if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
+ p_dev->conf.Attributes |= CONF_ENABLE_SPKR;
+ p_dev->conf.Status = CCSR_AUDIO_ENA;
+ }
+
+ /* Use power settings for Vcc and Vpp if present */
+ /* Note that the CIS values need to be rescaled */
+ if (cfg->vcc.present & (1<<CISTPL_POWER_VNOM)) {
+ if (vcc != cfg->vcc.param[CISTPL_POWER_VNOM]/10000)
+ return -ENODEV;
+ else if (dflt->vcc.present & (1<<CISTPL_POWER_VNOM)) {
+ if (vcc != dflt->vcc.param[CISTPL_POWER_VNOM]/10000)
+ return -ENODEV;
+ }
+
+ if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM)) {
+ p_dev->conf.Vpp =
+ cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+ } else if (dflt->vpp1.present & (1 << CISTPL_POWER_VNOM)) {
+ p_dev->conf.Vpp =
+ dflt->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+ }
+
+ /* Do we need to allocate an interrupt? */
+ if (cfg->irq.IRQInfo1 || dflt->irq.IRQInfo1)
+ p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
+
+ /* IO window settings */
+ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0;
+ if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(io->flags & CISTPL_IO_8BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(io->flags & CISTPL_IO_16BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ p_dev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+ p_dev->io.BasePort1 = io->win[0].base;
+ p_dev->io.NumPorts1 = io->win[0].len;
+ if (io->nwin > 1) {
+ p_dev->io.Attributes2 = p_dev->io.Attributes1;
+ p_dev->io.BasePort2 = io->win[1].base;
+ p_dev->io.NumPorts2 = io->win[1].len;
+ }
+ /* This reserves IO space but doesn't actually enable it */
+ if (pcmcia_request_io(p_dev, &p_dev->io) != 0)
+ goto next_entry;
+ }
+
+ if ((cfg->mem.nwin > 0) || (dflt->mem.nwin > 0)) {
+ memreq_t map;
+ cistpl_mem_t *mem =
+ (cfg->mem.nwin) ? &cfg->mem : &dflt->mem;
+ cfg_mem->req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM;
+ cfg_mem->req.Attributes |= WIN_ENABLE;
+ cfg_mem->req.Base = mem->win[0].host_addr;
+ cfg_mem->req.Size = mem->win[0].len;
+ if (cfg_mem->req.Size < 0x1000)
+ cfg_mem->req.Size = 0x1000;
+ cfg_mem->req.AccessSpeed = 0;
+ if (pcmcia_request_window(&p_dev, &cfg_mem->req, &p_dev->win) != 0)
+ goto next_entry;
+ map.Page = 0; map.CardOffset = mem->win[0].card_addr;
+ if (pcmcia_map_mem_page(p_dev->win, &map) != 0)
+ goto next_entry;
+
+ cfg_mem->data->MmioAddress = (unsigned long) ioremap_nocache(cfg_mem->req.Base, cfg_mem->req.Size);
+ cfg_mem->data->MmioLength = cfg_mem->req.Size;
+ }
+ /* If we got this far, we're cool! */
+ return 0;
+ }
+
+next_entry:
+ nsp_dbg(NSP_DEBUG_INIT, "next");
+ pcmcia_disable_device(p_dev);
+ return -ENODEV;
+}
+
static int nsp_cs_config(struct pcmcia_device *link)
{
int ret;
scsi_info_t *info = link->priv;
- tuple_t tuple;
- cisparse_t parse;
- int last_ret, last_fn;
- unsigned char tuple_data[64];
- config_info_t conf;
- win_req_t req;
- memreq_t map;
- cistpl_cftable_entry_t dflt = { 0 };
+ struct nsp_cs_configdata *cfg_mem;
struct Scsi_Host *host;
nsp_hw_data *data = &nsp_data_base;
nsp_dbg(NSP_DEBUG_INIT, "in");
- tuple.Attributes = 0;
- tuple.TupleData = tuple_data;
- tuple.TupleDataMax = sizeof(tuple_data);
- tuple.TupleOffset = 0;
+ cfg_mem = kzalloc(sizeof(cfg_mem), GFP_KERNEL);
+ if (!cfg_mem)
+ return -ENOMEM;
+ cfg_mem->data = data;
- /* Look up the current Vcc */
- CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(link, &conf));
-
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
- while (1) {
- cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
-
- if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
- pcmcia_parse_tuple(link, &tuple, &parse) != 0)
- goto next_entry;
-
- if (cfg->flags & CISTPL_CFTABLE_DEFAULT) { dflt = *cfg; }
- if (cfg->index == 0) { goto next_entry; }
- link->conf.ConfigIndex = cfg->index;
-
- /* Does this card need audio output? */
- if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
- link->conf.Attributes |= CONF_ENABLE_SPKR;
- link->conf.Status = CCSR_AUDIO_ENA;
- }
-
- /* Use power settings for Vcc and Vpp if present */
- /* Note that the CIS values need to be rescaled */
- if (cfg->vcc.present & (1<<CISTPL_POWER_VNOM)) {
- if (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM]/10000) {
- goto next_entry;
- }
- } else if (dflt.vcc.present & (1<<CISTPL_POWER_VNOM)) {
- if (conf.Vcc != dflt.vcc.param[CISTPL_POWER_VNOM]/10000) {
- goto next_entry;
- }
- }
-
- if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM)) {
- link->conf.Vpp =
- cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
- } else if (dflt.vpp1.present & (1 << CISTPL_POWER_VNOM)) {
- link->conf.Vpp =
- dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000;
- }
-
- /* Do we need to allocate an interrupt? */
- if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1) {
- link->conf.Attributes |= CONF_ENABLE_IRQ;
- }
-
- /* IO window settings */
- link->io.NumPorts1 = link->io.NumPorts2 = 0;
- if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
- cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
- if (!(io->flags & CISTPL_IO_8BIT))
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
- if (!(io->flags & CISTPL_IO_16BIT))
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
- link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
- link->io.BasePort1 = io->win[0].base;
- link->io.NumPorts1 = io->win[0].len;
- if (io->nwin > 1) {
- link->io.Attributes2 = link->io.Attributes1;
- link->io.BasePort2 = io->win[1].base;
- link->io.NumPorts2 = io->win[1].len;
- }
- /* This reserves IO space but doesn't actually enable it */
- if (pcmcia_request_io(link, &link->io) != 0)
- goto next_entry;
- }
-
- if ((cfg->mem.nwin > 0) || (dflt.mem.nwin > 0)) {
- cistpl_mem_t *mem =
- (cfg->mem.nwin) ? &cfg->mem : &dflt.mem;
- req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM;
- req.Attributes |= WIN_ENABLE;
- req.Base = mem->win[0].host_addr;
- req.Size = mem->win[0].len;
- if (req.Size < 0x1000) {
- req.Size = 0x1000;
- }
- req.AccessSpeed = 0;
- if (pcmcia_request_window(&link, &req, &link->win) != 0)
- goto next_entry;
- map.Page = 0; map.CardOffset = mem->win[0].card_addr;
- if (pcmcia_map_mem_page(link->win, &map) != 0)
- goto next_entry;
-
- data->MmioAddress = (unsigned long)ioremap_nocache(req.Base, req.Size);
- data->MmioLength = req.Size;
- }
- /* If we got this far, we're cool! */
- break;
-
- next_entry:
- nsp_dbg(NSP_DEBUG_INIT, "next");
- pcmcia_disable_device(link);
- CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple));
- }
+ ret = pcmcia_loop_config(link, nsp_cs_config_check, cfg_mem);
+ goto cs_failed;
if (link->conf.Attributes & CONF_ENABLE_IRQ) {
- CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
+ if (pcmcia_request_irq(link, &link->irq))
+ goto cs_failed;
}
- CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
+
+ ret = pcmcia_request_configuration(link, &link->conf);
+ if (ret)
+ goto cs_failed;
if (free_ports) {
if (link->io.BasePort1) {
@@ -1790,20 +1786,20 @@
printk(" & 0x%04x-0x%04x", link->io.BasePort2,
link->io.BasePort2+link->io.NumPorts2-1);
if (link->win)
- printk(", mem 0x%06lx-0x%06lx", req.Base,
- req.Base+req.Size-1);
+ printk(", mem 0x%06lx-0x%06lx", cfg_mem->req.Base,
+ cfg_mem->req.Base+cfg_mem->req.Size-1);
printk("\n");
+ kfree(cfg_mem);
return 0;
cs_failed:
nsp_dbg(NSP_DEBUG_INIT, "config fail");
- cs_error(link, last_fn, last_ret);
nsp_cs_release(link);
+ kfree(cfg_mem);
return -ENODEV;
} /* nsp_cs_config */
-#undef CS_CHECK
/*======================================================================
diff --git a/drivers/scsi/pcmcia/qlogic_stub.c b/drivers/scsi/pcmcia/qlogic_stub.c
index 67c5a58..20c3e5e 100644
--- a/drivers/scsi/pcmcia/qlogic_stub.c
+++ b/drivers/scsi/pcmcia/qlogic_stub.c
@@ -195,39 +195,33 @@
#define CS_CHECK(fn, ret) \
do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+static int qlogic_config_check(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ p_dev->io.BasePort1 = cfg->io.win[0].base;
+ p_dev->io.NumPorts1 = cfg->io.win[0].len;
+
+ if (p_dev->io.BasePort1 == 0)
+ return -ENODEV;
+
+ return pcmcia_request_io(p_dev, &p_dev->io);
+}
+
static int qlogic_config(struct pcmcia_device * link)
{
scsi_info_t *info = link->priv;
- tuple_t tuple;
- cisparse_t parse;
- int i, last_ret, last_fn;
- unsigned short tuple_data[32];
+ int last_ret, last_fn;
struct Scsi_Host *host;
DEBUG(0, "qlogic_config(0x%p)\n", link);
- info->manf_id = link->manf_id;
-
- tuple.TupleData = (cisdata_t *) tuple_data;
- tuple.TupleDataMax = 64;
- tuple.TupleOffset = 0;
-
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
- while (1) {
- if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
- pcmcia_parse_tuple(link, &tuple, &parse) != 0)
- goto next_entry;
- link->conf.ConfigIndex = parse.cftable_entry.index;
- link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
- link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
- if (link->io.BasePort1 != 0) {
- i = pcmcia_request_io(link, &link->io);
- if (i == CS_SUCCESS)
- break;
- }
- next_entry:
- CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple));
+ last_ret = pcmcia_loop_config(link, qlogic_config_check, NULL);
+ if (last_ret) {
+ cs_error(link, RequestIO, last_ret);
+ goto failed;
}
CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
@@ -262,6 +256,7 @@
cs_failed:
cs_error(link, last_fn, last_ret);
pcmcia_disable_device(link);
+failed:
return -ENODEV;
} /* qlogic_config */
diff --git a/drivers/scsi/pcmcia/sym53c500_cs.c b/drivers/scsi/pcmcia/sym53c500_cs.c
index 0be232b..b330c11 100644
--- a/drivers/scsi/pcmcia/sym53c500_cs.c
+++ b/drivers/scsi/pcmcia/sym53c500_cs.c
@@ -700,15 +700,27 @@
#define CS_CHECK(fn, ret) \
do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+static int SYM53C500_config_check(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ p_dev->io.BasePort1 = cfg->io.win[0].base;
+ p_dev->io.NumPorts1 = cfg->io.win[0].len;
+
+ if (p_dev->io.BasePort1 == 0)
+ return -ENODEV;
+
+ return pcmcia_request_io(p_dev, &p_dev->io);
+}
+
static int
SYM53C500_config(struct pcmcia_device *link)
{
struct scsi_info_t *info = link->priv;
- tuple_t tuple;
- cisparse_t parse;
- int i, last_ret, last_fn;
+ int last_ret, last_fn;
int irq_level, port_base;
- unsigned short tuple_data[32];
struct Scsi_Host *host;
struct scsi_host_template *tpnt = &sym53c500_driver_template;
struct sym53c500_data *data;
@@ -717,27 +729,10 @@
info->manf_id = link->manf_id;
- tuple.TupleData = (cisdata_t *)tuple_data;
- tuple.TupleDataMax = 64;
- tuple.TupleOffset = 0;
-
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
- while (1) {
- if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
- pcmcia_parse_tuple(link, &tuple, &parse) != 0)
- goto next_entry;
- link->conf.ConfigIndex = parse.cftable_entry.index;
- link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
- link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
-
- if (link->io.BasePort1 != 0) {
- i = pcmcia_request_io(link, &link->io);
- if (i == CS_SUCCESS)
- break;
- }
-next_entry:
- CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple));
+ last_ret = pcmcia_loop_config(link, SYM53C500_config_check, NULL);
+ if (last_ret) {
+ cs_error(link, RequestIO, last_ret);
+ goto failed;
}
CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
@@ -831,6 +826,7 @@
cs_failed:
cs_error(link, last_fn, last_ret);
+failed:
SYM53C500_release(link);
return -ENODEV;
} /* SYM53C500_config */
diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c
index 9053508..69d6ad8 100644
--- a/drivers/scsi/qlogicpti.c
+++ b/drivers/scsi/qlogicpti.c
@@ -1,6 +1,6 @@
/* qlogicpti.c: Performance Technologies QlogicISP sbus card driver.
*
- * Copyright (C) 1996, 2006 David S. Miller (davem@davemloft.net)
+ * Copyright (C) 1996, 2006, 2008 David S. Miller (davem@davemloft.net)
*
* A lot of this driver was directly stolen from Erik H. Moe's PCI
* Qlogic ISP driver. Mucho kudos to him for this code.
@@ -25,12 +25,14 @@
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/jiffies.h>
+#include <linux/dma-mapping.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <asm/byteorder.h>
#include "qlogicpti.h"
-#include <asm/sbus.h>
#include <asm/dma.h>
#include <asm/system.h>
#include <asm/ptrace.h>
@@ -157,7 +159,7 @@
* is a nop and the chip ends up using the smallest burst
* size. -DaveM
*/
- if (sbus_can_burst64(qpti->sdev) && (bursts & DMA_BURST64)) {
+ if (sbus_can_burst64() && (bursts & DMA_BURST64)) {
val = (SBUS_CFG1_BENAB | SBUS_CFG1_B64);
} else
#endif
@@ -684,19 +686,19 @@
static int __devinit qpti_map_regs(struct qlogicpti *qpti)
{
- struct sbus_dev *sdev = qpti->sdev;
+ struct of_device *op = qpti->op;
- qpti->qregs = sbus_ioremap(&sdev->resource[0], 0,
- sdev->reg_addrs[0].reg_size,
- "PTI Qlogic/ISP");
+ qpti->qregs = of_ioremap(&op->resource[0], 0,
+ resource_size(&op->resource[0]),
+ "PTI Qlogic/ISP");
if (!qpti->qregs) {
printk("PTI: Qlogic/ISP registers are unmappable\n");
return -1;
}
if (qpti->is_pti) {
- qpti->sreg = sbus_ioremap(&sdev->resource[0], (16 * 4096),
- sizeof(unsigned char),
- "PTI Qlogic/ISP statreg");
+ qpti->sreg = of_ioremap(&op->resource[0], (16 * 4096),
+ sizeof(unsigned char),
+ "PTI Qlogic/ISP statreg");
if (!qpti->sreg) {
printk("PTI: Qlogic/ISP status register is unmappable\n");
return -1;
@@ -707,9 +709,9 @@
static int __devinit qpti_register_irq(struct qlogicpti *qpti)
{
- struct sbus_dev *sdev = qpti->sdev;
+ struct of_device *op = qpti->op;
- qpti->qhost->irq = qpti->irq = sdev->irqs[0];
+ qpti->qhost->irq = qpti->irq = op->irqs[0];
/* We used to try various overly-clever things to
* reduce the interrupt processing overhead on
@@ -732,17 +734,19 @@
static void __devinit qpti_get_scsi_id(struct qlogicpti *qpti)
{
- qpti->scsi_id = prom_getintdefault(qpti->prom_node,
- "initiator-id",
- -1);
+ struct of_device *op = qpti->op;
+ struct device_node *dp;
+
+ dp = op->node;
+
+ qpti->scsi_id = of_getintprop_default(dp, "initiator-id", -1);
if (qpti->scsi_id == -1)
- qpti->scsi_id = prom_getintdefault(qpti->prom_node,
- "scsi-initiator-id",
- -1);
+ qpti->scsi_id = of_getintprop_default(dp, "scsi-initiator-id",
+ -1);
if (qpti->scsi_id == -1)
qpti->scsi_id =
- prom_getintdefault(qpti->sdev->bus->prom_node,
- "scsi-initiator-id", 7);
+ of_getintprop_default(dp->parent,
+ "scsi-initiator-id", 7);
qpti->qhost->this_id = qpti->scsi_id;
qpti->qhost->max_sectors = 64;
@@ -751,12 +755,11 @@
static void qpti_get_bursts(struct qlogicpti *qpti)
{
- struct sbus_dev *sdev = qpti->sdev;
+ struct of_device *op = qpti->op;
u8 bursts, bmask;
- bursts = prom_getintdefault(qpti->prom_node, "burst-sizes", 0xff);
- bmask = prom_getintdefault(sdev->bus->prom_node,
- "burst-sizes", 0xff);
+ bursts = of_getintprop_default(op->node, "burst-sizes", 0xff);
+ bmask = of_getintprop_default(op->node->parent, "burst-sizes", 0xff);
if (bmask != 0xff)
bursts &= bmask;
if (bursts == 0xff ||
@@ -785,25 +788,25 @@
*/
static int __devinit qpti_map_queues(struct qlogicpti *qpti)
{
- struct sbus_dev *sdev = qpti->sdev;
+ struct of_device *op = qpti->op;
#define QSIZE(entries) (((entries) + 1) * QUEUE_ENTRY_LEN)
- qpti->res_cpu = sbus_alloc_consistent(sdev,
- QSIZE(RES_QUEUE_LEN),
- &qpti->res_dvma);
+ qpti->res_cpu = dma_alloc_coherent(&op->dev,
+ QSIZE(RES_QUEUE_LEN),
+ &qpti->res_dvma, GFP_ATOMIC);
if (qpti->res_cpu == NULL ||
qpti->res_dvma == 0) {
printk("QPTI: Cannot map response queue.\n");
return -1;
}
- qpti->req_cpu = sbus_alloc_consistent(sdev,
- QSIZE(QLOGICPTI_REQ_QUEUE_LEN),
- &qpti->req_dvma);
+ qpti->req_cpu = dma_alloc_coherent(&op->dev,
+ QSIZE(QLOGICPTI_REQ_QUEUE_LEN),
+ &qpti->req_dvma, GFP_ATOMIC);
if (qpti->req_cpu == NULL ||
qpti->req_dvma == 0) {
- sbus_free_consistent(sdev, QSIZE(RES_QUEUE_LEN),
- qpti->res_cpu, qpti->res_dvma);
+ dma_free_coherent(&op->dev, QSIZE(RES_QUEUE_LEN),
+ qpti->res_cpu, qpti->res_dvma);
printk("QPTI: Cannot map request queue.\n");
return -1;
}
@@ -875,8 +878,9 @@
int sg_count;
sg = scsi_sglist(Cmnd);
- sg_count = sbus_map_sg(qpti->sdev, sg, scsi_sg_count(Cmnd),
- Cmnd->sc_data_direction);
+ sg_count = dma_map_sg(&qpti->op->dev, sg,
+ scsi_sg_count(Cmnd),
+ Cmnd->sc_data_direction);
ds = cmd->dataseg;
cmd->segment_cnt = sg_count;
@@ -1152,9 +1156,9 @@
Cmnd->result = DID_ERROR << 16;
if (scsi_bufflen(Cmnd))
- sbus_unmap_sg(qpti->sdev,
- scsi_sglist(Cmnd), scsi_sg_count(Cmnd),
- Cmnd->sc_data_direction);
+ dma_unmap_sg(&qpti->op->dev,
+ scsi_sglist(Cmnd), scsi_sg_count(Cmnd),
+ Cmnd->sc_data_direction);
qpti->cmd_count[Cmnd->device->id]--;
sbus_writew(out_ptr, qpti->qregs + MBOX5);
@@ -1268,34 +1272,32 @@
.use_clustering = ENABLE_CLUSTERING,
};
-static int __devinit qpti_sbus_probe(struct of_device *dev, const struct of_device_id *match)
+static int __devinit qpti_sbus_probe(struct of_device *op, const struct of_device_id *match)
{
- static int nqptis;
- struct sbus_dev *sdev = to_sbus_device(&dev->dev);
- struct device_node *dp = dev->node;
struct scsi_host_template *tpnt = match->data;
+ struct device_node *dp = op->node;
struct Scsi_Host *host;
struct qlogicpti *qpti;
+ static int nqptis;
const char *fcode;
/* Sometimes Antares cards come up not completely
* setup, and we get a report of a zero IRQ.
*/
- if (sdev->irqs[0] == 0)
+ if (op->irqs[0] == 0)
return -ENODEV;
host = scsi_host_alloc(tpnt, sizeof(struct qlogicpti));
if (!host)
return -ENOMEM;
- qpti = (struct qlogicpti *) host->hostdata;
+ qpti = shost_priv(host);
host->max_id = MAX_TARGETS;
qpti->qhost = host;
- qpti->sdev = sdev;
+ qpti->op = op;
qpti->qpti_id = nqptis;
- qpti->prom_node = sdev->prom_node;
- strcpy(qpti->prom_name, sdev->ofdev.node->name);
+ strcpy(qpti->prom_name, op->node->name);
qpti->is_pti = strcmp(qpti->prom_name, "QLGC,isp");
if (qpti_map_regs(qpti) < 0)
@@ -1341,12 +1343,12 @@
(qpti->ultra ? "Ultra" : "Fast"),
(qpti->differential ? "differential" : "single ended"));
- if (scsi_add_host(host, &dev->dev)) {
+ if (scsi_add_host(host, &op->dev)) {
printk("qlogicpti%d: Failed scsi_add_host\n", qpti->qpti_id);
goto fail_unmap_queues;
}
- dev_set_drvdata(&sdev->ofdev.dev, qpti);
+ dev_set_drvdata(&op->dev, qpti);
qpti_chain_add(qpti);
@@ -1357,19 +1359,20 @@
fail_unmap_queues:
#define QSIZE(entries) (((entries) + 1) * QUEUE_ENTRY_LEN)
- sbus_free_consistent(qpti->sdev,
- QSIZE(RES_QUEUE_LEN),
- qpti->res_cpu, qpti->res_dvma);
- sbus_free_consistent(qpti->sdev,
- QSIZE(QLOGICPTI_REQ_QUEUE_LEN),
- qpti->req_cpu, qpti->req_dvma);
+ dma_free_coherent(&op->dev,
+ QSIZE(RES_QUEUE_LEN),
+ qpti->res_cpu, qpti->res_dvma);
+ dma_free_coherent(&op->dev,
+ QSIZE(QLOGICPTI_REQ_QUEUE_LEN),
+ qpti->req_cpu, qpti->req_dvma);
#undef QSIZE
fail_unmap_regs:
- sbus_iounmap(qpti->qregs,
- qpti->sdev->reg_addrs[0].reg_size);
+ of_iounmap(&op->resource[0], qpti->qregs,
+ resource_size(&op->resource[0]));
if (qpti->is_pti)
- sbus_iounmap(qpti->sreg, sizeof(unsigned char));
+ of_iounmap(&op->resource[0], qpti->sreg,
+ sizeof(unsigned char));
fail_free_irq:
free_irq(qpti->irq, qpti);
@@ -1380,9 +1383,9 @@
return -ENODEV;
}
-static int __devexit qpti_sbus_remove(struct of_device *dev)
+static int __devexit qpti_sbus_remove(struct of_device *op)
{
- struct qlogicpti *qpti = dev_get_drvdata(&dev->dev);
+ struct qlogicpti *qpti = dev_get_drvdata(&op->dev);
qpti_chain_del(qpti);
@@ -1395,24 +1398,25 @@
free_irq(qpti->irq, qpti);
#define QSIZE(entries) (((entries) + 1) * QUEUE_ENTRY_LEN)
- sbus_free_consistent(qpti->sdev,
- QSIZE(RES_QUEUE_LEN),
- qpti->res_cpu, qpti->res_dvma);
- sbus_free_consistent(qpti->sdev,
- QSIZE(QLOGICPTI_REQ_QUEUE_LEN),
- qpti->req_cpu, qpti->req_dvma);
+ dma_free_coherent(&op->dev,
+ QSIZE(RES_QUEUE_LEN),
+ qpti->res_cpu, qpti->res_dvma);
+ dma_free_coherent(&op->dev,
+ QSIZE(QLOGICPTI_REQ_QUEUE_LEN),
+ qpti->req_cpu, qpti->req_dvma);
#undef QSIZE
- sbus_iounmap(qpti->qregs, qpti->sdev->reg_addrs[0].reg_size);
+ of_iounmap(&op->resource[0], qpti->qregs,
+ resource_size(&op->resource[0]));
if (qpti->is_pti)
- sbus_iounmap(qpti->sreg, sizeof(unsigned char));
+ of_iounmap(&op->resource[0], qpti->sreg, sizeof(unsigned char));
scsi_host_put(qpti->qhost);
return 0;
}
-static struct of_device_id qpti_match[] = {
+static const struct of_device_id qpti_match[] = {
{
.name = "ptisp",
.data = &qpti_template,
@@ -1442,7 +1446,7 @@
static int __init qpti_init(void)
{
- return of_register_driver(&qpti_sbus_driver, &sbus_bus_type);
+ return of_register_driver(&qpti_sbus_driver, &of_bus_type);
}
static void __exit qpti_exit(void)
@@ -1453,7 +1457,7 @@
MODULE_DESCRIPTION("QlogicISP SBUS driver");
MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
MODULE_LICENSE("GPL");
-MODULE_VERSION("2.0");
+MODULE_VERSION("2.1");
module_init(qpti_init);
module_exit(qpti_exit);
diff --git a/drivers/scsi/qlogicpti.h b/drivers/scsi/qlogicpti.h
index ef6da2d..9c053bb 100644
--- a/drivers/scsi/qlogicpti.h
+++ b/drivers/scsi/qlogicpti.h
@@ -342,7 +342,7 @@
u_int req_in_ptr; /* index of next request slot */
u_int res_out_ptr; /* index of next result slot */
long send_marker; /* must we send a marker? */
- struct sbus_dev *sdev;
+ struct of_device *op;
unsigned long __pad;
int cmd_count[MAX_TARGETS];
diff --git a/drivers/scsi/sun_esp.c b/drivers/scsi/sun_esp.c
index f9cf701..3d73aad 100644
--- a/drivers/scsi/sun_esp.c
+++ b/drivers/scsi/sun_esp.c
@@ -1,6 +1,6 @@
/* sun_esp.c: ESP front-end for Sparc SBUS systems.
*
- * Copyright (C) 2007 David S. Miller (davem@davemloft.net)
+ * Copyright (C) 2007, 2008 David S. Miller (davem@davemloft.net)
*/
#include <linux/kernel.h>
@@ -9,79 +9,89 @@
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/init.h>
+#include <linux/dma-mapping.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/dma.h>
-#include <asm/sbus.h>
-
#include <scsi/scsi_host.h>
#include "esp_scsi.h"
#define DRV_MODULE_NAME "sun_esp"
#define PFX DRV_MODULE_NAME ": "
-#define DRV_VERSION "1.000"
-#define DRV_MODULE_RELDATE "April 19, 2007"
+#define DRV_VERSION "1.100"
+#define DRV_MODULE_RELDATE "August 27, 2008"
#define dma_read32(REG) \
sbus_readl(esp->dma_regs + (REG))
#define dma_write32(VAL, REG) \
sbus_writel((VAL), esp->dma_regs + (REG))
-static int __devinit esp_sbus_find_dma(struct esp *esp, struct sbus_dev *dma_sdev)
+/* DVMA chip revisions */
+enum dvma_rev {
+ dvmarev0,
+ dvmaesc1,
+ dvmarev1,
+ dvmarev2,
+ dvmarev3,
+ dvmarevplus,
+ dvmahme
+};
+
+static int __devinit esp_sbus_setup_dma(struct esp *esp,
+ struct of_device *dma_of)
{
- struct sbus_dev *sdev = esp->dev;
- struct sbus_dma *dma;
+ esp->dma = dma_of;
- if (dma_sdev != NULL) {
- for_each_dvma(dma) {
- if (dma->sdev == dma_sdev)
- break;
- }
- } else {
- for_each_dvma(dma) {
- if (dma->sdev == NULL)
- break;
+ esp->dma_regs = of_ioremap(&dma_of->resource[0], 0,
+ resource_size(&dma_of->resource[0]),
+ "espdma");
+ if (!esp->dma_regs)
+ return -ENOMEM;
- /* If bus + slot are the same and it has the
- * correct OBP name, it's ours.
- */
- if (sdev->bus == dma->sdev->bus &&
- sdev->slot == dma->sdev->slot &&
- (!strcmp(dma->sdev->prom_name, "dma") ||
- !strcmp(dma->sdev->prom_name, "espdma")))
- break;
- }
+ switch (dma_read32(DMA_CSR) & DMA_DEVICE_ID) {
+ case DMA_VERS0:
+ esp->dmarev = dvmarev0;
+ break;
+ case DMA_ESCV1:
+ esp->dmarev = dvmaesc1;
+ break;
+ case DMA_VERS1:
+ esp->dmarev = dvmarev1;
+ break;
+ case DMA_VERS2:
+ esp->dmarev = dvmarev2;
+ break;
+ case DMA_VERHME:
+ esp->dmarev = dvmahme;
+ break;
+ case DMA_VERSPLUS:
+ esp->dmarev = dvmarevplus;
+ break;
}
- if (dma == NULL) {
- printk(KERN_ERR PFX "[%s] Cannot find dma.\n",
- sdev->ofdev.node->full_name);
- return -ENODEV;
- }
- esp->dma = dma;
- esp->dma_regs = dma->regs;
-
return 0;
}
static int __devinit esp_sbus_map_regs(struct esp *esp, int hme)
{
- struct sbus_dev *sdev = esp->dev;
+ struct of_device *op = esp->dev;
struct resource *res;
/* On HME, two reg sets exist, first is DVMA,
* second is ESP registers.
*/
if (hme)
- res = &sdev->resource[1];
+ res = &op->resource[1];
else
- res = &sdev->resource[0];
+ res = &op->resource[0];
- esp->regs = sbus_ioremap(res, 0, SBUS_ESP_REG_SIZE, "ESP");
+ esp->regs = of_ioremap(res, 0, SBUS_ESP_REG_SIZE, "ESP");
if (!esp->regs)
return -ENOMEM;
@@ -90,10 +100,11 @@
static int __devinit esp_sbus_map_command_block(struct esp *esp)
{
- struct sbus_dev *sdev = esp->dev;
+ struct of_device *op = esp->dev;
- esp->command_block = sbus_alloc_consistent(sdev, 16,
- &esp->command_block_dma);
+ esp->command_block = dma_alloc_coherent(&op->dev, 16,
+ &esp->command_block_dma,
+ GFP_ATOMIC);
if (!esp->command_block)
return -ENOMEM;
return 0;
@@ -102,17 +113,18 @@
static int __devinit esp_sbus_register_irq(struct esp *esp)
{
struct Scsi_Host *host = esp->host;
- struct sbus_dev *sdev = esp->dev;
+ struct of_device *op = esp->dev;
- host->irq = sdev->irqs[0];
+ host->irq = op->irqs[0];
return request_irq(host->irq, scsi_esp_intr, IRQF_SHARED, "ESP", esp);
}
-static void __devinit esp_get_scsi_id(struct esp *esp)
+static void __devinit esp_get_scsi_id(struct esp *esp, struct of_device *espdma)
{
- struct sbus_dev *sdev = esp->dev;
- struct device_node *dp = sdev->ofdev.node;
+ struct of_device *op = esp->dev;
+ struct device_node *dp;
+ dp = op->node;
esp->scsi_id = of_getintprop_default(dp, "initiator-id", 0xff);
if (esp->scsi_id != 0xff)
goto done;
@@ -121,13 +133,7 @@
if (esp->scsi_id != 0xff)
goto done;
- if (!sdev->bus) {
- /* SUN4 */
- esp->scsi_id = 7;
- goto done;
- }
-
- esp->scsi_id = of_getintprop_default(sdev->bus->ofdev.node,
+ esp->scsi_id = of_getintprop_default(espdma->node,
"scsi-initiator-id", 7);
done:
@@ -137,9 +143,10 @@
static void __devinit esp_get_differential(struct esp *esp)
{
- struct sbus_dev *sdev = esp->dev;
- struct device_node *dp = sdev->ofdev.node;
+ struct of_device *op = esp->dev;
+ struct device_node *dp;
+ dp = op->node;
if (of_find_property(dp, "differential", NULL))
esp->flags |= ESP_FLAG_DIFFERENTIAL;
else
@@ -148,43 +155,36 @@
static void __devinit esp_get_clock_params(struct esp *esp)
{
- struct sbus_dev *sdev = esp->dev;
- struct device_node *dp = sdev->ofdev.node;
- struct device_node *bus_dp;
+ struct of_device *op = esp->dev;
+ struct device_node *bus_dp, *dp;
int fmhz;
- bus_dp = NULL;
- if (sdev != NULL && sdev->bus != NULL)
- bus_dp = sdev->bus->ofdev.node;
+ dp = op->node;
+ bus_dp = dp->parent;
fmhz = of_getintprop_default(dp, "clock-frequency", 0);
if (fmhz == 0)
- fmhz = (!bus_dp) ? 0 :
- of_getintprop_default(bus_dp, "clock-frequency", 0);
+ fmhz = of_getintprop_default(bus_dp, "clock-frequency", 0);
esp->cfreq = fmhz;
}
-static void __devinit esp_get_bursts(struct esp *esp, struct sbus_dev *dma)
+static void __devinit esp_get_bursts(struct esp *esp, struct of_device *dma_of)
{
- struct sbus_dev *sdev = esp->dev;
- struct device_node *dp = sdev->ofdev.node;
- u8 bursts;
+ struct device_node *dma_dp = dma_of->node;
+ struct of_device *op = esp->dev;
+ struct device_node *dp;
+ u8 bursts, val;
+ dp = op->node;
bursts = of_getintprop_default(dp, "burst-sizes", 0xff);
- if (dma) {
- struct device_node *dma_dp = dma->ofdev.node;
- u8 val = of_getintprop_default(dma_dp, "burst-sizes", 0xff);
- if (val != 0xff)
- bursts &= val;
- }
+ val = of_getintprop_default(dma_dp, "burst-sizes", 0xff);
+ if (val != 0xff)
+ bursts &= val;
- if (sdev->bus) {
- u8 val = of_getintprop_default(sdev->bus->ofdev.node,
- "burst-sizes", 0xff);
- if (val != 0xff)
- bursts &= val;
- }
+ val = of_getintprop_default(dma_dp->parent, "burst-sizes", 0xff);
+ if (val != 0xff)
+ bursts &= val;
if (bursts == 0xff ||
(bursts & DMA_BURST16) == 0 ||
@@ -194,9 +194,9 @@
esp->bursts = bursts;
}
-static void __devinit esp_sbus_get_props(struct esp *esp, struct sbus_dev *espdma)
+static void __devinit esp_sbus_get_props(struct esp *esp, struct of_device *espdma)
{
- esp_get_scsi_id(esp);
+ esp_get_scsi_id(esp, espdma);
esp_get_differential(esp);
esp_get_clock_params(esp);
esp_get_bursts(esp, espdma);
@@ -215,25 +215,33 @@
static dma_addr_t sbus_esp_map_single(struct esp *esp, void *buf,
size_t sz, int dir)
{
- return sbus_map_single(esp->dev, buf, sz, dir);
+ struct of_device *op = esp->dev;
+
+ return dma_map_single(&op->dev, buf, sz, dir);
}
static int sbus_esp_map_sg(struct esp *esp, struct scatterlist *sg,
int num_sg, int dir)
{
- return sbus_map_sg(esp->dev, sg, num_sg, dir);
+ struct of_device *op = esp->dev;
+
+ return dma_map_sg(&op->dev, sg, num_sg, dir);
}
static void sbus_esp_unmap_single(struct esp *esp, dma_addr_t addr,
size_t sz, int dir)
{
- sbus_unmap_single(esp->dev, addr, sz, dir);
+ struct of_device *op = esp->dev;
+
+ dma_unmap_single(&op->dev, addr, sz, dir);
}
static void sbus_esp_unmap_sg(struct esp *esp, struct scatterlist *sg,
int num_sg, int dir)
{
- sbus_unmap_sg(esp->dev, sg, num_sg, dir);
+ struct of_device *op = esp->dev;
+
+ dma_unmap_sg(&op->dev, sg, num_sg, dir);
}
static int sbus_esp_irq_pending(struct esp *esp)
@@ -247,24 +255,26 @@
{
int can_do_burst16, can_do_burst32, can_do_burst64;
int can_do_sbus64, lim;
+ struct of_device *op;
u32 val;
can_do_burst16 = (esp->bursts & DMA_BURST16) != 0;
can_do_burst32 = (esp->bursts & DMA_BURST32) != 0;
can_do_burst64 = 0;
can_do_sbus64 = 0;
- if (sbus_can_dma_64bit(esp->dev))
+ op = esp->dev;
+ if (sbus_can_dma_64bit())
can_do_sbus64 = 1;
- if (sbus_can_burst64(esp->sdev))
+ if (sbus_can_burst64())
can_do_burst64 = (esp->bursts & DMA_BURST64) != 0;
/* Put the DVMA into a known state. */
- if (esp->dma->revision != dvmahme) {
+ if (esp->dmarev != dvmahme) {
val = dma_read32(DMA_CSR);
dma_write32(val | DMA_RST_SCSI, DMA_CSR);
dma_write32(val & ~DMA_RST_SCSI, DMA_CSR);
}
- switch (esp->dma->revision) {
+ switch (esp->dmarev) {
case dvmahme:
dma_write32(DMA_RESET_FAS366, DMA_CSR);
dma_write32(DMA_RST_SCSI, DMA_CSR);
@@ -282,7 +292,7 @@
if (can_do_sbus64) {
esp->prev_hme_dmacsr |= DMA_SCSI_SBUS64;
- sbus_set_sbus64(esp->dev, esp->bursts);
+ sbus_set_sbus64(&op->dev, esp->bursts);
}
lim = 1000;
@@ -346,14 +356,14 @@
u32 csr;
int lim;
- if (esp->dma->revision == dvmahme)
+ if (esp->dmarev == dvmahme)
return;
csr = dma_read32(DMA_CSR);
if (!(csr & DMA_FIFO_ISDRAIN))
return;
- if (esp->dma->revision != dvmarev3 && esp->dma->revision != dvmaesc1)
+ if (esp->dmarev != dvmarev3 && esp->dmarev != dvmaesc1)
dma_write32(csr | DMA_FIFO_STDRAIN, DMA_CSR);
lim = 1000;
@@ -369,7 +379,7 @@
static void sbus_esp_dma_invalidate(struct esp *esp)
{
- if (esp->dma->revision == dvmahme) {
+ if (esp->dmarev == dvmahme) {
dma_write32(DMA_RST_SCSI, DMA_CSR);
esp->prev_hme_dmacsr = ((esp->prev_hme_dmacsr |
@@ -440,7 +450,7 @@
else
csr &= ~DMA_ST_WRITE;
dma_write32(csr, DMA_CSR);
- if (esp->dma->revision == dvmaesc1) {
+ if (esp->dmarev == dvmaesc1) {
u32 end = PAGE_ALIGN(addr + dma_count + 16U);
dma_write32(end - addr, DMA_COUNT);
}
@@ -476,10 +486,8 @@
.dma_error = sbus_esp_dma_error,
};
-static int __devinit esp_sbus_probe_one(struct device *dev,
- struct sbus_dev *esp_dev,
- struct sbus_dev *espdma,
- struct sbus_bus *sbus,
+static int __devinit esp_sbus_probe_one(struct of_device *op,
+ struct of_device *espdma,
int hme)
{
struct scsi_host_template *tpnt = &scsi_esp_template;
@@ -497,13 +505,13 @@
esp = shost_priv(host);
esp->host = host;
- esp->dev = esp_dev;
+ esp->dev = op;
esp->ops = &sbus_esp_ops;
if (hme)
esp->flags |= ESP_FLAG_WIDE_CAPABLE;
- err = esp_sbus_find_dma(esp, espdma);
+ err = esp_sbus_setup_dma(esp, espdma);
if (err < 0)
goto fail_unlink;
@@ -525,15 +533,15 @@
* come up with the reset bit set, so make sure that
* is clear first.
*/
- if (esp->dma->revision == dvmaesc1) {
+ if (esp->dmarev == dvmaesc1) {
u32 val = dma_read32(DMA_CSR);
dma_write32(val & ~DMA_RST_SCSI, DMA_CSR);
}
- dev_set_drvdata(&esp_dev->ofdev.dev, esp);
+ dev_set_drvdata(&op->dev, esp);
- err = scsi_esp_register(esp, dev);
+ err = scsi_esp_register(esp, &op->dev);
if (err)
goto fail_free_irq;
@@ -542,41 +550,46 @@
fail_free_irq:
free_irq(host->irq, esp);
fail_unmap_command_block:
- sbus_free_consistent(esp->dev, 16,
- esp->command_block,
- esp->command_block_dma);
+ dma_free_coherent(&op->dev, 16,
+ esp->command_block,
+ esp->command_block_dma);
fail_unmap_regs:
- sbus_iounmap(esp->regs, SBUS_ESP_REG_SIZE);
+ of_iounmap(&op->resource[(hme ? 1 : 0)], esp->regs, SBUS_ESP_REG_SIZE);
fail_unlink:
scsi_host_put(host);
fail:
return err;
}
-static int __devinit esp_sbus_probe(struct of_device *dev, const struct of_device_id *match)
+static int __devinit esp_sbus_probe(struct of_device *op, const struct of_device_id *match)
{
- struct sbus_dev *sdev = to_sbus_device(&dev->dev);
- struct device_node *dp = dev->node;
- struct sbus_dev *dma_sdev = NULL;
+ struct device_node *dma_node = NULL;
+ struct device_node *dp = op->node;
+ struct of_device *dma_of = NULL;
int hme = 0;
if (dp->parent &&
(!strcmp(dp->parent->name, "espdma") ||
!strcmp(dp->parent->name, "dma")))
- dma_sdev = sdev->parent;
+ dma_node = dp->parent;
else if (!strcmp(dp->name, "SUNW,fas")) {
- dma_sdev = sdev;
+ dma_node = op->node;
hme = 1;
}
+ if (dma_node)
+ dma_of = of_find_device_by_node(dma_node);
+ if (!dma_of)
+ return -ENODEV;
- return esp_sbus_probe_one(&dev->dev, sdev, dma_sdev,
- sdev->bus, hme);
+ return esp_sbus_probe_one(op, dma_of, hme);
}
-static int __devexit esp_sbus_remove(struct of_device *dev)
+static int __devexit esp_sbus_remove(struct of_device *op)
{
- struct esp *esp = dev_get_drvdata(&dev->dev);
+ struct esp *esp = dev_get_drvdata(&op->dev);
+ struct of_device *dma_of = esp->dma;
unsigned int irq = esp->host->irq;
+ bool is_hme;
u32 val;
scsi_esp_unregister(esp);
@@ -586,17 +599,25 @@
dma_write32(val & ~DMA_INT_ENAB, DMA_CSR);
free_irq(irq, esp);
- sbus_free_consistent(esp->dev, 16,
- esp->command_block,
- esp->command_block_dma);
- sbus_iounmap(esp->regs, SBUS_ESP_REG_SIZE);
+
+ is_hme = (esp->dmarev == dvmahme);
+
+ dma_free_coherent(&op->dev, 16,
+ esp->command_block,
+ esp->command_block_dma);
+ of_iounmap(&op->resource[(is_hme ? 1 : 0)], esp->regs,
+ SBUS_ESP_REG_SIZE);
+ of_iounmap(&dma_of->resource[0], esp->dma_regs,
+ resource_size(&dma_of->resource[0]));
scsi_host_put(esp->host);
+ dev_set_drvdata(&op->dev, NULL);
+
return 0;
}
-static struct of_device_id esp_match[] = {
+static const struct of_device_id esp_match[] = {
{
.name = "SUNW,esp",
},
@@ -619,7 +640,7 @@
static int __init sunesp_init(void)
{
- return of_register_driver(&esp_sbus_driver, &sbus_bus_type);
+ return of_register_driver(&esp_sbus_driver, &of_bus_type);
}
static void __exit sunesp_exit(void)
diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
index 9ccc563..d3ca7d3 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -44,6 +44,10 @@
#include "8250.h"
+#ifdef CONFIG_SPARC
+#include "suncore.h"
+#endif
+
/*
* Configuration:
* share_irqs - whether we pass IRQF_SHARED to request_irq(). This option
@@ -53,6 +57,13 @@
static unsigned int nr_uarts = CONFIG_SERIAL_8250_RUNTIME_UARTS;
+static struct uart_driver serial8250_reg;
+
+static int serial_index(struct uart_port *port)
+{
+ return (serial8250_reg.minor - 64) + port->line;
+}
+
/*
* Debugging.
*/
@@ -536,7 +547,7 @@
/*
* FIFO support.
*/
-static inline void serial8250_clear_fifos(struct uart_8250_port *p)
+static void serial8250_clear_fifos(struct uart_8250_port *p)
{
if (p->capabilities & UART_CAP_FIFO) {
serial_outp(p, UART_FCR, UART_FCR_ENABLE_FIFO);
@@ -551,7 +562,7 @@
* capability" bit enabled. Note that on XR16C850s, we need to
* reset LCR to write to IER.
*/
-static inline void serial8250_set_sleep(struct uart_8250_port *p, int sleep)
+static void serial8250_set_sleep(struct uart_8250_port *p, int sleep)
{
if (p->capabilities & UART_CAP_SLEEP) {
if (p->capabilities & UART_CAP_EFR) {
@@ -993,7 +1004,7 @@
return;
DEBUG_AUTOCONF("ttyS%d: autoconf (0x%04x, 0x%p): ",
- up->port.line, up->port.iobase, up->port.membase);
+ serial_index(&up->port), up->port.iobase, up->port.membase);
/*
* We really do need global IRQs disabled here - we're going to
@@ -1128,8 +1139,8 @@
if (up->capabilities != uart_config[up->port.type].flags) {
printk(KERN_WARNING
"ttyS%d: detected caps %08x should be %08x\n",
- up->port.line, up->capabilities,
- uart_config[up->port.type].flags);
+ serial_index(&up->port), up->capabilities,
+ uart_config[up->port.type].flags);
}
up->port.fifosize = uart_config[up->port.type].fifo_size;
@@ -1424,8 +1435,7 @@
/*
* This handles the interrupt from one port.
*/
-static inline void
-serial8250_handle_port(struct uart_8250_port *up)
+static void serial8250_handle_port(struct uart_8250_port *up)
{
unsigned int status;
unsigned long flags;
@@ -1719,7 +1729,7 @@
/*
* Wait for transmitter & holding register to empty
*/
-static inline void wait_for_xmitr(struct uart_8250_port *up, int bits)
+static void wait_for_xmitr(struct uart_8250_port *up, int bits)
{
unsigned int status, tmout = 10000;
@@ -1854,7 +1864,8 @@
*/
if (!(up->port.flags & UPF_BUGGY_UART) &&
(serial_inp(up, UART_LSR) == 0xff)) {
- printk("ttyS%d: LSR safety check engaged!\n", up->port.line);
+ printk(KERN_INFO "ttyS%d: LSR safety check engaged!\n",
+ serial_index(&up->port));
return -ENODEV;
}
@@ -1909,7 +1920,8 @@
*/
if (!(iir1 & UART_IIR_NO_INT) && (iir & UART_IIR_NO_INT)) {
up->bugs |= UART_BUG_THRE;
- pr_debug("ttyS%d - using backup timer\n", port->line);
+ pr_debug("ttyS%d - using backup timer\n",
+ serial_index(port));
}
}
@@ -1969,7 +1981,7 @@
if (!(up->bugs & UART_BUG_TXEN)) {
up->bugs |= UART_BUG_TXEN;
pr_debug("ttyS%d - enabling bad tx status workarounds\n",
- port->line);
+ serial_index(port));
}
} else {
up->bugs &= ~UART_BUG_TXEN;
@@ -2630,7 +2642,6 @@
return serial8250_find_port_for_earlycon();
}
-static struct uart_driver serial8250_reg;
static struct console serial8250_console = {
.name = "ttyS",
.write = serial8250_console_write,
@@ -2677,7 +2688,6 @@
.dev_name = "ttyS",
.major = TTY_MAJOR,
.minor = 64,
- .nr = UART_NR,
.cons = SERIAL8250_CONSOLE,
};
@@ -2962,7 +2972,12 @@
for (i = 0; i < NR_IRQS; i++)
spin_lock_init(&irq_lists[i].lock);
+#ifdef CONFIG_SPARC
+ ret = sunserial_register_minors(&serial8250_reg, UART_NR);
+#else
+ serial8250_reg.nr = UART_NR;
ret = uart_register_driver(&serial8250_reg);
+#endif
if (ret)
goto out;
@@ -2987,7 +3002,11 @@
put_dev:
platform_device_put(serial8250_isa_devs);
unreg_uart_drv:
+#ifdef CONFIG_SPARC
+ sunserial_unregister_minors(&serial8250_reg, UART_NR);
+#else
uart_unregister_driver(&serial8250_reg);
+#endif
out:
return ret;
}
@@ -3006,7 +3025,11 @@
platform_driver_unregister(&serial8250_isa_driver);
platform_device_unregister(isa_dev);
+#ifdef CONFIG_SPARC
+ sunserial_unregister_minors(&serial8250_reg, UART_NR);
+#else
uart_unregister_driver(&serial8250_reg);
+#endif
}
module_init(serial8250_init);
diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c
index c2f2393..c014ffb 100644
--- a/drivers/serial/8250_pci.c
+++ b/drivers/serial/8250_pci.c
@@ -2041,9 +2041,9 @@
* The device may have been disabled. Re-enable it.
*/
err = pci_enable_device(dev);
+ /* FIXME: We cannot simply error out here */
if (err)
- return err;
-
+ printk(KERN_ERR "pciserial: Unable to re-enable ports, trying to continue.\n");
pciserial_resume_ports(priv);
}
return 0;
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 77cb342..31786b3 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -9,7 +9,6 @@
# The new 8250/16550 serial drivers
config SERIAL_8250
tristate "8250/16550 and compatible serial support"
- depends on (BROKEN || !SPARC)
select SERIAL_CORE
---help---
This selects whether you want to include the driver for the standard
@@ -994,24 +993,12 @@
bool "Support RTS/CTS on 68328 serial port"
depends on SERIAL_68328
-config SERIAL_COLDFIRE
- bool "ColdFire serial support (DEPRECATED)"
- depends on COLDFIRE
- help
- This driver supports the built-in serial ports of the Motorola ColdFire
- family of CPUs.
- This driver is deprecated because it supports only the old interface
- for serial drivers and features like magic keys are not working.
- Please switch to the new style driver because this driver will be
- removed soon.
-
config SERIAL_MCF
- bool "Coldfire serial support (new style driver)"
+ bool "Coldfire serial support"
depends on COLDFIRE
select SERIAL_CORE
help
- This new serial driver supports the Freescale Coldfire serial ports
- using the new serial driver subsystem.
+ This serial driver supports the Freescale Coldfire serial ports.
config SERIAL_MCF_BAUDRATE
int "Default baudrate for Coldfire serial ports"
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 7e7383e..0c17c8d 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -4,6 +4,16 @@
obj-$(CONFIG_SERIAL_CORE) += serial_core.o
obj-$(CONFIG_SERIAL_21285) += 21285.o
+
+# These Sparc drivers have to appear before others such as 8250
+# which share ttySx minor node space. Otherwise console device
+# names change and other unplesantries.
+obj-$(CONFIG_SERIAL_SUNCORE) += suncore.o
+obj-$(CONFIG_SERIAL_SUNHV) += sunhv.o
+obj-$(CONFIG_SERIAL_SUNZILOG) += sunzilog.o
+obj-$(CONFIG_SERIAL_SUNSU) += sunsu.o
+obj-$(CONFIG_SERIAL_SUNSAB) += sunsab.o
+
obj-$(CONFIG_SERIAL_8250) += 8250.o
obj-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o
obj-$(CONFIG_SERIAL_8250_GSC) += 8250_gsc.o
@@ -31,16 +41,10 @@
obj-$(CONFIG_SERIAL_S3C2410) += s3c2410.o
obj-$(CONFIG_SERIAL_S3C2412) += s3c2412.o
obj-$(CONFIG_SERIAL_S3C2440) += s3c2440.o
-obj-$(CONFIG_SERIAL_SUNCORE) += suncore.o
-obj-$(CONFIG_SERIAL_SUNHV) += sunhv.o
-obj-$(CONFIG_SERIAL_SUNZILOG) += sunzilog.o
obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o
-obj-$(CONFIG_SERIAL_SUNSU) += sunsu.o
-obj-$(CONFIG_SERIAL_SUNSAB) += sunsab.o
obj-$(CONFIG_SERIAL_MUX) += mux.o
obj-$(CONFIG_SERIAL_68328) += 68328serial.o
obj-$(CONFIG_SERIAL_68360) += 68360serial.o
-obj-$(CONFIG_SERIAL_COLDFIRE) += mcfserial.o
obj-$(CONFIG_SERIAL_MCF) += mcf.o
obj-$(CONFIG_SERIAL_PMACZILOG) += pmac_zilog.o
obj-$(CONFIG_SERIAL_LH7A40X) += serial_lh7a40x.o
diff --git a/drivers/serial/bfin_5xx.c b/drivers/serial/bfin_5xx.c
index 4a0d30b..569f0e2 100644
--- a/drivers/serial/bfin_5xx.c
+++ b/drivers/serial/bfin_5xx.c
@@ -1,7 +1,7 @@
/*
* Blackfin On-Chip Serial Driver
*
- * Copyright 2006-2007 Analog Devices Inc.
+ * Copyright 2006-2008 Analog Devices Inc.
*
* Enter bugs at http://blackfin.uclinux.org/
*
@@ -42,6 +42,9 @@
#define BFIN_SERIAL_MAJOR 204
#define BFIN_SERIAL_MINOR 64
+static struct bfin_serial_port bfin_serial_ports[BFIN_UART_NR_PORTS];
+static int nr_active_ports = ARRAY_SIZE(bfin_serial_resource);
+
/*
* Setup for console. Argument comes from the menuconfig
*/
@@ -126,13 +129,13 @@
void kgdb_put_debug_char(int chr)
{
struct bfin_serial_port *uart;
-
+
if (CONFIG_KGDB_UART_PORT < 0
|| CONFIG_KGDB_UART_PORT >= BFIN_UART_NR_PORTS)
uart = &bfin_serial_ports[0];
else
uart = &bfin_serial_ports[CONFIG_KGDB_UART_PORT];
-
+
while (!(UART_GET_LSR(uart) & THRE)) {
SSYNC();
}
@@ -152,7 +155,7 @@
uart = &bfin_serial_ports[0];
else
uart = &bfin_serial_ports[CONFIG_KGDB_UART_PORT];
-
+
while(!(UART_GET_LSR(uart) & DR)) {
SSYNC();
}
@@ -298,7 +301,11 @@
bfin_serial_mctrl_check(uart);
if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) {
- bfin_serial_stop_tx(&uart->port);
+#ifdef CONFIG_BF54x
+ /* Clear TFI bit */
+ UART_PUT_LSR(uart, TFI);
+#endif
+ UART_CLEAR_IER(uart, ETBEI);
return;
}
@@ -317,9 +324,6 @@
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&uart->port);
-
- if (uart_circ_empty(xmit))
- bfin_serial_stop_tx(&uart->port);
}
static irqreturn_t bfin_serial_rx_int(int irq, void *dev_id)
@@ -645,6 +649,42 @@
free_irq(uart->port.irq, uart);
return -EBUSY;
}
+
+# ifdef CONFIG_BF54x
+ {
+ unsigned uart_dma_ch_rx, uart_dma_ch_tx;
+
+ switch (uart->port.irq) {
+ case IRQ_UART3_RX:
+ uart_dma_ch_rx = CH_UART3_RX;
+ uart_dma_ch_tx = CH_UART3_TX;
+ break;
+ case IRQ_UART2_RX:
+ uart_dma_ch_rx = CH_UART2_RX;
+ uart_dma_ch_tx = CH_UART2_TX;
+ break;
+ default:
+ uart_dma_ch_rx = uart_dma_ch_tx = 0;
+ break;
+ };
+
+ if (uart_dma_ch_rx &&
+ request_dma(uart_dma_ch_rx, "BFIN_UART_RX") < 0) {
+ printk(KERN_NOTICE"Fail to attach UART interrupt\n");
+ free_irq(uart->port.irq, uart);
+ free_irq(uart->port.irq + 1, uart);
+ return -EBUSY;
+ }
+ if (uart_dma_ch_tx &&
+ request_dma(uart_dma_ch_tx, "BFIN_UART_TX") < 0) {
+ printk(KERN_NOTICE "Fail to attach UART interrupt\n");
+ free_dma(uart_dma_ch_rx);
+ free_irq(uart->port.irq, uart);
+ free_irq(uart->port.irq + 1, uart);
+ return -EBUSY;
+ }
+ }
+# endif
#endif
UART_SET_IER(uart, ERBFI);
return 0;
@@ -662,6 +702,20 @@
del_timer(&(uart->rx_dma_timer));
dma_free_coherent(NULL, PAGE_SIZE, uart->rx_dma_buf.buf, 0);
#else
+#ifdef CONFIG_BF54x
+ switch (uart->port.irq) {
+ case IRQ_UART3_RX:
+ free_dma(CH_UART3_RX);
+ free_dma(CH_UART3_TX);
+ break;
+ case IRQ_UART2_RX:
+ free_dma(CH_UART2_RX);
+ free_dma(CH_UART2_TX);
+ break;
+ default:
+ break;
+ };
+#endif
#ifdef CONFIG_KGDB_UART
if (uart->port.line != CONFIG_KGDB_UART_PORT)
#endif
@@ -757,6 +811,9 @@
val |= UCEN;
UART_PUT_GCTL(uart, val);
+ /* Port speed changed, update the per-port timeout. */
+ uart_update_timeout(port, termios->c_cflag, baud);
+
spin_unlock_irqrestore(&uart->port.lock, flags);
}
@@ -859,8 +916,9 @@
return;
first = 0;
- for (i = 0; i < nr_ports; i++) {
+ for (i = 0; i < nr_active_ports; i++) {
bfin_serial_ports[i].port.uartclk = get_sclk();
+ bfin_serial_ports[i].port.fifosize = BFIN_UART_TX_FIFO_SIZE;
bfin_serial_ports[i].port.ops = &bfin_serial_pops;
bfin_serial_ports[i].port.line = i;
bfin_serial_ports[i].port.iotype = UPIO_MEM;
@@ -961,7 +1019,7 @@
* if so, search for the first available port that does have
* console support.
*/
- if (co->index == -1 || co->index >= nr_ports)
+ if (co->index == -1 || co->index >= nr_active_ports)
co->index = 0;
uart = &bfin_serial_ports[co->index];
@@ -1056,7 +1114,7 @@
}
}
-static struct __init console bfin_early_serial_console = {
+static struct __initdata console bfin_early_serial_console = {
.name = "early_BFuart",
.write = early_serial_write,
.device = uart_console_device,
@@ -1072,7 +1130,7 @@
struct bfin_serial_port *uart;
struct ktermios t;
- if (port == -1 || port >= nr_ports)
+ if (port == -1 || port >= nr_active_ports)
port = 0;
bfin_serial_init_ports();
bfin_early_serial_console.index = port;
@@ -1100,20 +1158,26 @@
static int bfin_serial_suspend(struct platform_device *dev, pm_message_t state)
{
- struct bfin_serial_port *uart = platform_get_drvdata(dev);
+ int i;
- if (uart)
- uart_suspend_port(&bfin_serial_reg, &uart->port);
+ for (i = 0; i < nr_active_ports; i++) {
+ if (bfin_serial_ports[i].port.dev != &dev->dev)
+ continue;
+ uart_suspend_port(&bfin_serial_reg, &bfin_serial_ports[i].port);
+ }
return 0;
}
static int bfin_serial_resume(struct platform_device *dev)
{
- struct bfin_serial_port *uart = platform_get_drvdata(dev);
+ int i;
- if (uart)
- uart_resume_port(&bfin_serial_reg, &uart->port);
+ for (i = 0; i < nr_active_ports; i++) {
+ if (bfin_serial_ports[i].port.dev != &dev->dev)
+ continue;
+ uart_resume_port(&bfin_serial_reg, &bfin_serial_ports[i].port);
+ }
return 0;
}
@@ -1128,32 +1192,31 @@
break;
if (i < dev->num_resources) {
- for (i = 0; i < nr_ports; i++, res++) {
+ for (i = 0; i < nr_active_ports; i++, res++) {
if (bfin_serial_ports[i].port.mapbase != res->start)
continue;
bfin_serial_ports[i].port.dev = &dev->dev;
uart_add_one_port(&bfin_serial_reg, &bfin_serial_ports[i].port);
- platform_set_drvdata(dev, &bfin_serial_ports[i]);
}
}
return 0;
}
-static int bfin_serial_remove(struct platform_device *pdev)
+static int bfin_serial_remove(struct platform_device *dev)
{
- struct bfin_serial_port *uart = platform_get_drvdata(pdev);
+ int i;
-
+ for (i = 0; i < nr_active_ports; i++) {
+ if (bfin_serial_ports[i].port.dev != &dev->dev)
+ continue;
+ uart_remove_one_port(&bfin_serial_reg, &bfin_serial_ports[i].port);
+ bfin_serial_ports[i].port.dev = NULL;
#ifdef CONFIG_SERIAL_BFIN_CTSRTS
- gpio_free(uart->cts_pin);
- gpio_free(uart->rts_pin);
+ gpio_free(bfin_serial_ports[i].cts_pin);
+ gpio_free(bfin_serial_ports[i].rts_pin);
#endif
-
- platform_set_drvdata(pdev, NULL);
-
- if (uart)
- uart_remove_one_port(&bfin_serial_reg, &uart->port);
+ }
return 0;
}
diff --git a/drivers/serial/crisv10.c b/drivers/serial/crisv10.c
index bf94a77..211c217 100644
--- a/drivers/serial/crisv10.c
+++ b/drivers/serial/crisv10.c
@@ -457,7 +457,6 @@
#define NR_PORTS (sizeof(rs_table)/sizeof(struct e100_serial))
static struct ktermios *serial_termios[NR_PORTS];
-static struct ktermios *serial_termios_locked[NR_PORTS];
#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
static struct fast_timer fast_timers[NR_PORTS];
#endif
@@ -4419,6 +4418,7 @@
rs485_pa_bit)) {
printk(KERN_CRIT "ETRAX100LX serial: Could not allocate "
"RS485 pin\n");
+ put_tty_driver(driver);
return -EBUSY;
}
#endif
@@ -4427,6 +4427,7 @@
rs485_port_g_bit)) {
printk(KERN_CRIT "ETRAX100LX serial: Could not allocate "
"RS485 pin\n");
+ put_tty_driver(driver);
return -EBUSY;
}
#endif
@@ -4446,8 +4447,6 @@
driver->init_termios.c_ispeed = 115200;
driver->init_termios.c_ospeed = 115200;
driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
- driver->termios = serial_termios;
- driver->termios_locked = serial_termios_locked;
tty_set_operations(driver, &rs_ops);
serial_driver = driver;
diff --git a/drivers/serial/mcfserial.c b/drivers/serial/mcfserial.c
deleted file mode 100644
index fbe3835..0000000
--- a/drivers/serial/mcfserial.c
+++ /dev/null
@@ -1,1965 +0,0 @@
-#warning This driver is deprecated. Check Kconfig for details.
-/*
- * mcfserial.c -- serial driver for ColdFire internal UARTS.
- *
- * Copyright (C) 1999-2003 Greg Ungerer <gerg@snapgear.com>
- * Copyright (c) 2000-2001 Lineo, Inc. <www.lineo.com>
- * Copyright (C) 2001-2002 SnapGear Inc. <www.snapgear.com>
- *
- * Based on code from 68332serial.c which was:
- *
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1998 TSHG
- * Copyright (c) 1999 Rt-Control Inc. <jeff@uclinux.org>
- *
- * Changes:
- * 08/07/2003 Daniele Bellucci <bellucda@tiscali.it>
- * some cleanups in mcfrs_write.
- *
- */
-
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/wait.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/string.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/kernel.h>
-#include <linux/serial.h>
-#include <linux/serialP.h>
-#include <linux/console.h>
-#include <linux/init.h>
-#include <linux/bitops.h>
-#include <linux/delay.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/system.h>
-#include <asm/delay.h>
-#include <asm/coldfire.h>
-#include <asm/mcfsim.h>
-#include <asm/mcfuart.h>
-#include <asm/nettel.h>
-#include <asm/uaccess.h>
-#include "mcfserial.h"
-
-struct timer_list mcfrs_timer_struct;
-
-/*
- * Default console baud rate, we use this as the default
- * for all ports so init can just open /dev/console and
- * keep going. Perhaps one day the cflag settings for the
- * console can be used instead.
- */
-#if defined(CONFIG_HW_FEITH)
-#define CONSOLE_BAUD_RATE 38400
-#define DEFAULT_CBAUD B38400
-#elif defined(CONFIG_MOD5272) || defined(CONFIG_M5208EVB) || \
- defined(CONFIG_M5329EVB) || defined(CONFIG_GILBARCO)
-#define CONSOLE_BAUD_RATE 115200
-#define DEFAULT_CBAUD B115200
-#elif defined(CONFIG_ARNEWSH) || defined(CONFIG_FREESCALE) || \
- defined(CONFIG_senTec) || defined(CONFIG_SNEHA) || defined(CONFIG_AVNET)
-#define CONSOLE_BAUD_RATE 19200
-#define DEFAULT_CBAUD B19200
-#endif
-
-#ifndef CONSOLE_BAUD_RATE
-#define CONSOLE_BAUD_RATE 9600
-#define DEFAULT_CBAUD B9600
-#endif
-
-int mcfrs_console_inited = 0;
-int mcfrs_console_port = -1;
-int mcfrs_console_baud = CONSOLE_BAUD_RATE;
-int mcfrs_console_cbaud = DEFAULT_CBAUD;
-
-/*
- * Driver data structures.
- */
-static struct tty_driver *mcfrs_serial_driver;
-
-/* number of characters left in xmit buffer before we ask for more */
-#define WAKEUP_CHARS 256
-
-/* Debugging...
- */
-#undef SERIAL_DEBUG_OPEN
-#undef SERIAL_DEBUG_FLOW
-
-#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
- defined(CONFIG_M520x) || defined(CONFIG_M532x)
-#define IRQBASE (MCFINT_VECBASE+MCFINT_UART0)
-#else
-#define IRQBASE 73
-#endif
-
-/*
- * Configuration table, UARTs to look for at startup.
- */
-static struct mcf_serial mcfrs_table[] = {
- { /* ttyS0 */
- .magic = 0,
- .addr = (volatile unsigned char *) (MCF_MBAR+MCFUART_BASE1),
- .irq = IRQBASE,
- .flags = ASYNC_BOOT_AUTOCONF,
- },
-#ifdef MCFUART_BASE2
- { /* ttyS1 */
- .magic = 0,
- .addr = (volatile unsigned char *) (MCF_MBAR+MCFUART_BASE2),
- .irq = IRQBASE+1,
- .flags = ASYNC_BOOT_AUTOCONF,
- },
-#endif
-#ifdef MCFUART_BASE3
- { /* ttyS2 */
- .magic = 0,
- .addr = (volatile unsigned char *) (MCF_MBAR+MCFUART_BASE3),
- .irq = IRQBASE+2,
- .flags = ASYNC_BOOT_AUTOCONF,
- },
-#endif
-#ifdef MCFUART_BASE4
- { /* ttyS3 */
- .magic = 0,
- .addr = (volatile unsigned char *) (MCF_MBAR+MCFUART_BASE4),
- .irq = IRQBASE+3,
- .flags = ASYNC_BOOT_AUTOCONF,
- },
-#endif
-};
-
-
-#define NR_PORTS (sizeof(mcfrs_table) / sizeof(struct mcf_serial))
-
-/*
- * This is used to figure out the divisor speeds and the timeouts.
- */
-static int mcfrs_baud_table[] = {
- 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
- 9600, 19200, 38400, 57600, 115200, 230400, 460800, 0
-};
-#define MCFRS_BAUD_TABLE_SIZE \
- (sizeof(mcfrs_baud_table)/sizeof(mcfrs_baud_table[0]))
-
-
-#ifdef CONFIG_MAGIC_SYSRQ
-/*
- * Magic system request keys. Used for debugging...
- */
-extern int magic_sysrq_key(int ch);
-#endif
-
-
-/*
- * Forware declarations...
- */
-static void mcfrs_change_speed(struct mcf_serial *info);
-static void mcfrs_wait_until_sent(struct tty_struct *tty, int timeout);
-
-
-static inline int serial_paranoia_check(struct mcf_serial *info,
- char *name, const char *routine)
-{
-#ifdef SERIAL_PARANOIA_CHECK
- static const char badmagic[] =
- "MCFRS(warning): bad magic number for serial struct %s in %s\n";
- static const char badinfo[] =
- "MCFRS(warning): null mcf_serial for %s in %s\n";
-
- if (!info) {
- printk(badinfo, name, routine);
- return 1;
- }
- if (info->magic != SERIAL_MAGIC) {
- printk(badmagic, name, routine);
- return 1;
- }
-#endif
- return 0;
-}
-
-/*
- * Sets or clears DTR and RTS on the requested line.
- */
-static void mcfrs_setsignals(struct mcf_serial *info, int dtr, int rts)
-{
- volatile unsigned char *uartp;
- unsigned long flags;
-
-#if 0
- printk("%s(%d): mcfrs_setsignals(info=%x,dtr=%d,rts=%d)\n",
- __FILE__, __LINE__, info, dtr, rts);
-#endif
-
- local_irq_save(flags);
- if (dtr >= 0) {
-#ifdef MCFPP_DTR0
- if (info->line)
- mcf_setppdata(MCFPP_DTR1, (dtr ? 0 : MCFPP_DTR1));
- else
- mcf_setppdata(MCFPP_DTR0, (dtr ? 0 : MCFPP_DTR0));
-#endif
- }
- if (rts >= 0) {
- uartp = info->addr;
- if (rts) {
- info->sigs |= TIOCM_RTS;
- uartp[MCFUART_UOP1] = MCFUART_UOP_RTS;
- } else {
- info->sigs &= ~TIOCM_RTS;
- uartp[MCFUART_UOP0] = MCFUART_UOP_RTS;
- }
- }
- local_irq_restore(flags);
- return;
-}
-
-/*
- * Gets values of serial signals.
- */
-static int mcfrs_getsignals(struct mcf_serial *info)
-{
- volatile unsigned char *uartp;
- unsigned long flags;
- int sigs;
-#if defined(CONFIG_NETtel) && defined(CONFIG_M5307)
- unsigned short ppdata;
-#endif
-
-#if 0
- printk("%s(%d): mcfrs_getsignals(info=%x)\n", __FILE__, __LINE__);
-#endif
-
- local_irq_save(flags);
- uartp = info->addr;
- sigs = (uartp[MCFUART_UIPR] & MCFUART_UIPR_CTS) ? 0 : TIOCM_CTS;
- sigs |= (info->sigs & TIOCM_RTS);
-
-#ifdef MCFPP_DCD0
-{
- unsigned int ppdata;
- ppdata = mcf_getppdata();
- if (info->line == 0) {
- sigs |= (ppdata & MCFPP_DCD0) ? 0 : TIOCM_CD;
- sigs |= (ppdata & MCFPP_DTR0) ? 0 : TIOCM_DTR;
- } else if (info->line == 1) {
- sigs |= (ppdata & MCFPP_DCD1) ? 0 : TIOCM_CD;
- sigs |= (ppdata & MCFPP_DTR1) ? 0 : TIOCM_DTR;
- }
-}
-#endif
-
- local_irq_restore(flags);
- return(sigs);
-}
-
-/*
- * ------------------------------------------------------------
- * mcfrs_stop() and mcfrs_start()
- *
- * This routines are called before setting or resetting tty->stopped.
- * They enable or disable transmitter interrupts, as necessary.
- * ------------------------------------------------------------
- */
-static void mcfrs_stop(struct tty_struct *tty)
-{
- volatile unsigned char *uartp;
- struct mcf_serial *info = (struct mcf_serial *)tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->name, "mcfrs_stop"))
- return;
-
- local_irq_save(flags);
- uartp = info->addr;
- info->imr &= ~MCFUART_UIR_TXREADY;
- uartp[MCFUART_UIMR] = info->imr;
- local_irq_restore(flags);
-}
-
-static void mcfrs_start(struct tty_struct *tty)
-{
- volatile unsigned char *uartp;
- struct mcf_serial *info = (struct mcf_serial *)tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->name, "mcfrs_start"))
- return;
-
- local_irq_save(flags);
- if (info->xmit_cnt && info->xmit_buf) {
- uartp = info->addr;
- info->imr |= MCFUART_UIR_TXREADY;
- uartp[MCFUART_UIMR] = info->imr;
- }
- local_irq_restore(flags);
-}
-
-/*
- * ----------------------------------------------------------------------
- *
- * Here starts the interrupt handling routines. All of the following
- * subroutines are declared as inline and are folded into
- * mcfrs_interrupt(). They were separated out for readability's sake.
- *
- * Note: mcfrs_interrupt() is a "fast" interrupt, which means that it
- * runs with interrupts turned off. People who may want to modify
- * mcfrs_interrupt() should try to keep the interrupt handler as fast as
- * possible. After you are done making modifications, it is not a bad
- * idea to do:
- *
- * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
- *
- * and look at the resulting assemble code in serial.s.
- *
- * - Ted Ts'o (tytso@mit.edu), 7-Mar-93
- * -----------------------------------------------------------------------
- */
-
-static inline void receive_chars(struct mcf_serial *info)
-{
- volatile unsigned char *uartp;
- struct tty_struct *tty = info->port.tty;
- unsigned char status, ch, flag;
-
- if (!tty)
- return;
-
- uartp = info->addr;
-
- while ((status = uartp[MCFUART_USR]) & MCFUART_USR_RXREADY) {
- ch = uartp[MCFUART_URB];
- info->stats.rx++;
-
-#ifdef CONFIG_MAGIC_SYSRQ
- if (mcfrs_console_inited && (info->line == mcfrs_console_port)) {
- if (magic_sysrq_key(ch))
- continue;
- }
-#endif
-
- flag = TTY_NORMAL;
- if (status & MCFUART_USR_RXERR) {
- uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETERR;
- if (status & MCFUART_USR_RXBREAK) {
- info->stats.rxbreak++;
- flag = TTY_BREAK;
- } else if (status & MCFUART_USR_RXPARITY) {
- info->stats.rxparity++;
- flag = TTY_PARITY;
- } else if (status & MCFUART_USR_RXOVERRUN) {
- info->stats.rxoverrun++;
- flag = TTY_OVERRUN;
- } else if (status & MCFUART_USR_RXFRAMING) {
- info->stats.rxframing++;
- flag = TTY_FRAME;
- }
- }
- tty_insert_flip_char(tty, ch, flag);
- }
- tty_schedule_flip(tty);
- return;
-}
-
-static inline void transmit_chars(struct mcf_serial *info)
-{
- volatile unsigned char *uartp;
-
- uartp = info->addr;
-
- if (info->x_char) {
- /* Send special char - probably flow control */
- uartp[MCFUART_UTB] = info->x_char;
- info->x_char = 0;
- info->stats.tx++;
- }
-
- if ((info->xmit_cnt <= 0) || info->port.tty->stopped) {
- info->imr &= ~MCFUART_UIR_TXREADY;
- uartp[MCFUART_UIMR] = info->imr;
- return;
- }
-
- while (uartp[MCFUART_USR] & MCFUART_USR_TXREADY) {
- uartp[MCFUART_UTB] = info->xmit_buf[info->xmit_tail++];
- info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
- info->stats.tx++;
- if (--info->xmit_cnt <= 0)
- break;
- }
-
- if (info->xmit_cnt < WAKEUP_CHARS)
- schedule_work(&info->tqueue);
- return;
-}
-
-/*
- * This is the serial driver's generic interrupt routine
- */
-irqreturn_t mcfrs_interrupt(int irq, void *dev_id)
-{
- struct mcf_serial *info;
- unsigned char isr;
-
- info = &mcfrs_table[(irq - IRQBASE)];
- isr = info->addr[MCFUART_UISR] & info->imr;
-
- if (isr & MCFUART_UIR_RXREADY)
- receive_chars(info);
- if (isr & MCFUART_UIR_TXREADY)
- transmit_chars(info);
- return IRQ_HANDLED;
-}
-
-/*
- * -------------------------------------------------------------------
- * Here ends the serial interrupt routines.
- * -------------------------------------------------------------------
- */
-
-static void mcfrs_offintr(struct work_struct *work)
-{
- struct mcf_serial *info = container_of(work, struct mcf_serial, tqueue);
- struct tty_struct *tty = info->port.tty;
-
- if (tty)
- tty_wakeup(tty);
-}
-
-
-/*
- * Change of state on a DCD line.
- */
-void mcfrs_modem_change(struct mcf_serial *info, int dcd)
-{
- if (info->count == 0)
- return;
-
- if (info->flags & ASYNC_CHECK_CD) {
- if (dcd)
- wake_up_interruptible(&info->open_wait);
- else
- schedule_work(&info->tqueue_hangup);
- }
-}
-
-
-#ifdef MCFPP_DCD0
-
-unsigned short mcfrs_ppstatus;
-
-/*
- * This subroutine is called when the RS_TIMER goes off. It is used
- * to monitor the state of the DCD lines - since they have no edge
- * sensors and interrupt generators.
- */
-static void mcfrs_timer(void)
-{
- unsigned int ppstatus, dcdval, i;
-
- ppstatus = mcf_getppdata() & (MCFPP_DCD0 | MCFPP_DCD1);
-
- if (ppstatus != mcfrs_ppstatus) {
- for (i = 0; (i < 2); i++) {
- dcdval = (i ? MCFPP_DCD1 : MCFPP_DCD0);
- if ((ppstatus & dcdval) != (mcfrs_ppstatus & dcdval)) {
- mcfrs_modem_change(&mcfrs_table[i],
- ((ppstatus & dcdval) ? 0 : 1));
- }
- }
- }
- mcfrs_ppstatus = ppstatus;
-
- /* Re-arm timer */
- mcfrs_timer_struct.expires = jiffies + HZ/25;
- add_timer(&mcfrs_timer_struct);
-}
-
-#endif /* MCFPP_DCD0 */
-
-
-/*
- * This routine is called from the scheduler tqueue when the interrupt
- * routine has signalled that a hangup has occurred. The path of
- * hangup processing is:
- *
- * serial interrupt routine -> (scheduler tqueue) ->
- * do_serial_hangup() -> tty->hangup() -> mcfrs_hangup()
- *
- */
-static void do_serial_hangup(struct work_struct *work)
-{
- struct mcf_serial *info = container_of(work, struct mcf_serial, tqueue_hangup);
- struct tty_struct *tty = info->port.tty;
-
- if (tty)
- tty_hangup(tty);
-}
-
-static int startup(struct mcf_serial * info)
-{
- volatile unsigned char *uartp;
- unsigned long flags;
-
- if (info->flags & ASYNC_INITIALIZED)
- return 0;
-
- if (!info->xmit_buf) {
- info->xmit_buf = (unsigned char *) __get_free_page(GFP_KERNEL);
- if (!info->xmit_buf)
- return -ENOMEM;
- }
-
- local_irq_save(flags);
-
-#ifdef SERIAL_DEBUG_OPEN
- printk("starting up ttyS%d (irq %d)...\n", info->line, info->irq);
-#endif
-
- /*
- * Reset UART, get it into known state...
- */
- uartp = info->addr;
- uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETRX; /* reset RX */
- uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETTX; /* reset TX */
- mcfrs_setsignals(info, 1, 1);
-
- if (info->port.tty)
- clear_bit(TTY_IO_ERROR, &info->port.tty->flags);
- info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
-
- /*
- * and set the speed of the serial port
- */
- mcfrs_change_speed(info);
-
- /*
- * Lastly enable the UART transmitter and receiver, and
- * interrupt enables.
- */
- info->imr = MCFUART_UIR_RXREADY;
- uartp[MCFUART_UCR] = MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE;
- uartp[MCFUART_UIMR] = info->imr;
-
- info->flags |= ASYNC_INITIALIZED;
- local_irq_restore(flags);
- return 0;
-}
-
-/*
- * This routine will shutdown a serial port; interrupts are disabled, and
- * DTR is dropped if the hangup on close termio flag is on.
- */
-static void shutdown(struct mcf_serial * info)
-{
- volatile unsigned char *uartp;
- unsigned long flags;
-
- if (!(info->flags & ASYNC_INITIALIZED))
- return;
-
-#ifdef SERIAL_DEBUG_OPEN
- printk("Shutting down serial port %d (irq %d)....\n", info->line,
- info->irq);
-#endif
-
- local_irq_save(flags);
-
- uartp = info->addr;
- uartp[MCFUART_UIMR] = 0; /* mask all interrupts */
- uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETRX; /* reset RX */
- uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETTX; /* reset TX */
-
- if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL))
- mcfrs_setsignals(info, 0, 0);
-
- if (info->xmit_buf) {
- free_page((unsigned long) info->xmit_buf);
- info->xmit_buf = 0;
- }
-
- if (info->port.tty)
- set_bit(TTY_IO_ERROR, &info->port.tty->flags);
-
- info->flags &= ~ASYNC_INITIALIZED;
- local_irq_restore(flags);
-}
-
-
-/*
- * This routine is called to set the UART divisor registers to match
- * the specified baud rate for a serial port.
- */
-static void mcfrs_change_speed(struct mcf_serial *info)
-{
- volatile unsigned char *uartp;
- unsigned int baudclk, cflag;
- unsigned long flags;
- unsigned char mr1, mr2;
- int i;
-#ifdef CONFIG_M5272
- unsigned int fraction;
-#endif
-
- if (!info->port.tty || !info->port.tty->termios)
- return;
- cflag = info->port.tty->termios->c_cflag;
- if (info->addr == 0)
- return;
-
-#if 0
- printk("%s(%d): mcfrs_change_speed()\n", __FILE__, __LINE__);
-#endif
-
- i = cflag & CBAUD;
- if (i & CBAUDEX) {
- i &= ~CBAUDEX;
- if (i < 1 || i > 4)
- info->port.tty->termios->c_cflag &= ~CBAUDEX;
- else
- i += 15;
- }
- if (i == 0) {
- mcfrs_setsignals(info, 0, -1);
- return;
- }
-
- /* compute the baudrate clock */
-#ifdef CONFIG_M5272
- /*
- * For the MCF5272, also compute the baudrate fraction.
- */
- baudclk = (MCF_BUSCLK / mcfrs_baud_table[i]) / 32;
- fraction = MCF_BUSCLK - (baudclk * 32 * mcfrs_baud_table[i]);
- fraction *= 16;
- fraction /= (32 * mcfrs_baud_table[i]);
-#else
- baudclk = ((MCF_BUSCLK / mcfrs_baud_table[i]) + 16) / 32;
-#endif
-
- info->baud = mcfrs_baud_table[i];
-
- mr1 = MCFUART_MR1_RXIRQRDY | MCFUART_MR1_RXERRCHAR;
- mr2 = 0;
-
- switch (cflag & CSIZE) {
- case CS5: mr1 |= MCFUART_MR1_CS5; break;
- case CS6: mr1 |= MCFUART_MR1_CS6; break;
- case CS7: mr1 |= MCFUART_MR1_CS7; break;
- case CS8:
- default: mr1 |= MCFUART_MR1_CS8; break;
- }
-
- if (cflag & PARENB) {
- if (cflag & CMSPAR) {
- if (cflag & PARODD)
- mr1 |= MCFUART_MR1_PARITYMARK;
- else
- mr1 |= MCFUART_MR1_PARITYSPACE;
- } else {
- if (cflag & PARODD)
- mr1 |= MCFUART_MR1_PARITYODD;
- else
- mr1 |= MCFUART_MR1_PARITYEVEN;
- }
- } else {
- mr1 |= MCFUART_MR1_PARITYNONE;
- }
-
- if (cflag & CSTOPB)
- mr2 |= MCFUART_MR2_STOP2;
- else
- mr2 |= MCFUART_MR2_STOP1;
-
- if (cflag & CRTSCTS) {
- mr1 |= MCFUART_MR1_RXRTS;
- mr2 |= MCFUART_MR2_TXCTS;
- }
-
- if (cflag & CLOCAL)
- info->flags &= ~ASYNC_CHECK_CD;
- else
- info->flags |= ASYNC_CHECK_CD;
-
- uartp = info->addr;
-
- local_irq_save(flags);
-#if 0
- printk("%s(%d): mr1=%x mr2=%x baudclk=%x\n", __FILE__, __LINE__,
- mr1, mr2, baudclk);
-#endif
- /*
- Note: pg 12-16 of MCF5206e User's Manual states that a
- software reset should be performed prior to changing
- UMR1,2, UCSR, UACR, bit 7
- */
- uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETRX; /* reset RX */
- uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETTX; /* reset TX */
- uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETMRPTR; /* reset MR pointer */
- uartp[MCFUART_UMR] = mr1;
- uartp[MCFUART_UMR] = mr2;
- uartp[MCFUART_UBG1] = (baudclk & 0xff00) >> 8; /* set msb byte */
- uartp[MCFUART_UBG2] = (baudclk & 0xff); /* set lsb byte */
-#ifdef CONFIG_M5272
- uartp[MCFUART_UFPD] = (fraction & 0xf); /* set fraction */
-#endif
- uartp[MCFUART_UCSR] = MCFUART_UCSR_RXCLKTIMER | MCFUART_UCSR_TXCLKTIMER;
- uartp[MCFUART_UCR] = MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE;
- mcfrs_setsignals(info, 1, -1);
- local_irq_restore(flags);
- return;
-}
-
-static void mcfrs_flush_chars(struct tty_struct *tty)
-{
- volatile unsigned char *uartp;
- struct mcf_serial *info = (struct mcf_serial *)tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->name, "mcfrs_flush_chars"))
- return;
-
- uartp = (volatile unsigned char *) info->addr;
-
- /*
- * re-enable receiver interrupt
- */
- local_irq_save(flags);
- if ((!(info->imr & MCFUART_UIR_RXREADY)) &&
- (info->flags & ASYNC_INITIALIZED) ) {
- info->imr |= MCFUART_UIR_RXREADY;
- uartp[MCFUART_UIMR] = info->imr;
- }
- local_irq_restore(flags);
-
- if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
- !info->xmit_buf)
- return;
-
- /* Enable transmitter */
- local_irq_save(flags);
- info->imr |= MCFUART_UIR_TXREADY;
- uartp[MCFUART_UIMR] = info->imr;
- local_irq_restore(flags);
-}
-
-static int mcfrs_write(struct tty_struct * tty,
- const unsigned char *buf, int count)
-{
- volatile unsigned char *uartp;
- struct mcf_serial *info = (struct mcf_serial *)tty->driver_data;
- unsigned long flags;
- int c, total = 0;
-
-#if 0
- printk("%s(%d): mcfrs_write(tty=%x,buf=%x,count=%d)\n",
- __FILE__, __LINE__, (int)tty, (int)buf, count);
-#endif
-
- if (serial_paranoia_check(info, tty->name, "mcfrs_write"))
- return 0;
-
- if (!tty || !info->xmit_buf)
- return 0;
-
- local_save_flags(flags);
- while (1) {
- local_irq_disable();
- c = min(count, (int) min(((int)SERIAL_XMIT_SIZE) - info->xmit_cnt - 1,
- ((int)SERIAL_XMIT_SIZE) - info->xmit_head));
- local_irq_restore(flags);
-
- if (c <= 0)
- break;
-
- memcpy(info->xmit_buf + info->xmit_head, buf, c);
-
- local_irq_disable();
- info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
- info->xmit_cnt += c;
- local_irq_restore(flags);
-
- buf += c;
- count -= c;
- total += c;
- }
-
- local_irq_disable();
- uartp = info->addr;
- info->imr |= MCFUART_UIR_TXREADY;
- uartp[MCFUART_UIMR] = info->imr;
- local_irq_restore(flags);
-
- return total;
-}
-
-static int mcfrs_write_room(struct tty_struct *tty)
-{
- struct mcf_serial *info = (struct mcf_serial *)tty->driver_data;
- int ret;
-
- if (serial_paranoia_check(info, tty->name, "mcfrs_write_room"))
- return 0;
- ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
- if (ret < 0)
- ret = 0;
- return ret;
-}
-
-static int mcfrs_chars_in_buffer(struct tty_struct *tty)
-{
- struct mcf_serial *info = (struct mcf_serial *)tty->driver_data;
-
- if (serial_paranoia_check(info, tty->name, "mcfrs_chars_in_buffer"))
- return 0;
- return info->xmit_cnt;
-}
-
-static void mcfrs_flush_buffer(struct tty_struct *tty)
-{
- struct mcf_serial *info = (struct mcf_serial *)tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->name, "mcfrs_flush_buffer"))
- return;
-
- local_irq_save(flags);
- info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
- local_irq_restore(flags);
-
- tty_wakeup(tty);
-}
-
-/*
- * ------------------------------------------------------------
- * mcfrs_throttle()
- *
- * This routine is called by the upper-layer tty layer to signal that
- * incoming characters should be throttled.
- * ------------------------------------------------------------
- */
-static void mcfrs_throttle(struct tty_struct * tty)
-{
- struct mcf_serial *info = (struct mcf_serial *)tty->driver_data;
-#ifdef SERIAL_DEBUG_THROTTLE
- char buf[64];
-
- printk("throttle %s: %d....\n", tty_name(tty, buf),
- tty->ldisc.chars_in_buffer(tty));
-#endif
-
- if (serial_paranoia_check(info, tty->name, "mcfrs_throttle"))
- return;
-
- if (I_IXOFF(tty))
- info->x_char = STOP_CHAR(tty);
-
- /* Turn off RTS line (do this atomic) */
-}
-
-static void mcfrs_unthrottle(struct tty_struct * tty)
-{
- struct mcf_serial *info = (struct mcf_serial *)tty->driver_data;
-#ifdef SERIAL_DEBUG_THROTTLE
- char buf[64];
-
- printk("unthrottle %s: %d....\n", tty_name(tty, buf),
- tty->ldisc.chars_in_buffer(tty));
-#endif
-
- if (serial_paranoia_check(info, tty->name, "mcfrs_unthrottle"))
- return;
-
- if (I_IXOFF(tty)) {
- if (info->x_char)
- info->x_char = 0;
- else
- info->x_char = START_CHAR(tty);
- }
-
- /* Assert RTS line (do this atomic) */
-}
-
-/*
- * ------------------------------------------------------------
- * mcfrs_ioctl() and friends
- * ------------------------------------------------------------
- */
-
-static int get_serial_info(struct mcf_serial * info,
- struct serial_struct * retinfo)
-{
- struct serial_struct tmp;
-
- if (!retinfo)
- return -EFAULT;
- memset(&tmp, 0, sizeof(tmp));
- tmp.type = info->type;
- tmp.line = info->line;
- tmp.port = (unsigned int) info->addr;
- tmp.irq = info->irq;
- tmp.flags = info->flags;
- tmp.baud_base = info->baud_base;
- tmp.close_delay = info->close_delay;
- tmp.closing_wait = info->closing_wait;
- tmp.custom_divisor = info->custom_divisor;
- return copy_to_user(retinfo,&tmp,sizeof(*retinfo)) ? -EFAULT : 0;
-}
-
-static int set_serial_info(struct mcf_serial * info,
- struct serial_struct * new_info)
-{
- struct serial_struct new_serial;
- struct mcf_serial old_info;
- int retval = 0;
-
- if (!new_info)
- return -EFAULT;
- if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
- return -EFAULT;
- old_info = *info;
-
- if (!capable(CAP_SYS_ADMIN)) {
- if ((new_serial.baud_base != info->baud_base) ||
- (new_serial.type != info->type) ||
- (new_serial.close_delay != info->close_delay) ||
- ((new_serial.flags & ~ASYNC_USR_MASK) !=
- (info->flags & ~ASYNC_USR_MASK)))
- return -EPERM;
- info->flags = ((info->flags & ~ASYNC_USR_MASK) |
- (new_serial.flags & ASYNC_USR_MASK));
- info->custom_divisor = new_serial.custom_divisor;
- goto check_and_exit;
- }
-
- if (info->count > 1)
- return -EBUSY;
-
- /*
- * OK, past this point, all the error checking has been done.
- * At this point, we start making changes.....
- */
-
- info->baud_base = new_serial.baud_base;
- info->flags = ((info->flags & ~ASYNC_FLAGS) |
- (new_serial.flags & ASYNC_FLAGS));
- info->type = new_serial.type;
- info->close_delay = new_serial.close_delay;
- info->closing_wait = new_serial.closing_wait;
-
-check_and_exit:
- retval = startup(info);
- return retval;
-}
-
-/*
- * get_lsr_info - get line status register info
- *
- * Purpose: Let user call ioctl() to get info when the UART physically
- * is emptied. On bus types like RS485, the transmitter must
- * release the bus after transmitting. This must be done when
- * the transmit shift register is empty, not be done when the
- * transmit holding register is empty. This functionality
- * allows an RS485 driver to be written in user space.
- */
-static int get_lsr_info(struct mcf_serial * info, unsigned int *value)
-{
- volatile unsigned char *uartp;
- unsigned long flags;
- unsigned char status;
-
- local_irq_save(flags);
- uartp = info->addr;
- status = (uartp[MCFUART_USR] & MCFUART_USR_TXEMPTY) ? TIOCSER_TEMT : 0;
- local_irq_restore(flags);
-
- return put_user(status,value);
-}
-
-/*
- * This routine sends a break character out the serial port.
- */
-static void send_break( struct mcf_serial * info, int duration)
-{
- volatile unsigned char *uartp;
- unsigned long flags;
-
- if (!info->addr)
- return;
- set_current_state(TASK_INTERRUPTIBLE);
- uartp = info->addr;
-
- local_irq_save(flags);
- uartp[MCFUART_UCR] = MCFUART_UCR_CMDBREAKSTART;
- schedule_timeout(duration);
- uartp[MCFUART_UCR] = MCFUART_UCR_CMDBREAKSTOP;
- local_irq_restore(flags);
-}
-
-static int mcfrs_tiocmget(struct tty_struct *tty, struct file *file)
-{
- struct mcf_serial * info = (struct mcf_serial *)tty->driver_data;
-
- if (serial_paranoia_check(info, tty->name, "mcfrs_ioctl"))
- return -ENODEV;
- if (tty->flags & (1 << TTY_IO_ERROR))
- return -EIO;
-
- return mcfrs_getsignals(info);
-}
-
-static int mcfrs_tiocmset(struct tty_struct *tty, struct file *file,
- unsigned int set, unsigned int clear)
-{
- struct mcf_serial * info = (struct mcf_serial *)tty->driver_data;
- int rts = -1, dtr = -1;
-
- if (serial_paranoia_check(info, tty->name, "mcfrs_ioctl"))
- return -ENODEV;
- if (tty->flags & (1 << TTY_IO_ERROR))
- return -EIO;
-
- if (set & TIOCM_RTS)
- rts = 1;
- if (set & TIOCM_DTR)
- dtr = 1;
- if (clear & TIOCM_RTS)
- rts = 0;
- if (clear & TIOCM_DTR)
- dtr = 0;
-
- mcfrs_setsignals(info, dtr, rts);
-
- return 0;
-}
-
-static int mcfrs_ioctl(struct tty_struct *tty, struct file * file,
- unsigned int cmd, unsigned long arg)
-{
- struct mcf_serial * info = (struct mcf_serial *)tty->driver_data;
- int retval, error;
-
- if (serial_paranoia_check(info, tty->name, "mcfrs_ioctl"))
- return -ENODEV;
-
- if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
- (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) &&
- (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) {
- if (tty->flags & (1 << TTY_IO_ERROR))
- return -EIO;
- }
-
- switch (cmd) {
- case TCSBRK: /* SVID version: non-zero arg --> no break */
- retval = tty_check_change(tty);
- if (retval)
- return retval;
- tty_wait_until_sent(tty, 0);
- if (!arg)
- send_break(info, HZ/4); /* 1/4 second */
- return 0;
- case TCSBRKP: /* support for POSIX tcsendbreak() */
- retval = tty_check_change(tty);
- if (retval)
- return retval;
- tty_wait_until_sent(tty, 0);
- send_break(info, arg ? arg*(HZ/10) : HZ/4);
- return 0;
- case TIOCGSERIAL:
- if (access_ok(VERIFY_WRITE, (void *) arg,
- sizeof(struct serial_struct)))
- return get_serial_info(info,
- (struct serial_struct *) arg);
- return -EFAULT;
- case TIOCSSERIAL:
- return set_serial_info(info,
- (struct serial_struct *) arg);
- case TIOCSERGETLSR: /* Get line status register */
- if (access_ok(VERIFY_WRITE, (void *) arg,
- sizeof(unsigned int)))
- return get_lsr_info(info, (unsigned int *) arg);
- return -EFAULT;
- case TIOCSERGSTRUCT:
- error = copy_to_user((struct mcf_serial *) arg,
- info, sizeof(struct mcf_serial));
- if (error)
- return -EFAULT;
- return 0;
-
-#ifdef TIOCSET422
- case TIOCSET422: {
- unsigned int val;
- get_user(val, (unsigned int *) arg);
- mcf_setpa(MCFPP_PA11, (val ? 0 : MCFPP_PA11));
- break;
- }
- case TIOCGET422: {
- unsigned int val;
- val = (mcf_getpa() & MCFPP_PA11) ? 0 : 1;
- put_user(val, (unsigned int *) arg);
- break;
- }
-#endif
-
- default:
- return -ENOIOCTLCMD;
- }
- return 0;
-}
-
-static void mcfrs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
-{
- struct mcf_serial *info = (struct mcf_serial *)tty->driver_data;
-
- if (tty->termios->c_cflag == old_termios->c_cflag)
- return;
-
- mcfrs_change_speed(info);
-
- if ((old_termios->c_cflag & CRTSCTS) &&
- !(tty->termios->c_cflag & CRTSCTS)) {
- tty->hw_stopped = 0;
- mcfrs_setsignals(info, -1, 1);
-#if 0
- mcfrs_start(tty);
-#endif
- }
-}
-
-/*
- * ------------------------------------------------------------
- * mcfrs_close()
- *
- * This routine is called when the serial port gets closed. First, we
- * wait for the last remaining data to be sent. Then, we unlink its
- * S structure from the interrupt chain if necessary, and we free
- * that IRQ if nothing is left in the chain.
- * ------------------------------------------------------------
- */
-static void mcfrs_close(struct tty_struct *tty, struct file * filp)
-{
- volatile unsigned char *uartp;
- struct mcf_serial *info = (struct mcf_serial *)tty->driver_data;
- unsigned long flags;
-
- if (!info || serial_paranoia_check(info, tty->name, "mcfrs_close"))
- return;
-
- local_irq_save(flags);
-
- if (tty_hung_up_p(filp)) {
- local_irq_restore(flags);
- return;
- }
-
-#ifdef SERIAL_DEBUG_OPEN
- printk("mcfrs_close ttyS%d, count = %d\n", info->line, info->count);
-#endif
- if ((tty->count == 1) && (info->count != 1)) {
- /*
- * Uh, oh. tty->count is 1, which means that the tty
- * structure will be freed. Info->count should always
- * be one in these conditions. If it's greater than
- * one, we've got real problems, since it means the
- * serial port won't be shutdown.
- */
- printk("MCFRS: bad serial port count; tty->count is 1, "
- "info->count is %d\n", info->count);
- info->count = 1;
- }
- if (--info->count < 0) {
- printk("MCFRS: bad serial port count for ttyS%d: %d\n",
- info->line, info->count);
- info->count = 0;
- }
- if (info->count) {
- local_irq_restore(flags);
- return;
- }
- info->flags |= ASYNC_CLOSING;
-
- /*
- * Now we wait for the transmit buffer to clear; and we notify
- * the line discipline to only process XON/XOFF characters.
- */
- tty->closing = 1;
- if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
- tty_wait_until_sent(tty, info->closing_wait);
-
- /*
- * At this point we stop accepting input. To do this, we
- * disable the receive line status interrupts, and tell the
- * interrupt driver to stop checking the data ready bit in the
- * line status register.
- */
- info->imr &= ~MCFUART_UIR_RXREADY;
- uartp = info->addr;
- uartp[MCFUART_UIMR] = info->imr;
-
-#if 0
- /* FIXME: do we need to keep this enabled for console?? */
- if (mcfrs_console_inited && (mcfrs_console_port == info->line)) {
- /* Do not disable the UART */ ;
- } else
-#endif
- shutdown(info);
- mcfrs_flush_buffer(tty);
- tty_ldisc_flush(tty);
-
- tty->closing = 0;
- info->event = 0;
- info->port.tty = NULL;
-#if 0
- if (tty->ldisc.num != ldiscs[N_TTY].num) {
- if (tty->ldisc.close)
- (tty->ldisc.close)(tty);
- tty->ldisc = ldiscs[N_TTY];
- tty->termios->c_line = N_TTY;
- if (tty->ldisc.open)
- (tty->ldisc.open)(tty);
- }
-#endif
- if (info->blocked_open) {
- if (info->close_delay) {
- msleep_interruptible(jiffies_to_msecs(info->close_delay));
- }
- wake_up_interruptible(&info->open_wait);
- }
- info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
- wake_up_interruptible(&info->close_wait);
- local_irq_restore(flags);
-}
-
-/*
- * mcfrs_wait_until_sent() --- wait until the transmitter is empty
- */
-static void
-mcfrs_wait_until_sent(struct tty_struct *tty, int timeout)
-{
-#ifdef CONFIG_M5272
-#define MCF5272_FIFO_SIZE 25 /* fifo size + shift reg */
-
- struct mcf_serial * info = (struct mcf_serial *)tty->driver_data;
- volatile unsigned char *uartp;
- unsigned long orig_jiffies, fifo_time, char_time, fifo_cnt;
-
- if (serial_paranoia_check(info, tty->name, "mcfrs_wait_until_sent"))
- return;
-
- orig_jiffies = jiffies;
-
- /*
- * Set the check interval to be 1/5 of the approximate time
- * to send the entire fifo, and make it at least 1. The check
- * interval should also be less than the timeout.
- *
- * Note: we have to use pretty tight timings here to satisfy
- * the NIST-PCTS.
- */
- lock_kernel();
-
- fifo_time = (MCF5272_FIFO_SIZE * HZ * 10) / info->baud;
- char_time = fifo_time / 5;
- if (char_time == 0)
- char_time = 1;
- if (timeout && timeout < char_time)
- char_time = timeout;
-
- /*
- * Clamp the timeout period at 2 * the time to empty the
- * fifo. Just to be safe, set the minimum at .5 seconds.
- */
- fifo_time *= 2;
- if (fifo_time < (HZ/2))
- fifo_time = HZ/2;
- if (!timeout || timeout > fifo_time)
- timeout = fifo_time;
-
- /*
- * Account for the number of bytes in the UART
- * transmitter FIFO plus any byte being shifted out.
- */
- uartp = (volatile unsigned char *) info->addr;
- for (;;) {
- fifo_cnt = (uartp[MCFUART_UTF] & MCFUART_UTF_TXB);
- if ((uartp[MCFUART_USR] & (MCFUART_USR_TXREADY|
- MCFUART_USR_TXEMPTY)) ==
- MCFUART_USR_TXREADY)
- fifo_cnt++;
- if (fifo_cnt == 0)
- break;
- msleep_interruptible(jiffies_to_msecs(char_time));
- if (signal_pending(current))
- break;
- if (timeout && time_after(jiffies, orig_jiffies + timeout))
- break;
- }
- unlock_kernel();
-#else
- /*
- * For the other coldfire models, assume all data has been sent
- */
-#endif
-}
-
-/*
- * mcfrs_hangup() --- called by tty_hangup() when a hangup is signaled.
- */
-void mcfrs_hangup(struct tty_struct *tty)
-{
- struct mcf_serial * info = (struct mcf_serial *)tty->driver_data;
-
- if (serial_paranoia_check(info, tty->name, "mcfrs_hangup"))
- return;
-
- mcfrs_flush_buffer(tty);
- shutdown(info);
- info->event = 0;
- info->count = 0;
- info->flags &= ~ASYNC_NORMAL_ACTIVE;
- info->port.tty = NULL;
- wake_up_interruptible(&info->open_wait);
-}
-
-/*
- * ------------------------------------------------------------
- * mcfrs_open() and friends
- * ------------------------------------------------------------
- */
-static int block_til_ready(struct tty_struct *tty, struct file * filp,
- struct mcf_serial *info)
-{
- DECLARE_WAITQUEUE(wait, current);
- int retval;
- int do_clocal = 0;
-
- /*
- * If the device is in the middle of being closed, then block
- * until it's done, and then try again.
- */
- if (info->flags & ASYNC_CLOSING) {
- interruptible_sleep_on(&info->close_wait);
-#ifdef SERIAL_DO_RESTART
- if (info->flags & ASYNC_HUP_NOTIFY)
- return -EAGAIN;
- else
- return -ERESTARTSYS;
-#else
- return -EAGAIN;
-#endif
- }
-
- /*
- * If non-blocking mode is set, or the port is not enabled,
- * then make the check up front and then exit.
- */
- if ((filp->f_flags & O_NONBLOCK) ||
- (tty->flags & (1 << TTY_IO_ERROR))) {
- info->flags |= ASYNC_NORMAL_ACTIVE;
- return 0;
- }
-
- if (tty->termios->c_cflag & CLOCAL)
- do_clocal = 1;
-
- /*
- * Block waiting for the carrier detect and the line to become
- * free (i.e., not in use by the callout). While we are in
- * this loop, info->count is dropped by one, so that
- * mcfrs_close() knows when to free things. We restore it upon
- * exit, either normal or abnormal.
- */
- retval = 0;
- add_wait_queue(&info->open_wait, &wait);
-#ifdef SERIAL_DEBUG_OPEN
- printk("block_til_ready before block: ttyS%d, count = %d\n",
- info->line, info->count);
-#endif
- info->count--;
- info->blocked_open++;
- while (1) {
- local_irq_disable();
- mcfrs_setsignals(info, 1, 1);
- local_irq_enable();
- current->state = TASK_INTERRUPTIBLE;
- if (tty_hung_up_p(filp) ||
- !(info->flags & ASYNC_INITIALIZED)) {
-#ifdef SERIAL_DO_RESTART
- if (info->flags & ASYNC_HUP_NOTIFY)
- retval = -EAGAIN;
- else
- retval = -ERESTARTSYS;
-#else
- retval = -EAGAIN;
-#endif
- break;
- }
- if (!(info->flags & ASYNC_CLOSING) &&
- (do_clocal || (mcfrs_getsignals(info) & TIOCM_CD)))
- break;
- if (signal_pending(current)) {
- retval = -ERESTARTSYS;
- break;
- }
-#ifdef SERIAL_DEBUG_OPEN
- printk("block_til_ready blocking: ttyS%d, count = %d\n",
- info->line, info->count);
-#endif
- schedule();
- }
- current->state = TASK_RUNNING;
- remove_wait_queue(&info->open_wait, &wait);
- if (!tty_hung_up_p(filp))
- info->count++;
- info->blocked_open--;
-#ifdef SERIAL_DEBUG_OPEN
- printk("block_til_ready after blocking: ttyS%d, count = %d\n",
- info->line, info->count);
-#endif
- if (retval)
- return retval;
- info->flags |= ASYNC_NORMAL_ACTIVE;
- return 0;
-}
-
-/*
- * This routine is called whenever a serial port is opened. It
- * enables interrupts for a serial port, linking in its structure into
- * the IRQ chain. It also performs the serial-specific
- * initialization for the tty structure.
- */
-int mcfrs_open(struct tty_struct *tty, struct file * filp)
-{
- struct mcf_serial *info;
- int retval, line;
-
- line = tty->index;
- if ((line < 0) || (line >= NR_PORTS))
- return -ENODEV;
- info = mcfrs_table + line;
- if (serial_paranoia_check(info, tty->name, "mcfrs_open"))
- return -ENODEV;
-#ifdef SERIAL_DEBUG_OPEN
- printk("mcfrs_open %s, count = %d\n", tty->name, info->count);
-#endif
- info->count++;
- tty->driver_data = info;
- info->port.tty = tty;
-
- /*
- * Start up serial port
- */
- retval = startup(info);
- if (retval)
- return retval;
-
- retval = block_til_ready(tty, filp, info);
- if (retval) {
-#ifdef SERIAL_DEBUG_OPEN
- printk("mcfrs_open returning after block_til_ready with %d\n",
- retval);
-#endif
- return retval;
- }
-
-#ifdef SERIAL_DEBUG_OPEN
- printk("mcfrs_open %s successful...\n", tty->name);
-#endif
- return 0;
-}
-
-/*
- * Based on the line number set up the internal interrupt stuff.
- */
-static void mcfrs_irqinit(struct mcf_serial *info)
-{
-#if defined(CONFIG_M5272)
- volatile unsigned long *icrp;
- volatile unsigned long *portp;
- volatile unsigned char *uartp;
-
- uartp = info->addr;
- icrp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_ICR2);
-
- switch (info->line) {
- case 0:
- *icrp = 0xe0000000;
- break;
- case 1:
- *icrp = 0x0e000000;
- break;
- default:
- printk("MCFRS: don't know how to handle UART %d interrupt?\n",
- info->line);
- return;
- }
-
- /* Enable the output lines for the serial ports */
- portp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_PBCNT);
- *portp = (*portp & ~0x000000ff) | 0x00000055;
- portp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_PDCNT);
- *portp = (*portp & ~0x000003fc) | 0x000002a8;
-#elif defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x)
- volatile unsigned char *icrp, *uartp;
- volatile unsigned long *imrp;
-
- uartp = info->addr;
-
- icrp = (volatile unsigned char *) (MCF_MBAR + MCFICM_INTC0 +
- MCFINTC_ICR0 + MCFINT_UART0 + info->line);
- *icrp = 0x30 + info->line; /* level 6, line based priority */
-
- imrp = (volatile unsigned long *) (MCF_MBAR + MCFICM_INTC0 +
- MCFINTC_IMRL);
- *imrp &= ~((1 << (info->irq - MCFINT_VECBASE)) | 1);
-#if defined(CONFIG_M527x)
- {
- /*
- * External Pin Mask Setting & Enable External Pin for Interface
- * mrcbis@aliceposta.it
- */
- u16 *serpin_enable_mask;
- serpin_enable_mask = (u16 *) (MCF_IPSBAR + MCF_GPIO_PAR_UART);
- if (info->line == 0)
- *serpin_enable_mask |= UART0_ENABLE_MASK;
- else if (info->line == 1)
- *serpin_enable_mask |= UART1_ENABLE_MASK;
- else if (info->line == 2)
- *serpin_enable_mask |= UART2_ENABLE_MASK;
- }
-#endif
-#if defined(CONFIG_M528x)
- /* make sure PUAPAR is set for UART0 and UART1 */
- if (info->line < 2) {
- volatile unsigned char *portp = (volatile unsigned char *) (MCF_MBAR + MCF5282_GPIO_PUAPAR);
- *portp |= (0x03 << (info->line * 2));
- }
-#endif
-#elif defined(CONFIG_M520x)
- volatile unsigned char *icrp, *uartp;
- volatile unsigned long *imrp;
-
- uartp = info->addr;
-
- icrp = (volatile unsigned char *) (MCF_MBAR + MCFICM_INTC0 +
- MCFINTC_ICR0 + MCFINT_UART0 + info->line);
- *icrp = 0x03;
-
- imrp = (volatile unsigned long *) (MCF_MBAR + MCFICM_INTC0 +
- MCFINTC_IMRL);
- *imrp &= ~((1 << (info->irq - MCFINT_VECBASE)) | 1);
- if (info->line < 2) {
- unsigned short *uart_par;
- uart_par = (unsigned short *)(MCF_IPSBAR + MCF_GPIO_PAR_UART);
- if (info->line == 0)
- *uart_par |= MCF_GPIO_PAR_UART_PAR_UTXD0
- | MCF_GPIO_PAR_UART_PAR_URXD0;
- else if (info->line == 1)
- *uart_par |= MCF_GPIO_PAR_UART_PAR_UTXD1
- | MCF_GPIO_PAR_UART_PAR_URXD1;
- } else if (info->line == 2) {
- unsigned char *feci2c_par;
- feci2c_par = (unsigned char *)(MCF_IPSBAR + MCF_GPIO_PAR_FECI2C);
- *feci2c_par &= ~0x0F;
- *feci2c_par |= MCF_GPIO_PAR_FECI2C_PAR_SCL_UTXD2
- | MCF_GPIO_PAR_FECI2C_PAR_SDA_URXD2;
- }
-#elif defined(CONFIG_M532x)
- volatile unsigned char *uartp;
- uartp = info->addr;
- switch (info->line) {
- case 0:
- MCF_INTC0_ICR26 = 0x3;
- MCF_INTC0_CIMR = 26;
- /* GPIO initialization */
- MCF_GPIO_PAR_UART |= 0x000F;
- break;
- case 1:
- MCF_INTC0_ICR27 = 0x3;
- MCF_INTC0_CIMR = 27;
- /* GPIO initialization */
- MCF_GPIO_PAR_UART |= 0x0FF0;
- break;
- case 2:
- MCF_INTC0_ICR28 = 0x3;
- MCF_INTC0_CIMR = 28;
- /* GPIOs also must be initalized, depends on board */
- break;
- }
-#else
- volatile unsigned char *icrp, *uartp;
-
- switch (info->line) {
- case 0:
- icrp = (volatile unsigned char *) (MCF_MBAR + MCFSIM_UART1ICR);
- *icrp = /*MCFSIM_ICR_AUTOVEC |*/ MCFSIM_ICR_LEVEL6 |
- MCFSIM_ICR_PRI1;
- mcf_setimr(mcf_getimr() & ~MCFSIM_IMR_UART1);
- break;
- case 1:
- icrp = (volatile unsigned char *) (MCF_MBAR + MCFSIM_UART2ICR);
- *icrp = /*MCFSIM_ICR_AUTOVEC |*/ MCFSIM_ICR_LEVEL6 |
- MCFSIM_ICR_PRI2;
- mcf_setimr(mcf_getimr() & ~MCFSIM_IMR_UART2);
- break;
- default:
- printk("MCFRS: don't know how to handle UART %d interrupt?\n",
- info->line);
- return;
- }
-
- uartp = info->addr;
- uartp[MCFUART_UIVR] = info->irq;
-#endif
-
- /* Clear mask, so no surprise interrupts. */
- uartp[MCFUART_UIMR] = 0;
-
- if (request_irq(info->irq, mcfrs_interrupt, IRQF_DISABLED,
- "ColdFire UART", NULL)) {
- printk("MCFRS: Unable to attach ColdFire UART %d interrupt "
- "vector=%d\n", info->line, info->irq);
- }
-
- return;
-}
-
-
-char *mcfrs_drivername = "ColdFire internal UART serial driver version 1.00\n";
-
-
-/*
- * Serial stats reporting...
- */
-int mcfrs_readproc(char *page, char **start, off_t off, int count,
- int *eof, void *data)
-{
- struct mcf_serial *info;
- char str[20];
- int len, sigs, i;
-
- len = sprintf(page, mcfrs_drivername);
- for (i = 0; (i < NR_PORTS); i++) {
- info = &mcfrs_table[i];
- len += sprintf((page + len), "%d: port:%x irq=%d baud:%d ",
- i, (unsigned int) info->addr, info->irq, info->baud);
- if (info->stats.rx || info->stats.tx)
- len += sprintf((page + len), "tx:%d rx:%d ",
- info->stats.tx, info->stats.rx);
- if (info->stats.rxframing)
- len += sprintf((page + len), "fe:%d ",
- info->stats.rxframing);
- if (info->stats.rxparity)
- len += sprintf((page + len), "pe:%d ",
- info->stats.rxparity);
- if (info->stats.rxbreak)
- len += sprintf((page + len), "brk:%d ",
- info->stats.rxbreak);
- if (info->stats.rxoverrun)
- len += sprintf((page + len), "oe:%d ",
- info->stats.rxoverrun);
-
- str[0] = str[1] = 0;
- if ((sigs = mcfrs_getsignals(info))) {
- if (sigs & TIOCM_RTS)
- strcat(str, "|RTS");
- if (sigs & TIOCM_CTS)
- strcat(str, "|CTS");
- if (sigs & TIOCM_DTR)
- strcat(str, "|DTR");
- if (sigs & TIOCM_CD)
- strcat(str, "|CD");
- }
-
- len += sprintf((page + len), "%s\n", &str[1]);
- }
-
- return(len);
-}
-
-
-/* Finally, routines used to initialize the serial driver. */
-
-static void show_serial_version(void)
-{
- printk(mcfrs_drivername);
-}
-
-static const struct tty_operations mcfrs_ops = {
- .open = mcfrs_open,
- .close = mcfrs_close,
- .write = mcfrs_write,
- .flush_chars = mcfrs_flush_chars,
- .write_room = mcfrs_write_room,
- .chars_in_buffer = mcfrs_chars_in_buffer,
- .flush_buffer = mcfrs_flush_buffer,
- .ioctl = mcfrs_ioctl,
- .throttle = mcfrs_throttle,
- .unthrottle = mcfrs_unthrottle,
- .set_termios = mcfrs_set_termios,
- .stop = mcfrs_stop,
- .start = mcfrs_start,
- .hangup = mcfrs_hangup,
- .read_proc = mcfrs_readproc,
- .wait_until_sent = mcfrs_wait_until_sent,
- .tiocmget = mcfrs_tiocmget,
- .tiocmset = mcfrs_tiocmset,
-};
-
-/* mcfrs_init inits the driver */
-static int __init
-mcfrs_init(void)
-{
- struct mcf_serial *info;
- unsigned long flags;
- int i;
-
- /* Setup base handler, and timer table. */
-#ifdef MCFPP_DCD0
- init_timer(&mcfrs_timer_struct);
- mcfrs_timer_struct.function = mcfrs_timer;
- mcfrs_timer_struct.data = 0;
- mcfrs_timer_struct.expires = jiffies + HZ/25;
- add_timer(&mcfrs_timer_struct);
- mcfrs_ppstatus = mcf_getppdata() & (MCFPP_DCD0 | MCFPP_DCD1);
-#endif
- mcfrs_serial_driver = alloc_tty_driver(NR_PORTS);
- if (!mcfrs_serial_driver)
- return -ENOMEM;
-
- show_serial_version();
-
- /* Initialize the tty_driver structure */
- mcfrs_serial_driver->owner = THIS_MODULE;
- mcfrs_serial_driver->name = "ttyS";
- mcfrs_serial_driver->driver_name = "mcfserial";
- mcfrs_serial_driver->major = TTY_MAJOR;
- mcfrs_serial_driver->minor_start = 64;
- mcfrs_serial_driver->type = TTY_DRIVER_TYPE_SERIAL;
- mcfrs_serial_driver->subtype = SERIAL_TYPE_NORMAL;
- mcfrs_serial_driver->init_termios = tty_std_termios;
-
- mcfrs_serial_driver->init_termios.c_cflag =
- mcfrs_console_cbaud | CS8 | CREAD | HUPCL | CLOCAL;
- mcfrs_serial_driver->flags = TTY_DRIVER_REAL_RAW;
-
- tty_set_operations(mcfrs_serial_driver, &mcfrs_ops);
-
- if (tty_register_driver(mcfrs_serial_driver)) {
- printk("MCFRS: Couldn't register serial driver\n");
- put_tty_driver(mcfrs_serial_driver);
- return(-EBUSY);
- }
-
- local_irq_save(flags);
-
- /*
- * Configure all the attached serial ports.
- */
- for (i = 0, info = mcfrs_table; (i < NR_PORTS); i++, info++) {
- info->magic = SERIAL_MAGIC;
- info->line = i;
- info->port.tty = NULL;
- info->custom_divisor = 16;
- info->close_delay = 50;
- info->closing_wait = 3000;
- info->x_char = 0;
- info->event = 0;
- info->count = 0;
- info->blocked_open = 0;
- INIT_WORK(&info->tqueue, mcfrs_offintr);
- INIT_WORK(&info->tqueue_hangup, do_serial_hangup);
- init_waitqueue_head(&info->open_wait);
- init_waitqueue_head(&info->close_wait);
-
- info->imr = 0;
- mcfrs_setsignals(info, 0, 0);
- mcfrs_irqinit(info);
-
- printk("ttyS%d at 0x%04x (irq = %d)", info->line,
- (unsigned int) info->addr, info->irq);
- printk(" is a builtin ColdFire UART\n");
- }
-
- local_irq_restore(flags);
- return 0;
-}
-
-module_init(mcfrs_init);
-
-/****************************************************************************/
-/* Serial Console */
-/****************************************************************************/
-
-/*
- * Quick and dirty UART initialization, for console output.
- */
-
-void mcfrs_init_console(void)
-{
- volatile unsigned char *uartp;
- unsigned int clk;
-
- /*
- * Reset UART, get it into known state...
- */
- uartp = (volatile unsigned char *) (MCF_MBAR +
- (mcfrs_console_port ? MCFUART_BASE2 : MCFUART_BASE1));
-
- uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETRX; /* reset RX */
- uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETTX; /* reset TX */
- uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETMRPTR; /* reset MR pointer */
-
- /*
- * Set port for defined baud , 8 data bits, 1 stop bit, no parity.
- */
- uartp[MCFUART_UMR] = MCFUART_MR1_PARITYNONE | MCFUART_MR1_CS8;
- uartp[MCFUART_UMR] = MCFUART_MR2_STOP1;
-
-#ifdef CONFIG_M5272
-{
- /*
- * For the MCF5272, also compute the baudrate fraction.
- */
- int fraction = MCF_BUSCLK - (clk * 32 * mcfrs_console_baud);
- fraction *= 16;
- fraction /= (32 * mcfrs_console_baud);
- uartp[MCFUART_UFPD] = (fraction & 0xf); /* set fraction */
- clk = (MCF_BUSCLK / mcfrs_console_baud) / 32;
-}
-#else
- clk = ((MCF_BUSCLK / mcfrs_console_baud) + 16) / 32; /* set baud */
-#endif
-
- uartp[MCFUART_UBG1] = (clk & 0xff00) >> 8; /* set msb baud */
- uartp[MCFUART_UBG2] = (clk & 0xff); /* set lsb baud */
- uartp[MCFUART_UCSR] = MCFUART_UCSR_RXCLKTIMER | MCFUART_UCSR_TXCLKTIMER;
- uartp[MCFUART_UCR] = MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE;
-
- mcfrs_console_inited++;
- return;
-}
-
-
-/*
- * Setup for console. Argument comes from the boot command line.
- */
-
-int mcfrs_console_setup(struct console *cp, char *arg)
-{
- int i, n = CONSOLE_BAUD_RATE;
-
- if (!cp)
- return(-1);
-
- if (!strncmp(cp->name, "ttyS", 4))
- mcfrs_console_port = cp->index;
- else if (!strncmp(cp->name, "cua", 3))
- mcfrs_console_port = cp->index;
- else
- return(-1);
-
- if (arg)
- n = simple_strtoul(arg,NULL,0);
- for (i = 0; i < MCFRS_BAUD_TABLE_SIZE; i++)
- if (mcfrs_baud_table[i] == n)
- break;
- if (i < MCFRS_BAUD_TABLE_SIZE) {
- mcfrs_console_baud = n;
- mcfrs_console_cbaud = 0;
- if (i > 15) {
- mcfrs_console_cbaud |= CBAUDEX;
- i -= 15;
- }
- mcfrs_console_cbaud |= i;
- }
- mcfrs_init_console(); /* make sure baud rate changes */
- return(0);
-}
-
-
-static struct tty_driver *mcfrs_console_device(struct console *c, int *index)
-{
- *index = c->index;
- return mcfrs_serial_driver;
-}
-
-
-/*
- * Output a single character, using UART polled mode.
- * This is used for console output.
- */
-
-int mcfrs_put_char(char ch)
-{
- volatile unsigned char *uartp;
- unsigned long flags;
- int i;
-
- uartp = (volatile unsigned char *) (MCF_MBAR +
- (mcfrs_console_port ? MCFUART_BASE2 : MCFUART_BASE1));
-
- local_irq_save(flags);
- for (i = 0; (i < 0x10000); i++) {
- if (uartp[MCFUART_USR] & MCFUART_USR_TXREADY)
- break;
- }
- if (i < 0x10000) {
- uartp[MCFUART_UTB] = ch;
- for (i = 0; (i < 0x10000); i++)
- if (uartp[MCFUART_USR] & MCFUART_USR_TXEMPTY)
- break;
- }
- if (i >= 0x10000)
- mcfrs_init_console(); /* try and get it back */
- local_irq_restore(flags);
-
- return 1;
-}
-
-
-/*
- * rs_console_write is registered for printk output.
- */
-
-void mcfrs_console_write(struct console *cp, const char *p, unsigned len)
-{
- if (!mcfrs_console_inited)
- mcfrs_init_console();
- while (len-- > 0) {
- if (*p == '\n')
- mcfrs_put_char('\r');
- mcfrs_put_char(*p++);
- }
-}
-
-/*
- * declare our consoles
- */
-
-struct console mcfrs_console = {
- .name = "ttyS",
- .write = mcfrs_console_write,
- .device = mcfrs_console_device,
- .setup = mcfrs_console_setup,
- .flags = CON_PRINTBUFFER,
- .index = -1,
-};
-
-static int __init mcfrs_console_init(void)
-{
- register_console(&mcfrs_console);
- return 0;
-}
-
-console_initcall(mcfrs_console_init);
-
-/****************************************************************************/
diff --git a/drivers/serial/mcfserial.h b/drivers/serial/mcfserial.h
deleted file mode 100644
index 56420e2..0000000
--- a/drivers/serial/mcfserial.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * mcfserial.c -- serial driver for ColdFire internal UARTS.
- *
- * Copyright (c) 1999 Greg Ungerer <gerg@snapgear.com>
- * Copyright (c) 2000-2001 Lineo, Inc. <www.lineo.com>
- * Copyright (c) 2002 SnapGear Inc., <www.snapgear.com>
- *
- * Based on code from 68332serial.c which was:
- *
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1998 TSHG
- * Copyright (c) 1999 Rt-Control Inc. <jeff@uclinux.org>
- */
-#ifndef _MCF_SERIAL_H
-#define _MCF_SERIAL_H
-
-#include <linux/serial.h>
-
-#ifdef __KERNEL__
-
-/*
- * Define a local serial stats structure.
- */
-
-struct mcf_stats {
- unsigned int rx;
- unsigned int tx;
- unsigned int rxbreak;
- unsigned int rxframing;
- unsigned int rxparity;
- unsigned int rxoverrun;
-};
-
-
-/*
- * This is our internal structure for each serial port's state.
- * Each serial port has one of these structures associated with it.
- */
-
-struct mcf_serial {
- int magic;
- volatile unsigned char *addr; /* UART memory address */
- int irq;
- int flags; /* defined in tty.h */
- int type; /* UART type */
- struct tty_struct *tty;
- unsigned char imr; /* Software imr register */
- unsigned int baud;
- int sigs;
- int custom_divisor;
- int x_char; /* xon/xoff character */
- int baud_base;
- int close_delay;
- unsigned short closing_wait;
- unsigned short closing_wait2;
- unsigned long event;
- int line;
- int count; /* # of fd on device */
- int blocked_open; /* # of blocked opens */
- unsigned char *xmit_buf;
- int xmit_head;
- int xmit_tail;
- int xmit_cnt;
- struct mcf_stats stats;
- struct work_struct tqueue;
- struct work_struct tqueue_hangup;
- wait_queue_head_t open_wait;
- wait_queue_head_t close_wait;
-
-};
-
-#endif /* __KERNEL__ */
-
-#endif /* _MCF_SERIAL_H */
diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c
index f977c98..6bdf336 100644
--- a/drivers/serial/serial_core.c
+++ b/drivers/serial/serial_core.c
@@ -2051,7 +2051,8 @@
"transmitter\n",
port->dev ? port->dev->bus_id : "",
port->dev ? ": " : "",
- drv->dev_name, port->line);
+ drv->dev_name,
+ drv->tty_driver->name_base + port->line);
ops->shutdown(port);
}
@@ -2154,12 +2155,11 @@
switch (port->iotype) {
case UPIO_PORT:
- snprintf(address, sizeof(address),
- "I/O 0x%x", port->iobase);
+ snprintf(address, sizeof(address), "I/O 0x%lx", port->iobase);
break;
case UPIO_HUB6:
snprintf(address, sizeof(address),
- "I/O 0x%x offset 0x%x", port->iobase, port->hub6);
+ "I/O 0x%lx offset 0x%x", port->iobase, port->hub6);
break;
case UPIO_MEM:
case UPIO_MEM32:
@@ -2177,7 +2177,9 @@
printk(KERN_INFO "%s%s%s%d at %s (irq = %d) is a %s\n",
port->dev ? port->dev->bus_id : "",
port->dev ? ": " : "",
- drv->dev_name, port->line, address, port->irq, uart_type(port));
+ drv->dev_name,
+ drv->tty_driver->name_base + port->line,
+ address, port->irq, uart_type(port));
}
static void
diff --git a/drivers/serial/serial_cs.c b/drivers/serial/serial_cs.c
index 164d2a4..7546aa8 100644
--- a/drivers/serial/serial_cs.c
+++ b/drivers/serial/serial_cs.c
@@ -431,131 +431,103 @@
{
int i;
i = pcmcia_get_first_tuple(handle, tuple);
- if (i != CS_SUCCESS)
- return CS_NO_MORE_ITEMS;
- i = pcmcia_get_tuple_data(handle, tuple);
- if (i != CS_SUCCESS)
+ if (i != 0)
return i;
- return pcmcia_parse_tuple(handle, tuple, parse);
-}
-
-static int
-next_tuple(struct pcmcia_device *handle, tuple_t * tuple, cisparse_t * parse)
-{
- int i;
- i = pcmcia_get_next_tuple(handle, tuple);
- if (i != CS_SUCCESS)
- return CS_NO_MORE_ITEMS;
i = pcmcia_get_tuple_data(handle, tuple);
- if (i != CS_SUCCESS)
+ if (i != 0)
return i;
- return pcmcia_parse_tuple(handle, tuple, parse);
+ return pcmcia_parse_tuple(tuple, parse);
}
/*====================================================================*/
-static int simple_config(struct pcmcia_device *link)
+static int simple_config_check(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ static const int size_table[2] = { 8, 16 };
+ int *try = priv_data;
+
+ if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ p_dev->conf.Vpp =
+ cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+
+ if ((cf->io.nwin > 0) && (cf->io.win[0].len == size_table[(*try >> 1)])
+ && (cf->io.win[0].base != 0)) {
+ p_dev->io.BasePort1 = cf->io.win[0].base;
+ p_dev->io.IOAddrLines = ((*try & 0x1) == 0) ?
+ 16 : cf->io.flags & CISTPL_IO_LINES_MASK;
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int simple_config_check_notpicky(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
{
static const unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
- static const int size_table[2] = { 8, 16 };
+ int j;
+
+ if ((cf->io.nwin > 0) && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) {
+ for (j = 0; j < 5; j++) {
+ p_dev->io.BasePort1 = base[j];
+ p_dev->io.IOAddrLines = base[j] ? 16 : 3;
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+ return 0;
+ }
+ }
+ return -ENODEV;
+}
+
+static int simple_config(struct pcmcia_device *link)
+{
struct serial_info *info = link->priv;
- struct serial_cfg_mem *cfg_mem;
- tuple_t *tuple;
- u_char *buf;
- cisparse_t *parse;
- cistpl_cftable_entry_t *cf;
- config_info_t config;
- int i, j, try;
- int s;
-
- cfg_mem = kmalloc(sizeof(struct serial_cfg_mem), GFP_KERNEL);
- if (!cfg_mem)
- return -1;
-
- tuple = &cfg_mem->tuple;
- parse = &cfg_mem->parse;
- cf = &parse->cftable_entry;
- buf = cfg_mem->buf;
+ int i = -ENODEV, try;
/* If the card is already configured, look up the port and irq */
- i = pcmcia_get_configuration_info(link, &config);
- if ((i == CS_SUCCESS) && (config.Attributes & CONF_VALID_CLIENT)) {
+ if (link->function_config) {
unsigned int port = 0;
- if ((config.BasePort2 != 0) && (config.NumPorts2 == 8)) {
- port = config.BasePort2;
+ if ((link->io.BasePort2 != 0) &&
+ (link->io.NumPorts2 == 8)) {
+ port = link->io.BasePort2;
info->slave = 1;
} else if ((info->manfid == MANFID_OSITECH) &&
- (config.NumPorts1 == 0x40)) {
- port = config.BasePort1 + 0x28;
+ (link->io.NumPorts1 == 0x40)) {
+ port = link->io.BasePort1 + 0x28;
info->slave = 1;
}
if (info->slave) {
- kfree(cfg_mem);
- return setup_serial(link, info, port, config.AssignedIRQ);
+ return setup_serial(link, info, port,
+ link->irq.AssignedIRQ);
}
}
- /* First pass: look for a config entry that looks normal. */
- tuple->TupleData = (cisdata_t *) buf;
- tuple->TupleOffset = 0;
- tuple->TupleDataMax = 255;
- tuple->Attributes = 0;
- tuple->DesiredTuple = CISTPL_CFTABLE_ENTRY;
- /* Two tries: without IO aliases, then with aliases */
- for (s = 0; s < 2; s++) {
- for (try = 0; try < 2; try++) {
- i = first_tuple(link, tuple, parse);
- while (i != CS_NO_MORE_ITEMS) {
- if (i != CS_SUCCESS)
- goto next_entry;
- if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM))
- link->conf.Vpp =
- cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
- if ((cf->io.nwin > 0) && (cf->io.win[0].len == size_table[s]) &&
- (cf->io.win[0].base != 0)) {
- link->conf.ConfigIndex = cf->index;
- link->io.BasePort1 = cf->io.win[0].base;
- link->io.IOAddrLines = (try == 0) ?
- 16 : cf->io.flags & CISTPL_IO_LINES_MASK;
- i = pcmcia_request_io(link, &link->io);
- if (i == CS_SUCCESS)
- goto found_port;
- }
-next_entry:
- i = next_tuple(link, tuple, parse);
- }
- }
- }
+ /* First pass: look for a config entry that looks normal.
+ * Two tries: without IO aliases, then with aliases */
+ for (try = 0; try < 4; try++)
+ if (!pcmcia_loop_config(link, simple_config_check, &try))
+ goto found_port;
+
/* Second pass: try to find an entry that isn't picky about
its base address, then try to grab any standard serial port
address, and finally try to get any free port. */
- i = first_tuple(link, tuple, parse);
- while (i != CS_NO_MORE_ITEMS) {
- if ((i == CS_SUCCESS) && (cf->io.nwin > 0) &&
- ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) {
- link->conf.ConfigIndex = cf->index;
- for (j = 0; j < 5; j++) {
- link->io.BasePort1 = base[j];
- link->io.IOAddrLines = base[j] ? 16 : 3;
- i = pcmcia_request_io(link, &link->io);
- if (i == CS_SUCCESS)
- goto found_port;
- }
- }
- i = next_tuple(link, tuple, parse);
- }
+ if (!pcmcia_loop_config(link, simple_config_check_notpicky, NULL))
+ goto found_port;
- found_port:
- if (i != CS_SUCCESS) {
- printk(KERN_NOTICE
- "serial_cs: no usable port range found, giving up\n");
- cs_error(link, RequestIO, i);
- kfree(cfg_mem);
- return -1;
- }
+ printk(KERN_NOTICE
+ "serial_cs: no usable port range found, giving up\n");
+ cs_error(link, RequestIO, i);
+ return -1;
+found_port:
i = pcmcia_request_irq(link, &link->irq);
- if (i != CS_SUCCESS) {
+ if (i != 0) {
cs_error(link, RequestIRQ, i);
link->irq.AssignedIRQ = 0;
}
@@ -569,88 +541,76 @@
info->quirk->config(link);
i = pcmcia_request_configuration(link, &link->conf);
- if (i != CS_SUCCESS) {
+ if (i != 0) {
cs_error(link, RequestConfiguration, i);
- kfree(cfg_mem);
return -1;
}
- kfree(cfg_mem);
return setup_serial(link, info, link->io.BasePort1, link->irq.AssignedIRQ);
}
-static int multi_config(struct pcmcia_device * link)
+static int multi_config_check(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ int *base2 = priv_data;
+
+ /* The quad port cards have bad CIS's, so just look for a
+ window larger than 8 ports and assume it will be right */
+ if ((cf->io.nwin == 1) && (cf->io.win[0].len > 8)) {
+ p_dev->io.BasePort1 = cf->io.win[0].base;
+ p_dev->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK;
+ if (!pcmcia_request_io(p_dev, &p_dev->io)) {
+ *base2 = p_dev->io.BasePort1 + 8;
+ return 0;
+ }
+ }
+ return -ENODEV;
+}
+
+static int multi_config_check_notpicky(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ int *base2 = priv_data;
+
+ if (cf->io.nwin == 2) {
+ p_dev->io.BasePort1 = cf->io.win[0].base;
+ p_dev->io.BasePort2 = cf->io.win[1].base;
+ p_dev->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK;
+ if (!pcmcia_request_io(p_dev, &p_dev->io)) {
+ *base2 = p_dev->io.BasePort2;
+ return 0;
+ }
+ }
+ return -ENODEV;
+}
+
+static int multi_config(struct pcmcia_device *link)
{
struct serial_info *info = link->priv;
- struct serial_cfg_mem *cfg_mem;
- tuple_t *tuple;
- u_char *buf;
- cisparse_t *parse;
- cistpl_cftable_entry_t *cf;
- int i, rc, base2 = 0;
-
- cfg_mem = kmalloc(sizeof(struct serial_cfg_mem), GFP_KERNEL);
- if (!cfg_mem)
- return -1;
- tuple = &cfg_mem->tuple;
- parse = &cfg_mem->parse;
- cf = &parse->cftable_entry;
- buf = cfg_mem->buf;
-
- tuple->TupleData = (cisdata_t *) buf;
- tuple->TupleOffset = 0;
- tuple->TupleDataMax = 255;
- tuple->Attributes = 0;
- tuple->DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ int i, base2 = 0;
/* First, look for a generic full-sized window */
link->io.NumPorts1 = info->multi * 8;
- i = first_tuple(link, tuple, parse);
- while (i != CS_NO_MORE_ITEMS) {
- /* The quad port cards have bad CIS's, so just look for a
- window larger than 8 ports and assume it will be right */
- if ((i == CS_SUCCESS) && (cf->io.nwin == 1) &&
- (cf->io.win[0].len > 8)) {
- link->conf.ConfigIndex = cf->index;
- link->io.BasePort1 = cf->io.win[0].base;
- link->io.IOAddrLines =
- cf->io.flags & CISTPL_IO_LINES_MASK;
- i = pcmcia_request_io(link, &link->io);
- base2 = link->io.BasePort1 + 8;
- if (i == CS_SUCCESS)
- break;
- }
- i = next_tuple(link, tuple, parse);
- }
-
- /* If that didn't work, look for two windows */
- if (i != CS_SUCCESS) {
+ if (pcmcia_loop_config(link, multi_config_check, &base2)) {
+ /* If that didn't work, look for two windows */
link->io.NumPorts1 = link->io.NumPorts2 = 8;
info->multi = 2;
- i = first_tuple(link, tuple, parse);
- while (i != CS_NO_MORE_ITEMS) {
- if ((i == CS_SUCCESS) && (cf->io.nwin == 2)) {
- link->conf.ConfigIndex = cf->index;
- link->io.BasePort1 = cf->io.win[0].base;
- link->io.BasePort2 = cf->io.win[1].base;
- link->io.IOAddrLines =
- cf->io.flags & CISTPL_IO_LINES_MASK;
- i = pcmcia_request_io(link, &link->io);
- base2 = link->io.BasePort2;
- if (i == CS_SUCCESS)
- break;
- }
- i = next_tuple(link, tuple, parse);
+ if (pcmcia_loop_config(link, multi_config_check_notpicky,
+ &base2)) {
+ printk(KERN_NOTICE "serial_cs: no usable port range"
+ "found, giving up\n");
+ return -ENODEV;
}
}
- if (i != CS_SUCCESS) {
- cs_error(link, RequestIO, i);
- rc = -1;
- goto free_cfg_mem;
- }
-
i = pcmcia_request_irq(link, &link->irq);
- if (i != CS_SUCCESS) {
+ if (i != 0) {
+ /* FIXME: comment does not fit, error handling does not fit */
printk(KERN_NOTICE
"serial_cs: no usable port range found, giving up\n");
cs_error(link, RequestIRQ, i);
@@ -664,10 +624,9 @@
info->quirk->config(link);
i = pcmcia_request_configuration(link, &link->conf);
- if (i != CS_SUCCESS) {
+ if (i != 0) {
cs_error(link, RequestConfiguration, i);
- rc = -1;
- goto free_cfg_mem;
+ return -ENODEV;
}
/* The Oxford Semiconductor OXCF950 cards are in fact single-port:
@@ -678,7 +637,8 @@
info->prodid == PRODID_POSSIO_GCC)) {
int err;
- if (cf->index == 1 || cf->index == 3) {
+ if (link->conf.ConfigIndex == 1 ||
+ link->conf.ConfigIndex == 3) {
err = setup_serial(link, info, base2,
link->irq.AssignedIRQ);
base2 = link->io.BasePort1;
@@ -695,18 +655,14 @@
if (info->quirk && info->quirk->wakeup)
info->quirk->wakeup(link);
- rc = 0;
- goto free_cfg_mem;
+ return 0;
}
setup_serial(link, info, link->io.BasePort1, link->irq.AssignedIRQ);
for (i = 0; i < info->multi - 1; i++)
setup_serial(link, info, base2 + (8 * i),
link->irq.AssignedIRQ);
- rc = 0;
-free_cfg_mem:
- kfree(cfg_mem);
- return rc;
+ return 0;
}
/*======================================================================
@@ -746,7 +702,7 @@
/* Is this a compliant multifunction card? */
tuple->DesiredTuple = CISTPL_LONGLINK_MFC;
tuple->Attributes = TUPLE_RETURN_COMMON | TUPLE_RETURN_LINK;
- info->multi = (first_tuple(link, tuple, parse) == CS_SUCCESS);
+ info->multi = (first_tuple(link, tuple, parse) == 0);
/* Is this a multiport card? */
tuple->DesiredTuple = CISTPL_MANFID;
@@ -770,7 +726,7 @@
((link->func_id == CISTPL_FUNCID_MULTI) ||
(link->func_id == CISTPL_FUNCID_SERIAL))) {
tuple->DesiredTuple = CISTPL_CFTABLE_ENTRY;
- if (first_tuple(link, tuple, parse) == CS_SUCCESS) {
+ if (first_tuple(link, tuple, parse) == 0) {
if ((cf->io.nwin == 1) && (cf->io.win[0].len % 8 == 0))
info->multi = cf->io.win[0].len >> 3;
if ((cf->io.nwin == 2) && (cf->io.win[0].len == 8) &&
diff --git a/drivers/serial/sunhv.c b/drivers/serial/sunhv.c
index e41766d..a94a2ab 100644
--- a/drivers/serial/sunhv.c
+++ b/drivers/serial/sunhv.c
@@ -616,7 +616,7 @@
return 0;
}
-static struct of_device_id hv_match[] = {
+static const struct of_device_id hv_match[] = {
{
.name = "console",
.compatible = "qcn",
diff --git a/drivers/serial/sunsab.c b/drivers/serial/sunsab.c
index 29b4458..0355efe 100644
--- a/drivers/serial/sunsab.c
+++ b/drivers/serial/sunsab.c
@@ -1078,7 +1078,7 @@
return 0;
}
-static struct of_device_id sab_match[] = {
+static const struct of_device_id sab_match[] = {
{
.name = "se",
},
diff --git a/drivers/serial/sunsu.c b/drivers/serial/sunsu.c
index a378464..a4dc79b 100644
--- a/drivers/serial/sunsu.c
+++ b/drivers/serial/sunsu.c
@@ -1506,7 +1506,7 @@
return 0;
}
-static struct of_device_id su_match[] = {
+static const struct of_device_id su_match[] = {
{
.name = "su",
},
diff --git a/drivers/serial/sunzilog.c b/drivers/serial/sunzilog.c
index 3cb4c8a..45a299f 100644
--- a/drivers/serial/sunzilog.c
+++ b/drivers/serial/sunzilog.c
@@ -1480,7 +1480,7 @@
return 0;
}
-static struct of_device_id zs_match[] = {
+static const struct of_device_id zs_match[] = {
{
.name = "zs",
},
diff --git a/drivers/ssb/pcmcia.c b/drivers/ssb/pcmcia.c
index 24c2a46..fbfadba 100644
--- a/drivers/ssb/pcmcia.c
+++ b/drivers/ssb/pcmcia.c
@@ -80,7 +80,7 @@
reg.Action = CS_WRITE;
reg.Value = value;
res = pcmcia_access_configuration_register(bus->host_pcmcia, ®);
- if (unlikely(res != CS_SUCCESS))
+ if (unlikely(res != 0))
return -EBUSY;
return 0;
@@ -96,7 +96,7 @@
reg.Offset = offset;
reg.Action = CS_READ;
res = pcmcia_access_configuration_register(bus->host_pcmcia, ®);
- if (unlikely(res != CS_SUCCESS))
+ if (unlikely(res != 0))
return -EBUSY;
*value = reg.Value;
@@ -638,17 +638,17 @@
tuple.TupleData = buf;
tuple.TupleDataMax = sizeof(buf);
res = pcmcia_get_first_tuple(bus->host_pcmcia, &tuple);
- GOTO_ERROR_ON(res != CS_SUCCESS, "MAC first tpl");
+ GOTO_ERROR_ON(res != 0, "MAC first tpl");
res = pcmcia_get_tuple_data(bus->host_pcmcia, &tuple);
- GOTO_ERROR_ON(res != CS_SUCCESS, "MAC first tpl data");
+ GOTO_ERROR_ON(res != 0, "MAC first tpl data");
while (1) {
GOTO_ERROR_ON(tuple.TupleDataLen < 1, "MAC tpl < 1");
if (tuple.TupleData[0] == CISTPL_FUNCE_LAN_NODE_ID)
break;
res = pcmcia_get_next_tuple(bus->host_pcmcia, &tuple);
- GOTO_ERROR_ON(res != CS_SUCCESS, "MAC next tpl");
+ GOTO_ERROR_ON(res != 0, "MAC next tpl");
res = pcmcia_get_tuple_data(bus->host_pcmcia, &tuple);
- GOTO_ERROR_ON(res != CS_SUCCESS, "MAC next tpl data");
+ GOTO_ERROR_ON(res != 0, "MAC next tpl data");
}
GOTO_ERROR_ON(tuple.TupleDataLen != ETH_ALEN + 2, "MAC tpl size");
memcpy(sprom->il0mac, &tuple.TupleData[2], ETH_ALEN);
@@ -659,9 +659,9 @@
tuple.TupleData = buf;
tuple.TupleDataMax = sizeof(buf);
res = pcmcia_get_first_tuple(bus->host_pcmcia, &tuple);
- GOTO_ERROR_ON(res != CS_SUCCESS, "VEN first tpl");
+ GOTO_ERROR_ON(res != 0, "VEN first tpl");
res = pcmcia_get_tuple_data(bus->host_pcmcia, &tuple);
- GOTO_ERROR_ON(res != CS_SUCCESS, "VEN first tpl data");
+ GOTO_ERROR_ON(res != 0, "VEN first tpl data");
while (1) {
GOTO_ERROR_ON(tuple.TupleDataLen < 1, "VEN tpl < 1");
switch (tuple.TupleData[0]) {
@@ -733,11 +733,11 @@
break;
}
res = pcmcia_get_next_tuple(bus->host_pcmcia, &tuple);
- if (res == CS_NO_MORE_ITEMS)
+ if (res == -ENOSPC)
break;
- GOTO_ERROR_ON(res != CS_SUCCESS, "VEN next tpl");
+ GOTO_ERROR_ON(res != 0, "VEN next tpl");
res = pcmcia_get_tuple_data(bus->host_pcmcia, &tuple);
- GOTO_ERROR_ON(res != CS_SUCCESS, "VEN next tpl data");
+ GOTO_ERROR_ON(res != 0, "VEN next tpl data");
}
return 0;
diff --git a/drivers/telephony/ixj_pcmcia.c b/drivers/telephony/ixj_pcmcia.c
index ff9a29b..347c3ed 100644
--- a/drivers/telephony/ixj_pcmcia.c
+++ b/drivers/telephony/ixj_pcmcia.c
@@ -124,65 +124,53 @@
return;
}
+static int ixj_config_check(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
+ p_dev->io.BasePort1 = io->win[0].base;
+ p_dev->io.NumPorts1 = io->win[0].len;
+ if (io->nwin == 2) {
+ p_dev->io.BasePort2 = io->win[1].base;
+ p_dev->io.NumPorts2 = io->win[1].len;
+ }
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+ return 0;
+ }
+ return -ENODEV;
+}
+
static int ixj_config(struct pcmcia_device * link)
{
IXJ *j;
ixj_info_t *info;
- tuple_t tuple;
- u_short buf[128];
- cisparse_t parse;
- cistpl_cftable_entry_t *cfg = &parse.cftable_entry;
- cistpl_cftable_entry_t dflt =
- {
- 0
- };
- int last_ret, last_fn;
+ cistpl_cftable_entry_t dflt = { 0 };
+
info = link->priv;
DEBUG(0, "ixj_config(0x%p)\n", link);
- tuple.TupleData = (cisdata_t *) buf;
- tuple.TupleOffset = 0;
- tuple.TupleDataMax = 255;
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- tuple.Attributes = 0;
- CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
- while (1) {
- if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
- pcmcia_parse_tuple(link, &tuple, &parse) != 0)
- goto next_entry;
- if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
- cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
- link->conf.ConfigIndex = cfg->index;
- link->io.BasePort1 = io->win[0].base;
- link->io.NumPorts1 = io->win[0].len;
- if (io->nwin == 2) {
- link->io.BasePort2 = io->win[1].base;
- link->io.NumPorts2 = io->win[1].len;
- }
- if (pcmcia_request_io(link, &link->io) != 0)
- goto next_entry;
- /* If we've got this far, we're done */
- break;
- }
- next_entry:
- if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
- dflt = *cfg;
- CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple));
- }
- CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
+ if (pcmcia_loop_config(link, ixj_config_check, &dflt))
+ goto cs_failed;
+
+ if (pcmcia_request_configuration(link, &link->conf))
+ goto cs_failed;
/*
* Register the card with the core.
- */
- j=ixj_pcmcia_probe(link->io.BasePort1,link->io.BasePort1 + 0x10);
+ */
+ j = ixj_pcmcia_probe(link->io.BasePort1, link->io.BasePort1 + 0x10);
info->ndev = 1;
info->node.major = PHONE_MAJOR;
link->dev_node = &info->node;
ixj_get_serial(link, j);
return 0;
+
cs_failed:
- cs_error(link, last_fn, last_ret);
ixj_cs_release(link);
return -ENODEV;
}
diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c
index db410e9..77fa7a0 100644
--- a/drivers/usb/core/inode.c
+++ b/drivers/usb/core/inode.c
@@ -97,7 +97,7 @@
Opt_err,
};
-static match_table_t tokens = {
+static const match_table_t tokens = {
{Opt_devuid, "devuid=%u"},
{Opt_devgid, "devgid=%u"},
{Opt_devmode, "devmode=%o"},
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 5799298..b697a13 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -210,143 +210,7 @@
/*-------------------------------------------------------------------------*/
-/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */
-
-/* Section 2.2 Host Controller Capability Registers */
-struct ehci_caps {
- /* these fields are specified as 8 and 16 bit registers,
- * but some hosts can't perform 8 or 16 bit PCI accesses.
- */
- u32 hc_capbase;
-#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */
-#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */
- u32 hcs_params; /* HCSPARAMS - offset 0x4 */
-#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */
-#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */
-#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */
-#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */
-#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */
-#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */
-#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */
-
- u32 hcc_params; /* HCCPARAMS - offset 0x8 */
-#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */
-#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */
-#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */
-#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */
-#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/
-#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */
- u8 portroute [8]; /* nibbles for routing - offset 0xC */
-} __attribute__ ((packed));
-
-
-/* Section 2.3 Host Controller Operational Registers */
-struct ehci_regs {
-
- /* USBCMD: offset 0x00 */
- u32 command;
-/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */
-#define CMD_PARK (1<<11) /* enable "park" on async qh */
-#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */
-#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */
-#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */
-#define CMD_ASE (1<<5) /* async schedule enable */
-#define CMD_PSE (1<<4) /* periodic schedule enable */
-/* 3:2 is periodic frame list size */
-#define CMD_RESET (1<<1) /* reset HC not bus */
-#define CMD_RUN (1<<0) /* start/stop HC */
-
- /* USBSTS: offset 0x04 */
- u32 status;
-#define STS_ASS (1<<15) /* Async Schedule Status */
-#define STS_PSS (1<<14) /* Periodic Schedule Status */
-#define STS_RECL (1<<13) /* Reclamation */
-#define STS_HALT (1<<12) /* Not running (any reason) */
-/* some bits reserved */
- /* these STS_* flags are also intr_enable bits (USBINTR) */
-#define STS_IAA (1<<5) /* Interrupted on async advance */
-#define STS_FATAL (1<<4) /* such as some PCI access errors */
-#define STS_FLR (1<<3) /* frame list rolled over */
-#define STS_PCD (1<<2) /* port change detect */
-#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */
-#define STS_INT (1<<0) /* "normal" completion (short, ...) */
-
- /* USBINTR: offset 0x08 */
- u32 intr_enable;
-
- /* FRINDEX: offset 0x0C */
- u32 frame_index; /* current microframe number */
- /* CTRLDSSEGMENT: offset 0x10 */
- u32 segment; /* address bits 63:32 if needed */
- /* PERIODICLISTBASE: offset 0x14 */
- u32 frame_list; /* points to periodic list */
- /* ASYNCLISTADDR: offset 0x18 */
- u32 async_next; /* address of next async queue head */
-
- u32 reserved [9];
-
- /* CONFIGFLAG: offset 0x40 */
- u32 configured_flag;
-#define FLAG_CF (1<<0) /* true: we'll support "high speed" */
-
- /* PORTSC: offset 0x44 */
- u32 port_status [0]; /* up to N_PORTS */
-/* 31:23 reserved */
-#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */
-#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */
-#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */
-/* 19:16 for port testing */
-#define PORT_LED_OFF (0<<14)
-#define PORT_LED_AMBER (1<<14)
-#define PORT_LED_GREEN (2<<14)
-#define PORT_LED_MASK (3<<14)
-#define PORT_OWNER (1<<13) /* true: companion hc owns this port */
-#define PORT_POWER (1<<12) /* true: has power (see PPC) */
-#define PORT_USB11(x) (((x)&(3<<10))==(1<<10)) /* USB 1.1 device */
-/* 11:10 for detecting lowspeed devices (reset vs release ownership) */
-/* 9 reserved */
-#define PORT_RESET (1<<8) /* reset port */
-#define PORT_SUSPEND (1<<7) /* suspend port */
-#define PORT_RESUME (1<<6) /* resume it */
-#define PORT_OCC (1<<5) /* over current change */
-#define PORT_OC (1<<4) /* over current active */
-#define PORT_PEC (1<<3) /* port enable change */
-#define PORT_PE (1<<2) /* port enable */
-#define PORT_CSC (1<<1) /* connect status change */
-#define PORT_CONNECT (1<<0) /* device connected */
-#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC)
-} __attribute__ ((packed));
-
-#define USBMODE 0x68 /* USB Device mode */
-#define USBMODE_SDIS (1<<3) /* Stream disable */
-#define USBMODE_BE (1<<2) /* BE/LE endianness select */
-#define USBMODE_CM_HC (3<<0) /* host controller mode */
-#define USBMODE_CM_IDLE (0<<0) /* idle state */
-
-/* Appendix C, Debug port ... intended for use with special "debug devices"
- * that can help if there's no serial console. (nonstandard enumeration.)
- */
-struct ehci_dbg_port {
- u32 control;
-#define DBGP_OWNER (1<<30)
-#define DBGP_ENABLED (1<<28)
-#define DBGP_DONE (1<<16)
-#define DBGP_INUSE (1<<10)
-#define DBGP_ERRCODE(x) (((x)>>7)&0x07)
-# define DBGP_ERR_BAD 1
-# define DBGP_ERR_SIGNAL 2
-#define DBGP_ERROR (1<<6)
-#define DBGP_GO (1<<5)
-#define DBGP_OUT (1<<4)
-#define DBGP_LEN(x) (((x)>>0)&0x0f)
- u32 pids;
-#define DBGP_PID_GET(x) (((x)>>16)&0xff)
-#define DBGP_PID_SET(data,tok) (((data)<<8)|(tok))
- u32 data03;
- u32 data47;
- u32 address;
-#define DBGP_EPADDR(dev,ep) (((dev)<<8)|(ep))
-} __attribute__ ((packed));
+#include <linux/usb/ehci_def.h>
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/host/sl811_cs.c b/drivers/usb/host/sl811_cs.c
index 5da63f5..516848d 100644
--- a/drivers/usb/host/sl811_cs.c
+++ b/drivers/usb/host/sl811_cs.c
@@ -112,7 +112,8 @@
.num_resources = ARRAY_SIZE(resources),
};
-static int sl811_hc_init(struct device *parent, ioaddr_t base_addr, int irq)
+static int sl811_hc_init(struct device *parent, resource_size_t base_addr,
+ int irq)
{
if (platform_dev.dev.parent)
return -EBUSY;
@@ -155,97 +156,72 @@
platform_device_unregister(&platform_dev);
}
+static int sl811_cs_config_check(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ if (cfg->index == 0)
+ return -ENODEV;
+
+ /* Use power settings for Vcc and Vpp if present */
+ /* Note that the CIS values need to be rescaled */
+ if (cfg->vcc.present & (1<<CISTPL_POWER_VNOM)) {
+ if (cfg->vcc.param[CISTPL_POWER_VNOM]/10000 != vcc)
+ return -ENODEV;
+ } else if (dflt->vcc.present & (1<<CISTPL_POWER_VNOM)) {
+ if (dflt->vcc.param[CISTPL_POWER_VNOM]/10000 != vcc)
+ return -ENODEV;
+ }
+
+ if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM))
+ p_dev->conf.Vpp =
+ cfg->vpp1.param[CISTPL_POWER_VNOM]/10000;
+ else if (dflt->vpp1.present & (1<<CISTPL_POWER_VNOM))
+ p_dev->conf.Vpp =
+ dflt->vpp1.param[CISTPL_POWER_VNOM]/10000;
+
+ /* we need an interrupt */
+ if (cfg->irq.IRQInfo1 || dflt->irq.IRQInfo1)
+ p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
+
+ /* IO window settings */
+ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0;
+ if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
+
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ p_dev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+ p_dev->io.BasePort1 = io->win[0].base;
+ p_dev->io.NumPorts1 = io->win[0].len;
+
+ return pcmcia_request_io(p_dev, &p_dev->io);
+ }
+ pcmcia_disable_device(p_dev);
+ return -ENODEV;
+}
+
+
static int sl811_cs_config(struct pcmcia_device *link)
{
struct device *parent = &handle_to_dev(link);
local_info_t *dev = link->priv;
- tuple_t tuple;
- cisparse_t parse;
int last_fn, last_ret;
- u_char buf[64];
- config_info_t conf;
- cistpl_cftable_entry_t dflt = { 0 };
DBG(0, "sl811_cs_config(0x%p)\n", link);
- /* Look up the current Vcc */
- CS_CHECK(GetConfigurationInfo,
- pcmcia_get_configuration_info(link, &conf));
-
- tuple.Attributes = 0;
- tuple.TupleData = buf;
- tuple.TupleDataMax = sizeof(buf);
- tuple.TupleOffset = 0;
- tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
- CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
- while (1) {
- cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
-
- if (pcmcia_get_tuple_data(link, &tuple) != 0
- || pcmcia_parse_tuple(link, &tuple, &parse)
- != 0)
- goto next_entry;
-
- if (cfg->flags & CISTPL_CFTABLE_DEFAULT) {
- dflt = *cfg;
- }
-
- if (cfg->index == 0)
- goto next_entry;
-
- link->conf.ConfigIndex = cfg->index;
-
- /* Use power settings for Vcc and Vpp if present */
- /* Note that the CIS values need to be rescaled */
- if (cfg->vcc.present & (1<<CISTPL_POWER_VNOM)) {
- if (cfg->vcc.param[CISTPL_POWER_VNOM]/10000
- != conf.Vcc)
- goto next_entry;
- } else if (dflt.vcc.present & (1<<CISTPL_POWER_VNOM)) {
- if (dflt.vcc.param[CISTPL_POWER_VNOM]/10000
- != conf.Vcc)
- goto next_entry;
- }
-
- if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM))
- link->conf.Vpp =
- cfg->vpp1.param[CISTPL_POWER_VNOM]/10000;
- else if (dflt.vpp1.present & (1<<CISTPL_POWER_VNOM))
- link->conf.Vpp =
- dflt.vpp1.param[CISTPL_POWER_VNOM]/10000;
-
- /* we need an interrupt */
- if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
- link->conf.Attributes |= CONF_ENABLE_IRQ;
-
- /* IO window settings */
- link->io.NumPorts1 = link->io.NumPorts2 = 0;
- if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
- cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
-
- link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
- link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
- link->io.BasePort1 = io->win[0].base;
- link->io.NumPorts1 = io->win[0].len;
-
- if (pcmcia_request_io(link, &link->io) != 0)
- goto next_entry;
- }
- break;
-
-next_entry:
- pcmcia_disable_device(link);
- last_ret = pcmcia_get_next_tuple(link, &tuple);
- }
+ if (pcmcia_loop_config(link, sl811_cs_config_check, NULL))
+ goto failed;
/* require an IRQ and two registers */
if (!link->io.NumPorts1 || link->io.NumPorts1 < 2)
- goto cs_failed;
+ goto failed;
if (link->conf.Attributes & CONF_ENABLE_IRQ)
CS_CHECK(RequestIRQ,
pcmcia_request_irq(link, &link->irq));
else
- goto cs_failed;
+ goto failed;
CS_CHECK(RequestConfiguration,
pcmcia_request_configuration(link, &link->conf));
@@ -266,8 +242,9 @@
if (sl811_hc_init(parent, link->io.BasePort1, link->irq.AssignedIRQ)
< 0) {
cs_failed:
- printk("sl811_cs_config failed\n");
cs_error(link, last_fn, last_ret);
+failed:
+ printk(KERN_WARNING "sl811_cs_config failed\n");
sl811_cs_release(link);
return -ENODEV;
}
diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c
index 79ea98c..99fb7dc 100644
--- a/drivers/usb/serial/aircable.c
+++ b/drivers/usb/serial/aircable.c
@@ -272,7 +272,7 @@
* 64 bytes, to ensure I do not get throttled.
* Ask USB mailing list for better aproach.
*/
- tty = port->port.tty;
+ tty = tty_port_tty_get(&port->port);
if (!tty) {
schedule_work(&priv->rx_work);
@@ -283,12 +283,13 @@
count = min(64, serial_buf_data_avail(priv->rx_buf));
if (count <= 0)
- return; /* We have finished sending everything. */
+ goto out; /* We have finished sending everything. */
tty_prepare_flip_string(tty, &data, count);
if (!data) {
- err("%s- kzalloc(%d) failed.", __func__, count);
- return;
+ dev_err(&port->dev, "%s- kzalloc(%d) failed.",
+ __func__, count);
+ goto out;
}
serial_buf_get(priv->rx_buf, data, count);
@@ -297,7 +298,8 @@
if (serial_buf_data_avail(priv->rx_buf))
schedule_work(&priv->rx_work);
-
+out:
+ tty_kref_put(tty);
return;
}
/* End of private methods */
@@ -495,7 +497,7 @@
usb_serial_debug_data(debug, &port->dev, __func__,
urb->actual_length, urb->transfer_buffer);
- tty = port->port.tty;
+ tty = tty_port_tty_get(&port->port);
if (tty && urb->actual_length) {
if (urb->actual_length <= 2) {
/* This is an incomplete package */
@@ -527,6 +529,7 @@
}
aircable_read(&priv->rx_work);
}
+ tty_kref_put(tty);
/* Schedule the next read _if_ we are still open */
if (port->port.count) {
diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c
index 2ebe06c..1913bc7 100644
--- a/drivers/usb/serial/belkin_sa.c
+++ b/drivers/usb/serial/belkin_sa.c
@@ -322,7 +322,7 @@
* to look in to this before committing any code.
*/
if (priv->last_lsr & BELKIN_SA_LSR_ERR) {
- tty = port->port.tty;
+ tty = tty_port_tty_get(&port->port);
/* Overrun Error */
if (priv->last_lsr & BELKIN_SA_LSR_OE) {
}
@@ -335,6 +335,7 @@
/* Break Indicator */
if (priv->last_lsr & BELKIN_SA_LSR_BI) {
}
+ tty_kref_put(tty);
}
#endif
spin_unlock_irqrestore(&priv->lock, flags);
diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c
index e980766..5b20de1 100644
--- a/drivers/usb/serial/console.c
+++ b/drivers/usb/serial/console.c
@@ -117,7 +117,7 @@
}
port = serial->port[0];
- port->port.tty = NULL;
+ tty_port_tty_set(&port->port, NULL);
info->port = port;
@@ -143,7 +143,7 @@
}
memset(&dummy, 0, sizeof(struct ktermios));
tty->termios = termios;
- port->port.tty = tty;
+ tty_port_tty_set(&port->port, tty);
}
/* only call the device specific open if this
@@ -163,7 +163,7 @@
tty_termios_encode_baud_rate(termios, baud, baud);
serial->type->set_termios(tty, port, &dummy);
- port->port.tty = NULL;
+ tty_port_tty_set(&port->port, NULL);
kfree(termios);
kfree(tty);
}
@@ -176,7 +176,7 @@
return retval;
free_termios:
kfree(termios);
- port->port.tty = NULL;
+ tty_port_tty_set(&port->port, NULL);
free_tty:
kfree(tty);
reset_open_count:
diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c
index b4d7235..94ef36c 100644
--- a/drivers/usb/serial/cyberjack.c
+++ b/drivers/usb/serial/cyberjack.c
@@ -384,7 +384,7 @@
return;
}
- tty = port->port.tty;
+ tty = tty_port_tty_get(&port->port);
if (!tty) {
dbg("%s - ignoring since device not open\n", __func__);
return;
@@ -394,6 +394,7 @@
tty_insert_flip_string(tty, data, urb->actual_length);
tty_flip_buffer_push(tty);
}
+ tty_kref_put(tty);
spin_lock(&priv->lock);
diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c
index 22837a3..f3514a9 100644
--- a/drivers/usb/serial/cypress_m8.c
+++ b/drivers/usb/serial/cypress_m8.c
@@ -1286,7 +1286,7 @@
}
spin_unlock_irqrestore(&priv->lock, flags);
- tty = port->port.tty;
+ tty = tty_port_tty_get(&port->port);
if (!tty) {
dbg("%s - bad tty pointer - exiting", __func__);
return;
@@ -1362,7 +1362,7 @@
data[i]);
tty_insert_flip_char(tty, data[i], tty_flag);
}
- tty_flip_buffer_push(port->port.tty);
+ tty_flip_buffer_push(tty);
}
spin_lock_irqsave(&priv->lock, flags);
@@ -1371,6 +1371,7 @@
spin_unlock_irqrestore(&priv->lock, flags);
continue_read:
+ tty_kref_put(tty);
/* Continue trying to always read... unless the port has closed. */
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
index 240aad1..5756ac6 100644
--- a/drivers/usb/serial/digi_acceleport.c
+++ b/drivers/usb/serial/digi_acceleport.c
@@ -604,7 +604,9 @@
static void digi_wakeup_write(struct usb_serial_port *port)
{
- tty_wakeup(port->port.tty);
+ struct tty_struct *tty = tty_port_tty_get(&port->port);
+ tty_wakeup(tty);
+ tty_kref_put(tty);
}
@@ -1668,7 +1670,7 @@
{
struct usb_serial_port *port = urb->context;
- struct tty_struct *tty = port->port.tty;
+ struct tty_struct *tty;
struct digi_port *priv = usb_get_serial_port_data(port);
int opcode = ((unsigned char *)urb->transfer_buffer)[0];
int len = ((unsigned char *)urb->transfer_buffer)[1];
@@ -1692,6 +1694,7 @@
return -1;
}
+ tty = tty_port_tty_get(&port->port);
spin_lock(&priv->dp_port_lock);
/* check for throttle; if set, do not resubmit read urb */
@@ -1735,6 +1738,7 @@
}
}
spin_unlock(&priv->dp_port_lock);
+ tty_kref_put(tty);
if (opcode == DIGI_CMD_RECEIVE_DISABLE)
dbg("%s: got RECEIVE_DISABLE", __func__);
@@ -1760,6 +1764,7 @@
struct usb_serial_port *port = urb->context;
struct usb_serial *serial = port->serial;
+ struct tty_struct *tty;
struct digi_port *priv = usb_get_serial_port_data(port);
int opcode, line, status, val;
int i;
@@ -1787,10 +1792,11 @@
if (priv == NULL)
return -1;
+ tty = tty_port_tty_get(&port->port);
rts = 0;
if (port->port.count)
- rts = port->port.tty->termios->c_cflag & CRTSCTS;
-
+ rts = tty->termios->c_cflag & CRTSCTS;
+
if (opcode == DIGI_CMD_READ_INPUT_SIGNALS) {
spin_lock(&priv->dp_port_lock);
/* convert from digi flags to termiox flags */
@@ -1798,14 +1804,14 @@
priv->dp_modem_signals |= TIOCM_CTS;
/* port must be open to use tty struct */
if (rts) {
- port->port.tty->hw_stopped = 0;
+ tty->hw_stopped = 0;
digi_wakeup_write(port);
}
} else {
priv->dp_modem_signals &= ~TIOCM_CTS;
/* port must be open to use tty struct */
if (rts)
- port->port.tty->hw_stopped = 1;
+ tty->hw_stopped = 1;
}
if (val & DIGI_READ_INPUT_SIGNALS_DSR)
priv->dp_modem_signals |= TIOCM_DSR;
@@ -1830,6 +1836,7 @@
} else if (opcode == DIGI_CMD_IFLUSH_FIFO) {
wake_up_interruptible(&priv->dp_flush_wait);
}
+ tty_kref_put(tty);
}
return 0;
diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c
index a6ab5b5..1072e84 100644
--- a/drivers/usb/serial/empeg.c
+++ b/drivers/usb/serial/empeg.c
@@ -33,9 +33,8 @@
* Moved MOD_DEC_USE_COUNT to end of empeg_close().
*
* (12/03/2000) gb
- * Added port->port.tty->ldisc.set_termios(port->port.tty, NULL) to
- * empeg_open(). This notifies the tty driver that the termios have
- * changed.
+ * Added tty->ldisc.set_termios(port, tty, NULL) to empeg_open().
+ * This notifies the tty driver that the termios have changed.
*
* (11/13/2000) gb
* Moved tty->low_latency = 1 from empeg_read_bulk_callback() to
@@ -354,7 +353,7 @@
usb_serial_debug_data(debug, &port->dev, __func__,
urb->actual_length, data);
- tty = port->port.tty;
+ tty = tty_port_tty_get(&port->port);
if (urb->actual_length) {
tty_buffer_request_room(tty, urb->actual_length);
@@ -362,6 +361,7 @@
tty_flip_buffer_push(tty);
bytes_in += urb->actual_length;
}
+ tty_kref_put(tty);
/* Continue trying to always read */
usb_fill_bulk_urb(
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 3dc93b5..c2ac129 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -860,7 +860,7 @@
kfree(buf);
if (rv < 0) {
- err("%s Error from MODEM_CTRL urb: DTR %s, RTS %s",
+ dbg("%s Error from MODEM_CTRL urb: DTR %s, RTS %s",
__func__,
(set & TIOCM_DTR) ? "HIGH" :
(clear & TIOCM_DTR) ? "LOW" : "unchanged",
@@ -1808,7 +1808,7 @@
if (port->port.count <= 0)
return;
- tty = port->port.tty;
+ tty = tty_port_tty_get(&port->port);
if (!tty) {
dbg("%s - bad tty pointer - exiting", __func__);
return;
@@ -1817,7 +1817,7 @@
priv = usb_get_serial_port_data(port);
if (!priv) {
dbg("%s - bad port private data pointer - exiting", __func__);
- return;
+ goto out;
}
if (urb != port->read_urb)
@@ -1827,7 +1827,7 @@
/* This will happen at close every time so it is a dbg not an
err */
dbg("(this is ok on close) nonzero read bulk status received: %d", status);
- return;
+ goto out;
}
/* count data bytes, but not status bytes */
@@ -1838,7 +1838,8 @@
spin_unlock_irqrestore(&priv->rx_lock, flags);
ftdi_process_read(&priv->rx_work.work);
-
+out:
+ tty_kref_put(tty);
} /* ftdi_read_bulk_callback */
@@ -1863,7 +1864,7 @@
if (port->port.count <= 0)
return;
- tty = port->port.tty;
+ tty = tty_port_tty_get(&port->port);
if (!tty) {
dbg("%s - bad tty pointer - exiting", __func__);
return;
@@ -1872,13 +1873,13 @@
priv = usb_get_serial_port_data(port);
if (!priv) {
dbg("%s - bad port private data pointer - exiting", __func__);
- return;
+ goto out;
}
urb = port->read_urb;
if (!urb) {
dbg("%s - bad read_urb pointer - exiting", __func__);
- return;
+ goto out;
}
data = urb->transfer_buffer;
@@ -2020,7 +2021,7 @@
schedule_delayed_work(&priv->rx_work, 1);
else
dbg("%s - port is closed", __func__);
- return;
+ goto out;
}
/* urb is completely processed */
@@ -2041,6 +2042,8 @@
err("%s - failed resubmitting read urb, error %d",
__func__, result);
}
+out:
+ tty_kref_put(tty);
} /* ftdi_process_read */
@@ -2256,7 +2259,7 @@
0, 0,
buf, 1, WDR_TIMEOUT);
if (ret < 0) {
- err("%s Could not get modem status of device - err: %d", __func__,
+ dbg("%s Could not get modem status of device - err: %d", __func__,
ret);
return ret;
}
@@ -2275,7 +2278,7 @@
0, priv->interface,
buf, 2, WDR_TIMEOUT);
if (ret < 0) {
- err("%s Could not get modem status of device - err: %d", __func__,
+ dbg("%s Could not get modem status of device - err: %d", __func__,
ret);
return ret;
}
diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c
index d953820..2ad0569 100644
--- a/drivers/usb/serial/garmin_gps.c
+++ b/drivers/usb/serial/garmin_gps.c
@@ -276,7 +276,7 @@
static void send_to_tty(struct usb_serial_port *port,
char *data, unsigned int actual_length)
{
- struct tty_struct *tty = port->port.tty;
+ struct tty_struct *tty = tty_port_tty_get(&port->port);
if (tty && actual_length) {
@@ -287,6 +287,7 @@
tty_insert_flip_string(tty, data, actual_length);
tty_flip_buffer_push(tty);
}
+ tty_kref_put(tty);
}
diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c
index fe84c88..814909f 100644
--- a/drivers/usb/serial/generic.c
+++ b/drivers/usb/serial/generic.c
@@ -330,7 +330,7 @@
static void flush_and_resubmit_read_urb(struct usb_serial_port *port)
{
struct urb *urb = port->read_urb;
- struct tty_struct *tty = port->port.tty;
+ struct tty_struct *tty = tty_port_tty_get(&port->port);
int room;
/* Push data to tty */
@@ -341,6 +341,7 @@
tty_flip_buffer_push(tty);
}
}
+ tty_kref_put(tty);
resubmit_read_urb(port, GFP_ATOMIC);
}
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index bfa508d..611f97f 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -600,6 +600,7 @@
struct edgeport_serial *edge_serial = urb->context;
struct edgeport_port *edge_port;
struct usb_serial_port *port;
+ struct tty_struct *tty;
unsigned char *data = urb->transfer_buffer;
int length = urb->actual_length;
int bytes_avail;
@@ -675,9 +676,12 @@
/* tell the tty driver that something
has changed */
- if (edge_port->port->port.tty)
- tty_wakeup(edge_port->port->port.tty);
-
+ tty = tty_port_tty_get(
+ &edge_port->port->port);
+ if (tty) {
+ tty_wakeup(tty);
+ tty_kref_put(tty);
+ }
/* Since we have more credit, check
if more data can be sent */
send_more_port_data(edge_serial,
@@ -778,13 +782,14 @@
__func__, status);
}
- tty = edge_port->port->port.tty;
+ tty = tty_port_tty_get(&edge_port->port->port);
if (tty && edge_port->open) {
/* let the tty driver wakeup if it has a special
write_wakeup function */
tty_wakeup(tty);
}
+ tty_kref_put(tty);
/* Release the Write URB */
edge_port->write_in_progress = false;
@@ -826,11 +831,12 @@
}
/* Get pointer to tty */
- tty = edge_port->port->port.tty;
+ tty = tty_port_tty_get(&edge_port->port->port);
/* tell the tty driver that something has changed */
if (tty && edge_port->open)
tty_wakeup(tty);
+ tty_kref_put(tty);
/* we have completed the command */
edge_port->commandPending = false;
@@ -1932,11 +1938,13 @@
edge_serial->rxPort];
edge_port = usb_get_serial_port_data(port);
if (edge_port->open) {
- tty = edge_port->port->port.tty;
+ tty = tty_port_tty_get(
+ &edge_port->port->port);
if (tty) {
dbg("%s - Sending %d bytes to TTY for port %d",
__func__, rxLen, edge_serial->rxPort);
edge_tty_recv(&edge_serial->serial->dev->dev, tty, buffer, rxLen);
+ tty_kref_put(tty);
}
edge_port->icount.rx += rxLen;
}
@@ -1971,6 +1979,7 @@
{
struct usb_serial_port *port;
struct edgeport_port *edge_port;
+ struct tty_struct *tty;
__u8 code = edge_serial->rxStatusCode;
/* switch the port pointer to the one being currently talked about */
@@ -2020,10 +2029,12 @@
/* send the current line settings to the port so we are
in sync with any further termios calls */
- /* FIXME: locking on tty */
- if (edge_port->port->port.tty)
- change_port_settings(edge_port->port->port.tty,
- edge_port, edge_port->port->port.tty->termios);
+ tty = tty_port_tty_get(&edge_port->port->port);
+ if (tty) {
+ change_port_settings(tty,
+ edge_port, tty->termios);
+ tty_kref_put(tty);
+ }
/* we have completed the open */
edge_port->openPending = false;
@@ -2163,10 +2174,14 @@
}
/* Place LSR data byte into Rx buffer */
- if (lsrData && edge_port->port->port.tty)
- edge_tty_recv(&edge_port->port->dev,
- edge_port->port->port.tty, &data, 1);
-
+ if (lsrData) {
+ struct tty_struct *tty =
+ tty_port_tty_get(&edge_port->port->port);
+ if (tty) {
+ edge_tty_recv(&edge_port->port->dev, tty, &data, 1);
+ tty_kref_put(tty);
+ }
+ }
/* update input line counters */
icount = &edge_port->icount;
if (newLsr & LSR_BREAK)
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
index cb4c543..541dd8e 100644
--- a/drivers/usb/serial/io_ti.c
+++ b/drivers/usb/serial/io_ti.c
@@ -572,7 +572,7 @@
int flush)
{
int baud_rate;
- struct tty_struct *tty = port->port->port.tty;
+ struct tty_struct *tty = tty_port_tty_get(&port->port->port);
wait_queue_t wait;
unsigned long flags;
@@ -599,6 +599,7 @@
if (flush)
edge_buf_clear(port->ep_out_buf);
spin_unlock_irqrestore(&port->ep_lock, flags);
+ tty_kref_put(tty);
/* wait for data to drain from the device */
timeout += jiffies;
@@ -1554,7 +1555,7 @@
/* Save the new modem status */
edge_port->shadow_msr = msr & 0xf0;
- tty = edge_port->port->port.tty;
+ tty = tty_port_tty_get(&edge_port->port->port);
/* handle CTS flow control */
if (tty && C_CRTSCTS(tty)) {
if (msr & EDGEPORT_MSR_CTS) {
@@ -1564,6 +1565,7 @@
tty->hw_stopped = 1;
}
}
+ tty_kref_put(tty);
return;
}
@@ -1574,6 +1576,7 @@
struct async_icount *icount;
__u8 new_lsr = (__u8)(lsr & (__u8)(LSR_OVER_ERR | LSR_PAR_ERR |
LSR_FRM_ERR | LSR_BREAK));
+ struct tty_struct *tty;
dbg("%s - %02x", __func__, new_lsr);
@@ -1587,8 +1590,13 @@
new_lsr &= (__u8)(LSR_OVER_ERR | LSR_BREAK);
/* Place LSR data byte into Rx buffer */
- if (lsr_data && edge_port->port->port.tty)
- edge_tty_recv(&edge_port->port->dev, edge_port->port->port.tty, &data, 1);
+ if (lsr_data) {
+ tty = tty_port_tty_get(&edge_port->port->port);
+ if (tty) {
+ edge_tty_recv(&edge_port->port->dev, tty, &data, 1);
+ tty_kref_put(tty);
+ }
+ }
/* update input line counters */
icount = &edge_port->icount;
@@ -1749,7 +1757,7 @@
++data;
}
- tty = edge_port->port->port.tty;
+ tty = tty_port_tty_get(&edge_port->port->port);
if (tty && urb->actual_length) {
usb_serial_debug_data(debug, &edge_port->port->dev,
__func__, urb->actual_length, data);
@@ -1761,6 +1769,7 @@
urb->actual_length);
edge_port->icount.rx += urb->actual_length;
}
+ tty_kref_put(tty);
exit:
/* continue read unless stopped */
@@ -1796,6 +1805,7 @@
struct usb_serial_port *port = urb->context;
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
int status = urb->status;
+ struct tty_struct *tty;
dbg("%s - port %d", __func__, port->number);
@@ -1818,7 +1828,9 @@
}
/* send any buffered data */
- edge_send(port->port.tty);
+ tty = tty_port_tty_get(&port->port);
+ edge_send(tty);
+ tty_kref_put(tty);
}
static int edge_open(struct tty_struct *tty,
@@ -1876,7 +1888,7 @@
/* set up the port settings */
if (tty)
- edge_set_termios(tty, port, port->port.tty->termios);
+ edge_set_termios(tty, port, tty->termios);
/* open up the port */
diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c
index cd9a2e13..2affa9c 100644
--- a/drivers/usb/serial/ipaq.c
+++ b/drivers/usb/serial/ipaq.c
@@ -764,13 +764,14 @@
usb_serial_debug_data(debug, &port->dev, __func__,
urb->actual_length, data);
- tty = port->port.tty;
+ tty = tty_port_tty_get(&port->port);
if (tty && urb->actual_length) {
tty_buffer_request_room(tty, urb->actual_length);
tty_insert_flip_string(tty, data, urb->actual_length);
tty_flip_buffer_push(tty);
bytes_in += urb->actual_length;
}
+ tty_kref_put(tty);
/* Continue trying to always read */
usb_fill_bulk_urb(port->read_urb, port->serial->dev,
diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c
index a842025..480cac2 100644
--- a/drivers/usb/serial/ipw.c
+++ b/drivers/usb/serial/ipw.c
@@ -170,12 +170,13 @@
usb_serial_debug_data(debug, &port->dev, __func__,
urb->actual_length, data);
- tty = port->port.tty;
+ tty = tty_port_tty_get(&port->port);
if (tty && urb->actual_length) {
tty_buffer_request_room(tty, urb->actual_length);
tty_insert_flip_string(tty, data, urb->actual_length);
tty_flip_buffer_push(tty);
}
+ tty_kref_put(tty);
/* Continue trying to always read */
usb_fill_bulk_urb(port->read_urb, port->serial->dev,
diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c
index e59155c..45d4043 100644
--- a/drivers/usb/serial/ir-usb.c
+++ b/drivers/usb/serial/ir-usb.c
@@ -465,11 +465,12 @@
ir_baud = *data & 0x0f;
usb_serial_debug_data(debug, &port->dev, __func__,
urb->actual_length, data);
- tty = port->port.tty;
+ tty = tty_port_tty_get(&port->port);
if (tty_buffer_request_room(tty, urb->actual_length - 1)) {
tty_insert_flip_string(tty, data+1, urb->actual_length - 1);
tty_flip_buffer_push(tty);
}
+ tty_kref_put(tty);
/*
* No break here.
diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c
index ddff37f..53710aa 100644
--- a/drivers/usb/serial/iuu_phoenix.c
+++ b/drivers/usb/serial/iuu_phoenix.c
@@ -629,13 +629,14 @@
}
dbg("%s - %i chars to write", __func__, urb->actual_length);
- tty = port->port.tty;
+ tty = tty_port_tty_get(&port->port);
if (data == NULL)
dbg("%s - data is NULL !!!", __func__);
if (tty && urb->actual_length && data) {
tty_insert_flip_string(tty, data, urb->actual_length);
tty_flip_buffer_push(tty);
}
+ tty_kref_put(tty);
iuu_led_activity_on(urb);
}
diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c
index 704716f..15447af 100644
--- a/drivers/usb/serial/keyspan.c
+++ b/drivers/usb/serial/keyspan.c
@@ -430,7 +430,7 @@
}
port = urb->context;
- tty = port->port.tty;
+ tty = tty_port_tty_get(&port->port);
if (tty && urb->actual_length) {
/* 0x80 bit is error flag */
if ((data[0] & 0x80) == 0) {
@@ -459,6 +459,7 @@
}
tty_flip_buffer_push(tty);
}
+ tty_kref_put(tty);
/* Resubmit urb so we continue receiving */
urb->dev = port->serial->dev;
@@ -513,6 +514,7 @@
struct usb_serial *serial;
struct usb_serial_port *port;
struct keyspan_port_private *p_priv;
+ struct tty_struct *tty;
int old_dcd_state, err;
int status = urb->status;
@@ -553,12 +555,11 @@
p_priv->dcd_state = ((msg->gpia_dcd) ? 1 : 0);
p_priv->ri_state = ((msg->ri) ? 1 : 0);
- if (port->port.tty && !C_CLOCAL(port->port.tty)
- && old_dcd_state != p_priv->dcd_state) {
- if (old_dcd_state)
- tty_hangup(port->port.tty);
- /* else */
- /* wake_up_interruptible(&p_priv->open_wait); */
+ if (old_dcd_state != p_priv->dcd_state) {
+ tty = tty_port_tty_get(&port->port);
+ if (tty && !C_CLOCAL(tty))
+ tty_hangup(tty);
+ tty_kref_put(tty);
}
/* Resubmit urb so we continue receiving */
@@ -604,11 +605,12 @@
p_priv = usb_get_serial_port_data(port);
data = urb->transfer_buffer;
- tty = port->port.tty;
- if (urb->actual_length) {
+ tty =tty_port_tty_get(&port->port);
+ if (tty && urb->actual_length) {
tty_insert_flip_string(tty, data, urb->actual_length);
tty_flip_buffer_push(tty);
}
+ tty_kref_put(tty);
/* Resubmit urb so we continue receiving */
urb->dev = port->serial->dev;
@@ -652,6 +654,7 @@
struct usb_serial *serial;
struct usb_serial_port *port;
struct keyspan_port_private *p_priv;
+ struct tty_struct *tty;
int old_dcd_state;
int status = urb->status;
@@ -689,12 +692,11 @@
p_priv->dcd_state = ((msg->dcd) ? 1 : 0);
p_priv->ri_state = ((msg->ri) ? 1 : 0);
- if (port->port.tty && !C_CLOCAL(port->port.tty)
- && old_dcd_state != p_priv->dcd_state) {
- if (old_dcd_state)
- tty_hangup(port->port.tty);
- /* else */
- /* wake_up_interruptible(&p_priv->open_wait); */
+ if( old_dcd_state != p_priv->dcd_state && old_dcd_state) {
+ tty = tty_port_tty_get(&port->port);
+ if (tty && !C_CLOCAL(tty))
+ tty_hangup(tty);
+ tty_kref_put(tty);
}
/* Resubmit urb so we continue receiving */
@@ -785,12 +787,11 @@
p_priv->dcd_state = ((msg->dcd) ? 1 : 0);
p_priv->ri_state = ((msg->ri) ? 1 : 0);
- if (port->port.tty && !C_CLOCAL(port->port.tty)
- && old_dcd_state != p_priv->dcd_state) {
- if (old_dcd_state)
- tty_hangup(port->port.tty);
- /* else */
- /* wake_up_interruptible(&p_priv->open_wait); */
+ if (old_dcd_state != p_priv->dcd_state && old_dcd_state) {
+ struct tty_struct *tty = tty_port_tty_get(&port->port);
+ if (tty && !C_CLOCAL(tty))
+ tty_hangup(tty);
+ tty_kref_put(tty);
}
/* Resubmit urb so we continue receiving */
@@ -827,7 +828,7 @@
}
port = urb->context;
- tty = port->port.tty;
+ tty = tty_port_tty_get(&port->port);
if (tty && urb->actual_length) {
/* 0x80 bit is error flag */
if ((data[0] & 0x80) == 0) {
@@ -850,6 +851,7 @@
}
tty_flip_buffer_push(tty);
}
+ tty_kref_put(tty);
/* Resubmit urb so we continue receiving */
urb->dev = port->serial->dev;
@@ -893,7 +895,7 @@
return;
}
port = serial->port[data[i++]];
- tty = port->port.tty;
+ tty = tty_port_tty_get(&port->port);
len = data[i++];
/* 0x80 bit is error flag */
@@ -927,6 +929,7 @@
}
if (port->port.count)
tty_flip_buffer_push(tty);
+ tty_kref_put(tty);
}
}
@@ -967,8 +970,8 @@
port = urb->context;
p_priv = usb_get_serial_port_data(port);
- tty = port->port.tty;
if (urb->actual_length) {
+ tty = tty_port_tty_get(&port->port);
/* if current mode is DMA, looks like usa28 format
otherwise looks like usa26 data format */
@@ -1004,6 +1007,7 @@
}
}
tty_flip_buffer_push(tty);
+ tty_kref_put(tty);
}
/* Resubmit urb so we continue receiving */
@@ -1025,6 +1029,7 @@
struct usb_serial *serial;
struct usb_serial_port *port;
struct keyspan_port_private *p_priv;
+ struct tty_struct *tty;
int old_dcd_state, err;
int status = urb->status;
@@ -1053,12 +1058,11 @@
p_priv->dcd_state = ((msg->dcd) ? 1 : 0);
p_priv->ri_state = ((msg->ri) ? 1 : 0);
- if (port->port.tty && !C_CLOCAL(port->port.tty)
- && old_dcd_state != p_priv->dcd_state) {
- if (old_dcd_state)
- tty_hangup(port->port.tty);
- /* else */
- /* wake_up_interruptible(&p_priv->open_wait); */
+ if (old_dcd_state != p_priv->dcd_state && old_dcd_state) {
+ tty = tty_port_tty_get(&port->port);
+ if (tty && !C_CLOCAL(tty))
+ tty_hangup(tty);
+ tty_kref_put(tty);
}
/* Resubmit urb so we continue receiving */
@@ -1130,12 +1134,11 @@
p_priv->cts_state = ((msg->hskia_cts) ? 1 : 0);
p_priv->dcd_state = ((msg->gpia_dcd) ? 1 : 0);
- if (port->port.tty && !C_CLOCAL(port->port.tty)
- && old_dcd_state != p_priv->dcd_state) {
- if (old_dcd_state)
- tty_hangup(port->port.tty);
- /* else */
- /* wake_up_interruptible(&p_priv->open_wait); */
+ if (old_dcd_state != p_priv->dcd_state && old_dcd_state) {
+ struct tty_struct *tty = tty_port_tty_get(&port->port);
+ if (tty && !C_CLOCAL(tty))
+ tty_hangup(tty);
+ tty_kref_put(tty);
}
/* Resubmit urb so we continue receiving */
@@ -1332,7 +1335,7 @@
stop_urb(p_priv->out_urbs[i]);
}
}
- port->port.tty = NULL;
+ tty_port_tty_set(&port->port, NULL);
}
/* download the firmware to a pre-renumeration device */
diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
index 040040a..99e9a14 100644
--- a/drivers/usb/serial/keyspan_pda.c
+++ b/drivers/usb/serial/keyspan_pda.c
@@ -172,8 +172,9 @@
struct keyspan_pda_private *priv =
container_of(work, struct keyspan_pda_private, wakeup_work);
struct usb_serial_port *port = priv->port;
-
- tty_wakeup(port->port.tty);
+ struct tty_struct *tty = tty_port_tty_get(&port->port);
+ tty_wakeup(tty);
+ tty_kref_put(tty);
}
static void keyspan_pda_request_unthrottle(struct work_struct *work)
@@ -205,7 +206,7 @@
static void keyspan_pda_rx_interrupt(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
- struct tty_struct *tty = port->port.tty;
+ struct tty_struct *tty = tty_port_tty_get(&port->port);
unsigned char *data = urb->transfer_buffer;
int retval;
int status = urb->status;
@@ -222,7 +223,7 @@
/* this urb is terminated, clean up */
dbg("%s - urb shutting down with status: %d",
__func__, status);
- return;
+ goto out;
default:
dbg("%s - nonzero urb status received: %d",
__func__, status);
@@ -261,8 +262,11 @@
exit:
retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval)
- err("%s - usb_submit_urb failed with result %d",
- __func__, retval);
+ dev_err(&port->dev,
+ "%s - usb_submit_urb failed with result %d",
+ __func__, retval);
+out:
+ tty_kref_put(tty);
}
diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c
index b84dddc..ff3a07f 100644
--- a/drivers/usb/serial/kl5kusb105.c
+++ b/drivers/usb/serial/kl5kusb105.c
@@ -658,7 +658,7 @@
} else {
int bytes_sent = ((__u8 *) data)[0] +
((unsigned int) ((__u8 *) data)[1] << 8);
- tty = port->port.tty;
+ tty = tty_port_tty_get(&port->port);
/* we should immediately resubmit the URB, before attempting
* to pass the data on to the tty layer. But that needs locking
* against re-entry an then mixed-up data because of
@@ -679,6 +679,7 @@
tty_buffer_request_room(tty, bytes_sent);
tty_insert_flip_string(tty, data + 2, bytes_sent);
tty_flip_buffer_push(tty);
+ tty_kref_put(tty);
/* again lockless, but debug info only */
priv->bytes_in += bytes_sent;
diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c
index deba28ec..cfcf37c 100644
--- a/drivers/usb/serial/kobil_sct.c
+++ b/drivers/usb/serial/kobil_sct.c
@@ -383,7 +383,7 @@
return;
}
- tty = port->port.tty;
+ tty = tty_port_tty_get(&port->port);
if (urb->actual_length) {
/* BEGIN DEBUG */
@@ -405,6 +405,7 @@
tty_insert_flip_string(tty, data, urb->actual_length);
tty_flip_buffer_push(tty);
}
+ tty_kref_put(tty);
/* someone sets the dev to 0 if the close method has been called */
port->interrupt_in_urb->dev = port->serial->dev;
diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c
index 0ded8bd..9b2cef8 100644
--- a/drivers/usb/serial/mct_u232.c
+++ b/drivers/usb/serial/mct_u232.c
@@ -563,10 +563,11 @@
* Work-a-round: handle the 'usual' bulk-in pipe here
*/
if (urb->transfer_buffer_length > 2) {
- tty = port->port.tty;
+ tty = tty_port_tty_get(&port->port);
if (urb->actual_length) {
tty_insert_flip_string(tty, data, urb->actual_length);
tty_flip_buffer_push(tty);
+ tty_kref_put(tty);
}
goto exit;
}
@@ -591,7 +592,7 @@
* to look in to this before committing any code.
*/
if (priv->last_lsr & MCT_U232_LSR_ERR) {
- tty = port->port.tty;
+ tty = tty_port_tty_get(&port->port);
/* Overrun Error */
if (priv->last_lsr & MCT_U232_LSR_OE) {
}
@@ -604,6 +605,7 @@
/* Break Indicator */
if (priv->last_lsr & MCT_U232_LSR_BI) {
}
+ tty_kref_put(tty);
}
#endif
spin_unlock_irqrestore(&priv->lock, flags);
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index 7c4917d..7b538ca 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -216,12 +216,13 @@
data = urb->transfer_buffer;
- tty = port->port.tty;
+ tty = tty_port_tty_get(&port->port);
if (tty && urb->actual_length) {
tty_buffer_request_room(tty, urb->actual_length);
tty_insert_flip_string(tty, data, urb->actual_length);
tty_flip_buffer_push(tty);
}
+ tty_kref_put(tty);
if (!port->read_urb) {
dbg("URB KILLED !!!");
@@ -262,10 +263,11 @@
dbg("Entering .........");
- tty = mos7720_port->port->port.tty;
+ tty = tty_port_tty_get(&mos7720_port->port->port);
if (tty && mos7720_port->open)
tty_wakeup(tty);
+ tty_kref_put(tty);
}
/*
@@ -1267,29 +1269,6 @@
return 0;
}
-/*
- * get_number_bytes_avail - get number of bytes available
- *
- * Purpose: Let user call ioctl to get the count of number of bytes available.
- */
-static int get_number_bytes_avail(struct moschip_port *mos7720_port,
- unsigned int __user *value)
-{
- unsigned int result = 0;
- struct tty_struct *tty = mos7720_port->port->port.tty;
-
- if (!tty)
- return -ENOIOCTLCMD;
-
- result = tty->read_cnt;
-
- dbg("%s(%d) = %d", __func__, mos7720_port->port->number, result);
- if (copy_to_user(value, &result, sizeof(int)))
- return -EFAULT;
-
- return -ENOIOCTLCMD;
-}
-
static int set_modem_info(struct moschip_port *mos7720_port, unsigned int cmd,
unsigned int __user *value)
{
@@ -1409,13 +1388,6 @@
dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd);
switch (cmd) {
- case TIOCINQ:
- /* return number of bytes available */
- dbg("%s (%d) TIOCINQ", __func__, port->number);
- return get_number_bytes_avail(mos7720_port,
- (unsigned int __user *)arg);
- break;
-
case TIOCSERGETLSR:
dbg("%s (%d) TIOCSERGETLSR", __func__, port->number);
return get_lsr_info(tty, mos7720_port,
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index 09d8206..60543d7 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -709,12 +709,13 @@
dbg("%s", "Entering ........... \n");
if (urb->actual_length) {
- tty = mos7840_port->port->port.tty;
+ tty = tty_port_tty_get(&mos7840_port->port->port);
if (tty) {
tty_buffer_request_room(tty, urb->actual_length);
tty_insert_flip_string(tty, data, urb->actual_length);
dbg(" %s \n", data);
tty_flip_buffer_push(tty);
+ tty_kref_put(tty);
}
mos7840_port->icount.rx += urb->actual_length;
smp_wmb();
@@ -773,10 +774,10 @@
dbg("%s \n", "Entering .........");
- tty = mos7840_port->port->port.tty;
-
+ tty = tty_port_tty_get(&mos7840_port->port->port);
if (tty && mos7840_port->open)
tty_wakeup(tty);
+ tty_kref_put(tty);
}
diff --git a/drivers/usb/serial/navman.c b/drivers/usb/serial/navman.c
index d673653..bcdcbb8 100644
--- a/drivers/usb/serial/navman.c
+++ b/drivers/usb/serial/navman.c
@@ -64,12 +64,13 @@
usb_serial_debug_data(debug, &port->dev, __func__,
urb->actual_length, data);
- tty = port->port.tty;
+ tty = tty_port_tty_get(&port->port);
if (tty && urb->actual_length) {
tty_buffer_request_room(tty, urb->actual_length);
tty_insert_flip_string(tty, data, urb->actual_length);
tty_flip_buffer_push(tty);
}
+ tty_kref_put(tty);
exit:
result = usb_submit_urb(urb, GFP_ATOMIC);
diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c
index ae8e227..c4d70b0 100644
--- a/drivers/usb/serial/omninet.c
+++ b/drivers/usb/serial/omninet.c
@@ -172,7 +172,7 @@
dbg("%s - port %d", __func__, port->number);
wport = serial->port[1];
- wport->port.tty = tty; /* FIXME */
+ tty_port_tty_set(&wport->port, tty);
/* Start reading from the device */
usb_fill_bulk_urb(port->read_urb, serial->dev,
@@ -229,9 +229,11 @@
}
if (urb->actual_length && header->oh_len) {
- tty_insert_flip_string(port->port.tty,
- data + OMNINET_DATAOFFSET, header->oh_len);
- tty_flip_buffer_push(port->port.tty);
+ struct tty_struct *tty = tty_port_tty_get(&port->port);
+ tty_insert_flip_string(tty, data + OMNINET_DATAOFFSET,
+ header->oh_len);
+ tty_flip_buffer_push(tty);
+ tty_kref_put(tty);
}
/* Continue trying to always read */
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 73f8277..6b1727e 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -571,14 +571,14 @@
dbg("%s: nonzero status: %d on endpoint %02x.",
__func__, status, endpoint);
} else {
- tty = port->port.tty;
+ tty = tty_port_tty_get(&port->port);
if (urb->actual_length) {
tty_buffer_request_room(tty, urb->actual_length);
tty_insert_flip_string(tty, data, urb->actual_length);
tty_flip_buffer_push(tty);
- } else {
+ } else
dbg("%s: empty read urb received", __func__);
- }
+ tty_kref_put(tty);
/* Resubmit urb so we continue receiving */
if (port->port.count && status != -ESHUTDOWN) {
@@ -647,9 +647,13 @@
portdata->dsr_state = ((signals & 0x02) ? 1 : 0);
portdata->ri_state = ((signals & 0x08) ? 1 : 0);
- if (port->port.tty && !C_CLOCAL(port->port.tty) &&
- old_dcd_state && !portdata->dcd_state)
- tty_hangup(port->port.tty);
+ if (old_dcd_state && !portdata->dcd_state) {
+ struct tty_struct *tty =
+ tty_port_tty_get(&port->port);
+ if (tty && !C_CLOCAL(tty))
+ tty_hangup(tty);
+ tty_kref_put(tty);
+ }
} else {
dbg("%s: type %x req %x", __func__,
req_pkt->bRequestType, req_pkt->bRequest);
@@ -793,7 +797,7 @@
for (i = 0; i < N_OUT_URB; i++)
usb_kill_urb(portdata->out_urbs[i]);
}
- port->port.tty = NULL; /* FIXME */
+ tty_port_tty_set(&port->port, NULL);
}
/* Helper functions used by option_setup_urbs */
diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c
index 81db571..ba551f0 100644
--- a/drivers/usb/serial/oti6858.c
+++ b/drivers/usb/serial/oti6858.c
@@ -224,10 +224,6 @@
struct usb_serial_port *port; /* USB port with which associated */
};
-#undef dbg
-/* #define dbg(format, arg...) printk(KERN_INFO "%s: " format "\n", __FILE__, ## arg) */
-#define dbg(format, arg...) printk(KERN_INFO "" format "\n", ## arg)
-
static void setup_line(struct work_struct *work)
{
struct oti6858_private *priv = container_of(work,
@@ -1002,11 +998,12 @@
return;
}
- tty = port->port.tty;
+ tty = tty_port_tty_get(&port->port);
if (tty != NULL && urb->actual_length > 0) {
tty_insert_flip_string(tty, data, urb->actual_length);
tty_flip_buffer_push(tty);
}
+ tty_kref_put(tty);
/* schedule the interrupt urb if we are still open */
if (port->port.count != 0) {
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index 1ede144..9084378 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -154,7 +154,6 @@
wait_queue_head_t delta_msr_wait;
u8 line_control;
u8 line_status;
- u8 termios_initialized;
enum pl2303_type type;
};
@@ -526,16 +525,6 @@
dbg("%s - port %d", __func__, port->number);
- spin_lock_irqsave(&priv->lock, flags);
- if (!priv->termios_initialized) {
- *(tty->termios) = tty_std_termios;
- tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
- tty->termios->c_ispeed = 9600;
- tty->termios->c_ospeed = 9600;
- priv->termios_initialized = 1;
- }
- spin_unlock_irqrestore(&priv->lock, flags);
-
/* The PL2303 is reported to lose bytes if you change
serial settings even to the same values as before. Thus
we actually need to filter in this specific case */
@@ -1057,7 +1046,7 @@
tty_flag = TTY_FRAME;
dbg("%s - tty_flag = %d", __func__, tty_flag);
- tty = port->port.tty;
+ tty = tty_port_tty_get(&port->port);
if (tty && urb->actual_length) {
tty_buffer_request_room(tty, urb->actual_length + 1);
/* overrun is special, not associated with a char */
@@ -1067,7 +1056,7 @@
tty_insert_flip_char(tty, data[i], tty_flag);
tty_flip_buffer_push(tty);
}
-
+ tty_kref_put(tty);
/* Schedule the next read _if_ we are still open */
if (port->port.count) {
urb->dev = port->serial->dev;
diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c
index def52d0..72903ac 100644
--- a/drivers/usb/serial/safe_serial.c
+++ b/drivers/usb/serial/safe_serial.c
@@ -217,6 +217,7 @@
struct usb_serial_port *port = urb->context;
unsigned char *data = urb->transfer_buffer;
unsigned char length = urb->actual_length;
+ struct tty_struct *tty;
int result;
int status = urb->status;
@@ -242,6 +243,7 @@
printk("\n");
}
#endif
+ tty = tty_port_tty_get(&port->port);
if (safe) {
__u16 fcs;
fcs = fcs_compute10(data, length, CRC10_INITFCS);
@@ -250,9 +252,9 @@
if (actual_length <= (length - 2)) {
info("%s - actual: %d", __func__,
actual_length);
- tty_insert_flip_string(port->port.tty,
+ tty_insert_flip_string(tty,
data, actual_length);
- tty_flip_buffer_push(port->port.tty);
+ tty_flip_buffer_push(tty);
} else {
err("%s - inconsistent lengths %d:%d",
__func__, actual_length, length);
@@ -261,9 +263,10 @@
err("%s - bad CRC %x", __func__, fcs);
}
} else {
- tty_insert_flip_string(port->port.tty, data, length);
- tty_flip_buffer_push(port->port.tty);
+ tty_insert_flip_string(tty, data, length);
+ tty_flip_buffer_push(tty);
}
+ tty_kref_put(tty);
/* Continue trying to always read */
usb_fill_bulk_urb(urb, port->serial->dev,
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c
index ea1a103..8b9eaf3 100644
--- a/drivers/usb/serial/sierra.c
+++ b/drivers/usb/serial/sierra.c
@@ -440,14 +440,14 @@
dbg("%s: nonzero status: %d on endpoint %02x.",
__func__, status, endpoint);
} else {
- tty = port->port.tty;
if (urb->actual_length) {
+ tty = tty_port_tty_get(&port->port);
tty_buffer_request_room(tty, urb->actual_length);
tty_insert_flip_string(tty, data, urb->actual_length);
tty_flip_buffer_push(tty);
- } else {
+ tty_kref_put(tty);
+ } else
dbg("%s: empty read urb received", __func__);
- }
/* Resubmit urb so we continue receiving */
if (port->port.count && status != -ESHUTDOWN) {
@@ -485,6 +485,7 @@
unsigned char signals = *((unsigned char *)
urb->transfer_buffer +
sizeof(struct usb_ctrlrequest));
+ struct tty_struct *tty;
dbg("%s: signal x%x", __func__, signals);
@@ -494,9 +495,11 @@
portdata->dsr_state = ((signals & 0x02) ? 1 : 0);
portdata->ri_state = ((signals & 0x08) ? 1 : 0);
- if (port->port.tty && !C_CLOCAL(port->port.tty) &&
+ tty = tty_port_tty_get(&port->port);
+ if (tty && !C_CLOCAL(tty) &&
old_dcd_state && !portdata->dcd_state)
- tty_hangup(port->port.tty);
+ tty_hangup(tty);
+ tty_kref_put(tty);
} else {
dbg("%s: type %x req %x", __func__,
req_pkt->bRequestType, req_pkt->bRequest);
@@ -616,8 +619,7 @@
}
usb_kill_urb(port->interrupt_in_urb);
-
- port->port.tty = NULL; /* FIXME */
+ tty_port_tty_set(&port->port, NULL);
}
static int sierra_startup(struct usb_serial *serial)
diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c
index 283cf6b..1533d6e 100644
--- a/drivers/usb/serial/spcp8x5.c
+++ b/drivers/usb/serial/spcp8x5.c
@@ -755,7 +755,7 @@
tty_flag = TTY_FRAME;
dev_dbg(&port->dev, "tty_flag = %d\n", tty_flag);
- tty = port->port.tty;
+ tty = tty_port_tty_get(&port->port);
if (tty && urb->actual_length) {
tty_buffer_request_room(tty, urb->actual_length + 1);
/* overrun is special, not associated with a char */
@@ -765,6 +765,7 @@
tty_insert_flip_char(tty, data[i], tty_flag);
tty_flip_buffer_push(tty);
}
+ tty_kref_put(tty);
/* Schedule the next read _if_ we are still open */
if (port->port.count) {
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c
index 9a3e495..c90237d 100644
--- a/drivers/usb/serial/ti_usb_3410_5052.c
+++ b/drivers/usb/serial/ti_usb_3410_5052.c
@@ -179,7 +179,7 @@
static int ti_get_lsr(struct ti_port *tport);
static int ti_get_serial_info(struct ti_port *tport,
struct serial_struct __user *ret_arg);
-static int ti_set_serial_info(struct ti_port *tport,
+static int ti_set_serial_info(struct tty_struct *tty, struct ti_port *tport,
struct serial_struct __user *new_arg);
static void ti_handle_new_msr(struct ti_port *tport, __u8 msr);
@@ -857,8 +857,8 @@
(struct serial_struct __user *)arg);
case TIOCSSERIAL:
dbg("%s - (%d) TIOCSSERIAL", __func__, port->number);
- return ti_set_serial_info(tport,
- (struct serial_struct __user *)arg);
+ return ti_set_serial_info(tty, tport,
+ (struct serial_struct __user *)arg);
case TIOCMIWAIT:
dbg("%s - (%d) TIOCMIWAIT", __func__, port->number);
cprev = tport->tp_icount;
@@ -1211,6 +1211,7 @@
struct device *dev = &urb->dev->dev;
int status = urb->status;
int retval = 0;
+ struct tty_struct *tty;
dbg("%s", __func__);
@@ -1239,20 +1240,22 @@
return;
}
- if (port->port.tty && urb->actual_length) {
+ tty = tty_port_tty_get(&port->port);
+ if (tty && urb->actual_length) {
usb_serial_debug_data(debug, dev, __func__,
urb->actual_length, urb->transfer_buffer);
if (!tport->tp_is_open)
dbg("%s - port closed, dropping data", __func__);
else
- ti_recv(&urb->dev->dev, port->port.tty,
+ ti_recv(&urb->dev->dev, tty,
urb->transfer_buffer,
urb->actual_length);
spin_lock(&tport->tp_lock);
tport->tp_icount.rx += urb->actual_length;
spin_unlock(&tport->tp_lock);
+ tty_kref_put(tty);
}
exit:
@@ -1330,7 +1333,7 @@
{
int count, result;
struct usb_serial_port *port = tport->tp_port;
- struct tty_struct *tty = port->port.tty; /* FIXME */
+ struct tty_struct *tty = tty_port_tty_get(&port->port); /* FIXME */
unsigned long flags;
@@ -1338,19 +1341,15 @@
spin_lock_irqsave(&tport->tp_lock, flags);
- if (tport->tp_write_urb_in_use) {
- spin_unlock_irqrestore(&tport->tp_lock, flags);
- return;
- }
+ if (tport->tp_write_urb_in_use)
+ goto unlock;
count = ti_buf_get(tport->tp_write_buf,
port->write_urb->transfer_buffer,
port->bulk_out_size);
- if (count == 0) {
- spin_unlock_irqrestore(&tport->tp_lock, flags);
- return;
- }
+ if (count == 0)
+ goto unlock;
tport->tp_write_urb_in_use = 1;
@@ -1380,7 +1379,13 @@
/* more room in the buffer for new writes, wakeup */
if (tty)
tty_wakeup(tty);
+ tty_kref_put(tty);
wake_up_interruptible(&tport->tp_write_wait);
+ return;
+unlock:
+ spin_unlock_irqrestore(&tport->tp_lock, flags);
+ tty_kref_put(tty);
+ return;
}
@@ -1464,20 +1469,16 @@
}
-static int ti_set_serial_info(struct ti_port *tport,
+static int ti_set_serial_info(struct tty_struct *tty, struct ti_port *tport,
struct serial_struct __user *new_arg)
{
- struct usb_serial_port *port = tport->tp_port;
struct serial_struct new_serial;
if (copy_from_user(&new_serial, new_arg, sizeof(new_serial)))
return -EFAULT;
tport->tp_flags = new_serial.flags & TI_SET_SERIAL_FLAGS;
- /* FIXME */
- if (port->port.tty)
- port->port.tty->low_latency =
- (tport->tp_flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+ tty->low_latency = (tport->tp_flags & ASYNC_LOW_LATENCY) ? 1 : 0;
tport->tp_closing_wait = new_serial.closing_wait;
return 0;
@@ -1510,7 +1511,7 @@
tport->tp_msr = msr & TI_MSR_MASK;
/* handle CTS flow control */
- tty = tport->tp_port->port.tty;
+ tty = tty_port_tty_get(&tport->tp_port->port);
if (tty && C_CRTSCTS(tty)) {
if (msr & TI_MSR_CTS) {
tty->hw_stopped = 0;
@@ -1519,6 +1520,7 @@
tty->hw_stopped = 1;
}
}
+ tty_kref_put(tty);
}
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index 4f7f9e3..e7d4246 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -214,7 +214,7 @@
/* set up our port structure making the tty driver
* remember our port object, and us it */
tty->driver_data = port;
- port->port.tty = tty;
+ tty_port_tty_set(&port->port, tty);
if (port->port.count == 1) {
@@ -246,7 +246,7 @@
bailout_mutex_unlock:
port->port.count = 0;
tty->driver_data = NULL;
- port->port.tty = NULL;
+ tty_port_tty_set(&port->port, NULL);
mutex_unlock(&port->mutex);
bailout_kref_put:
usb_serial_put(serial);
@@ -276,10 +276,11 @@
port->serial->type->close(tty, port, filp);
if (port->port.count == (port->console? 1 : 0)) {
- if (port->port.tty) {
- if (port->port.tty->driver_data)
- port->port.tty->driver_data = NULL;
- port->port.tty = NULL;
+ struct tty_struct *tty = tty_port_tty_get(&port->port);
+ if (tty) {
+ if (tty->driver_data)
+ tty->driver_data = NULL;
+ tty_port_tty_set(&port->port, NULL);
}
}
@@ -508,11 +509,12 @@
if (!port)
return;
- tty = port->port.tty;
+ tty = tty_port_tty_get(&port->port);
if (!tty)
return;
tty_wakeup(tty);
+ tty_kref_put(tty);
}
static void port_release(struct device *dev)
@@ -819,6 +821,7 @@
port = kzalloc(sizeof(struct usb_serial_port), GFP_KERNEL);
if (!port)
goto probe_error;
+ tty_port_init(&port->port);
port->serial = serial;
spin_lock_init(&port->lock);
mutex_init(&port->mutex);
@@ -1040,8 +1043,11 @@
for (i = 0; i < serial->num_ports; ++i) {
port = serial->port[i];
if (port) {
- if (port->port.tty)
- tty_hangup(port->port.tty);
+ struct tty_struct *tty = tty_port_tty_get(&port->port);
+ if (tty) {
+ tty_hangup(tty);
+ tty_kref_put(tty);
+ }
kill_traffic(port);
}
}
diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c
index cf8924f..a6d1c75 100644
--- a/drivers/usb/serial/visor.c
+++ b/drivers/usb/serial/visor.c
@@ -499,7 +499,7 @@
int status = urb->status;
struct tty_struct *tty;
int result;
- int available_room;
+ int available_room = 0;
dbg("%s - port %d", __func__, port->number);
@@ -512,13 +512,17 @@
usb_serial_debug_data(debug, &port->dev, __func__,
urb->actual_length, data);
- tty = port->port.tty;
- if (tty && urb->actual_length) {
- available_room = tty_buffer_request_room(tty,
+ if (urb->actual_length) {
+ tty = tty_port_tty_get(&port->port);
+ if (tty) {
+ available_room = tty_buffer_request_room(tty,
urb->actual_length);
- if (available_room) {
- tty_insert_flip_string(tty, data, available_room);
- tty_flip_buffer_push(tty);
+ if (available_room) {
+ tty_insert_flip_string(tty, data,
+ available_room);
+ tty_flip_buffer_push(tty);
+ }
+ tty_kref_put(tty);
}
spin_lock(&priv->lock);
priv->bytes_in += available_room;
diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c
index 3a9d143..11c8b97 100644
--- a/drivers/usb/serial/whiteheat.c
+++ b/drivers/usb/serial/whiteheat.c
@@ -1481,7 +1481,7 @@
struct whiteheat_private *info =
container_of(work, struct whiteheat_private, rx_work);
struct usb_serial_port *port = info->port;
- struct tty_struct *tty = port->port.tty;
+ struct tty_struct *tty = tty_port_tty_get(&port->port);
struct whiteheat_urb_wrap *wrap;
struct urb *urb;
unsigned long flags;
@@ -1493,7 +1493,7 @@
spin_lock_irqsave(&info->lock, flags);
if (info->flags & THROTTLED) {
spin_unlock_irqrestore(&info->lock, flags);
- return;
+ goto out;
}
list_for_each_safe(tmp, tmp2, &info->rx_urb_q) {
@@ -1513,7 +1513,7 @@
spin_unlock_irqrestore(&info->lock, flags);
tty_flip_buffer_push(tty);
schedule_work(&info->rx_work);
- return;
+ goto out;
}
tty_insert_flip_string(tty, urb->transfer_buffer, len);
sent += len;
@@ -1536,6 +1536,8 @@
if (sent)
tty_flip_buffer_push(tty);
+out:
+ tty_kref_put(tty);
}
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index d85a74c..f79c204 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -673,7 +673,6 @@
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
- select VIDEO_SELECT
help
This is the frame buffer device driver for generic VESA 2.0
compliant graphic cards. The older VESA 1.2 cards are not supported.
@@ -1578,7 +1577,6 @@
tristate "Cyberblade/i1 support"
depends on FB && PCI && X86_32 && !64BIT
select FB_CFB_IMAGEBLIT
- select VIDEO_SELECT
---help---
This driver is supposed to support the Trident Cyberblade/i1
graphics core integrated in the VIA VT8601A North Bridge,
diff --git a/drivers/video/backlight/mbp_nvidia_bl.c b/drivers/video/backlight/mbp_nvidia_bl.c
index 385cba4..06964af 100644
--- a/drivers/video/backlight/mbp_nvidia_bl.c
+++ b/drivers/video/backlight/mbp_nvidia_bl.c
@@ -111,6 +111,4 @@
MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
MODULE_DESCRIPTION("Nvidia-based Macbook Pro Backlight Driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("svnAppleInc.:pnMacBookPro3,1");
-MODULE_ALIAS("svnAppleInc.:pnMacBookPro3,2");
-MODULE_ALIAS("svnAppleInc.:pnMacBookPro4,1");
+MODULE_DEVICE_TABLE(dmi, mbp_device_table);
diff --git a/drivers/video/bw2.c b/drivers/video/bw2.c
index e721644..1e35ba6 100644
--- a/drivers/video/bw2.c
+++ b/drivers/video/bw2.c
@@ -372,7 +372,7 @@
return 0;
}
-static struct of_device_id bw2_match[] = {
+static const struct of_device_id bw2_match[] = {
{
.name = "bwtwo",
},
diff --git a/drivers/video/cg14.c b/drivers/video/cg14.c
index b17e746..a2d1882 100644
--- a/drivers/video/cg14.c
+++ b/drivers/video/cg14.c
@@ -589,7 +589,7 @@
return 0;
}
-static struct of_device_id cg14_match[] = {
+static const struct of_device_id cg14_match[] = {
{
.name = "cgfourteen",
},
diff --git a/drivers/video/cg3.c b/drivers/video/cg3.c
index 3aa7b6c..99f87fb 100644
--- a/drivers/video/cg3.c
+++ b/drivers/video/cg3.c
@@ -456,7 +456,7 @@
return 0;
}
-static struct of_device_id cg3_match[] = {
+static const struct of_device_id cg3_match[] = {
{
.name = "cgthree",
},
diff --git a/drivers/video/cg6.c b/drivers/video/cg6.c
index 2f64bb3..940ec04 100644
--- a/drivers/video/cg6.c
+++ b/drivers/video/cg6.c
@@ -34,10 +34,11 @@
static void cg6_imageblit(struct fb_info *, const struct fb_image *);
static void cg6_fillrect(struct fb_info *, const struct fb_fillrect *);
+static void cg6_copyarea(struct fb_info *info, const struct fb_copyarea *area);
static int cg6_sync(struct fb_info *);
static int cg6_mmap(struct fb_info *, struct vm_area_struct *);
static int cg6_ioctl(struct fb_info *, unsigned int, unsigned long);
-static void cg6_copyarea(struct fb_info *info, const struct fb_copyarea *area);
+static int cg6_pan_display(struct fb_var_screeninfo *, struct fb_info *);
/*
* Frame buffer operations
@@ -47,6 +48,7 @@
.owner = THIS_MODULE,
.fb_setcolreg = cg6_setcolreg,
.fb_blank = cg6_blank,
+ .fb_pan_display = cg6_pan_display,
.fb_fillrect = cg6_fillrect,
.fb_copyarea = cg6_copyarea,
.fb_imageblit = cg6_imageblit,
@@ -161,6 +163,7 @@
#define CG6_THC_MISC_INT_ENAB (1 << 5)
#define CG6_THC_MISC_INT (1 << 4)
#define CG6_THC_MISC_INIT 0x9f
+#define CG6_THC_CURSOFF ((65536-32) | ((65536-32) << 16))
/* The contents are unknown */
struct cg6_tec {
@@ -280,6 +283,33 @@
return 0;
}
+static void cg6_switch_from_graph(struct cg6_par *par)
+{
+ struct cg6_thc __iomem *thc = par->thc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&par->lock, flags);
+
+ /* Hide the cursor. */
+ sbus_writel(CG6_THC_CURSOFF, &thc->thc_cursxy);
+
+ spin_unlock_irqrestore(&par->lock, flags);
+}
+
+static int cg6_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ struct cg6_par *par = (struct cg6_par *)info->par;
+
+ /* We just use this to catch switches out of
+ * graphics mode.
+ */
+ cg6_switch_from_graph(par);
+
+ if (var->xoffset || var->yoffset || var->vmode)
+ return -EINVAL;
+ return 0;
+}
+
/**
* cg6_fillrect - Draws a rectangle on the screen.
*
@@ -643,9 +673,13 @@
struct cg6_par *par = (struct cg6_par *)info->par;
struct cg6_tec __iomem *tec = par->tec;
struct cg6_fbc __iomem *fbc = par->fbc;
+ struct cg6_thc __iomem *thc = par->thc;
u32 rev, conf, mode;
int i;
+ /* Hide the cursor. */
+ sbus_writel(CG6_THC_CURSOFF, &thc->thc_cursxy);
+
/* Turn off stuff in the Transform Engine. */
sbus_writel(0, &tec->tec_matrix);
sbus_writel(0, &tec->tec_clip);
@@ -814,7 +848,7 @@
return 0;
}
-static struct of_device_id cg6_match[] = {
+static const struct of_device_id cg6_match[] = {
{
.name = "cgsix",
},
diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig
index 06f87b0..2f50a80 100644
--- a/drivers/video/console/Kconfig
+++ b/drivers/video/console/Kconfig
@@ -43,22 +43,6 @@
buffer. Each 64KB will give you approximately 16 80x25
screenfuls of scrollback buffer
-config VIDEO_SELECT
- bool "Video mode selection support"
- depends on X86 && VGA_CONSOLE
- ---help---
- This enables support for text mode selection on kernel startup. If
- you want to take advantage of some high-resolution text mode your
- card's BIOS offers, but the traditional Linux utilities like
- SVGATextMode don't, you can say Y here and set the mode using the
- "vga=" option from your boot loader (lilo or loadlin) or set
- "vga=ask" which brings up a video mode menu on kernel startup. (Try
- "man bootparam" or see the documentation of your boot loader about
- how to pass options to the kernel.)
-
- Read the file <file:Documentation/svga.txt> for more information
- about the Video mode selection support. If unsure, say N.
-
config MDA_CONSOLE
depends on !M68K && !PARISC && ISA
tristate "MDA text console (dual-headed) (EXPERIMENTAL)"
diff --git a/drivers/video/ffb.c b/drivers/video/ffb.c
index 7992b13..9dbb964 100644
--- a/drivers/video/ffb.c
+++ b/drivers/video/ffb.c
@@ -1042,7 +1042,7 @@
return 0;
}
-static struct of_device_id ffb_match[] = {
+static const struct of_device_id ffb_match[] = {
{
.name = "SUNW,ffb",
},
diff --git a/drivers/video/leo.c b/drivers/video/leo.c
index 13fea61..7c7e8c2 100644
--- a/drivers/video/leo.c
+++ b/drivers/video/leo.c
@@ -33,6 +33,7 @@
static int leo_mmap(struct fb_info *, struct vm_area_struct *);
static int leo_ioctl(struct fb_info *, unsigned int, unsigned long);
+static int leo_pan_display(struct fb_var_screeninfo *, struct fb_info *);
/*
* Frame buffer operations
@@ -42,6 +43,7 @@
.owner = THIS_MODULE,
.fb_setcolreg = leo_setcolreg,
.fb_blank = leo_blank,
+ .fb_pan_display = leo_pan_display,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
@@ -206,6 +208,60 @@
return;
}
+static void leo_switch_from_graph(struct fb_info *info)
+{
+ struct leo_par *par = (struct leo_par *) info->par;
+ struct leo_ld_ss0 __iomem *ss = par->ld_ss0;
+ struct leo_cursor __iomem *cursor = par->cursor;
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&par->lock, flags);
+
+ par->extent = ((info->var.xres - 1) |
+ ((info->var.yres - 1) << 16));
+
+ sbus_writel(0xffffffff, &ss->wid);
+ sbus_writel(0xffff, &ss->wmask);
+ sbus_writel(0, &ss->vclipmin);
+ sbus_writel(par->extent, &ss->vclipmax);
+ sbus_writel(0, &ss->fg);
+ sbus_writel(0xff000000, &ss->planemask);
+ sbus_writel(0x310850, &ss->rop);
+ sbus_writel(0, &ss->widclip);
+ sbus_writel((info->var.xres-1) | ((info->var.yres-1) << 11),
+ &par->lc_ss0_usr->extent);
+ sbus_writel(4, &par->lc_ss0_usr->addrspace);
+ sbus_writel(0x80000000, &par->lc_ss0_usr->fill);
+ sbus_writel(0, &par->lc_ss0_usr->fontt);
+ do {
+ val = sbus_readl(&par->lc_ss0_usr->csr);
+ } while (val & 0x20000000);
+
+ /* setup screen buffer for cfb_* functions */
+ sbus_writel(1, &ss->wid);
+ sbus_writel(0x00ffffff, &ss->planemask);
+ sbus_writel(0x310b90, &ss->rop);
+ sbus_writel(0, &par->lc_ss0_usr->addrspace);
+
+ /* hide cursor */
+ sbus_writel(sbus_readl(&cursor->cur_misc) & ~LEO_CUR_ENABLE, &cursor->cur_misc);
+
+ spin_unlock_irqrestore(&par->lock, flags);
+}
+
+static int leo_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ /* We just use this to catch switches out of
+ * graphics mode.
+ */
+ leo_switch_from_graph(info);
+
+ if (var->xoffset || var->yoffset || var->vmode)
+ return -EINVAL;
+ return 0;
+}
+
/**
* leo_setcolreg - Optional function. Sets a color register.
* @regno: boolean, 0 copy local, 1 get_user() function
@@ -454,44 +510,6 @@
leo_wid_put(info, &wl);
}
-static void leo_switch_from_graph(struct fb_info *info)
-{
- struct leo_par *par = (struct leo_par *) info->par;
- struct leo_ld_ss0 __iomem *ss = par->ld_ss0;
- unsigned long flags;
- u32 val;
-
- spin_lock_irqsave(&par->lock, flags);
-
- par->extent = ((info->var.xres - 1) |
- ((info->var.yres - 1) << 16));
-
- sbus_writel(0xffffffff, &ss->wid);
- sbus_writel(0xffff, &ss->wmask);
- sbus_writel(0, &ss->vclipmin);
- sbus_writel(par->extent, &ss->vclipmax);
- sbus_writel(0, &ss->fg);
- sbus_writel(0xff000000, &ss->planemask);
- sbus_writel(0x310850, &ss->rop);
- sbus_writel(0, &ss->widclip);
- sbus_writel((info->var.xres-1) | ((info->var.yres-1) << 11),
- &par->lc_ss0_usr->extent);
- sbus_writel(4, &par->lc_ss0_usr->addrspace);
- sbus_writel(0x80000000, &par->lc_ss0_usr->fill);
- sbus_writel(0, &par->lc_ss0_usr->fontt);
- do {
- val = sbus_readl(&par->lc_ss0_usr->csr);
- } while (val & 0x20000000);
-
- /* setup screen buffer for cfb_* functions */
- sbus_writel(1, &ss->wid);
- sbus_writel(0x00ffffff, &ss->planemask);
- sbus_writel(0x310b90, &ss->rop);
- sbus_writel(0, &par->lc_ss0_usr->addrspace);
-
- spin_unlock_irqrestore(&par->lock, flags);
-}
-
static void leo_init_hw(struct fb_info *info)
{
struct leo_par *par = (struct leo_par *) info->par;
@@ -641,7 +659,7 @@
return 0;
}
-static struct of_device_id leo_match[] = {
+static const struct of_device_id leo_match[] = {
{
.name = "SUNW,leo",
},
diff --git a/drivers/video/p9100.c b/drivers/video/p9100.c
index 9e90345..7000f2c 100644
--- a/drivers/video/p9100.c
+++ b/drivers/video/p9100.c
@@ -349,7 +349,7 @@
return 0;
}
-static struct of_device_id p9100_match[] = {
+static const struct of_device_id p9100_match[] = {
{
.name = "p9100",
},
diff --git a/drivers/video/tcx.c b/drivers/video/tcx.c
index 2a03f78..643afbfe 100644
--- a/drivers/video/tcx.c
+++ b/drivers/video/tcx.c
@@ -505,7 +505,7 @@
return 0;
}
-static struct of_device_id tcx_match[] = {
+static const struct of_device_id tcx_match[] = {
{
.name = "SUNW,tcx",
},
diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c
index 47ed39b..a463b3d 100644
--- a/drivers/video/xen-fbfront.c
+++ b/drivers/video/xen-fbfront.c
@@ -680,11 +680,11 @@
static int __init xenfb_init(void)
{
- if (!is_running_on_xen())
+ if (!xen_domain())
return -ENODEV;
/* Nothing to do if running in dom0. */
- if (is_initial_xendomain())
+ if (xen_initial_domain())
return -ENODEV;
return xenbus_register_frontend(&xenfb);
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index c510367..1a22fe7 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -66,6 +66,13 @@
Watchdog timer embedded into AT91RM9200 chips. This will reboot your
system when the timeout is reached.
+config AT91SAM9X_WATCHDOG
+ tristate "AT91SAM9X watchdog"
+ depends on WATCHDOG && (ARCH_AT91SAM9260 || ARCH_AT91SAM9261)
+ help
+ Watchdog timer embedded into AT91SAM9X chips. This will reboot your
+ system when the timeout is reached.
+
config 21285_WATCHDOG
tristate "DC21285 watchdog"
depends on FOOTBRIDGE
@@ -217,6 +224,15 @@
NOTE: once enabled, this timer cannot be disabled.
Say N if you are unsure.
+config ORION5X_WATCHDOG
+ tristate "Orion5x watchdog"
+ depends on ARCH_ORION5X
+ help
+ Say Y here if to include support for the watchdog timer
+ in the Orion5x ARM SoCs.
+ To compile this driver as a module, choose M here: the
+ module will be called orion5x_wdt.
+
# ARM26 Architecture
# AVR32 Architecture
@@ -416,6 +432,18 @@
To compile this driver as a module, choose M here: the
module will be called it8712f_wdt.
+config IT87_WDT
+ tristate "IT87 Watchdog Timer"
+ depends on X86 && EXPERIMENTAL
+ ---help---
+ This is the driver for the hardware watchdog on the ITE IT8716,
+ IT8718, IT8726, IT8712(Version J,K) Super I/O chips. This watchdog
+ simply watches your kernel to make sure it doesn't freeze, and if
+ it does, it reboots your computer after a certain amount of time.
+
+ To compile this driver as a module, choose M here: the module will
+ be called it87_wdt.
+
config HP_WATCHDOG
tristate "HP Proliant iLO 2 Hardware Watchdog Timer"
depends on X86
@@ -573,6 +601,21 @@
Most people will say N.
+config W83697UG_WDT
+ tristate "W83697UG/W83697UF Watchdog Timer"
+ depends on X86
+ ---help---
+ This is the driver for the hardware watchdog on the W83697UG/UF
+ chipset as used in MSI Fuzzy CX700 VIA motherboards (and likely others).
+ This watchdog simply watches your kernel to make sure it doesn't
+ freeze, and if it does, it reboots your computer after a certain
+ amount of time.
+
+ To compile this driver as a module, choose M here: the
+ module will be called w83697ug_wdt.
+
+ Most people will say N.
+
config W83877F_WDT
tristate "W83877F (EMACS) Watchdog Timer"
depends on X86
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index e0ef123..e352bbb 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -26,6 +26,7 @@
# ARM Architecture
obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
+obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o
obj-$(CONFIG_21285_WATCHDOG) += wdt285.o
obj-$(CONFIG_977_WATCHDOG) += wdt977.o
@@ -39,6 +40,7 @@
obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o
obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o
obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o
+obj-$(CONFIG_ORION5X_WATCHDOG) += orion5x_wdt.o
# ARM26 Architecture
@@ -71,6 +73,7 @@
obj-$(CONFIG_ITCO_WDT) += iTCO_vendor_support.o
endif
obj-$(CONFIG_IT8712F_WDT) += it8712f_wdt.o
+obj-$(CONFIG_IT87_WDT) += it87_wdt.o
obj-$(CONFIG_HP_WATCHDOG) += hpwdt.o
obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o
obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o
@@ -83,6 +86,7 @@
obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o
obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o
obj-$(CONFIG_W83697HF_WDT) += w83697hf_wdt.o
+obj-$(CONFIG_W83697UG_WDT) += w83697ug_wdt.o
obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o
obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o
obj-$(CONFIG_MACHZ_WDT) += machzwd.o
@@ -123,6 +127,9 @@
# SPARC64 Architecture
+obj-$(CONFIG_WATCHDOG_RIO) += riowd.o
+obj-$(CONFIG_WATCHDOG_CP1XXX) += cpwd.o
+
# XTENSA Architecture
# Architecture Independant
diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c
new file mode 100644
index 0000000..b4babfc
--- /dev/null
+++ b/drivers/watchdog/at91sam9_wdt.c
@@ -0,0 +1,328 @@
+/*
+ * Watchdog driver for Atmel AT91SAM9x processors.
+ *
+ * Copyright (C) 2008 Renaud CERRATO r.cerrato@til-technologies.fr
+ *
+ * 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.
+ */
+
+/*
+ * The Watchdog Timer Mode Register can be only written to once. If the
+ * timeout need to be set from Linux, be sure that the bootstrap or the
+ * bootloader doesn't write to this register.
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+#include <linux/jiffies.h>
+#include <linux/timer.h>
+#include <linux/bitops.h>
+#include <linux/uaccess.h>
+
+#include <asm/arch/at91_wdt.h>
+
+#define DRV_NAME "AT91SAM9 Watchdog"
+
+/* AT91SAM9 watchdog runs a 12bit counter @ 256Hz,
+ * use this to convert a watchdog
+ * value from/to milliseconds.
+ */
+#define ms_to_ticks(t) (((t << 8) / 1000) - 1)
+#define ticks_to_ms(t) (((t + 1) * 1000) >> 8)
+
+/* Hardware timeout in seconds */
+#define WDT_HW_TIMEOUT 2
+
+/* Timer heartbeat (500ms) */
+#define WDT_TIMEOUT (HZ/2)
+
+/* User land timeout */
+#define WDT_HEARTBEAT 15
+static int heartbeat = WDT_HEARTBEAT;
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. "
+ "(default = " __MODULE_STRING(WDT_HEARTBEAT) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
+ "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static void at91_ping(unsigned long data);
+
+static struct {
+ unsigned long next_heartbeat; /* the next_heartbeat for the timer */
+ unsigned long open;
+ char expect_close;
+ struct timer_list timer; /* The timer that pings the watchdog */
+} at91wdt_private;
+
+/* ......................................................................... */
+
+
+/*
+ * Reload the watchdog timer. (ie, pat the watchdog)
+ */
+static inline void at91_wdt_reset(void)
+{
+ at91_sys_write(AT91_WDT_CR, AT91_WDT_KEY | AT91_WDT_WDRSTT);
+}
+
+/*
+ * Timer tick
+ */
+static void at91_ping(unsigned long data)
+{
+ if (time_before(jiffies, at91wdt_private.next_heartbeat) ||
+ (!nowayout && !at91wdt_private.open)) {
+ at91_wdt_reset();
+ mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT);
+ } else
+ printk(KERN_CRIT DRV_NAME": I will reset your machine !\n");
+}
+
+/*
+ * Watchdog device is opened, and watchdog starts running.
+ */
+static int at91_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &at91wdt_private.open))
+ return -EBUSY;
+
+ at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
+ mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT);
+
+ return nonseekable_open(inode, file);
+}
+
+/*
+ * Close the watchdog device.
+ */
+static int at91_wdt_close(struct inode *inode, struct file *file)
+{
+ clear_bit(0, &at91wdt_private.open);
+
+ /* stop internal ping */
+ if (!at91wdt_private.expect_close)
+ del_timer(&at91wdt_private.timer);
+
+ at91wdt_private.expect_close = 0;
+ return 0;
+}
+
+/*
+ * Set the watchdog time interval in 1/256Hz (write-once)
+ * Counter is 12 bit.
+ */
+static int at91_wdt_settimeout(unsigned int timeout)
+{
+ unsigned int reg;
+ unsigned int mr;
+
+ /* Check if disabled */
+ mr = at91_sys_read(AT91_WDT_MR);
+ if (mr & AT91_WDT_WDDIS) {
+ printk(KERN_ERR DRV_NAME": sorry, watchdog is disabled\n");
+ return -EIO;
+ }
+
+ /*
+ * All counting occurs at SLOW_CLOCK / 128 = 256 Hz
+ *
+ * Since WDV is a 12-bit counter, the maximum period is
+ * 4096 / 256 = 16 seconds.
+ */
+ reg = AT91_WDT_WDRSTEN /* causes watchdog reset */
+ /* | AT91_WDT_WDRPROC causes processor reset only */
+ | AT91_WDT_WDDBGHLT /* disabled in debug mode */
+ | AT91_WDT_WDD /* restart at any time */
+ | (timeout & AT91_WDT_WDV); /* timer value */
+ at91_sys_write(AT91_WDT_MR, reg);
+
+ return 0;
+}
+
+static const struct watchdog_info at91_wdt_info = {
+ .identity = DRV_NAME,
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+};
+
+/*
+ * Handle commands from user-space.
+ */
+static long at91_wdt_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int new_value;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(argp, &at91_wdt_info,
+ sizeof(at91_wdt_info)) ? -EFAULT : 0;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+
+ case WDIOC_KEEPALIVE:
+ at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
+ return 0;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_value, p))
+ return -EFAULT;
+
+ heartbeat = new_value;
+ at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
+
+ return put_user(new_value, p); /* return current value */
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(heartbeat, p);
+ }
+ return -ENOTTY;
+}
+
+/*
+ * Pat the watchdog whenever device is written to.
+ */
+static ssize_t at91_wdt_write(struct file *file, const char *data, size_t len,
+ loff_t *ppos)
+{
+ if (!len)
+ return 0;
+
+ /* Scan for magic character */
+ if (!nowayout) {
+ size_t i;
+
+ at91wdt_private.expect_close = 0;
+
+ for (i = 0; i < len; i++) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V') {
+ at91wdt_private.expect_close = 42;
+ break;
+ }
+ }
+ }
+
+ at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
+
+ return len;
+}
+
+/* ......................................................................... */
+
+static const struct file_operations at91wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .unlocked_ioctl = at91_wdt_ioctl,
+ .open = at91_wdt_open,
+ .release = at91_wdt_close,
+ .write = at91_wdt_write,
+};
+
+static struct miscdevice at91wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &at91wdt_fops,
+};
+
+static int __init at91wdt_probe(struct platform_device *pdev)
+{
+ int res;
+
+ if (at91wdt_miscdev.parent)
+ return -EBUSY;
+ at91wdt_miscdev.parent = &pdev->dev;
+
+ /* Set watchdog */
+ res = at91_wdt_settimeout(ms_to_ticks(WDT_HW_TIMEOUT * 1000));
+ if (res)
+ return res;
+
+ res = misc_register(&at91wdt_miscdev);
+ if (res)
+ return res;
+
+ at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ;
+ setup_timer(&at91wdt_private.timer, at91_ping, 0);
+ mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT);
+
+ printk(KERN_INFO DRV_NAME " enabled (heartbeat=%d sec, nowayout=%d)\n",
+ heartbeat, nowayout);
+
+ return 0;
+}
+
+static int __exit at91wdt_remove(struct platform_device *pdev)
+{
+ int res;
+
+ res = misc_deregister(&at91wdt_miscdev);
+ if (!res)
+ at91wdt_miscdev.parent = NULL;
+
+ return res;
+}
+
+#ifdef CONFIG_PM
+
+static int at91wdt_suspend(struct platform_device *pdev, pm_message_t message)
+{
+ return 0;
+}
+
+static int at91wdt_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+
+#else
+#define at91wdt_suspend NULL
+#define at91wdt_resume NULL
+#endif
+
+static struct platform_driver at91wdt_driver = {
+ .remove = __exit_p(at91wdt_remove),
+ .suspend = at91wdt_suspend,
+ .resume = at91wdt_resume,
+ .driver = {
+ .name = "at91_wdt",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init at91sam_wdt_init(void)
+{
+ return platform_driver_probe(&at91wdt_driver, at91wdt_probe);
+}
+
+static void __exit at91sam_wdt_exit(void)
+{
+ platform_driver_unregister(&at91wdt_driver);
+}
+
+module_init(at91sam_wdt_init);
+module_exit(at91sam_wdt_exit);
+
+MODULE_AUTHOR("Renaud CERRATO <r.cerrato@til-technologies.fr>");
+MODULE_DESCRIPTION("Watchdog driver for Atmel AT91SAM9x processors");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/cpwd.c b/drivers/watchdog/cpwd.c
new file mode 100644
index 0000000..084dfe9
--- /dev/null
+++ b/drivers/watchdog/cpwd.c
@@ -0,0 +1,695 @@
+/* cpwd.c - driver implementation for hardware watchdog
+ * timers found on Sun Microsystems CP1400 and CP1500 boards.
+ *
+ * This device supports both the generic Linux watchdog
+ * interface and Solaris-compatible ioctls as best it is
+ * able.
+ *
+ * NOTE: CP1400 systems appear to have a defective intr_mask
+ * register on the PLD, preventing the disabling of
+ * timer interrupts. We use a timer to periodically
+ * reset 'stopped' watchdogs on affected platforms.
+ *
+ * Copyright (c) 2000 Eric Brower (ebrower@usa.net)
+ * Copyright (C) 2008 David S. Miller <davem@davemloft.net>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/major.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/smp_lock.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#include <asm/watchdog.h>
+
+#define DRIVER_NAME "cpwd"
+#define PFX DRIVER_NAME ": "
+
+#define WD_OBPNAME "watchdog"
+#define WD_BADMODEL "SUNW,501-5336"
+#define WD_BTIMEOUT (jiffies + (HZ * 1000))
+#define WD_BLIMIT 0xFFFF
+
+#define WD0_MINOR 212
+#define WD1_MINOR 213
+#define WD2_MINOR 214
+
+/* Internal driver definitions. */
+#define WD0_ID 0
+#define WD1_ID 1
+#define WD2_ID 2
+#define WD_NUMDEVS 3
+
+#define WD_INTR_OFF 0
+#define WD_INTR_ON 1
+
+#define WD_STAT_INIT 0x01 /* Watchdog timer is initialized */
+#define WD_STAT_BSTOP 0x02 /* Watchdog timer is brokenstopped */
+#define WD_STAT_SVCD 0x04 /* Watchdog interrupt occurred */
+
+/* Register value definitions
+ */
+#define WD0_INTR_MASK 0x01 /* Watchdog device interrupt masks */
+#define WD1_INTR_MASK 0x02
+#define WD2_INTR_MASK 0x04
+
+#define WD_S_RUNNING 0x01 /* Watchdog device status running */
+#define WD_S_EXPIRED 0x02 /* Watchdog device status expired */
+
+struct cpwd {
+ void __iomem *regs;
+ spinlock_t lock;
+
+ unsigned int irq;
+
+ unsigned long timeout;
+ bool enabled;
+ bool reboot;
+ bool broken;
+ bool initialized;
+
+ struct {
+ struct miscdevice misc;
+ void __iomem *regs;
+ u8 intr_mask;
+ u8 runstatus;
+ u16 timeout;
+ } devs[WD_NUMDEVS];
+};
+
+static struct cpwd *cpwd_device;
+
+/* Sun uses Altera PLD EPF8820ATC144-4
+ * providing three hardware watchdogs:
+ *
+ * 1) RIC - sends an interrupt when triggered
+ * 2) XIR - asserts XIR_B_RESET when triggered, resets CPU
+ * 3) POR - asserts POR_B_RESET when triggered, resets CPU, backplane, board
+ *
+ *** Timer register block definition (struct wd_timer_regblk)
+ *
+ * dcntr and limit registers (halfword access):
+ * -------------------
+ * | 15 | ...| 1 | 0 |
+ * -------------------
+ * |- counter val -|
+ * -------------------
+ * dcntr - Current 16-bit downcounter value.
+ * When downcounter reaches '0' watchdog expires.
+ * Reading this register resets downcounter with 'limit' value.
+ * limit - 16-bit countdown value in 1/10th second increments.
+ * Writing this register begins countdown with input value.
+ * Reading from this register does not affect counter.
+ * NOTES: After watchdog reset, dcntr and limit contain '1'
+ *
+ * status register (byte access):
+ * ---------------------------
+ * | 7 | ... | 2 | 1 | 0 |
+ * --------------+------------
+ * |- UNUSED -| EXP | RUN |
+ * ---------------------------
+ * status- Bit 0 - Watchdog is running
+ * Bit 1 - Watchdog has expired
+ *
+ *** PLD register block definition (struct wd_pld_regblk)
+ *
+ * intr_mask register (byte access):
+ * ---------------------------------
+ * | 7 | ... | 3 | 2 | 1 | 0 |
+ * +-------------+------------------
+ * |- UNUSED -| WD3 | WD2 | WD1 |
+ * ---------------------------------
+ * WD3 - 1 == Interrupt disabled for watchdog 3
+ * WD2 - 1 == Interrupt disabled for watchdog 2
+ * WD1 - 1 == Interrupt disabled for watchdog 1
+ *
+ * pld_status register (byte access):
+ * UNKNOWN, MAGICAL MYSTERY REGISTER
+ *
+ */
+#define WD_TIMER_REGSZ 16
+#define WD0_OFF 0
+#define WD1_OFF (WD_TIMER_REGSZ * 1)
+#define WD2_OFF (WD_TIMER_REGSZ * 2)
+#define PLD_OFF (WD_TIMER_REGSZ * 3)
+
+#define WD_DCNTR 0x00
+#define WD_LIMIT 0x04
+#define WD_STATUS 0x08
+
+#define PLD_IMASK (PLD_OFF + 0x00)
+#define PLD_STATUS (PLD_OFF + 0x04)
+
+static struct timer_list cpwd_timer;
+
+static int wd0_timeout = 0;
+static int wd1_timeout = 0;
+static int wd2_timeout = 0;
+
+module_param (wd0_timeout, int, 0);
+MODULE_PARM_DESC(wd0_timeout, "Default watchdog0 timeout in 1/10secs");
+module_param (wd1_timeout, int, 0);
+MODULE_PARM_DESC(wd1_timeout, "Default watchdog1 timeout in 1/10secs");
+module_param (wd2_timeout, int, 0);
+MODULE_PARM_DESC(wd2_timeout, "Default watchdog2 timeout in 1/10secs");
+
+MODULE_AUTHOR("Eric Brower <ebrower@usa.net>");
+MODULE_DESCRIPTION("Hardware watchdog driver for Sun Microsystems CP1400/1500");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("watchdog");
+
+static void cpwd_writew(u16 val, void __iomem *addr)
+{
+ writew(cpu_to_le16(val), addr);
+}
+static u16 cpwd_readw(void __iomem *addr)
+{
+ u16 val = readw(addr);
+
+ return le16_to_cpu(val);
+}
+
+static void cpwd_writeb(u8 val, void __iomem *addr)
+{
+ writeb(val, addr);
+}
+
+static u8 cpwd_readb(void __iomem *addr)
+{
+ return readb(addr);
+}
+
+/* Enable or disable watchdog interrupts
+ * Because of the CP1400 defect this should only be
+ * called during initialzation or by wd_[start|stop]timer()
+ *
+ * index - sub-device index, or -1 for 'all'
+ * enable - non-zero to enable interrupts, zero to disable
+ */
+static void cpwd_toggleintr(struct cpwd *p, int index, int enable)
+{
+ unsigned char curregs = cpwd_readb(p->regs + PLD_IMASK);
+ unsigned char setregs =
+ (index == -1) ?
+ (WD0_INTR_MASK | WD1_INTR_MASK | WD2_INTR_MASK) :
+ (p->devs[index].intr_mask);
+
+ if (enable == WD_INTR_ON)
+ curregs &= ~setregs;
+ else
+ curregs |= setregs;
+
+ cpwd_writeb(curregs, p->regs + PLD_IMASK);
+}
+
+/* Restarts timer with maximum limit value and
+ * does not unset 'brokenstop' value.
+ */
+static void cpwd_resetbrokentimer(struct cpwd *p, int index)
+{
+ cpwd_toggleintr(p, index, WD_INTR_ON);
+ cpwd_writew(WD_BLIMIT, p->devs[index].regs + WD_LIMIT);
+}
+
+/* Timer method called to reset stopped watchdogs--
+ * because of the PLD bug on CP1400, we cannot mask
+ * interrupts within the PLD so me must continually
+ * reset the timers ad infinitum.
+ */
+static void cpwd_brokentimer(unsigned long data)
+{
+ struct cpwd *p = (struct cpwd *) data;
+ int id, tripped = 0;
+
+ /* kill a running timer instance, in case we
+ * were called directly instead of by kernel timer
+ */
+ if (timer_pending(&cpwd_timer))
+ del_timer(&cpwd_timer);
+
+ for (id = 0; id < WD_NUMDEVS; id++) {
+ if (p->devs[id].runstatus & WD_STAT_BSTOP) {
+ ++tripped;
+ cpwd_resetbrokentimer(p, id);
+ }
+ }
+
+ if (tripped) {
+ /* there is at least one timer brokenstopped-- reschedule */
+ cpwd_timer.expires = WD_BTIMEOUT;
+ add_timer(&cpwd_timer);
+ }
+}
+
+/* Reset countdown timer with 'limit' value and continue countdown.
+ * This will not start a stopped timer.
+ */
+static void cpwd_pingtimer(struct cpwd *p, int index)
+{
+ if (cpwd_readb(p->devs[index].regs + WD_STATUS) & WD_S_RUNNING)
+ cpwd_readw(p->devs[index].regs + WD_DCNTR);
+}
+
+/* Stop a running watchdog timer-- the timer actually keeps
+ * running, but the interrupt is masked so that no action is
+ * taken upon expiration.
+ */
+static void cpwd_stoptimer(struct cpwd *p, int index)
+{
+ if (cpwd_readb(p->devs[index].regs + WD_STATUS) & WD_S_RUNNING) {
+ cpwd_toggleintr(p, index, WD_INTR_OFF);
+
+ if (p->broken) {
+ p->devs[index].runstatus |= WD_STAT_BSTOP;
+ cpwd_brokentimer((unsigned long) p);
+ }
+ }
+}
+
+/* Start a watchdog timer with the specified limit value
+ * If the watchdog is running, it will be restarted with
+ * the provided limit value.
+ *
+ * This function will enable interrupts on the specified
+ * watchdog.
+ */
+static void cpwd_starttimer(struct cpwd *p, int index)
+{
+ if (p->broken)
+ p->devs[index].runstatus &= ~WD_STAT_BSTOP;
+
+ p->devs[index].runstatus &= ~WD_STAT_SVCD;
+
+ cpwd_writew(p->devs[index].timeout, p->devs[index].regs + WD_LIMIT);
+ cpwd_toggleintr(p, index, WD_INTR_ON);
+}
+
+static int cpwd_getstatus(struct cpwd *p, int index)
+{
+ unsigned char stat = cpwd_readb(p->devs[index].regs + WD_STATUS);
+ unsigned char intr = cpwd_readb(p->devs[index].regs + PLD_IMASK);
+ unsigned char ret = WD_STOPPED;
+
+ /* determine STOPPED */
+ if (!stat)
+ return ret;
+
+ /* determine EXPIRED vs FREERUN vs RUNNING */
+ else if (WD_S_EXPIRED & stat) {
+ ret = WD_EXPIRED;
+ } else if(WD_S_RUNNING & stat) {
+ if (intr & p->devs[index].intr_mask) {
+ ret = WD_FREERUN;
+ } else {
+ /* Fudge WD_EXPIRED status for defective CP1400--
+ * IF timer is running
+ * AND brokenstop is set
+ * AND an interrupt has been serviced
+ * we are WD_EXPIRED.
+ *
+ * IF timer is running
+ * AND brokenstop is set
+ * AND no interrupt has been serviced
+ * we are WD_FREERUN.
+ */
+ if (p->broken &&
+ (p->devs[index].runstatus & WD_STAT_BSTOP)) {
+ if (p->devs[index].runstatus & WD_STAT_SVCD) {
+ ret = WD_EXPIRED;
+ } else {
+ /* we could as well pretend we are expired */
+ ret = WD_FREERUN;
+ }
+ } else {
+ ret = WD_RUNNING;
+ }
+ }
+ }
+
+ /* determine SERVICED */
+ if (p->devs[index].runstatus & WD_STAT_SVCD)
+ ret |= WD_SERVICED;
+
+ return(ret);
+}
+
+static irqreturn_t cpwd_interrupt(int irq, void *dev_id)
+{
+ struct cpwd *p = dev_id;
+
+ /* Only WD0 will interrupt-- others are NMI and we won't
+ * see them here....
+ */
+ spin_lock_irq(&p->lock);
+
+ cpwd_stoptimer(p, WD0_ID);
+ p->devs[WD0_ID].runstatus |= WD_STAT_SVCD;
+
+ spin_unlock_irq(&p->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int cpwd_open(struct inode *inode, struct file *f)
+{
+ struct cpwd *p = cpwd_device;
+
+ lock_kernel();
+ switch(iminor(inode)) {
+ case WD0_MINOR:
+ case WD1_MINOR:
+ case WD2_MINOR:
+ break;
+
+ default:
+ unlock_kernel();
+ return -ENODEV;
+ }
+
+ /* Register IRQ on first open of device */
+ if (!p->initialized) {
+ if (request_irq(p->irq, &cpwd_interrupt,
+ IRQF_SHARED, DRIVER_NAME, p)) {
+ printk(KERN_ERR PFX "Cannot register IRQ %d\n",
+ p->irq);
+ unlock_kernel();
+ return -EBUSY;
+ }
+ p->initialized = true;
+ }
+
+ unlock_kernel();
+
+ return nonseekable_open(inode, f);
+}
+
+static int cpwd_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static int cpwd_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ static struct watchdog_info info = {
+ .options = WDIOF_SETTIMEOUT,
+ .firmware_version = 1,
+ .identity = DRIVER_NAME,
+ };
+ void __user *argp = (void __user *)arg;
+ int index = iminor(inode) - WD0_MINOR;
+ struct cpwd *p = cpwd_device;
+ int setopt = 0;
+
+ switch (cmd) {
+ /* Generic Linux IOCTLs */
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &info, sizeof(struct watchdog_info)))
+ return -EFAULT;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ if (put_user(0, (int __user *)argp))
+ return -EFAULT;
+ break;
+
+ case WDIOC_KEEPALIVE:
+ cpwd_pingtimer(p, index);
+ break;
+
+ case WDIOC_SETOPTIONS:
+ if (copy_from_user(&setopt, argp, sizeof(unsigned int)))
+ return -EFAULT;
+
+ if (setopt & WDIOS_DISABLECARD) {
+ if (p->enabled)
+ return -EINVAL;
+ cpwd_stoptimer(p, index);
+ } else if (setopt & WDIOS_ENABLECARD) {
+ cpwd_starttimer(p, index);
+ } else {
+ return -EINVAL;
+ }
+ break;
+
+ /* Solaris-compatible IOCTLs */
+ case WIOCGSTAT:
+ setopt = cpwd_getstatus(p, index);
+ if (copy_to_user(argp, &setopt, sizeof(unsigned int)))
+ return -EFAULT;
+ break;
+
+ case WIOCSTART:
+ cpwd_starttimer(p, index);
+ break;
+
+ case WIOCSTOP:
+ if (p->enabled)
+ return(-EINVAL);
+
+ cpwd_stoptimer(p, index);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static long cpwd_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int rval = -ENOIOCTLCMD;
+
+ switch (cmd) {
+ /* solaris ioctls are specific to this driver */
+ case WIOCSTART:
+ case WIOCSTOP:
+ case WIOCGSTAT:
+ lock_kernel();
+ rval = cpwd_ioctl(file->f_path.dentry->d_inode, file, cmd, arg);
+ unlock_kernel();
+ break;
+
+ /* everything else is handled by the generic compat layer */
+ default:
+ break;
+ }
+
+ return rval;
+}
+
+static ssize_t cpwd_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct inode *inode = file->f_path.dentry->d_inode;
+ struct cpwd *p = cpwd_device;
+ int index = iminor(inode);
+
+ if (count) {
+ cpwd_pingtimer(p, index);
+ return 1;
+ }
+
+ return 0;
+}
+
+static ssize_t cpwd_read(struct file * file, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ return -EINVAL;
+}
+
+static const struct file_operations cpwd_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = cpwd_ioctl,
+ .compat_ioctl = cpwd_compat_ioctl,
+ .open = cpwd_open,
+ .write = cpwd_write,
+ .read = cpwd_read,
+ .release = cpwd_release,
+};
+
+static int __devinit cpwd_probe(struct of_device *op,
+ const struct of_device_id *match)
+{
+ struct device_node *options;
+ const char *str_prop;
+ const void *prop_val;
+ int i, err = -EINVAL;
+ struct cpwd *p;
+
+ if (cpwd_device)
+ return -EINVAL;
+
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ err = -ENOMEM;
+ if (!p) {
+ printk(KERN_ERR PFX "Unable to allocate struct cpwd.\n");
+ goto out;
+ }
+
+ p->irq = op->irqs[0];
+
+ spin_lock_init(&p->lock);
+
+ p->regs = of_ioremap(&op->resource[0], 0,
+ 4 * WD_TIMER_REGSZ, DRIVER_NAME);
+ if (!p->regs) {
+ printk(KERN_ERR PFX "Unable to map registers.\n");
+ goto out_free;
+ }
+
+ options = of_find_node_by_path("/options");
+ err = -ENODEV;
+ if (!options) {
+ printk(KERN_ERR PFX "Unable to find /options node.\n");
+ goto out_iounmap;
+ }
+
+ prop_val = of_get_property(options, "watchdog-enable?", NULL);
+ p->enabled = (prop_val ? true : false);
+
+ prop_val = of_get_property(options, "watchdog-reboot?", NULL);
+ p->reboot = (prop_val ? true : false);
+
+ str_prop = of_get_property(options, "watchdog-timeout", NULL);
+ if (str_prop)
+ p->timeout = simple_strtoul(str_prop, NULL, 10);
+
+ /* CP1400s seem to have broken PLD implementations-- the
+ * interrupt_mask register cannot be written, so no timer
+ * interrupts can be masked within the PLD.
+ */
+ str_prop = of_get_property(op->node, "model", NULL);
+ p->broken = (str_prop && !strcmp(str_prop, WD_BADMODEL));
+
+ if (!p->enabled)
+ cpwd_toggleintr(p, -1, WD_INTR_OFF);
+
+ for (i = 0; i < WD_NUMDEVS; i++) {
+ static const char *cpwd_names[] = { "RIC", "XIR", "POR" };
+ static int *parms[] = { &wd0_timeout,
+ &wd1_timeout,
+ &wd2_timeout };
+ struct miscdevice *mp = &p->devs[i].misc;
+
+ mp->minor = WD0_MINOR + i;
+ mp->name = cpwd_names[i];
+ mp->fops = &cpwd_fops;
+
+ p->devs[i].regs = p->regs + (i * WD_TIMER_REGSZ);
+ p->devs[i].intr_mask = (WD0_INTR_MASK << i);
+ p->devs[i].runstatus &= ~WD_STAT_BSTOP;
+ p->devs[i].runstatus |= WD_STAT_INIT;
+ p->devs[i].timeout = p->timeout;
+ if (*parms[i])
+ p->devs[i].timeout = *parms[i];
+
+ err = misc_register(&p->devs[i].misc);
+ if (err) {
+ printk(KERN_ERR "Could not register misc device for "
+ "dev %d\n", i);
+ goto out_unregister;
+ }
+ }
+
+ if (p->broken) {
+ init_timer(&cpwd_timer);
+ cpwd_timer.function = cpwd_brokentimer;
+ cpwd_timer.data = (unsigned long) p;
+ cpwd_timer.expires = WD_BTIMEOUT;
+
+ printk(KERN_INFO PFX "PLD defect workaround enabled for "
+ "model " WD_BADMODEL ".\n");
+ }
+
+ dev_set_drvdata(&op->dev, p);
+ cpwd_device = p;
+ err = 0;
+
+out:
+ return err;
+
+out_unregister:
+ for (i--; i >= 0; i--)
+ misc_deregister(&p->devs[i].misc);
+
+out_iounmap:
+ of_iounmap(&op->resource[0], p->regs, 4 * WD_TIMER_REGSZ);
+
+out_free:
+ kfree(p);
+ goto out;
+}
+
+static int __devexit cpwd_remove(struct of_device *op)
+{
+ struct cpwd *p = dev_get_drvdata(&op->dev);
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ misc_deregister(&p->devs[i].misc);
+
+ if (!p->enabled) {
+ cpwd_stoptimer(p, i);
+ if (p->devs[i].runstatus & WD_STAT_BSTOP)
+ cpwd_resetbrokentimer(p, i);
+ }
+ }
+
+ if (p->broken)
+ del_timer_sync(&cpwd_timer);
+
+ if (p->initialized)
+ free_irq(p->irq, p);
+
+ of_iounmap(&op->resource[0], p->regs, 4 * WD_TIMER_REGSZ);
+ kfree(p);
+
+ cpwd_device = NULL;
+
+ return 0;
+}
+
+static const struct of_device_id cpwd_match[] = {
+ {
+ .name = "watchdog",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cpwd_match);
+
+static struct of_platform_driver cpwd_driver = {
+ .name = DRIVER_NAME,
+ .match_table = cpwd_match,
+ .probe = cpwd_probe,
+ .remove = __devexit_p(cpwd_remove),
+};
+
+static int __init cpwd_init(void)
+{
+ return of_register_driver(&cpwd_driver, &of_bus_type);
+}
+
+static void __exit cpwd_exit(void)
+{
+ of_unregister_driver(&cpwd_driver);
+}
+
+module_init(cpwd_init);
+module_exit(cpwd_exit);
diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c
new file mode 100644
index 0000000..afb8af3
--- /dev/null
+++ b/drivers/watchdog/it87_wdt.c
@@ -0,0 +1,725 @@
+/*
+ * Watchdog Timer Driver
+ * for ITE IT87xx Environment Control - Low Pin Count Input / Output
+ *
+ * (c) Copyright 2007 Oliver Schuster <olivers137@aol.com>
+ *
+ * Based on softdog.c by Alan Cox,
+ * 83977f_wdt.c by Jose Goncalves,
+ * it87.c by Chris Gauthron, Jean Delvare
+ *
+ * Data-sheets: Publicly available at the ITE website
+ * http://www.ite.com.tw/
+ *
+ * Support of the watchdog timers, which are available on
+ * IT8716, IT8718, IT8726 and IT8712 (J,K version).
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/watchdog.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include <asm/system.h>
+
+#define WATCHDOG_VERSION "1.12"
+#define WATCHDOG_NAME "IT87 WDT"
+#define PFX WATCHDOG_NAME ": "
+#define DRIVER_VERSION WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n"
+#define WD_MAGIC 'V'
+
+/* Defaults for Module Parameter */
+#define DEFAULT_NOGAMEPORT 0
+#define DEFAULT_EXCLUSIVE 1
+#define DEFAULT_TIMEOUT 60
+#define DEFAULT_TESTMODE 0
+#define DEFAULT_NOWAYOUT WATCHDOG_NOWAYOUT
+
+/* IO Ports */
+#define REG 0x2e
+#define VAL 0x2f
+
+/* Logical device Numbers LDN */
+#define GPIO 0x07
+#define GAMEPORT 0x09
+#define CIR 0x0a
+
+/* Configuration Registers and Functions */
+#define LDNREG 0x07
+#define CHIPID 0x20
+#define CHIPREV 0x22
+#define ACTREG 0x30
+#define BASEREG 0x60
+
+/* Chip Id numbers */
+#define NO_DEV_ID 0xffff
+#define IT8705_ID 0x8705
+#define IT8712_ID 0x8712
+#define IT8716_ID 0x8716
+#define IT8718_ID 0x8718
+#define IT8726_ID 0x8726 /* the data sheet suggest wrongly 0x8716 */
+
+/* GPIO Configuration Registers LDN=0x07 */
+#define WDTCTRL 0x71
+#define WDTCFG 0x72
+#define WDTVALLSB 0x73
+#define WDTVALMSB 0x74
+
+/* GPIO Bits WDTCTRL */
+#define WDT_CIRINT 0x80
+#define WDT_MOUSEINT 0x40
+#define WDT_KYBINT 0x20
+#define WDT_GAMEPORT 0x10 /* not it8718 */
+#define WDT_FORCE 0x02
+#define WDT_ZERO 0x01
+
+/* GPIO Bits WDTCFG */
+#define WDT_TOV1 0x80
+#define WDT_KRST 0x40
+#define WDT_TOVE 0x20
+#define WDT_PWROK 0x10
+#define WDT_INT_MASK 0x0f
+
+/* CIR Configuration Register LDN=0x0a */
+#define CIR_ILS 0x70
+
+/* The default Base address is not always available, we use this */
+#define CIR_BASE 0x0208
+
+/* CIR Controller */
+#define CIR_DR(b) (b)
+#define CIR_IER(b) (b + 1)
+#define CIR_RCR(b) (b + 2)
+#define CIR_TCR1(b) (b + 3)
+#define CIR_TCR2(b) (b + 4)
+#define CIR_TSR(b) (b + 5)
+#define CIR_RSR(b) (b + 6)
+#define CIR_BDLR(b) (b + 5)
+#define CIR_BDHR(b) (b + 6)
+#define CIR_IIR(b) (b + 7)
+
+/* Default Base address of Game port */
+#define GP_BASE_DEFAULT 0x0201
+
+/* wdt_status */
+#define WDTS_TIMER_RUN 0
+#define WDTS_DEV_OPEN 1
+#define WDTS_KEEPALIVE 2
+#define WDTS_LOCKED 3
+#define WDTS_USE_GP 4
+#define WDTS_EXPECTED 5
+
+static unsigned int base, gpact, ciract;
+static unsigned long wdt_status;
+static DEFINE_SPINLOCK(spinlock);
+
+static int nogameport = DEFAULT_NOGAMEPORT;
+static int exclusive = DEFAULT_EXCLUSIVE;
+static int timeout = DEFAULT_TIMEOUT;
+static int testmode = DEFAULT_TESTMODE;
+static int nowayout = DEFAULT_NOWAYOUT;
+
+module_param(nogameport, int, 0);
+MODULE_PARM_DESC(nogameport, "Forbid the activation of game port, default="
+ __MODULE_STRING(DEFAULT_NOGAMEPORT));
+module_param(exclusive, int, 0);
+MODULE_PARM_DESC(exclusive, "Watchdog exclusive device open, default="
+ __MODULE_STRING(DEFAULT_EXCLUSIVE));
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, default="
+ __MODULE_STRING(DEFAULT_TIMEOUT));
+module_param(testmode, int, 0);
+MODULE_PARM_DESC(testmode, "Watchdog test mode (1 = no reboot), default="
+ __MODULE_STRING(DEFAULT_TESTMODE));
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started, default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT));
+
+/* Superio Chip */
+
+static inline void superio_enter(void)
+{
+ outb(0x87, REG);
+ outb(0x01, REG);
+ outb(0x55, REG);
+ outb(0x55, REG);
+}
+
+static inline void superio_exit(void)
+{
+ outb(0x02, REG);
+ outb(0x02, VAL);
+}
+
+static inline void superio_select(int ldn)
+{
+ outb(LDNREG, REG);
+ outb(ldn, VAL);
+}
+
+static inline int superio_inb(int reg)
+{
+ outb(reg, REG);
+ return inb(VAL);
+}
+
+static inline void superio_outb(int val, int reg)
+{
+ outb(reg, REG);
+ outb(val, VAL);
+}
+
+static inline int superio_inw(int reg)
+{
+ int val;
+ outb(reg++, REG);
+ val = inb(VAL) << 8;
+ outb(reg, REG);
+ val |= inb(VAL);
+ return val;
+}
+
+static inline void superio_outw(int val, int reg)
+{
+ outb(reg++, REG);
+ outb(val >> 8, VAL);
+ outb(reg, REG);
+ outb(val, VAL);
+}
+
+/* watchdog timer handling */
+
+static void wdt_keepalive(void)
+{
+ if (test_bit(WDTS_USE_GP, &wdt_status))
+ inb(base);
+ else
+ /* The timer reloads with around 5 msec delay */
+ outb(0x55, CIR_DR(base));
+ set_bit(WDTS_KEEPALIVE, &wdt_status);
+}
+
+static void wdt_start(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&spinlock, flags);
+ superio_enter();
+
+ superio_select(GPIO);
+ if (test_bit(WDTS_USE_GP, &wdt_status))
+ superio_outb(WDT_GAMEPORT, WDTCTRL);
+ else
+ superio_outb(WDT_CIRINT, WDTCTRL);
+ if (!testmode)
+ superio_outb(WDT_TOV1 | WDT_KRST | WDT_PWROK, WDTCFG);
+ else
+ superio_outb(WDT_TOV1, WDTCFG);
+ superio_outb(timeout>>8, WDTVALMSB);
+ superio_outb(timeout, WDTVALLSB);
+
+ superio_exit();
+ spin_unlock_irqrestore(&spinlock, flags);
+}
+
+static void wdt_stop(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&spinlock, flags);
+ superio_enter();
+
+ superio_select(GPIO);
+ superio_outb(0x00, WDTCTRL);
+ superio_outb(WDT_TOV1, WDTCFG);
+ superio_outb(0x00, WDTVALMSB);
+ superio_outb(0x00, WDTVALLSB);
+
+ superio_exit();
+ spin_unlock_irqrestore(&spinlock, flags);
+}
+
+/**
+ * wdt_set_timeout - set a new timeout value with watchdog ioctl
+ * @t: timeout value in seconds
+ *
+ * The hardware device has a 16 bit watchdog timer, thus the
+ * timeout time ranges between 1 and 65535 seconds.
+ *
+ * Used within WDIOC_SETTIMEOUT watchdog device ioctl.
+ */
+
+static int wdt_set_timeout(int t)
+{
+ unsigned long flags;
+
+ if (t < 1 || t > 65535)
+ return -EINVAL;
+
+ timeout = t;
+
+ spin_lock_irqsave(&spinlock, flags);
+ if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
+ superio_enter();
+
+ superio_select(GPIO);
+ superio_outb(t>>8, WDTVALMSB);
+ superio_outb(t, WDTVALLSB);
+
+ superio_exit();
+ }
+ spin_unlock_irqrestore(&spinlock, flags);
+ return 0;
+}
+
+/**
+ * wdt_get_status - determines the status supported by watchdog ioctl
+ * @status: status returned to user space
+ *
+ * The status bit of the device does not allow to distinguish
+ * between a regular system reset and a watchdog forced reset.
+ * But, in test mode it is useful, so it is supported through
+ * WDIOC_GETSTATUS watchdog ioctl. Additionally the driver
+ * reports the keepalive signal and the acception of the magic.
+ *
+ * Used within WDIOC_GETSTATUS watchdog device ioctl.
+ */
+
+static int wdt_get_status(int *status)
+{
+ unsigned long flags;
+
+ *status = 0;
+ if (testmode) {
+ spin_lock_irqsave(&spinlock, flags);
+ superio_enter();
+ superio_select(GPIO);
+ if (superio_inb(WDTCTRL) & WDT_ZERO) {
+ superio_outb(0x00, WDTCTRL);
+ clear_bit(WDTS_TIMER_RUN, &wdt_status);
+ *status |= WDIOF_CARDRESET;
+ }
+
+ superio_exit();
+ spin_unlock_irqrestore(&spinlock, flags);
+ }
+ if (test_and_clear_bit(WDTS_KEEPALIVE, &wdt_status))
+ *status |= WDIOF_KEEPALIVEPING;
+ if (test_bit(WDTS_EXPECTED, &wdt_status))
+ *status |= WDIOF_MAGICCLOSE;
+ return 0;
+}
+
+/* /dev/watchdog handling */
+
+/**
+ * wdt_open - watchdog file_operations .open
+ * @inode: inode of the device
+ * @file: file handle to the device
+ *
+ * The watchdog timer starts by opening the device.
+ *
+ * Used within the file operation of the watchdog device.
+ */
+
+static int wdt_open(struct inode *inode, struct file *file)
+{
+ if (exclusive && test_and_set_bit(WDTS_DEV_OPEN, &wdt_status))
+ return -EBUSY;
+ if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) {
+ if (nowayout && !test_and_set_bit(WDTS_LOCKED, &wdt_status))
+ __module_get(THIS_MODULE);
+ wdt_start();
+ }
+ return nonseekable_open(inode, file);
+}
+
+/**
+ * wdt_release - watchdog file_operations .release
+ * @inode: inode of the device
+ * @file: file handle to the device
+ *
+ * Closing the watchdog device either stops the watchdog timer
+ * or in the case, that nowayout is set or the magic character
+ * wasn't written, a critical warning about an running watchdog
+ * timer is given.
+ *
+ * Used within the file operation of the watchdog device.
+ */
+
+static int wdt_release(struct inode *inode, struct file *file)
+{
+ if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
+ if (test_and_clear_bit(WDTS_EXPECTED, &wdt_status)) {
+ wdt_stop();
+ clear_bit(WDTS_TIMER_RUN, &wdt_status);
+ } else {
+ wdt_keepalive();
+ printk(KERN_CRIT PFX
+ "unexpected close, not stopping watchdog!\n");
+ }
+ }
+ clear_bit(WDTS_DEV_OPEN, &wdt_status);
+ return 0;
+}
+
+/**
+ * wdt_write - watchdog file_operations .write
+ * @file: file handle to the watchdog
+ * @buf: buffer to write
+ * @count: count of bytes
+ * @ppos: pointer to the position to write. No seeks allowed
+ *
+ * A write to a watchdog device is defined as a keepalive signal. Any
+ * write of data will do, as we don't define content meaning.
+ *
+ * Used within the file operation of the watchdog device.
+ */
+
+static ssize_t wdt_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ if (count) {
+ clear_bit(WDTS_EXPECTED, &wdt_status);
+ wdt_keepalive();
+ }
+ if (!nowayout) {
+ size_t ofs;
+
+ /* note: just in case someone wrote the magic character long ago */
+ for (ofs = 0; ofs != count; ofs++) {
+ char c;
+ if (get_user(c, buf + ofs))
+ return -EFAULT;
+ if (c == WD_MAGIC)
+ set_bit(WDTS_EXPECTED, &wdt_status);
+ }
+ }
+ return count;
+}
+
+static struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
+ .firmware_version = 1,
+ .identity = WATCHDOG_NAME,
+};
+
+/**
+ * wdt_ioctl - watchdog file_operations .unlocked_ioctl
+ * @file: file handle to the device
+ * @cmd: watchdog command
+ * @arg: argument pointer
+ *
+ * The watchdog API defines a common set of functions for all watchdogs
+ * according to their available features.
+ *
+ * Used within the file operation of the watchdog device.
+ */
+
+static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int rc = 0, status, new_options, new_timeout;
+ union {
+ struct watchdog_info __user *ident;
+ int __user *i;
+ } uarg;
+
+ uarg.i = (int __user *)arg;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(uarg.ident,
+ &ident, sizeof(ident)) ? -EFAULT : 0;
+
+ case WDIOC_GETSTATUS:
+ wdt_get_status(&status);
+ return put_user(status, uarg.i);
+
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, uarg.i);
+
+ case WDIOC_KEEPALIVE:
+ wdt_keepalive();
+ return 0;
+
+ case WDIOC_SETOPTIONS:
+ if (get_user(new_options, uarg.i))
+ return -EFAULT;
+
+ switch (new_options) {
+ case WDIOS_DISABLECARD:
+ if (test_bit(WDTS_TIMER_RUN, &wdt_status))
+ wdt_stop();
+ clear_bit(WDTS_TIMER_RUN, &wdt_status);
+ return 0;
+
+ case WDIOS_ENABLECARD:
+ if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status))
+ wdt_start();
+ return 0;
+
+ default:
+ return -EFAULT;
+ }
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_timeout, uarg.i))
+ return -EFAULT;
+ rc = wdt_set_timeout(new_timeout);
+ case WDIOC_GETTIMEOUT:
+ if (put_user(timeout, uarg.i))
+ return -EFAULT;
+ return rc;
+
+ default:
+ return -ENOTTY;
+ }
+}
+
+static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ wdt_stop();
+ return NOTIFY_DONE;
+}
+
+static const struct file_operations wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = wdt_write,
+ .unlocked_ioctl = wdt_ioctl,
+ .open = wdt_open,
+ .release = wdt_release,
+};
+
+static struct miscdevice wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &wdt_fops,
+};
+
+static struct notifier_block wdt_notifier = {
+ .notifier_call = wdt_notify_sys,
+};
+
+static int __init it87_wdt_init(void)
+{
+ int rc = 0;
+ u16 chip_type;
+ u8 chip_rev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&spinlock, flags);
+ superio_enter();
+ chip_type = superio_inw(CHIPID);
+ chip_rev = superio_inb(CHIPREV) & 0x0f;
+ superio_exit();
+ spin_unlock_irqrestore(&spinlock, flags);
+
+ switch (chip_type) {
+ case IT8716_ID:
+ case IT8718_ID:
+ case IT8726_ID:
+ break;
+ case IT8712_ID:
+ if (chip_rev > 7)
+ break;
+ case IT8705_ID:
+ printk(KERN_ERR PFX
+ "Unsupported Chip found, Chip %04x Revision %02x\n",
+ chip_type, chip_rev);
+ return -ENODEV;
+ case NO_DEV_ID:
+ printk(KERN_ERR PFX "no device\n");
+ return -ENODEV;
+ default:
+ printk(KERN_ERR PFX
+ "Unknown Chip found, Chip %04x Revision %04x\n",
+ chip_type, chip_rev);
+ return -ENODEV;
+ }
+
+ spin_lock_irqsave(&spinlock, flags);
+ superio_enter();
+
+ superio_select(GPIO);
+ superio_outb(WDT_TOV1, WDTCFG);
+ superio_outb(0x00, WDTCTRL);
+
+ /* First try to get Gameport support */
+ if (chip_type != IT8718_ID && !nogameport) {
+ superio_select(GAMEPORT);
+ base = superio_inw(BASEREG);
+ if (!base) {
+ base = GP_BASE_DEFAULT;
+ superio_outw(base, BASEREG);
+ }
+ gpact = superio_inb(ACTREG);
+ superio_outb(0x01, ACTREG);
+ superio_exit();
+ spin_unlock_irqrestore(&spinlock, flags);
+ if (request_region(base, 1, WATCHDOG_NAME))
+ set_bit(WDTS_USE_GP, &wdt_status);
+ else
+ rc = -EIO;
+ } else {
+ superio_exit();
+ spin_unlock_irqrestore(&spinlock, flags);
+ }
+
+ /* If we haven't Gameport support, try to get CIR support */
+ if (!test_bit(WDTS_USE_GP, &wdt_status)) {
+ if (!request_region(CIR_BASE, 8, WATCHDOG_NAME)) {
+ if (rc == -EIO)
+ printk(KERN_ERR PFX
+ "I/O Address 0x%04x and 0x%04x"
+ " already in use\n", base, CIR_BASE);
+ else
+ printk(KERN_ERR PFX
+ "I/O Address 0x%04x already in use\n",
+ CIR_BASE);
+ rc = -EIO;
+ goto err_out;
+ }
+ base = CIR_BASE;
+ spin_lock_irqsave(&spinlock, flags);
+ superio_enter();
+
+ superio_select(CIR);
+ superio_outw(base, BASEREG);
+ superio_outb(0x00, CIR_ILS);
+ ciract = superio_inb(ACTREG);
+ superio_outb(0x01, ACTREG);
+ if (rc == -EIO) {
+ superio_select(GAMEPORT);
+ superio_outb(gpact, ACTREG);
+ }
+
+ superio_exit();
+ spin_unlock_irqrestore(&spinlock, flags);
+ }
+
+ if (timeout < 1 || timeout > 65535) {
+ timeout = DEFAULT_TIMEOUT;
+ printk(KERN_WARNING PFX
+ "Timeout value out of range, use default %d sec\n",
+ DEFAULT_TIMEOUT);
+ }
+
+ rc = register_reboot_notifier(&wdt_notifier);
+ if (rc) {
+ printk(KERN_ERR PFX
+ "Cannot register reboot notifier (err=%d)\n", rc);
+ goto err_out_region;
+ }
+
+ rc = misc_register(&wdt_miscdev);
+ if (rc) {
+ printk(KERN_ERR PFX
+ "Cannot register miscdev on minor=%d (err=%d)\n",
+ wdt_miscdev.minor, rc);
+ goto err_out_reboot;
+ }
+
+ /* Initialize CIR to use it as keepalive source */
+ if (!test_bit(WDTS_USE_GP, &wdt_status)) {
+ outb(0x00, CIR_RCR(base));
+ outb(0xc0, CIR_TCR1(base));
+ outb(0x5c, CIR_TCR2(base));
+ outb(0x10, CIR_IER(base));
+ outb(0x00, CIR_BDHR(base));
+ outb(0x01, CIR_BDLR(base));
+ outb(0x09, CIR_IER(base));
+ }
+
+ printk(KERN_INFO PFX "Chip it%04x revision %d initialized. "
+ "timeout=%d sec (nowayout=%d testmode=%d exclusive=%d "
+ "nogameport=%d)\n", chip_type, chip_rev, timeout,
+ nowayout, testmode, exclusive, nogameport);
+
+ return 0;
+
+err_out_reboot:
+ unregister_reboot_notifier(&wdt_notifier);
+err_out_region:
+ release_region(base, test_bit(WDTS_USE_GP, &wdt_status) ? 1 : 8);
+ if (!test_bit(WDTS_USE_GP, &wdt_status)) {
+ spin_lock_irqsave(&spinlock, flags);
+ superio_enter();
+ superio_select(CIR);
+ superio_outb(ciract, ACTREG);
+ superio_exit();
+ spin_unlock_irqrestore(&spinlock, flags);
+ }
+err_out:
+ if (chip_type != IT8718_ID && !nogameport) {
+ spin_lock_irqsave(&spinlock, flags);
+ superio_enter();
+ superio_select(GAMEPORT);
+ superio_outb(gpact, ACTREG);
+ superio_exit();
+ spin_unlock_irqrestore(&spinlock, flags);
+ }
+
+ return rc;
+}
+
+static void __exit it87_wdt_exit(void)
+{
+ unsigned long flags;
+ int nolock;
+
+ nolock = !spin_trylock_irqsave(&spinlock, flags);
+ superio_enter();
+ superio_select(GPIO);
+ superio_outb(0x00, WDTCTRL);
+ superio_outb(0x00, WDTCFG);
+ superio_outb(0x00, WDTVALMSB);
+ superio_outb(0x00, WDTVALLSB);
+ if (test_bit(WDTS_USE_GP, &wdt_status)) {
+ superio_select(GAMEPORT);
+ superio_outb(gpact, ACTREG);
+ } else {
+ superio_select(CIR);
+ superio_outb(ciract, ACTREG);
+ }
+ superio_exit();
+ if (!nolock)
+ spin_unlock_irqrestore(&spinlock, flags);
+
+ misc_deregister(&wdt_miscdev);
+ unregister_reboot_notifier(&wdt_notifier);
+ release_region(base, test_bit(WDTS_USE_GP, &wdt_status) ? 1 : 8);
+}
+
+module_init(it87_wdt_init);
+module_exit(it87_wdt_exit);
+
+MODULE_AUTHOR("Oliver Schuster");
+MODULE_DESCRIPTION("Hardware Watchdog Device Driver for IT87xx EC-LPC I/O");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
index 3a11dad..7bcbb7f 100644
--- a/drivers/watchdog/omap_wdt.c
+++ b/drivers/watchdog/omap_wdt.c
@@ -1,7 +1,7 @@
/*
- * linux/drivers/char/watchdog/omap_wdt.c
+ * omap_wdt.c
*
- * Watchdog driver for the TI OMAP 16xx & 24xx 32KHz (non-secure) watchdog
+ * Watchdog driver for the TI OMAP 16xx & 24xx/34xx 32KHz (non-secure) watchdog
*
* Author: MontaVista Software, Inc.
* <gdavis@mvista.com> or <source@mvista.com>
@@ -47,50 +47,68 @@
#include "omap_wdt.h"
+static struct platform_device *omap_wdt_dev;
+
static unsigned timer_margin;
module_param(timer_margin, uint, 0);
MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)");
-static int omap_wdt_users;
-static struct clk *armwdt_ck;
-static struct clk *mpu_wdt_ick;
-static struct clk *mpu_wdt_fck;
-
static unsigned int wdt_trgr_pattern = 0x1234;
static spinlock_t wdt_lock;
-static void omap_wdt_ping(void)
+struct omap_wdt_dev {
+ void __iomem *base; /* physical */
+ struct device *dev;
+ int omap_wdt_users;
+ struct clk *armwdt_ck;
+ struct clk *mpu_wdt_ick;
+ struct clk *mpu_wdt_fck;
+ struct resource *mem;
+ struct miscdevice omap_wdt_miscdev;
+};
+
+static void omap_wdt_ping(struct omap_wdt_dev *wdev)
{
+ void __iomem *base = wdev->base;
+
/* wait for posted write to complete */
- while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x08)
+ while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08)
cpu_relax();
+
wdt_trgr_pattern = ~wdt_trgr_pattern;
- omap_writel(wdt_trgr_pattern, (OMAP_WATCHDOG_TGR));
+ __raw_writel(wdt_trgr_pattern, (base + OMAP_WATCHDOG_TGR));
+
/* wait for posted write to complete */
- while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x08)
+ while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08)
cpu_relax();
/* reloaded WCRR from WLDR */
}
-static void omap_wdt_enable(void)
+static void omap_wdt_enable(struct omap_wdt_dev *wdev)
{
+ void __iomem *base = wdev->base;
+
/* Sequence to enable the watchdog */
- omap_writel(0xBBBB, OMAP_WATCHDOG_SPR);
- while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x10)
+ __raw_writel(0xBBBB, base + OMAP_WATCHDOG_SPR);
+ while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x10)
cpu_relax();
- omap_writel(0x4444, OMAP_WATCHDOG_SPR);
- while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x10)
+
+ __raw_writel(0x4444, base + OMAP_WATCHDOG_SPR);
+ while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x10)
cpu_relax();
}
-static void omap_wdt_disable(void)
+static void omap_wdt_disable(struct omap_wdt_dev *wdev)
{
+ void __iomem *base = wdev->base;
+
/* sequence required to disable watchdog */
- omap_writel(0xAAAA, OMAP_WATCHDOG_SPR); /* TIMER_MODE */
- while (omap_readl(OMAP_WATCHDOG_WPS) & 0x10)
+ __raw_writel(0xAAAA, base + OMAP_WATCHDOG_SPR); /* TIMER_MODE */
+ while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x10)
cpu_relax();
- omap_writel(0x5555, OMAP_WATCHDOG_SPR); /* TIMER_MODE */
- while (omap_readl(OMAP_WATCHDOG_WPS) & 0x10)
+
+ __raw_writel(0x5555, base + OMAP_WATCHDOG_SPR); /* TIMER_MODE */
+ while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x10)
cpu_relax();
}
@@ -103,83 +121,90 @@
timer_margin = new_timeout;
}
-static void omap_wdt_set_timeout(void)
+static void omap_wdt_set_timeout(struct omap_wdt_dev *wdev)
{
u32 pre_margin = GET_WLDR_VAL(timer_margin);
+ void __iomem *base = wdev->base;
/* just count up at 32 KHz */
- while (omap_readl(OMAP_WATCHDOG_WPS) & 0x04)
+ while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x04)
cpu_relax();
- omap_writel(pre_margin, OMAP_WATCHDOG_LDR);
- while (omap_readl(OMAP_WATCHDOG_WPS) & 0x04)
+
+ __raw_writel(pre_margin, base + OMAP_WATCHDOG_LDR);
+ while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x04)
cpu_relax();
}
/*
* Allow only one task to hold it open
*/
-
static int omap_wdt_open(struct inode *inode, struct file *file)
{
- if (test_and_set_bit(1, (unsigned long *)&omap_wdt_users))
+ struct omap_wdt_dev *wdev = platform_get_drvdata(omap_wdt_dev);
+ void __iomem *base = wdev->base;
+
+ if (test_and_set_bit(1, (unsigned long *)&(wdev->omap_wdt_users)))
return -EBUSY;
if (cpu_is_omap16xx())
- clk_enable(armwdt_ck); /* Enable the clock */
+ clk_enable(wdev->armwdt_ck); /* Enable the clock */
- if (cpu_is_omap24xx()) {
- clk_enable(mpu_wdt_ick); /* Enable the interface clock */
- clk_enable(mpu_wdt_fck); /* Enable the functional clock */
+ if (cpu_is_omap24xx() || cpu_is_omap34xx()) {
+ clk_enable(wdev->mpu_wdt_ick); /* Enable the interface clock */
+ clk_enable(wdev->mpu_wdt_fck); /* Enable the functional clock */
}
/* initialize prescaler */
- while (omap_readl(OMAP_WATCHDOG_WPS) & 0x01)
- cpu_relax();
- omap_writel((1 << 5) | (PTV << 2), OMAP_WATCHDOG_CNTRL);
- while (omap_readl(OMAP_WATCHDOG_WPS) & 0x01)
+ while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01)
cpu_relax();
- omap_wdt_set_timeout();
- omap_wdt_enable();
+ __raw_writel((1 << 5) | (PTV << 2), base + OMAP_WATCHDOG_CNTRL);
+ while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01)
+ cpu_relax();
+
+ file->private_data = (void *) wdev;
+
+ omap_wdt_set_timeout(wdev);
+ omap_wdt_enable(wdev);
+
return nonseekable_open(inode, file);
}
static int omap_wdt_release(struct inode *inode, struct file *file)
{
+ struct omap_wdt_dev *wdev = file->private_data;
+
/*
* Shut off the timer unless NOWAYOUT is defined.
*/
#ifndef CONFIG_WATCHDOG_NOWAYOUT
- omap_wdt_disable();
- if (cpu_is_omap16xx()) {
- clk_disable(armwdt_ck); /* Disable the clock */
- clk_put(armwdt_ck);
- armwdt_ck = NULL;
- }
+ omap_wdt_disable(wdev);
- if (cpu_is_omap24xx()) {
- clk_disable(mpu_wdt_ick); /* Disable the clock */
- clk_disable(mpu_wdt_fck); /* Disable the clock */
- clk_put(mpu_wdt_ick);
- clk_put(mpu_wdt_fck);
- mpu_wdt_ick = NULL;
- mpu_wdt_fck = NULL;
+ if (cpu_is_omap16xx())
+ clk_disable(wdev->armwdt_ck); /* Disable the clock */
+
+ if (cpu_is_omap24xx() || cpu_is_omap34xx()) {
+ clk_disable(wdev->mpu_wdt_ick); /* Disable the clock */
+ clk_disable(wdev->mpu_wdt_fck); /* Disable the clock */
}
#else
printk(KERN_CRIT "omap_wdt: Unexpected close, not stopping!\n");
#endif
- omap_wdt_users = 0;
+ wdev->omap_wdt_users = 0;
+
return 0;
}
static ssize_t omap_wdt_write(struct file *file, const char __user *data,
size_t len, loff_t *ppos)
{
+ struct omap_wdt_dev *wdev = file->private_data;
+
/* Refresh LOAD_TIME. */
if (len) {
spin_lock(&wdt_lock);
- omap_wdt_ping();
+ omap_wdt_ping(wdev);
spin_unlock(&wdt_lock);
}
return len;
@@ -188,6 +213,7 @@
static long omap_wdt_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
+ struct omap_wdt_dev *wdev;
int new_margin;
static const struct watchdog_info ident = {
.identity = "OMAP Watchdog",
@@ -195,6 +221,8 @@
.firmware_version = 0,
};
+ wdev = file->private_data;
+
switch (cmd) {
case WDIOC_GETSUPPORT:
return copy_to_user((struct watchdog_info __user *)arg, &ident,
@@ -203,14 +231,14 @@
return put_user(0, (int __user *)arg);
case WDIOC_GETBOOTSTATUS:
if (cpu_is_omap16xx())
- return put_user(omap_readw(ARM_SYSST),
+ return put_user(__raw_readw(ARM_SYSST),
(int __user *)arg);
if (cpu_is_omap24xx())
return put_user(omap_prcm_get_reset_sources(),
(int __user *)arg);
case WDIOC_KEEPALIVE:
spin_lock(&wdt_lock);
- omap_wdt_ping();
+ omap_wdt_ping(wdev);
spin_unlock(&wdt_lock);
return 0;
case WDIOC_SETTIMEOUT:
@@ -219,11 +247,11 @@
omap_wdt_adjust_timeout(new_margin);
spin_lock(&wdt_lock);
- omap_wdt_disable();
- omap_wdt_set_timeout();
- omap_wdt_enable();
+ omap_wdt_disable(wdev);
+ omap_wdt_set_timeout(wdev);
+ omap_wdt_enable(wdev);
- omap_wdt_ping();
+ omap_wdt_ping(wdev);
spin_unlock(&wdt_lock);
/* Fall */
case WDIOC_GETTIMEOUT:
@@ -241,96 +269,173 @@
.release = omap_wdt_release,
};
-static struct miscdevice omap_wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &omap_wdt_fops,
-};
-
static int __init omap_wdt_probe(struct platform_device *pdev)
{
struct resource *res, *mem;
+ struct omap_wdt_dev *wdev;
int ret;
/* reserve static register mappings */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENOENT;
+ if (!res) {
+ ret = -ENOENT;
+ goto err_get_resource;
+ }
+
+ if (omap_wdt_dev) {
+ ret = -EBUSY;
+ goto err_busy;
+ }
mem = request_mem_region(res->start, res->end - res->start + 1,
pdev->name);
- if (mem == NULL)
- return -EBUSY;
+ if (!mem) {
+ ret = -EBUSY;
+ goto err_busy;
+ }
- platform_set_drvdata(pdev, mem);
+ wdev = kzalloc(sizeof(struct omap_wdt_dev), GFP_KERNEL);
+ if (!wdev) {
+ ret = -ENOMEM;
+ goto err_kzalloc;
+ }
- omap_wdt_users = 0;
+ wdev->omap_wdt_users = 0;
+ wdev->mem = mem;
if (cpu_is_omap16xx()) {
- armwdt_ck = clk_get(&pdev->dev, "armwdt_ck");
- if (IS_ERR(armwdt_ck)) {
- ret = PTR_ERR(armwdt_ck);
- armwdt_ck = NULL;
- goto fail;
+ wdev->armwdt_ck = clk_get(&pdev->dev, "armwdt_ck");
+ if (IS_ERR(wdev->armwdt_ck)) {
+ ret = PTR_ERR(wdev->armwdt_ck);
+ wdev->armwdt_ck = NULL;
+ goto err_clk;
}
}
if (cpu_is_omap24xx()) {
- mpu_wdt_ick = clk_get(&pdev->dev, "mpu_wdt_ick");
- if (IS_ERR(mpu_wdt_ick)) {
- ret = PTR_ERR(mpu_wdt_ick);
- mpu_wdt_ick = NULL;
- goto fail;
+ wdev->mpu_wdt_ick = clk_get(&pdev->dev, "mpu_wdt_ick");
+ if (IS_ERR(wdev->mpu_wdt_ick)) {
+ ret = PTR_ERR(wdev->mpu_wdt_ick);
+ wdev->mpu_wdt_ick = NULL;
+ goto err_clk;
}
- mpu_wdt_fck = clk_get(&pdev->dev, "mpu_wdt_fck");
- if (IS_ERR(mpu_wdt_fck)) {
- ret = PTR_ERR(mpu_wdt_fck);
- mpu_wdt_fck = NULL;
- goto fail;
+ wdev->mpu_wdt_fck = clk_get(&pdev->dev, "mpu_wdt_fck");
+ if (IS_ERR(wdev->mpu_wdt_fck)) {
+ ret = PTR_ERR(wdev->mpu_wdt_fck);
+ wdev->mpu_wdt_fck = NULL;
+ goto err_clk;
}
}
- omap_wdt_disable();
+ if (cpu_is_omap34xx()) {
+ wdev->mpu_wdt_ick = clk_get(&pdev->dev, "wdt2_ick");
+ if (IS_ERR(wdev->mpu_wdt_ick)) {
+ ret = PTR_ERR(wdev->mpu_wdt_ick);
+ wdev->mpu_wdt_ick = NULL;
+ goto err_clk;
+ }
+ wdev->mpu_wdt_fck = clk_get(&pdev->dev, "wdt2_fck");
+ if (IS_ERR(wdev->mpu_wdt_fck)) {
+ ret = PTR_ERR(wdev->mpu_wdt_fck);
+ wdev->mpu_wdt_fck = NULL;
+ goto err_clk;
+ }
+ }
+ wdev->base = ioremap(res->start, res->end - res->start + 1);
+ if (!wdev->base) {
+ ret = -ENOMEM;
+ goto err_ioremap;
+ }
+
+ platform_set_drvdata(pdev, wdev);
+
+ omap_wdt_disable(wdev);
omap_wdt_adjust_timeout(timer_margin);
- omap_wdt_miscdev.parent = &pdev->dev;
- ret = misc_register(&omap_wdt_miscdev);
- if (ret)
- goto fail;
+ wdev->omap_wdt_miscdev.parent = &pdev->dev;
+ wdev->omap_wdt_miscdev.minor = WATCHDOG_MINOR;
+ wdev->omap_wdt_miscdev.name = "watchdog";
+ wdev->omap_wdt_miscdev.fops = &omap_wdt_fops;
- pr_info("OMAP Watchdog Timer: initial timeout %d sec\n", timer_margin);
+ ret = misc_register(&(wdev->omap_wdt_miscdev));
+ if (ret)
+ goto err_misc;
+
+ pr_info("OMAP Watchdog Timer Rev 0x%02x: initial timeout %d sec\n",
+ __raw_readl(wdev->base + OMAP_WATCHDOG_REV) & 0xFF,
+ timer_margin);
/* autogate OCP interface clock */
- omap_writel(0x01, OMAP_WATCHDOG_SYS_CONFIG);
+ __raw_writel(0x01, wdev->base + OMAP_WATCHDOG_SYS_CONFIG);
+
+ omap_wdt_dev = pdev;
+
return 0;
-fail:
- if (armwdt_ck)
- clk_put(armwdt_ck);
- if (mpu_wdt_ick)
- clk_put(mpu_wdt_ick);
- if (mpu_wdt_fck)
- clk_put(mpu_wdt_fck);
- release_resource(mem);
+err_misc:
+ platform_set_drvdata(pdev, NULL);
+ iounmap(wdev->base);
+
+err_ioremap:
+ wdev->base = NULL;
+
+err_clk:
+ if (wdev->armwdt_ck)
+ clk_put(wdev->armwdt_ck);
+ if (wdev->mpu_wdt_ick)
+ clk_put(wdev->mpu_wdt_ick);
+ if (wdev->mpu_wdt_fck)
+ clk_put(wdev->mpu_wdt_fck);
+ kfree(wdev);
+
+err_kzalloc:
+ release_mem_region(res->start, res->end - res->start + 1);
+
+err_busy:
+err_get_resource:
+
return ret;
}
static void omap_wdt_shutdown(struct platform_device *pdev)
{
- omap_wdt_disable();
+ struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
+
+ if (wdev->omap_wdt_users)
+ omap_wdt_disable(wdev);
}
static int omap_wdt_remove(struct platform_device *pdev)
{
- struct resource *mem = platform_get_drvdata(pdev);
- misc_deregister(&omap_wdt_miscdev);
- release_resource(mem);
- if (armwdt_ck)
- clk_put(armwdt_ck);
- if (mpu_wdt_ick)
- clk_put(mpu_wdt_ick);
- if (mpu_wdt_fck)
- clk_put(mpu_wdt_fck);
+ struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!res)
+ return -ENOENT;
+
+ misc_deregister(&(wdev->omap_wdt_miscdev));
+ release_mem_region(res->start, res->end - res->start + 1);
+ platform_set_drvdata(pdev, NULL);
+
+ if (wdev->armwdt_ck) {
+ clk_put(wdev->armwdt_ck);
+ wdev->armwdt_ck = NULL;
+ }
+
+ if (wdev->mpu_wdt_ick) {
+ clk_put(wdev->mpu_wdt_ick);
+ wdev->mpu_wdt_ick = NULL;
+ }
+
+ if (wdev->mpu_wdt_fck) {
+ clk_put(wdev->mpu_wdt_fck);
+ wdev->mpu_wdt_fck = NULL;
+ }
+ iounmap(wdev->base);
+
+ kfree(wdev);
+ omap_wdt_dev = NULL;
+
return 0;
}
@@ -344,17 +449,23 @@
static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state)
{
- if (omap_wdt_users)
- omap_wdt_disable();
+ struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
+
+ if (wdev->omap_wdt_users)
+ omap_wdt_disable(wdev);
+
return 0;
}
static int omap_wdt_resume(struct platform_device *pdev)
{
- if (omap_wdt_users) {
- omap_wdt_enable();
- omap_wdt_ping();
+ struct omap_wdt_dev *wdev = platform_get_drvdata(pdev);
+
+ if (wdev->omap_wdt_users) {
+ omap_wdt_enable(wdev);
+ omap_wdt_ping(wdev);
}
+
return 0;
}
diff --git a/drivers/watchdog/omap_wdt.h b/drivers/watchdog/omap_wdt.h
index 52a532a5..fc02ec6 100644
--- a/drivers/watchdog/omap_wdt.h
+++ b/drivers/watchdog/omap_wdt.h
@@ -30,25 +30,15 @@
#ifndef _OMAP_WATCHDOG_H
#define _OMAP_WATCHDOG_H
-#define OMAP1610_WATCHDOG_BASE 0xfffeb000
-#define OMAP2420_WATCHDOG_BASE 0x48022000 /*WDT Timer 2 */
-
-#ifdef CONFIG_ARCH_OMAP24XX
-#define OMAP_WATCHDOG_BASE OMAP2420_WATCHDOG_BASE
-#else
-#define OMAP_WATCHDOG_BASE OMAP1610_WATCHDOG_BASE
-#define RM_RSTST_WKUP 0
-#endif
-
-#define OMAP_WATCHDOG_REV (OMAP_WATCHDOG_BASE + 0x00)
-#define OMAP_WATCHDOG_SYS_CONFIG (OMAP_WATCHDOG_BASE + 0x10)
-#define OMAP_WATCHDOG_STATUS (OMAP_WATCHDOG_BASE + 0x14)
-#define OMAP_WATCHDOG_CNTRL (OMAP_WATCHDOG_BASE + 0x24)
-#define OMAP_WATCHDOG_CRR (OMAP_WATCHDOG_BASE + 0x28)
-#define OMAP_WATCHDOG_LDR (OMAP_WATCHDOG_BASE + 0x2c)
-#define OMAP_WATCHDOG_TGR (OMAP_WATCHDOG_BASE + 0x30)
-#define OMAP_WATCHDOG_WPS (OMAP_WATCHDOG_BASE + 0x34)
-#define OMAP_WATCHDOG_SPR (OMAP_WATCHDOG_BASE + 0x48)
+#define OMAP_WATCHDOG_REV (0x00)
+#define OMAP_WATCHDOG_SYS_CONFIG (0x10)
+#define OMAP_WATCHDOG_STATUS (0x14)
+#define OMAP_WATCHDOG_CNTRL (0x24)
+#define OMAP_WATCHDOG_CRR (0x28)
+#define OMAP_WATCHDOG_LDR (0x2c)
+#define OMAP_WATCHDOG_TGR (0x30)
+#define OMAP_WATCHDOG_WPS (0x34)
+#define OMAP_WATCHDOG_SPR (0x48)
/* Using the prescaler, the OMAP watchdog could go for many
* months before firing. These limits work without scaling,
diff --git a/drivers/watchdog/orion5x_wdt.c b/drivers/watchdog/orion5x_wdt.c
new file mode 100644
index 0000000..14a339f
--- /dev/null
+++ b/drivers/watchdog/orion5x_wdt.c
@@ -0,0 +1,245 @@
+/*
+ * drivers/watchdog/orion5x_wdt.c
+ *
+ * Watchdog driver for Orion5x processors
+ *
+ * Author: Sylver Bruneau <sylver.bruneau@googlemail.com>
+ *
+ * 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 <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/init.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+
+/*
+ * Watchdog timer block registers.
+ */
+#define TIMER_CTRL (TIMER_VIRT_BASE + 0x0000)
+#define WDT_EN 0x0010
+#define WDT_VAL (TIMER_VIRT_BASE + 0x0024)
+
+#define WDT_MAX_DURATION (0xffffffff / ORION5X_TCLK)
+#define WDT_IN_USE 0
+#define WDT_OK_TO_CLOSE 1
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+static int heartbeat = WDT_MAX_DURATION; /* (seconds) */
+static unsigned long wdt_status;
+static spinlock_t wdt_lock;
+
+static void wdt_enable(void)
+{
+ u32 reg;
+
+ spin_lock(&wdt_lock);
+
+ /* Set watchdog duration */
+ writel(ORION5X_TCLK * heartbeat, WDT_VAL);
+
+ /* Clear watchdog timer interrupt */
+ reg = readl(BRIDGE_CAUSE);
+ reg &= ~WDT_INT_REQ;
+ writel(reg, BRIDGE_CAUSE);
+
+ /* Enable watchdog timer */
+ reg = readl(TIMER_CTRL);
+ reg |= WDT_EN;
+ writel(reg, TIMER_CTRL);
+
+ /* Enable reset on watchdog */
+ reg = readl(CPU_RESET_MASK);
+ reg |= WDT_RESET;
+ writel(reg, CPU_RESET_MASK);
+
+ spin_unlock(&wdt_lock);
+}
+
+static void wdt_disable(void)
+{
+ u32 reg;
+
+ spin_lock(&wdt_lock);
+
+ /* Disable reset on watchdog */
+ reg = readl(CPU_RESET_MASK);
+ reg &= ~WDT_RESET;
+ writel(reg, CPU_RESET_MASK);
+
+ /* Disable watchdog timer */
+ reg = readl(TIMER_CTRL);
+ reg &= ~WDT_EN;
+ writel(reg, TIMER_CTRL);
+
+ spin_unlock(&wdt_lock);
+}
+
+static int orion5x_wdt_get_timeleft(int *time_left)
+{
+ spin_lock(&wdt_lock);
+ *time_left = readl(WDT_VAL) / ORION5X_TCLK;
+ spin_unlock(&wdt_lock);
+ return 0;
+}
+
+static int orion5x_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(WDT_IN_USE, &wdt_status))
+ return -EBUSY;
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ wdt_enable();
+ return nonseekable_open(inode, file);
+}
+
+static ssize_t orion5x_wdt_write(struct file *file, const char *data,
+ size_t len, loff_t *ppos)
+{
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ set_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ }
+ }
+ wdt_enable();
+ }
+ return len;
+}
+
+static struct watchdog_info ident = {
+ .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING,
+ .identity = "Orion5x Watchdog",
+};
+
+
+static long orion5x_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = -ENOTTY;
+ int time;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user((struct watchdog_info *)arg, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(0, (int *)arg);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ wdt_enable();
+ ret = 0;
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(time, (int *)arg);
+ if (ret)
+ break;
+
+ if (time <= 0 || time > WDT_MAX_DURATION) {
+ ret = -EINVAL;
+ break;
+ }
+ heartbeat = time;
+ wdt_enable();
+ /* Fall through */
+
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(heartbeat, (int *)arg);
+ break;
+
+ case WDIOC_GETTIMELEFT:
+ if (orion5x_wdt_get_timeleft(&time)) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = put_user(time, (int *)arg);
+ break;
+ }
+ return ret;
+}
+
+static int orion5x_wdt_release(struct inode *inode, struct file *file)
+{
+ if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
+ wdt_disable();
+ else
+ printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - "
+ "timer will not stop\n");
+ clear_bit(WDT_IN_USE, &wdt_status);
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ return 0;
+}
+
+
+static const struct file_operations orion5x_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = orion5x_wdt_write,
+ .unlocked_ioctl = orion5x_wdt_ioctl,
+ .open = orion5x_wdt_open,
+ .release = orion5x_wdt_release,
+};
+
+static struct miscdevice orion5x_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &orion5x_wdt_fops,
+};
+
+static int __init orion5x_wdt_init(void)
+{
+ int ret;
+
+ spin_lock_init(&wdt_lock);
+
+ ret = misc_register(&orion5x_wdt_miscdev);
+ if (ret == 0)
+ printk("Orion5x Watchdog Timer: heartbeat %d sec\n",
+ heartbeat);
+
+ return ret;
+}
+
+static void __exit orion5x_wdt_exit(void)
+{
+ misc_deregister(&orion5x_wdt_miscdev);
+}
+
+module_init(orion5x_wdt_init);
+module_exit(orion5x_wdt_exit);
+
+MODULE_AUTHOR("Sylver Bruneau <sylver.bruneau@googlemail.com>");
+MODULE_DESCRIPTION("Orion5x Processor Watchdog");
+
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default is "
+ __MODULE_STRING(WDT_MAX_DURATION) ")");
+
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/riowd.c b/drivers/watchdog/riowd.c
new file mode 100644
index 0000000..09cb183
--- /dev/null
+++ b/drivers/watchdog/riowd.c
@@ -0,0 +1,259 @@
+/* riowd.c - driver for hw watchdog inside Super I/O of RIO
+ *
+ * Copyright (C) 2001, 2008 David S. Miller (davem@davemloft.net)
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/smp_lock.h>
+#include <linux/watchdog.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+
+/* RIO uses the NatSemi Super I/O power management logical device
+ * as its' watchdog.
+ *
+ * When the watchdog triggers, it asserts a line to the BBC (Boot Bus
+ * Controller) of the machine. The BBC can only be configured to
+ * trigger a power-on reset when the signal is asserted. The BBC
+ * can be configured to ignore the signal entirely as well.
+ *
+ * The only Super I/O device register we care about is at index
+ * 0x05 (WDTO_INDEX) which is the watchdog time-out in minutes (1-255).
+ * If set to zero, this disables the watchdog. When set, the system
+ * must periodically (before watchdog expires) clear (set to zero) and
+ * re-set the watchdog else it will trigger.
+ *
+ * There are two other indexed watchdog registers inside this Super I/O
+ * logical device, but they are unused. The first, at index 0x06 is
+ * the watchdog control and can be used to make the watchdog timer re-set
+ * when the PS/2 mouse or serial lines show activity. The second, at
+ * index 0x07 is merely a sampling of the line from the watchdog to the
+ * BBC.
+ *
+ * The watchdog device generates no interrupts.
+ */
+
+MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
+MODULE_DESCRIPTION("Hardware watchdog driver for Sun RIO");
+MODULE_SUPPORTED_DEVICE("watchdog");
+MODULE_LICENSE("GPL");
+
+#define DRIVER_NAME "riowd"
+#define PFX DRIVER_NAME ": "
+
+struct riowd {
+ void __iomem *regs;
+ spinlock_t lock;
+};
+
+static struct riowd *riowd_device;
+
+#define WDTO_INDEX 0x05
+
+static int riowd_timeout = 1; /* in minutes */
+module_param(riowd_timeout, int, 0);
+MODULE_PARM_DESC(riowd_timeout, "Watchdog timeout in minutes");
+
+static void riowd_writereg(struct riowd *p, u8 val, int index)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&p->lock, flags);
+ writeb(index, p->regs + 0);
+ writeb(val, p->regs + 1);
+ spin_unlock_irqrestore(&p->lock, flags);
+}
+
+static int riowd_open(struct inode *inode, struct file *filp)
+{
+ cycle_kernel_lock();
+ nonseekable_open(inode, filp);
+ return 0;
+}
+
+static int riowd_release(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+static int riowd_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ static struct watchdog_info info = {
+ .options = WDIOF_SETTIMEOUT,
+ .firmware_version = 1,
+ .identity = DRIVER_NAME,
+ };
+ void __user *argp = (void __user *)arg;
+ struct riowd *p = riowd_device;
+ unsigned int options;
+ int new_margin;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &info, sizeof(info)))
+ return -EFAULT;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ if (put_user(0, (int __user *)argp))
+ return -EFAULT;
+ break;
+
+ case WDIOC_KEEPALIVE:
+ riowd_writereg(p, riowd_timeout, WDTO_INDEX);
+ break;
+
+ case WDIOC_SETOPTIONS:
+ if (copy_from_user(&options, argp, sizeof(options)))
+ return -EFAULT;
+
+ if (options & WDIOS_DISABLECARD)
+ riowd_writereg(p, 0, WDTO_INDEX);
+ else if (options & WDIOS_ENABLECARD)
+ riowd_writereg(p, riowd_timeout, WDTO_INDEX);
+ else
+ return -EINVAL;
+
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_margin, (int __user *)argp))
+ return -EFAULT;
+ if ((new_margin < 60) || (new_margin > (255 * 60)))
+ return -EINVAL;
+ riowd_timeout = (new_margin + 59) / 60;
+ riowd_writereg(p, riowd_timeout, WDTO_INDEX);
+ /* Fall */
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(riowd_timeout * 60, (int __user *)argp);
+
+ default:
+ return -EINVAL;
+ };
+
+ return 0;
+}
+
+static ssize_t riowd_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
+{
+ struct riowd *p = riowd_device;
+
+ if (count) {
+ riowd_writereg(p, riowd_timeout, WDTO_INDEX);
+ return 1;
+ }
+
+ return 0;
+}
+
+static const struct file_operations riowd_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .ioctl = riowd_ioctl,
+ .open = riowd_open,
+ .write = riowd_write,
+ .release = riowd_release,
+};
+
+static struct miscdevice riowd_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &riowd_fops
+};
+
+static int __devinit riowd_probe(struct of_device *op,
+ const struct of_device_id *match)
+{
+ struct riowd *p;
+ int err = -EINVAL;
+
+ if (riowd_device)
+ goto out;
+
+ err = -ENOMEM;
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ goto out;
+
+ spin_lock_init(&p->lock);
+
+ p->regs = of_ioremap(&op->resource[0], 0, 2, DRIVER_NAME);
+ if (!p->regs) {
+ printk(KERN_ERR PFX "Cannot map registers.\n");
+ goto out_free;
+ }
+
+ err = misc_register(&riowd_miscdev);
+ if (err) {
+ printk(KERN_ERR PFX "Cannot register watchdog misc device.\n");
+ goto out_iounmap;
+ }
+
+ printk(KERN_INFO PFX "Hardware watchdog [%i minutes], "
+ "regs at %p\n", riowd_timeout, p->regs);
+
+ dev_set_drvdata(&op->dev, p);
+ riowd_device = p;
+ err = 0;
+
+out_iounmap:
+ of_iounmap(&op->resource[0], p->regs, 2);
+
+out_free:
+ kfree(p);
+
+out:
+ return err;
+}
+
+static int __devexit riowd_remove(struct of_device *op)
+{
+ struct riowd *p = dev_get_drvdata(&op->dev);
+
+ misc_deregister(&riowd_miscdev);
+ of_iounmap(&op->resource[0], p->regs, 2);
+ kfree(p);
+
+ return 0;
+}
+
+static const struct of_device_id riowd_match[] = {
+ {
+ .name = "pmc",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, riowd_match);
+
+static struct of_platform_driver riowd_driver = {
+ .name = DRIVER_NAME,
+ .match_table = riowd_match,
+ .probe = riowd_probe,
+ .remove = __devexit_p(riowd_remove),
+};
+
+static int __init riowd_init(void)
+{
+ return of_register_driver(&riowd_driver, &of_bus_type);
+}
+
+static void __exit riowd_exit(void)
+{
+ of_unregister_driver(&riowd_driver);
+}
+
+module_init(riowd_init);
+module_exit(riowd_exit);
diff --git a/drivers/watchdog/w83697ug_wdt.c b/drivers/watchdog/w83697ug_wdt.c
new file mode 100644
index 0000000..c73b5e2
--- /dev/null
+++ b/drivers/watchdog/w83697ug_wdt.c
@@ -0,0 +1,392 @@
+/*
+ * w83697ug/uf WDT driver
+ *
+ * (c) Copyright 2008 Flemming Fransen <ff@nrvissing.net>
+ * reused original code to supoprt w83697ug/uf.
+ *
+ * Based on w83627hf_wdt.c which is based on advantechwdt.c
+ * which is based on wdt.c.
+ * Original copyright messages:
+ *
+ * (c) Copyright 2007 Vlad Drukker <vlad@storewiz.com>
+ * added support for W83627THF.
+ *
+ * (c) Copyright 2003 Pádraig Brady <P@draigBrady.com>
+ *
+ * (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
+ *
+ * (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
+ * http://www.redhat.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.
+ *
+ * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
+ * warranty for any of this software. This material is provided
+ * "AS-IS" and at no charge.
+ *
+ * (c) Copyright 1995 Alan Cox <alan@redhat.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include <asm/system.h>
+
+#define WATCHDOG_NAME "w83697ug/uf WDT"
+#define PFX WATCHDOG_NAME ": "
+#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
+
+static unsigned long wdt_is_open;
+static char expect_close;
+static DEFINE_SPINLOCK(io_lock);
+
+static int wdt_io = 0x2e;
+module_param(wdt_io, int, 0);
+MODULE_PARM_DESC(wdt_io, "w83697ug/uf WDT io port (default 0x2e)");
+
+static int timeout = WATCHDOG_TIMEOUT; /* in seconds */
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. 1<= timeout <=255 (default="
+ __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/*
+ * Kernel methods.
+ */
+
+#define WDT_EFER (wdt_io+0) /* Extended Function Enable Registers */
+#define WDT_EFIR (wdt_io+0) /* Extended Function Index Register
+ (same as EFER) */
+#define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */
+
+static void w83697ug_select_wd_register(void)
+{
+ unsigned char c;
+ unsigned char version;
+
+ outb_p(0x87, WDT_EFER); /* Enter extended function mode */
+ outb_p(0x87, WDT_EFER); /* Again according to manual */
+
+ outb(0x20, WDT_EFER); /* check chip version */
+ version = inb(WDT_EFDR);
+
+ if (version == 0x68) { /* W83697UG */
+ printk(KERN_INFO PFX "Watchdog chip version 0x%02x = "
+ "W83697UG/UF found at 0x%04x\n", version, wdt_io);
+
+ outb_p(0x2b, WDT_EFER);
+ c = inb_p(WDT_EFDR); /* select WDT0 */
+ c &= ~0x04;
+ outb_p(0x2b, WDT_EFER);
+ outb_p(c, WDT_EFDR); /* set pin118 to WDT0 */
+
+ } else {
+ printk(KERN_ERR PFX "No W83697UG/UF could be found\n");
+ return -EIO;
+ }
+
+ outb_p(0x07, WDT_EFER); /* point to logical device number reg */
+ outb_p(0x08, WDT_EFDR); /* select logical device 8 (GPIO2) */
+ outb_p(0x30, WDT_EFER); /* select CR30 */
+ c = inb_p(WDT_EFDR);
+ outb_p(c || 0x01, WDT_EFDR); /* set bit 0 to activate GPIO2 */
+}
+
+static void w83697ug_unselect_wd_register(void)
+{
+ outb_p(0xAA, WDT_EFER); /* Leave extended function mode */
+}
+
+static void w83697ug_init(void)
+{
+ unsigned char t;
+
+ w83697ug_select_wd_register();
+
+ outb_p(0xF6, WDT_EFER); /* Select CRF6 */
+ t = inb_p(WDT_EFDR); /* read CRF6 */
+ if (t != 0) {
+ printk(KERN_INFO PFX "Watchdog already running."
+ " Resetting timeout to %d sec\n", timeout);
+ outb_p(timeout, WDT_EFDR); /* Write back to CRF6 */
+ }
+ outb_p(0xF5, WDT_EFER); /* Select CRF5 */
+ t = inb_p(WDT_EFDR); /* read CRF5 */
+ t &= ~0x0C; /* set second mode &
+ disable keyboard turning off watchdog */
+ outb_p(t, WDT_EFDR); /* Write back to CRF5 */
+
+ w83697ug_unselect_wd_register();
+}
+
+static void wdt_ctrl(int timeout)
+{
+ spin_lock(&io_lock);
+
+ w83697ug_select_wd_register();
+
+ outb_p(0xF4, WDT_EFER); /* Select CRF4 */
+ outb_p(timeout, WDT_EFDR); /* Write Timeout counter to CRF4 */
+
+ w83697ug_unselect_wd_register();
+
+ spin_unlock(&io_lock);
+}
+
+static int wdt_ping(void)
+{
+ wdt_ctrl(timeout);
+ return 0;
+}
+
+static int wdt_disable(void)
+{
+ wdt_ctrl(0);
+ return 0;
+}
+
+static int wdt_set_heartbeat(int t)
+{
+ if (t < 1 || t > 255)
+ return -EINVAL;
+
+ timeout = t;
+ return 0;
+}
+
+static ssize_t wdt_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ if (count) {
+ if (!nowayout) {
+ size_t i;
+
+ expect_close = 0;
+
+ for (i = 0; i != count; i++) {
+ char c;
+ if (get_user(c, buf + i))
+ return -EFAULT;
+ if (c == 'V')
+ expect_close = 42;
+ }
+ }
+ wdt_ping();
+ }
+ return count;
+}
+
+static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int new_timeout;
+ static const struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING |
+ WDIOF_SETTIMEOUT |
+ WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = "W83697UG WDT",
+ };
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ if (copy_to_user(argp, &ident, sizeof(ident)))
+ return -EFAULT;
+ break;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+
+ case WDIOC_SETOPTIONS:
+ {
+ int options, retval = -EINVAL;
+
+ if (get_user(options, p))
+ return -EFAULT;
+
+ if (options & WDIOS_DISABLECARD) {
+ wdt_disable();
+ retval = 0;
+ }
+
+ if (options & WDIOS_ENABLECARD) {
+ wdt_ping();
+ retval = 0;
+ }
+
+ return retval;
+ }
+
+ case WDIOC_KEEPALIVE:
+ wdt_ping();
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_timeout, p))
+ return -EFAULT;
+ if (wdt_set_heartbeat(new_timeout))
+ return -EINVAL;
+ wdt_ping();
+ /* Fall */
+
+ case WDIOC_GETTIMEOUT:
+ return put_user(timeout, p);
+
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+static int wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &wdt_is_open))
+ return -EBUSY;
+ /*
+ * Activate
+ */
+
+ wdt_ping();
+ return nonseekable_open(inode, file);
+}
+
+static int wdt_close(struct inode *inode, struct file *file)
+{
+ if (expect_close == 42)
+ wdt_disable();
+ else {
+ printk(KERN_CRIT PFX
+ "Unexpected close, not stopping watchdog!\n");
+ wdt_ping();
+ }
+ expect_close = 0;
+ clear_bit(0, &wdt_is_open);
+ return 0;
+}
+
+/*
+ * Notifier for system down
+ */
+
+static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ wdt_disable(); /* Turn the WDT off */
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * Kernel Interfaces
+ */
+
+static const struct file_operations wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = wdt_write,
+ .unlocked_ioctl = wdt_ioctl,
+ .open = wdt_open,
+ .release = wdt_close,
+};
+
+static struct miscdevice wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &wdt_fops,
+};
+
+/*
+ * The WDT needs to learn about soft shutdowns in order to
+ * turn the timebomb registers off.
+ */
+
+static struct notifier_block wdt_notifier = {
+ .notifier_call = wdt_notify_sys,
+};
+
+static int __init wdt_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO "WDT driver for the Winbond(TM) W83697UG/UF Super I/O chip initialising.\n");
+
+ if (wdt_set_heartbeat(timeout)) {
+ wdt_set_heartbeat(WATCHDOG_TIMEOUT);
+ printk(KERN_INFO PFX
+ "timeout value must be 1<=timeout<=255, using %d\n",
+ WATCHDOG_TIMEOUT);
+ }
+
+ if (!request_region(wdt_io, 1, WATCHDOG_NAME)) {
+ printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
+ wdt_io);
+ ret = -EIO;
+ goto out;
+ }
+
+ w83697ug_init();
+
+ ret = register_reboot_notifier(&wdt_notifier);
+ if (ret != 0) {
+ printk(KERN_ERR PFX
+ "cannot register reboot notifier (err=%d)\n", ret);
+ goto unreg_regions;
+ }
+
+ ret = misc_register(&wdt_miscdev);
+ if (ret != 0) {
+ printk(KERN_ERR PFX
+ "cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
+ goto unreg_reboot;
+ }
+
+ printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n",
+ timeout, nowayout);
+
+out:
+ return ret;
+unreg_reboot:
+ unregister_reboot_notifier(&wdt_notifier);
+unreg_regions:
+ release_region(wdt_io, 1);
+ goto out;
+}
+
+static void __exit wdt_exit(void)
+{
+ misc_deregister(&wdt_miscdev);
+ unregister_reboot_notifier(&wdt_notifier);
+ release_region(wdt_io, 1);
+}
+
+module_init(wdt_init);
+module_exit(wdt_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Flemming Frandsen <ff@nrvissing.net>");
+MODULE_DESCRIPTION("w83697ug/uf WDT driver");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile
index 363286c..d2a8fdf 100644
--- a/drivers/xen/Makefile
+++ b/drivers/xen/Makefile
@@ -1,4 +1,5 @@
obj-y += grant-table.o features.o events.o manage.o
obj-y += xenbus/
+obj-$(CONFIG_HOTPLUG_CPU) += cpu_hotplug.o
obj-$(CONFIG_XEN_XENCOMM) += xencomm.o
obj-$(CONFIG_XEN_BALLOON) += balloon.o
diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c
index 2e15da54..8c83abc 100644
--- a/drivers/xen/balloon.c
+++ b/drivers/xen/balloon.c
@@ -53,7 +53,6 @@
#include <asm/tlb.h>
#include <xen/interface/memory.h>
-#include <xen/balloon.h>
#include <xen/xenbus.h>
#include <xen/features.h>
#include <xen/page.h>
@@ -226,9 +225,8 @@
}
set_xen_guest_handle(reservation.extent_start, frame_list);
- reservation.nr_extents = nr_pages;
- rc = HYPERVISOR_memory_op(
- XENMEM_populate_physmap, &reservation);
+ reservation.nr_extents = nr_pages;
+ rc = HYPERVISOR_memory_op(XENMEM_populate_physmap, &reservation);
if (rc < nr_pages) {
if (rc > 0) {
int ret;
@@ -236,7 +234,7 @@
/* We hit the Xen hard limit: reprobe. */
reservation.nr_extents = rc;
ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation,
- &reservation);
+ &reservation);
BUG_ON(ret != rc);
}
if (rc >= 0)
@@ -420,7 +418,7 @@
unsigned long pfn;
struct page *page;
- if (!is_running_on_xen())
+ if (!xen_pv_domain())
return -ENODEV;
pr_info("xen_balloon: Initialising balloon driver.\n");
@@ -464,136 +462,13 @@
module_exit(balloon_exit);
-static void balloon_update_driver_allowance(long delta)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&balloon_lock, flags);
- balloon_stats.driver_pages += delta;
- spin_unlock_irqrestore(&balloon_lock, flags);
-}
-
-static int dealloc_pte_fn(
- pte_t *pte, struct page *pmd_page, unsigned long addr, void *data)
-{
- unsigned long mfn = pte_mfn(*pte);
- int ret;
- struct xen_memory_reservation reservation = {
- .nr_extents = 1,
- .extent_order = 0,
- .domid = DOMID_SELF
- };
- set_xen_guest_handle(reservation.extent_start, &mfn);
- set_pte_at(&init_mm, addr, pte, __pte_ma(0ull));
- set_phys_to_machine(__pa(addr) >> PAGE_SHIFT, INVALID_P2M_ENTRY);
- ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, &reservation);
- BUG_ON(ret != 1);
- return 0;
-}
-
-static struct page **alloc_empty_pages_and_pagevec(int nr_pages)
-{
- unsigned long vaddr, flags;
- struct page *page, **pagevec;
- int i, ret;
-
- pagevec = kmalloc(sizeof(page) * nr_pages, GFP_KERNEL);
- if (pagevec == NULL)
- return NULL;
-
- for (i = 0; i < nr_pages; i++) {
- page = pagevec[i] = alloc_page(GFP_KERNEL);
- if (page == NULL)
- goto err;
-
- vaddr = (unsigned long)page_address(page);
-
- scrub_page(page);
-
- spin_lock_irqsave(&balloon_lock, flags);
-
- if (xen_feature(XENFEAT_auto_translated_physmap)) {
- unsigned long gmfn = page_to_pfn(page);
- struct xen_memory_reservation reservation = {
- .nr_extents = 1,
- .extent_order = 0,
- .domid = DOMID_SELF
- };
- set_xen_guest_handle(reservation.extent_start, &gmfn);
- ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation,
- &reservation);
- if (ret == 1)
- ret = 0; /* success */
- } else {
- ret = apply_to_page_range(&init_mm, vaddr, PAGE_SIZE,
- dealloc_pte_fn, NULL);
- }
-
- if (ret != 0) {
- spin_unlock_irqrestore(&balloon_lock, flags);
- __free_page(page);
- goto err;
- }
-
- totalram_pages = --balloon_stats.current_pages;
-
- spin_unlock_irqrestore(&balloon_lock, flags);
- }
-
- out:
- schedule_work(&balloon_worker);
- flush_tlb_all();
- return pagevec;
-
- err:
- spin_lock_irqsave(&balloon_lock, flags);
- while (--i >= 0)
- balloon_append(pagevec[i]);
- spin_unlock_irqrestore(&balloon_lock, flags);
- kfree(pagevec);
- pagevec = NULL;
- goto out;
-}
-
-static void free_empty_pages_and_pagevec(struct page **pagevec, int nr_pages)
-{
- unsigned long flags;
- int i;
-
- if (pagevec == NULL)
- return;
-
- spin_lock_irqsave(&balloon_lock, flags);
- for (i = 0; i < nr_pages; i++) {
- BUG_ON(page_count(pagevec[i]) != 1);
- balloon_append(pagevec[i]);
- }
- spin_unlock_irqrestore(&balloon_lock, flags);
-
- kfree(pagevec);
-
- schedule_work(&balloon_worker);
-}
-
-static void balloon_release_driver_page(struct page *page)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&balloon_lock, flags);
- balloon_append(page);
- balloon_stats.driver_pages--;
- spin_unlock_irqrestore(&balloon_lock, flags);
-
- schedule_work(&balloon_worker);
-}
-
-
-#define BALLOON_SHOW(name, format, args...) \
- static ssize_t show_##name(struct sys_device *dev, \
- char *buf) \
- { \
- return sprintf(buf, format, ##args); \
- } \
+#define BALLOON_SHOW(name, format, args...) \
+ static ssize_t show_##name(struct sys_device *dev, \
+ struct sysdev_attribute *attr, \
+ char *buf) \
+ { \
+ return sprintf(buf, format, ##args); \
+ } \
static SYSDEV_ATTR(name, S_IRUGO, show_##name, NULL)
BALLOON_SHOW(current_kb, "%lu\n", PAGES2KB(balloon_stats.current_pages));
@@ -604,7 +479,8 @@
(balloon_stats.hard_limit!=~0UL) ? PAGES2KB(balloon_stats.hard_limit) : 0);
BALLOON_SHOW(driver_kb, "%lu\n", PAGES2KB(balloon_stats.driver_pages));
-static ssize_t show_target_kb(struct sys_device *dev, char *buf)
+static ssize_t show_target_kb(struct sys_device *dev, struct sysdev_attribute *attr,
+ char *buf)
{
return sprintf(buf, "%lu\n", PAGES2KB(balloon_stats.target_pages));
}
@@ -614,19 +490,14 @@
const char *buf,
size_t count)
{
- char memstring[64], *endchar;
+ char *endchar;
unsigned long long target_bytes;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- if (count <= 1)
- return -EBADMSG; /* runt */
- if (count > sizeof(memstring))
- return -EFBIG; /* too long */
- strcpy(memstring, buf);
+ target_bytes = memparse(buf, &endchar);
- target_bytes = memparse(memstring, &endchar);
balloon_set_new_target(target_bytes >> PAGE_SHIFT);
return count;
@@ -694,20 +565,4 @@
return error;
}
-static void unregister_balloon(struct sys_device *sysdev)
-{
- int i;
-
- sysfs_remove_group(&sysdev->kobj, &balloon_info_group);
- for (i = 0; i < ARRAY_SIZE(balloon_attrs); i++)
- sysdev_remove_file(sysdev, balloon_attrs[i]);
- sysdev_unregister(sysdev);
- sysdev_class_unregister(&balloon_sysdev_class);
-}
-
-static void balloon_sysfs_exit(void)
-{
- unregister_balloon(&balloon_sysdev);
-}
-
MODULE_LICENSE("GPL");
diff --git a/drivers/xen/cpu_hotplug.c b/drivers/xen/cpu_hotplug.c
new file mode 100644
index 0000000..565280ec
--- /dev/null
+++ b/drivers/xen/cpu_hotplug.c
@@ -0,0 +1,90 @@
+#include <linux/notifier.h>
+
+#include <xen/xenbus.h>
+
+#include <asm-x86/xen/hypervisor.h>
+#include <asm/cpu.h>
+
+static void enable_hotplug_cpu(int cpu)
+{
+ if (!cpu_present(cpu))
+ arch_register_cpu(cpu);
+
+ cpu_set(cpu, cpu_present_map);
+}
+
+static void disable_hotplug_cpu(int cpu)
+{
+ if (cpu_present(cpu))
+ arch_unregister_cpu(cpu);
+
+ cpu_clear(cpu, cpu_present_map);
+}
+
+static void vcpu_hotplug(unsigned int cpu)
+{
+ int err;
+ char dir[32], state[32];
+
+ if (!cpu_possible(cpu))
+ return;
+
+ sprintf(dir, "cpu/%u", cpu);
+ err = xenbus_scanf(XBT_NIL, dir, "availability", "%s", state);
+ if (err != 1) {
+ printk(KERN_ERR "XENBUS: Unable to read cpu state\n");
+ return;
+ }
+
+ if (strcmp(state, "online") == 0) {
+ enable_hotplug_cpu(cpu);
+ } else if (strcmp(state, "offline") == 0) {
+ (void)cpu_down(cpu);
+ disable_hotplug_cpu(cpu);
+ } else {
+ printk(KERN_ERR "XENBUS: unknown state(%s) on CPU%d\n",
+ state, cpu);
+ }
+}
+
+static void handle_vcpu_hotplug_event(struct xenbus_watch *watch,
+ const char **vec, unsigned int len)
+{
+ unsigned int cpu;
+ char *cpustr;
+ const char *node = vec[XS_WATCH_PATH];
+
+ cpustr = strstr(node, "cpu/");
+ if (cpustr != NULL) {
+ sscanf(cpustr, "cpu/%u", &cpu);
+ vcpu_hotplug(cpu);
+ }
+}
+
+static int setup_cpu_watcher(struct notifier_block *notifier,
+ unsigned long event, void *data)
+{
+ static struct xenbus_watch cpu_watch = {
+ .node = "cpu",
+ .callback = handle_vcpu_hotplug_event};
+
+ (void)register_xenbus_watch(&cpu_watch);
+
+ return NOTIFY_DONE;
+}
+
+static int __init setup_vcpu_hotplug_event(void)
+{
+ static struct notifier_block xsn_cpu = {
+ .notifier_call = setup_cpu_watcher };
+
+ if (!xen_pv_domain())
+ return -ENODEV;
+
+ register_xenstore_notifier(&xsn_cpu);
+
+ return 0;
+}
+
+arch_initcall(setup_vcpu_hotplug_event);
+
diff --git a/drivers/xen/events.c b/drivers/xen/events.c
index 0e0c285..c3290bc 100644
--- a/drivers/xen/events.c
+++ b/drivers/xen/events.c
@@ -84,17 +84,6 @@
/* Xen will never allocate port zero for any purpose. */
#define VALID_EVTCHN(chn) ((chn) != 0)
-/*
- * Force a proper event-channel callback from Xen after clearing the
- * callback mask. We do this in a very simple manner, by making a call
- * down into Xen. The pending flag will be checked by Xen on return.
- */
-void force_evtchn_callback(void)
-{
- (void)HYPERVISOR_xen_version(0, NULL);
-}
-EXPORT_SYMBOL_GPL(force_evtchn_callback);
-
static struct irq_chip xen_dynamic_chip;
/* Constructor for packed IRQ information. */
@@ -175,6 +164,12 @@
sync_set_bit(port, &s->evtchn_pending[0]);
}
+static inline int test_evtchn(int port)
+{
+ struct shared_info *s = HYPERVISOR_shared_info;
+ return sync_test_bit(port, &s->evtchn_pending[0]);
+}
+
/**
* notify_remote_via_irq - send event to remote end of event channel via irq
@@ -365,6 +360,10 @@
per_cpu(virq_to_irq, cpu_from_evtchn(evtchn))
[index_from_irq(irq)] = -1;
break;
+ case IRQT_IPI:
+ per_cpu(ipi_to_irq, cpu_from_evtchn(evtchn))
+ [index_from_irq(irq)] = -1;
+ break;
default:
break;
}
@@ -743,6 +742,25 @@
clear_evtchn(evtchn);
}
+void xen_set_irq_pending(int irq)
+{
+ int evtchn = evtchn_from_irq(irq);
+
+ if (VALID_EVTCHN(evtchn))
+ set_evtchn(evtchn);
+}
+
+bool xen_test_irq_pending(int irq)
+{
+ int evtchn = evtchn_from_irq(irq);
+ bool ret = false;
+
+ if (VALID_EVTCHN(evtchn))
+ ret = test_evtchn(evtchn);
+
+ return ret;
+}
+
/* Poll waiting for an irq to become pending. In the usual case, the
irq will be disabled so it won't deliver an interrupt. */
void xen_poll_irq(int irq)
diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c
index e9e1116..06592b9 100644
--- a/drivers/xen/grant-table.c
+++ b/drivers/xen/grant-table.c
@@ -508,7 +508,7 @@
unsigned int max_nr_glist_frames, nr_glist_frames;
unsigned int nr_init_grefs;
- if (!is_running_on_xen())
+ if (!xen_domain())
return -ENODEV;
nr_grant_frames = 1;
diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c
index 57ceb53..7f24a98 100644
--- a/drivers/xen/xenbus/xenbus_probe.c
+++ b/drivers/xen/xenbus/xenbus_probe.c
@@ -814,7 +814,7 @@
DPRINTK("");
err = -ENODEV;
- if (!is_running_on_xen())
+ if (!xen_domain())
goto out_error;
/* Register ourselves with the kernel bus subsystem */
@@ -829,7 +829,7 @@
/*
* Domain0 doesn't have a store_evtchn or store_mfn yet.
*/
- if (is_initial_xendomain()) {
+ if (xen_initial_domain()) {
/* dom0 not yet supported */
} else {
xenstored_ready = 1;
@@ -846,7 +846,7 @@
goto out_unreg_back;
}
- if (!is_initial_xendomain())
+ if (!xen_initial_domain())
xenbus_probe(NULL);
return 0;
@@ -937,7 +937,7 @@
unsigned long timeout = jiffies + 10*HZ;
struct device_driver *drv = xendrv ? &xendrv->driver : NULL;
- if (!ready_to_wait_for_devices || !is_running_on_xen())
+ if (!ready_to_wait_for_devices || !xen_domain())
return;
while (exists_disconnected_device(drv)) {
diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c
index 047c791..c061c3f 100644
--- a/fs/9p/v9fs.c
+++ b/fs/9p/v9fs.c
@@ -55,7 +55,7 @@
Opt_err
};
-static match_table_t tokens = {
+static const match_table_t tokens = {
{Opt_debug, "debug=%x"},
{Opt_dfltuid, "dfltuid=%u"},
{Opt_dfltgid, "dfltgid=%u"},
diff --git a/fs/Kconfig b/fs/Kconfig
index abccb5d..f54a157 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -136,37 +136,51 @@
If you are not using a security module that requires using
extended attributes for file security labels, say N.
-config EXT4DEV_FS
- tristate "Ext4dev/ext4 extended fs support development (EXPERIMENTAL)"
- depends on EXPERIMENTAL
+config EXT4_FS
+ tristate "The Extended 4 (ext4) filesystem"
select JBD2
select CRC16
help
- Ext4dev is a predecessor filesystem of the next generation
- extended fs ext4, based on ext3 filesystem code. It will be
- renamed ext4 fs later, once ext4dev is mature and stabilized.
+ This is the next generation of the ext3 filesystem.
Unlike the change from ext2 filesystem to ext3 filesystem,
- the on-disk format of ext4dev is not the same as ext3 any more:
- it is based on extent maps and it supports 48-bit physical block
- numbers. These combined on-disk format changes will allow
- ext4dev/ext4 to handle more than 16 TB filesystem volumes --
- a hard limit that ext3 cannot overcome without changing the
- on-disk format.
+ the on-disk format of ext4 is not forwards compatible with
+ ext3; it is based on extent maps and it supports 48-bit
+ physical block numbers. The ext4 filesystem also supports delayed
+ allocation, persistent preallocation, high resolution time stamps,
+ and a number of other features to improve performance and speed
+ up fsck time. For more information, please see the web pages at
+ http://ext4.wiki.kernel.org.
- Other than extent maps and 48-bit block numbers, ext4dev also is
- likely to have other new features such as persistent preallocation,
- high resolution time stamps, and larger file support etc. These
- features will be added to ext4dev gradually.
+ The ext4 filesystem will support mounting an ext3
+ filesystem; while there will be some performance gains from
+ the delayed allocation and inode table readahead, the best
+ performance gains will require enabling ext4 features in the
+ filesystem, or formating a new filesystem as an ext4
+ filesystem initially.
To compile this file system support as a module, choose M here. The
module will be called ext4dev.
If unsure, say N.
-config EXT4DEV_FS_XATTR
- bool "Ext4dev extended attributes"
- depends on EXT4DEV_FS
+config EXT4DEV_COMPAT
+ bool "Enable ext4dev compatibility"
+ depends on EXT4_FS
+ help
+ Starting with 2.6.28, the name of the ext4 filesystem was
+ renamed from ext4dev to ext4. Unfortunately there are some
+ legacy userspace programs (such as klibc's fstype) have
+ "ext4dev" hardcoded.
+
+ To enable backwards compatibility so that systems that are
+ still expecting to mount ext4 filesystems using ext4dev,
+ chose Y here. This feature will go away by 2.6.31, so
+ please arrange to get your userspace programs fixed!
+
+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
@@ -175,11 +189,11 @@
If unsure, say N.
- You need this for POSIX ACL support on ext4dev/ext4.
+ You need this for POSIX ACL support on ext4.
-config EXT4DEV_FS_POSIX_ACL
- bool "Ext4dev POSIX Access Control Lists"
- depends on EXT4DEV_FS_XATTR
+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
@@ -190,14 +204,14 @@
If you don't know what Access Control Lists are, say N
-config EXT4DEV_FS_SECURITY
- bool "Ext4dev Security Labels"
- depends on EXT4DEV_FS_XATTR
+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
enables an extended attribute handler for file security
- labels in the ext4dev/ext4 filesystem.
+ labels in the ext4 filesystem.
If you are not using a security module that requires using
extended attributes for file security labels, say N.
@@ -240,22 +254,22 @@
help
This is a generic journaling layer for block devices that support
both 32-bit and 64-bit block numbers. It is currently used by
- the ext4dev/ext4 filesystem, but it could also be used to add
+ the ext4 filesystem, but it could also be used to add
journal support to other file systems or block devices such
as RAID or LVM.
- If you are using ext4dev/ext4, you need to say Y here. If you are not
- using ext4dev/ext4 then you will probably want to say N.
+ If you are using ext4, you need to say Y here. If you are not
+ using ext4 then you will probably want to say N.
To compile this device as a module, choose M here. The module will be
- called jbd2. If you are compiling ext4dev/ext4 into the kernel,
+ called jbd2. If you are compiling ext4 into the kernel,
you cannot compile this code as a module.
config JBD2_DEBUG
- bool "JBD2 (ext4dev/ext4) debugging support"
+ bool "JBD2 (ext4) debugging support"
depends on JBD2 && DEBUG_FS
help
- If you are using the ext4dev/ext4 journaled file system (or
+ If you are using the ext4 journaled file system (or
potentially any other filesystem/device using JBD2), this option
allows you to enable debugging output while the system is running,
in order to help track down any problems you are having.
@@ -270,9 +284,9 @@
config FS_MBCACHE
# Meta block cache for Extended Attributes (ext2/ext3/ext4)
tristate
- depends on EXT2_FS_XATTR || EXT3_FS_XATTR || EXT4DEV_FS_XATTR
- default y if EXT2_FS=y || EXT3_FS=y || EXT4DEV_FS=y
- default m if EXT2_FS=m || EXT3_FS=m || EXT4DEV_FS=m
+ depends on EXT2_FS_XATTR || EXT3_FS_XATTR || EXT4_FS_XATTR
+ default y if EXT2_FS=y || EXT3_FS=y || EXT4_FS=y
+ default m if EXT2_FS=m || EXT3_FS=m || EXT4_FS=m
config REISERFS_FS
tristate "Reiserfs support"
diff --git a/fs/Kconfig.binfmt b/fs/Kconfig.binfmt
index 4a551af..17c9c5e 100644
--- a/fs/Kconfig.binfmt
+++ b/fs/Kconfig.binfmt
@@ -59,10 +59,12 @@
help
Support FLAT shared libraries
+config HAVE_AOUT
+ def_bool n
+
config BINFMT_AOUT
tristate "Kernel support for a.out and ECOFF binaries"
- depends on ARCH_SUPPORTS_AOUT && \
- (X86_32 || ALPHA || ARM || M68K)
+ depends on HAVE_AOUT
---help---
A.out (Assembler.OUTput) is a set of formats for libraries and
executables used in the earliest versions of UNIX. Linux used
diff --git a/fs/Makefile b/fs/Makefile
index a1482a5..de404b0 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -69,7 +69,7 @@
# Do not add any filesystems before this line
obj-$(CONFIG_REISERFS_FS) += reiserfs/
obj-$(CONFIG_EXT3_FS) += ext3/ # Before ext2 so root fs can be ext3
-obj-$(CONFIG_EXT4DEV_FS) += ext4/ # Before ext2 so root fs can be ext4dev
+obj-$(CONFIG_EXT4_FS) += ext4/ # Before ext2 so root fs can be ext4dev
obj-$(CONFIG_JBD) += jbd/
obj-$(CONFIG_JBD2) += jbd2/
obj-$(CONFIG_EXT2_FS) += ext2/
diff --git a/fs/adfs/super.c b/fs/adfs/super.c
index 26f3b43..7f83a46 100644
--- a/fs/adfs/super.c
+++ b/fs/adfs/super.c
@@ -157,7 +157,7 @@
enum {Opt_uid, Opt_gid, Opt_ownmask, Opt_othmask, Opt_err};
-static match_table_t tokens = {
+static const match_table_t tokens = {
{Opt_uid, "uid=%u"},
{Opt_gid, "gid=%u"},
{Opt_ownmask, "ownmask=%o"},
diff --git a/fs/affs/super.c b/fs/affs/super.c
index 3a89094..8989c93 100644
--- a/fs/affs/super.c
+++ b/fs/affs/super.c
@@ -135,7 +135,7 @@
Opt_verbose, Opt_volume, Opt_ignore, Opt_err,
};
-static match_table_t tokens = {
+static const match_table_t tokens = {
{Opt_bs, "bs=%u"},
{Opt_mode, "mode=%o"},
{Opt_mufs, "mufs"},
diff --git a/fs/afs/super.c b/fs/afs/super.c
index 250d8c4..aee239a 100644
--- a/fs/afs/super.c
+++ b/fs/afs/super.c
@@ -64,7 +64,7 @@
afs_opt_vol,
};
-static match_table_t afs_options_list = {
+static const match_table_t afs_options_list = {
{ afs_opt_cell, "cell=%s" },
{ afs_opt_rwpath, "rwpath" },
{ afs_opt_vol, "vol=%s" },
diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c
index dda510d..b70eea1 100644
--- a/fs/autofs/inode.c
+++ b/fs/autofs/inode.c
@@ -59,7 +59,7 @@
enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto};
-static match_table_t autofs_tokens = {
+static const match_table_t autofs_tokens = {
{Opt_fd, "fd=%u"},
{Opt_uid, "uid=%u"},
{Opt_gid, "gid=%u"},
diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c
index 7bb3e5b..45d5581 100644
--- a/fs/autofs4/inode.c
+++ b/fs/autofs4/inode.c
@@ -213,7 +213,7 @@
enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto,
Opt_indirect, Opt_direct, Opt_offset};
-static match_table_t tokens = {
+static const match_table_t tokens = {
{Opt_fd, "fd=%u"},
{Opt_uid, "uid=%u"},
{Opt_gid, "gid=%u"},
diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c
index 740f536..9286b2a 100644
--- a/fs/befs/linuxvfs.c
+++ b/fs/befs/linuxvfs.c
@@ -650,7 +650,7 @@
Opt_uid, Opt_gid, Opt_charset, Opt_debug, Opt_err,
};
-static match_table_t befs_tokens = {
+static const match_table_t befs_tokens = {
{Opt_uid, "uid=%d"},
{Opt_gid, "gid=%d"},
{Opt_charset, "iocharset=%s"},
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 654d972..88786ba 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -311,8 +311,6 @@
buffer->Flags2 = SMBFLG2_KNOWS_LONG_NAMES;
buffer->Pid = cpu_to_le16((__u16)current->tgid);
buffer->PidHigh = cpu_to_le16((__u16)(current->tgid >> 16));
- spin_lock(&GlobalMid_Lock);
- spin_unlock(&GlobalMid_Lock);
if (treeCon) {
buffer->Tid = treeCon->tid;
if (treeCon->ses) {
diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c
index 08e28c9..3dbe216 100644
--- a/fs/debugfs/inode.c
+++ b/fs/debugfs/inode.c
@@ -26,8 +26,7 @@
#include <linux/debugfs.h>
#include <linux/fsnotify.h>
#include <linux/string.h>
-
-#define DEBUGFS_MAGIC 0x64626720
+#include <linux/magic.h>
static struct vfsmount *debugfs_mount;
static int debugfs_mount_count;
diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c
index 488eb42..4a714f6c 100644
--- a/fs/devpts/inode.c
+++ b/fs/devpts/inode.c
@@ -27,6 +27,7 @@
#define DEVPTS_SUPER_MAGIC 0x1cd1
#define DEVPTS_DEFAULT_MODE 0600
+#define PTMX_MINOR 2
extern int pty_limit; /* Config limit on Unix98 ptys */
static DEFINE_IDA(allocated_ptys);
@@ -48,7 +49,7 @@
Opt_err
};
-static match_table_t tokens = {
+static const match_table_t tokens = {
{Opt_uid, "uid=%u"},
{Opt_gid, "gid=%u"},
{Opt_mode, "mode=%o"},
@@ -169,15 +170,7 @@
* to the System V naming convention
*/
-static struct dentry *get_node(int num)
-{
- char s[12];
- struct dentry *root = devpts_root;
- mutex_lock(&root->d_inode->i_mutex);
- return lookup_one_len(s, root, sprintf(s, "%d", num));
-}
-
-int devpts_new_index(void)
+int devpts_new_index(struct inode *ptmx_inode)
{
int index;
int ida_ret;
@@ -205,20 +198,21 @@
return index;
}
-void devpts_kill_index(int idx)
+void devpts_kill_index(struct inode *ptmx_inode, int idx)
{
mutex_lock(&allocated_ptys_lock);
ida_remove(&allocated_ptys, idx);
mutex_unlock(&allocated_ptys_lock);
}
-int devpts_pty_new(struct tty_struct *tty)
+int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty)
{
int number = tty->index; /* tty layer puts index from devpts_new_index() in here */
struct tty_driver *driver = tty->driver;
dev_t device = MKDEV(driver->major, driver->minor_start+number);
struct dentry *dentry;
struct inode *inode = new_inode(devpts_mnt->mnt_sb);
+ char s[12];
/* We're supposed to be given the slave end of a pty */
BUG_ON(driver->type != TTY_DRIVER_TYPE_PTY);
@@ -233,10 +227,15 @@
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
init_special_inode(inode, S_IFCHR|config.mode, device);
inode->i_private = tty;
+ tty->driver_data = inode;
- dentry = get_node(number);
- if (!IS_ERR(dentry) && !dentry->d_inode) {
- d_instantiate(dentry, inode);
+ sprintf(s, "%d", number);
+
+ mutex_lock(&devpts_root->d_inode->i_mutex);
+
+ dentry = d_alloc_name(devpts_root, s);
+ if (!IS_ERR(dentry)) {
+ d_add(dentry, inode);
fsnotify_create(devpts_root->d_inode, dentry);
}
@@ -245,36 +244,31 @@
return 0;
}
-struct tty_struct *devpts_get_tty(int number)
+struct tty_struct *devpts_get_tty(struct inode *pts_inode, int number)
{
- struct dentry *dentry = get_node(number);
- struct tty_struct *tty;
+ BUG_ON(pts_inode->i_rdev == MKDEV(TTYAUX_MAJOR, PTMX_MINOR));
- tty = NULL;
- if (!IS_ERR(dentry)) {
- if (dentry->d_inode)
- tty = dentry->d_inode->i_private;
- dput(dentry);
- }
-
- mutex_unlock(&devpts_root->d_inode->i_mutex);
-
- return tty;
+ if (pts_inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC)
+ return (struct tty_struct *)pts_inode->i_private;
+ return NULL;
}
-void devpts_pty_kill(int number)
+void devpts_pty_kill(struct tty_struct *tty)
{
- struct dentry *dentry = get_node(number);
+ struct inode *inode = tty->driver_data;
+ struct dentry *dentry;
- if (!IS_ERR(dentry)) {
- struct inode *inode = dentry->d_inode;
- if (inode) {
- inode->i_nlink--;
- d_delete(dentry);
- dput(dentry);
- }
+ BUG_ON(inode->i_rdev == MKDEV(TTYAUX_MAJOR, PTMX_MINOR));
+
+ mutex_lock(&devpts_root->d_inode->i_mutex);
+
+ dentry = d_find_alias(inode);
+ if (dentry && !IS_ERR(dentry)) {
+ inode->i_nlink--;
+ d_delete(dentry);
dput(dentry);
}
+
mutex_unlock(&devpts_root->d_inode->i_mutex);
}
diff --git a/fs/dquot.c b/fs/dquot.c
index 8ec4d6c..ad7e590 100644
--- a/fs/dquot.c
+++ b/fs/dquot.c
@@ -895,10 +895,9 @@
warntype == QUOTA_NL_BSOFTBELOW || !need_print_warning(dquot))
return;
- mutex_lock(&tty_mutex);
tty = get_current_tty();
if (!tty)
- goto out_lock;
+ return;
tty_write_message(tty, dquot->dq_sb->s_id);
if (warntype == QUOTA_NL_ISOFTWARN || warntype == QUOTA_NL_BSOFTWARN)
tty_write_message(tty, ": warning, ");
@@ -926,8 +925,7 @@
break;
}
tty_write_message(tty, msg);
-out_lock:
- mutex_unlock(&tty_mutex);
+ tty_kref_put(tty);
}
#endif
diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c
index 448dfd5..8ebe9a5 100644
--- a/fs/ecryptfs/main.c
+++ b/fs/ecryptfs/main.c
@@ -211,7 +211,7 @@
ecryptfs_opt_passthrough, ecryptfs_opt_xattr_metadata,
ecryptfs_opt_encrypted_view, ecryptfs_opt_err };
-static match_table_t tokens = {
+static const match_table_t tokens = {
{ecryptfs_opt_sig, "sig=%s"},
{ecryptfs_opt_ecryptfs_sig, "ecryptfs_sig=%s"},
{ecryptfs_opt_cipher, "cipher=%s"},
diff --git a/fs/efs/super.c b/fs/efs/super.c
index 567b134..73b19cf 100644
--- a/fs/efs/super.c
+++ b/fs/efs/super.c
@@ -341,8 +341,6 @@
sb->inode_blocks *
(EFS_BLOCKSIZE / sizeof(struct efs_dinode));
buf->f_ffree = sb->inode_free; /* free inodes */
- buf->f_fsid.val[0] = (sb->fs_magic >> 16) & 0xffff; /* fs ID */
- buf->f_fsid.val[1] = sb->fs_magic & 0xffff; /* fs ID */
buf->f_namelen = EFS_MAXNAMELEN; /* max filename length */
return 0;
diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h
index 47d88da..bae998c 100644
--- a/fs/ext2/ext2.h
+++ b/fs/ext2/ext2.h
@@ -133,6 +133,8 @@
extern int ext2_setattr (struct dentry *, struct iattr *);
extern void ext2_set_inode_flags(struct inode *inode);
extern void ext2_get_inode_flags(struct ext2_inode_info *);
+extern int ext2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
+ u64 start, u64 len);
int __ext2_write_begin(struct file *file, struct address_space *mapping,
loff_t pos, unsigned len, unsigned flags,
struct page **pagep, void **fsdata);
diff --git a/fs/ext2/file.c b/fs/ext2/file.c
index 5f2fa9c..45ed071 100644
--- a/fs/ext2/file.c
+++ b/fs/ext2/file.c
@@ -86,4 +86,5 @@
#endif
.setattr = ext2_setattr,
.permission = ext2_permission,
+ .fiemap = ext2_fiemap,
};
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index 991d6df..7658b33 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -31,6 +31,7 @@
#include <linux/writeback.h>
#include <linux/buffer_head.h>
#include <linux/mpage.h>
+#include <linux/fiemap.h>
#include "ext2.h"
#include "acl.h"
#include "xip.h"
@@ -704,6 +705,13 @@
}
+int ext2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
+ u64 start, u64 len)
+{
+ return generic_block_fiemap(inode, fieinfo, start, len,
+ ext2_get_block);
+}
+
static int ext2_writepage(struct page *page, struct writeback_control *wbc)
{
return block_write_full_page(page, ext2_get_block, wbc);
diff --git a/fs/ext2/super.c b/fs/ext2/super.c
index fd88c7b..647cd88 100644
--- a/fs/ext2/super.c
+++ b/fs/ext2/super.c
@@ -393,7 +393,7 @@
Opt_usrquota, Opt_grpquota, Opt_reservation, Opt_noreservation
};
-static match_table_t tokens = {
+static const match_table_t tokens = {
{Opt_bsd_df, "bsddf"},
{Opt_minix_df, "minixdf"},
{Opt_grpid, "grpid"},
diff --git a/fs/ext3/file.c b/fs/ext3/file.c
index acc4913..3be1e06 100644
--- a/fs/ext3/file.c
+++ b/fs/ext3/file.c
@@ -134,5 +134,6 @@
.removexattr = generic_removexattr,
#endif
.permission = ext3_permission,
+ .fiemap = ext3_fiemap,
};
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c
index 507d868..ebfec4d 100644
--- a/fs/ext3/inode.c
+++ b/fs/ext3/inode.c
@@ -36,6 +36,7 @@
#include <linux/mpage.h>
#include <linux/uio.h>
#include <linux/bio.h>
+#include <linux/fiemap.h>
#include "xattr.h"
#include "acl.h"
@@ -981,6 +982,13 @@
return ret;
}
+int ext3_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
+ u64 start, u64 len)
+{
+ return generic_block_fiemap(inode, fieinfo, start, len,
+ ext3_get_block);
+}
+
/*
* `handle' can be NULL if create is zero
*/
diff --git a/fs/ext3/super.c b/fs/ext3/super.c
index f38a5af..399a96a 100644
--- a/fs/ext3/super.c
+++ b/fs/ext3/super.c
@@ -760,7 +760,7 @@
Opt_grpquota
};
-static match_table_t tokens = {
+static const match_table_t tokens = {
{Opt_bsd_df, "bsddf"},
{Opt_minix_df, "minixdf"},
{Opt_grpid, "grpid"},
diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
index ac6fa8c..a8ff003 100644
--- a/fs/ext4/Makefile
+++ b/fs/ext4/Makefile
@@ -2,12 +2,12 @@
# Makefile for the linux ext4-filesystem routines.
#
-obj-$(CONFIG_EXT4DEV_FS) += ext4dev.o
+obj-$(CONFIG_EXT4_FS) += ext4.o
-ext4dev-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \
+ext4-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \
ioctl.o namei.o super.o symlink.o hash.o resize.o extents.o \
ext4_jbd2.o migrate.o mballoc.o
-ext4dev-$(CONFIG_EXT4DEV_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o
-ext4dev-$(CONFIG_EXT4DEV_FS_POSIX_ACL) += acl.o
-ext4dev-$(CONFIG_EXT4DEV_FS_SECURITY) += xattr_security.o
+ext4-$(CONFIG_EXT4_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o
+ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o
+ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o
diff --git a/fs/ext4/acl.h b/fs/ext4/acl.h
index cd2b855..cb45257 100644
--- a/fs/ext4/acl.h
+++ b/fs/ext4/acl.h
@@ -51,18 +51,18 @@
}
}
-#ifdef CONFIG_EXT4DEV_FS_POSIX_ACL
+#ifdef CONFIG_EXT4_FS_POSIX_ACL
/* Value for inode->u.ext4_i.i_acl and inode->u.ext4_i.i_default_acl
if the ACL has not been cached */
#define EXT4_ACL_NOT_CACHED ((void *)-1)
/* acl.c */
-extern int ext4_permission (struct inode *, int);
-extern int ext4_acl_chmod (struct inode *);
-extern int ext4_init_acl (handle_t *, struct inode *, struct inode *);
+extern int ext4_permission(struct inode *, int);
+extern int ext4_acl_chmod(struct inode *);
+extern int ext4_init_acl(handle_t *, struct inode *, struct inode *);
-#else /* CONFIG_EXT4DEV_FS_POSIX_ACL */
+#else /* CONFIG_EXT4_FS_POSIX_ACL */
#include <linux/sched.h>
#define ext4_permission NULL
@@ -77,5 +77,5 @@
{
return 0;
}
-#endif /* CONFIG_EXT4DEV_FS_POSIX_ACL */
+#endif /* CONFIG_EXT4_FS_POSIX_ACL */
diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c
index e9fa960..bd2ece2 100644
--- a/fs/ext4/balloc.c
+++ b/fs/ext4/balloc.c
@@ -83,6 +83,7 @@
}
return used_blocks;
}
+
/* Initializes an uninitialized block bitmap if given, and returns the
* number of blocks free in the group. */
unsigned ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh,
@@ -132,7 +133,7 @@
*/
group_blocks = ext4_blocks_count(sbi->s_es) -
le32_to_cpu(sbi->s_es->s_first_data_block) -
- (EXT4_BLOCKS_PER_GROUP(sb) * (sbi->s_groups_count -1));
+ (EXT4_BLOCKS_PER_GROUP(sb) * (sbi->s_groups_count - 1));
} else {
group_blocks = EXT4_BLOCKS_PER_GROUP(sb);
}
@@ -200,20 +201,20 @@
* @bh: pointer to the buffer head to store the block
* group descriptor
*/
-struct ext4_group_desc * ext4_get_group_desc(struct super_block * sb,
+struct ext4_group_desc * ext4_get_group_desc(struct super_block *sb,
ext4_group_t block_group,
- struct buffer_head ** bh)
+ struct buffer_head **bh)
{
unsigned long group_desc;
unsigned long offset;
- struct ext4_group_desc * desc;
+ struct ext4_group_desc *desc;
struct ext4_sb_info *sbi = EXT4_SB(sb);
if (block_group >= sbi->s_groups_count) {
- ext4_error (sb, "ext4_get_group_desc",
- "block_group >= groups_count - "
- "block_group = %lu, groups_count = %lu",
- block_group, sbi->s_groups_count);
+ ext4_error(sb, "ext4_get_group_desc",
+ "block_group >= groups_count - "
+ "block_group = %lu, groups_count = %lu",
+ block_group, sbi->s_groups_count);
return NULL;
}
@@ -222,10 +223,10 @@
group_desc = block_group >> EXT4_DESC_PER_BLOCK_BITS(sb);
offset = block_group & (EXT4_DESC_PER_BLOCK(sb) - 1);
if (!sbi->s_group_desc[group_desc]) {
- ext4_error (sb, "ext4_get_group_desc",
- "Group descriptor not loaded - "
- "block_group = %lu, group_desc = %lu, desc = %lu",
- block_group, group_desc, offset);
+ ext4_error(sb, "ext4_get_group_desc",
+ "Group descriptor not loaded - "
+ "block_group = %lu, group_desc = %lu, desc = %lu",
+ block_group, group_desc, offset);
return NULL;
}
@@ -302,8 +303,8 @@
struct buffer_head *
ext4_read_block_bitmap(struct super_block *sb, ext4_group_t block_group)
{
- struct ext4_group_desc * desc;
- struct buffer_head * bh = NULL;
+ struct ext4_group_desc *desc;
+ struct buffer_head *bh = NULL;
ext4_fsblk_t bitmap_blk;
desc = ext4_get_group_desc(sb, block_group, NULL);
@@ -318,9 +319,11 @@
block_group, bitmap_blk);
return NULL;
}
- if (bh_uptodate_or_lock(bh))
+ if (buffer_uptodate(bh) &&
+ !(desc->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)))
return bh;
+ lock_buffer(bh);
spin_lock(sb_bgl_lock(EXT4_SB(sb), block_group));
if (desc->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) {
ext4_init_block_bitmap(sb, bh, block_group, desc);
@@ -345,301 +348,6 @@
*/
return bh;
}
-/*
- * The reservation window structure operations
- * --------------------------------------------
- * Operations include:
- * dump, find, add, remove, is_empty, find_next_reservable_window, etc.
- *
- * We use a red-black tree to represent per-filesystem reservation
- * windows.
- *
- */
-
-/**
- * __rsv_window_dump() -- Dump the filesystem block allocation reservation map
- * @rb_root: root of per-filesystem reservation rb tree
- * @verbose: verbose mode
- * @fn: function which wishes to dump the reservation map
- *
- * If verbose is turned on, it will print the whole block reservation
- * windows(start, end). Otherwise, it will only print out the "bad" windows,
- * those windows that overlap with their immediate neighbors.
- */
-#if 1
-static void __rsv_window_dump(struct rb_root *root, int verbose,
- const char *fn)
-{
- struct rb_node *n;
- struct ext4_reserve_window_node *rsv, *prev;
- int bad;
-
-restart:
- n = rb_first(root);
- bad = 0;
- prev = NULL;
-
- printk("Block Allocation Reservation Windows Map (%s):\n", fn);
- while (n) {
- rsv = rb_entry(n, struct ext4_reserve_window_node, rsv_node);
- if (verbose)
- printk("reservation window 0x%p "
- "start: %llu, end: %llu\n",
- rsv, rsv->rsv_start, rsv->rsv_end);
- if (rsv->rsv_start && rsv->rsv_start >= rsv->rsv_end) {
- printk("Bad reservation %p (start >= end)\n",
- rsv);
- bad = 1;
- }
- if (prev && prev->rsv_end >= rsv->rsv_start) {
- printk("Bad reservation %p (prev->end >= start)\n",
- rsv);
- bad = 1;
- }
- if (bad) {
- if (!verbose) {
- printk("Restarting reservation walk in verbose mode\n");
- verbose = 1;
- goto restart;
- }
- }
- n = rb_next(n);
- prev = rsv;
- }
- printk("Window map complete.\n");
- BUG_ON(bad);
-}
-#define rsv_window_dump(root, verbose) \
- __rsv_window_dump((root), (verbose), __func__)
-#else
-#define rsv_window_dump(root, verbose) do {} while (0)
-#endif
-
-/**
- * goal_in_my_reservation()
- * @rsv: inode's reservation window
- * @grp_goal: given goal block relative to the allocation block group
- * @group: the current allocation block group
- * @sb: filesystem super block
- *
- * Test if the given goal block (group relative) is within the file's
- * own block reservation window range.
- *
- * If the reservation window is outside the goal allocation group, return 0;
- * grp_goal (given goal block) could be -1, which means no specific
- * goal block. In this case, always return 1.
- * If the goal block is within the reservation window, return 1;
- * otherwise, return 0;
- */
-static int
-goal_in_my_reservation(struct ext4_reserve_window *rsv, ext4_grpblk_t grp_goal,
- ext4_group_t group, struct super_block *sb)
-{
- ext4_fsblk_t group_first_block, group_last_block;
-
- group_first_block = ext4_group_first_block_no(sb, group);
- group_last_block = group_first_block + (EXT4_BLOCKS_PER_GROUP(sb) - 1);
-
- if ((rsv->_rsv_start > group_last_block) ||
- (rsv->_rsv_end < group_first_block))
- return 0;
- if ((grp_goal >= 0) && ((grp_goal + group_first_block < rsv->_rsv_start)
- || (grp_goal + group_first_block > rsv->_rsv_end)))
- return 0;
- return 1;
-}
-
-/**
- * search_reserve_window()
- * @rb_root: root of reservation tree
- * @goal: target allocation block
- *
- * Find the reserved window which includes the goal, or the previous one
- * if the goal is not in any window.
- * Returns NULL if there are no windows or if all windows start after the goal.
- */
-static struct ext4_reserve_window_node *
-search_reserve_window(struct rb_root *root, ext4_fsblk_t goal)
-{
- struct rb_node *n = root->rb_node;
- struct ext4_reserve_window_node *rsv;
-
- if (!n)
- return NULL;
-
- do {
- rsv = rb_entry(n, struct ext4_reserve_window_node, rsv_node);
-
- if (goal < rsv->rsv_start)
- n = n->rb_left;
- else if (goal > rsv->rsv_end)
- n = n->rb_right;
- else
- return rsv;
- } while (n);
- /*
- * We've fallen off the end of the tree: the goal wasn't inside
- * any particular node. OK, the previous node must be to one
- * side of the interval containing the goal. If it's the RHS,
- * we need to back up one.
- */
- if (rsv->rsv_start > goal) {
- n = rb_prev(&rsv->rsv_node);
- rsv = rb_entry(n, struct ext4_reserve_window_node, rsv_node);
- }
- return rsv;
-}
-
-/**
- * ext4_rsv_window_add() -- Insert a window to the block reservation rb tree.
- * @sb: super block
- * @rsv: reservation window to add
- *
- * Must be called with rsv_lock hold.
- */
-void ext4_rsv_window_add(struct super_block *sb,
- struct ext4_reserve_window_node *rsv)
-{
- struct rb_root *root = &EXT4_SB(sb)->s_rsv_window_root;
- struct rb_node *node = &rsv->rsv_node;
- ext4_fsblk_t start = rsv->rsv_start;
-
- struct rb_node ** p = &root->rb_node;
- struct rb_node * parent = NULL;
- struct ext4_reserve_window_node *this;
-
- while (*p)
- {
- parent = *p;
- this = rb_entry(parent, struct ext4_reserve_window_node, rsv_node);
-
- if (start < this->rsv_start)
- p = &(*p)->rb_left;
- else if (start > this->rsv_end)
- p = &(*p)->rb_right;
- else {
- rsv_window_dump(root, 1);
- BUG();
- }
- }
-
- rb_link_node(node, parent, p);
- rb_insert_color(node, root);
-}
-
-/**
- * ext4_rsv_window_remove() -- unlink a window from the reservation rb tree
- * @sb: super block
- * @rsv: reservation window to remove
- *
- * Mark the block reservation window as not allocated, and unlink it
- * from the filesystem reservation window rb tree. Must be called with
- * rsv_lock hold.
- */
-static void rsv_window_remove(struct super_block *sb,
- struct ext4_reserve_window_node *rsv)
-{
- rsv->rsv_start = EXT4_RESERVE_WINDOW_NOT_ALLOCATED;
- rsv->rsv_end = EXT4_RESERVE_WINDOW_NOT_ALLOCATED;
- rsv->rsv_alloc_hit = 0;
- rb_erase(&rsv->rsv_node, &EXT4_SB(sb)->s_rsv_window_root);
-}
-
-/*
- * rsv_is_empty() -- Check if the reservation window is allocated.
- * @rsv: given reservation window to check
- *
- * returns 1 if the end block is EXT4_RESERVE_WINDOW_NOT_ALLOCATED.
- */
-static inline int rsv_is_empty(struct ext4_reserve_window *rsv)
-{
- /* a valid reservation end block could not be 0 */
- return rsv->_rsv_end == EXT4_RESERVE_WINDOW_NOT_ALLOCATED;
-}
-
-/**
- * ext4_init_block_alloc_info()
- * @inode: file inode structure
- *
- * Allocate and initialize the reservation window structure, and
- * link the window to the ext4 inode structure at last
- *
- * The reservation window structure is only dynamically allocated
- * and linked to ext4 inode the first time the open file
- * needs a new block. So, before every ext4_new_block(s) call, for
- * regular files, we should check whether the reservation window
- * structure exists or not. In the latter case, this function is called.
- * Fail to do so will result in block reservation being turned off for that
- * open file.
- *
- * This function is called from ext4_get_blocks_handle(), also called
- * when setting the reservation window size through ioctl before the file
- * is open for write (needs block allocation).
- *
- * Needs down_write(i_data_sem) protection prior to call this function.
- */
-void ext4_init_block_alloc_info(struct inode *inode)
-{
- struct ext4_inode_info *ei = EXT4_I(inode);
- struct ext4_block_alloc_info *block_i = ei->i_block_alloc_info;
- struct super_block *sb = inode->i_sb;
-
- block_i = kmalloc(sizeof(*block_i), GFP_NOFS);
- if (block_i) {
- struct ext4_reserve_window_node *rsv = &block_i->rsv_window_node;
-
- rsv->rsv_start = EXT4_RESERVE_WINDOW_NOT_ALLOCATED;
- rsv->rsv_end = EXT4_RESERVE_WINDOW_NOT_ALLOCATED;
-
- /*
- * if filesystem is mounted with NORESERVATION, the goal
- * reservation window size is set to zero to indicate
- * block reservation is off
- */
- if (!test_opt(sb, RESERVATION))
- rsv->rsv_goal_size = 0;
- else
- rsv->rsv_goal_size = EXT4_DEFAULT_RESERVE_BLOCKS;
- rsv->rsv_alloc_hit = 0;
- block_i->last_alloc_logical_block = 0;
- block_i->last_alloc_physical_block = 0;
- }
- ei->i_block_alloc_info = block_i;
-}
-
-/**
- * ext4_discard_reservation()
- * @inode: inode
- *
- * Discard(free) block reservation window on last file close, or truncate
- * or at last iput().
- *
- * It is being called in three cases:
- * ext4_release_file(): last writer close the file
- * ext4_clear_inode(): last iput(), when nobody link to this file.
- * ext4_truncate(): when the block indirect map is about to change.
- *
- */
-void ext4_discard_reservation(struct inode *inode)
-{
- struct ext4_inode_info *ei = EXT4_I(inode);
- struct ext4_block_alloc_info *block_i = ei->i_block_alloc_info;
- struct ext4_reserve_window_node *rsv;
- spinlock_t *rsv_lock = &EXT4_SB(inode->i_sb)->s_rsv_window_lock;
-
- ext4_mb_discard_inode_preallocations(inode);
-
- if (!block_i)
- return;
-
- rsv = &block_i->rsv_window_node;
- if (!rsv_is_empty(&rsv->rsv_window)) {
- spin_lock(rsv_lock);
- if (!rsv_is_empty(&rsv->rsv_window))
- rsv_window_remove(inode->i_sb, rsv);
- spin_unlock(rsv_lock);
- }
-}
/**
* ext4_free_blocks_sb() -- Free given blocks and update quota
@@ -648,6 +356,13 @@
* @block: start physcial block to free
* @count: number of blocks to free
* @pdquot_freed_blocks: pointer to quota
+ *
+ * XXX This function is only used by the on-line resizing code, which
+ * should probably be fixed up to call the mballoc variant. There
+ * this needs to be cleaned up later; in fact, I'm not convinced this
+ * is 100% correct in the face of the mballoc code. The online resizing
+ * code needs to be fixed up to more tightly (and correctly) interlock
+ * with the mballoc code.
*/
void ext4_free_blocks_sb(handle_t *handle, struct super_block *sb,
ext4_fsblk_t block, unsigned long count,
@@ -659,8 +374,8 @@
ext4_grpblk_t bit;
unsigned long i;
unsigned long overflow;
- struct ext4_group_desc * desc;
- struct ext4_super_block * es;
+ struct ext4_group_desc *desc;
+ struct ext4_super_block *es;
struct ext4_sb_info *sbi;
int err = 0, ret;
ext4_grpblk_t group_freed;
@@ -671,13 +386,13 @@
if (block < le32_to_cpu(es->s_first_data_block) ||
block + count < block ||
block + count > ext4_blocks_count(es)) {
- ext4_error (sb, "ext4_free_blocks",
- "Freeing blocks not in datazone - "
- "block = %llu, count = %lu", block, count);
+ ext4_error(sb, "ext4_free_blocks",
+ "Freeing blocks not in datazone - "
+ "block = %llu, count = %lu", block, count);
goto error_return;
}
- ext4_debug ("freeing block(s) %llu-%llu\n", block, block + count - 1);
+ ext4_debug("freeing block(s) %llu-%llu\n", block, block + count - 1);
do_more:
overflow = 0;
@@ -694,7 +409,7 @@
bitmap_bh = ext4_read_block_bitmap(sb, block_group);
if (!bitmap_bh)
goto error_return;
- desc = ext4_get_group_desc (sb, block_group, &gd_bh);
+ desc = ext4_get_group_desc(sb, block_group, &gd_bh);
if (!desc)
goto error_return;
@@ -703,10 +418,10 @@
in_range(block, ext4_inode_table(sb, desc), sbi->s_itb_per_group) ||
in_range(block + count - 1, ext4_inode_table(sb, desc),
sbi->s_itb_per_group)) {
- ext4_error (sb, "ext4_free_blocks",
- "Freeing blocks in system zones - "
- "Block = %llu, count = %lu",
- block, count);
+ ext4_error(sb, "ext4_free_blocks",
+ "Freeing blocks in system zones - "
+ "Block = %llu, count = %lu",
+ block, count);
goto error_return;
}
@@ -848,7 +563,7 @@
ext4_fsblk_t block, unsigned long count,
int metadata)
{
- struct super_block * sb;
+ struct super_block *sb;
unsigned long dquot_freed_blocks;
/* this isn't the right place to decide whether block is metadata
@@ -859,748 +574,52 @@
sb = inode->i_sb;
- if (!test_opt(sb, MBALLOC) || !EXT4_SB(sb)->s_group_info)
- ext4_free_blocks_sb(handle, sb, block, count,
- &dquot_freed_blocks);
- else
- ext4_mb_free_blocks(handle, inode, block, count,
- metadata, &dquot_freed_blocks);
+ ext4_mb_free_blocks(handle, inode, block, count,
+ metadata, &dquot_freed_blocks);
if (dquot_freed_blocks)
DQUOT_FREE_BLOCK(inode, dquot_freed_blocks);
return;
}
-/**
- * ext4_test_allocatable()
- * @nr: given allocation block group
- * @bh: bufferhead contains the bitmap of the given block group
- *
- * For ext4 allocations, we must not reuse any blocks which are
- * allocated in the bitmap buffer's "last committed data" copy. This
- * prevents deletes from freeing up the page for reuse until we have
- * committed the delete transaction.
- *
- * If we didn't do this, then deleting something and reallocating it as
- * data would allow the old block to be overwritten before the
- * transaction committed (because we force data to disk before commit).
- * This would lead to corruption if we crashed between overwriting the
- * data and committing the delete.
- *
- * @@@ We may want to make this allocation behaviour conditional on
- * data-writes at some point, and disable it for metadata allocations or
- * sync-data inodes.
- */
-static int ext4_test_allocatable(ext4_grpblk_t nr, struct buffer_head *bh)
+int ext4_claim_free_blocks(struct ext4_sb_info *sbi,
+ s64 nblocks)
{
- int ret;
- struct journal_head *jh = bh2jh(bh);
+ s64 free_blocks, dirty_blocks;
+ s64 root_blocks = 0;
+ struct percpu_counter *fbc = &sbi->s_freeblocks_counter;
+ struct percpu_counter *dbc = &sbi->s_dirtyblocks_counter;
- if (ext4_test_bit(nr, bh->b_data))
- return 0;
+ free_blocks = percpu_counter_read_positive(fbc);
+ dirty_blocks = percpu_counter_read_positive(dbc);
- jbd_lock_bh_state(bh);
- if (!jh->b_committed_data)
- ret = 1;
- else
- ret = !ext4_test_bit(nr, jh->b_committed_data);
- jbd_unlock_bh_state(bh);
- return ret;
-}
+ if (!capable(CAP_SYS_RESOURCE) &&
+ sbi->s_resuid != current->fsuid &&
+ (sbi->s_resgid == 0 || !in_group_p(sbi->s_resgid)))
+ root_blocks = ext4_r_blocks_count(sbi->s_es);
-/**
- * bitmap_search_next_usable_block()
- * @start: the starting block (group relative) of the search
- * @bh: bufferhead contains the block group bitmap
- * @maxblocks: the ending block (group relative) of the reservation
- *
- * The bitmap search --- search forward alternately through the actual
- * bitmap on disk and the last-committed copy in journal, until we find a
- * bit free in both bitmaps.
- */
-static ext4_grpblk_t
-bitmap_search_next_usable_block(ext4_grpblk_t start, struct buffer_head *bh,
- ext4_grpblk_t maxblocks)
-{
- ext4_grpblk_t next;
- struct journal_head *jh = bh2jh(bh);
-
- while (start < maxblocks) {
- next = ext4_find_next_zero_bit(bh->b_data, maxblocks, start);
- if (next >= maxblocks)
- return -1;
- if (ext4_test_allocatable(next, bh))
- return next;
- jbd_lock_bh_state(bh);
- if (jh->b_committed_data)
- start = ext4_find_next_zero_bit(jh->b_committed_data,
- maxblocks, next);
- jbd_unlock_bh_state(bh);
- }
- return -1;
-}
-
-/**
- * find_next_usable_block()
- * @start: the starting block (group relative) to find next
- * allocatable block in bitmap.
- * @bh: bufferhead contains the block group bitmap
- * @maxblocks: the ending block (group relative) for the search
- *
- * Find an allocatable block in a bitmap. We honor both the bitmap and
- * its last-committed copy (if that exists), and perform the "most
- * appropriate allocation" algorithm of looking for a free block near
- * the initial goal; then for a free byte somewhere in the bitmap; then
- * for any free bit in the bitmap.
- */
-static ext4_grpblk_t
-find_next_usable_block(ext4_grpblk_t start, struct buffer_head *bh,
- ext4_grpblk_t maxblocks)
-{
- ext4_grpblk_t here, next;
- char *p, *r;
-
- if (start > 0) {
- /*
- * The goal was occupied; search forward for a free
- * block within the next XX blocks.
- *
- * end_goal is more or less random, but it has to be
- * less than EXT4_BLOCKS_PER_GROUP. Aligning up to the
- * next 64-bit boundary is simple..
- */
- ext4_grpblk_t end_goal = (start + 63) & ~63;
- if (end_goal > maxblocks)
- end_goal = maxblocks;
- here = ext4_find_next_zero_bit(bh->b_data, end_goal, start);
- if (here < end_goal && ext4_test_allocatable(here, bh))
- return here;
- ext4_debug("Bit not found near goal\n");
- }
-
- here = start;
- if (here < 0)
- here = 0;
-
- p = ((char *)bh->b_data) + (here >> 3);
- r = memscan(p, 0, ((maxblocks + 7) >> 3) - (here >> 3));
- next = (r - ((char *)bh->b_data)) << 3;
-
- if (next < maxblocks && next >= start && ext4_test_allocatable(next, bh))
- return next;
-
- /*
- * The bitmap search --- search forward alternately through the actual
- * bitmap and the last-committed copy until we find a bit free in
- * both
- */
- here = bitmap_search_next_usable_block(here, bh, maxblocks);
- return here;
-}
-
-/**
- * claim_block()
- * @block: the free block (group relative) to allocate
- * @bh: the bufferhead containts the block group bitmap
- *
- * We think we can allocate this block in this bitmap. Try to set the bit.
- * If that succeeds then check that nobody has allocated and then freed the
- * block since we saw that is was not marked in b_committed_data. If it _was_
- * allocated and freed then clear the bit in the bitmap again and return
- * zero (failure).
- */
-static inline int
-claim_block(spinlock_t *lock, ext4_grpblk_t block, struct buffer_head *bh)
-{
- struct journal_head *jh = bh2jh(bh);
- int ret;
-
- if (ext4_set_bit_atomic(lock, block, bh->b_data))
- return 0;
- jbd_lock_bh_state(bh);
- if (jh->b_committed_data && ext4_test_bit(block,jh->b_committed_data)) {
- ext4_clear_bit_atomic(lock, block, bh->b_data);
- ret = 0;
- } else {
- ret = 1;
- }
- jbd_unlock_bh_state(bh);
- return ret;
-}
-
-/**
- * ext4_try_to_allocate()
- * @sb: superblock
- * @handle: handle to this transaction
- * @group: given allocation block group
- * @bitmap_bh: bufferhead holds the block bitmap
- * @grp_goal: given target block within the group
- * @count: target number of blocks to allocate
- * @my_rsv: reservation window
- *
- * Attempt to allocate blocks within a give range. Set the range of allocation
- * first, then find the first free bit(s) from the bitmap (within the range),
- * and at last, allocate the blocks by claiming the found free bit as allocated.
- *
- * To set the range of this allocation:
- * if there is a reservation window, only try to allocate block(s) from the
- * file's own reservation window;
- * Otherwise, the allocation range starts from the give goal block, ends at
- * the block group's last block.
- *
- * If we failed to allocate the desired block then we may end up crossing to a
- * new bitmap. In that case we must release write access to the old one via
- * ext4_journal_release_buffer(), else we'll run out of credits.
- */
-static ext4_grpblk_t
-ext4_try_to_allocate(struct super_block *sb, handle_t *handle,
- ext4_group_t group, struct buffer_head *bitmap_bh,
- ext4_grpblk_t grp_goal, unsigned long *count,
- struct ext4_reserve_window *my_rsv)
-{
- ext4_fsblk_t group_first_block;
- ext4_grpblk_t start, end;
- unsigned long num = 0;
-
- /* we do allocation within the reservation window if we have a window */
- if (my_rsv) {
- group_first_block = ext4_group_first_block_no(sb, group);
- if (my_rsv->_rsv_start >= group_first_block)
- start = my_rsv->_rsv_start - group_first_block;
- else
- /* reservation window cross group boundary */
- start = 0;
- end = my_rsv->_rsv_end - group_first_block + 1;
- if (end > EXT4_BLOCKS_PER_GROUP(sb))
- /* reservation window crosses group boundary */
- end = EXT4_BLOCKS_PER_GROUP(sb);
- if ((start <= grp_goal) && (grp_goal < end))
- start = grp_goal;
- else
- grp_goal = -1;
- } else {
- if (grp_goal > 0)
- start = grp_goal;
- else
- start = 0;
- end = EXT4_BLOCKS_PER_GROUP(sb);
- }
-
- BUG_ON(start > EXT4_BLOCKS_PER_GROUP(sb));
-
-repeat:
- if (grp_goal < 0 || !ext4_test_allocatable(grp_goal, bitmap_bh)) {
- grp_goal = find_next_usable_block(start, bitmap_bh, end);
- if (grp_goal < 0)
- goto fail_access;
- if (!my_rsv) {
- int i;
-
- for (i = 0; i < 7 && grp_goal > start &&
- ext4_test_allocatable(grp_goal - 1,
- bitmap_bh);
- i++, grp_goal--)
- ;
+ if (free_blocks - (nblocks + root_blocks + dirty_blocks) <
+ EXT4_FREEBLOCKS_WATERMARK) {
+ free_blocks = percpu_counter_sum(fbc);
+ dirty_blocks = percpu_counter_sum(dbc);
+ if (dirty_blocks < 0) {
+ printk(KERN_CRIT "Dirty block accounting "
+ "went wrong %lld\n",
+ dirty_blocks);
}
}
- start = grp_goal;
-
- if (!claim_block(sb_bgl_lock(EXT4_SB(sb), group),
- grp_goal, bitmap_bh)) {
- /*
- * The block was allocated by another thread, or it was
- * allocated and then freed by another thread
- */
- start++;
- grp_goal++;
- if (start >= end)
- goto fail_access;
- goto repeat;
- }
- num++;
- grp_goal++;
- while (num < *count && grp_goal < end
- && ext4_test_allocatable(grp_goal, bitmap_bh)
- && claim_block(sb_bgl_lock(EXT4_SB(sb), group),
- grp_goal, bitmap_bh)) {
- num++;
- grp_goal++;
- }
- *count = num;
- return grp_goal - num;
-fail_access:
- *count = num;
- return -1;
-}
-
-/**
- * find_next_reservable_window():
- * find a reservable space within the given range.
- * It does not allocate the reservation window for now:
- * alloc_new_reservation() will do the work later.
- *
- * @search_head: the head of the searching list;
- * This is not necessarily the list head of the whole filesystem
- *
- * We have both head and start_block to assist the search
- * for the reservable space. The list starts from head,
- * but we will shift to the place where start_block is,
- * then start from there, when looking for a reservable space.
- *
- * @size: the target new reservation window size
- *
- * @group_first_block: the first block we consider to start
- * the real search from
- *
- * @last_block:
- * the maximum block number that our goal reservable space
- * could start from. This is normally the last block in this
- * group. The search will end when we found the start of next
- * possible reservable space is out of this boundary.
- * This could handle the cross boundary reservation window
- * request.
- *
- * basically we search from the given range, rather than the whole
- * reservation double linked list, (start_block, last_block)
- * to find a free region that is of my size and has not
- * been reserved.
- *
- */
-static int find_next_reservable_window(
- struct ext4_reserve_window_node *search_head,
- struct ext4_reserve_window_node *my_rsv,
- struct super_block * sb,
- ext4_fsblk_t start_block,
- ext4_fsblk_t last_block)
-{
- struct rb_node *next;
- struct ext4_reserve_window_node *rsv, *prev;
- ext4_fsblk_t cur;
- int size = my_rsv->rsv_goal_size;
-
- /* TODO: make the start of the reservation window byte-aligned */
- /* cur = *start_block & ~7;*/
- cur = start_block;
- rsv = search_head;
- if (!rsv)
- return -1;
-
- while (1) {
- if (cur <= rsv->rsv_end)
- cur = rsv->rsv_end + 1;
-
- /* TODO?
- * in the case we could not find a reservable space
- * that is what is expected, during the re-search, we could
- * remember what's the largest reservable space we could have
- * and return that one.
- *
- * For now it will fail if we could not find the reservable
- * space with expected-size (or more)...
- */
- if (cur > last_block)
- return -1; /* fail */
-
- prev = rsv;
- next = rb_next(&rsv->rsv_node);
- rsv = rb_entry(next,struct ext4_reserve_window_node,rsv_node);
-
- /*
- * Reached the last reservation, we can just append to the
- * previous one.
- */
- if (!next)
- break;
-
- if (cur + size <= rsv->rsv_start) {
- /*
- * Found a reserveable space big enough. We could
- * have a reservation across the group boundary here
- */
- break;
- }
- }
- /*
- * we come here either :
- * when we reach the end of the whole list,
- * and there is empty reservable space after last entry in the list.
- * append it to the end of the list.
- *
- * or we found one reservable space in the middle of the list,
- * return the reservation window that we could append to.
- * succeed.
+ /* Check whether we have space after
+ * accounting for current dirty blocks
*/
+ if (free_blocks < ((root_blocks + nblocks) + dirty_blocks))
+ /* we don't have free space */
+ return -ENOSPC;
- if ((prev != my_rsv) && (!rsv_is_empty(&my_rsv->rsv_window)))
- rsv_window_remove(sb, my_rsv);
-
- /*
- * Let's book the whole avaliable window for now. We will check the
- * disk bitmap later and then, if there are free blocks then we adjust
- * the window size if it's larger than requested.
- * Otherwise, we will remove this node from the tree next time
- * call find_next_reservable_window.
- */
- my_rsv->rsv_start = cur;
- my_rsv->rsv_end = cur + size - 1;
- my_rsv->rsv_alloc_hit = 0;
-
- if (prev != my_rsv)
- ext4_rsv_window_add(sb, my_rsv);
-
+ /* Add the blocks to nblocks */
+ percpu_counter_add(dbc, nblocks);
return 0;
}
/**
- * alloc_new_reservation()--allocate a new reservation window
- *
- * To make a new reservation, we search part of the filesystem
- * reservation list (the list that inside the group). We try to
- * allocate a new reservation window near the allocation goal,
- * or the beginning of the group, if there is no goal.
- *
- * We first find a reservable space after the goal, then from
- * there, we check the bitmap for the first free block after
- * it. If there is no free block until the end of group, then the
- * whole group is full, we failed. Otherwise, check if the free
- * block is inside the expected reservable space, if so, we
- * succeed.
- * If the first free block is outside the reservable space, then
- * start from the first free block, we search for next available
- * space, and go on.
- *
- * on succeed, a new reservation will be found and inserted into the list
- * It contains at least one free block, and it does not overlap with other
- * reservation windows.
- *
- * failed: we failed to find a reservation window in this group
- *
- * @rsv: the reservation
- *
- * @grp_goal: The goal (group-relative). It is where the search for a
- * free reservable space should start from.
- * if we have a grp_goal(grp_goal >0 ), then start from there,
- * no grp_goal(grp_goal = -1), we start from the first block
- * of the group.
- *
- * @sb: the super block
- * @group: the group we are trying to allocate in
- * @bitmap_bh: the block group block bitmap
- *
- */
-static int alloc_new_reservation(struct ext4_reserve_window_node *my_rsv,
- ext4_grpblk_t grp_goal, struct super_block *sb,
- ext4_group_t group, struct buffer_head *bitmap_bh)
-{
- struct ext4_reserve_window_node *search_head;
- ext4_fsblk_t group_first_block, group_end_block, start_block;
- ext4_grpblk_t first_free_block;
- struct rb_root *fs_rsv_root = &EXT4_SB(sb)->s_rsv_window_root;
- unsigned long size;
- int ret;
- spinlock_t *rsv_lock = &EXT4_SB(sb)->s_rsv_window_lock;
-
- group_first_block = ext4_group_first_block_no(sb, group);
- group_end_block = group_first_block + (EXT4_BLOCKS_PER_GROUP(sb) - 1);
-
- if (grp_goal < 0)
- start_block = group_first_block;
- else
- start_block = grp_goal + group_first_block;
-
- size = my_rsv->rsv_goal_size;
-
- if (!rsv_is_empty(&my_rsv->rsv_window)) {
- /*
- * if the old reservation is cross group boundary
- * and if the goal is inside the old reservation window,
- * we will come here when we just failed to allocate from
- * the first part of the window. We still have another part
- * that belongs to the next group. In this case, there is no
- * point to discard our window and try to allocate a new one
- * in this group(which will fail). we should
- * keep the reservation window, just simply move on.
- *
- * Maybe we could shift the start block of the reservation
- * window to the first block of next group.
- */
-
- if ((my_rsv->rsv_start <= group_end_block) &&
- (my_rsv->rsv_end > group_end_block) &&
- (start_block >= my_rsv->rsv_start))
- return -1;
-
- if ((my_rsv->rsv_alloc_hit >
- (my_rsv->rsv_end - my_rsv->rsv_start + 1) / 2)) {
- /*
- * if the previously allocation hit ratio is
- * greater than 1/2, then we double the size of
- * the reservation window the next time,
- * otherwise we keep the same size window
- */
- size = size * 2;
- if (size > EXT4_MAX_RESERVE_BLOCKS)
- size = EXT4_MAX_RESERVE_BLOCKS;
- my_rsv->rsv_goal_size= size;
- }
- }
-
- spin_lock(rsv_lock);
- /*
- * shift the search start to the window near the goal block
- */
- search_head = search_reserve_window(fs_rsv_root, start_block);
-
- /*
- * find_next_reservable_window() simply finds a reservable window
- * inside the given range(start_block, group_end_block).
- *
- * To make sure the reservation window has a free bit inside it, we
- * need to check the bitmap after we found a reservable window.
- */
-retry:
- ret = find_next_reservable_window(search_head, my_rsv, sb,
- start_block, group_end_block);
-
- if (ret == -1) {
- if (!rsv_is_empty(&my_rsv->rsv_window))
- rsv_window_remove(sb, my_rsv);
- spin_unlock(rsv_lock);
- return -1;
- }
-
- /*
- * On success, find_next_reservable_window() returns the
- * reservation window where there is a reservable space after it.
- * Before we reserve this reservable space, we need
- * to make sure there is at least a free block inside this region.
- *
- * searching the first free bit on the block bitmap and copy of
- * last committed bitmap alternatively, until we found a allocatable
- * block. Search start from the start block of the reservable space
- * we just found.
- */
- spin_unlock(rsv_lock);
- first_free_block = bitmap_search_next_usable_block(
- my_rsv->rsv_start - group_first_block,
- bitmap_bh, group_end_block - group_first_block + 1);
-
- if (first_free_block < 0) {
- /*
- * no free block left on the bitmap, no point
- * to reserve the space. return failed.
- */
- spin_lock(rsv_lock);
- if (!rsv_is_empty(&my_rsv->rsv_window))
- rsv_window_remove(sb, my_rsv);
- spin_unlock(rsv_lock);
- return -1; /* failed */
- }
-
- start_block = first_free_block + group_first_block;
- /*
- * check if the first free block is within the
- * free space we just reserved
- */
- if (start_block >= my_rsv->rsv_start && start_block <= my_rsv->rsv_end)
- return 0; /* success */
- /*
- * if the first free bit we found is out of the reservable space
- * continue search for next reservable space,
- * start from where the free block is,
- * we also shift the list head to where we stopped last time
- */
- search_head = my_rsv;
- spin_lock(rsv_lock);
- goto retry;
-}
-
-/**
- * try_to_extend_reservation()
- * @my_rsv: given reservation window
- * @sb: super block
- * @size: the delta to extend
- *
- * Attempt to expand the reservation window large enough to have
- * required number of free blocks
- *
- * Since ext4_try_to_allocate() will always allocate blocks within
- * the reservation window range, if the window size is too small,
- * multiple blocks allocation has to stop at the end of the reservation
- * window. To make this more efficient, given the total number of
- * blocks needed and the current size of the window, we try to
- * expand the reservation window size if necessary on a best-effort
- * basis before ext4_new_blocks() tries to allocate blocks,
- */
-static void try_to_extend_reservation(struct ext4_reserve_window_node *my_rsv,
- struct super_block *sb, int size)
-{
- struct ext4_reserve_window_node *next_rsv;
- struct rb_node *next;
- spinlock_t *rsv_lock = &EXT4_SB(sb)->s_rsv_window_lock;
-
- if (!spin_trylock(rsv_lock))
- return;
-
- next = rb_next(&my_rsv->rsv_node);
-
- if (!next)
- my_rsv->rsv_end += size;
- else {
- next_rsv = rb_entry(next, struct ext4_reserve_window_node, rsv_node);
-
- if ((next_rsv->rsv_start - my_rsv->rsv_end - 1) >= size)
- my_rsv->rsv_end += size;
- else
- my_rsv->rsv_end = next_rsv->rsv_start - 1;
- }
- spin_unlock(rsv_lock);
-}
-
-/**
- * ext4_try_to_allocate_with_rsv()
- * @sb: superblock
- * @handle: handle to this transaction
- * @group: given allocation block group
- * @bitmap_bh: bufferhead holds the block bitmap
- * @grp_goal: given target block within the group
- * @count: target number of blocks to allocate
- * @my_rsv: reservation window
- * @errp: pointer to store the error code
- *
- * This is the main function used to allocate a new block and its reservation
- * window.
- *
- * Each time when a new block allocation is need, first try to allocate from
- * its own reservation. If it does not have a reservation window, instead of
- * looking for a free bit on bitmap first, then look up the reservation list to
- * see if it is inside somebody else's reservation window, we try to allocate a
- * reservation window for it starting from the goal first. Then do the block
- * allocation within the reservation window.
- *
- * This will avoid keeping on searching the reservation list again and
- * again when somebody is looking for a free block (without
- * reservation), and there are lots of free blocks, but they are all
- * being reserved.
- *
- * We use a red-black tree for the per-filesystem reservation list.
- *
- */
-static ext4_grpblk_t
-ext4_try_to_allocate_with_rsv(struct super_block *sb, handle_t *handle,
- ext4_group_t group, struct buffer_head *bitmap_bh,
- ext4_grpblk_t grp_goal,
- struct ext4_reserve_window_node * my_rsv,
- unsigned long *count, int *errp)
-{
- ext4_fsblk_t group_first_block, group_last_block;
- ext4_grpblk_t ret = 0;
- int fatal;
- unsigned long num = *count;
-
- *errp = 0;
-
- /*
- * Make sure we use undo access for the bitmap, because it is critical
- * that we do the frozen_data COW on bitmap buffers in all cases even
- * if the buffer is in BJ_Forget state in the committing transaction.
- */
- BUFFER_TRACE(bitmap_bh, "get undo access for new block");
- fatal = ext4_journal_get_undo_access(handle, bitmap_bh);
- if (fatal) {
- *errp = fatal;
- return -1;
- }
-
- /*
- * we don't deal with reservation when
- * filesystem is mounted without reservation
- * or the file is not a regular file
- * or last attempt to allocate a block with reservation turned on failed
- */
- if (my_rsv == NULL ) {
- ret = ext4_try_to_allocate(sb, handle, group, bitmap_bh,
- grp_goal, count, NULL);
- goto out;
- }
- /*
- * grp_goal is a group relative block number (if there is a goal)
- * 0 <= grp_goal < EXT4_BLOCKS_PER_GROUP(sb)
- * first block is a filesystem wide block number
- * first block is the block number of the first block in this group
- */
- group_first_block = ext4_group_first_block_no(sb, group);
- group_last_block = group_first_block + (EXT4_BLOCKS_PER_GROUP(sb) - 1);
-
- /*
- * Basically we will allocate a new block from inode's reservation
- * window.
- *
- * We need to allocate a new reservation window, if:
- * a) inode does not have a reservation window; or
- * b) last attempt to allocate a block from existing reservation
- * failed; or
- * c) we come here with a goal and with a reservation window
- *
- * We do not need to allocate a new reservation window if we come here
- * at the beginning with a goal and the goal is inside the window, or
- * we don't have a goal but already have a reservation window.
- * then we could go to allocate from the reservation window directly.
- */
- while (1) {
- if (rsv_is_empty(&my_rsv->rsv_window) || (ret < 0) ||
- !goal_in_my_reservation(&my_rsv->rsv_window,
- grp_goal, group, sb)) {
- if (my_rsv->rsv_goal_size < *count)
- my_rsv->rsv_goal_size = *count;
- ret = alloc_new_reservation(my_rsv, grp_goal, sb,
- group, bitmap_bh);
- if (ret < 0)
- break; /* failed */
-
- if (!goal_in_my_reservation(&my_rsv->rsv_window,
- grp_goal, group, sb))
- grp_goal = -1;
- } else if (grp_goal >= 0) {
- int curr = my_rsv->rsv_end -
- (grp_goal + group_first_block) + 1;
-
- if (curr < *count)
- try_to_extend_reservation(my_rsv, sb,
- *count - curr);
- }
-
- if ((my_rsv->rsv_start > group_last_block) ||
- (my_rsv->rsv_end < group_first_block)) {
- rsv_window_dump(&EXT4_SB(sb)->s_rsv_window_root, 1);
- BUG();
- }
- ret = ext4_try_to_allocate(sb, handle, group, bitmap_bh,
- grp_goal, &num, &my_rsv->rsv_window);
- if (ret >= 0) {
- my_rsv->rsv_alloc_hit += num;
- *count = num;
- break; /* succeed */
- }
- num = *count;
- }
-out:
- if (ret >= 0) {
- BUFFER_TRACE(bitmap_bh, "journal_dirty_metadata for "
- "bitmap block");
- fatal = ext4_journal_dirty_metadata(handle, bitmap_bh);
- if (fatal) {
- *errp = fatal;
- return -1;
- }
- return ret;
- }
-
- BUFFER_TRACE(bitmap_bh, "journal_release_buffer");
- ext4_journal_release_buffer(handle, bitmap_bh);
- return ret;
-}
-
-/**
* ext4_has_free_blocks()
* @sbi: in-core super block structure.
* @nblocks: number of neeed blocks
@@ -1610,29 +629,34 @@
* On success, return nblocks
*/
ext4_fsblk_t ext4_has_free_blocks(struct ext4_sb_info *sbi,
- ext4_fsblk_t nblocks)
+ s64 nblocks)
{
- ext4_fsblk_t free_blocks;
- ext4_fsblk_t root_blocks = 0;
+ s64 free_blocks, dirty_blocks;
+ s64 root_blocks = 0;
+ struct percpu_counter *fbc = &sbi->s_freeblocks_counter;
+ struct percpu_counter *dbc = &sbi->s_dirtyblocks_counter;
- free_blocks = percpu_counter_read_positive(&sbi->s_freeblocks_counter);
+ free_blocks = percpu_counter_read_positive(fbc);
+ dirty_blocks = percpu_counter_read_positive(dbc);
if (!capable(CAP_SYS_RESOURCE) &&
sbi->s_resuid != current->fsuid &&
(sbi->s_resgid == 0 || !in_group_p(sbi->s_resgid)))
root_blocks = ext4_r_blocks_count(sbi->s_es);
-#ifdef CONFIG_SMP
- if (free_blocks - root_blocks < FBC_BATCH)
- free_blocks =
- percpu_counter_sum_and_set(&sbi->s_freeblocks_counter);
-#endif
- if (free_blocks <= root_blocks)
+
+ if (free_blocks - (nblocks + root_blocks + dirty_blocks) <
+ EXT4_FREEBLOCKS_WATERMARK) {
+ free_blocks = percpu_counter_sum(fbc);
+ dirty_blocks = percpu_counter_sum(dbc);
+ }
+ if (free_blocks <= (root_blocks + dirty_blocks))
/* we don't have free space */
return 0;
- if (free_blocks - root_blocks < nblocks)
- return free_blocks - root_blocks;
+
+ if (free_blocks - (root_blocks + dirty_blocks) < nblocks)
+ return free_blocks - (root_blocks + dirty_blocks);
return nblocks;
- }
+}
/**
@@ -1657,303 +681,6 @@
return jbd2_journal_force_commit_nested(EXT4_SB(sb)->s_journal);
}
-/**
- * ext4_old_new_blocks() -- core block bitmap based block allocation function
- *
- * @handle: handle to this transaction
- * @inode: file inode
- * @goal: given target block(filesystem wide)
- * @count: target number of blocks to allocate
- * @errp: error code
- *
- * ext4_old_new_blocks uses a goal block to assist allocation and look up
- * the block bitmap directly to do block allocation. It tries to
- * allocate block(s) from the block group contains the goal block first. If
- * that fails, it will try to allocate block(s) from other block groups
- * without any specific goal block.
- *
- * This function is called when -o nomballoc mount option is enabled
- *
- */
-ext4_fsblk_t ext4_old_new_blocks(handle_t *handle, struct inode *inode,
- ext4_fsblk_t goal, unsigned long *count, int *errp)
-{
- struct buffer_head *bitmap_bh = NULL;
- struct buffer_head *gdp_bh;
- ext4_group_t group_no;
- ext4_group_t goal_group;
- ext4_grpblk_t grp_target_blk; /* blockgroup relative goal block */
- ext4_grpblk_t grp_alloc_blk; /* blockgroup-relative allocated block*/
- ext4_fsblk_t ret_block; /* filesyetem-wide allocated block */
- ext4_group_t bgi; /* blockgroup iteration index */
- int fatal = 0, err;
- int performed_allocation = 0;
- ext4_grpblk_t free_blocks; /* number of free blocks in a group */
- struct super_block *sb;
- struct ext4_group_desc *gdp;
- struct ext4_super_block *es;
- struct ext4_sb_info *sbi;
- struct ext4_reserve_window_node *my_rsv = NULL;
- struct ext4_block_alloc_info *block_i;
- unsigned short windowsz = 0;
- ext4_group_t ngroups;
- unsigned long num = *count;
-
- sb = inode->i_sb;
- if (!sb) {
- *errp = -ENODEV;
- printk("ext4_new_block: nonexistent device");
- return 0;
- }
-
- sbi = EXT4_SB(sb);
- if (!EXT4_I(inode)->i_delalloc_reserved_flag) {
- /*
- * With delalloc we already reserved the blocks
- */
- *count = ext4_has_free_blocks(sbi, *count);
- }
- if (*count == 0) {
- *errp = -ENOSPC;
- return 0; /*return with ENOSPC error */
- }
- num = *count;
-
- /*
- * Check quota for allocation of this block.
- */
- if (DQUOT_ALLOC_BLOCK(inode, num)) {
- *errp = -EDQUOT;
- return 0;
- }
-
- sbi = EXT4_SB(sb);
- es = EXT4_SB(sb)->s_es;
- ext4_debug("goal=%llu.\n", goal);
- /*
- * Allocate a block from reservation only when
- * filesystem is mounted with reservation(default,-o reservation), and
- * it's a regular file, and
- * the desired window size is greater than 0 (One could use ioctl
- * command EXT4_IOC_SETRSVSZ to set the window size to 0 to turn off
- * reservation on that particular file)
- */
- block_i = EXT4_I(inode)->i_block_alloc_info;
- if (block_i && ((windowsz = block_i->rsv_window_node.rsv_goal_size) > 0))
- my_rsv = &block_i->rsv_window_node;
-
- /*
- * First, test whether the goal block is free.
- */
- if (goal < le32_to_cpu(es->s_first_data_block) ||
- goal >= ext4_blocks_count(es))
- goal = le32_to_cpu(es->s_first_data_block);
- ext4_get_group_no_and_offset(sb, goal, &group_no, &grp_target_blk);
- goal_group = group_no;
-retry_alloc:
- gdp = ext4_get_group_desc(sb, group_no, &gdp_bh);
- if (!gdp)
- goto io_error;
-
- free_blocks = le16_to_cpu(gdp->bg_free_blocks_count);
- /*
- * if there is not enough free blocks to make a new resevation
- * turn off reservation for this allocation
- */
- if (my_rsv && (free_blocks < windowsz)
- && (rsv_is_empty(&my_rsv->rsv_window)))
- my_rsv = NULL;
-
- if (free_blocks > 0) {
- bitmap_bh = ext4_read_block_bitmap(sb, group_no);
- if (!bitmap_bh)
- goto io_error;
- grp_alloc_blk = ext4_try_to_allocate_with_rsv(sb, handle,
- group_no, bitmap_bh, grp_target_blk,
- my_rsv, &num, &fatal);
- if (fatal)
- goto out;
- if (grp_alloc_blk >= 0)
- goto allocated;
- }
-
- ngroups = EXT4_SB(sb)->s_groups_count;
- smp_rmb();
-
- /*
- * Now search the rest of the groups. We assume that
- * group_no and gdp correctly point to the last group visited.
- */
- for (bgi = 0; bgi < ngroups; bgi++) {
- group_no++;
- if (group_no >= ngroups)
- group_no = 0;
- gdp = ext4_get_group_desc(sb, group_no, &gdp_bh);
- if (!gdp)
- goto io_error;
- free_blocks = le16_to_cpu(gdp->bg_free_blocks_count);
- /*
- * skip this group if the number of
- * free blocks is less than half of the reservation
- * window size.
- */
- if (free_blocks <= (windowsz/2))
- continue;
-
- brelse(bitmap_bh);
- bitmap_bh = ext4_read_block_bitmap(sb, group_no);
- if (!bitmap_bh)
- goto io_error;
- /*
- * try to allocate block(s) from this group, without a goal(-1).
- */
- grp_alloc_blk = ext4_try_to_allocate_with_rsv(sb, handle,
- group_no, bitmap_bh, -1, my_rsv,
- &num, &fatal);
- if (fatal)
- goto out;
- if (grp_alloc_blk >= 0)
- goto allocated;
- }
- /*
- * We may end up a bogus ealier ENOSPC error due to
- * filesystem is "full" of reservations, but
- * there maybe indeed free blocks avaliable on disk
- * In this case, we just forget about the reservations
- * just do block allocation as without reservations.
- */
- if (my_rsv) {
- my_rsv = NULL;
- windowsz = 0;
- group_no = goal_group;
- goto retry_alloc;
- }
- /* No space left on the device */
- *errp = -ENOSPC;
- goto out;
-
-allocated:
-
- ext4_debug("using block group %lu(%d)\n",
- group_no, gdp->bg_free_blocks_count);
-
- BUFFER_TRACE(gdp_bh, "get_write_access");
- fatal = ext4_journal_get_write_access(handle, gdp_bh);
- if (fatal)
- goto out;
-
- ret_block = grp_alloc_blk + ext4_group_first_block_no(sb, group_no);
-
- if (in_range(ext4_block_bitmap(sb, gdp), ret_block, num) ||
- in_range(ext4_inode_bitmap(sb, gdp), ret_block, num) ||
- in_range(ret_block, ext4_inode_table(sb, gdp),
- EXT4_SB(sb)->s_itb_per_group) ||
- in_range(ret_block + num - 1, ext4_inode_table(sb, gdp),
- EXT4_SB(sb)->s_itb_per_group)) {
- ext4_error(sb, "ext4_new_block",
- "Allocating block in system zone - "
- "blocks from %llu, length %lu",
- ret_block, num);
- /*
- * claim_block marked the blocks we allocated
- * as in use. So we may want to selectively
- * mark some of the blocks as free
- */
- goto retry_alloc;
- }
-
- performed_allocation = 1;
-
-#ifdef CONFIG_JBD2_DEBUG
- {
- struct buffer_head *debug_bh;
-
- /* Record bitmap buffer state in the newly allocated block */
- debug_bh = sb_find_get_block(sb, ret_block);
- if (debug_bh) {
- BUFFER_TRACE(debug_bh, "state when allocated");
- BUFFER_TRACE2(debug_bh, bitmap_bh, "bitmap state");
- brelse(debug_bh);
- }
- }
- jbd_lock_bh_state(bitmap_bh);
- spin_lock(sb_bgl_lock(sbi, group_no));
- if (buffer_jbd(bitmap_bh) && bh2jh(bitmap_bh)->b_committed_data) {
- int i;
-
- for (i = 0; i < num; i++) {
- if (ext4_test_bit(grp_alloc_blk+i,
- bh2jh(bitmap_bh)->b_committed_data)) {
- printk("%s: block was unexpectedly set in "
- "b_committed_data\n", __func__);
- }
- }
- }
- ext4_debug("found bit %d\n", grp_alloc_blk);
- spin_unlock(sb_bgl_lock(sbi, group_no));
- jbd_unlock_bh_state(bitmap_bh);
-#endif
-
- if (ret_block + num - 1 >= ext4_blocks_count(es)) {
- ext4_error(sb, "ext4_new_block",
- "block(%llu) >= blocks count(%llu) - "
- "block_group = %lu, es == %p ", ret_block,
- ext4_blocks_count(es), group_no, es);
- goto out;
- }
-
- /*
- * It is up to the caller to add the new buffer to a journal
- * list of some description. We don't know in advance whether
- * the caller wants to use it as metadata or data.
- */
- spin_lock(sb_bgl_lock(sbi, group_no));
- if (gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT))
- gdp->bg_flags &= cpu_to_le16(~EXT4_BG_BLOCK_UNINIT);
- le16_add_cpu(&gdp->bg_free_blocks_count, -num);
- gdp->bg_checksum = ext4_group_desc_csum(sbi, group_no, gdp);
- spin_unlock(sb_bgl_lock(sbi, group_no));
- if (!EXT4_I(inode)->i_delalloc_reserved_flag)
- percpu_counter_sub(&sbi->s_freeblocks_counter, num);
-
- if (sbi->s_log_groups_per_flex) {
- ext4_group_t flex_group = ext4_flex_group(sbi, group_no);
- spin_lock(sb_bgl_lock(sbi, flex_group));
- sbi->s_flex_groups[flex_group].free_blocks -= num;
- spin_unlock(sb_bgl_lock(sbi, flex_group));
- }
-
- BUFFER_TRACE(gdp_bh, "journal_dirty_metadata for group descriptor");
- err = ext4_journal_dirty_metadata(handle, gdp_bh);
- if (!fatal)
- fatal = err;
-
- sb->s_dirt = 1;
- if (fatal)
- goto out;
-
- *errp = 0;
- brelse(bitmap_bh);
- DQUOT_FREE_BLOCK(inode, *count-num);
- *count = num;
- return ret_block;
-
-io_error:
- *errp = -EIO;
-out:
- if (fatal) {
- *errp = fatal;
- ext4_std_error(sb, fatal);
- }
- /*
- * Undo the block allocation
- */
- if (!performed_allocation)
- DQUOT_FREE_BLOCK(inode, *count);
- brelse(bitmap_bh);
- return 0;
-}
-
#define EXT4_META_BLOCK 0x1
static ext4_fsblk_t do_blk_alloc(handle_t *handle, struct inode *inode,
@@ -1963,10 +690,6 @@
struct ext4_allocation_request ar;
ext4_fsblk_t ret;
- if (!test_opt(inode->i_sb, MBALLOC)) {
- return ext4_old_new_blocks(handle, inode, goal, count, errp);
- }
-
memset(&ar, 0, sizeof(ar));
/* Fill with neighbour allocated blocks */
@@ -2008,7 +731,7 @@
/*
* Account for the allocated meta blocks
*/
- if (!(*errp)) {
+ if (!(*errp) && EXT4_I(inode)->i_delalloc_reserved_flag) {
spin_lock(&EXT4_I(inode)->i_block_reservation_lock);
EXT4_I(inode)->i_allocated_meta_blocks += *count;
spin_unlock(&EXT4_I(inode)->i_block_reservation_lock);
@@ -2093,10 +816,9 @@
bitmap_count += x;
}
brelse(bitmap_bh);
- printk("ext4_count_free_blocks: stored = %llu"
- ", computed = %llu, %llu\n",
- ext4_free_blocks_count(es),
- desc_count, bitmap_count);
+ printk(KERN_DEBUG "ext4_count_free_blocks: stored = %llu"
+ ", computed = %llu, %llu\n", ext4_free_blocks_count(es),
+ desc_count, bitmap_count);
return bitmap_count;
#else
desc_count = 0;
@@ -2183,8 +905,9 @@
if (!EXT4_HAS_INCOMPAT_FEATURE(sb,EXT4_FEATURE_INCOMPAT_META_BG) ||
metagroup < first_meta_bg)
- return ext4_bg_num_gdb_nometa(sb,group);
+ return ext4_bg_num_gdb_nometa(sb, group);
return ext4_bg_num_gdb_meta(sb,group);
}
+
diff --git a/fs/ext4/bitmap.c b/fs/ext4/bitmap.c
index d37ea67..0a7a666 100644
--- a/fs/ext4/bitmap.c
+++ b/fs/ext4/bitmap.c
@@ -15,17 +15,17 @@
static const int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0};
-unsigned long ext4_count_free (struct buffer_head * map, unsigned int numchars)
+unsigned long ext4_count_free(struct buffer_head *map, unsigned int numchars)
{
unsigned int i;
unsigned long sum = 0;
if (!map)
- return (0);
+ return 0;
for (i = 0; i < numchars; i++)
sum += nibblemap[map->b_data[i] & 0xf] +
nibblemap[(map->b_data[i] >> 4) & 0xf];
- return (sum);
+ return sum;
}
#endif /* EXT4FS_DEBUG */
diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c
index ec8e33b..3ca6a2b 100644
--- a/fs/ext4/dir.c
+++ b/fs/ext4/dir.c
@@ -33,10 +33,10 @@
};
static int ext4_readdir(struct file *, void *, filldir_t);
-static int ext4_dx_readdir(struct file * filp,
- void * dirent, filldir_t filldir);
-static int ext4_release_dir (struct inode * inode,
- struct file * filp);
+static int ext4_dx_readdir(struct file *filp,
+ void *dirent, filldir_t filldir);
+static int ext4_release_dir(struct inode *inode,
+ struct file *filp);
const struct file_operations ext4_dir_operations = {
.llseek = generic_file_llseek,
@@ -61,12 +61,12 @@
}
-int ext4_check_dir_entry (const char * function, struct inode * dir,
- struct ext4_dir_entry_2 * de,
- struct buffer_head * bh,
- unsigned long offset)
+int ext4_check_dir_entry(const char *function, struct inode *dir,
+ struct ext4_dir_entry_2 *de,
+ struct buffer_head *bh,
+ unsigned long offset)
{
- const char * error_msg = NULL;
+ const char *error_msg = NULL;
const int rlen = ext4_rec_len_from_disk(de->rec_len);
if (rlen < EXT4_DIR_REC_LEN(1))
@@ -82,7 +82,7 @@
error_msg = "inode out of bounds";
if (error_msg != NULL)
- ext4_error (dir->i_sb, function,
+ ext4_error(dir->i_sb, function,
"bad entry in directory #%lu: %s - "
"offset=%lu, inode=%lu, rec_len=%d, name_len=%d",
dir->i_ino, error_msg, offset,
@@ -91,8 +91,8 @@
return error_msg == NULL ? 1 : 0;
}
-static int ext4_readdir(struct file * filp,
- void * dirent, filldir_t filldir)
+static int ext4_readdir(struct file *filp,
+ void *dirent, filldir_t filldir)
{
int error = 0;
unsigned long offset;
@@ -102,6 +102,7 @@
int err;
struct inode *inode = filp->f_path.dentry->d_inode;
int ret = 0;
+ int dir_has_error = 0;
sb = inode->i_sb;
@@ -148,9 +149,13 @@
* of recovering data when there's a bad sector
*/
if (!bh) {
- ext4_error (sb, "ext4_readdir",
- "directory #%lu contains a hole at offset %lu",
- inode->i_ino, (unsigned long)filp->f_pos);
+ if (!dir_has_error) {
+ ext4_error(sb, __func__, "directory #%lu "
+ "contains a hole at offset %Lu",
+ inode->i_ino,
+ (unsigned long long) filp->f_pos);
+ dir_has_error = 1;
+ }
/* corrupt size? Maybe no more blocks to read */
if (filp->f_pos > inode->i_blocks << 9)
break;
@@ -187,14 +192,14 @@
while (!error && filp->f_pos < inode->i_size
&& offset < sb->s_blocksize) {
de = (struct ext4_dir_entry_2 *) (bh->b_data + offset);
- if (!ext4_check_dir_entry ("ext4_readdir", inode, de,
- bh, offset)) {
+ if (!ext4_check_dir_entry("ext4_readdir", inode, de,
+ bh, offset)) {
/*
* On error, skip the f_pos to the next block
*/
filp->f_pos = (filp->f_pos |
(sb->s_blocksize - 1)) + 1;
- brelse (bh);
+ brelse(bh);
ret = stored;
goto out;
}
@@ -218,12 +223,12 @@
break;
if (version != filp->f_version)
goto revalidate;
- stored ++;
+ stored++;
}
filp->f_pos += ext4_rec_len_from_disk(de->rec_len);
}
offset = 0;
- brelse (bh);
+ brelse(bh);
}
out:
return ret;
@@ -290,9 +295,9 @@
parent = rb_parent(n);
fname = rb_entry(n, struct fname, rb_hash);
while (fname) {
- struct fname * old = fname;
+ struct fname *old = fname;
fname = fname->next;
- kfree (old);
+ kfree(old);
}
if (!parent)
root->rb_node = NULL;
@@ -331,7 +336,7 @@
struct ext4_dir_entry_2 *dirent)
{
struct rb_node **p, *parent = NULL;
- struct fname * fname, *new_fn;
+ struct fname *fname, *new_fn;
struct dir_private_info *info;
int len;
@@ -388,19 +393,20 @@
* for all entres on the fname linked list. (Normally there is only
* one entry on the linked list, unless there are 62 bit hash collisions.)
*/
-static int call_filldir(struct file * filp, void * dirent,
+static int call_filldir(struct file *filp, void *dirent,
filldir_t filldir, struct fname *fname)
{
struct dir_private_info *info = filp->private_data;
loff_t curr_pos;
struct inode *inode = filp->f_path.dentry->d_inode;
- struct super_block * sb;
+ struct super_block *sb;
int error;
sb = inode->i_sb;
if (!fname) {
- printk("call_filldir: called with null fname?!?\n");
+ printk(KERN_ERR "ext4: call_filldir: called with "
+ "null fname?!?\n");
return 0;
}
curr_pos = hash2pos(fname->hash, fname->minor_hash);
@@ -419,8 +425,8 @@
return 0;
}
-static int ext4_dx_readdir(struct file * filp,
- void * dirent, filldir_t filldir)
+static int ext4_dx_readdir(struct file *filp,
+ void *dirent, filldir_t filldir)
{
struct dir_private_info *info = filp->private_data;
struct inode *inode = filp->f_path.dentry->d_inode;
@@ -511,7 +517,7 @@
return 0;
}
-static int ext4_release_dir (struct inode * inode, struct file * filp)
+static int ext4_release_dir(struct inode *inode, struct file *filp)
{
if (filp->private_data)
ext4_htree_free_dir_info(filp->private_data);
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 2950032..6690a41 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -44,9 +44,9 @@
#ifdef EXT4FS_DEBUG
#define ext4_debug(f, a...) \
do { \
- printk (KERN_DEBUG "EXT4-fs DEBUG (%s, %d): %s:", \
+ printk(KERN_DEBUG "EXT4-fs DEBUG (%s, %d): %s:", \
__FILE__, __LINE__, __func__); \
- printk (KERN_DEBUG f, ## a); \
+ printk(KERN_DEBUG f, ## a); \
} while (0)
#else
#define ext4_debug(f, a...) do {} while (0)
@@ -128,7 +128,7 @@
#else
# define EXT4_BLOCK_SIZE(s) (EXT4_MIN_BLOCK_SIZE << (s)->s_log_block_size)
#endif
-#define EXT4_ADDR_PER_BLOCK(s) (EXT4_BLOCK_SIZE(s) / sizeof (__u32))
+#define EXT4_ADDR_PER_BLOCK(s) (EXT4_BLOCK_SIZE(s) / sizeof(__u32))
#ifdef __KERNEL__
# define EXT4_BLOCK_SIZE_BITS(s) ((s)->s_blocksize_bits)
#else
@@ -245,7 +245,7 @@
#define EXT4_RESERVED_FL 0x80000000 /* reserved for ext4 lib */
#define EXT4_FL_USER_VISIBLE 0x000BDFFF /* User visible flags */
-#define EXT4_FL_USER_MODIFIABLE 0x000380FF /* User modifiable flags */
+#define EXT4_FL_USER_MODIFIABLE 0x000B80FF /* User modifiable flags */
/*
* Inode dynamic state flags
@@ -291,8 +291,6 @@
#define EXT4_IOC_SETFLAGS FS_IOC_SETFLAGS
#define EXT4_IOC_GETVERSION _IOR('f', 3, long)
#define EXT4_IOC_SETVERSION _IOW('f', 4, long)
-#define EXT4_IOC_GROUP_EXTEND _IOW('f', 7, unsigned long)
-#define EXT4_IOC_GROUP_ADD _IOW('f', 8,struct ext4_new_group_input)
#define EXT4_IOC_GETVERSION_OLD FS_IOC_GETVERSION
#define EXT4_IOC_SETVERSION_OLD FS_IOC_SETVERSION
#ifdef CONFIG_JBD2_DEBUG
@@ -300,7 +298,10 @@
#endif
#define EXT4_IOC_GETRSVSZ _IOR('f', 5, long)
#define EXT4_IOC_SETRSVSZ _IOW('f', 6, long)
-#define EXT4_IOC_MIGRATE _IO('f', 7)
+#define EXT4_IOC_GROUP_EXTEND _IOW('f', 7, unsigned long)
+#define EXT4_IOC_GROUP_ADD _IOW('f', 8, struct ext4_new_group_input)
+#define EXT4_IOC_MIGRATE _IO('f', 9)
+ /* note ioctl 11 reserved for filesystem-independent FIEMAP ioctl */
/*
* ioctl commands in 32 bit emulation
@@ -538,8 +539,9 @@
#define EXT4_MOUNT_JOURNAL_CHECKSUM 0x800000 /* Journal checksums */
#define EXT4_MOUNT_JOURNAL_ASYNC_COMMIT 0x1000000 /* Journal Async Commit */
#define EXT4_MOUNT_I_VERSION 0x2000000 /* i_version support */
-#define EXT4_MOUNT_MBALLOC 0x4000000 /* Buddy allocation support */
#define EXT4_MOUNT_DELALLOC 0x8000000 /* Delalloc support */
+#define EXT4_MOUNT_DATA_ERR_ABORT 0x10000000 /* Abort on file data write */
+
/* Compatibility, for having both ext2_fs.h and ext4_fs.h included at once */
#ifndef _LINUX_EXT2_FS_H
#define clear_opt(o, opt) o &= ~EXT4_MOUNT_##opt
@@ -667,7 +669,7 @@
};
#ifdef __KERNEL__
-static inline struct ext4_sb_info * EXT4_SB(struct super_block *sb)
+static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb)
{
return sb->s_fs_info;
}
@@ -725,11 +727,11 @@
*/
#define EXT4_HAS_COMPAT_FEATURE(sb,mask) \
- ( EXT4_SB(sb)->s_es->s_feature_compat & cpu_to_le32(mask) )
+ (EXT4_SB(sb)->s_es->s_feature_compat & cpu_to_le32(mask))
#define EXT4_HAS_RO_COMPAT_FEATURE(sb,mask) \
- ( EXT4_SB(sb)->s_es->s_feature_ro_compat & cpu_to_le32(mask) )
+ (EXT4_SB(sb)->s_es->s_feature_ro_compat & cpu_to_le32(mask))
#define EXT4_HAS_INCOMPAT_FEATURE(sb,mask) \
- ( EXT4_SB(sb)->s_es->s_feature_incompat & cpu_to_le32(mask) )
+ (EXT4_SB(sb)->s_es->s_feature_incompat & cpu_to_le32(mask))
#define EXT4_SET_COMPAT_FEATURE(sb,mask) \
EXT4_SB(sb)->s_es->s_feature_compat |= cpu_to_le32(mask)
#define EXT4_SET_RO_COMPAT_FEATURE(sb,mask) \
@@ -789,6 +791,8 @@
#define EXT4_DEF_RESUID 0
#define EXT4_DEF_RESGID 0
+#define EXT4_DEF_INODE_READAHEAD_BLKS 32
+
/*
* Default mount options
*/
@@ -954,6 +958,24 @@
void ext4_get_group_no_and_offset(struct super_block *sb, ext4_fsblk_t blocknr,
unsigned long *blockgrpp, ext4_grpblk_t *offsetp);
+extern struct proc_dir_entry *ext4_proc_root;
+
+#ifdef CONFIG_PROC_FS
+extern const struct file_operations ext4_ui_proc_fops;
+
+#define EXT4_PROC_HANDLER(name, var) \
+do { \
+ proc = proc_create_data(name, mode, sbi->s_proc, \
+ &ext4_ui_proc_fops, &sbi->s_##var); \
+ if (proc == NULL) { \
+ printk(KERN_ERR "EXT4-fs: can't create %s\n", name); \
+ goto err_out; \
+ } \
+} while (0)
+#else
+#define EXT4_PROC_HANDLER(name, var)
+#endif
+
/*
* Function prototypes
*/
@@ -981,23 +1003,20 @@
extern ext4_fsblk_t ext4_new_blocks(handle_t *handle, struct inode *inode,
ext4_lblk_t iblock, ext4_fsblk_t goal,
unsigned long *count, int *errp);
-extern ext4_fsblk_t ext4_old_new_blocks(handle_t *handle, struct inode *inode,
- ext4_fsblk_t goal, unsigned long *count, int *errp);
+extern int ext4_claim_free_blocks(struct ext4_sb_info *sbi, s64 nblocks);
extern ext4_fsblk_t ext4_has_free_blocks(struct ext4_sb_info *sbi,
- ext4_fsblk_t nblocks);
-extern void ext4_free_blocks (handle_t *handle, struct inode *inode,
+ s64 nblocks);
+extern void ext4_free_blocks(handle_t *handle, struct inode *inode,
ext4_fsblk_t block, unsigned long count, int metadata);
-extern void ext4_free_blocks_sb (handle_t *handle, struct super_block *sb,
- ext4_fsblk_t block, unsigned long count,
+extern void ext4_free_blocks_sb(handle_t *handle, struct super_block *sb,
+ ext4_fsblk_t block, unsigned long count,
unsigned long *pdquot_freed_blocks);
-extern ext4_fsblk_t ext4_count_free_blocks (struct super_block *);
-extern void ext4_check_blocks_bitmap (struct super_block *);
+extern ext4_fsblk_t ext4_count_free_blocks(struct super_block *);
+extern void ext4_check_blocks_bitmap(struct super_block *);
extern struct ext4_group_desc * ext4_get_group_desc(struct super_block * sb,
ext4_group_t block_group,
struct buffer_head ** bh);
extern int ext4_should_retry_alloc(struct super_block *sb, int *retries);
-extern void ext4_init_block_alloc_info(struct inode *);
-extern void ext4_rsv_window_add(struct super_block *sb, struct ext4_reserve_window_node *rsv);
/* dir.c */
extern int ext4_check_dir_entry(const char *, struct inode *,
@@ -1009,20 +1028,20 @@
extern void ext4_htree_free_dir_info(struct dir_private_info *p);
/* fsync.c */
-extern int ext4_sync_file (struct file *, struct dentry *, int);
+extern int ext4_sync_file(struct file *, struct dentry *, int);
/* hash.c */
extern int ext4fs_dirhash(const char *name, int len, struct
dx_hash_info *hinfo);
/* ialloc.c */
-extern struct inode * ext4_new_inode (handle_t *, struct inode *, int);
-extern void ext4_free_inode (handle_t *, struct inode *);
-extern struct inode * ext4_orphan_get (struct super_block *, unsigned long);
-extern unsigned long ext4_count_free_inodes (struct super_block *);
-extern unsigned long ext4_count_dirs (struct super_block *);
-extern void ext4_check_inodes_bitmap (struct super_block *);
-extern unsigned long ext4_count_free (struct buffer_head *, unsigned);
+extern struct inode * ext4_new_inode(handle_t *, struct inode *, int);
+extern void ext4_free_inode(handle_t *, struct inode *);
+extern struct inode * ext4_orphan_get(struct super_block *, unsigned long);
+extern unsigned long ext4_count_free_inodes(struct super_block *);
+extern unsigned long ext4_count_dirs(struct super_block *);
+extern void ext4_check_inodes_bitmap(struct super_block *);
+extern unsigned long ext4_count_free(struct buffer_head *, unsigned);
/* mballoc.c */
extern long ext4_mb_stats;
@@ -1032,7 +1051,7 @@
extern ext4_fsblk_t ext4_mb_new_blocks(handle_t *,
struct ext4_allocation_request *, int *);
extern int ext4_mb_reserve_blocks(struct super_block *, int);
-extern void ext4_mb_discard_inode_preallocations(struct inode *);
+extern void ext4_discard_preallocations(struct inode *);
extern int __init init_ext4_mballoc(void);
extern void exit_ext4_mballoc(void);
extern void ext4_mb_free_blocks(handle_t *, struct inode *,
@@ -1050,24 +1069,25 @@
ext4_lblk_t, int, int *);
struct buffer_head *ext4_bread(handle_t *, struct inode *,
ext4_lblk_t, int, int *);
+int ext4_get_block(struct inode *inode, sector_t iblock,
+ struct buffer_head *bh_result, int create);
int ext4_get_blocks_handle(handle_t *handle, struct inode *inode,
ext4_lblk_t iblock, unsigned long maxblocks,
struct buffer_head *bh_result,
int create, int extend_disksize);
extern struct inode *ext4_iget(struct super_block *, unsigned long);
-extern int ext4_write_inode (struct inode *, int);
-extern int ext4_setattr (struct dentry *, struct iattr *);
+extern int ext4_write_inode(struct inode *, int);
+extern int ext4_setattr(struct dentry *, struct iattr *);
extern int ext4_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat);
-extern void ext4_delete_inode (struct inode *);
-extern int ext4_sync_inode (handle_t *, struct inode *);
-extern void ext4_discard_reservation (struct inode *);
+extern void ext4_delete_inode(struct inode *);
+extern int ext4_sync_inode(handle_t *, struct inode *);
extern void ext4_dirty_inode(struct inode *);
extern int ext4_change_inode_journal_flag(struct inode *, int);
extern int ext4_get_inode_loc(struct inode *, struct ext4_iloc *);
extern int ext4_can_truncate(struct inode *inode);
-extern void ext4_truncate (struct inode *);
+extern void ext4_truncate(struct inode *);
extern void ext4_set_inode_flags(struct inode *);
extern void ext4_get_inode_flags(struct ext4_inode_info *);
extern void ext4_set_aops(struct inode *inode);
@@ -1080,11 +1100,10 @@
/* ioctl.c */
extern long ext4_ioctl(struct file *, unsigned int, unsigned long);
-extern long ext4_compat_ioctl (struct file *, unsigned int, unsigned long);
+extern long ext4_compat_ioctl(struct file *, unsigned int, unsigned long);
/* migrate.c */
-extern int ext4_ext_migrate(struct inode *, struct file *, unsigned int,
- unsigned long);
+extern int ext4_ext_migrate(struct inode *);
/* namei.c */
extern int ext4_orphan_add(handle_t *, struct inode *);
extern int ext4_orphan_del(handle_t *, struct inode *);
@@ -1099,14 +1118,14 @@
ext4_fsblk_t n_blocks_count);
/* super.c */
-extern void ext4_error (struct super_block *, const char *, const char *, ...)
+extern void ext4_error(struct super_block *, const char *, const char *, ...)
__attribute__ ((format (printf, 3, 4)));
-extern void __ext4_std_error (struct super_block *, const char *, int);
-extern void ext4_abort (struct super_block *, const char *, const char *, ...)
+extern void __ext4_std_error(struct super_block *, const char *, int);
+extern void ext4_abort(struct super_block *, const char *, const char *, ...)
__attribute__ ((format (printf, 3, 4)));
-extern void ext4_warning (struct super_block *, const char *, const char *, ...)
+extern void ext4_warning(struct super_block *, const char *, const char *, ...)
__attribute__ ((format (printf, 3, 4)));
-extern void ext4_update_dynamic_rev (struct super_block *sb);
+extern void ext4_update_dynamic_rev(struct super_block *sb);
extern int ext4_update_compat_feature(handle_t *handle, struct super_block *sb,
__u32 compat);
extern int ext4_update_rocompat_feature(handle_t *handle,
@@ -1179,7 +1198,7 @@
static inline
struct ext4_group_info *ext4_get_group_info(struct super_block *sb,
- ext4_group_t group)
+ ext4_group_t group)
{
struct ext4_group_info ***grp_info;
long indexv, indexh;
@@ -1207,6 +1226,28 @@
__ext4_std_error((sb), __func__, (errno)); \
} while (0)
+#ifdef CONFIG_SMP
+/* Each CPU can accumulate FBC_BATCH blocks in their local
+ * counters. So we need to make sure we have free blocks more
+ * than FBC_BATCH * nr_cpu_ids. Also add a window of 4 times.
+ */
+#define EXT4_FREEBLOCKS_WATERMARK (4 * (FBC_BATCH * nr_cpu_ids))
+#else
+#define EXT4_FREEBLOCKS_WATERMARK 0
+#endif
+
+static inline void ext4_update_i_disksize(struct inode *inode, loff_t newsize)
+{
+ /*
+ * XXX: replace with spinlock if seen contended -bzzz
+ */
+ down_write(&EXT4_I(inode)->i_data_sem);
+ if (newsize > EXT4_I(inode)->i_disksize)
+ EXT4_I(inode)->i_disksize = newsize;
+ up_write(&EXT4_I(inode)->i_data_sem);
+ return ;
+}
+
/*
* Inodes and files operations
*/
diff --git a/fs/ext4/ext4_extents.h b/fs/ext4/ext4_extents.h
index d33dc56..bec7ce5 100644
--- a/fs/ext4/ext4_extents.h
+++ b/fs/ext4/ext4_extents.h
@@ -124,6 +124,19 @@
#define EXT4_EXT_CACHE_GAP 1
#define EXT4_EXT_CACHE_EXTENT 2
+/*
+ * to be called by ext4_ext_walk_space()
+ * negative retcode - error
+ * positive retcode - signal for ext4_ext_walk_space(), see below
+ * callback must return valid extent (passed or newly created)
+ */
+typedef int (*ext_prepare_callback)(struct inode *, struct ext4_ext_path *,
+ struct ext4_ext_cache *,
+ struct ext4_extent *, void *);
+
+#define EXT_CONTINUE 0
+#define EXT_BREAK 1
+#define EXT_REPEAT 2
#define EXT_MAX_BLOCK 0xffffffff
@@ -224,6 +237,8 @@
struct ext4_extent *);
extern unsigned int ext4_ext_check_overlap(struct inode *, struct ext4_extent *, struct ext4_ext_path *);
extern int ext4_ext_insert_extent(handle_t *, struct inode *, struct ext4_ext_path *, struct ext4_extent *);
+extern int ext4_ext_walk_space(struct inode *, ext4_lblk_t, ext4_lblk_t,
+ ext_prepare_callback, void *);
extern struct ext4_ext_path *ext4_ext_find_extent(struct inode *, ext4_lblk_t,
struct ext4_ext_path *);
extern int ext4_ext_search_left(struct inode *, struct ext4_ext_path *,
diff --git a/fs/ext4/ext4_i.h b/fs/ext4/ext4_i.h
index ef7409f..5c124c0 100644
--- a/fs/ext4/ext4_i.h
+++ b/fs/ext4/ext4_i.h
@@ -33,38 +33,6 @@
/* data type for block group number */
typedef unsigned long ext4_group_t;
-struct ext4_reserve_window {
- ext4_fsblk_t _rsv_start; /* First byte reserved */
- ext4_fsblk_t _rsv_end; /* Last byte reserved or 0 */
-};
-
-struct ext4_reserve_window_node {
- struct rb_node rsv_node;
- __u32 rsv_goal_size;
- __u32 rsv_alloc_hit;
- struct ext4_reserve_window rsv_window;
-};
-
-struct ext4_block_alloc_info {
- /* information about reservation window */
- struct ext4_reserve_window_node rsv_window_node;
- /*
- * was i_next_alloc_block in ext4_inode_info
- * is the logical (file-relative) number of the
- * most-recently-allocated block in this file.
- * We use this for detecting linearly ascending allocation requests.
- */
- ext4_lblk_t last_alloc_logical_block;
- /*
- * Was i_next_alloc_goal in ext4_inode_info
- * is the *physical* companion to i_next_alloc_block.
- * it the physical block number of the block which was most-recentl
- * allocated to this file. This give us the goal (target) for the next
- * allocation when we detect linearly ascending requests.
- */
- ext4_fsblk_t last_alloc_physical_block;
-};
-
#define rsv_start rsv_window._rsv_start
#define rsv_end rsv_window._rsv_end
@@ -97,11 +65,8 @@
ext4_group_t i_block_group;
__u32 i_state; /* Dynamic state flags for ext4 */
- /* block reservation info */
- struct ext4_block_alloc_info *i_block_alloc_info;
-
ext4_lblk_t i_dir_start_lookup;
-#ifdef CONFIG_EXT4DEV_FS_XATTR
+#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
@@ -111,7 +76,7 @@
*/
struct rw_semaphore xattr_sem;
#endif
-#ifdef CONFIG_EXT4DEV_FS_POSIX_ACL
+#ifdef CONFIG_EXT4_FS_POSIX_ACL
struct posix_acl *i_acl;
struct posix_acl *i_default_acl;
#endif
diff --git a/fs/ext4/ext4_sb.h b/fs/ext4/ext4_sb.h
index 6300226..6a0b40d 100644
--- a/fs/ext4/ext4_sb.h
+++ b/fs/ext4/ext4_sb.h
@@ -40,8 +40,8 @@
unsigned long s_blocks_last; /* Last seen block count */
loff_t s_bitmap_maxbytes; /* max bytes for bitmap files */
struct buffer_head * s_sbh; /* Buffer containing the super block */
- struct ext4_super_block * s_es; /* Pointer to the super block in the buffer */
- struct buffer_head ** s_group_desc;
+ struct ext4_super_block *s_es; /* Pointer to the super block in the buffer */
+ struct buffer_head **s_group_desc;
unsigned long s_mount_opt;
ext4_fsblk_t s_sb_block;
uid_t s_resuid;
@@ -52,6 +52,7 @@
int s_desc_per_block_bits;
int s_inode_size;
int s_first_ino;
+ unsigned int s_inode_readahead_blks;
spinlock_t s_next_gen_lock;
u32 s_next_generation;
u32 s_hash_seed[4];
@@ -59,16 +60,17 @@
struct percpu_counter s_freeblocks_counter;
struct percpu_counter s_freeinodes_counter;
struct percpu_counter s_dirs_counter;
+ struct percpu_counter s_dirtyblocks_counter;
struct blockgroup_lock s_blockgroup_lock;
+ struct proc_dir_entry *s_proc;
/* root of the per fs reservation window tree */
spinlock_t s_rsv_window_lock;
struct rb_root s_rsv_window_root;
- struct ext4_reserve_window_node s_rsv_window_head;
/* Journaling */
- struct inode * s_journal_inode;
- struct journal_s * s_journal;
+ struct inode *s_journal_inode;
+ struct journal_s *s_journal;
struct list_head s_orphan;
unsigned long s_commit_interval;
struct block_device *journal_bdev;
@@ -106,12 +108,12 @@
/* tunables */
unsigned long s_stripe;
- unsigned long s_mb_stream_request;
- unsigned long s_mb_max_to_scan;
- unsigned long s_mb_min_to_scan;
- unsigned long s_mb_stats;
- unsigned long s_mb_order2_reqs;
- unsigned long s_mb_group_prealloc;
+ unsigned int s_mb_stream_request;
+ unsigned int s_mb_max_to_scan;
+ unsigned int s_mb_min_to_scan;
+ unsigned int s_mb_stats;
+ unsigned int s_mb_order2_reqs;
+ unsigned int s_mb_group_prealloc;
/* where last allocation was done - for stream allocation */
unsigned long s_mb_last_group;
unsigned long s_mb_last_start;
@@ -121,7 +123,6 @@
int s_mb_history_cur;
int s_mb_history_max;
int s_mb_history_num;
- struct proc_dir_entry *s_mb_proc;
spinlock_t s_mb_history_lock;
int s_mb_history_filter;
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index b24d3c5..ea2ce3c 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -40,6 +40,7 @@
#include <linux/slab.h>
#include <linux/falloc.h>
#include <asm/uaccess.h>
+#include <linux/fiemap.h>
#include "ext4_jbd2.h"
#include "ext4_extents.h"
@@ -383,8 +384,8 @@
ext_debug("\n");
}
#else
-#define ext4_ext_show_path(inode,path)
-#define ext4_ext_show_leaf(inode,path)
+#define ext4_ext_show_path(inode, path)
+#define ext4_ext_show_leaf(inode, path)
#endif
void ext4_ext_drop_refs(struct ext4_ext_path *path)
@@ -440,9 +441,10 @@
for (k = 0; k < le16_to_cpu(eh->eh_entries); k++, ix++) {
if (k != 0 &&
le32_to_cpu(ix->ei_block) <= le32_to_cpu(ix[-1].ei_block)) {
- printk("k=%d, ix=0x%p, first=0x%p\n", k,
- ix, EXT_FIRST_INDEX(eh));
- printk("%u <= %u\n",
+ printk(KERN_DEBUG "k=%d, ix=0x%p, "
+ "first=0x%p\n", k,
+ ix, EXT_FIRST_INDEX(eh));
+ printk(KERN_DEBUG "%u <= %u\n",
le32_to_cpu(ix->ei_block),
le32_to_cpu(ix[-1].ei_block));
}
@@ -1475,7 +1477,7 @@
struct ext4_ext_path *path,
struct ext4_extent *newext)
{
- struct ext4_extent_header * eh;
+ struct ext4_extent_header *eh;
struct ext4_extent *ex, *fex;
struct ext4_extent *nearex; /* nearest extent */
struct ext4_ext_path *npath = NULL;
@@ -1625,6 +1627,113 @@
return err;
}
+int ext4_ext_walk_space(struct inode *inode, ext4_lblk_t block,
+ ext4_lblk_t num, ext_prepare_callback func,
+ void *cbdata)
+{
+ struct ext4_ext_path *path = NULL;
+ struct ext4_ext_cache cbex;
+ struct ext4_extent *ex;
+ ext4_lblk_t next, start = 0, end = 0;
+ ext4_lblk_t last = block + num;
+ int depth, exists, err = 0;
+
+ BUG_ON(func == NULL);
+ BUG_ON(inode == NULL);
+
+ while (block < last && block != EXT_MAX_BLOCK) {
+ num = last - block;
+ /* find extent for this block */
+ path = ext4_ext_find_extent(inode, block, path);
+ if (IS_ERR(path)) {
+ err = PTR_ERR(path);
+ path = NULL;
+ break;
+ }
+
+ depth = ext_depth(inode);
+ BUG_ON(path[depth].p_hdr == NULL);
+ ex = path[depth].p_ext;
+ next = ext4_ext_next_allocated_block(path);
+
+ exists = 0;
+ if (!ex) {
+ /* there is no extent yet, so try to allocate
+ * all requested space */
+ start = block;
+ end = block + num;
+ } else if (le32_to_cpu(ex->ee_block) > block) {
+ /* need to allocate space before found extent */
+ start = block;
+ end = le32_to_cpu(ex->ee_block);
+ if (block + num < end)
+ end = block + num;
+ } else if (block >= le32_to_cpu(ex->ee_block)
+ + ext4_ext_get_actual_len(ex)) {
+ /* need to allocate space after found extent */
+ start = block;
+ end = block + num;
+ if (end >= next)
+ end = next;
+ } else if (block >= le32_to_cpu(ex->ee_block)) {
+ /*
+ * some part of requested space is covered
+ * by found extent
+ */
+ start = block;
+ end = le32_to_cpu(ex->ee_block)
+ + ext4_ext_get_actual_len(ex);
+ if (block + num < end)
+ end = block + num;
+ exists = 1;
+ } else {
+ BUG();
+ }
+ BUG_ON(end <= start);
+
+ if (!exists) {
+ cbex.ec_block = start;
+ cbex.ec_len = end - start;
+ cbex.ec_start = 0;
+ cbex.ec_type = EXT4_EXT_CACHE_GAP;
+ } else {
+ cbex.ec_block = le32_to_cpu(ex->ee_block);
+ cbex.ec_len = ext4_ext_get_actual_len(ex);
+ cbex.ec_start = ext_pblock(ex);
+ cbex.ec_type = EXT4_EXT_CACHE_EXTENT;
+ }
+
+ BUG_ON(cbex.ec_len == 0);
+ err = func(inode, path, &cbex, ex, cbdata);
+ ext4_ext_drop_refs(path);
+
+ if (err < 0)
+ break;
+
+ if (err == EXT_REPEAT)
+ continue;
+ else if (err == EXT_BREAK) {
+ err = 0;
+ break;
+ }
+
+ if (ext_depth(inode) != depth) {
+ /* depth was changed. we have to realloc path */
+ kfree(path);
+ path = NULL;
+ }
+
+ block = cbex.ec_block + cbex.ec_len;
+ }
+
+ if (path) {
+ ext4_ext_drop_refs(path);
+ kfree(path);
+ }
+
+ return err;
+}
+
static void
ext4_ext_put_in_cache(struct inode *inode, ext4_lblk_t block,
__u32 len, ext4_fsblk_t start, int type)
@@ -2142,7 +2251,7 @@
*/
if (test_opt(sb, EXTENTS)) {
- printk("EXT4-fs: file extents enabled");
+ printk(KERN_INFO "EXT4-fs: file extents enabled");
#ifdef AGGRESSIVE_TEST
printk(", aggressive tests");
#endif
@@ -2696,11 +2805,8 @@
goto out2;
}
/*
- * Okay, we need to do block allocation. Lazily initialize the block
- * allocation info here if necessary.
+ * Okay, we need to do block allocation.
*/
- if (S_ISREG(inode->i_mode) && (!EXT4_I(inode)->i_block_alloc_info))
- ext4_init_block_alloc_info(inode);
/* find neighbour allocated blocks */
ar.lleft = iblock;
@@ -2760,7 +2866,7 @@
/* free data blocks we just allocated */
/* not a good idea to call discard here directly,
* but otherwise we'd need to call it every free() */
- ext4_mb_discard_inode_preallocations(inode);
+ ext4_discard_preallocations(inode);
ext4_free_blocks(handle, inode, ext_pblock(&newex),
ext4_ext_get_actual_len(&newex), 0);
goto out2;
@@ -2824,7 +2930,7 @@
down_write(&EXT4_I(inode)->i_data_sem);
ext4_ext_invalidate_cache(inode);
- ext4_discard_reservation(inode);
+ ext4_discard_preallocations(inode);
/*
* TODO: optimization is possible here.
@@ -2877,10 +2983,11 @@
* Update only when preallocation was requested beyond
* the file size.
*/
- if (!(mode & FALLOC_FL_KEEP_SIZE) &&
- new_size > i_size_read(inode)) {
- i_size_write(inode, new_size);
- EXT4_I(inode)->i_disksize = new_size;
+ if (!(mode & FALLOC_FL_KEEP_SIZE)) {
+ if (new_size > i_size_read(inode))
+ i_size_write(inode, new_size);
+ if (new_size > EXT4_I(inode)->i_disksize)
+ ext4_update_i_disksize(inode, new_size);
}
}
@@ -2972,3 +3079,143 @@
mutex_unlock(&inode->i_mutex);
return ret > 0 ? ret2 : ret;
}
+
+/*
+ * Callback function called for each extent to gather FIEMAP information.
+ */
+int ext4_ext_fiemap_cb(struct inode *inode, struct ext4_ext_path *path,
+ struct ext4_ext_cache *newex, struct ext4_extent *ex,
+ void *data)
+{
+ struct fiemap_extent_info *fieinfo = data;
+ unsigned long blksize_bits = inode->i_sb->s_blocksize_bits;
+ __u64 logical;
+ __u64 physical;
+ __u64 length;
+ __u32 flags = 0;
+ int error;
+
+ logical = (__u64)newex->ec_block << blksize_bits;
+
+ if (newex->ec_type == EXT4_EXT_CACHE_GAP) {
+ pgoff_t offset;
+ struct page *page;
+ struct buffer_head *bh = NULL;
+
+ offset = logical >> PAGE_SHIFT;
+ page = find_get_page(inode->i_mapping, offset);
+ if (!page || !page_has_buffers(page))
+ return EXT_CONTINUE;
+
+ bh = page_buffers(page);
+
+ if (!bh)
+ return EXT_CONTINUE;
+
+ if (buffer_delay(bh)) {
+ flags |= FIEMAP_EXTENT_DELALLOC;
+ page_cache_release(page);
+ } else {
+ page_cache_release(page);
+ return EXT_CONTINUE;
+ }
+ }
+
+ physical = (__u64)newex->ec_start << blksize_bits;
+ length = (__u64)newex->ec_len << blksize_bits;
+
+ if (ex && ext4_ext_is_uninitialized(ex))
+ flags |= FIEMAP_EXTENT_UNWRITTEN;
+
+ /*
+ * If this extent reaches EXT_MAX_BLOCK, it must be last.
+ *
+ * Or if ext4_ext_next_allocated_block is EXT_MAX_BLOCK,
+ * this also indicates no more allocated blocks.
+ *
+ * XXX this might miss a single-block extent at EXT_MAX_BLOCK
+ */
+ if (logical + length - 1 == EXT_MAX_BLOCK ||
+ ext4_ext_next_allocated_block(path) == EXT_MAX_BLOCK)
+ flags |= FIEMAP_EXTENT_LAST;
+
+ error = fiemap_fill_next_extent(fieinfo, logical, physical,
+ length, flags);
+ if (error < 0)
+ return error;
+ if (error == 1)
+ return EXT_BREAK;
+
+ return EXT_CONTINUE;
+}
+
+/* fiemap flags we can handle specified here */
+#define EXT4_FIEMAP_FLAGS (FIEMAP_FLAG_SYNC|FIEMAP_FLAG_XATTR)
+
+int ext4_xattr_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo)
+{
+ __u64 physical = 0;
+ __u64 length;
+ __u32 flags = FIEMAP_EXTENT_LAST;
+ int blockbits = inode->i_sb->s_blocksize_bits;
+ int error = 0;
+
+ /* in-inode? */
+ if (EXT4_I(inode)->i_state & EXT4_STATE_XATTR) {
+ struct ext4_iloc iloc;
+ int offset; /* offset of xattr in inode */
+
+ error = ext4_get_inode_loc(inode, &iloc);
+ if (error)
+ return error;
+ physical = iloc.bh->b_blocknr << blockbits;
+ offset = EXT4_GOOD_OLD_INODE_SIZE +
+ EXT4_I(inode)->i_extra_isize;
+ physical += offset;
+ length = EXT4_SB(inode->i_sb)->s_inode_size - offset;
+ flags |= FIEMAP_EXTENT_DATA_INLINE;
+ } else { /* external block */
+ physical = EXT4_I(inode)->i_file_acl << blockbits;
+ length = inode->i_sb->s_blocksize;
+ }
+
+ if (physical)
+ error = fiemap_fill_next_extent(fieinfo, 0, physical,
+ length, flags);
+ return (error < 0 ? error : 0);
+}
+
+int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
+ __u64 start, __u64 len)
+{
+ ext4_lblk_t start_blk;
+ ext4_lblk_t len_blks;
+ int error = 0;
+
+ /* fallback to generic here if not in extents fmt */
+ if (!(EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL))
+ return generic_block_fiemap(inode, fieinfo, start, len,
+ ext4_get_block);
+
+ if (fiemap_check_flags(fieinfo, EXT4_FIEMAP_FLAGS))
+ return -EBADR;
+
+ if (fieinfo->fi_flags & FIEMAP_FLAG_XATTR) {
+ error = ext4_xattr_fiemap(inode, fieinfo);
+ } else {
+ start_blk = start >> inode->i_sb->s_blocksize_bits;
+ len_blks = len >> inode->i_sb->s_blocksize_bits;
+
+ /*
+ * Walk the extent tree gathering extent information.
+ * ext4_ext_fiemap_cb will push extents back to user.
+ */
+ down_write(&EXT4_I(inode)->i_data_sem);
+ error = ext4_ext_walk_space(inode, start_blk, len_blks,
+ ext4_ext_fiemap_cb, fieinfo);
+ up_write(&EXT4_I(inode)->i_data_sem);
+ }
+
+ return error;
+}
+
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index 430eb79..6bd11fb 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -31,14 +31,14 @@
* from ext4_file_open: open gets called at every open, but release
* gets called only when /all/ the files are closed.
*/
-static int ext4_release_file (struct inode * inode, struct file * filp)
+static int ext4_release_file(struct inode *inode, struct file *filp)
{
/* if we are the last writer on the inode, drop the block reservation */
if ((filp->f_mode & FMODE_WRITE) &&
(atomic_read(&inode->i_writecount) == 1))
{
down_write(&EXT4_I(inode)->i_data_sem);
- ext4_discard_reservation(inode);
+ ext4_discard_preallocations(inode);
up_write(&EXT4_I(inode)->i_data_sem);
}
if (is_dx(inode) && filp->private_data)
@@ -140,6 +140,9 @@
return 0;
}
+extern int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
+ __u64 start, __u64 len);
+
const struct file_operations ext4_file_operations = {
.llseek = generic_file_llseek,
.read = do_sync_read,
@@ -162,7 +165,7 @@
.truncate = ext4_truncate,
.setattr = ext4_setattr,
.getattr = ext4_getattr,
-#ifdef CONFIG_EXT4DEV_FS_XATTR
+#ifdef CONFIG_EXT4_FS_XATTR
.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
.listxattr = ext4_listxattr,
@@ -170,5 +173,6 @@
#endif
.permission = ext4_permission,
.fallocate = ext4_fallocate,
+ .fiemap = ext4_fiemap,
};
diff --git a/fs/ext4/fsync.c b/fs/ext4/fsync.c
index a45c373..5afe437 100644
--- a/fs/ext4/fsync.c
+++ b/fs/ext4/fsync.c
@@ -28,6 +28,7 @@
#include <linux/writeback.h>
#include <linux/jbd2.h>
#include <linux/blkdev.h>
+#include <linux/marker.h>
#include "ext4.h"
#include "ext4_jbd2.h"
@@ -43,7 +44,7 @@
* inode to disk.
*/
-int ext4_sync_file(struct file * file, struct dentry *dentry, int datasync)
+int ext4_sync_file(struct file *file, struct dentry *dentry, int datasync)
{
struct inode *inode = dentry->d_inode;
journal_t *journal = EXT4_SB(inode->i_sb)->s_journal;
@@ -51,6 +52,10 @@
J_ASSERT(ext4_journal_current_handle() == NULL);
+ trace_mark(ext4_sync_file, "dev %s datasync %d ino %ld parent %ld",
+ inode->i_sb->s_id, datasync, inode->i_ino,
+ dentry->d_parent->d_inode->i_ino);
+
/*
* data=writeback:
* The caller's filemap_fdatawrite()/wait will sync the data.
diff --git a/fs/ext4/hash.c b/fs/ext4/hash.c
index 1d6329d..556ca8e 100644
--- a/fs/ext4/hash.c
+++ b/fs/ext4/hash.c
@@ -27,7 +27,7 @@
sum += DELTA;
b0 += ((b1 << 4)+a) ^ (b1+sum) ^ ((b1 >> 5)+b);
b1 += ((b0 << 4)+c) ^ (b0+sum) ^ ((b0 >> 5)+d);
- } while(--n);
+ } while (--n);
buf[0] += b0;
buf[1] += b1;
@@ -35,7 +35,7 @@
/* The old legacy hash */
-static __u32 dx_hack_hash (const char *name, int len)
+static __u32 dx_hack_hash(const char *name, int len)
{
__u32 hash0 = 0x12a3fe2d, hash1 = 0x37abe8f9;
while (len--) {
@@ -59,7 +59,7 @@
val = pad;
if (len > num*4)
len = num * 4;
- for (i=0; i < len; i++) {
+ for (i = 0; i < len; i++) {
if ((i % 4) == 0)
val = pad;
val = msg[i] + (val << 8);
@@ -104,7 +104,7 @@
/* Check to see if the seed is all zero's */
if (hinfo->seed) {
- for (i=0; i < 4; i++) {
+ for (i = 0; i < 4; i++) {
if (hinfo->seed[i])
break;
}
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index f344834..fe34d74 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -115,9 +115,11 @@
block_group, bitmap_blk);
return NULL;
}
- if (bh_uptodate_or_lock(bh))
+ if (buffer_uptodate(bh) &&
+ !(desc->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT)))
return bh;
+ lock_buffer(bh);
spin_lock(sb_bgl_lock(EXT4_SB(sb), block_group));
if (desc->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT)) {
ext4_init_inode_bitmap(sb, bh, block_group, desc);
@@ -154,39 +156,40 @@
* though), and then we'd have two inodes sharing the
* same inode number and space on the harddisk.
*/
-void ext4_free_inode (handle_t *handle, struct inode * inode)
+void ext4_free_inode(handle_t *handle, struct inode *inode)
{
- struct super_block * sb = inode->i_sb;
+ struct super_block *sb = inode->i_sb;
int is_directory;
unsigned long ino;
struct buffer_head *bitmap_bh = NULL;
struct buffer_head *bh2;
ext4_group_t block_group;
unsigned long bit;
- struct ext4_group_desc * gdp;
- struct ext4_super_block * es;
+ struct ext4_group_desc *gdp;
+ struct ext4_super_block *es;
struct ext4_sb_info *sbi;
int fatal = 0, err;
ext4_group_t flex_group;
if (atomic_read(&inode->i_count) > 1) {
- printk ("ext4_free_inode: inode has count=%d\n",
- atomic_read(&inode->i_count));
+ printk(KERN_ERR "ext4_free_inode: inode has count=%d\n",
+ atomic_read(&inode->i_count));
return;
}
if (inode->i_nlink) {
- printk ("ext4_free_inode: inode has nlink=%d\n",
- inode->i_nlink);
+ printk(KERN_ERR "ext4_free_inode: inode has nlink=%d\n",
+ inode->i_nlink);
return;
}
if (!sb) {
- printk("ext4_free_inode: inode on nonexistent device\n");
+ printk(KERN_ERR "ext4_free_inode: inode on "
+ "nonexistent device\n");
return;
}
sbi = EXT4_SB(sb);
ino = inode->i_ino;
- ext4_debug ("freeing inode %lu\n", ino);
+ ext4_debug("freeing inode %lu\n", ino);
/*
* Note: we must free any quota before locking the superblock,
@@ -200,12 +203,12 @@
is_directory = S_ISDIR(inode->i_mode);
/* Do this BEFORE marking the inode not in use or returning an error */
- clear_inode (inode);
+ clear_inode(inode);
es = EXT4_SB(sb)->s_es;
if (ino < EXT4_FIRST_INO(sb) || ino > le32_to_cpu(es->s_inodes_count)) {
- ext4_error (sb, "ext4_free_inode",
- "reserved or nonexistent inode %lu", ino);
+ ext4_error(sb, "ext4_free_inode",
+ "reserved or nonexistent inode %lu", ino);
goto error_return;
}
block_group = (ino - 1) / EXT4_INODES_PER_GROUP(sb);
@@ -222,10 +225,10 @@
/* Ok, now we can actually update the inode bitmaps.. */
if (!ext4_clear_bit_atomic(sb_bgl_lock(sbi, block_group),
bit, bitmap_bh->b_data))
- ext4_error (sb, "ext4_free_inode",
- "bit already cleared for inode %lu", ino);
+ ext4_error(sb, "ext4_free_inode",
+ "bit already cleared for inode %lu", ino);
else {
- gdp = ext4_get_group_desc (sb, block_group, &bh2);
+ gdp = ext4_get_group_desc(sb, block_group, &bh2);
BUFFER_TRACE(bh2, "get_write_access");
fatal = ext4_journal_get_write_access(handle, bh2);
@@ -287,7 +290,7 @@
avefreei = freei / ngroups;
for (group = 0; group < ngroups; group++) {
- desc = ext4_get_group_desc (sb, group, NULL);
+ desc = ext4_get_group_desc(sb, group, NULL);
if (!desc || !desc->bg_free_inodes_count)
continue;
if (le16_to_cpu(desc->bg_free_inodes_count) < avefreei)
@@ -576,16 +579,16 @@
* For other inodes, search forward from the parent directory's block
* group to find a free inode.
*/
-struct inode *ext4_new_inode(handle_t *handle, struct inode * dir, int mode)
+struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, int mode)
{
struct super_block *sb;
struct buffer_head *bitmap_bh = NULL;
struct buffer_head *bh2;
ext4_group_t group = 0;
unsigned long ino = 0;
- struct inode * inode;
- struct ext4_group_desc * gdp = NULL;
- struct ext4_super_block * es;
+ struct inode *inode;
+ struct ext4_group_desc *gdp = NULL;
+ struct ext4_super_block *es;
struct ext4_inode_info *ei;
struct ext4_sb_info *sbi;
int ret2, err = 0;
@@ -613,7 +616,7 @@
}
if (S_ISDIR(mode)) {
- if (test_opt (sb, OLDALLOC))
+ if (test_opt(sb, OLDALLOC))
ret2 = find_group_dir(sb, dir, &group);
else
ret2 = find_group_orlov(sb, dir, &group);
@@ -783,7 +786,7 @@
}
inode->i_uid = current->fsuid;
- if (test_opt (sb, GRPID))
+ if (test_opt(sb, GRPID))
inode->i_gid = dir->i_gid;
else if (dir->i_mode & S_ISGID) {
inode->i_gid = dir->i_gid;
@@ -816,7 +819,6 @@
ei->i_flags &= ~EXT4_DIRSYNC_FL;
ei->i_file_acl = 0;
ei->i_dtime = 0;
- ei->i_block_alloc_info = NULL;
ei->i_block_group = group;
ext4_set_inode_flags(inode);
@@ -832,7 +834,7 @@
ei->i_extra_isize = EXT4_SB(sb)->s_want_extra_isize;
ret = inode;
- if(DQUOT_ALLOC_INODE(inode)) {
+ if (DQUOT_ALLOC_INODE(inode)) {
err = -EDQUOT;
goto fail_drop;
}
@@ -841,7 +843,7 @@
if (err)
goto fail_free_drop;
- err = ext4_init_security(handle,inode, dir);
+ err = ext4_init_security(handle, inode, dir);
if (err)
goto fail_free_drop;
@@ -959,7 +961,7 @@
return ERR_PTR(err);
}
-unsigned long ext4_count_free_inodes (struct super_block * sb)
+unsigned long ext4_count_free_inodes(struct super_block *sb)
{
unsigned long desc_count;
struct ext4_group_desc *gdp;
@@ -974,7 +976,7 @@
bitmap_count = 0;
gdp = NULL;
for (i = 0; i < EXT4_SB(sb)->s_groups_count; i++) {
- gdp = ext4_get_group_desc (sb, i, NULL);
+ gdp = ext4_get_group_desc(sb, i, NULL);
if (!gdp)
continue;
desc_count += le16_to_cpu(gdp->bg_free_inodes_count);
@@ -989,13 +991,14 @@
bitmap_count += x;
}
brelse(bitmap_bh);
- printk("ext4_count_free_inodes: stored = %u, computed = %lu, %lu\n",
- le32_to_cpu(es->s_free_inodes_count), desc_count, bitmap_count);
+ printk(KERN_DEBUG "ext4_count_free_inodes: "
+ "stored = %u, computed = %lu, %lu\n",
+ le32_to_cpu(es->s_free_inodes_count), desc_count, bitmap_count);
return desc_count;
#else
desc_count = 0;
for (i = 0; i < EXT4_SB(sb)->s_groups_count; i++) {
- gdp = ext4_get_group_desc (sb, i, NULL);
+ gdp = ext4_get_group_desc(sb, i, NULL);
if (!gdp)
continue;
desc_count += le16_to_cpu(gdp->bg_free_inodes_count);
@@ -1006,13 +1009,13 @@
}
/* Called at mount-time, super-block is locked */
-unsigned long ext4_count_dirs (struct super_block * sb)
+unsigned long ext4_count_dirs(struct super_block * sb)
{
unsigned long count = 0;
ext4_group_t i;
for (i = 0; i < EXT4_SB(sb)->s_groups_count; i++) {
- struct ext4_group_desc *gdp = ext4_get_group_desc (sb, i, NULL);
+ struct ext4_group_desc *gdp = ext4_get_group_desc(sb, i, NULL);
if (!gdp)
continue;
count += le16_to_cpu(gdp->bg_used_dirs_count);
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 7e91913e..9b4ec9d 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -190,7 +190,7 @@
/*
* Called at the last iput() if i_nlink is zero.
*/
-void ext4_delete_inode (struct inode * inode)
+void ext4_delete_inode(struct inode *inode)
{
handle_t *handle;
int err;
@@ -330,11 +330,11 @@
int final = 0;
if (i_block < 0) {
- ext4_warning (inode->i_sb, "ext4_block_to_path", "block < 0");
+ ext4_warning(inode->i_sb, "ext4_block_to_path", "block < 0");
} else if (i_block < direct_blocks) {
offsets[n++] = i_block;
final = direct_blocks;
- } else if ( (i_block -= direct_blocks) < indirect_blocks) {
+ } else if ((i_block -= direct_blocks) < indirect_blocks) {
offsets[n++] = EXT4_IND_BLOCK;
offsets[n++] = i_block;
final = ptrs;
@@ -400,14 +400,14 @@
*err = 0;
/* i_data is not going away, no lock needed */
- add_chain (chain, NULL, EXT4_I(inode)->i_data + *offsets);
+ add_chain(chain, NULL, EXT4_I(inode)->i_data + *offsets);
if (!p->key)
goto no_block;
while (--depth) {
bh = sb_bread(sb, le32_to_cpu(p->key));
if (!bh)
goto failure;
- add_chain(++p, bh, (__le32*)bh->b_data + *++offsets);
+ add_chain(++p, bh, (__le32 *)bh->b_data + *++offsets);
/* Reader: end */
if (!p->key)
goto no_block;
@@ -443,7 +443,7 @@
static ext4_fsblk_t ext4_find_near(struct inode *inode, Indirect *ind)
{
struct ext4_inode_info *ei = EXT4_I(inode);
- __le32 *start = ind->bh ? (__le32*) ind->bh->b_data : ei->i_data;
+ __le32 *start = ind->bh ? (__le32 *) ind->bh->b_data : ei->i_data;
__le32 *p;
ext4_fsblk_t bg_start;
ext4_fsblk_t last_block;
@@ -486,18 +486,9 @@
static ext4_fsblk_t ext4_find_goal(struct inode *inode, ext4_lblk_t block,
Indirect *partial)
{
- struct ext4_block_alloc_info *block_i;
-
- block_i = EXT4_I(inode)->i_block_alloc_info;
-
/*
- * try the heuristic for sequential allocation,
- * failing that at least try to get decent locality.
+ * XXX need to get goal block from mballoc's data structures
*/
- if (block_i && (block == block_i->last_alloc_logical_block + 1)
- && (block_i->last_alloc_physical_block != 0)) {
- return block_i->last_alloc_physical_block + 1;
- }
return ext4_find_near(inode, partial);
}
@@ -630,7 +621,7 @@
*err = 0;
return ret;
failed_out:
- for (i = 0; i <index; i++)
+ for (i = 0; i < index; i++)
ext4_free_blocks(handle, inode, new_blocks[i], 1, 0);
return ret;
}
@@ -703,7 +694,7 @@
branch[n].p = (__le32 *) bh->b_data + offsets[n];
branch[n].key = cpu_to_le32(new_blocks[n]);
*branch[n].p = branch[n].key;
- if ( n == indirect_blks) {
+ if (n == indirect_blks) {
current_block = new_blocks[n];
/*
* End of chain, update the last new metablock of
@@ -730,7 +721,7 @@
BUFFER_TRACE(branch[i].bh, "call jbd2_journal_forget");
ext4_journal_forget(handle, branch[i].bh);
}
- for (i = 0; i <indirect_blks; i++)
+ for (i = 0; i < indirect_blks; i++)
ext4_free_blocks(handle, inode, new_blocks[i], 1, 0);
ext4_free_blocks(handle, inode, new_blocks[i], num, 0);
@@ -757,10 +748,8 @@
{
int i;
int err = 0;
- struct ext4_block_alloc_info *block_i;
ext4_fsblk_t current_block;
- block_i = EXT4_I(inode)->i_block_alloc_info;
/*
* If we're splicing into a [td]indirect block (as opposed to the
* inode) then we need to get write access to the [td]indirect block
@@ -783,18 +772,7 @@
if (num == 0 && blks > 1) {
current_block = le32_to_cpu(where->key) + 1;
for (i = 1; i < blks; i++)
- *(where->p + i ) = cpu_to_le32(current_block++);
- }
-
- /*
- * update the most recently allocated logical & physical block
- * in i_block_alloc_info, to assist find the proper goal block for next
- * allocation
- */
- if (block_i) {
- block_i->last_alloc_logical_block = block + blks - 1;
- block_i->last_alloc_physical_block =
- le32_to_cpu(where[num].key) + blks - 1;
+ *(where->p + i) = cpu_to_le32(current_block++);
}
/* We are done with atomic stuff, now do the rest of housekeeping */
@@ -914,12 +892,8 @@
goto cleanup;
/*
- * Okay, we need to do block allocation. Lazily initialize the block
- * allocation info here if necessary
+ * Okay, we need to do block allocation.
*/
- if (S_ISREG(inode->i_mode) && (!ei->i_block_alloc_info))
- ext4_init_block_alloc_info(inode);
-
goal = ext4_find_goal(inode, iblock, partial);
/* the number of blocks need to allocate for [d,t]indirect blocks */
@@ -1030,19 +1004,20 @@
BUG_ON(mdb > EXT4_I(inode)->i_reserved_meta_blocks);
mdb_free = EXT4_I(inode)->i_reserved_meta_blocks - mdb;
- /* Account for allocated meta_blocks */
- mdb_free -= EXT4_I(inode)->i_allocated_meta_blocks;
+ if (mdb_free) {
+ /* Account for allocated meta_blocks */
+ mdb_free -= EXT4_I(inode)->i_allocated_meta_blocks;
- /* update fs free blocks counter for truncate case */
- percpu_counter_add(&sbi->s_freeblocks_counter, mdb_free);
+ /* update fs dirty blocks counter */
+ percpu_counter_sub(&sbi->s_dirtyblocks_counter, mdb_free);
+ EXT4_I(inode)->i_allocated_meta_blocks = 0;
+ EXT4_I(inode)->i_reserved_meta_blocks = mdb;
+ }
/* update per-inode reservations */
BUG_ON(used > EXT4_I(inode)->i_reserved_data_blocks);
EXT4_I(inode)->i_reserved_data_blocks -= used;
- BUG_ON(mdb > EXT4_I(inode)->i_reserved_meta_blocks);
- EXT4_I(inode)->i_reserved_meta_blocks = mdb;
- EXT4_I(inode)->i_allocated_meta_blocks = 0;
spin_unlock(&EXT4_I(inode)->i_block_reservation_lock);
}
@@ -1160,8 +1135,8 @@
/* Maximum number of blocks we map for direct IO at once. */
#define DIO_MAX_BLOCKS 4096
-static int ext4_get_block(struct inode *inode, sector_t iblock,
- struct buffer_head *bh_result, int create)
+int ext4_get_block(struct inode *inode, sector_t iblock,
+ struct buffer_head *bh_result, int create)
{
handle_t *handle = ext4_journal_current_handle();
int ret = 0, started = 0;
@@ -1241,7 +1216,7 @@
BUFFER_TRACE(bh, "call get_create_access");
fatal = ext4_journal_get_create_access(handle, bh);
if (!fatal && !buffer_uptodate(bh)) {
- memset(bh->b_data,0,inode->i_sb->s_blocksize);
+ memset(bh->b_data, 0, inode->i_sb->s_blocksize);
set_buffer_uptodate(bh);
}
unlock_buffer(bh);
@@ -1266,7 +1241,7 @@
struct buffer_head *ext4_bread(handle_t *handle, struct inode *inode,
ext4_lblk_t block, int create, int *err)
{
- struct buffer_head * bh;
+ struct buffer_head *bh;
bh = ext4_getblk(handle, inode, block, create, err);
if (!bh)
@@ -1282,13 +1257,13 @@
return NULL;
}
-static int walk_page_buffers( handle_t *handle,
- struct buffer_head *head,
- unsigned from,
- unsigned to,
- int *partial,
- int (*fn)( handle_t *handle,
- struct buffer_head *bh))
+static int walk_page_buffers(handle_t *handle,
+ struct buffer_head *head,
+ unsigned from,
+ unsigned to,
+ int *partial,
+ int (*fn)(handle_t *handle,
+ struct buffer_head *bh))
{
struct buffer_head *bh;
unsigned block_start, block_end;
@@ -1296,9 +1271,9 @@
int err, ret = 0;
struct buffer_head *next;
- for ( bh = head, block_start = 0;
- ret == 0 && (bh != head || !block_start);
- block_start = block_end, bh = next)
+ for (bh = head, block_start = 0;
+ ret == 0 && (bh != head || !block_start);
+ block_start = block_end, bh = next)
{
next = bh->b_this_page;
block_end = block_start + blocksize;
@@ -1351,23 +1326,23 @@
loff_t pos, unsigned len, unsigned flags,
struct page **pagep, void **fsdata)
{
- struct inode *inode = mapping->host;
+ struct inode *inode = mapping->host;
int ret, needed_blocks = ext4_writepage_trans_blocks(inode);
handle_t *handle;
int retries = 0;
- struct page *page;
+ struct page *page;
pgoff_t index;
- unsigned from, to;
+ unsigned from, to;
index = pos >> PAGE_CACHE_SHIFT;
- from = pos & (PAGE_CACHE_SIZE - 1);
- to = from + len;
+ from = pos & (PAGE_CACHE_SIZE - 1);
+ to = from + len;
retry:
- handle = ext4_journal_start(inode, needed_blocks);
- if (IS_ERR(handle)) {
- ret = PTR_ERR(handle);
- goto out;
+ handle = ext4_journal_start(inode, needed_blocks);
+ if (IS_ERR(handle)) {
+ ret = PTR_ERR(handle);
+ goto out;
}
page = __grab_cache_page(mapping, index);
@@ -1387,9 +1362,16 @@
}
if (ret) {
- unlock_page(page);
+ unlock_page(page);
ext4_journal_stop(handle);
- page_cache_release(page);
+ page_cache_release(page);
+ /*
+ * block_write_begin may have instantiated a few blocks
+ * outside i_size. Trim these off again. Don't need
+ * i_size_read because we hold i_mutex.
+ */
+ if (pos + len > inode->i_size)
+ vmtruncate(inode, inode->i_size);
}
if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
@@ -1426,16 +1408,18 @@
ret = ext4_jbd2_file_inode(handle, inode);
if (ret == 0) {
- /*
- * generic_write_end() will run mark_inode_dirty() if i_size
- * changes. So let's piggyback the i_disksize mark_inode_dirty
- * into that.
- */
loff_t new_i_size;
new_i_size = pos + copied;
- if (new_i_size > EXT4_I(inode)->i_disksize)
- EXT4_I(inode)->i_disksize = new_i_size;
+ if (new_i_size > EXT4_I(inode)->i_disksize) {
+ ext4_update_i_disksize(inode, new_i_size);
+ /* We need to mark inode dirty even if
+ * new_i_size is less that inode->i_size
+ * bu greater than i_disksize.(hint delalloc)
+ */
+ ext4_mark_inode_dirty(handle, inode);
+ }
+
ret2 = generic_write_end(file, mapping, pos, len, copied,
page, fsdata);
copied = ret2;
@@ -1460,8 +1444,14 @@
loff_t new_i_size;
new_i_size = pos + copied;
- if (new_i_size > EXT4_I(inode)->i_disksize)
- EXT4_I(inode)->i_disksize = new_i_size;
+ if (new_i_size > EXT4_I(inode)->i_disksize) {
+ ext4_update_i_disksize(inode, new_i_size);
+ /* We need to mark inode dirty even if
+ * new_i_size is less that inode->i_size
+ * bu greater than i_disksize.(hint delalloc)
+ */
+ ext4_mark_inode_dirty(handle, inode);
+ }
ret2 = generic_write_end(file, mapping, pos, len, copied,
page, fsdata);
@@ -1486,6 +1476,7 @@
int ret = 0, ret2;
int partial = 0;
unsigned from, to;
+ loff_t new_i_size;
from = pos & (PAGE_CACHE_SIZE - 1);
to = from + len;
@@ -1500,11 +1491,12 @@
to, &partial, write_end_fn);
if (!partial)
SetPageUptodate(page);
- if (pos+copied > inode->i_size)
+ new_i_size = pos + copied;
+ if (new_i_size > inode->i_size)
i_size_write(inode, pos+copied);
EXT4_I(inode)->i_state |= EXT4_STATE_JDATA;
- if (inode->i_size > EXT4_I(inode)->i_disksize) {
- EXT4_I(inode)->i_disksize = inode->i_size;
+ if (new_i_size > EXT4_I(inode)->i_disksize) {
+ ext4_update_i_disksize(inode, new_i_size);
ret2 = ext4_mark_inode_dirty(handle, inode);
if (!ret)
ret = ret2;
@@ -1521,6 +1513,7 @@
static int ext4_da_reserve_space(struct inode *inode, int nrblocks)
{
+ int retries = 0;
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
unsigned long md_needed, mdblocks, total = 0;
@@ -1529,6 +1522,7 @@
* in order to allocate nrblocks
* worse case is one extent per block
*/
+repeat:
spin_lock(&EXT4_I(inode)->i_block_reservation_lock);
total = EXT4_I(inode)->i_reserved_data_blocks + nrblocks;
mdblocks = ext4_calc_metadata_amount(inode, total);
@@ -1537,13 +1531,14 @@
md_needed = mdblocks - EXT4_I(inode)->i_reserved_meta_blocks;
total = md_needed + nrblocks;
- if (ext4_has_free_blocks(sbi, total) < total) {
+ if (ext4_claim_free_blocks(sbi, total)) {
spin_unlock(&EXT4_I(inode)->i_block_reservation_lock);
+ if (ext4_should_retry_alloc(inode->i_sb, &retries)) {
+ yield();
+ goto repeat;
+ }
return -ENOSPC;
}
- /* reduce fs free blocks counter */
- percpu_counter_sub(&sbi->s_freeblocks_counter, total);
-
EXT4_I(inode)->i_reserved_data_blocks += nrblocks;
EXT4_I(inode)->i_reserved_meta_blocks = mdblocks;
@@ -1585,8 +1580,8 @@
release = to_free + mdb_free;
- /* update fs free blocks counter for truncate case */
- percpu_counter_add(&sbi->s_freeblocks_counter, release);
+ /* update fs dirty blocks counter for truncate case */
+ percpu_counter_sub(&sbi->s_dirtyblocks_counter, release);
/* update per-inode reservations */
BUG_ON(to_free > EXT4_I(inode)->i_reserved_data_blocks);
@@ -1630,6 +1625,7 @@
struct writeback_control *wbc;
int io_done;
long pages_written;
+ int retval;
};
/*
@@ -1783,6 +1779,57 @@
unmap_underlying_metadata(bdev, bh->b_blocknr + i);
}
+static void ext4_da_block_invalidatepages(struct mpage_da_data *mpd,
+ sector_t logical, long blk_cnt)
+{
+ int nr_pages, i;
+ pgoff_t index, end;
+ struct pagevec pvec;
+ struct inode *inode = mpd->inode;
+ struct address_space *mapping = inode->i_mapping;
+
+ index = logical >> (PAGE_CACHE_SHIFT - inode->i_blkbits);
+ end = (logical + blk_cnt - 1) >>
+ (PAGE_CACHE_SHIFT - inode->i_blkbits);
+ while (index <= end) {
+ nr_pages = pagevec_lookup(&pvec, mapping, index, PAGEVEC_SIZE);
+ if (nr_pages == 0)
+ break;
+ for (i = 0; i < nr_pages; i++) {
+ struct page *page = pvec.pages[i];
+ index = page->index;
+ if (index > end)
+ break;
+ index++;
+
+ BUG_ON(!PageLocked(page));
+ BUG_ON(PageWriteback(page));
+ block_invalidatepage(page, 0);
+ ClearPageUptodate(page);
+ unlock_page(page);
+ }
+ }
+ return;
+}
+
+static void ext4_print_free_blocks(struct inode *inode)
+{
+ struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
+ printk(KERN_EMERG "Total free blocks count %lld\n",
+ ext4_count_free_blocks(inode->i_sb));
+ printk(KERN_EMERG "Free/Dirty block details\n");
+ printk(KERN_EMERG "free_blocks=%lld\n",
+ percpu_counter_sum(&sbi->s_freeblocks_counter));
+ printk(KERN_EMERG "dirty_blocks=%lld\n",
+ percpu_counter_sum(&sbi->s_dirtyblocks_counter));
+ printk(KERN_EMERG "Block reservation details\n");
+ printk(KERN_EMERG "i_reserved_data_blocks=%lu\n",
+ EXT4_I(inode)->i_reserved_data_blocks);
+ printk(KERN_EMERG "i_reserved_meta_blocks=%lu\n",
+ EXT4_I(inode)->i_reserved_meta_blocks);
+ return;
+}
+
/*
* mpage_da_map_blocks - go through given space
*
@@ -1792,32 +1839,69 @@
* The function skips space we know is already mapped to disk blocks.
*
*/
-static void mpage_da_map_blocks(struct mpage_da_data *mpd)
+static int mpage_da_map_blocks(struct mpage_da_data *mpd)
{
int err = 0;
- struct buffer_head *lbh = &mpd->lbh;
- sector_t next = lbh->b_blocknr;
struct buffer_head new;
+ struct buffer_head *lbh = &mpd->lbh;
+ sector_t next;
/*
* We consider only non-mapped and non-allocated blocks
*/
if (buffer_mapped(lbh) && !buffer_delay(lbh))
- return;
-
+ return 0;
new.b_state = lbh->b_state;
new.b_blocknr = 0;
new.b_size = lbh->b_size;
-
+ next = lbh->b_blocknr;
/*
* If we didn't accumulate anything
* to write simply return
*/
if (!new.b_size)
- return;
+ return 0;
err = mpd->get_block(mpd->inode, next, &new, 1);
- if (err)
- return;
+ if (err) {
+
+ /* If get block returns with error
+ * we simply return. Later writepage
+ * will redirty the page and writepages
+ * will find the dirty page again
+ */
+ if (err == -EAGAIN)
+ return 0;
+
+ if (err == -ENOSPC &&
+ ext4_count_free_blocks(mpd->inode->i_sb)) {
+ mpd->retval = err;
+ return 0;
+ }
+
+ /*
+ * get block failure will cause us
+ * to loop in writepages. Because
+ * a_ops->writepage won't be able to
+ * make progress. The page will be redirtied
+ * by writepage and writepages will again
+ * try to write the same.
+ */
+ printk(KERN_EMERG "%s block allocation failed for inode %lu "
+ "at logical offset %llu with max blocks "
+ "%zd with error %d\n",
+ __func__, mpd->inode->i_ino,
+ (unsigned long long)next,
+ lbh->b_size >> mpd->inode->i_blkbits, err);
+ printk(KERN_EMERG "This should not happen.!! "
+ "Data will be lost\n");
+ if (err == -ENOSPC) {
+ ext4_print_free_blocks(mpd->inode);
+ }
+ /* invlaidate all the pages */
+ ext4_da_block_invalidatepages(mpd, next,
+ lbh->b_size >> mpd->inode->i_blkbits);
+ return err;
+ }
BUG_ON(new.b_size == 0);
if (buffer_new(&new))
@@ -1830,7 +1914,7 @@
if (buffer_delay(lbh) || buffer_unwritten(lbh))
mpage_put_bnr_to_bhs(mpd, next, &new);
- return;
+ return 0;
}
#define BH_FLAGS ((1 << BH_Uptodate) | (1 << BH_Mapped) | \
@@ -1899,8 +1983,8 @@
* We couldn't merge the block to our extent, so we
* need to flush current extent and start new one
*/
- mpage_da_map_blocks(mpd);
- mpage_da_submit_io(mpd);
+ if (mpage_da_map_blocks(mpd) == 0)
+ mpage_da_submit_io(mpd);
mpd->io_done = 1;
return;
}
@@ -1942,8 +2026,8 @@
* and start IO on them using writepage()
*/
if (mpd->next_page != mpd->first_page) {
- mpage_da_map_blocks(mpd);
- mpage_da_submit_io(mpd);
+ if (mpage_da_map_blocks(mpd) == 0)
+ mpage_da_submit_io(mpd);
/*
* skip rest of the page in the page_vec
*/
@@ -2018,39 +2102,36 @@
*/
static int mpage_da_writepages(struct address_space *mapping,
struct writeback_control *wbc,
- get_block_t get_block)
+ struct mpage_da_data *mpd)
{
- struct mpage_da_data mpd;
long to_write;
int ret;
- if (!get_block)
+ if (!mpd->get_block)
return generic_writepages(mapping, wbc);
- mpd.wbc = wbc;
- mpd.inode = mapping->host;
- mpd.lbh.b_size = 0;
- mpd.lbh.b_state = 0;
- mpd.lbh.b_blocknr = 0;
- mpd.first_page = 0;
- mpd.next_page = 0;
- mpd.get_block = get_block;
- mpd.io_done = 0;
- mpd.pages_written = 0;
+ mpd->lbh.b_size = 0;
+ mpd->lbh.b_state = 0;
+ mpd->lbh.b_blocknr = 0;
+ mpd->first_page = 0;
+ mpd->next_page = 0;
+ mpd->io_done = 0;
+ mpd->pages_written = 0;
+ mpd->retval = 0;
to_write = wbc->nr_to_write;
- ret = write_cache_pages(mapping, wbc, __mpage_da_writepage, &mpd);
+ ret = write_cache_pages(mapping, wbc, __mpage_da_writepage, mpd);
/*
* Handle last extent of pages
*/
- if (!mpd.io_done && mpd.next_page != mpd.first_page) {
- mpage_da_map_blocks(&mpd);
- mpage_da_submit_io(&mpd);
+ if (!mpd->io_done && mpd->next_page != mpd->first_page) {
+ if (mpage_da_map_blocks(mpd) == 0)
+ mpage_da_submit_io(mpd);
}
- wbc->nr_to_write = to_write - mpd.pages_written;
+ wbc->nr_to_write = to_write - mpd->pages_written;
return ret;
}
@@ -2103,18 +2184,24 @@
handle_t *handle = NULL;
handle = ext4_journal_current_handle();
- if (!handle) {
- ret = ext4_get_blocks_wrap(handle, inode, iblock, max_blocks,
- bh_result, 0, 0, 0);
- BUG_ON(!ret);
- } else {
- ret = ext4_get_blocks_wrap(handle, inode, iblock, max_blocks,
- bh_result, create, 0, EXT4_DELALLOC_RSVED);
- }
-
+ BUG_ON(!handle);
+ ret = ext4_get_blocks_wrap(handle, inode, iblock, max_blocks,
+ bh_result, create, 0, EXT4_DELALLOC_RSVED);
if (ret > 0) {
+
bh_result->b_size = (ret << inode->i_blkbits);
+ if (ext4_should_order_data(inode)) {
+ int retval;
+ retval = ext4_jbd2_file_inode(handle, inode);
+ if (retval)
+ /*
+ * Failed to add inode for ordered
+ * mode. Don't update file size
+ */
+ return retval;
+ }
+
/*
* Update on-disk size along with block allocation
* we don't use 'extend_disksize' as size may change
@@ -2124,18 +2211,9 @@
if (disksize > i_size_read(inode))
disksize = i_size_read(inode);
if (disksize > EXT4_I(inode)->i_disksize) {
- /*
- * XXX: replace with spinlock if seen contended -bzzz
- */
- down_write(&EXT4_I(inode)->i_data_sem);
- if (disksize > EXT4_I(inode)->i_disksize)
- EXT4_I(inode)->i_disksize = disksize;
- up_write(&EXT4_I(inode)->i_data_sem);
-
- if (EXT4_I(inode)->i_disksize == disksize) {
- ret = ext4_mark_inode_dirty(handle, inode);
- return ret;
- }
+ ext4_update_i_disksize(inode, disksize);
+ ret = ext4_mark_inode_dirty(handle, inode);
+ return ret;
}
ret = 0;
}
@@ -2284,6 +2362,7 @@
{
handle_t *handle = NULL;
loff_t range_start = 0;
+ struct mpage_da_data mpd;
struct inode *inode = mapping->host;
int needed_blocks, ret = 0, nr_to_writebump = 0;
long to_write, pages_skipped = 0;
@@ -2317,6 +2396,9 @@
range_start = wbc->range_start;
pages_skipped = wbc->pages_skipped;
+ mpd.wbc = wbc;
+ mpd.inode = mapping->host;
+
restart_loop:
to_write = wbc->nr_to_write;
while (!ret && to_write > 0) {
@@ -2340,23 +2422,17 @@
dump_stack();
goto out_writepages;
}
- if (ext4_should_order_data(inode)) {
- /*
- * With ordered mode we need to add
- * the inode to the journal handl
- * when we do block allocation.
- */
- ret = ext4_jbd2_file_inode(handle, inode);
- if (ret) {
- ext4_journal_stop(handle);
- goto out_writepages;
- }
- }
-
to_write -= wbc->nr_to_write;
- ret = mpage_da_writepages(mapping, wbc,
- ext4_da_get_block_write);
+
+ mpd.get_block = ext4_da_get_block_write;
+ ret = mpage_da_writepages(mapping, wbc, &mpd);
+
ext4_journal_stop(handle);
+
+ if (mpd.retval == -ENOSPC)
+ jbd2_journal_force_commit_nested(sbi->s_journal);
+
+ /* reset the retry count */
if (ret == MPAGE_DA_EXTENT_TAIL) {
/*
* got one extent now try with
@@ -2391,6 +2467,33 @@
return ret;
}
+#define FALL_BACK_TO_NONDELALLOC 1
+static int ext4_nonda_switch(struct super_block *sb)
+{
+ s64 free_blocks, dirty_blocks;
+ struct ext4_sb_info *sbi = EXT4_SB(sb);
+
+ /*
+ * switch to non delalloc mode if we are running low
+ * on free block. The free block accounting via percpu
+ * counters can get slightly wrong with FBC_BATCH getting
+ * accumulated on each CPU without updating global counters
+ * Delalloc need an accurate free block accounting. So switch
+ * to non delalloc when we are near to error range.
+ */
+ free_blocks = percpu_counter_read_positive(&sbi->s_freeblocks_counter);
+ dirty_blocks = percpu_counter_read_positive(&sbi->s_dirtyblocks_counter);
+ if (2 * free_blocks < 3 * dirty_blocks ||
+ free_blocks < (dirty_blocks + EXT4_FREEBLOCKS_WATERMARK)) {
+ /*
+ * free block count is less that 150% of dirty blocks
+ * or free blocks is less that watermark
+ */
+ return 1;
+ }
+ return 0;
+}
+
static int ext4_da_write_begin(struct file *file, struct address_space *mapping,
loff_t pos, unsigned len, unsigned flags,
struct page **pagep, void **fsdata)
@@ -2406,6 +2509,12 @@
from = pos & (PAGE_CACHE_SIZE - 1);
to = from + len;
+ if (ext4_nonda_switch(inode->i_sb)) {
+ *fsdata = (void *)FALL_BACK_TO_NONDELALLOC;
+ return ext4_write_begin(file, mapping, pos,
+ len, flags, pagep, fsdata);
+ }
+ *fsdata = (void *)0;
retry:
/*
* With delayed allocation, we don't log the i_disksize update
@@ -2433,6 +2542,13 @@
unlock_page(page);
ext4_journal_stop(handle);
page_cache_release(page);
+ /*
+ * block_write_begin may have instantiated a few blocks
+ * outside i_size. Trim these off again. Don't need
+ * i_size_read because we hold i_mutex.
+ */
+ if (pos + len > inode->i_size)
+ vmtruncate(inode, inode->i_size);
}
if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
@@ -2456,7 +2572,7 @@
bh = page_buffers(page);
idx = offset >> inode->i_blkbits;
- for (i=0; i < idx; i++)
+ for (i = 0; i < idx; i++)
bh = bh->b_this_page;
if (!buffer_mapped(bh) || (buffer_delay(bh)))
@@ -2474,9 +2590,22 @@
handle_t *handle = ext4_journal_current_handle();
loff_t new_i_size;
unsigned long start, end;
+ int write_mode = (int)(unsigned long)fsdata;
+
+ if (write_mode == FALL_BACK_TO_NONDELALLOC) {
+ if (ext4_should_order_data(inode)) {
+ return ext4_ordered_write_end(file, mapping, pos,
+ len, copied, page, fsdata);
+ } else if (ext4_should_writeback_data(inode)) {
+ return ext4_writeback_write_end(file, mapping, pos,
+ len, copied, page, fsdata);
+ } else {
+ BUG();
+ }
+ }
start = pos & (PAGE_CACHE_SIZE - 1);
- end = start + copied -1;
+ end = start + copied - 1;
/*
* generic_write_end() will run mark_inode_dirty() if i_size
@@ -2500,6 +2629,11 @@
EXT4_I(inode)->i_disksize = new_i_size;
}
up_write(&EXT4_I(inode)->i_data_sem);
+ /* We need to mark inode dirty even if
+ * new_i_size is less that inode->i_size
+ * bu greater than i_disksize.(hint delalloc)
+ */
+ ext4_mark_inode_dirty(handle, inode);
}
}
ret2 = generic_write_end(file, mapping, pos, len, copied,
@@ -2591,7 +2725,7 @@
return 0;
}
- return generic_block_bmap(mapping,block,ext4_get_block);
+ return generic_block_bmap(mapping, block, ext4_get_block);
}
static int bget_one(handle_t *handle, struct buffer_head *bh)
@@ -3197,7 +3331,7 @@
if (!partial->key && *partial->p)
/* Writer: end */
goto no_top;
- for (p=partial; p>chain && all_zeroes((__le32*)p->bh->b_data,p->p); p--)
+ for (p = partial; (p > chain) && all_zeroes((__le32 *) p->bh->b_data, p->p); p--)
;
/*
* OK, we've found the last block that must survive. The rest of our
@@ -3216,7 +3350,7 @@
}
/* Writer: end */
- while(partial > p) {
+ while (partial > p) {
brelse(partial->bh);
partial--;
}
@@ -3408,9 +3542,9 @@
/* This zaps the entire block. Bottom up. */
BUFFER_TRACE(bh, "free child branches");
ext4_free_branches(handle, inode, bh,
- (__le32*)bh->b_data,
- (__le32*)bh->b_data + addr_per_block,
- depth);
+ (__le32 *) bh->b_data,
+ (__le32 *) bh->b_data + addr_per_block,
+ depth);
/*
* We've probably journalled the indirect block several
@@ -3578,7 +3712,7 @@
*/
down_write(&ei->i_data_sem);
- ext4_discard_reservation(inode);
+ ext4_discard_preallocations(inode);
/*
* The orphan list entry will now protect us from any crash which
@@ -3673,41 +3807,6 @@
ext4_journal_stop(handle);
}
-static ext4_fsblk_t ext4_get_inode_block(struct super_block *sb,
- unsigned long ino, struct ext4_iloc *iloc)
-{
- ext4_group_t block_group;
- unsigned long offset;
- ext4_fsblk_t block;
- struct ext4_group_desc *gdp;
-
- if (!ext4_valid_inum(sb, ino)) {
- /*
- * This error is already checked for in namei.c unless we are
- * looking at an NFS filehandle, in which case no error
- * report is needed
- */
- return 0;
- }
-
- block_group = (ino - 1) / EXT4_INODES_PER_GROUP(sb);
- gdp = ext4_get_group_desc(sb, block_group, NULL);
- if (!gdp)
- return 0;
-
- /*
- * Figure out the offset within the block group inode table
- */
- offset = ((ino - 1) % EXT4_INODES_PER_GROUP(sb)) *
- EXT4_INODE_SIZE(sb);
- block = ext4_inode_table(sb, gdp) +
- (offset >> EXT4_BLOCK_SIZE_BITS(sb));
-
- iloc->block_group = block_group;
- iloc->offset = offset & (EXT4_BLOCK_SIZE(sb) - 1);
- return block;
-}
-
/*
* ext4_get_inode_loc returns with an extra refcount against the inode's
* underlying buffer_head on success. If 'in_mem' is true, we have all
@@ -3717,19 +3816,35 @@
static int __ext4_get_inode_loc(struct inode *inode,
struct ext4_iloc *iloc, int in_mem)
{
- ext4_fsblk_t block;
- struct buffer_head *bh;
+ struct ext4_group_desc *gdp;
+ struct buffer_head *bh;
+ struct super_block *sb = inode->i_sb;
+ ext4_fsblk_t block;
+ int inodes_per_block, inode_offset;
- block = ext4_get_inode_block(inode->i_sb, inode->i_ino, iloc);
- if (!block)
+ iloc->bh = 0;
+ if (!ext4_valid_inum(sb, inode->i_ino))
return -EIO;
- bh = sb_getblk(inode->i_sb, block);
+ iloc->block_group = (inode->i_ino - 1) / EXT4_INODES_PER_GROUP(sb);
+ gdp = ext4_get_group_desc(sb, iloc->block_group, NULL);
+ if (!gdp)
+ return -EIO;
+
+ /*
+ * Figure out the offset within the block group inode table
+ */
+ inodes_per_block = (EXT4_BLOCK_SIZE(sb) / EXT4_INODE_SIZE(sb));
+ inode_offset = ((inode->i_ino - 1) %
+ EXT4_INODES_PER_GROUP(sb));
+ block = ext4_inode_table(sb, gdp) + (inode_offset / inodes_per_block);
+ iloc->offset = (inode_offset % inodes_per_block) * EXT4_INODE_SIZE(sb);
+
+ bh = sb_getblk(sb, block);
if (!bh) {
- ext4_error (inode->i_sb, "ext4_get_inode_loc",
- "unable to read inode block - "
- "inode=%lu, block=%llu",
- inode->i_ino, block);
+ ext4_error(sb, "ext4_get_inode_loc", "unable to read "
+ "inode block - inode=%lu, block=%llu",
+ inode->i_ino, block);
return -EIO;
}
if (!buffer_uptodate(bh)) {
@@ -3757,28 +3872,12 @@
*/
if (in_mem) {
struct buffer_head *bitmap_bh;
- struct ext4_group_desc *desc;
- int inodes_per_buffer;
- int inode_offset, i;
- ext4_group_t block_group;
- int start;
+ int i, start;
- block_group = (inode->i_ino - 1) /
- EXT4_INODES_PER_GROUP(inode->i_sb);
- inodes_per_buffer = bh->b_size /
- EXT4_INODE_SIZE(inode->i_sb);
- inode_offset = ((inode->i_ino - 1) %
- EXT4_INODES_PER_GROUP(inode->i_sb));
- start = inode_offset & ~(inodes_per_buffer - 1);
+ start = inode_offset & ~(inodes_per_block - 1);
/* Is the inode bitmap in cache? */
- desc = ext4_get_group_desc(inode->i_sb,
- block_group, NULL);
- if (!desc)
- goto make_io;
-
- bitmap_bh = sb_getblk(inode->i_sb,
- ext4_inode_bitmap(inode->i_sb, desc));
+ bitmap_bh = sb_getblk(sb, ext4_inode_bitmap(sb, gdp));
if (!bitmap_bh)
goto make_io;
@@ -3791,14 +3890,14 @@
brelse(bitmap_bh);
goto make_io;
}
- for (i = start; i < start + inodes_per_buffer; i++) {
+ for (i = start; i < start + inodes_per_block; i++) {
if (i == inode_offset)
continue;
if (ext4_test_bit(i, bitmap_bh->b_data))
break;
}
brelse(bitmap_bh);
- if (i == start + inodes_per_buffer) {
+ if (i == start + inodes_per_block) {
/* all other inodes are free, so skip I/O */
memset(bh->b_data, 0, bh->b_size);
set_buffer_uptodate(bh);
@@ -3809,6 +3908,36 @@
make_io:
/*
+ * If we need to do any I/O, try to pre-readahead extra
+ * blocks from the inode table.
+ */
+ if (EXT4_SB(sb)->s_inode_readahead_blks) {
+ ext4_fsblk_t b, end, table;
+ unsigned num;
+
+ table = ext4_inode_table(sb, gdp);
+ /* Make sure s_inode_readahead_blks is a power of 2 */
+ while (EXT4_SB(sb)->s_inode_readahead_blks &
+ (EXT4_SB(sb)->s_inode_readahead_blks-1))
+ EXT4_SB(sb)->s_inode_readahead_blks =
+ (EXT4_SB(sb)->s_inode_readahead_blks &
+ (EXT4_SB(sb)->s_inode_readahead_blks-1));
+ b = block & ~(EXT4_SB(sb)->s_inode_readahead_blks-1);
+ if (table > b)
+ b = table;
+ end = b + EXT4_SB(sb)->s_inode_readahead_blks;
+ num = EXT4_INODES_PER_GROUP(sb);
+ if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
+ EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
+ num -= le16_to_cpu(gdp->bg_itable_unused);
+ table += num / inodes_per_block;
+ if (end > table)
+ end = table;
+ while (b <= end)
+ sb_breadahead(sb, b++);
+ }
+
+ /*
* There are other valid inodes in the buffer, this inode
* has in-inode xattrs, or we don't have this inode in memory.
* Read the block from disk.
@@ -3818,10 +3947,9 @@
submit_bh(READ_META, bh);
wait_on_buffer(bh);
if (!buffer_uptodate(bh)) {
- ext4_error(inode->i_sb, "ext4_get_inode_loc",
- "unable to read inode block - "
- "inode=%lu, block=%llu",
- inode->i_ino, block);
+ ext4_error(sb, __func__,
+ "unable to read inode block - inode=%lu, "
+ "block=%llu", inode->i_ino, block);
brelse(bh);
return -EIO;
}
@@ -3913,11 +4041,10 @@
return inode;
ei = EXT4_I(inode);
-#ifdef CONFIG_EXT4DEV_FS_POSIX_ACL
+#ifdef CONFIG_EXT4_FS_POSIX_ACL
ei->i_acl = EXT4_ACL_NOT_CACHED;
ei->i_default_acl = EXT4_ACL_NOT_CACHED;
#endif
- ei->i_block_alloc_info = NULL;
ret = __ext4_get_inode_loc(inode, &iloc, 0);
if (ret < 0)
@@ -3927,7 +4054,7 @@
inode->i_mode = le16_to_cpu(raw_inode->i_mode);
inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low);
inode->i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low);
- if(!(test_opt (inode->i_sb, NO_UID32))) {
+ if (!(test_opt(inode->i_sb, NO_UID32))) {
inode->i_uid |= le16_to_cpu(raw_inode->i_uid_high) << 16;
inode->i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16;
}
@@ -3945,7 +4072,7 @@
if (inode->i_mode == 0 ||
!(EXT4_SB(inode->i_sb)->s_mount_state & EXT4_ORPHAN_FS)) {
/* this inode is deleted */
- brelse (bh);
+ brelse(bh);
ret = -ESTALE;
goto bad_inode;
}
@@ -3978,7 +4105,7 @@
ei->i_extra_isize = le16_to_cpu(raw_inode->i_extra_isize);
if (EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize >
EXT4_INODE_SIZE(inode->i_sb)) {
- brelse (bh);
+ brelse(bh);
ret = -EIO;
goto bad_inode;
}
@@ -4031,7 +4158,7 @@
init_special_inode(inode, inode->i_mode,
new_decode_dev(le32_to_cpu(raw_inode->i_block[1])));
}
- brelse (iloc.bh);
+ brelse(iloc.bh);
ext4_set_inode_flags(inode);
unlock_new_inode(inode);
return inode;
@@ -4113,14 +4240,14 @@
ext4_get_inode_flags(ei);
raw_inode->i_mode = cpu_to_le16(inode->i_mode);
- if(!(test_opt(inode->i_sb, NO_UID32))) {
+ if (!(test_opt(inode->i_sb, NO_UID32))) {
raw_inode->i_uid_low = cpu_to_le16(low_16_bits(inode->i_uid));
raw_inode->i_gid_low = cpu_to_le16(low_16_bits(inode->i_gid));
/*
* Fix up interoperability with old kernels. Otherwise, old inodes get
* re-used with the upper 16 bits of the uid/gid intact
*/
- if(!ei->i_dtime) {
+ if (!ei->i_dtime) {
raw_inode->i_uid_high =
cpu_to_le16(high_16_bits(inode->i_uid));
raw_inode->i_gid_high =
@@ -4208,7 +4335,7 @@
ei->i_state &= ~EXT4_STATE_NEW;
out_brelse:
- brelse (bh);
+ brelse(bh);
ext4_std_error(inode->i_sb, err);
return err;
}
@@ -4811,6 +4938,7 @@
loff_t size;
unsigned long len;
int ret = -EINVAL;
+ void *fsdata;
struct file *file = vma->vm_file;
struct inode *inode = file->f_path.dentry->d_inode;
struct address_space *mapping = inode->i_mapping;
@@ -4849,11 +4977,11 @@
* on the same page though
*/
ret = mapping->a_ops->write_begin(file, mapping, page_offset(page),
- len, AOP_FLAG_UNINTERRUPTIBLE, &page, NULL);
+ len, AOP_FLAG_UNINTERRUPTIBLE, &page, &fsdata);
if (ret < 0)
goto out_unlock;
ret = mapping->a_ops->write_end(file, mapping, page_offset(page),
- len, len, page, NULL);
+ len, len, page, fsdata);
if (ret < 0)
goto out_unlock;
ret = 0;
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index 7a6c2f1..dc99b47 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -23,9 +23,8 @@
struct inode *inode = filp->f_dentry->d_inode;
struct ext4_inode_info *ei = EXT4_I(inode);
unsigned int flags;
- unsigned short rsv_window_size;
- ext4_debug ("cmd = %u, arg = %lu\n", cmd, arg);
+ ext4_debug("cmd = %u, arg = %lu\n", cmd, arg);
switch (cmd) {
case EXT4_IOC_GETFLAGS:
@@ -34,7 +33,7 @@
return put_user(flags, (int __user *) arg);
case EXT4_IOC_SETFLAGS: {
handle_t *handle = NULL;
- int err;
+ int err, migrate = 0;
struct ext4_iloc iloc;
unsigned int oldflags;
unsigned int jflag;
@@ -82,6 +81,17 @@
if (!capable(CAP_SYS_RESOURCE))
goto flags_out;
}
+ if (oldflags & EXT4_EXTENTS_FL) {
+ /* We don't support clearning extent flags */
+ if (!(flags & EXT4_EXTENTS_FL)) {
+ err = -EOPNOTSUPP;
+ goto flags_out;
+ }
+ } else if (flags & EXT4_EXTENTS_FL) {
+ /* migrate the file */
+ migrate = 1;
+ flags &= ~EXT4_EXTENTS_FL;
+ }
handle = ext4_journal_start(inode, 1);
if (IS_ERR(handle)) {
@@ -109,6 +119,10 @@
if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL))
err = ext4_change_inode_journal_flag(inode, jflag);
+ if (err)
+ goto flags_out;
+ if (migrate)
+ err = ext4_ext_migrate(inode);
flags_out:
mutex_unlock(&inode->i_mutex);
mnt_drop_write(filp->f_path.mnt);
@@ -175,53 +189,10 @@
return ret;
}
#endif
- case EXT4_IOC_GETRSVSZ:
- if (test_opt(inode->i_sb, RESERVATION)
- && S_ISREG(inode->i_mode)
- && ei->i_block_alloc_info) {
- rsv_window_size = ei->i_block_alloc_info->rsv_window_node.rsv_goal_size;
- return put_user(rsv_window_size, (int __user *)arg);
- }
- return -ENOTTY;
- case EXT4_IOC_SETRSVSZ: {
- int err;
-
- if (!test_opt(inode->i_sb, RESERVATION) ||!S_ISREG(inode->i_mode))
- return -ENOTTY;
-
- if (!is_owner_or_cap(inode))
- return -EACCES;
-
- if (get_user(rsv_window_size, (int __user *)arg))
- return -EFAULT;
-
- err = mnt_want_write(filp->f_path.mnt);
- if (err)
- return err;
-
- if (rsv_window_size > EXT4_MAX_RESERVE_BLOCKS)
- rsv_window_size = EXT4_MAX_RESERVE_BLOCKS;
-
- /*
- * need to allocate reservation structure for this inode
- * before set the window size
- */
- down_write(&ei->i_data_sem);
- if (!ei->i_block_alloc_info)
- ext4_init_block_alloc_info(inode);
-
- if (ei->i_block_alloc_info){
- struct ext4_reserve_window_node *rsv = &ei->i_block_alloc_info->rsv_window_node;
- rsv->rsv_goal_size = rsv_window_size;
- }
- up_write(&ei->i_data_sem);
- mnt_drop_write(filp->f_path.mnt);
- return 0;
- }
case EXT4_IOC_GROUP_EXTEND: {
ext4_fsblk_t n_blocks_count;
struct super_block *sb = inode->i_sb;
- int err;
+ int err, err2;
if (!capable(CAP_SYS_RESOURCE))
return -EPERM;
@@ -235,8 +206,10 @@
err = ext4_group_extend(sb, EXT4_SB(sb)->s_es, n_blocks_count);
jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal);
- jbd2_journal_flush(EXT4_SB(sb)->s_journal);
+ err2 = jbd2_journal_flush(EXT4_SB(sb)->s_journal);
jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
+ if (err == 0)
+ err = err2;
mnt_drop_write(filp->f_path.mnt);
return err;
@@ -244,7 +217,7 @@
case EXT4_IOC_GROUP_ADD: {
struct ext4_new_group_data input;
struct super_block *sb = inode->i_sb;
- int err;
+ int err, err2;
if (!capable(CAP_SYS_RESOURCE))
return -EPERM;
@@ -259,15 +232,36 @@
err = ext4_group_add(sb, &input);
jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal);
- jbd2_journal_flush(EXT4_SB(sb)->s_journal);
+ err2 = jbd2_journal_flush(EXT4_SB(sb)->s_journal);
jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
+ if (err == 0)
+ err = err2;
mnt_drop_write(filp->f_path.mnt);
return err;
}
case EXT4_IOC_MIGRATE:
- return ext4_ext_migrate(inode, filp, cmd, arg);
+ {
+ int err;
+ if (!is_owner_or_cap(inode))
+ return -EACCES;
+
+ err = mnt_want_write(filp->f_path.mnt);
+ if (err)
+ return err;
+ /*
+ * inode_mutex prevent write and truncate on the file.
+ * Read still goes through. We take i_data_sem in
+ * ext4_ext_swap_inode_data before we switch the
+ * inode format to prevent read.
+ */
+ mutex_lock(&(inode->i_mutex));
+ err = ext4_ext_migrate(inode);
+ mutex_unlock(&(inode->i_mutex));
+ mnt_drop_write(filp->f_path.mnt);
+ return err;
+ }
default:
return -ENOTTY;
diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
index e0e3a5e..b580714 100644
--- a/fs/ext4/mballoc.c
+++ b/fs/ext4/mballoc.c
@@ -477,9 +477,10 @@
b2 = (unsigned char *) bitmap;
for (i = 0; i < e4b->bd_sb->s_blocksize; i++) {
if (b1[i] != b2[i]) {
- printk("corruption in group %lu at byte %u(%u):"
- " %x in copy != %x on disk/prealloc\n",
- e4b->bd_group, i, i * 8, b1[i], b2[i]);
+ printk(KERN_ERR "corruption in group %lu "
+ "at byte %u(%u): %x in copy != %x "
+ "on disk/prealloc\n",
+ e4b->bd_group, i, i * 8, b1[i], b2[i]);
BUG();
}
}
@@ -533,9 +534,6 @@
void *buddy;
void *buddy2;
- if (!test_opt(sb, MBALLOC))
- return 0;
-
{
static int mb_check_counter;
if (mb_check_counter++ % 100 != 0)
@@ -784,9 +782,11 @@
if (bh[i] == NULL)
goto out;
- if (bh_uptodate_or_lock(bh[i]))
+ if (buffer_uptodate(bh[i]) &&
+ !(desc->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)))
continue;
+ lock_buffer(bh[i]);
spin_lock(sb_bgl_lock(EXT4_SB(sb), first_group + i));
if (desc->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) {
ext4_init_block_bitmap(sb, bh[i],
@@ -2169,9 +2169,10 @@
{
struct ext4_sb_info *sbi = EXT4_SB(sb);
- remove_proc_entry("mb_groups", sbi->s_mb_proc);
- remove_proc_entry("mb_history", sbi->s_mb_proc);
-
+ if (sbi->s_proc != NULL) {
+ remove_proc_entry("mb_groups", sbi->s_proc);
+ remove_proc_entry("mb_history", sbi->s_proc);
+ }
kfree(sbi->s_mb_history);
}
@@ -2180,10 +2181,10 @@
struct ext4_sb_info *sbi = EXT4_SB(sb);
int i;
- if (sbi->s_mb_proc != NULL) {
- proc_create_data("mb_history", S_IRUGO, sbi->s_mb_proc,
+ if (sbi->s_proc != NULL) {
+ proc_create_data("mb_history", S_IRUGO, sbi->s_proc,
&ext4_mb_seq_history_fops, sb);
- proc_create_data("mb_groups", S_IRUGO, sbi->s_mb_proc,
+ proc_create_data("mb_groups", S_IRUGO, sbi->s_proc,
&ext4_mb_seq_groups_fops, sb);
}
@@ -2485,19 +2486,14 @@
unsigned max;
int ret;
- if (!test_opt(sb, MBALLOC))
- return 0;
-
i = (sb->s_blocksize_bits + 2) * sizeof(unsigned short);
sbi->s_mb_offsets = kmalloc(i, GFP_KERNEL);
if (sbi->s_mb_offsets == NULL) {
- clear_opt(sbi->s_mount_opt, MBALLOC);
return -ENOMEM;
}
sbi->s_mb_maxs = kmalloc(i, GFP_KERNEL);
if (sbi->s_mb_maxs == NULL) {
- clear_opt(sbi->s_mount_opt, MBALLOC);
kfree(sbi->s_mb_maxs);
return -ENOMEM;
}
@@ -2520,7 +2516,6 @@
/* init file for buddy data */
ret = ext4_mb_init_backend(sb);
if (ret != 0) {
- clear_opt(sbi->s_mount_opt, MBALLOC);
kfree(sbi->s_mb_offsets);
kfree(sbi->s_mb_maxs);
return ret;
@@ -2540,17 +2535,15 @@
sbi->s_mb_history_filter = EXT4_MB_HISTORY_DEFAULT;
sbi->s_mb_group_prealloc = MB_DEFAULT_GROUP_PREALLOC;
- i = sizeof(struct ext4_locality_group) * nr_cpu_ids;
- sbi->s_locality_groups = kmalloc(i, GFP_KERNEL);
+ sbi->s_locality_groups = alloc_percpu(struct ext4_locality_group);
if (sbi->s_locality_groups == NULL) {
- clear_opt(sbi->s_mount_opt, MBALLOC);
kfree(sbi->s_mb_offsets);
kfree(sbi->s_mb_maxs);
return -ENOMEM;
}
- for (i = 0; i < nr_cpu_ids; i++) {
+ for_each_possible_cpu(i) {
struct ext4_locality_group *lg;
- lg = &sbi->s_locality_groups[i];
+ lg = per_cpu_ptr(sbi->s_locality_groups, i);
mutex_init(&lg->lg_mutex);
for (j = 0; j < PREALLOC_TB_SIZE; j++)
INIT_LIST_HEAD(&lg->lg_prealloc_list[j]);
@@ -2560,7 +2553,7 @@
ext4_mb_init_per_dev_proc(sb);
ext4_mb_history_init(sb);
- printk("EXT4-fs: mballoc enabled\n");
+ printk(KERN_INFO "EXT4-fs: mballoc enabled\n");
return 0;
}
@@ -2589,9 +2582,6 @@
struct ext4_group_info *grinfo;
struct ext4_sb_info *sbi = EXT4_SB(sb);
- if (!test_opt(sb, MBALLOC))
- return 0;
-
/* release freed, non-committed blocks */
spin_lock(&sbi->s_md_lock);
list_splice_init(&sbi->s_closed_transaction,
@@ -2647,8 +2637,7 @@
atomic_read(&sbi->s_mb_discarded));
}
- kfree(sbi->s_locality_groups);
-
+ free_percpu(sbi->s_locality_groups);
ext4_mb_history_release(sb);
ext4_mb_destroy_per_dev_proc(sb);
@@ -2721,118 +2710,46 @@
#define EXT4_MB_STREAM_REQ "stream_req"
#define EXT4_MB_GROUP_PREALLOC "group_prealloc"
-
-
-#define MB_PROC_FOPS(name) \
-static int ext4_mb_##name##_proc_show(struct seq_file *m, void *v) \
-{ \
- struct ext4_sb_info *sbi = m->private; \
- \
- seq_printf(m, "%ld\n", sbi->s_mb_##name); \
- return 0; \
-} \
- \
-static int ext4_mb_##name##_proc_open(struct inode *inode, struct file *file)\
-{ \
- return single_open(file, ext4_mb_##name##_proc_show, PDE(inode)->data);\
-} \
- \
-static ssize_t ext4_mb_##name##_proc_write(struct file *file, \
- const char __user *buf, size_t cnt, loff_t *ppos) \
-{ \
- struct ext4_sb_info *sbi = PDE(file->f_path.dentry->d_inode)->data;\
- char str[32]; \
- long value; \
- if (cnt >= sizeof(str)) \
- return -EINVAL; \
- if (copy_from_user(str, buf, cnt)) \
- return -EFAULT; \
- value = simple_strtol(str, NULL, 0); \
- if (value <= 0) \
- return -ERANGE; \
- sbi->s_mb_##name = value; \
- return cnt; \
-} \
- \
-static const struct file_operations ext4_mb_##name##_proc_fops = { \
- .owner = THIS_MODULE, \
- .open = ext4_mb_##name##_proc_open, \
- .read = seq_read, \
- .llseek = seq_lseek, \
- .release = single_release, \
- .write = ext4_mb_##name##_proc_write, \
-};
-
-MB_PROC_FOPS(stats);
-MB_PROC_FOPS(max_to_scan);
-MB_PROC_FOPS(min_to_scan);
-MB_PROC_FOPS(order2_reqs);
-MB_PROC_FOPS(stream_request);
-MB_PROC_FOPS(group_prealloc);
-
-#define MB_PROC_HANDLER(name, var) \
-do { \
- proc = proc_create_data(name, mode, sbi->s_mb_proc, \
- &ext4_mb_##var##_proc_fops, sbi); \
- if (proc == NULL) { \
- printk(KERN_ERR "EXT4-fs: can't to create %s\n", name); \
- goto err_out; \
- } \
-} while (0)
-
static int ext4_mb_init_per_dev_proc(struct super_block *sb)
{
mode_t mode = S_IFREG | S_IRUGO | S_IWUSR;
struct ext4_sb_info *sbi = EXT4_SB(sb);
struct proc_dir_entry *proc;
- char devname[64];
- if (proc_root_ext4 == NULL) {
- sbi->s_mb_proc = NULL;
+ if (sbi->s_proc == NULL)
return -EINVAL;
- }
- bdevname(sb->s_bdev, devname);
- sbi->s_mb_proc = proc_mkdir(devname, proc_root_ext4);
- MB_PROC_HANDLER(EXT4_MB_STATS_NAME, stats);
- MB_PROC_HANDLER(EXT4_MB_MAX_TO_SCAN_NAME, max_to_scan);
- MB_PROC_HANDLER(EXT4_MB_MIN_TO_SCAN_NAME, min_to_scan);
- MB_PROC_HANDLER(EXT4_MB_ORDER2_REQ, order2_reqs);
- MB_PROC_HANDLER(EXT4_MB_STREAM_REQ, stream_request);
- MB_PROC_HANDLER(EXT4_MB_GROUP_PREALLOC, group_prealloc);
-
+ EXT4_PROC_HANDLER(EXT4_MB_STATS_NAME, mb_stats);
+ EXT4_PROC_HANDLER(EXT4_MB_MAX_TO_SCAN_NAME, mb_max_to_scan);
+ EXT4_PROC_HANDLER(EXT4_MB_MIN_TO_SCAN_NAME, mb_min_to_scan);
+ EXT4_PROC_HANDLER(EXT4_MB_ORDER2_REQ, mb_order2_reqs);
+ EXT4_PROC_HANDLER(EXT4_MB_STREAM_REQ, mb_stream_request);
+ EXT4_PROC_HANDLER(EXT4_MB_GROUP_PREALLOC, mb_group_prealloc);
return 0;
err_out:
- printk(KERN_ERR "EXT4-fs: Unable to create %s\n", devname);
- remove_proc_entry(EXT4_MB_GROUP_PREALLOC, sbi->s_mb_proc);
- remove_proc_entry(EXT4_MB_STREAM_REQ, sbi->s_mb_proc);
- remove_proc_entry(EXT4_MB_ORDER2_REQ, sbi->s_mb_proc);
- remove_proc_entry(EXT4_MB_MIN_TO_SCAN_NAME, sbi->s_mb_proc);
- remove_proc_entry(EXT4_MB_MAX_TO_SCAN_NAME, sbi->s_mb_proc);
- remove_proc_entry(EXT4_MB_STATS_NAME, sbi->s_mb_proc);
- remove_proc_entry(devname, proc_root_ext4);
- sbi->s_mb_proc = NULL;
-
+ remove_proc_entry(EXT4_MB_GROUP_PREALLOC, sbi->s_proc);
+ remove_proc_entry(EXT4_MB_STREAM_REQ, sbi->s_proc);
+ remove_proc_entry(EXT4_MB_ORDER2_REQ, sbi->s_proc);
+ remove_proc_entry(EXT4_MB_MIN_TO_SCAN_NAME, sbi->s_proc);
+ remove_proc_entry(EXT4_MB_MAX_TO_SCAN_NAME, sbi->s_proc);
+ remove_proc_entry(EXT4_MB_STATS_NAME, sbi->s_proc);
return -ENOMEM;
}
static int ext4_mb_destroy_per_dev_proc(struct super_block *sb)
{
struct ext4_sb_info *sbi = EXT4_SB(sb);
- char devname[64];
- if (sbi->s_mb_proc == NULL)
+ if (sbi->s_proc == NULL)
return -EINVAL;
- bdevname(sb->s_bdev, devname);
- remove_proc_entry(EXT4_MB_GROUP_PREALLOC, sbi->s_mb_proc);
- remove_proc_entry(EXT4_MB_STREAM_REQ, sbi->s_mb_proc);
- remove_proc_entry(EXT4_MB_ORDER2_REQ, sbi->s_mb_proc);
- remove_proc_entry(EXT4_MB_MIN_TO_SCAN_NAME, sbi->s_mb_proc);
- remove_proc_entry(EXT4_MB_MAX_TO_SCAN_NAME, sbi->s_mb_proc);
- remove_proc_entry(EXT4_MB_STATS_NAME, sbi->s_mb_proc);
- remove_proc_entry(devname, proc_root_ext4);
+ remove_proc_entry(EXT4_MB_GROUP_PREALLOC, sbi->s_proc);
+ remove_proc_entry(EXT4_MB_STREAM_REQ, sbi->s_proc);
+ remove_proc_entry(EXT4_MB_ORDER2_REQ, sbi->s_proc);
+ remove_proc_entry(EXT4_MB_MIN_TO_SCAN_NAME, sbi->s_proc);
+ remove_proc_entry(EXT4_MB_MAX_TO_SCAN_NAME, sbi->s_proc);
+ remove_proc_entry(EXT4_MB_STATS_NAME, sbi->s_proc);
return 0;
}
@@ -2854,11 +2771,6 @@
kmem_cache_destroy(ext4_pspace_cachep);
return -ENOMEM;
}
-#ifdef CONFIG_PROC_FS
- proc_root_ext4 = proc_mkdir("fs/ext4", NULL);
- if (proc_root_ext4 == NULL)
- printk(KERN_ERR "EXT4-fs: Unable to create fs/ext4\n");
-#endif
return 0;
}
@@ -2867,9 +2779,6 @@
/* XXX: synchronize_rcu(); */
kmem_cache_destroy(ext4_pspace_cachep);
kmem_cache_destroy(ext4_ac_cachep);
-#ifdef CONFIG_PROC_FS
- remove_proc_entry("fs/ext4", NULL);
-#endif
}
@@ -2879,7 +2788,7 @@
*/
static noinline_for_stack int
ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac,
- handle_t *handle)
+ handle_t *handle, unsigned long reserv_blks)
{
struct buffer_head *bitmap_bh = NULL;
struct ext4_super_block *es;
@@ -2968,15 +2877,16 @@
le16_add_cpu(&gdp->bg_free_blocks_count, -ac->ac_b_ex.fe_len);
gdp->bg_checksum = ext4_group_desc_csum(sbi, ac->ac_b_ex.fe_group, gdp);
spin_unlock(sb_bgl_lock(sbi, ac->ac_b_ex.fe_group));
-
+ percpu_counter_sub(&sbi->s_freeblocks_counter, ac->ac_b_ex.fe_len);
/*
- * free blocks account has already be reduced/reserved
- * at write_begin() time for delayed allocation
- * do not double accounting
+ * Now reduce the dirty block count also. Should not go negative
*/
if (!(ac->ac_flags & EXT4_MB_DELALLOC_RESERVED))
- percpu_counter_sub(&sbi->s_freeblocks_counter,
- ac->ac_b_ex.fe_len);
+ /* release all the reserved blocks if non delalloc */
+ percpu_counter_sub(&sbi->s_dirtyblocks_counter, reserv_blks);
+ else
+ percpu_counter_sub(&sbi->s_dirtyblocks_counter,
+ ac->ac_b_ex.fe_len);
if (sbi->s_log_groups_per_flex) {
ext4_group_t flex_group = ext4_flex_group(sbi,
@@ -3884,7 +3794,7 @@
*
* FIXME!! Make sure it is valid at all the call sites
*/
-void ext4_mb_discard_inode_preallocations(struct inode *inode)
+void ext4_discard_preallocations(struct inode *inode)
{
struct ext4_inode_info *ei = EXT4_I(inode);
struct super_block *sb = inode->i_sb;
@@ -3896,7 +3806,7 @@
struct ext4_buddy e4b;
int err;
- if (!test_opt(sb, MBALLOC) || !S_ISREG(inode->i_mode)) {
+ if (!S_ISREG(inode->i_mode)) {
/*BUG_ON(!list_empty(&ei->i_prealloc_list));*/
return;
}
@@ -4094,8 +4004,7 @@
* per cpu locality group is to reduce the contention between block
* request from multiple CPUs.
*/
- ac->ac_lg = &sbi->s_locality_groups[get_cpu()];
- put_cpu();
+ ac->ac_lg = per_cpu_ptr(sbi->s_locality_groups, raw_smp_processor_id());
/* we're going to use group allocation */
ac->ac_flags |= EXT4_MB_HINT_GROUP_ALLOC;
@@ -4369,33 +4278,32 @@
ext4_fsblk_t ext4_mb_new_blocks(handle_t *handle,
struct ext4_allocation_request *ar, int *errp)
{
+ int freed;
struct ext4_allocation_context *ac = NULL;
struct ext4_sb_info *sbi;
struct super_block *sb;
ext4_fsblk_t block = 0;
- int freed;
- int inquota;
+ unsigned long inquota;
+ unsigned long reserv_blks = 0;
sb = ar->inode->i_sb;
sbi = EXT4_SB(sb);
- if (!test_opt(sb, MBALLOC)) {
- block = ext4_old_new_blocks(handle, ar->inode, ar->goal,
- &(ar->len), errp);
- return block;
- }
if (!EXT4_I(ar->inode)->i_delalloc_reserved_flag) {
/*
* With delalloc we already reserved the blocks
*/
- ar->len = ext4_has_free_blocks(sbi, ar->len);
+ while (ar->len && ext4_claim_free_blocks(sbi, ar->len)) {
+ /* let others to free the space */
+ yield();
+ ar->len = ar->len >> 1;
+ }
+ if (!ar->len) {
+ *errp = -ENOSPC;
+ return 0;
+ }
+ reserv_blks = ar->len;
}
-
- if (ar->len == 0) {
- *errp = -ENOSPC;
- return 0;
- }
-
while (ar->len && DQUOT_ALLOC_BLOCK(ar->inode, ar->len)) {
ar->flags |= EXT4_MB_HINT_NOPREALLOC;
ar->len--;
@@ -4441,7 +4349,7 @@
}
if (likely(ac->ac_status == AC_STATUS_FOUND)) {
- *errp = ext4_mb_mark_diskspace_used(ac, handle);
+ *errp = ext4_mb_mark_diskspace_used(ac, handle, reserv_blks);
if (*errp == -EAGAIN) {
ac->ac_b_ex.fe_group = 0;
ac->ac_b_ex.fe_start = 0;
diff --git a/fs/ext4/mballoc.h b/fs/ext4/mballoc.h
index c7c9906..b3b4828 100644
--- a/fs/ext4/mballoc.h
+++ b/fs/ext4/mballoc.h
@@ -257,7 +257,6 @@
#define in_range(b, first, len) ((b) >= (first) && (b) <= (first) + (len) - 1)
-static struct proc_dir_entry *proc_root_ext4;
struct buffer_head *read_block_bitmap(struct super_block *, ext4_group_t);
static void ext4_mb_generate_from_pa(struct super_block *sb, void *bitmap,
diff --git a/fs/ext4/migrate.c b/fs/ext4/migrate.c
index 46fc0b5..f2a9cf4 100644
--- a/fs/ext4/migrate.c
+++ b/fs/ext4/migrate.c
@@ -447,8 +447,7 @@
}
-int ext4_ext_migrate(struct inode *inode, struct file *filp,
- unsigned int cmd, unsigned long arg)
+int ext4_ext_migrate(struct inode *inode)
{
handle_t *handle;
int retval = 0, i;
@@ -516,12 +515,6 @@
* when we add extents we extent the journal
*/
/*
- * inode_mutex prevent write and truncate on the file. Read still goes
- * through. We take i_data_sem in ext4_ext_swap_inode_data before we
- * switch the inode format to prevent read.
- */
- mutex_lock(&(inode->i_mutex));
- /*
* Even though we take i_mutex we can still cause block allocation
* via mmap write to holes. If we have allocated new blocks we fail
* migrate. New block allocation will clear EXT4_EXT_MIGRATE flag.
@@ -623,7 +616,6 @@
tmp_inode->i_nlink = 0;
ext4_journal_stop(handle);
- mutex_unlock(&(inode->i_mutex));
if (tmp_inode)
iput(tmp_inode);
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 387ad98..92db9e9 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -151,34 +151,36 @@
static inline ext4_lblk_t dx_get_block(struct dx_entry *entry);
static void dx_set_block(struct dx_entry *entry, ext4_lblk_t value);
-static inline unsigned dx_get_hash (struct dx_entry *entry);
-static void dx_set_hash (struct dx_entry *entry, unsigned value);
-static unsigned dx_get_count (struct dx_entry *entries);
-static unsigned dx_get_limit (struct dx_entry *entries);
-static void dx_set_count (struct dx_entry *entries, unsigned value);
-static void dx_set_limit (struct dx_entry *entries, unsigned value);
-static unsigned dx_root_limit (struct inode *dir, unsigned infosize);
-static unsigned dx_node_limit (struct inode *dir);
-static struct dx_frame *dx_probe(struct dentry *dentry,
+static inline unsigned dx_get_hash(struct dx_entry *entry);
+static void dx_set_hash(struct dx_entry *entry, unsigned value);
+static unsigned dx_get_count(struct dx_entry *entries);
+static unsigned dx_get_limit(struct dx_entry *entries);
+static void dx_set_count(struct dx_entry *entries, unsigned value);
+static void dx_set_limit(struct dx_entry *entries, unsigned value);
+static unsigned dx_root_limit(struct inode *dir, unsigned infosize);
+static unsigned dx_node_limit(struct inode *dir);
+static struct dx_frame *dx_probe(const struct qstr *d_name,
struct inode *dir,
struct dx_hash_info *hinfo,
struct dx_frame *frame,
int *err);
-static void dx_release (struct dx_frame *frames);
-static int dx_make_map (struct ext4_dir_entry_2 *de, int size,
- struct dx_hash_info *hinfo, struct dx_map_entry map[]);
+static void dx_release(struct dx_frame *frames);
+static int dx_make_map(struct ext4_dir_entry_2 *de, int size,
+ struct dx_hash_info *hinfo, struct dx_map_entry map[]);
static void dx_sort_map(struct dx_map_entry *map, unsigned count);
-static struct ext4_dir_entry_2 *dx_move_dirents (char *from, char *to,
+static struct ext4_dir_entry_2 *dx_move_dirents(char *from, char *to,
struct dx_map_entry *offsets, int count);
-static struct ext4_dir_entry_2* dx_pack_dirents (char *base, int size);
+static struct ext4_dir_entry_2* dx_pack_dirents(char *base, int size);
static void dx_insert_block(struct dx_frame *frame,
u32 hash, ext4_lblk_t block);
static int ext4_htree_next_block(struct inode *dir, __u32 hash,
struct dx_frame *frame,
struct dx_frame *frames,
__u32 *start_hash);
-static struct buffer_head * ext4_dx_find_entry(struct dentry *dentry,
- struct ext4_dir_entry_2 **res_dir, int *err);
+static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
+ const struct qstr *d_name,
+ struct ext4_dir_entry_2 **res_dir,
+ int *err);
static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry,
struct inode *inode);
@@ -207,44 +209,44 @@
entry->block = cpu_to_le32(value);
}
-static inline unsigned dx_get_hash (struct dx_entry *entry)
+static inline unsigned dx_get_hash(struct dx_entry *entry)
{
return le32_to_cpu(entry->hash);
}
-static inline void dx_set_hash (struct dx_entry *entry, unsigned value)
+static inline void dx_set_hash(struct dx_entry *entry, unsigned value)
{
entry->hash = cpu_to_le32(value);
}
-static inline unsigned dx_get_count (struct dx_entry *entries)
+static inline unsigned dx_get_count(struct dx_entry *entries)
{
return le16_to_cpu(((struct dx_countlimit *) entries)->count);
}
-static inline unsigned dx_get_limit (struct dx_entry *entries)
+static inline unsigned dx_get_limit(struct dx_entry *entries)
{
return le16_to_cpu(((struct dx_countlimit *) entries)->limit);
}
-static inline void dx_set_count (struct dx_entry *entries, unsigned value)
+static inline void dx_set_count(struct dx_entry *entries, unsigned value)
{
((struct dx_countlimit *) entries)->count = cpu_to_le16(value);
}
-static inline void dx_set_limit (struct dx_entry *entries, unsigned value)
+static inline void dx_set_limit(struct dx_entry *entries, unsigned value)
{
((struct dx_countlimit *) entries)->limit = cpu_to_le16(value);
}
-static inline unsigned dx_root_limit (struct inode *dir, unsigned infosize)
+static inline unsigned dx_root_limit(struct inode *dir, unsigned infosize)
{
unsigned entry_space = dir->i_sb->s_blocksize - EXT4_DIR_REC_LEN(1) -
EXT4_DIR_REC_LEN(2) - infosize;
return entry_space / sizeof(struct dx_entry);
}
-static inline unsigned dx_node_limit (struct inode *dir)
+static inline unsigned dx_node_limit(struct inode *dir)
{
unsigned entry_space = dir->i_sb->s_blocksize - EXT4_DIR_REC_LEN(0);
return entry_space / sizeof(struct dx_entry);
@@ -254,12 +256,12 @@
* Debug
*/
#ifdef DX_DEBUG
-static void dx_show_index (char * label, struct dx_entry *entries)
+static void dx_show_index(char * label, struct dx_entry *entries)
{
int i, n = dx_get_count (entries);
- printk("%s index ", label);
+ printk(KERN_DEBUG "%s index ", label);
for (i = 0; i < n; i++) {
- printk("%x->%lu ", i? dx_get_hash(entries + i) :
+ printk("%x->%lu ", i ? dx_get_hash(entries + i) :
0, (unsigned long)dx_get_block(entries + i));
}
printk("\n");
@@ -306,7 +308,7 @@
struct dx_entry *entries, int levels)
{
unsigned blocksize = dir->i_sb->s_blocksize;
- unsigned count = dx_get_count (entries), names = 0, space = 0, i;
+ unsigned count = dx_get_count(entries), names = 0, space = 0, i;
unsigned bcount = 0;
struct buffer_head *bh;
int err;
@@ -325,11 +327,12 @@
names += stats.names;
space += stats.space;
bcount += stats.bcount;
- brelse (bh);
+ brelse(bh);
}
if (bcount)
- printk("%snames %u, fullness %u (%u%%)\n", levels?"":" ",
- names, space/bcount,(space/bcount)*100/blocksize);
+ printk(KERN_DEBUG "%snames %u, fullness %u (%u%%)\n",
+ levels ? "" : " ", names, space/bcount,
+ (space/bcount)*100/blocksize);
return (struct stats) { names, space, bcount};
}
#endif /* DX_DEBUG */
@@ -344,7 +347,7 @@
* back to userspace.
*/
static struct dx_frame *
-dx_probe(struct dentry *dentry, struct inode *dir,
+dx_probe(const struct qstr *d_name, struct inode *dir,
struct dx_hash_info *hinfo, struct dx_frame *frame_in, int *err)
{
unsigned count, indirect;
@@ -355,8 +358,6 @@
u32 hash;
frame->bh = NULL;
- if (dentry)
- dir = dentry->d_parent->d_inode;
if (!(bh = ext4_bread (NULL,dir, 0, 0, err)))
goto fail;
root = (struct dx_root *) bh->b_data;
@@ -372,8 +373,8 @@
}
hinfo->hash_version = root->info.hash_version;
hinfo->seed = EXT4_SB(dir->i_sb)->s_hash_seed;
- if (dentry)
- ext4fs_dirhash(dentry->d_name.name, dentry->d_name.len, hinfo);
+ if (d_name)
+ ext4fs_dirhash(d_name->name, d_name->len, hinfo);
hash = hinfo->hash;
if (root->info.unused_flags & 1) {
@@ -406,7 +407,7 @@
goto fail;
}
- dxtrace (printk("Look up %x", hash));
+ dxtrace(printk("Look up %x", hash));
while (1)
{
count = dx_get_count(entries);
@@ -555,7 +556,7 @@
0, &err)))
return err; /* Failure */
p++;
- brelse (p->bh);
+ brelse(p->bh);
p->bh = bh;
p->at = p->entries = ((struct dx_node *) bh->b_data)->entries;
}
@@ -593,7 +594,7 @@
/* On error, skip the f_pos to the next block. */
dir_file->f_pos = (dir_file->f_pos |
(dir->i_sb->s_blocksize - 1)) + 1;
- brelse (bh);
+ brelse(bh);
return count;
}
ext4fs_dirhash(de->name, de->name_len, hinfo);
@@ -635,8 +636,8 @@
int ret, err;
__u32 hashval;
- dxtrace(printk("In htree_fill_tree, start hash: %x:%x\n", start_hash,
- start_minor_hash));
+ dxtrace(printk(KERN_DEBUG "In htree_fill_tree, start hash: %x:%x\n",
+ start_hash, start_minor_hash));
dir = dir_file->f_path.dentry->d_inode;
if (!(EXT4_I(dir)->i_flags & EXT4_INDEX_FL)) {
hinfo.hash_version = EXT4_SB(dir->i_sb)->s_def_hash_version;
@@ -648,7 +649,7 @@
}
hinfo.hash = start_hash;
hinfo.minor_hash = 0;
- frame = dx_probe(NULL, dir_file->f_path.dentry->d_inode, &hinfo, frames, &err);
+ frame = dx_probe(NULL, dir, &hinfo, frames, &err);
if (!frame)
return err;
@@ -694,8 +695,8 @@
break;
}
dx_release(frames);
- dxtrace(printk("Fill tree: returned %d entries, next hash: %x\n",
- count, *next_hash));
+ dxtrace(printk(KERN_DEBUG "Fill tree: returned %d entries, "
+ "next hash: %x\n", count, *next_hash));
return count;
errout:
dx_release(frames);
@@ -802,17 +803,17 @@
/*
* Returns 0 if not found, -1 on failure, and 1 on success
*/
-static inline int search_dirblock(struct buffer_head * bh,
+static inline int search_dirblock(struct buffer_head *bh,
struct inode *dir,
- struct dentry *dentry,
+ const struct qstr *d_name,
unsigned long offset,
struct ext4_dir_entry_2 ** res_dir)
{
struct ext4_dir_entry_2 * de;
char * dlimit;
int de_len;
- const char *name = dentry->d_name.name;
- int namelen = dentry->d_name.len;
+ const char *name = d_name->name;
+ int namelen = d_name->len;
de = (struct ext4_dir_entry_2 *) bh->b_data;
dlimit = bh->b_data + dir->i_sb->s_blocksize;
@@ -851,12 +852,13 @@
* The returned buffer_head has ->b_count elevated. The caller is expected
* to brelse() it when appropriate.
*/
-static struct buffer_head * ext4_find_entry (struct dentry *dentry,
+static struct buffer_head * ext4_find_entry (struct inode *dir,
+ const struct qstr *d_name,
struct ext4_dir_entry_2 ** res_dir)
{
- struct super_block * sb;
- struct buffer_head * bh_use[NAMEI_RA_SIZE];
- struct buffer_head * bh, *ret = NULL;
+ struct super_block *sb;
+ struct buffer_head *bh_use[NAMEI_RA_SIZE];
+ struct buffer_head *bh, *ret = NULL;
ext4_lblk_t start, block, b;
int ra_max = 0; /* Number of bh's in the readahead
buffer, bh_use[] */
@@ -865,16 +867,15 @@
int num = 0;
ext4_lblk_t nblocks;
int i, err;
- struct inode *dir = dentry->d_parent->d_inode;
int namelen;
*res_dir = NULL;
sb = dir->i_sb;
- namelen = dentry->d_name.len;
+ namelen = d_name->len;
if (namelen > EXT4_NAME_LEN)
return NULL;
if (is_dx(dir)) {
- bh = ext4_dx_find_entry(dentry, res_dir, &err);
+ bh = ext4_dx_find_entry(dir, d_name, res_dir, &err);
/*
* On success, or if the error was file not found,
* return. Otherwise, fall back to doing a search the
@@ -882,7 +883,8 @@
*/
if (bh || (err != ERR_BAD_DX_DIR))
return bh;
- dxtrace(printk("ext4_find_entry: dx failed, falling back\n"));
+ dxtrace(printk(KERN_DEBUG "ext4_find_entry: dx failed, "
+ "falling back\n"));
}
nblocks = dir->i_size >> EXT4_BLOCK_SIZE_BITS(sb);
start = EXT4_I(dir)->i_dir_start_lookup;
@@ -926,7 +928,7 @@
brelse(bh);
goto next;
}
- i = search_dirblock(bh, dir, dentry,
+ i = search_dirblock(bh, dir, d_name,
block << EXT4_BLOCK_SIZE_BITS(sb), res_dir);
if (i == 1) {
EXT4_I(dir)->i_dir_start_lookup = block;
@@ -956,11 +958,11 @@
cleanup_and_exit:
/* Clean up the read-ahead blocks */
for (; ra_ptr < ra_max; ra_ptr++)
- brelse (bh_use[ra_ptr]);
+ brelse(bh_use[ra_ptr]);
return ret;
}
-static struct buffer_head * ext4_dx_find_entry(struct dentry *dentry,
+static struct buffer_head * ext4_dx_find_entry(struct inode *dir, const struct qstr *d_name,
struct ext4_dir_entry_2 **res_dir, int *err)
{
struct super_block * sb;
@@ -971,14 +973,13 @@
struct buffer_head *bh;
ext4_lblk_t block;
int retval;
- int namelen = dentry->d_name.len;
- const u8 *name = dentry->d_name.name;
- struct inode *dir = dentry->d_parent->d_inode;
+ int namelen = d_name->len;
+ const u8 *name = d_name->name;
sb = dir->i_sb;
/* NFS may look up ".." - look at dx_root directory block */
if (namelen > 2 || name[0] != '.'||(name[1] != '.' && name[1] != '\0')){
- if (!(frame = dx_probe(dentry, NULL, &hinfo, frames, err)))
+ if (!(frame = dx_probe(d_name, dir, &hinfo, frames, err)))
return NULL;
} else {
frame = frames;
@@ -1010,7 +1011,7 @@
return bh;
}
}
- brelse (bh);
+ brelse(bh);
/* Check to see if we should continue to search */
retval = ext4_htree_next_block(dir, hash, frame,
frames, NULL);
@@ -1025,25 +1026,25 @@
*err = -ENOENT;
errout:
- dxtrace(printk("%s not found\n", name));
+ dxtrace(printk(KERN_DEBUG "%s not found\n", name));
dx_release (frames);
return NULL;
}
-static struct dentry *ext4_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd)
+static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
{
- struct inode * inode;
- struct ext4_dir_entry_2 * de;
- struct buffer_head * bh;
+ struct inode *inode;
+ struct ext4_dir_entry_2 *de;
+ struct buffer_head *bh;
if (dentry->d_name.len > EXT4_NAME_LEN)
return ERR_PTR(-ENAMETOOLONG);
- bh = ext4_find_entry(dentry, &de);
+ bh = ext4_find_entry(dir, &dentry->d_name, &de);
inode = NULL;
if (bh) {
unsigned long ino = le32_to_cpu(de->inode);
- brelse (bh);
+ brelse(bh);
if (!ext4_valid_inum(dir->i_sb, ino)) {
ext4_error(dir->i_sb, "ext4_lookup",
"bad inode number: %lu", ino);
@@ -1062,15 +1063,14 @@
unsigned long ino;
struct dentry *parent;
struct inode *inode;
- struct dentry dotdot;
+ static const struct qstr dotdot = {
+ .name = "..",
+ .len = 2,
+ };
struct ext4_dir_entry_2 * de;
struct buffer_head *bh;
- dotdot.d_name.name = "..";
- dotdot.d_name.len = 2;
- dotdot.d_parent = child; /* confusing, isn't it! */
-
- bh = ext4_find_entry(&dotdot, &de);
+ bh = ext4_find_entry(child->d_inode, &dotdot, &de);
inode = NULL;
if (!bh)
return ERR_PTR(-ENOENT);
@@ -1201,10 +1201,10 @@
/* create map in the end of data2 block */
map = (struct dx_map_entry *) (data2 + blocksize);
- count = dx_make_map ((struct ext4_dir_entry_2 *) data1,
+ count = dx_make_map((struct ext4_dir_entry_2 *) data1,
blocksize, hinfo, map);
map -= count;
- dx_sort_map (map, count);
+ dx_sort_map(map, count);
/* Split the existing block in the middle, size-wise */
size = 0;
move = 0;
@@ -1225,7 +1225,7 @@
/* Fancy dance to stay within two buffers */
de2 = dx_move_dirents(data1, data2, map + split, count - split);
- de = dx_pack_dirents(data1,blocksize);
+ de = dx_pack_dirents(data1, blocksize);
de->rec_len = ext4_rec_len_to_disk(data1 + blocksize - (char *) de);
de2->rec_len = ext4_rec_len_to_disk(data2 + blocksize - (char *) de2);
dxtrace(dx_show_leaf (hinfo, (struct ext4_dir_entry_2 *) data1, blocksize, 1));
@@ -1237,15 +1237,15 @@
swap(*bh, bh2);
de = de2;
}
- dx_insert_block (frame, hash2 + continued, newblock);
- err = ext4_journal_dirty_metadata (handle, bh2);
+ dx_insert_block(frame, hash2 + continued, newblock);
+ err = ext4_journal_dirty_metadata(handle, bh2);
if (err)
goto journal_error;
- err = ext4_journal_dirty_metadata (handle, frame->bh);
+ err = ext4_journal_dirty_metadata(handle, frame->bh);
if (err)
goto journal_error;
- brelse (bh2);
- dxtrace(dx_show_index ("frame", frame->entries));
+ brelse(bh2);
+ dxtrace(dx_show_index("frame", frame->entries));
return de;
journal_error:
@@ -1271,7 +1271,7 @@
*/
static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
struct inode *inode, struct ext4_dir_entry_2 *de,
- struct buffer_head * bh)
+ struct buffer_head *bh)
{
struct inode *dir = dentry->d_parent->d_inode;
const char *name = dentry->d_name.name;
@@ -1288,11 +1288,11 @@
while ((char *) de <= top) {
if (!ext4_check_dir_entry("ext4_add_entry", dir, de,
bh, offset)) {
- brelse (bh);
+ brelse(bh);
return -EIO;
}
- if (ext4_match (namelen, name, de)) {
- brelse (bh);
+ if (ext4_match(namelen, name, de)) {
+ brelse(bh);
return -EEXIST;
}
nlen = EXT4_DIR_REC_LEN(de->name_len);
@@ -1329,7 +1329,7 @@
} else
de->inode = 0;
de->name_len = namelen;
- memcpy (de->name, name, namelen);
+ memcpy(de->name, name, namelen);
/*
* XXX shouldn't update any times until successful
* completion of syscall, but too many callers depend
@@ -1377,7 +1377,7 @@
struct fake_dirent *fde;
blocksize = dir->i_sb->s_blocksize;
- dxtrace(printk("Creating index\n"));
+ dxtrace(printk(KERN_DEBUG "Creating index\n"));
retval = ext4_journal_get_write_access(handle, bh);
if (retval) {
ext4_std_error(dir->i_sb, retval);
@@ -1386,7 +1386,7 @@
}
root = (struct dx_root *) bh->b_data;
- bh2 = ext4_append (handle, dir, &block, &retval);
+ bh2 = ext4_append(handle, dir, &block, &retval);
if (!(bh2)) {
brelse(bh);
return retval;
@@ -1412,9 +1412,9 @@
root->info.info_length = sizeof(root->info);
root->info.hash_version = EXT4_SB(dir->i_sb)->s_def_hash_version;
entries = root->entries;
- dx_set_block (entries, 1);
- dx_set_count (entries, 1);
- dx_set_limit (entries, dx_root_limit(dir, sizeof(root->info)));
+ dx_set_block(entries, 1);
+ dx_set_count(entries, 1);
+ dx_set_limit(entries, dx_root_limit(dir, sizeof(root->info)));
/* Initialize as for dx_probe */
hinfo.hash_version = root->info.hash_version;
@@ -1443,14 +1443,14 @@
* may not sleep between calling this and putting something into
* the entry, as someone else might have used it while you slept.
*/
-static int ext4_add_entry (handle_t *handle, struct dentry *dentry,
- struct inode *inode)
+static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
+ struct inode *inode)
{
struct inode *dir = dentry->d_parent->d_inode;
unsigned long offset;
- struct buffer_head * bh;
+ struct buffer_head *bh;
struct ext4_dir_entry_2 *de;
- struct super_block * sb;
+ struct super_block *sb;
int retval;
int dx_fallback=0;
unsigned blocksize;
@@ -1500,13 +1500,13 @@
struct dx_frame frames[2], *frame;
struct dx_entry *entries, *at;
struct dx_hash_info hinfo;
- struct buffer_head * bh;
+ struct buffer_head *bh;
struct inode *dir = dentry->d_parent->d_inode;
- struct super_block * sb = dir->i_sb;
+ struct super_block *sb = dir->i_sb;
struct ext4_dir_entry_2 *de;
int err;
- frame = dx_probe(dentry, NULL, &hinfo, frames, &err);
+ frame = dx_probe(&dentry->d_name, dir, &hinfo, frames, &err);
if (!frame)
return err;
entries = frame->entries;
@@ -1527,7 +1527,7 @@
}
/* Block full, should compress but for now just split */
- dxtrace(printk("using %u of %u node entries\n",
+ dxtrace(printk(KERN_DEBUG "using %u of %u node entries\n",
dx_get_count(entries), dx_get_limit(entries)));
/* Need to split index? */
if (dx_get_count(entries) == dx_get_limit(entries)) {
@@ -1559,7 +1559,8 @@
if (levels) {
unsigned icount1 = icount/2, icount2 = icount - icount1;
unsigned hash2 = dx_get_hash(entries + icount1);
- dxtrace(printk("Split index %i/%i\n", icount1, icount2));
+ dxtrace(printk(KERN_DEBUG "Split index %i/%i\n",
+ icount1, icount2));
BUFFER_TRACE(frame->bh, "get_write_access"); /* index root */
err = ext4_journal_get_write_access(handle,
@@ -1567,11 +1568,11 @@
if (err)
goto journal_error;
- memcpy ((char *) entries2, (char *) (entries + icount1),
- icount2 * sizeof(struct dx_entry));
- dx_set_count (entries, icount1);
- dx_set_count (entries2, icount2);
- dx_set_limit (entries2, dx_node_limit(dir));
+ memcpy((char *) entries2, (char *) (entries + icount1),
+ icount2 * sizeof(struct dx_entry));
+ dx_set_count(entries, icount1);
+ dx_set_count(entries2, icount2);
+ dx_set_limit(entries2, dx_node_limit(dir));
/* Which index block gets the new entry? */
if (at - entries >= icount1) {
@@ -1579,16 +1580,17 @@
frame->entries = entries = entries2;
swap(frame->bh, bh2);
}
- dx_insert_block (frames + 0, hash2, newblock);
- dxtrace(dx_show_index ("node", frames[1].entries));
- dxtrace(dx_show_index ("node",
+ dx_insert_block(frames + 0, hash2, newblock);
+ dxtrace(dx_show_index("node", frames[1].entries));
+ dxtrace(dx_show_index("node",
((struct dx_node *) bh2->b_data)->entries));
err = ext4_journal_dirty_metadata(handle, bh2);
if (err)
goto journal_error;
brelse (bh2);
} else {
- dxtrace(printk("Creating second level index...\n"));
+ dxtrace(printk(KERN_DEBUG
+ "Creating second level index...\n"));
memcpy((char *) entries2, (char *) entries,
icount * sizeof(struct dx_entry));
dx_set_limit(entries2, dx_node_limit(dir));
@@ -1630,12 +1632,12 @@
* ext4_delete_entry deletes a directory entry by merging it with the
* previous entry
*/
-static int ext4_delete_entry (handle_t *handle,
- struct inode * dir,
- struct ext4_dir_entry_2 * de_del,
- struct buffer_head * bh)
+static int ext4_delete_entry(handle_t *handle,
+ struct inode *dir,
+ struct ext4_dir_entry_2 *de_del,
+ struct buffer_head *bh)
{
- struct ext4_dir_entry_2 * de, * pde;
+ struct ext4_dir_entry_2 *de, *pde;
int i;
i = 0;
@@ -1716,11 +1718,11 @@
* If the create succeeds, we fill in the inode information
* with d_instantiate().
*/
-static int ext4_create (struct inode * dir, struct dentry * dentry, int mode,
- struct nameidata *nd)
+static int ext4_create(struct inode *dir, struct dentry *dentry, int mode,
+ struct nameidata *nd)
{
handle_t *handle;
- struct inode * inode;
+ struct inode *inode;
int err, retries = 0;
retry:
@@ -1747,8 +1749,8 @@
return err;
}
-static int ext4_mknod (struct inode * dir, struct dentry *dentry,
- int mode, dev_t rdev)
+static int ext4_mknod(struct inode *dir, struct dentry *dentry,
+ int mode, dev_t rdev)
{
handle_t *handle;
struct inode *inode;
@@ -1767,11 +1769,11 @@
if (IS_DIRSYNC(dir))
handle->h_sync = 1;
- inode = ext4_new_inode (handle, dir, mode);
+ inode = ext4_new_inode(handle, dir, mode);
err = PTR_ERR(inode);
if (!IS_ERR(inode)) {
init_special_inode(inode, inode->i_mode, rdev);
-#ifdef CONFIG_EXT4DEV_FS_XATTR
+#ifdef CONFIG_EXT4_FS_XATTR
inode->i_op = &ext4_special_inode_operations;
#endif
err = ext4_add_nondir(handle, dentry, inode);
@@ -1782,12 +1784,12 @@
return err;
}
-static int ext4_mkdir(struct inode * dir, struct dentry * dentry, int mode)
+static int ext4_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
handle_t *handle;
- struct inode * inode;
- struct buffer_head * dir_block;
- struct ext4_dir_entry_2 * de;
+ struct inode *inode;
+ struct buffer_head *dir_block;
+ struct ext4_dir_entry_2 *de;
int err, retries = 0;
if (EXT4_DIR_LINK_MAX(dir))
@@ -1803,7 +1805,7 @@
if (IS_DIRSYNC(dir))
handle->h_sync = 1;
- inode = ext4_new_inode (handle, dir, S_IFDIR | mode);
+ inode = ext4_new_inode(handle, dir, S_IFDIR | mode);
err = PTR_ERR(inode);
if (IS_ERR(inode))
goto out_stop;
@@ -1811,7 +1813,7 @@
inode->i_op = &ext4_dir_inode_operations;
inode->i_fop = &ext4_dir_operations;
inode->i_size = EXT4_I(inode)->i_disksize = inode->i_sb->s_blocksize;
- dir_block = ext4_bread (handle, inode, 0, 1, &err);
+ dir_block = ext4_bread(handle, inode, 0, 1, &err);
if (!dir_block)
goto out_clear_inode;
BUFFER_TRACE(dir_block, "get_write_access");
@@ -1820,26 +1822,26 @@
de->inode = cpu_to_le32(inode->i_ino);
de->name_len = 1;
de->rec_len = ext4_rec_len_to_disk(EXT4_DIR_REC_LEN(de->name_len));
- strcpy (de->name, ".");
+ strcpy(de->name, ".");
ext4_set_de_type(dir->i_sb, de, S_IFDIR);
de = ext4_next_entry(de);
de->inode = cpu_to_le32(dir->i_ino);
de->rec_len = ext4_rec_len_to_disk(inode->i_sb->s_blocksize -
EXT4_DIR_REC_LEN(1));
de->name_len = 2;
- strcpy (de->name, "..");
+ strcpy(de->name, "..");
ext4_set_de_type(dir->i_sb, de, S_IFDIR);
inode->i_nlink = 2;
BUFFER_TRACE(dir_block, "call ext4_journal_dirty_metadata");
ext4_journal_dirty_metadata(handle, dir_block);
- brelse (dir_block);
+ brelse(dir_block);
ext4_mark_inode_dirty(handle, inode);
- err = ext4_add_entry (handle, dentry, inode);
+ err = ext4_add_entry(handle, dentry, inode);
if (err) {
out_clear_inode:
clear_nlink(inode);
ext4_mark_inode_dirty(handle, inode);
- iput (inode);
+ iput(inode);
goto out_stop;
}
ext4_inc_count(handle, dir);
@@ -1856,17 +1858,17 @@
/*
* routine to check that the specified directory is empty (for rmdir)
*/
-static int empty_dir (struct inode * inode)
+static int empty_dir(struct inode *inode)
{
unsigned long offset;
- struct buffer_head * bh;
- struct ext4_dir_entry_2 * de, * de1;
- struct super_block * sb;
+ struct buffer_head *bh;
+ struct ext4_dir_entry_2 *de, *de1;
+ struct super_block *sb;
int err = 0;
sb = inode->i_sb;
if (inode->i_size < EXT4_DIR_REC_LEN(1) + EXT4_DIR_REC_LEN(2) ||
- !(bh = ext4_bread (NULL, inode, 0, 0, &err))) {
+ !(bh = ext4_bread(NULL, inode, 0, 0, &err))) {
if (err)
ext4_error(inode->i_sb, __func__,
"error %d reading directory #%lu offset 0",
@@ -1881,23 +1883,23 @@
de1 = ext4_next_entry(de);
if (le32_to_cpu(de->inode) != inode->i_ino ||
!le32_to_cpu(de1->inode) ||
- strcmp (".", de->name) ||
- strcmp ("..", de1->name)) {
- ext4_warning (inode->i_sb, "empty_dir",
- "bad directory (dir #%lu) - no `.' or `..'",
- inode->i_ino);
- brelse (bh);
+ strcmp(".", de->name) ||
+ strcmp("..", de1->name)) {
+ ext4_warning(inode->i_sb, "empty_dir",
+ "bad directory (dir #%lu) - no `.' or `..'",
+ inode->i_ino);
+ brelse(bh);
return 1;
}
offset = ext4_rec_len_from_disk(de->rec_len) +
ext4_rec_len_from_disk(de1->rec_len);
de = ext4_next_entry(de1);
- while (offset < inode->i_size ) {
+ while (offset < inode->i_size) {
if (!bh ||
(void *) de >= (void *) (bh->b_data+sb->s_blocksize)) {
err = 0;
- brelse (bh);
- bh = ext4_bread (NULL, inode,
+ brelse(bh);
+ bh = ext4_bread(NULL, inode,
offset >> EXT4_BLOCK_SIZE_BITS(sb), 0, &err);
if (!bh) {
if (err)
@@ -1917,13 +1919,13 @@
continue;
}
if (le32_to_cpu(de->inode)) {
- brelse (bh);
+ brelse(bh);
return 0;
}
offset += ext4_rec_len_from_disk(de->rec_len);
de = ext4_next_entry(de);
}
- brelse (bh);
+ brelse(bh);
return 1;
}
@@ -1954,8 +1956,8 @@
* ->i_nlink. For, say it, character device. Not a regular file,
* not a directory, not a symlink and ->i_nlink > 0.
*/
- J_ASSERT ((S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
- S_ISLNK(inode->i_mode)) || inode->i_nlink == 0);
+ J_ASSERT((S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+ S_ISLNK(inode->i_mode)) || inode->i_nlink == 0);
BUFFER_TRACE(EXT4_SB(sb)->s_sbh, "get_write_access");
err = ext4_journal_get_write_access(handle, EXT4_SB(sb)->s_sbh);
@@ -2069,12 +2071,12 @@
goto out_err;
}
-static int ext4_rmdir (struct inode * dir, struct dentry *dentry)
+static int ext4_rmdir(struct inode *dir, struct dentry *dentry)
{
int retval;
- struct inode * inode;
- struct buffer_head * bh;
- struct ext4_dir_entry_2 * de;
+ struct inode *inode;
+ struct buffer_head *bh;
+ struct ext4_dir_entry_2 *de;
handle_t *handle;
/* Initialize quotas before so that eventual writes go in
@@ -2085,7 +2087,7 @@
return PTR_ERR(handle);
retval = -ENOENT;
- bh = ext4_find_entry (dentry, &de);
+ bh = ext4_find_entry(dir, &dentry->d_name, &de);
if (!bh)
goto end_rmdir;
@@ -2099,16 +2101,16 @@
goto end_rmdir;
retval = -ENOTEMPTY;
- if (!empty_dir (inode))
+ if (!empty_dir(inode))
goto end_rmdir;
retval = ext4_delete_entry(handle, dir, de, bh);
if (retval)
goto end_rmdir;
if (!EXT4_DIR_LINK_EMPTY(inode))
- ext4_warning (inode->i_sb, "ext4_rmdir",
- "empty directory has too many links (%d)",
- inode->i_nlink);
+ ext4_warning(inode->i_sb, "ext4_rmdir",
+ "empty directory has too many links (%d)",
+ inode->i_nlink);
inode->i_version++;
clear_nlink(inode);
/* There's no need to set i_disksize: the fact that i_nlink is
@@ -2124,16 +2126,16 @@
end_rmdir:
ext4_journal_stop(handle);
- brelse (bh);
+ brelse(bh);
return retval;
}
-static int ext4_unlink(struct inode * dir, struct dentry *dentry)
+static int ext4_unlink(struct inode *dir, struct dentry *dentry)
{
int retval;
- struct inode * inode;
- struct buffer_head * bh;
- struct ext4_dir_entry_2 * de;
+ struct inode *inode;
+ struct buffer_head *bh;
+ struct ext4_dir_entry_2 *de;
handle_t *handle;
/* Initialize quotas before so that eventual writes go
@@ -2147,7 +2149,7 @@
handle->h_sync = 1;
retval = -ENOENT;
- bh = ext4_find_entry (dentry, &de);
+ bh = ext4_find_entry(dir, &dentry->d_name, &de);
if (!bh)
goto end_unlink;
@@ -2158,9 +2160,9 @@
goto end_unlink;
if (!inode->i_nlink) {
- ext4_warning (inode->i_sb, "ext4_unlink",
- "Deleting nonexistent file (%lu), %d",
- inode->i_ino, inode->i_nlink);
+ ext4_warning(inode->i_sb, "ext4_unlink",
+ "Deleting nonexistent file (%lu), %d",
+ inode->i_ino, inode->i_nlink);
inode->i_nlink = 1;
}
retval = ext4_delete_entry(handle, dir, de, bh);
@@ -2178,15 +2180,15 @@
end_unlink:
ext4_journal_stop(handle);
- brelse (bh);
+ brelse(bh);
return retval;
}
-static int ext4_symlink (struct inode * dir,
- struct dentry *dentry, const char * symname)
+static int ext4_symlink(struct inode *dir,
+ struct dentry *dentry, const char *symname)
{
handle_t *handle;
- struct inode * inode;
+ struct inode *inode;
int l, err, retries = 0;
l = strlen(symname)+1;
@@ -2203,12 +2205,12 @@
if (IS_DIRSYNC(dir))
handle->h_sync = 1;
- inode = ext4_new_inode (handle, dir, S_IFLNK|S_IRWXUGO);
+ inode = ext4_new_inode(handle, dir, S_IFLNK|S_IRWXUGO);
err = PTR_ERR(inode);
if (IS_ERR(inode))
goto out_stop;
- if (l > sizeof (EXT4_I(inode)->i_data)) {
+ if (l > sizeof(EXT4_I(inode)->i_data)) {
inode->i_op = &ext4_symlink_inode_operations;
ext4_set_aops(inode);
/*
@@ -2221,14 +2223,14 @@
if (err) {
clear_nlink(inode);
ext4_mark_inode_dirty(handle, inode);
- iput (inode);
+ iput(inode);
goto out_stop;
}
} else {
/* clear the extent format for fast symlink */
EXT4_I(inode)->i_flags &= ~EXT4_EXTENTS_FL;
inode->i_op = &ext4_fast_symlink_inode_operations;
- memcpy((char*)&EXT4_I(inode)->i_data,symname,l);
+ memcpy((char *)&EXT4_I(inode)->i_data, symname, l);
inode->i_size = l-1;
}
EXT4_I(inode)->i_disksize = inode->i_size;
@@ -2240,8 +2242,8 @@
return err;
}
-static int ext4_link (struct dentry * old_dentry,
- struct inode * dir, struct dentry *dentry)
+static int ext4_link(struct dentry *old_dentry,
+ struct inode *dir, struct dentry *dentry)
{
handle_t *handle;
struct inode *inode = old_dentry->d_inode;
@@ -2284,13 +2286,13 @@
* Anybody can rename anything with this: the permission checks are left to the
* higher-level routines.
*/
-static int ext4_rename (struct inode * old_dir, struct dentry *old_dentry,
- struct inode * new_dir,struct dentry *new_dentry)
+static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
{
handle_t *handle;
- struct inode * old_inode, * new_inode;
- struct buffer_head * old_bh, * new_bh, * dir_bh;
- struct ext4_dir_entry_2 * old_de, * new_de;
+ struct inode *old_inode, *new_inode;
+ struct buffer_head *old_bh, *new_bh, *dir_bh;
+ struct ext4_dir_entry_2 *old_de, *new_de;
int retval;
old_bh = new_bh = dir_bh = NULL;
@@ -2308,7 +2310,7 @@
if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir))
handle->h_sync = 1;
- old_bh = ext4_find_entry (old_dentry, &old_de);
+ old_bh = ext4_find_entry(old_dir, &old_dentry->d_name, &old_de);
/*
* Check for inode number is _not_ due to possible IO errors.
* We might rmdir the source, keep it as pwd of some process
@@ -2321,32 +2323,32 @@
goto end_rename;
new_inode = new_dentry->d_inode;
- new_bh = ext4_find_entry (new_dentry, &new_de);
+ new_bh = ext4_find_entry(new_dir, &new_dentry->d_name, &new_de);
if (new_bh) {
if (!new_inode) {
- brelse (new_bh);
+ brelse(new_bh);
new_bh = NULL;
}
}
if (S_ISDIR(old_inode->i_mode)) {
if (new_inode) {
retval = -ENOTEMPTY;
- if (!empty_dir (new_inode))
+ if (!empty_dir(new_inode))
goto end_rename;
}
retval = -EIO;
- dir_bh = ext4_bread (handle, old_inode, 0, 0, &retval);
+ dir_bh = ext4_bread(handle, old_inode, 0, 0, &retval);
if (!dir_bh)
goto end_rename;
if (le32_to_cpu(PARENT_INO(dir_bh->b_data)) != old_dir->i_ino)
goto end_rename;
retval = -EMLINK;
- if (!new_inode && new_dir!=old_dir &&
+ if (!new_inode && new_dir != old_dir &&
new_dir->i_nlink >= EXT4_LINK_MAX)
goto end_rename;
}
if (!new_bh) {
- retval = ext4_add_entry (handle, new_dentry, old_inode);
+ retval = ext4_add_entry(handle, new_dentry, old_inode);
if (retval)
goto end_rename;
} else {
@@ -2388,7 +2390,7 @@
struct buffer_head *old_bh2;
struct ext4_dir_entry_2 *old_de2;
- old_bh2 = ext4_find_entry(old_dentry, &old_de2);
+ old_bh2 = ext4_find_entry(old_dir, &old_dentry->d_name, &old_de2);
if (old_bh2) {
retval = ext4_delete_entry(handle, old_dir,
old_de2, old_bh2);
@@ -2433,9 +2435,9 @@
retval = 0;
end_rename:
- brelse (dir_bh);
- brelse (old_bh);
- brelse (new_bh);
+ brelse(dir_bh);
+ brelse(old_bh);
+ brelse(new_bh);
ext4_journal_stop(handle);
return retval;
}
@@ -2454,7 +2456,7 @@
.mknod = ext4_mknod,
.rename = ext4_rename,
.setattr = ext4_setattr,
-#ifdef CONFIG_EXT4DEV_FS_XATTR
+#ifdef CONFIG_EXT4_FS_XATTR
.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
.listxattr = ext4_listxattr,
@@ -2465,7 +2467,7 @@
const struct inode_operations ext4_special_inode_operations = {
.setattr = ext4_setattr,
-#ifdef CONFIG_EXT4DEV_FS_XATTR
+#ifdef CONFIG_EXT4_FS_XATTR
.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
.listxattr = ext4_listxattr,
diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c
index b3d3560..b6ec184 100644
--- a/fs/ext4/resize.c
+++ b/fs/ext4/resize.c
@@ -416,8 +416,8 @@
"EXT4-fs: ext4_add_new_gdb: adding group block %lu\n",
gdb_num);
- /*
- * If we are not using the primary superblock/GDT copy don't resize,
+ /*
+ * If we are not using the primary superblock/GDT copy don't resize,
* because the user tools have no way of handling this. Probably a
* bad time to do it anyways.
*/
@@ -870,11 +870,10 @@
* We can allocate memory for mb_alloc based on the new group
* descriptor
*/
- if (test_opt(sb, MBALLOC)) {
- err = ext4_mb_add_more_groupinfo(sb, input->group, gdp);
- if (err)
- goto exit_journal;
- }
+ err = ext4_mb_add_more_groupinfo(sb, input->group, gdp);
+ if (err)
+ goto exit_journal;
+
/*
* Make the new blocks and inodes valid next. We do this before
* increasing the group count so that once the group is enabled,
@@ -929,6 +928,15 @@
percpu_counter_add(&sbi->s_freeinodes_counter,
EXT4_INODES_PER_GROUP(sb));
+ if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_FLEX_BG)) {
+ ext4_group_t flex_group;
+ flex_group = ext4_flex_group(sbi, input->group);
+ sbi->s_flex_groups[flex_group].free_blocks +=
+ input->free_blocks_count;
+ sbi->s_flex_groups[flex_group].free_inodes +=
+ EXT4_INODES_PER_GROUP(sb);
+ }
+
ext4_journal_dirty_metadata(handle, sbi->s_sbh);
sb->s_dirt = 1;
@@ -964,7 +972,7 @@
ext4_group_t o_groups_count;
ext4_grpblk_t last;
ext4_grpblk_t add;
- struct buffer_head * bh;
+ struct buffer_head *bh;
handle_t *handle;
int err;
unsigned long freed_blocks;
@@ -1077,8 +1085,15 @@
/*
* Mark mballoc pages as not up to date so that they will be updated
* next time they are loaded by ext4_mb_load_buddy.
+ *
+ * XXX Bad, Bad, BAD!!! We should not be overloading the
+ * Uptodate flag, particularly on thte bitmap bh, as way of
+ * hinting to ext4_mb_load_buddy() that it needs to be
+ * overloaded. A user could take a LVM snapshot, then do an
+ * on-line fsck, and clear the uptodate flag, and this would
+ * not be a bug in userspace, but a bug in the kernel. FIXME!!!
*/
- if (test_opt(sb, MBALLOC)) {
+ {
struct ext4_sb_info *sbi = EXT4_SB(sb);
struct inode *inode = sbi->s_buddy_cache;
int blocks_per_page;
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 566344b..dea8f13 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -34,6 +34,8 @@
#include <linux/namei.h>
#include <linux/quotaops.h>
#include <linux/seq_file.h>
+#include <linux/proc_fs.h>
+#include <linux/marker.h>
#include <linux/log2.h>
#include <linux/crc16.h>
#include <asm/uaccess.h>
@@ -45,6 +47,8 @@
#include "namei.h"
#include "group.h"
+struct proc_dir_entry *ext4_proc_root;
+
static int ext4_load_journal(struct super_block *, struct ext4_super_block *,
unsigned long journal_devnum);
static int ext4_create_journal(struct super_block *, struct ext4_super_block *,
@@ -503,15 +507,18 @@
ext4_mb_release(sb);
ext4_ext_release(sb);
ext4_xattr_put_super(sb);
- jbd2_journal_destroy(sbi->s_journal);
+ if (jbd2_journal_destroy(sbi->s_journal) < 0)
+ ext4_abort(sb, __func__, "Couldn't clean up the journal");
sbi->s_journal = NULL;
if (!(sb->s_flags & MS_RDONLY)) {
EXT4_CLEAR_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER);
es->s_state = cpu_to_le16(sbi->s_mount_state);
- BUFFER_TRACE(sbi->s_sbh, "marking dirty");
- mark_buffer_dirty(sbi->s_sbh);
ext4_commit_super(sb, es, 1);
}
+ if (sbi->s_proc) {
+ remove_proc_entry("inode_readahead_blks", sbi->s_proc);
+ remove_proc_entry(sb->s_id, ext4_proc_root);
+ }
for (i = 0; i < sbi->s_gdb_count; i++)
brelse(sbi->s_group_desc[i]);
@@ -520,6 +527,7 @@
percpu_counter_destroy(&sbi->s_freeblocks_counter);
percpu_counter_destroy(&sbi->s_freeinodes_counter);
percpu_counter_destroy(&sbi->s_dirs_counter);
+ percpu_counter_destroy(&sbi->s_dirtyblocks_counter);
brelse(sbi->s_sbh);
#ifdef CONFIG_QUOTA
for (i = 0; i < MAXQUOTAS; i++)
@@ -562,11 +570,10 @@
ei = kmem_cache_alloc(ext4_inode_cachep, GFP_NOFS);
if (!ei)
return NULL;
-#ifdef CONFIG_EXT4DEV_FS_POSIX_ACL
+#ifdef CONFIG_EXT4_FS_POSIX_ACL
ei->i_acl = EXT4_ACL_NOT_CACHED;
ei->i_default_acl = EXT4_ACL_NOT_CACHED;
#endif
- ei->i_block_alloc_info = NULL;
ei->vfs_inode.i_version = 1;
ei->vfs_inode.i_data.writeback_index = 0;
memset(&ei->i_cached_extent, 0, sizeof(struct ext4_ext_cache));
@@ -599,7 +606,7 @@
struct ext4_inode_info *ei = (struct ext4_inode_info *) foo;
INIT_LIST_HEAD(&ei->i_orphan);
-#ifdef CONFIG_EXT4DEV_FS_XATTR
+#ifdef CONFIG_EXT4_FS_XATTR
init_rwsem(&ei->xattr_sem);
#endif
init_rwsem(&ei->i_data_sem);
@@ -625,8 +632,7 @@
static void ext4_clear_inode(struct inode *inode)
{
- struct ext4_block_alloc_info *rsv = EXT4_I(inode)->i_block_alloc_info;
-#ifdef CONFIG_EXT4DEV_FS_POSIX_ACL
+#ifdef CONFIG_EXT4_FS_POSIX_ACL
if (EXT4_I(inode)->i_acl &&
EXT4_I(inode)->i_acl != EXT4_ACL_NOT_CACHED) {
posix_acl_release(EXT4_I(inode)->i_acl);
@@ -638,10 +644,7 @@
EXT4_I(inode)->i_default_acl = EXT4_ACL_NOT_CACHED;
}
#endif
- ext4_discard_reservation(inode);
- EXT4_I(inode)->i_block_alloc_info = NULL;
- if (unlikely(rsv))
- kfree(rsv);
+ ext4_discard_preallocations(inode);
jbd2_journal_release_jbd_inode(EXT4_SB(inode->i_sb)->s_journal,
&EXT4_I(inode)->jinode);
}
@@ -654,7 +657,7 @@
if (sbi->s_jquota_fmt)
seq_printf(seq, ",jqfmt=%s",
- (sbi->s_jquota_fmt == QFMT_VFS_OLD) ? "vfsold": "vfsv0");
+ (sbi->s_jquota_fmt == QFMT_VFS_OLD) ? "vfsold" : "vfsv0");
if (sbi->s_qf_names[USRQUOTA])
seq_printf(seq, ",usrjquota=%s", sbi->s_qf_names[USRQUOTA]);
@@ -718,7 +721,7 @@
seq_puts(seq, ",debug");
if (test_opt(sb, OLDALLOC))
seq_puts(seq, ",oldalloc");
-#ifdef CONFIG_EXT4DEV_FS_XATTR
+#ifdef CONFIG_EXT4_FS_XATTR
if (test_opt(sb, XATTR_USER) &&
!(def_mount_opts & EXT4_DEFM_XATTR_USER))
seq_puts(seq, ",user_xattr");
@@ -727,7 +730,7 @@
seq_puts(seq, ",nouser_xattr");
}
#endif
-#ifdef CONFIG_EXT4DEV_FS_POSIX_ACL
+#ifdef CONFIG_EXT4_FS_POSIX_ACL
if (test_opt(sb, POSIX_ACL) && !(def_mount_opts & EXT4_DEFM_ACL))
seq_puts(seq, ",acl");
if (!test_opt(sb, POSIX_ACL) && (def_mount_opts & EXT4_DEFM_ACL))
@@ -752,8 +755,6 @@
seq_puts(seq, ",nobh");
if (!test_opt(sb, EXTENTS))
seq_puts(seq, ",noextents");
- if (!test_opt(sb, MBALLOC))
- seq_puts(seq, ",nomballoc");
if (test_opt(sb, I_VERSION))
seq_puts(seq, ",i_version");
if (!test_opt(sb, DELALLOC))
@@ -773,6 +774,13 @@
else if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_WRITEBACK_DATA)
seq_puts(seq, ",data=writeback");
+ if (sbi->s_inode_readahead_blks != EXT4_DEF_INODE_READAHEAD_BLKS)
+ seq_printf(seq, ",inode_readahead_blks=%u",
+ sbi->s_inode_readahead_blks);
+
+ if (test_opt(sb, DATA_ERR_ABORT))
+ seq_puts(seq, ",data_err=abort");
+
ext4_show_quota_options(seq, sb);
return 0;
}
@@ -822,7 +830,7 @@
}
#ifdef CONFIG_QUOTA
-#define QTYPE2NAME(t) ((t) == USRQUOTA?"user":"group")
+#define QTYPE2NAME(t) ((t) == USRQUOTA ? "user" : "group")
#define QTYPE2MOPT(on, t) ((t) == USRQUOTA?((on)##USRJQUOTA):((on)##GRPJQUOTA))
static int ext4_dquot_initialize(struct inode *inode, int type);
@@ -902,14 +910,16 @@
Opt_commit, Opt_journal_update, Opt_journal_inum, Opt_journal_dev,
Opt_journal_checksum, Opt_journal_async_commit,
Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback,
+ Opt_data_err_abort, Opt_data_err_ignore,
Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota,
Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_quota, Opt_noquota,
Opt_ignore, Opt_barrier, Opt_err, Opt_resize, Opt_usrquota,
Opt_grpquota, Opt_extents, Opt_noextents, Opt_i_version,
Opt_mballoc, Opt_nomballoc, Opt_stripe, Opt_delalloc, Opt_nodelalloc,
+ Opt_inode_readahead_blks
};
-static match_table_t tokens = {
+static const match_table_t tokens = {
{Opt_bsd_df, "bsddf"},
{Opt_minix_df, "minixdf"},
{Opt_grpid, "grpid"},
@@ -947,6 +957,8 @@
{Opt_data_journal, "data=journal"},
{Opt_data_ordered, "data=ordered"},
{Opt_data_writeback, "data=writeback"},
+ {Opt_data_err_abort, "data_err=abort"},
+ {Opt_data_err_ignore, "data_err=ignore"},
{Opt_offusrjquota, "usrjquota="},
{Opt_usrjquota, "usrjquota=%s"},
{Opt_offgrpjquota, "grpjquota="},
@@ -967,6 +979,7 @@
{Opt_resize, "resize"},
{Opt_delalloc, "delalloc"},
{Opt_nodelalloc, "nodelalloc"},
+ {Opt_inode_readahead_blks, "inode_readahead_blks=%u"},
{Opt_err, NULL},
};
@@ -981,7 +994,7 @@
/*todo: use simple_strtoll with >32bit ext4 */
sb_block = simple_strtoul(options, &options, 0);
if (*options && *options != ',') {
- printk("EXT4-fs: Invalid sb specification: %s\n",
+ printk(KERN_ERR "EXT4-fs: Invalid sb specification: %s\n",
(char *) *data);
return 1;
}
@@ -1072,7 +1085,7 @@
case Opt_orlov:
clear_opt(sbi->s_mount_opt, OLDALLOC);
break;
-#ifdef CONFIG_EXT4DEV_FS_XATTR
+#ifdef CONFIG_EXT4_FS_XATTR
case Opt_user_xattr:
set_opt(sbi->s_mount_opt, XATTR_USER);
break;
@@ -1082,10 +1095,11 @@
#else
case Opt_user_xattr:
case Opt_nouser_xattr:
- printk("EXT4 (no)user_xattr options not supported\n");
+ printk(KERN_ERR "EXT4 (no)user_xattr options "
+ "not supported\n");
break;
#endif
-#ifdef CONFIG_EXT4DEV_FS_POSIX_ACL
+#ifdef CONFIG_EXT4_FS_POSIX_ACL
case Opt_acl:
set_opt(sbi->s_mount_opt, POSIX_ACL);
break;
@@ -1095,7 +1109,8 @@
#else
case Opt_acl:
case Opt_noacl:
- printk("EXT4 (no)acl options not supported\n");
+ printk(KERN_ERR "EXT4 (no)acl options "
+ "not supported\n");
break;
#endif
case Opt_reservation:
@@ -1178,6 +1193,12 @@
sbi->s_mount_opt |= data_opt;
}
break;
+ case Opt_data_err_abort:
+ set_opt(sbi->s_mount_opt, DATA_ERR_ABORT);
+ break;
+ case Opt_data_err_ignore:
+ clear_opt(sbi->s_mount_opt, DATA_ERR_ABORT);
+ break;
#ifdef CONFIG_QUOTA
case Opt_usrjquota:
qtype = USRQUOTA;
@@ -1189,8 +1210,8 @@
sb_any_quota_suspended(sb)) &&
!sbi->s_qf_names[qtype]) {
printk(KERN_ERR
- "EXT4-fs: Cannot change journaled "
- "quota options when quota turned on.\n");
+ "EXT4-fs: Cannot change journaled "
+ "quota options when quota turned on.\n");
return 0;
}
qname = match_strdup(&args[0]);
@@ -1357,12 +1378,6 @@
case Opt_nodelalloc:
clear_opt(sbi->s_mount_opt, DELALLOC);
break;
- case Opt_mballoc:
- set_opt(sbi->s_mount_opt, MBALLOC);
- break;
- case Opt_nomballoc:
- clear_opt(sbi->s_mount_opt, MBALLOC);
- break;
case Opt_stripe:
if (match_int(&args[0], &option))
return 0;
@@ -1373,6 +1388,13 @@
case Opt_delalloc:
set_opt(sbi->s_mount_opt, DELALLOC);
break;
+ case Opt_inode_readahead_blks:
+ if (match_int(&args[0], &option))
+ return 0;
+ if (option < 0 || option > (1 << 30))
+ return 0;
+ sbi->s_inode_readahead_blks = option;
+ break;
default:
printk(KERN_ERR
"EXT4-fs: Unrecognized mount option \"%s\" "
@@ -1473,15 +1495,9 @@
EXT4_INODES_PER_GROUP(sb),
sbi->s_mount_opt);
- printk(KERN_INFO "EXT4 FS on %s, ", sb->s_id);
- if (EXT4_SB(sb)->s_journal->j_inode == NULL) {
- char b[BDEVNAME_SIZE];
-
- printk("external journal on %s\n",
- bdevname(EXT4_SB(sb)->s_journal->j_dev, b));
- } else {
- printk("internal journal\n");
- }
+ printk(KERN_INFO "EXT4 FS on %s, %s journal on %s\n",
+ sb->s_id, EXT4_SB(sb)->s_journal->j_inode ? "internal" :
+ "external", EXT4_SB(sb)->s_journal->j_devname);
return res;
}
@@ -1504,8 +1520,11 @@
sbi->s_log_groups_per_flex = sbi->s_es->s_log_groups_per_flex;
groups_per_flex = 1 << sbi->s_log_groups_per_flex;
- flex_group_count = (sbi->s_groups_count + groups_per_flex - 1) /
- groups_per_flex;
+ /* We allocate both existing and potentially added groups */
+ flex_group_count = ((sbi->s_groups_count + groups_per_flex - 1) +
+ ((sbi->s_es->s_reserved_gdt_blocks +1 ) <<
+ EXT4_DESC_PER_BLOCK_BITS(sb))) /
+ groups_per_flex;
sbi->s_flex_groups = kzalloc(flex_group_count *
sizeof(struct flex_groups), GFP_KERNEL);
if (sbi->s_flex_groups == NULL) {
@@ -1584,7 +1603,7 @@
if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_FLEX_BG))
flexbg_flag = 1;
- ext4_debug ("Checking group descriptors");
+ ext4_debug("Checking group descriptors");
for (i = 0; i < sbi->s_groups_count; i++) {
struct ext4_group_desc *gdp = ext4_get_group_desc(sb, i, NULL);
@@ -1623,8 +1642,10 @@
"Checksum for group %lu failed (%u!=%u)\n",
i, le16_to_cpu(ext4_group_desc_csum(sbi, i,
gdp)), le16_to_cpu(gdp->bg_checksum));
- if (!(sb->s_flags & MS_RDONLY))
+ if (!(sb->s_flags & MS_RDONLY)) {
+ spin_unlock(sb_bgl_lock(sbi, i));
return 0;
+ }
}
spin_unlock(sb_bgl_lock(sbi, i));
if (!flexbg_flag)
@@ -1714,9 +1735,9 @@
DQUOT_INIT(inode);
if (inode->i_nlink) {
printk(KERN_DEBUG
- "%s: truncating inode %lu to %Ld bytes\n",
+ "%s: truncating inode %lu to %lld bytes\n",
__func__, inode->i_ino, inode->i_size);
- jbd_debug(2, "truncating inode %lu to %Ld bytes\n",
+ jbd_debug(2, "truncating inode %lu to %lld bytes\n",
inode->i_ino, inode->i_size);
ext4_truncate(inode);
nr_truncates++;
@@ -1914,6 +1935,7 @@
unsigned long journal_devnum = 0;
unsigned long def_mount_opts;
struct inode *root;
+ char *cp;
int ret = -EINVAL;
int blocksize;
int db_count;
@@ -1930,10 +1952,15 @@
sbi->s_mount_opt = 0;
sbi->s_resuid = EXT4_DEF_RESUID;
sbi->s_resgid = EXT4_DEF_RESGID;
+ sbi->s_inode_readahead_blks = EXT4_DEF_INODE_READAHEAD_BLKS;
sbi->s_sb_block = sb_block;
unlock_kernel();
+ /* Cleanup superblock name */
+ for (cp = sb->s_id; (cp = strchr(cp, '/'));)
+ *cp = '!';
+
blocksize = sb_min_blocksize(sb, EXT4_MIN_BLOCK_SIZE);
if (!blocksize) {
printk(KERN_ERR "EXT4-fs: unable to set blocksize\n");
@@ -1973,11 +2000,11 @@
set_opt(sbi->s_mount_opt, GRPID);
if (def_mount_opts & EXT4_DEFM_UID16)
set_opt(sbi->s_mount_opt, NO_UID32);
-#ifdef CONFIG_EXT4DEV_FS_XATTR
+#ifdef CONFIG_EXT4_FS_XATTR
if (def_mount_opts & EXT4_DEFM_XATTR_USER)
set_opt(sbi->s_mount_opt, XATTR_USER);
#endif
-#ifdef CONFIG_EXT4DEV_FS_POSIX_ACL
+#ifdef CONFIG_EXT4_FS_POSIX_ACL
if (def_mount_opts & EXT4_DEFM_ACL)
set_opt(sbi->s_mount_opt, POSIX_ACL);
#endif
@@ -2012,11 +2039,6 @@
ext4_warning(sb, __func__,
"extents feature not enabled on this filesystem, "
"use tune2fs.\n");
- /*
- * turn on mballoc code by default in ext4 filesystem
- * Use -o nomballoc to turn it off
- */
- set_opt(sbi->s_mount_opt, MBALLOC);
/*
* enable delayed allocation by default
@@ -2041,16 +2063,6 @@
"running e2fsck is recommended\n");
/*
- * Since ext4 is still considered development code, we require
- * that the TEST_FILESYS flag in s->flags be set.
- */
- if (!(le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS)) {
- printk(KERN_WARNING "EXT4-fs: %s: not marked "
- "OK to use with test code.\n", sb->s_id);
- goto failed_mount;
- }
-
- /*
* Check feature flags regardless of the revision level, since we
* previously didn't change the revision level when setting the flags,
* so there is a chance incompat flags are set on a rev 0 filesystem.
@@ -2219,6 +2231,16 @@
goto failed_mount;
}
+#ifdef CONFIG_PROC_FS
+ if (ext4_proc_root)
+ sbi->s_proc = proc_mkdir(sb->s_id, ext4_proc_root);
+
+ if (sbi->s_proc)
+ proc_create_data("inode_readahead_blks", 0644, sbi->s_proc,
+ &ext4_ui_proc_fops,
+ &sbi->s_inode_readahead_blks);
+#endif
+
bgl_lock_init(&sbi->s_blockgroup_lock);
for (i = 0; i < db_count; i++) {
@@ -2257,24 +2279,14 @@
err = percpu_counter_init(&sbi->s_dirs_counter,
ext4_count_dirs(sb));
}
+ if (!err) {
+ err = percpu_counter_init(&sbi->s_dirtyblocks_counter, 0);
+ }
if (err) {
printk(KERN_ERR "EXT4-fs: insufficient memory\n");
goto failed_mount3;
}
- /* per fileystem reservation list head & lock */
- spin_lock_init(&sbi->s_rsv_window_lock);
- sbi->s_rsv_window_root = RB_ROOT;
- /* Add a single, static dummy reservation to the start of the
- * reservation window list --- it gives us a placeholder for
- * append-at-start-of-list which makes the allocation logic
- * _much_ simpler. */
- sbi->s_rsv_window_head.rsv_start = EXT4_RESERVE_WINDOW_NOT_ALLOCATED;
- sbi->s_rsv_window_head.rsv_end = EXT4_RESERVE_WINDOW_NOT_ALLOCATED;
- sbi->s_rsv_window_head.rsv_alloc_hit = 0;
- sbi->s_rsv_window_head.rsv_goal_size = 0;
- ext4_rsv_window_add(sb, &sbi->s_rsv_window_head);
-
sbi->s_stripe = ext4_get_stripe_size(sbi);
/*
@@ -2471,7 +2483,12 @@
printk(KERN_INFO "EXT4-fs: delayed allocation enabled\n");
ext4_ext_init(sb);
- ext4_mb_init(sb, needs_recovery);
+ err = ext4_mb_init(sb, needs_recovery);
+ if (err) {
+ printk(KERN_ERR "EXT4-fs: failed to initalize mballoc (%d)\n",
+ err);
+ goto failed_mount4;
+ }
lock_kernel();
return 0;
@@ -2489,11 +2506,16 @@
percpu_counter_destroy(&sbi->s_freeblocks_counter);
percpu_counter_destroy(&sbi->s_freeinodes_counter);
percpu_counter_destroy(&sbi->s_dirs_counter);
+ percpu_counter_destroy(&sbi->s_dirtyblocks_counter);
failed_mount2:
for (i = 0; i < db_count; i++)
brelse(sbi->s_group_desc[i]);
kfree(sbi->s_group_desc);
failed_mount:
+ if (sbi->s_proc) {
+ remove_proc_entry("inode_readahead_blks", sbi->s_proc);
+ remove_proc_entry(sb->s_id, ext4_proc_root);
+ }
#ifdef CONFIG_QUOTA
for (i = 0; i < MAXQUOTAS; i++)
kfree(sbi->s_qf_names[i]);
@@ -2527,6 +2549,10 @@
journal->j_flags |= JBD2_BARRIER;
else
journal->j_flags &= ~JBD2_BARRIER;
+ if (test_opt(sb, DATA_ERR_ABORT))
+ journal->j_flags |= JBD2_ABORT_ON_SYNCDATA_ERR;
+ else
+ journal->j_flags &= ~JBD2_ABORT_ON_SYNCDATA_ERR;
spin_unlock(&journal->j_state_lock);
}
@@ -2552,7 +2578,7 @@
return NULL;
}
- jbd_debug(2, "Journal inode found at %p: %Ld bytes\n",
+ jbd_debug(2, "Journal inode found at %p: %lld bytes\n",
journal_inode, journal_inode->i_size);
if (!S_ISREG(journal_inode->i_mode)) {
printk(KERN_ERR "EXT4-fs: invalid journal inode.\n");
@@ -2715,6 +2741,11 @@
return -EINVAL;
}
+ if (journal->j_flags & JBD2_BARRIER)
+ printk(KERN_INFO "EXT4-fs: barriers enabled\n");
+ else
+ printk(KERN_INFO "EXT4-fs: barriers disabled\n");
+
if (!really_read_only && test_opt(sb, UPDATE_JOURNAL)) {
err = jbd2_journal_update_format(journal);
if (err) {
@@ -2799,13 +2830,34 @@
if (!sbh)
return;
+ if (buffer_write_io_error(sbh)) {
+ /*
+ * Oh, dear. A previous attempt to write the
+ * superblock failed. This could happen because the
+ * USB device was yanked out. Or it could happen to
+ * be a transient write error and maybe the block will
+ * be remapped. Nothing we can do but to retry the
+ * write and hope for the best.
+ */
+ printk(KERN_ERR "ext4: previous I/O error to "
+ "superblock detected for %s.\n", sb->s_id);
+ clear_buffer_write_io_error(sbh);
+ set_buffer_uptodate(sbh);
+ }
es->s_wtime = cpu_to_le32(get_seconds());
ext4_free_blocks_count_set(es, ext4_count_free_blocks(sb));
es->s_free_inodes_count = cpu_to_le32(ext4_count_free_inodes(sb));
BUFFER_TRACE(sbh, "marking dirty");
mark_buffer_dirty(sbh);
- if (sync)
+ if (sync) {
sync_dirty_buffer(sbh);
+ if (buffer_write_io_error(sbh)) {
+ printk(KERN_ERR "ext4: I/O error while writing "
+ "superblock for %s.\n", sb->s_id);
+ clear_buffer_write_io_error(sbh);
+ set_buffer_uptodate(sbh);
+ }
+ }
}
@@ -2820,7 +2872,9 @@
journal_t *journal = EXT4_SB(sb)->s_journal;
jbd2_journal_lock_updates(journal);
- jbd2_journal_flush(journal);
+ if (jbd2_journal_flush(journal) < 0)
+ goto out;
+
lock_super(sb);
if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER) &&
sb->s_flags & MS_RDONLY) {
@@ -2829,6 +2883,8 @@
ext4_commit_super(sb, es, 1);
}
unlock_super(sb);
+
+out:
jbd2_journal_unlock_updates(journal);
}
@@ -2907,6 +2963,7 @@
{
tid_t target;
+ trace_mark(ext4_sync_fs, "dev %s wait %d", sb->s_id, wait);
sb->s_dirt = 0;
if (jbd2_journal_start_commit(EXT4_SB(sb)->s_journal, &target)) {
if (wait)
@@ -2928,7 +2985,13 @@
/* Now we set up the journal barrier. */
jbd2_journal_lock_updates(journal);
- jbd2_journal_flush(journal);
+
+ /*
+ * We don't want to clear needs_recovery flag when we failed
+ * to flush the journal.
+ */
+ if (jbd2_journal_flush(journal) < 0)
+ return;
/* Journal blocked and flushed, clear needs_recovery flag. */
EXT4_CLEAR_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER);
@@ -3162,7 +3225,8 @@
buf->f_type = EXT4_SUPER_MAGIC;
buf->f_bsize = sb->s_blocksize;
buf->f_blocks = ext4_blocks_count(es) - sbi->s_overhead_last;
- buf->f_bfree = percpu_counter_sum_positive(&sbi->s_freeblocks_counter);
+ buf->f_bfree = percpu_counter_sum_positive(&sbi->s_freeblocks_counter) -
+ percpu_counter_sum_positive(&sbi->s_dirtyblocks_counter);
ext4_free_blocks_count_set(es, buf->f_bfree);
buf->f_bavail = buf->f_bfree - ext4_r_blocks_count(es);
if (buf->f_bfree < ext4_r_blocks_count(es))
@@ -3367,8 +3431,12 @@
* otherwise be livelocked...
*/
jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal);
- jbd2_journal_flush(EXT4_SB(sb)->s_journal);
+ err = jbd2_journal_flush(EXT4_SB(sb)->s_journal);
jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
+ if (err) {
+ path_put(&nd.path);
+ return err;
+ }
}
err = vfs_quota_on_path(sb, type, format_id, &nd.path);
@@ -3432,7 +3500,7 @@
handle_t *handle = journal_current_handle();
if (!handle) {
- printk(KERN_WARNING "EXT4-fs: Quota write (off=%Lu, len=%Lu)"
+ printk(KERN_WARNING "EXT4-fs: Quota write (off=%llu, len=%llu)"
" cancelled because transaction is not started.\n",
(unsigned long long)off, (unsigned long long)len);
return -EIO;
@@ -3493,18 +3561,82 @@
return get_sb_bdev(fs_type, flags, dev_name, data, ext4_fill_super, mnt);
}
-static struct file_system_type ext4dev_fs_type = {
+#ifdef CONFIG_PROC_FS
+static int ext4_ui_proc_show(struct seq_file *m, void *v)
+{
+ unsigned int *p = m->private;
+
+ seq_printf(m, "%u\n", *p);
+ return 0;
+}
+
+static int ext4_ui_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ext4_ui_proc_show, PDE(inode)->data);
+}
+
+static ssize_t ext4_ui_proc_write(struct file *file, const char __user *buf,
+ size_t cnt, loff_t *ppos)
+{
+ unsigned int *p = PDE(file->f_path.dentry->d_inode)->data;
+ char str[32];
+ unsigned long value;
+
+ if (cnt >= sizeof(str))
+ return -EINVAL;
+ if (copy_from_user(str, buf, cnt))
+ return -EFAULT;
+ value = simple_strtol(str, NULL, 0);
+ if (value < 0)
+ return -ERANGE;
+ *p = value;
+ return cnt;
+}
+
+const struct file_operations ext4_ui_proc_fops = {
.owner = THIS_MODULE,
- .name = "ext4dev",
+ .open = ext4_ui_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = ext4_ui_proc_write,
+};
+#endif
+
+static struct file_system_type ext4_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "ext4",
.get_sb = ext4_get_sb,
.kill_sb = kill_block_super,
.fs_flags = FS_REQUIRES_DEV,
};
+#ifdef CONFIG_EXT4DEV_COMPAT
+static int ext4dev_get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *data, struct vfsmount *mnt)
+{
+ printk(KERN_WARNING "EXT4-fs: Update your userspace programs "
+ "to mount using ext4\n");
+ printk(KERN_WARNING "EXT4-fs: ext4dev backwards compatibility "
+ "will go away by 2.6.31\n");
+ return get_sb_bdev(fs_type, flags, dev_name, data, ext4_fill_super, mnt);
+}
+
+static struct file_system_type ext4dev_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "ext4dev",
+ .get_sb = ext4dev_get_sb,
+ .kill_sb = kill_block_super,
+ .fs_flags = FS_REQUIRES_DEV,
+};
+MODULE_ALIAS("ext4dev");
+#endif
+
static int __init init_ext4_fs(void)
{
int err;
+ ext4_proc_root = proc_mkdir("fs/ext4", NULL);
err = init_ext4_mballoc();
if (err)
return err;
@@ -3515,9 +3647,16 @@
err = init_inodecache();
if (err)
goto out1;
- err = register_filesystem(&ext4dev_fs_type);
+ err = register_filesystem(&ext4_fs_type);
if (err)
goto out;
+#ifdef CONFIG_EXT4DEV_COMPAT
+ err = register_filesystem(&ext4dev_fs_type);
+ if (err) {
+ unregister_filesystem(&ext4_fs_type);
+ goto out;
+ }
+#endif
return 0;
out:
destroy_inodecache();
@@ -3530,10 +3669,14 @@
static void __exit exit_ext4_fs(void)
{
+ unregister_filesystem(&ext4_fs_type);
+#ifdef CONFIG_EXT4DEV_COMPAT
unregister_filesystem(&ext4dev_fs_type);
+#endif
destroy_inodecache();
exit_ext4_xattr();
exit_ext4_mballoc();
+ remove_proc_entry("fs/ext4", NULL);
}
MODULE_AUTHOR("Remy Card, Stephen Tweedie, Andrew Morton, Andreas Dilger, Theodore Ts'o and others");
diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c
index e917864..00740cb 100644
--- a/fs/ext4/symlink.c
+++ b/fs/ext4/symlink.c
@@ -23,10 +23,10 @@
#include "ext4.h"
#include "xattr.h"
-static void * ext4_follow_link(struct dentry *dentry, struct nameidata *nd)
+static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd)
{
struct ext4_inode_info *ei = EXT4_I(dentry->d_inode);
- nd_set_link(nd, (char*)ei->i_data);
+ nd_set_link(nd, (char *) ei->i_data);
return NULL;
}
@@ -34,7 +34,7 @@
.readlink = generic_readlink,
.follow_link = page_follow_link_light,
.put_link = page_put_link,
-#ifdef CONFIG_EXT4DEV_FS_XATTR
+#ifdef CONFIG_EXT4_FS_XATTR
.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
.listxattr = ext4_listxattr,
@@ -45,7 +45,7 @@
const struct inode_operations ext4_fast_symlink_inode_operations = {
.readlink = generic_readlink,
.follow_link = ext4_follow_link,
-#ifdef CONFIG_EXT4DEV_FS_XATTR
+#ifdef CONFIG_EXT4_FS_XATTR
.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
.listxattr = ext4_listxattr,
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index 8954208..80626d5 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -99,12 +99,12 @@
static struct xattr_handler *ext4_xattr_handler_map[] = {
[EXT4_XATTR_INDEX_USER] = &ext4_xattr_user_handler,
-#ifdef CONFIG_EXT4DEV_FS_POSIX_ACL
+#ifdef CONFIG_EXT4_FS_POSIX_ACL
[EXT4_XATTR_INDEX_POSIX_ACL_ACCESS] = &ext4_xattr_acl_access_handler,
[EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT] = &ext4_xattr_acl_default_handler,
#endif
[EXT4_XATTR_INDEX_TRUSTED] = &ext4_xattr_trusted_handler,
-#ifdef CONFIG_EXT4DEV_FS_SECURITY
+#ifdef CONFIG_EXT4_FS_SECURITY
[EXT4_XATTR_INDEX_SECURITY] = &ext4_xattr_security_handler,
#endif
};
@@ -112,11 +112,11 @@
struct xattr_handler *ext4_xattr_handlers[] = {
&ext4_xattr_user_handler,
&ext4_xattr_trusted_handler,
-#ifdef CONFIG_EXT4DEV_FS_POSIX_ACL
+#ifdef CONFIG_EXT4_FS_POSIX_ACL
&ext4_xattr_acl_access_handler,
&ext4_xattr_acl_default_handler,
#endif
-#ifdef CONFIG_EXT4DEV_FS_SECURITY
+#ifdef CONFIG_EXT4_FS_SECURITY
&ext4_xattr_security_handler,
#endif
NULL
@@ -959,6 +959,7 @@
struct ext4_xattr_block_find bs = {
.s = { .not_found = -ENODATA, },
};
+ unsigned long no_expand;
int error;
if (!name)
@@ -966,6 +967,9 @@
if (strlen(name) > 255)
return -ERANGE;
down_write(&EXT4_I(inode)->xattr_sem);
+ no_expand = EXT4_I(inode)->i_state & EXT4_STATE_NO_EXPAND;
+ EXT4_I(inode)->i_state |= EXT4_STATE_NO_EXPAND;
+
error = ext4_get_inode_loc(inode, &is.iloc);
if (error)
goto cleanup;
@@ -1042,6 +1046,8 @@
cleanup:
brelse(is.iloc.bh);
brelse(bs.bh);
+ if (no_expand == 0)
+ EXT4_I(inode)->i_state &= ~EXT4_STATE_NO_EXPAND;
up_write(&EXT4_I(inode)->xattr_sem);
return error;
}
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index 5992fe9..8ede88b 100644
--- a/fs/ext4/xattr.h
+++ b/fs/ext4/xattr.h
@@ -51,8 +51,8 @@
(((name_len) + EXT4_XATTR_ROUND + \
sizeof(struct ext4_xattr_entry)) & ~EXT4_XATTR_ROUND)
#define EXT4_XATTR_NEXT(entry) \
- ( (struct ext4_xattr_entry *)( \
- (char *)(entry) + EXT4_XATTR_LEN((entry)->e_name_len)) )
+ ((struct ext4_xattr_entry *)( \
+ (char *)(entry) + EXT4_XATTR_LEN((entry)->e_name_len)))
#define EXT4_XATTR_SIZE(size) \
(((size) + EXT4_XATTR_ROUND) & ~EXT4_XATTR_ROUND)
@@ -63,7 +63,7 @@
EXT4_I(inode)->i_extra_isize))
#define IFIRST(hdr) ((struct ext4_xattr_entry *)((hdr)+1))
-# ifdef CONFIG_EXT4DEV_FS_XATTR
+# ifdef CONFIG_EXT4_FS_XATTR
extern struct xattr_handler ext4_xattr_user_handler;
extern struct xattr_handler ext4_xattr_trusted_handler;
@@ -88,7 +88,7 @@
extern struct xattr_handler *ext4_xattr_handlers[];
-# else /* CONFIG_EXT4DEV_FS_XATTR */
+# else /* CONFIG_EXT4_FS_XATTR */
static inline int
ext4_xattr_get(struct inode *inode, int name_index, const char *name,
@@ -141,9 +141,9 @@
#define ext4_xattr_handlers NULL
-# endif /* CONFIG_EXT4DEV_FS_XATTR */
+# endif /* CONFIG_EXT4_FS_XATTR */
-#ifdef CONFIG_EXT4DEV_FS_SECURITY
+#ifdef CONFIG_EXT4_FS_SECURITY
extern int ext4_init_security(handle_t *handle, struct inode *inode,
struct inode *dir);
#else
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index 80ff338..d12cdf2 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -855,7 +855,7 @@
Opt_obsolate, Opt_flush, Opt_tz_utc, Opt_err,
};
-static match_table_t fat_tokens = {
+static const match_table_t fat_tokens = {
{Opt_check_r, "check=relaxed"},
{Opt_check_s, "check=strict"},
{Opt_check_n, "check=normal"},
@@ -890,14 +890,14 @@
{Opt_tz_utc, "tz=UTC"},
{Opt_err, NULL},
};
-static match_table_t msdos_tokens = {
+static const match_table_t msdos_tokens = {
{Opt_nodots, "nodots"},
{Opt_nodots, "dotsOK=no"},
{Opt_dots, "dots"},
{Opt_dots, "dotsOK=yes"},
{Opt_err, NULL}
};
-static match_table_t vfat_tokens = {
+static const match_table_t vfat_tokens = {
{Opt_charset, "iocharset=%s"},
{Opt_shortname_lower, "shortname=lower"},
{Opt_shortname_win95, "shortname=win95"},
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index d2249f1..6a84388 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -354,7 +354,7 @@
OPT_ERR
};
-static match_table_t tokens = {
+static const match_table_t tokens = {
{OPT_FD, "fd=%u"},
{OPT_ROOTMODE, "rootmode=%o"},
{OPT_USER_ID, "user_id=%u"},
diff --git a/fs/gfs2/mount.c b/fs/gfs2/mount.c
index df48333..f96eb90 100644
--- a/fs/gfs2/mount.c
+++ b/fs/gfs2/mount.c
@@ -46,7 +46,7 @@
Opt_err,
};
-static match_table_t tokens = {
+static const match_table_t tokens = {
{Opt_lockproto, "lockproto=%s"},
{Opt_locktable, "locktable=%s"},
{Opt_hostdata, "hostdata=%s"},
diff --git a/fs/hfs/super.c b/fs/hfs/super.c
index 4abb104..3c7c763 100644
--- a/fs/hfs/super.c
+++ b/fs/hfs/super.c
@@ -173,7 +173,7 @@
opt_err
};
-static match_table_t tokens = {
+static const match_table_t tokens = {
{ opt_uid, "uid=%u" },
{ opt_gid, "gid=%u" },
{ opt_umask, "umask=%o" },
diff --git a/fs/hfsplus/options.c b/fs/hfsplus/options.c
index 9997cbf..9699c56 100644
--- a/fs/hfsplus/options.c
+++ b/fs/hfsplus/options.c
@@ -25,7 +25,7 @@
opt_force, opt_err
};
-static match_table_t tokens = {
+static const match_table_t tokens = {
{ opt_creator, "creator=%s" },
{ opt_type, "type=%s" },
{ opt_umask, "umask=%o" },
diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c
index b8ae9c9..29ad461 100644
--- a/fs/hpfs/super.c
+++ b/fs/hpfs/super.c
@@ -215,7 +215,7 @@
Opt_timeshift, Opt_err,
};
-static match_table_t tokens = {
+static const match_table_t tokens = {
{Opt_help, "help"},
{Opt_uid, "uid=%u"},
{Opt_gid, "gid=%u"},
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index 3f58923..61edc70 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -57,7 +57,7 @@
Opt_err,
};
-static match_table_t tokens = {
+static const match_table_t tokens = {
{Opt_size, "size=%s"},
{Opt_nr_inodes, "nr_inodes=%s"},
{Opt_mode, "mode=%o"},
diff --git a/fs/ioctl.c b/fs/ioctl.c
index 7db32b3..d152856 100644
--- a/fs/ioctl.c
+++ b/fs/ioctl.c
@@ -13,9 +13,14 @@
#include <linux/security.h>
#include <linux/module.h>
#include <linux/uaccess.h>
+#include <linux/writeback.h>
+#include <linux/buffer_head.h>
#include <asm/ioctls.h>
+/* So that the fiemap access checks can't overflow on 32 bit machines. */
+#define FIEMAP_MAX_EXTENTS (UINT_MAX / sizeof(struct fiemap_extent))
+
/**
* vfs_ioctl - call filesystem specific ioctl methods
* @filp: open file to invoke ioctl method on
@@ -71,6 +76,276 @@
return put_user(res, p);
}
+/**
+ * fiemap_fill_next_extent - Fiemap helper function
+ * @fieinfo: Fiemap context passed into ->fiemap
+ * @logical: Extent logical start offset, in bytes
+ * @phys: Extent physical start offset, in bytes
+ * @len: Extent length, in bytes
+ * @flags: FIEMAP_EXTENT flags that describe this extent
+ *
+ * Called from file system ->fiemap callback. Will populate extent
+ * info as passed in via arguments and copy to user memory. On
+ * success, extent count on fieinfo is incremented.
+ *
+ * Returns 0 on success, -errno on error, 1 if this was the last
+ * extent that will fit in user array.
+ */
+#define SET_UNKNOWN_FLAGS (FIEMAP_EXTENT_DELALLOC)
+#define SET_NO_UNMOUNTED_IO_FLAGS (FIEMAP_EXTENT_DATA_ENCRYPTED)
+#define SET_NOT_ALIGNED_FLAGS (FIEMAP_EXTENT_DATA_TAIL|FIEMAP_EXTENT_DATA_INLINE)
+int fiemap_fill_next_extent(struct fiemap_extent_info *fieinfo, u64 logical,
+ u64 phys, u64 len, u32 flags)
+{
+ struct fiemap_extent extent;
+ struct fiemap_extent *dest = fieinfo->fi_extents_start;
+
+ /* only count the extents */
+ if (fieinfo->fi_extents_max == 0) {
+ fieinfo->fi_extents_mapped++;
+ return (flags & FIEMAP_EXTENT_LAST) ? 1 : 0;
+ }
+
+ if (fieinfo->fi_extents_mapped >= fieinfo->fi_extents_max)
+ return 1;
+
+ if (flags & SET_UNKNOWN_FLAGS)
+ flags |= FIEMAP_EXTENT_UNKNOWN;
+ if (flags & SET_NO_UNMOUNTED_IO_FLAGS)
+ flags |= FIEMAP_EXTENT_ENCODED;
+ if (flags & SET_NOT_ALIGNED_FLAGS)
+ flags |= FIEMAP_EXTENT_NOT_ALIGNED;
+
+ memset(&extent, 0, sizeof(extent));
+ extent.fe_logical = logical;
+ extent.fe_physical = phys;
+ extent.fe_length = len;
+ extent.fe_flags = flags;
+
+ dest += fieinfo->fi_extents_mapped;
+ if (copy_to_user(dest, &extent, sizeof(extent)))
+ return -EFAULT;
+
+ fieinfo->fi_extents_mapped++;
+ if (fieinfo->fi_extents_mapped == fieinfo->fi_extents_max)
+ return 1;
+ return (flags & FIEMAP_EXTENT_LAST) ? 1 : 0;
+}
+EXPORT_SYMBOL(fiemap_fill_next_extent);
+
+/**
+ * fiemap_check_flags - check validity of requested flags for fiemap
+ * @fieinfo: Fiemap context passed into ->fiemap
+ * @fs_flags: Set of fiemap flags that the file system understands
+ *
+ * Called from file system ->fiemap callback. This will compute the
+ * intersection of valid fiemap flags and those that the fs supports. That
+ * value is then compared against the user supplied flags. In case of bad user
+ * flags, the invalid values will be written into the fieinfo structure, and
+ * -EBADR is returned, which tells ioctl_fiemap() to return those values to
+ * userspace. For this reason, a return code of -EBADR should be preserved.
+ *
+ * Returns 0 on success, -EBADR on bad flags.
+ */
+int fiemap_check_flags(struct fiemap_extent_info *fieinfo, u32 fs_flags)
+{
+ u32 incompat_flags;
+
+ incompat_flags = fieinfo->fi_flags & ~(FIEMAP_FLAGS_COMPAT & fs_flags);
+ if (incompat_flags) {
+ fieinfo->fi_flags = incompat_flags;
+ return -EBADR;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(fiemap_check_flags);
+
+static int fiemap_check_ranges(struct super_block *sb,
+ u64 start, u64 len, u64 *new_len)
+{
+ *new_len = len;
+
+ if (len == 0)
+ return -EINVAL;
+
+ if (start > sb->s_maxbytes)
+ return -EFBIG;
+
+ /*
+ * Shrink request scope to what the fs can actually handle.
+ */
+ if ((len > sb->s_maxbytes) ||
+ (sb->s_maxbytes - len) < start)
+ *new_len = sb->s_maxbytes - start;
+
+ return 0;
+}
+
+static int ioctl_fiemap(struct file *filp, unsigned long arg)
+{
+ struct fiemap fiemap;
+ struct fiemap_extent_info fieinfo = { 0, };
+ struct inode *inode = filp->f_path.dentry->d_inode;
+ struct super_block *sb = inode->i_sb;
+ u64 len;
+ int error;
+
+ if (!inode->i_op->fiemap)
+ return -EOPNOTSUPP;
+
+ if (copy_from_user(&fiemap, (struct fiemap __user *)arg,
+ sizeof(struct fiemap)))
+ return -EFAULT;
+
+ if (fiemap.fm_extent_count > FIEMAP_MAX_EXTENTS)
+ return -EINVAL;
+
+ error = fiemap_check_ranges(sb, fiemap.fm_start, fiemap.fm_length,
+ &len);
+ if (error)
+ return error;
+
+ fieinfo.fi_flags = fiemap.fm_flags;
+ fieinfo.fi_extents_max = fiemap.fm_extent_count;
+ fieinfo.fi_extents_start = (struct fiemap_extent *)(arg + sizeof(fiemap));
+
+ if (fiemap.fm_extent_count != 0 &&
+ !access_ok(VERIFY_WRITE, fieinfo.fi_extents_start,
+ fieinfo.fi_extents_max * sizeof(struct fiemap_extent)))
+ return -EFAULT;
+
+ if (fieinfo.fi_flags & FIEMAP_FLAG_SYNC)
+ filemap_write_and_wait(inode->i_mapping);
+
+ error = inode->i_op->fiemap(inode, &fieinfo, fiemap.fm_start, len);
+ fiemap.fm_flags = fieinfo.fi_flags;
+ fiemap.fm_mapped_extents = fieinfo.fi_extents_mapped;
+ if (copy_to_user((char *)arg, &fiemap, sizeof(fiemap)))
+ error = -EFAULT;
+
+ return error;
+}
+
+#ifdef CONFIG_BLOCK
+
+#define blk_to_logical(inode, blk) (blk << (inode)->i_blkbits)
+#define logical_to_blk(inode, offset) (offset >> (inode)->i_blkbits);
+
+/*
+ * @inode - the inode to map
+ * @arg - the pointer to userspace where we copy everything to
+ * @get_block - the fs's get_block function
+ *
+ * This does FIEMAP for block based inodes. Basically it will just loop
+ * through get_block until we hit the number of extents we want to map, or we
+ * go past the end of the file and hit a hole.
+ *
+ * If it is possible to have data blocks beyond a hole past @inode->i_size, then
+ * please do not use this function, it will stop at the first unmapped block
+ * beyond i_size
+ */
+int generic_block_fiemap(struct inode *inode,
+ struct fiemap_extent_info *fieinfo, u64 start,
+ u64 len, get_block_t *get_block)
+{
+ struct buffer_head tmp;
+ unsigned int start_blk;
+ long long length = 0, map_len = 0;
+ u64 logical = 0, phys = 0, size = 0;
+ u32 flags = FIEMAP_EXTENT_MERGED;
+ int ret = 0;
+
+ if ((ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC)))
+ return ret;
+
+ start_blk = logical_to_blk(inode, start);
+
+ /* guard against change */
+ mutex_lock(&inode->i_mutex);
+
+ length = (long long)min_t(u64, len, i_size_read(inode));
+ map_len = length;
+
+ do {
+ /*
+ * we set b_size to the total size we want so it will map as
+ * many contiguous blocks as possible at once
+ */
+ memset(&tmp, 0, sizeof(struct buffer_head));
+ tmp.b_size = map_len;
+
+ ret = get_block(inode, start_blk, &tmp, 0);
+ if (ret)
+ break;
+
+ /* HOLE */
+ if (!buffer_mapped(&tmp)) {
+ /*
+ * first hole after going past the EOF, this is our
+ * last extent
+ */
+ if (length <= 0) {
+ flags = FIEMAP_EXTENT_MERGED|FIEMAP_EXTENT_LAST;
+ ret = fiemap_fill_next_extent(fieinfo, logical,
+ phys, size,
+ flags);
+ break;
+ }
+
+ length -= blk_to_logical(inode, 1);
+
+ /* if we have holes up to/past EOF then we're done */
+ if (length <= 0)
+ break;
+
+ start_blk++;
+ } else {
+ if (length <= 0 && size) {
+ ret = fiemap_fill_next_extent(fieinfo, logical,
+ phys, size,
+ flags);
+ if (ret)
+ break;
+ }
+
+ logical = blk_to_logical(inode, start_blk);
+ phys = blk_to_logical(inode, tmp.b_blocknr);
+ size = tmp.b_size;
+ flags = FIEMAP_EXTENT_MERGED;
+
+ length -= tmp.b_size;
+ start_blk += logical_to_blk(inode, size);
+
+ /*
+ * if we are past the EOF we need to loop again to see
+ * if there is a hole so we can mark this extent as the
+ * last one, and if not keep mapping things until we
+ * find a hole, or we run out of slots in the extent
+ * array
+ */
+ if (length <= 0)
+ continue;
+
+ ret = fiemap_fill_next_extent(fieinfo, logical, phys,
+ size, flags);
+ if (ret)
+ break;
+ }
+ cond_resched();
+ } while (1);
+
+ mutex_unlock(&inode->i_mutex);
+
+ /* if ret is 1 then we just hit the end of the extent array */
+ if (ret == 1)
+ ret = 0;
+
+ return ret;
+}
+EXPORT_SYMBOL(generic_block_fiemap);
+
+#endif /* CONFIG_BLOCK */
+
static int file_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
@@ -80,6 +355,8 @@
switch (cmd) {
case FIBMAP:
return ioctl_fibmap(filp, p);
+ case FS_IOC_FIEMAP:
+ return ioctl_fiemap(filp, arg);
case FIGETBSZ:
return put_user(inode->i_sb->s_blocksize, p);
case FIONREAD:
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index 26948a6..3f8af0f 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -310,7 +310,7 @@
Opt_nocompress, Opt_hide, Opt_showassoc, Opt_dmode,
};
-static match_table_t tokens = {
+static const match_table_t tokens = {
{Opt_norock, "norock"},
{Opt_nojoliet, "nojoliet"},
{Opt_unhide, "unhide"},
diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c
index 91389c8..9203c33 100644
--- a/fs/jbd2/checkpoint.c
+++ b/fs/jbd2/checkpoint.c
@@ -20,6 +20,7 @@
#include <linux/time.h>
#include <linux/fs.h>
#include <linux/jbd2.h>
+#include <linux/marker.h>
#include <linux/errno.h>
#include <linux/slab.h>
@@ -93,7 +94,8 @@
int ret = 0;
struct buffer_head *bh = jh2bh(jh);
- if (jh->b_jlist == BJ_None && !buffer_locked(bh) && !buffer_dirty(bh)) {
+ if (jh->b_jlist == BJ_None && !buffer_locked(bh) &&
+ !buffer_dirty(bh) && !buffer_write_io_error(bh)) {
JBUFFER_TRACE(jh, "remove from checkpoint list");
ret = __jbd2_journal_remove_checkpoint(jh) + 1;
jbd_unlock_bh_state(bh);
@@ -126,14 +128,29 @@
/*
* Test again, another process may have checkpointed while we
- * were waiting for the checkpoint lock
+ * were waiting for the checkpoint lock. If there are no
+ * outstanding transactions there is nothing to checkpoint and
+ * we can't make progress. Abort the journal in this case.
*/
spin_lock(&journal->j_state_lock);
+ spin_lock(&journal->j_list_lock);
nblocks = jbd_space_needed(journal);
if (__jbd2_log_space_left(journal) < nblocks) {
+ int chkpt = journal->j_checkpoint_transactions != NULL;
+
+ spin_unlock(&journal->j_list_lock);
spin_unlock(&journal->j_state_lock);
- jbd2_log_do_checkpoint(journal);
+ if (chkpt) {
+ jbd2_log_do_checkpoint(journal);
+ } else {
+ printk(KERN_ERR "%s: no transactions\n",
+ __func__);
+ jbd2_journal_abort(journal, 0);
+ }
+
spin_lock(&journal->j_state_lock);
+ } else {
+ spin_unlock(&journal->j_list_lock);
}
mutex_unlock(&journal->j_checkpoint_mutex);
}
@@ -160,21 +177,25 @@
* buffers. Note that we take the buffers in the opposite ordering
* from the one in which they were submitted for IO.
*
+ * Return 0 on success, and return <0 if some buffers have failed
+ * to be written out.
+ *
* Called with j_list_lock held.
*/
-static void __wait_cp_io(journal_t *journal, transaction_t *transaction)
+static int __wait_cp_io(journal_t *journal, transaction_t *transaction)
{
struct journal_head *jh;
struct buffer_head *bh;
tid_t this_tid;
int released = 0;
+ int ret = 0;
this_tid = transaction->t_tid;
restart:
/* Did somebody clean up the transaction in the meanwhile? */
if (journal->j_checkpoint_transactions != transaction ||
transaction->t_tid != this_tid)
- return;
+ return ret;
while (!released && transaction->t_checkpoint_io_list) {
jh = transaction->t_checkpoint_io_list;
bh = jh2bh(jh);
@@ -194,6 +215,9 @@
spin_lock(&journal->j_list_lock);
goto restart;
}
+ if (unlikely(buffer_write_io_error(bh)))
+ ret = -EIO;
+
/*
* Now in whatever state the buffer currently is, we know that
* it has been written out and so we can drop it from the list
@@ -203,6 +227,8 @@
jbd2_journal_remove_journal_head(bh);
__brelse(bh);
}
+
+ return ret;
}
#define NR_BATCH 64
@@ -226,7 +252,8 @@
* Try to flush one buffer from the checkpoint list to disk.
*
* Return 1 if something happened which requires us to abort the current
- * scan of the checkpoint list.
+ * scan of the checkpoint list. Return <0 if the buffer has failed to
+ * be written out.
*
* Called with j_list_lock held and drops it if 1 is returned
* Called under jbd_lock_bh_state(jh2bh(jh)), and drops it
@@ -258,6 +285,9 @@
jbd2_log_wait_commit(journal, tid);
ret = 1;
} else if (!buffer_dirty(bh)) {
+ ret = 1;
+ if (unlikely(buffer_write_io_error(bh)))
+ ret = -EIO;
J_ASSERT_JH(jh, !buffer_jbddirty(bh));
BUFFER_TRACE(bh, "remove from checkpoint");
__jbd2_journal_remove_checkpoint(jh);
@@ -265,7 +295,6 @@
jbd_unlock_bh_state(bh);
jbd2_journal_remove_journal_head(bh);
__brelse(bh);
- ret = 1;
} else {
/*
* Important: we are about to write the buffer, and
@@ -298,6 +327,7 @@
* to disk. We submit larger chunks of data at once.
*
* The journal should be locked before calling this function.
+ * Called with j_checkpoint_mutex held.
*/
int jbd2_log_do_checkpoint(journal_t *journal)
{
@@ -313,6 +343,8 @@
* journal straight away.
*/
result = jbd2_cleanup_journal_tail(journal);
+ trace_mark(jbd2_checkpoint, "dev %s need_checkpoint %d",
+ journal->j_devname, result);
jbd_debug(1, "cleanup_journal_tail returned %d\n", result);
if (result <= 0)
return result;
@@ -321,6 +353,7 @@
* OK, we need to start writing disk blocks. Take one transaction
* and write it.
*/
+ result = 0;
spin_lock(&journal->j_list_lock);
if (!journal->j_checkpoint_transactions)
goto out;
@@ -339,7 +372,7 @@
int batch_count = 0;
struct buffer_head *bhs[NR_BATCH];
struct journal_head *jh;
- int retry = 0;
+ int retry = 0, err;
while (!retry && transaction->t_checkpoint_list) {
struct buffer_head *bh;
@@ -353,6 +386,8 @@
}
retry = __process_buffer(journal, jh, bhs, &batch_count,
transaction);
+ if (retry < 0 && !result)
+ result = retry;
if (!retry && (need_resched() ||
spin_needbreak(&journal->j_list_lock))) {
spin_unlock(&journal->j_list_lock);
@@ -377,14 +412,18 @@
* Now we have cleaned up the first transaction's checkpoint
* list. Let's clean up the second one
*/
- __wait_cp_io(journal, transaction);
+ err = __wait_cp_io(journal, transaction);
+ if (!result)
+ result = err;
}
out:
spin_unlock(&journal->j_list_lock);
- result = jbd2_cleanup_journal_tail(journal);
if (result < 0)
- return result;
- return 0;
+ jbd2_journal_abort(journal, result);
+ else
+ result = jbd2_cleanup_journal_tail(journal);
+
+ return (result < 0) ? result : 0;
}
/*
@@ -400,8 +439,9 @@
* This is the only part of the journaling code which really needs to be
* aware of transaction aborts. Checkpointing involves writing to the
* main filesystem area rather than to the journal, so it can proceed
- * even in abort state, but we must not update the journal superblock if
- * we have an abort error outstanding.
+ * even in abort state, but we must not update the super block if
+ * checkpointing may have failed. Otherwise, we would lose some metadata
+ * buffers which should be written-back to the filesystem.
*/
int jbd2_cleanup_journal_tail(journal_t *journal)
@@ -410,6 +450,9 @@
tid_t first_tid;
unsigned long blocknr, freed;
+ if (is_journal_aborted(journal))
+ return 1;
+
/* OK, work out the oldest transaction remaining in the log, and
* the log block it starts at.
*
diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c
index f2ad061..0abe02c 100644
--- a/fs/jbd2/commit.c
+++ b/fs/jbd2/commit.c
@@ -16,6 +16,7 @@
#include <linux/time.h>
#include <linux/fs.h>
#include <linux/jbd2.h>
+#include <linux/marker.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/mm.h>
@@ -126,8 +127,7 @@
JBUFFER_TRACE(descriptor, "submit commit block");
lock_buffer(bh);
- get_bh(bh);
- set_buffer_dirty(bh);
+ clear_buffer_dirty(bh);
set_buffer_uptodate(bh);
bh->b_end_io = journal_end_buffer_io_sync;
@@ -147,12 +147,9 @@
* to remember if we sent a barrier request
*/
if (ret == -EOPNOTSUPP && barrier_done) {
- char b[BDEVNAME_SIZE];
-
printk(KERN_WARNING
- "JBD: barrier-based sync failed on %s - "
- "disabling barriers\n",
- bdevname(journal->j_dev, b));
+ "JBD: barrier-based sync failed on %s - "
+ "disabling barriers\n", journal->j_devname);
spin_lock(&journal->j_state_lock);
journal->j_flags &= ~JBD2_BARRIER;
spin_unlock(&journal->j_state_lock);
@@ -160,7 +157,7 @@
/* And try again, without the barrier */
lock_buffer(bh);
set_buffer_uptodate(bh);
- set_buffer_dirty(bh);
+ clear_buffer_dirty(bh);
ret = submit_bh(WRITE, bh);
}
*cbh = bh;
@@ -371,6 +368,8 @@
commit_transaction = journal->j_running_transaction;
J_ASSERT(commit_transaction->t_state == T_RUNNING);
+ trace_mark(jbd2_start_commit, "dev %s transaction %d",
+ journal->j_devname, commit_transaction->t_tid);
jbd_debug(1, "JBD: starting commit of transaction %d\n",
commit_transaction->t_tid);
@@ -505,9 +504,10 @@
jh = commit_transaction->t_buffers;
/* If we're in abort mode, we just un-journal the buffer and
- release it for background writing. */
+ release it. */
if (is_journal_aborted(journal)) {
+ clear_buffer_jbddirty(jh2bh(jh));
JBUFFER_TRACE(jh, "journal is aborting: refile");
jbd2_journal_refile_buffer(journal, jh);
/* If that was the last one, we need to clean up
@@ -681,11 +681,11 @@
*/
err = journal_finish_inode_data_buffers(journal, commit_transaction);
if (err) {
- char b[BDEVNAME_SIZE];
-
printk(KERN_WARNING
"JBD2: Detected IO errors while flushing file data "
- "on %s\n", bdevname(journal->j_fs_dev, b));
+ "on %s\n", journal->j_devname);
+ if (journal->j_flags & JBD2_ABORT_ON_SYNCDATA_ERR)
+ jbd2_journal_abort(journal, err);
err = 0;
}
@@ -786,6 +786,9 @@
/* AKPM: bforget here */
}
+ if (err)
+ jbd2_journal_abort(journal, err);
+
jbd_debug(3, "JBD: commit phase 5\n");
if (!JBD2_HAS_INCOMPAT_FEATURE(journal,
@@ -884,6 +887,8 @@
if (buffer_jbddirty(bh)) {
JBUFFER_TRACE(jh, "add to new checkpointing trans");
__jbd2_journal_insert_checkpoint(jh, commit_transaction);
+ if (is_journal_aborted(journal))
+ clear_buffer_jbddirty(bh);
JBUFFER_TRACE(jh, "refile for checkpoint writeback");
__jbd2_journal_refile_buffer(jh);
jbd_unlock_bh_state(bh);
@@ -990,6 +995,9 @@
}
spin_unlock(&journal->j_list_lock);
+ trace_mark(jbd2_end_commit, "dev %s transaction %d head %d",
+ journal->j_devname, commit_transaction->t_tid,
+ journal->j_tail_sequence);
jbd_debug(1, "JBD: commit %d complete, head %d\n",
journal->j_commit_sequence, journal->j_tail_sequence);
diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
index 8207a01..783de11 100644
--- a/fs/jbd2/journal.c
+++ b/fs/jbd2/journal.c
@@ -597,13 +597,9 @@
if (ret)
*retp = ret;
else {
- char b[BDEVNAME_SIZE];
-
printk(KERN_ALERT "%s: journal block not found "
"at offset %lu on %s\n",
- __func__,
- blocknr,
- bdevname(journal->j_dev, b));
+ __func__, blocknr, journal->j_devname);
err = -EIO;
__journal_abort_soft(journal, err);
}
@@ -901,10 +897,7 @@
static void jbd2_stats_proc_init(journal_t *journal)
{
- char name[BDEVNAME_SIZE];
-
- bdevname(journal->j_dev, name);
- journal->j_proc_entry = proc_mkdir(name, proc_jbd2_stats);
+ journal->j_proc_entry = proc_mkdir(journal->j_devname, proc_jbd2_stats);
if (journal->j_proc_entry) {
proc_create_data("history", S_IRUGO, journal->j_proc_entry,
&jbd2_seq_history_fops, journal);
@@ -915,12 +908,9 @@
static void jbd2_stats_proc_exit(journal_t *journal)
{
- char name[BDEVNAME_SIZE];
-
- bdevname(journal->j_dev, name);
remove_proc_entry("info", journal->j_proc_entry);
remove_proc_entry("history", journal->j_proc_entry);
- remove_proc_entry(name, proc_jbd2_stats);
+ remove_proc_entry(journal->j_devname, proc_jbd2_stats);
}
static void journal_init_stats(journal_t *journal)
@@ -1018,6 +1008,7 @@
{
journal_t *journal = journal_init_common();
struct buffer_head *bh;
+ char *p;
int n;
if (!journal)
@@ -1039,6 +1030,10 @@
journal->j_fs_dev = fs_dev;
journal->j_blk_offset = start;
journal->j_maxlen = len;
+ bdevname(journal->j_dev, journal->j_devname);
+ p = journal->j_devname;
+ while ((p = strchr(p, '/')))
+ *p = '!';
jbd2_stats_proc_init(journal);
bh = __getblk(journal->j_dev, start, journal->j_blocksize);
@@ -1061,6 +1056,7 @@
{
struct buffer_head *bh;
journal_t *journal = journal_init_common();
+ char *p;
int err;
int n;
unsigned long long blocknr;
@@ -1070,6 +1066,12 @@
journal->j_dev = journal->j_fs_dev = inode->i_sb->s_bdev;
journal->j_inode = inode;
+ bdevname(journal->j_dev, journal->j_devname);
+ p = journal->j_devname;
+ while ((p = strchr(p, '/')))
+ *p = '!';
+ p = journal->j_devname + strlen(journal->j_devname);
+ sprintf(p, ":%lu", journal->j_inode->i_ino);
jbd_debug(1,
"journal %p: inode %s/%ld, size %Ld, bits %d, blksize %ld\n",
journal, inode->i_sb->s_id, inode->i_ino,
@@ -1253,6 +1255,22 @@
goto out;
}
+ if (buffer_write_io_error(bh)) {
+ /*
+ * Oh, dear. A previous attempt to write the journal
+ * superblock failed. This could happen because the
+ * USB device was yanked out. Or it could happen to
+ * be a transient write error and maybe the block will
+ * be remapped. Nothing we can do but to retry the
+ * write and hope for the best.
+ */
+ printk(KERN_ERR "JBD2: previous I/O error detected "
+ "for journal superblock update for %s.\n",
+ journal->j_devname);
+ clear_buffer_write_io_error(bh);
+ set_buffer_uptodate(bh);
+ }
+
spin_lock(&journal->j_state_lock);
jbd_debug(1,"JBD: updating superblock (start %ld, seq %d, errno %d)\n",
journal->j_tail, journal->j_tail_sequence, journal->j_errno);
@@ -1264,9 +1282,16 @@
BUFFER_TRACE(bh, "marking dirty");
mark_buffer_dirty(bh);
- if (wait)
+ if (wait) {
sync_dirty_buffer(bh);
- else
+ if (buffer_write_io_error(bh)) {
+ printk(KERN_ERR "JBD2: I/O error detected "
+ "when updating journal superblock for %s.\n",
+ journal->j_devname);
+ clear_buffer_write_io_error(bh);
+ set_buffer_uptodate(bh);
+ }
+ } else
ll_rw_block(SWRITE, 1, &bh);
out:
@@ -1426,9 +1451,12 @@
*
* Release a journal_t structure once it is no longer in use by the
* journaled object.
+ * Return <0 if we couldn't clean up the journal.
*/
-void jbd2_journal_destroy(journal_t *journal)
+int jbd2_journal_destroy(journal_t *journal)
{
+ int err = 0;
+
/* Wait for the commit thread to wake up and die. */
journal_kill_thread(journal);
@@ -1451,11 +1479,16 @@
J_ASSERT(journal->j_checkpoint_transactions == NULL);
spin_unlock(&journal->j_list_lock);
- /* We can now mark the journal as empty. */
- journal->j_tail = 0;
- journal->j_tail_sequence = ++journal->j_transaction_sequence;
if (journal->j_sb_buffer) {
- jbd2_journal_update_superblock(journal, 1);
+ if (!is_journal_aborted(journal)) {
+ /* We can now mark the journal as empty. */
+ journal->j_tail = 0;
+ journal->j_tail_sequence =
+ ++journal->j_transaction_sequence;
+ jbd2_journal_update_superblock(journal, 1);
+ } else {
+ err = -EIO;
+ }
brelse(journal->j_sb_buffer);
}
@@ -1467,6 +1500,8 @@
jbd2_journal_destroy_revoke(journal);
kfree(journal->j_wbuf);
kfree(journal);
+
+ return err;
}
@@ -1692,10 +1727,16 @@
spin_lock(&journal->j_list_lock);
while (!err && journal->j_checkpoint_transactions != NULL) {
spin_unlock(&journal->j_list_lock);
+ mutex_lock(&journal->j_checkpoint_mutex);
err = jbd2_log_do_checkpoint(journal);
+ mutex_unlock(&journal->j_checkpoint_mutex);
spin_lock(&journal->j_list_lock);
}
spin_unlock(&journal->j_list_lock);
+
+ if (is_journal_aborted(journal))
+ return -EIO;
+
jbd2_cleanup_journal_tail(journal);
/* Finally, mark the journal as really needing no recovery.
@@ -1717,7 +1758,7 @@
J_ASSERT(journal->j_head == journal->j_tail);
J_ASSERT(journal->j_tail_sequence == journal->j_transaction_sequence);
spin_unlock(&journal->j_state_lock);
- return err;
+ return 0;
}
/**
@@ -1761,23 +1802,6 @@
}
/*
- * journal_dev_name: format a character string to describe on what
- * device this journal is present.
- */
-
-static const char *journal_dev_name(journal_t *journal, char *buffer)
-{
- struct block_device *bdev;
-
- if (journal->j_inode)
- bdev = journal->j_inode->i_sb->s_bdev;
- else
- bdev = journal->j_dev;
-
- return bdevname(bdev, buffer);
-}
-
-/*
* Journal abort has very specific semantics, which we describe
* for journal abort.
*
@@ -1793,13 +1817,12 @@
void __jbd2_journal_abort_hard(journal_t *journal)
{
transaction_t *transaction;
- char b[BDEVNAME_SIZE];
if (journal->j_flags & JBD2_ABORT)
return;
printk(KERN_ERR "Aborting journal on device %s.\n",
- journal_dev_name(journal, b));
+ journal->j_devname);
spin_lock(&journal->j_state_lock);
journal->j_flags |= JBD2_ABORT;
diff --git a/fs/jbd2/recovery.c b/fs/jbd2/recovery.c
index 058f50f..7306328 100644
--- a/fs/jbd2/recovery.c
+++ b/fs/jbd2/recovery.c
@@ -225,7 +225,7 @@
*/
int jbd2_journal_recover(journal_t *journal)
{
- int err;
+ int err, err2;
journal_superblock_t * sb;
struct recovery_info info;
@@ -263,7 +263,10 @@
journal->j_transaction_sequence = ++info.end_transaction;
jbd2_journal_clear_revoke(journal);
- sync_blockdev(journal->j_fs_dev);
+ err2 = sync_blockdev(journal->j_fs_dev);
+ if (!err)
+ err = err2;
+
return err;
}
diff --git a/fs/jfs/super.c b/fs/jfs/super.c
index 3630718..0dae345 100644
--- a/fs/jfs/super.c
+++ b/fs/jfs/super.c
@@ -199,7 +199,7 @@
Opt_usrquota, Opt_grpquota, Opt_uid, Opt_gid, Opt_umask
};
-static match_table_t tokens = {
+static const match_table_t tokens = {
{Opt_integrity, "integrity"},
{Opt_nointegrity, "nointegrity"},
{Opt_iocharset, "iocharset=%s"},
diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c
index 46763d1..8478fc2 100644
--- a/fs/nfs/nfsroot.c
+++ b/fs/nfs/nfsroot.c
@@ -127,7 +127,7 @@
Opt_err
};
-static match_table_t __initdata tokens = {
+static match_table_t __initconst tokens = {
{Opt_port, "port=%u"},
{Opt_rsize, "rsize=%u"},
{Opt_wsize, "wsize=%u"},
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index e9b2017..ffb6974 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -98,7 +98,7 @@
Opt_err
};
-static match_table_t nfs_mount_option_tokens = {
+static const match_table_t nfs_mount_option_tokens = {
{ Opt_userspace, "bg" },
{ Opt_userspace, "fg" },
{ Opt_userspace, "retry=%s" },
@@ -163,7 +163,7 @@
Opt_xprt_err
};
-static match_table_t nfs_xprt_protocol_tokens = {
+static const match_table_t nfs_xprt_protocol_tokens = {
{ Opt_xprt_udp, "udp" },
{ Opt_xprt_tcp, "tcp" },
{ Opt_xprt_rdma, "rdma" },
@@ -180,7 +180,7 @@
Opt_sec_err
};
-static match_table_t nfs_secflavor_tokens = {
+static const match_table_t nfs_secflavor_tokens = {
{ Opt_sec_none, "none" },
{ Opt_sec_none, "null" },
{ Opt_sec_sys, "sys" },
diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c
index 10bfb46..29ff57e 100644
--- a/fs/ocfs2/alloc.c
+++ b/fs/ocfs2/alloc.c
@@ -990,15 +990,6 @@
}
/*
- * This is only valid for leaf nodes, which are the only ones that can
- * have empty extents anyway.
- */
-static inline int ocfs2_is_empty_extent(struct ocfs2_extent_rec *rec)
-{
- return !rec->e_leaf_clusters;
-}
-
-/*
* This function will discard the rightmost extent record.
*/
static void ocfs2_shift_records_right(struct ocfs2_extent_list *el)
diff --git a/fs/ocfs2/alloc.h b/fs/ocfs2/alloc.h
index 42ff94b..60cd3d5 100644
--- a/fs/ocfs2/alloc.h
+++ b/fs/ocfs2/alloc.h
@@ -146,4 +146,13 @@
return le16_to_cpu(rec->e_leaf_clusters);
}
+/*
+ * This is only valid for leaf nodes, which are the only ones that can
+ * have empty extents anyway.
+ */
+static inline int ocfs2_is_empty_extent(struct ocfs2_extent_rec *rec)
+{
+ return !rec->e_leaf_clusters;
+}
+
#endif /* OCFS2_ALLOC_H */
diff --git a/fs/ocfs2/extent_map.c b/fs/ocfs2/extent_map.c
index c58668a..aed268e 100644
--- a/fs/ocfs2/extent_map.c
+++ b/fs/ocfs2/extent_map.c
@@ -25,6 +25,7 @@
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/types.h>
+#include <linux/fiemap.h>
#define MLOG_MASK_PREFIX ML_EXTENT_MAP
#include <cluster/masklog.h>
@@ -32,6 +33,7 @@
#include "ocfs2.h"
#include "alloc.h"
+#include "dlmglue.h"
#include "extent_map.h"
#include "inode.h"
#include "super.h"
@@ -282,6 +284,51 @@
kfree(new_emi);
}
+static int ocfs2_last_eb_is_empty(struct inode *inode,
+ struct ocfs2_dinode *di)
+{
+ int ret, next_free;
+ u64 last_eb_blk = le64_to_cpu(di->i_last_eb_blk);
+ struct buffer_head *eb_bh = NULL;
+ struct ocfs2_extent_block *eb;
+ struct ocfs2_extent_list *el;
+
+ ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), last_eb_blk,
+ &eb_bh, OCFS2_BH_CACHED, inode);
+ if (ret) {
+ mlog_errno(ret);
+ goto out;
+ }
+
+ eb = (struct ocfs2_extent_block *) eb_bh->b_data;
+ el = &eb->h_list;
+
+ if (!OCFS2_IS_VALID_EXTENT_BLOCK(eb)) {
+ ret = -EROFS;
+ OCFS2_RO_ON_INVALID_EXTENT_BLOCK(inode->i_sb, eb);
+ goto out;
+ }
+
+ if (el->l_tree_depth) {
+ ocfs2_error(inode->i_sb,
+ "Inode %lu has non zero tree depth in "
+ "leaf block %llu\n", inode->i_ino,
+ (unsigned long long)eb_bh->b_blocknr);
+ ret = -EROFS;
+ goto out;
+ }
+
+ next_free = le16_to_cpu(el->l_next_free_rec);
+
+ if (next_free == 0 ||
+ (next_free == 1 && ocfs2_is_empty_extent(&el->l_recs[0])))
+ ret = 1;
+
+out:
+ brelse(eb_bh);
+ return ret;
+}
+
/*
* Return the 1st index within el which contains an extent start
* larger than v_cluster.
@@ -373,42 +420,28 @@
return ret;
}
-int ocfs2_get_clusters(struct inode *inode, u32 v_cluster,
- u32 *p_cluster, u32 *num_clusters,
- unsigned int *extent_flags)
+static int ocfs2_get_clusters_nocache(struct inode *inode,
+ struct buffer_head *di_bh,
+ u32 v_cluster, unsigned int *hole_len,
+ struct ocfs2_extent_rec *ret_rec,
+ unsigned int *is_last)
{
- int ret, i;
- unsigned int flags = 0;
- struct buffer_head *di_bh = NULL;
- struct buffer_head *eb_bh = NULL;
+ int i, ret, tree_height, len;
struct ocfs2_dinode *di;
- struct ocfs2_extent_block *eb;
+ struct ocfs2_extent_block *uninitialized_var(eb);
struct ocfs2_extent_list *el;
struct ocfs2_extent_rec *rec;
- u32 coff;
+ struct buffer_head *eb_bh = NULL;
- if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL) {
- ret = -ERANGE;
- mlog_errno(ret);
- goto out;
- }
-
- ret = ocfs2_extent_map_lookup(inode, v_cluster, p_cluster,
- num_clusters, extent_flags);
- if (ret == 0)
- goto out;
-
- ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), OCFS2_I(inode)->ip_blkno,
- &di_bh, OCFS2_BH_CACHED, inode);
- if (ret) {
- mlog_errno(ret);
- goto out;
- }
+ memset(ret_rec, 0, sizeof(*ret_rec));
+ if (is_last)
+ *is_last = 0;
di = (struct ocfs2_dinode *) di_bh->b_data;
el = &di->id2.i_list;
+ tree_height = le16_to_cpu(el->l_tree_depth);
- if (el->l_tree_depth) {
+ if (tree_height > 0) {
ret = ocfs2_find_leaf(inode, el, v_cluster, &eb_bh);
if (ret) {
mlog_errno(ret);
@@ -431,46 +464,143 @@
i = ocfs2_search_extent_list(el, v_cluster);
if (i == -1) {
/*
+ * Holes can be larger than the maximum size of an
+ * extent, so we return their lengths in a seperate
+ * field.
+ */
+ if (hole_len) {
+ ret = ocfs2_figure_hole_clusters(inode, el, eb_bh,
+ v_cluster, &len);
+ if (ret) {
+ mlog_errno(ret);
+ goto out;
+ }
+
+ *hole_len = len;
+ }
+ goto out_hole;
+ }
+
+ rec = &el->l_recs[i];
+
+ BUG_ON(v_cluster < le32_to_cpu(rec->e_cpos));
+
+ if (!rec->e_blkno) {
+ ocfs2_error(inode->i_sb, "Inode %lu has bad extent "
+ "record (%u, %u, 0)", inode->i_ino,
+ le32_to_cpu(rec->e_cpos),
+ ocfs2_rec_clusters(el, rec));
+ ret = -EROFS;
+ goto out;
+ }
+
+ *ret_rec = *rec;
+
+ /*
+ * Checking for last extent is potentially expensive - we
+ * might have to look at the next leaf over to see if it's
+ * empty.
+ *
+ * The first two checks are to see whether the caller even
+ * cares for this information, and if the extent is at least
+ * the last in it's list.
+ *
+ * If those hold true, then the extent is last if any of the
+ * additional conditions hold true:
+ * - Extent list is in-inode
+ * - Extent list is right-most
+ * - Extent list is 2nd to rightmost, with empty right-most
+ */
+ if (is_last) {
+ if (i == (le16_to_cpu(el->l_next_free_rec) - 1)) {
+ if (tree_height == 0)
+ *is_last = 1;
+ else if (eb->h_blkno == di->i_last_eb_blk)
+ *is_last = 1;
+ else if (eb->h_next_leaf_blk == di->i_last_eb_blk) {
+ ret = ocfs2_last_eb_is_empty(inode, di);
+ if (ret < 0) {
+ mlog_errno(ret);
+ goto out;
+ }
+ if (ret == 1)
+ *is_last = 1;
+ }
+ }
+ }
+
+out_hole:
+ ret = 0;
+out:
+ brelse(eb_bh);
+ return ret;
+}
+
+static void ocfs2_relative_extent_offsets(struct super_block *sb,
+ u32 v_cluster,
+ struct ocfs2_extent_rec *rec,
+ u32 *p_cluster, u32 *num_clusters)
+
+{
+ u32 coff = v_cluster - le32_to_cpu(rec->e_cpos);
+
+ *p_cluster = ocfs2_blocks_to_clusters(sb, le64_to_cpu(rec->e_blkno));
+ *p_cluster = *p_cluster + coff;
+
+ if (num_clusters)
+ *num_clusters = le16_to_cpu(rec->e_leaf_clusters) - coff;
+}
+
+int ocfs2_get_clusters(struct inode *inode, u32 v_cluster,
+ u32 *p_cluster, u32 *num_clusters,
+ unsigned int *extent_flags)
+{
+ int ret;
+ unsigned int uninitialized_var(hole_len), flags = 0;
+ struct buffer_head *di_bh = NULL;
+ struct ocfs2_extent_rec rec;
+
+ if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL) {
+ ret = -ERANGE;
+ mlog_errno(ret);
+ goto out;
+ }
+
+ ret = ocfs2_extent_map_lookup(inode, v_cluster, p_cluster,
+ num_clusters, extent_flags);
+ if (ret == 0)
+ goto out;
+
+ ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), OCFS2_I(inode)->ip_blkno,
+ &di_bh, OCFS2_BH_CACHED, inode);
+ if (ret) {
+ mlog_errno(ret);
+ goto out;
+ }
+
+ ret = ocfs2_get_clusters_nocache(inode, di_bh, v_cluster, &hole_len,
+ &rec, NULL);
+ if (ret) {
+ mlog_errno(ret);
+ goto out;
+ }
+
+ if (rec.e_blkno == 0ULL) {
+ /*
* A hole was found. Return some canned values that
* callers can key on. If asked for, num_clusters will
* be populated with the size of the hole.
*/
*p_cluster = 0;
if (num_clusters) {
- ret = ocfs2_figure_hole_clusters(inode, el, eb_bh,
- v_cluster,
- num_clusters);
- if (ret) {
- mlog_errno(ret);
- goto out;
- }
+ *num_clusters = hole_len;
}
} else {
- rec = &el->l_recs[i];
+ ocfs2_relative_extent_offsets(inode->i_sb, v_cluster, &rec,
+ p_cluster, num_clusters);
+ flags = rec.e_flags;
- BUG_ON(v_cluster < le32_to_cpu(rec->e_cpos));
-
- if (!rec->e_blkno) {
- ocfs2_error(inode->i_sb, "Inode %lu has bad extent "
- "record (%u, %u, 0)", inode->i_ino,
- le32_to_cpu(rec->e_cpos),
- ocfs2_rec_clusters(el, rec));
- ret = -EROFS;
- goto out;
- }
-
- coff = v_cluster - le32_to_cpu(rec->e_cpos);
-
- *p_cluster = ocfs2_blocks_to_clusters(inode->i_sb,
- le64_to_cpu(rec->e_blkno));
- *p_cluster = *p_cluster + coff;
-
- if (num_clusters)
- *num_clusters = ocfs2_rec_clusters(el, rec) - coff;
-
- flags = rec->e_flags;
-
- ocfs2_extent_map_insert_rec(inode, rec);
+ ocfs2_extent_map_insert_rec(inode, &rec);
}
if (extent_flags)
@@ -478,7 +608,6 @@
out:
brelse(di_bh);
- brelse(eb_bh);
return ret;
}
@@ -521,3 +650,114 @@
out:
return ret;
}
+
+static int ocfs2_fiemap_inline(struct inode *inode, struct buffer_head *di_bh,
+ struct fiemap_extent_info *fieinfo,
+ u64 map_start)
+{
+ int ret;
+ unsigned int id_count;
+ struct ocfs2_dinode *di;
+ u64 phys;
+ u32 flags = FIEMAP_EXTENT_DATA_INLINE|FIEMAP_EXTENT_LAST;
+ struct ocfs2_inode_info *oi = OCFS2_I(inode);
+
+ di = (struct ocfs2_dinode *)di_bh->b_data;
+ id_count = le16_to_cpu(di->id2.i_data.id_count);
+
+ if (map_start < id_count) {
+ phys = oi->ip_blkno << inode->i_sb->s_blocksize_bits;
+ phys += offsetof(struct ocfs2_dinode, id2.i_data.id_data);
+
+ ret = fiemap_fill_next_extent(fieinfo, 0, phys, id_count,
+ flags);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+#define OCFS2_FIEMAP_FLAGS (FIEMAP_FLAG_SYNC)
+
+int ocfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
+ u64 map_start, u64 map_len)
+{
+ int ret, is_last;
+ u32 mapping_end, cpos;
+ unsigned int hole_size;
+ struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+ u64 len_bytes, phys_bytes, virt_bytes;
+ struct buffer_head *di_bh = NULL;
+ struct ocfs2_extent_rec rec;
+
+ ret = fiemap_check_flags(fieinfo, OCFS2_FIEMAP_FLAGS);
+ if (ret)
+ return ret;
+
+ ret = ocfs2_inode_lock(inode, &di_bh, 0);
+ if (ret) {
+ mlog_errno(ret);
+ goto out;
+ }
+
+ down_read(&OCFS2_I(inode)->ip_alloc_sem);
+
+ /*
+ * Handle inline-data separately.
+ */
+ if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL) {
+ ret = ocfs2_fiemap_inline(inode, di_bh, fieinfo, map_start);
+ goto out_unlock;
+ }
+
+ cpos = map_start >> osb->s_clustersize_bits;
+ mapping_end = ocfs2_clusters_for_bytes(inode->i_sb,
+ map_start + map_len);
+ mapping_end -= cpos;
+ is_last = 0;
+ while (cpos < mapping_end && !is_last) {
+ u32 fe_flags;
+
+ ret = ocfs2_get_clusters_nocache(inode, di_bh, cpos,
+ &hole_size, &rec, &is_last);
+ if (ret) {
+ mlog_errno(ret);
+ goto out;
+ }
+
+ if (rec.e_blkno == 0ULL) {
+ cpos += hole_size;
+ continue;
+ }
+
+ fe_flags = 0;
+ if (rec.e_flags & OCFS2_EXT_UNWRITTEN)
+ fe_flags |= FIEMAP_EXTENT_UNWRITTEN;
+ if (is_last)
+ fe_flags |= FIEMAP_EXTENT_LAST;
+ len_bytes = (u64)le16_to_cpu(rec.e_leaf_clusters) << osb->s_clustersize_bits;
+ phys_bytes = le64_to_cpu(rec.e_blkno) << osb->sb->s_blocksize_bits;
+ virt_bytes = (u64)le32_to_cpu(rec.e_cpos) << osb->s_clustersize_bits;
+
+ ret = fiemap_fill_next_extent(fieinfo, virt_bytes, phys_bytes,
+ len_bytes, fe_flags);
+ if (ret)
+ break;
+
+ cpos = le32_to_cpu(rec.e_cpos)+ le16_to_cpu(rec.e_leaf_clusters);
+ }
+
+ if (ret > 0)
+ ret = 0;
+
+out_unlock:
+ brelse(di_bh);
+
+ up_read(&OCFS2_I(inode)->ip_alloc_sem);
+
+ ocfs2_inode_unlock(inode, 0);
+out:
+
+ return ret;
+}
diff --git a/fs/ocfs2/extent_map.h b/fs/ocfs2/extent_map.h
index de91e3e..1b97490 100644
--- a/fs/ocfs2/extent_map.h
+++ b/fs/ocfs2/extent_map.h
@@ -50,4 +50,7 @@
int ocfs2_extent_map_get_blocks(struct inode *inode, u64 v_blkno, u64 *p_blkno,
u64 *ret_count, unsigned int *extent_flags);
+int ocfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
+ u64 map_start, u64 map_len);
+
#endif /* _EXTENT_MAP_H */
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index ec2ed15..ed38796 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -2228,6 +2228,7 @@
.getattr = ocfs2_getattr,
.permission = ocfs2_permission,
.fallocate = ocfs2_fallocate,
+ .fiemap = ocfs2_fiemap,
};
const struct inode_operations ocfs2_special_file_iops = {
diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c
index 88255d3f..70334d8 100644
--- a/fs/ocfs2/super.c
+++ b/fs/ocfs2/super.c
@@ -157,7 +157,7 @@
Opt_err,
};
-static match_table_t tokens = {
+static const match_table_t tokens = {
{Opt_barrier, "barrier=%u"},
{Opt_err_panic, "errors=panic"},
{Opt_err_ro, "errors=remount-ro"},
diff --git a/fs/omfs/inode.c b/fs/omfs/inode.c
index d29047b..cbf047a 100644
--- a/fs/omfs/inode.c
+++ b/fs/omfs/inode.c
@@ -346,7 +346,7 @@
Opt_uid, Opt_gid, Opt_umask, Opt_dmask, Opt_fmask
};
-static match_table_t tokens = {
+static const match_table_t tokens = {
{Opt_uid, "uid=%u"},
{Opt_gid, "gid=%u"},
{Opt_umask, "umask=%o"},
diff --git a/fs/open.c b/fs/open.c
index 07da935..5596049 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -1141,8 +1141,7 @@
asmlinkage long sys_vhangup(void)
{
if (capable(CAP_SYS_TTY_CONFIG)) {
- /* XXX: this needs locking */
- tty_vhangup(current->signal->tty);
+ tty_vhangup_self();
return 0;
}
return -EPERM;
diff --git a/fs/proc/Kconfig b/fs/proc/Kconfig
index 73cd7a4..50f8f06 100644
--- a/fs/proc/Kconfig
+++ b/fs/proc/Kconfig
@@ -57,3 +57,13 @@
As it is generally a good thing, you should say Y here unless
building a kernel for install/rescue disks or your system is very
limited in memory.
+
+config PROC_PAGE_MONITOR
+ default y
+ depends on PROC_FS && MMU
+ bool "Enable /proc page monitoring" if EMBEDDED
+ help
+ Various /proc files exist to monitor process memory utilization:
+ /proc/pid/smaps, /proc/pid/clear_refs, /proc/pid/pagemap,
+ /proc/kpagecount, and /proc/kpageflags. Disabling these
+ interfaces will reduce the size of the kernel by approximately 4kb.
diff --git a/fs/proc/array.c b/fs/proc/array.c
index 71c9be5..f4bc0e7 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -86,11 +86,6 @@
#include <asm/processor.h>
#include "internal.h"
-/* Gcc optimizes away "strlen(x)" for constant x */
-#define ADDBUF(buffer, string) \
-do { memcpy(buffer, string, strlen(string)); \
- buffer += strlen(string); } while (0)
-
static inline void task_name(struct seq_file *m, struct task_struct *p)
{
int i;
@@ -261,7 +256,6 @@
sigemptyset(&ignored);
sigemptyset(&caught);
- rcu_read_lock();
if (lock_task_sighand(p, &flags)) {
pending = p->pending.signal;
shpending = p->signal->shared_pending.signal;
@@ -272,7 +266,6 @@
qlim = p->signal->rlim[RLIMIT_SIGPENDING].rlim_cur;
unlock_task_sighand(p, &flags);
}
- rcu_read_unlock();
seq_printf(m, "Threads:\t%d\n", num_threads);
seq_printf(m, "SigQ:\t%lu/%lu\n", qsize, qlim);
diff --git a/fs/proc/base.c b/fs/proc/base.c
index a28840b..b5918ae 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -148,9 +148,6 @@
return count;
}
-int maps_protect;
-EXPORT_SYMBOL(maps_protect);
-
static struct fs_struct *get_fs_struct(struct task_struct *task)
{
struct fs_struct *fs;
@@ -164,7 +161,6 @@
static int get_nr_threads(struct task_struct *tsk)
{
- /* Must be called with the rcu_read_lock held */
unsigned long flags;
int count = 0;
@@ -471,14 +467,10 @@
struct rlimit rlim[RLIM_NLIMITS];
- rcu_read_lock();
- if (!lock_task_sighand(task,&flags)) {
- rcu_read_unlock();
+ if (!lock_task_sighand(task, &flags))
return 0;
- }
memcpy(rlim, task->signal->rlim, sizeof(struct rlimit) * RLIM_NLIMITS);
unlock_task_sighand(task, &flags);
- rcu_read_unlock();
/*
* print the file header
@@ -2443,6 +2435,13 @@
}
#endif /* CONFIG_TASK_IO_ACCOUNTING */
+static int proc_pid_personality(struct seq_file *m, struct pid_namespace *ns,
+ struct pid *pid, struct task_struct *task)
+{
+ seq_printf(m, "%08x\n", task->personality);
+ return 0;
+}
+
/*
* Thread groups
*/
@@ -2459,6 +2458,7 @@
REG("environ", S_IRUSR, environ),
INF("auxv", S_IRUSR, pid_auxv),
ONE("status", S_IRUGO, pid_status),
+ ONE("personality", S_IRUSR, pid_personality),
INF("limits", S_IRUSR, pid_limits),
#ifdef CONFIG_SCHED_DEBUG
REG("sched", S_IRUGO|S_IWUSR, pid_sched),
@@ -2794,6 +2794,7 @@
REG("environ", S_IRUSR, environ),
INF("auxv", S_IRUSR, pid_auxv),
ONE("status", S_IRUGO, pid_status),
+ ONE("personality", S_IRUSR, pid_personality),
INF("limits", S_IRUSR, pid_limits),
#ifdef CONFIG_SCHED_DEBUG
REG("sched", S_IRUGO|S_IWUSR, pid_sched),
@@ -3088,9 +3089,7 @@
generic_fillattr(inode, stat);
if (p) {
- rcu_read_lock();
stat->nlink += get_nr_threads(p);
- rcu_read_unlock();
put_task_struct(p);
}
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 8bb03f0..c6b4fa7 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -342,7 +342,7 @@
if (!pde->proc_fops) {
spin_unlock(&pde->pde_unload_lock);
kfree(pdeo);
- return rv;
+ return -EINVAL;
}
pde->pde_users++;
open = pde->proc_fops->open;
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index 4422023..3bfb7b8 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -45,8 +45,6 @@
extern int nommu_vma_show(struct seq_file *, struct vm_area_struct *);
#endif
-extern int maps_protect;
-
extern int proc_tid_stat(struct seq_file *m, struct pid_namespace *ns,
struct pid *pid, struct task_struct *task);
extern int proc_tgid_stat(struct seq_file *m, struct pid_namespace *ns,
diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c
index 29e20c6..66c1ab8 100644
--- a/fs/proc/proc_misc.c
+++ b/fs/proc/proc_misc.c
@@ -68,7 +68,6 @@
extern int get_hardware_list(char *);
extern int get_stram_list(char *);
extern int get_exec_domain_list(char *);
-extern int get_dma_list(char *);
static int proc_calc_metrics(char *page, char **start, off_t off,
int count, int *eof, int len)
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index f9a8b89..945a810 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -66,7 +66,7 @@
return NULL;
}
-struct ctl_table_header *grab_header(struct inode *inode)
+static struct ctl_table_header *grab_header(struct inode *inode)
{
if (PROC_I(inode)->sysctl)
return sysctl_head_grab(PROC_I(inode)->sysctl);
@@ -395,10 +395,10 @@
.d_compare = proc_sys_compare,
};
-static struct proc_dir_entry *proc_sys_root;
-
int proc_sys_init(void)
{
+ struct proc_dir_entry *proc_sys_root;
+
proc_sys_root = proc_mkdir("sys", NULL);
proc_sys_root->proc_iops = &proc_sys_dir_operations;
proc_sys_root->proc_fops = &proc_sys_dir_file_operations;
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index 73d1891..4806830 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -210,9 +210,6 @@
dev_t dev = 0;
int len;
- if (maps_protect && !ptrace_may_access(task, PTRACE_MODE_READ))
- return -EACCES;
-
if (file) {
struct inode *inode = vma->vm_file->f_path.dentry->d_inode;
dev = inode->i_sb->s_dev;
@@ -742,22 +739,11 @@
#ifdef CONFIG_NUMA
extern int show_numa_map(struct seq_file *m, void *v);
-static int show_numa_map_checked(struct seq_file *m, void *v)
-{
- struct proc_maps_private *priv = m->private;
- struct task_struct *task = priv->task;
-
- if (maps_protect && !ptrace_may_access(task, PTRACE_MODE_READ))
- return -EACCES;
-
- return show_numa_map(m, v);
-}
-
static const struct seq_operations proc_pid_numa_maps_op = {
.start = m_start,
.next = m_next,
.stop = m_stop,
- .show = show_numa_map_checked
+ .show = show_numa_map,
};
static int numa_maps_open(struct inode *inode, struct file *file)
diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c
index 5d84e71..219bd79 100644
--- a/fs/proc/task_nommu.c
+++ b/fs/proc/task_nommu.c
@@ -110,11 +110,6 @@
static int show_map(struct seq_file *m, void *_vml)
{
struct vm_list_struct *vml = _vml;
- struct proc_maps_private *priv = m->private;
- struct task_struct *task = priv->task;
-
- if (maps_protect && !ptrace_may_access(task, PTRACE_MODE_READ))
- return -EACCES;
return nommu_vma_show(m, vml->vma);
}
diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c
index 9ac0f5e..841368b 100644
--- a/fs/proc/vmcore.c
+++ b/fs/proc/vmcore.c
@@ -165,14 +165,8 @@
return acc;
}
-static int open_vmcore(struct inode *inode, struct file *filp)
-{
- return 0;
-}
-
const struct file_operations proc_vmcore_operations = {
.read = read_vmcore,
- .open = open_vmcore,
};
static struct vmcore* __init get_new_element(void)
diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
index 3f49020..9a92203 100644
--- a/fs/ubifs/super.c
+++ b/fs/ubifs/super.c
@@ -848,7 +848,7 @@
Opt_err,
};
-static match_table_t tokens = {
+static const match_table_t tokens = {
{Opt_fast_unmount, "fast_unmount"},
{Opt_norm_unmount, "norm_unmount"},
{Opt_err, NULL},
diff --git a/fs/udf/super.c b/fs/udf/super.c
index 5698bbf..e25e701 100644
--- a/fs/udf/super.c
+++ b/fs/udf/super.c
@@ -369,7 +369,7 @@
Opt_err, Opt_uforget, Opt_uignore, Opt_gforget, Opt_gignore
};
-static match_table_t tokens = {
+static const match_table_t tokens = {
{Opt_novrs, "novrs"},
{Opt_nostrict, "nostrict"},
{Opt_bs, "bs=%u"},
diff --git a/fs/ufs/super.c b/fs/ufs/super.c
index 3141969..e65212d 100644
--- a/fs/ufs/super.c
+++ b/fs/ufs/super.c
@@ -309,7 +309,7 @@
Opt_err
};
-static match_table_t tokens = {
+static const match_table_t tokens = {
{Opt_type_old, "ufstype=old"},
{Opt_type_sunx86, "ufstype=sunx86"},
{Opt_type_sun, "ufstype=sun"},
@@ -1233,7 +1233,7 @@
{
struct ufs_sb_info *sbi = UFS_SB(vfs->mnt_sb);
unsigned mval = sbi->s_mount_opt & UFS_MOUNT_UFSTYPE;
- struct match_token *tp = tokens;
+ const struct match_token *tp = tokens;
while (tp->token != Opt_onerror_panic && tp->token != mval)
++tp;
diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c
index 18d3c84..7227b2e 100644
--- a/fs/xfs/linux-2.6/xfs_super.c
+++ b/fs/xfs/linux-2.6/xfs_super.c
@@ -158,7 +158,7 @@
Opt_barrier, Opt_nobarrier, Opt_err
};
-static match_table_t tokens = {
+static const match_table_t tokens = {
{Opt_barrier, "barrier"},
{Opt_nobarrier, "nobarrier"},
{Opt_err, NULL}
diff --git a/include/asm-cris/a.out.h b/include/asm-cris/a.out.h
deleted file mode 100644
index c82e9f9..0000000
--- a/include/asm-cris/a.out.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#ifndef __CRIS_A_OUT_H__
-#define __CRIS_A_OUT_H__
-
-/* we don't support a.out binaries on Linux/CRIS anyway, so this is
- * not really used but still needed because binfmt_elf.c for some reason
- * wants to know about a.out even if there is no interpreter available...
- */
-
-struct exec
-{
- unsigned long a_info; /* Use macros N_MAGIC, etc for access */
- unsigned a_text; /* length of text, in bytes */
- unsigned a_data; /* length of data, in bytes */
- unsigned a_bss; /* length of uninitialized data area for file, in bytes */
- unsigned a_syms; /* length of symbol table data in file, in bytes */
- unsigned a_entry; /* start address */
- unsigned a_trsize; /* length of relocation info for text, in bytes */
- unsigned a_drsize; /* length of relocation info for data, in bytes */
-};
-
-
-#define N_TRSIZE(a) ((a).a_trsize)
-#define N_DRSIZE(a) ((a).a_drsize)
-#define N_SYMSIZE(a) ((a).a_syms)
-
-#endif
diff --git a/include/asm-generic/siginfo.h b/include/asm-generic/siginfo.h
index 8786e01..9695701 100644
--- a/include/asm-generic/siginfo.h
+++ b/include/asm-generic/siginfo.h
@@ -199,6 +199,8 @@
*/
#define TRAP_BRKPT (__SI_FAULT|1) /* process breakpoint */
#define TRAP_TRACE (__SI_FAULT|2) /* process trace trap */
+#define TRAP_BRANCH (__SI_FAULT|3) /* process taken branch trap */
+#define TRAP_HWBKPT (__SI_FAULT|4) /* hardware breakpoint/watchpoint */
#define NSIGTRAP 2
/*
diff --git a/include/asm-generic/statfs.h b/include/asm-generic/statfs.h
index 1d01043..6129d68 100644
--- a/include/asm-generic/statfs.h
+++ b/include/asm-generic/statfs.h
@@ -6,33 +6,64 @@
typedef __kernel_fsid_t fsid_t;
#endif
+/*
+ * Most 64-bit platforms use 'long', while most 32-bit platforms use '__u32'.
+ * Yes, they differ in signedness as well as size.
+ * Special cases can override it for themselves -- except for S390x, which
+ * is just a little too special for us. And MIPS, which I'm not touching
+ * with a 10' pole.
+ */
+#ifndef __statfs_word
+#if BITS_PER_LONG == 64
+#define __statfs_word long
+#else
+#define __statfs_word __u32
+#endif
+#endif
+
struct statfs {
- __u32 f_type;
- __u32 f_bsize;
- __u32 f_blocks;
- __u32 f_bfree;
- __u32 f_bavail;
- __u32 f_files;
- __u32 f_ffree;
+ __statfs_word f_type;
+ __statfs_word f_bsize;
+ __statfs_word f_blocks;
+ __statfs_word f_bfree;
+ __statfs_word f_bavail;
+ __statfs_word f_files;
+ __statfs_word f_ffree;
__kernel_fsid_t f_fsid;
- __u32 f_namelen;
- __u32 f_frsize;
- __u32 f_spare[5];
+ __statfs_word f_namelen;
+ __statfs_word f_frsize;
+ __statfs_word f_spare[5];
};
+/*
+ * ARM needs to avoid the 32-bit padding at the end, for consistency
+ * between EABI and OABI
+ */
+#ifndef ARCH_PACK_STATFS64
+#define ARCH_PACK_STATFS64
+#endif
+
struct statfs64 {
- __u32 f_type;
- __u32 f_bsize;
+ __statfs_word f_type;
+ __statfs_word f_bsize;
__u64 f_blocks;
__u64 f_bfree;
__u64 f_bavail;
__u64 f_files;
__u64 f_ffree;
__kernel_fsid_t f_fsid;
- __u32 f_namelen;
- __u32 f_frsize;
- __u32 f_spare[5];
-};
+ __statfs_word f_namelen;
+ __statfs_word f_frsize;
+ __statfs_word f_spare[5];
+} ARCH_PACK_STATFS64;
+
+/*
+ * IA64 and x86_64 need to avoid the 32-bit padding at the end,
+ * to be compatible with the i386 ABI
+ */
+#ifndef ARCH_PACK_COMPAT_STATFS64
+#define ARCH_PACK_COMPAT_STATFS64
+#endif
struct compat_statfs64 {
__u32 f_type;
@@ -46,6 +77,6 @@
__u32 f_namelen;
__u32 f_frsize;
__u32 f_spare[5];
-};
+} ARCH_PACK_COMPAT_STATFS64;
#endif
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index cb752ba..7440a0d 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -385,6 +385,7 @@
. = ALIGN(align); \
VMLINUX_SYMBOL(__per_cpu_start) = .; \
.data.percpu : AT(ADDR(.data.percpu) - LOAD_OFFSET) { \
+ *(.data.percpu.page_aligned) \
*(.data.percpu) \
*(.data.percpu.shared_aligned) \
} \
diff --git a/include/asm-m32r/a.out.h b/include/asm-m32r/a.out.h
deleted file mode 100644
index ab150f5..0000000
--- a/include/asm-m32r/a.out.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef _ASM_M32R_A_OUT_H
-#define _ASM_M32R_A_OUT_H
-
-struct exec
-{
- unsigned long a_info; /* Use macros N_MAGIC, etc for access */
- unsigned a_text; /* length of text, in bytes */
- unsigned a_data; /* length of data, in bytes */
- unsigned a_bss; /* length of uninitialized data area for file, in bytes */
- unsigned a_syms; /* length of symbol table data in file, in bytes */
- unsigned a_entry; /* start address */
- unsigned a_trsize; /* length of relocation info for text, in bytes */
- unsigned a_drsize; /* length of relocation info for data, in bytes */
-};
-
-#define N_TRSIZE(a) ((a).a_trsize)
-#define N_DRSIZE(a) ((a).a_drsize)
-#define N_SYMSIZE(a) ((a).a_syms)
-
-#endif /* _ASM_M32R_A_OUT_H */
diff --git a/include/asm-m68k/atarihw.h b/include/asm-m68k/atarihw.h
index ecf007d..1412b4a 100644
--- a/include/asm-m68k/atarihw.h
+++ b/include/asm-m68k/atarihw.h
@@ -39,7 +39,6 @@
#define MACH_IS_TT ((atari_mch_cookie >> 16) == ATARI_MCH_TT)
#define MACH_IS_FALCON ((atari_mch_cookie >> 16) == ATARI_MCH_FALCON)
#define MACH_IS_MEDUSA (atari_mch_type == ATARI_MACH_MEDUSA)
-#define MACH_IS_HADES (atari_mch_type == ATARI_MACH_HADES)
#define MACH_IS_AB40 (atari_mch_type == ATARI_MACH_AB40)
/* values for atari_switches */
diff --git a/include/asm-m68k/dma-mapping.h b/include/asm-m68k/dma-mapping.h
index 91f7944..26f5054 100644
--- a/include/asm-m68k/dma-mapping.h
+++ b/include/asm-m68k/dma-mapping.h
@@ -74,6 +74,14 @@
extern void dma_sync_sg_for_device(struct device *, struct scatterlist *, int,
enum dma_data_direction);
+static inline void dma_sync_single_range_for_device(struct device *dev,
+ dma_addr_t dma_handle, unsigned long offset, size_t size,
+ enum dma_data_direction direction)
+{
+ /* just sync everything for now */
+ dma_sync_single_for_device(dev, dma_handle, offset + size, direction);
+}
+
static inline void dma_sync_single_for_cpu(struct device *dev, dma_addr_t handle,
size_t size, enum dma_data_direction dir)
{
@@ -84,6 +92,14 @@
{
}
+static inline void dma_sync_single_range_for_cpu(struct device *dev,
+ dma_addr_t dma_handle, unsigned long offset, size_t size,
+ enum dma_data_direction direction)
+{
+ /* just sync everything for now */
+ dma_sync_single_for_cpu(dev, dma_handle, offset + size, direction);
+}
+
static inline int dma_mapping_error(struct device *dev, dma_addr_t handle)
{
return 0;
diff --git a/include/asm-m68k/dma.h b/include/asm-m68k/dma.h
index d0c9e61..4240fbc 100644
--- a/include/asm-m68k/dma.h
+++ b/include/asm-m68k/dma.h
@@ -11,10 +11,6 @@
extern int request_dma(unsigned int dmanr, const char * device_id); /* reserve a DMA channel */
extern void free_dma(unsigned int dmanr); /* release it again */
-#ifdef CONFIG_PCI
-extern int isa_dma_bridge_buggy;
-#else
#define isa_dma_bridge_buggy (0)
-#endif
#endif /* _M68K_DMA_H */
diff --git a/include/asm-m68k/entry.h b/include/asm-m68k/entry.h
index f8f6b18..5202f5a 100644
--- a/include/asm-m68k/entry.h
+++ b/include/asm-m68k/entry.h
@@ -31,7 +31,7 @@
*/
/* the following macro is used when enabling interrupts */
-#if defined(MACH_ATARI_ONLY) && !defined(CONFIG_HADES)
+#if defined(MACH_ATARI_ONLY)
/* block out HSYNC on the atari */
#define ALLOWINT (~0x400)
#define MAX_NOINT_IPL 3
diff --git a/include/asm-m68k/io.h b/include/asm-m68k/io.h
index 657187f..9e673e3 100644
--- a/include/asm-m68k/io.h
+++ b/include/asm-m68k/io.h
@@ -7,15 +7,12 @@
* - added skeleton for GG-II and Amiga PCMCIA
* 2/3/01 RZ: - moved a few more defs into raw_io.h
*
- * inX/outX/readX/writeX should not be used by any driver unless it does
- * ISA or PCI access. Other drivers should use function defined in raw_io.h
+ * inX/outX should not be used by any driver unless it does
+ * ISA access. Other drivers should use function defined in raw_io.h
* or define its own macros on top of these.
*
- * inX(),outX() are for PCI and ISA I/O
- * readX(),writeX() are for PCI memory
+ * inX(),outX() are for ISA I/O
* isa_readX(),isa_writeX() are for ISA memory
- *
- * moved mem{cpy,set}_*io inside CONFIG_PCI
*/
#ifndef _IO_H
@@ -256,10 +253,7 @@
(ISA_SEX ? raw_outsl(isa_itl(port), (u32 *)(buf), (nr)) : \
raw_outsw_swapw(isa_itw(port), (u16 *)(buf), (nr)<<1))
-#endif /* CONFIG_ISA */
-
-#if defined(CONFIG_ISA) && !defined(CONFIG_PCI)
#define inb isa_inb
#define inb_p isa_inb_p
#define outb isa_outb
@@ -282,55 +276,9 @@
#define readw isa_readw
#define writeb isa_writeb
#define writew isa_writew
-#endif /* CONFIG_ISA */
-#if defined(CONFIG_PCI)
+#else /* CONFIG_ISA */
-#define readl(addr) in_le32(addr)
-#define writel(val,addr) out_le32((addr),(val))
-
-/* those can be defined for both ISA and PCI - it won't work though */
-#define readb(addr) in_8(addr)
-#define readw(addr) in_le16(addr)
-#define writeb(val,addr) out_8((addr),(val))
-#define writew(val,addr) out_le16((addr),(val))
-
-#define readb_relaxed(addr) readb(addr)
-#define readw_relaxed(addr) readw(addr)
-#define readl_relaxed(addr) readl(addr)
-
-#ifndef CONFIG_ISA
-#define inb(port) in_8(port)
-#define outb(val,port) out_8((port),(val))
-#define inw(port) in_le16(port)
-#define outw(val,port) out_le16((port),(val))
-#define inl(port) in_le32(port)
-#define outl(val,port) out_le32((port),(val))
-
-#else
-/*
- * kernel with both ISA and PCI compiled in, those have
- * conflicting defs for in/out. Simply consider port < 1024
- * ISA and everything else PCI. read,write not defined
- * in this case
- */
-#define inb(port) ((port)<1024 ? isa_inb(port) : in_8(port))
-#define inb_p(port) ((port)<1024 ? isa_inb_p(port) : in_8(port))
-#define inw(port) ((port)<1024 ? isa_inw(port) : in_le16(port))
-#define inw_p(port) ((port)<1024 ? isa_inw_p(port) : in_le16(port))
-#define inl(port) ((port)<1024 ? isa_inl(port) : in_le32(port))
-#define inl_p(port) ((port)<1024 ? isa_inl_p(port) : in_le32(port))
-
-#define outb(val,port) ((port)<1024 ? isa_outb((val),(port)) : out_8((port),(val)))
-#define outb_p(val,port) ((port)<1024 ? isa_outb_p((val),(port)) : out_8((port),(val)))
-#define outw(val,port) ((port)<1024 ? isa_outw((val),(port)) : out_le16((port),(val)))
-#define outw_p(val,port) ((port)<1024 ? isa_outw_p((val),(port)) : out_le16((port),(val)))
-#define outl(val,port) ((port)<1024 ? isa_outl((val),(port)) : out_le32((port),(val)))
-#define outl_p(val,port) ((port)<1024 ? isa_outl_p((val),(port)) : out_le32((port),(val)))
-#endif
-#endif /* CONFIG_PCI */
-
-#if !defined(CONFIG_ISA) && !defined(CONFIG_PCI)
/*
* We need to define dummy functions for GENERIC_IOMAP support.
*/
@@ -357,11 +305,11 @@
#define writeb(val,addr) out_8((addr),(val))
#define readw(addr) in_le16(addr)
#define writew(val,addr) out_le16((addr),(val))
-#endif
-#if !defined(CONFIG_PCI)
+
+#endif /* CONFIG_ISA */
+
#define readl(addr) in_le32(addr)
#define writel(val,addr) out_le32((addr),(val))
-#endif
#define mmiowb()
diff --git a/include/asm-m68k/pci.h b/include/asm-m68k/pci.h
index 678cb0b..4ad0aea 100644
--- a/include/asm-m68k/pci.h
+++ b/include/asm-m68k/pci.h
@@ -1,52 +1,7 @@
#ifndef _ASM_M68K_PCI_H
#define _ASM_M68K_PCI_H
-/*
- * asm-m68k/pci_m68k.h - m68k specific PCI declarations.
- *
- * Written by Wout Klaren.
- */
-
-#include <asm/scatterlist.h>
-
-struct pci_ops;
-
-/*
- * Structure with hardware dependent information and functions of the
- * PCI bus.
- */
-
-struct pci_bus_info
-{
- /*
- * Resources of the PCI bus.
- */
-
- struct resource mem_space;
- struct resource io_space;
-
- /*
- * System dependent functions.
- */
-
- struct pci_ops *m68k_pci_ops;
-
- void (*fixup)(int pci_modify);
- void (*conf_device)(struct pci_dev *dev);
-};
-
-#define pcibios_assign_all_busses() 0
-#define pcibios_scan_all_fns(a, b) 0
-
-static inline void pcibios_set_master(struct pci_dev *dev)
-{
- /* No special bus mastering setup handling */
-}
-
-static inline void pcibios_penalize_isa_irq(int irq, int active)
-{
- /* We don't do dynamic PCI IRQ allocation */
-}
+#include <asm-generic/pci-dma-compat.h>
/* The PCI address space does equal the physical memory
* address space. The networking and block device layers use
diff --git a/include/asm-m68k/virtconvert.h b/include/asm-m68k/virtconvert.h
index dea32fb..22ab05c 100644
--- a/include/asm-m68k/virtconvert.h
+++ b/include/asm-m68k/virtconvert.h
@@ -40,15 +40,9 @@
/*
* IO bus memory addresses are 1:1 with the physical address,
- * except on the PCI bus of the Hades.
*/
-#ifdef CONFIG_HADES
-#define virt_to_bus(a) (virt_to_phys(a) + (MACH_IS_HADES ? 0x80000000 : 0))
-#define bus_to_virt(a) (phys_to_virt((a) - (MACH_IS_HADES ? 0x80000000 : 0)))
-#else
#define virt_to_bus virt_to_phys
#define bus_to_virt phys_to_virt
-#endif
#endif
#endif
diff --git a/include/asm-parisc/a.out.h b/include/asm-parisc/a.out.h
deleted file mode 100644
index eb04e34..0000000
--- a/include/asm-parisc/a.out.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef __PARISC_A_OUT_H__
-#define __PARISC_A_OUT_H__
-
-struct exec
-{
- unsigned int a_info; /* Use macros N_MAGIC, etc for access */
- unsigned a_text; /* length of text, in bytes */
- unsigned a_data; /* length of data, in bytes */
- unsigned a_bss; /* length of uninitialized data area for file, in bytes */
- unsigned a_syms; /* length of symbol table data in file, in bytes */
- unsigned a_entry; /* start address */
- unsigned a_trsize; /* length of relocation info for text, in bytes */
- unsigned a_drsize; /* length of relocation info for data, in bytes */
-};
-
-#define N_TRSIZE(a) ((a).a_trsize)
-#define N_DRSIZE(a) ((a).a_drsize)
-#define N_SYMSIZE(a) ((a).a_syms)
-
-#endif /* __A_OUT_GNU_H__ */
diff --git a/include/asm-parisc/siginfo.h b/include/asm-parisc/siginfo.h
index d4909f5..d703472 100644
--- a/include/asm-parisc/siginfo.h
+++ b/include/asm-parisc/siginfo.h
@@ -3,11 +3,6 @@
#include <asm-generic/siginfo.h>
-/*
- * SIGTRAP si_codes
- */
-#define TRAP_BRANCH (__SI_FAULT|3) /* process taken branch trap */
-#define TRAP_HWBKPT (__SI_FAULT|4) /* hardware breakpoint or watchpoint */
#undef NSIGTRAP
#define NSIGTRAP 4
diff --git a/include/asm-parisc/statfs.h b/include/asm-parisc/statfs.h
index 1d2b813..324bea9 100644
--- a/include/asm-parisc/statfs.h
+++ b/include/asm-parisc/statfs.h
@@ -1,58 +1,7 @@
#ifndef _PARISC_STATFS_H
#define _PARISC_STATFS_H
-#ifndef __KERNEL_STRICT_NAMES
-
-#include <linux/types.h>
-
-typedef __kernel_fsid_t fsid_t;
-
-#endif
-
-/*
- * It appears that PARISC could be 64 _or_ 32 bit.
- * 64-bit fields must be explicitly 64-bit in statfs64.
- */
-struct statfs {
- long f_type;
- long f_bsize;
- long f_blocks;
- long f_bfree;
- long f_bavail;
- long f_files;
- long f_ffree;
- __kernel_fsid_t f_fsid;
- long f_namelen;
- long f_frsize;
- long f_spare[5];
-};
-
-struct statfs64 {
- long f_type;
- long f_bsize;
- __u64 f_blocks;
- __u64 f_bfree;
- __u64 f_bavail;
- __u64 f_files;
- __u64 f_ffree;
- __kernel_fsid_t f_fsid;
- long f_namelen;
- long f_frsize;
- long f_spare[5];
-};
-
-struct compat_statfs64 {
- __u32 f_type;
- __u32 f_bsize;
- __u64 f_blocks;
- __u64 f_bfree;
- __u64 f_bavail;
- __u64 f_files;
- __u64 f_ffree;
- __kernel_fsid_t f_fsid;
- __u32 f_namelen;
- __u32 f_frsize;
- __u32 f_spare[5];
-};
+#define __statfs_word long
+#include <asm-generic/statfs.h>
#endif
diff --git a/include/asm-x86/apic.h b/include/asm-x86/apic.h
index 65590c9..d76a083 100644
--- a/include/asm-x86/apic.h
+++ b/include/asm-x86/apic.h
@@ -9,6 +9,8 @@
#include <asm/apicdef.h>
#include <asm/processor.h>
#include <asm/system.h>
+#include <asm/cpufeature.h>
+#include <asm/msr.h>
#define ARCH_APICTIMER_STOPS_ON_C3 1
@@ -47,8 +49,6 @@
#ifdef CONFIG_PARAVIRT
#include <asm/paravirt.h>
#else
-#define apic_write native_apic_write
-#define apic_read native_apic_read
#define setup_boot_clock setup_boot_APIC_clock
#define setup_secondary_clock setup_secondary_APIC_clock
#endif
@@ -60,7 +60,7 @@
extern void xapic_icr_write(u32, u32);
extern int setup_profiling_timer(unsigned int);
-static inline void native_apic_write(unsigned long reg, u32 v)
+static inline void native_apic_mem_write(u32 reg, u32 v)
{
volatile u32 *addr = (volatile u32 *)(APIC_BASE + reg);
@@ -69,15 +69,68 @@
ASM_OUTPUT2("0" (v), "m" (*addr)));
}
-static inline u32 native_apic_read(unsigned long reg)
+static inline u32 native_apic_mem_read(u32 reg)
{
return *((volatile u32 *)(APIC_BASE + reg));
}
-extern void apic_wait_icr_idle(void);
-extern u32 safe_apic_wait_icr_idle(void);
+static inline void native_apic_msr_write(u32 reg, u32 v)
+{
+ if (reg == APIC_DFR || reg == APIC_ID || reg == APIC_LDR ||
+ reg == APIC_LVR)
+ return;
+
+ wrmsr(APIC_BASE_MSR + (reg >> 4), v, 0);
+}
+
+static inline u32 native_apic_msr_read(u32 reg)
+{
+ u32 low, high;
+
+ if (reg == APIC_DFR)
+ return -1;
+
+ rdmsr(APIC_BASE_MSR + (reg >> 4), low, high);
+ return low;
+}
+
+#ifndef CONFIG_X86_32
+extern int x2apic, x2apic_preenabled;
+extern void check_x2apic(void);
+extern void enable_x2apic(void);
+extern void enable_IR_x2apic(void);
+extern void x2apic_icr_write(u32 low, u32 id);
+#endif
+
+struct apic_ops {
+ u32 (*read)(u32 reg);
+ void (*write)(u32 reg, u32 v);
+ u64 (*icr_read)(void);
+ void (*icr_write)(u32 low, u32 high);
+ void (*wait_icr_idle)(void);
+ u32 (*safe_wait_icr_idle)(void);
+};
+
+extern struct apic_ops *apic_ops;
+
+#define apic_read (apic_ops->read)
+#define apic_write (apic_ops->write)
+#define apic_icr_read (apic_ops->icr_read)
+#define apic_icr_write (apic_ops->icr_write)
+#define apic_wait_icr_idle (apic_ops->wait_icr_idle)
+#define safe_apic_wait_icr_idle (apic_ops->safe_wait_icr_idle)
+
extern int get_physical_broadcast(void);
+#ifdef CONFIG_X86_64
+static inline void ack_x2APIC_irq(void)
+{
+ /* Docs say use 0 for future compatibility */
+ native_apic_msr_write(APIC_EOI, 0);
+}
+#endif
+
+
static inline void ack_APIC_irq(void)
{
/*
diff --git a/include/asm-x86/apicdef.h b/include/asm-x86/apicdef.h
index c40687d..b922c85 100644
--- a/include/asm-x86/apicdef.h
+++ b/include/asm-x86/apicdef.h
@@ -105,6 +105,7 @@
#define APIC_TMICT 0x380
#define APIC_TMCCT 0x390
#define APIC_TDCR 0x3E0
+#define APIC_SELF_IPI 0x3F0
#define APIC_TDR_DIV_TMBASE (1 << 2)
#define APIC_TDR_DIV_1 0xB
#define APIC_TDR_DIV_2 0x0
@@ -128,6 +129,8 @@
#define APIC_EILVT3 0x530
#define APIC_BASE (fix_to_virt(FIX_APIC_BASE))
+#define APIC_BASE_MSR 0x800
+#define X2APIC_ENABLE (1UL << 10)
#ifdef CONFIG_X86_32
# define MAX_IO_APICS 64
diff --git a/include/asm-x86/arch_hooks.h b/include/asm-x86/arch_hooks.h
index 72adc3a..de4596b 100644
--- a/include/asm-x86/arch_hooks.h
+++ b/include/asm-x86/arch_hooks.h
@@ -12,8 +12,6 @@
/* these aren't arch hooks, they are generic routines
* that can be used by the hooks */
extern void init_ISA_irqs(void);
-extern void apic_intr_init(void);
-extern void smp_intr_init(void);
extern irqreturn_t timer_interrupt(int irq, void *dev_id);
/* these are the defined hooks */
diff --git a/include/asm-x86/mach-bigsmp/mach_apic.h b/include/asm-x86/bigsmp/apic.h
similarity index 94%
rename from include/asm-x86/mach-bigsmp/mach_apic.h
rename to include/asm-x86/bigsmp/apic.h
index 05362d4..0a9cd7c 100644
--- a/include/asm-x86/mach-bigsmp/mach_apic.h
+++ b/include/asm-x86/bigsmp/apic.h
@@ -1,5 +1,5 @@
-#ifndef ASM_X86__MACH_BIGSMP__MACH_APIC_H
-#define ASM_X86__MACH_BIGSMP__MACH_APIC_H
+#ifndef __ASM_MACH_APIC_H
+#define __ASM_MACH_APIC_H
#define xapic_phys_to_log_apicid(cpu) (per_cpu(x86_bios_cpu_apicid, cpu))
#define esr_disable (1)
@@ -11,7 +11,7 @@
/* Round robin the irqs amoung the online cpus */
static inline cpumask_t target_cpus(void)
-{
+{
static unsigned long cpu = NR_CPUS;
do {
if (cpu >= NR_CPUS)
@@ -23,7 +23,7 @@
}
#undef APIC_DEST_LOGICAL
-#define APIC_DEST_LOGICAL 0
+#define APIC_DEST_LOGICAL 0
#define TARGET_CPUS (target_cpus())
#define APIC_DFR_VALUE (APIC_DFR_FLAT)
#define INT_DELIVERY_MODE (dest_Fixed)
@@ -141,4 +141,4 @@
return cpuid_apic >> index_msb;
}
-#endif /* ASM_X86__MACH_BIGSMP__MACH_APIC_H */
+#endif /* __ASM_MACH_APIC_H */
diff --git a/include/asm-x86/bigsmp/apicdef.h b/include/asm-x86/bigsmp/apicdef.h
new file mode 100644
index 0000000..392c3f5
--- /dev/null
+++ b/include/asm-x86/bigsmp/apicdef.h
@@ -0,0 +1,13 @@
+#ifndef __ASM_MACH_APICDEF_H
+#define __ASM_MACH_APICDEF_H
+
+#define APIC_ID_MASK (0xFF<<24)
+
+static inline unsigned get_apic_id(unsigned long x)
+{
+ return (((x)>>24)&0xFF);
+}
+
+#define GET_APIC_ID(x) get_apic_id(x)
+
+#endif
diff --git a/include/asm-x86/mach-bigsmp/mach_ipi.h b/include/asm-x86/bigsmp/ipi.h
similarity index 77%
rename from include/asm-x86/mach-bigsmp/mach_ipi.h
rename to include/asm-x86/bigsmp/ipi.h
index b1b0f96..9404c53 100644
--- a/include/asm-x86/mach-bigsmp/mach_ipi.h
+++ b/include/asm-x86/bigsmp/ipi.h
@@ -1,5 +1,5 @@
-#ifndef ASM_X86__MACH_BIGSMP__MACH_IPI_H
-#define ASM_X86__MACH_BIGSMP__MACH_IPI_H
+#ifndef __ASM_MACH_IPI_H
+#define __ASM_MACH_IPI_H
void send_IPI_mask_sequence(cpumask_t mask, int vector);
@@ -22,4 +22,4 @@
send_IPI_mask(cpu_online_map, vector);
}
-#endif /* ASM_X86__MACH_BIGSMP__MACH_IPI_H */
+#endif /* __ASM_MACH_IPI_H */
diff --git a/include/asm-x86/bios_ebda.h b/include/asm-x86/bios_ebda.h
index ec42ed8..79b4b88 100644
--- a/include/asm-x86/bios_ebda.h
+++ b/include/asm-x86/bios_ebda.h
@@ -16,4 +16,21 @@
void reserve_ebda_region(void);
+#ifdef CONFIG_X86_CHECK_BIOS_CORRUPTION
+/*
+ * This is obviously not a great place for this, but we want to be
+ * able to scatter it around anywhere in the kernel.
+ */
+void check_for_bios_corruption(void);
+void start_periodic_check_for_corruption(void);
+#else
+static inline void check_for_bios_corruption(void)
+{
+}
+
+static inline void start_periodic_check_for_corruption(void)
+{
+}
+#endif
+
#endif /* ASM_X86__BIOS_EBDA_H */
diff --git a/include/asm-x86/boot.h b/include/asm-x86/boot.h
index 825de5d..1d63bd5 100644
--- a/include/asm-x86/boot.h
+++ b/include/asm-x86/boot.h
@@ -2,9 +2,7 @@
#define ASM_X86__BOOT_H
/* Don't touch these, unless you really know what you're doing. */
-#define DEF_INITSEG 0x9000
#define DEF_SYSSEG 0x1000
-#define DEF_SETUPSEG 0x9020
#define DEF_SYSSIZE 0x7F00
/* Internal svga startup constants */
diff --git a/include/asm-x86/bugs.h b/include/asm-x86/bugs.h
index 4761c46..dc60498 100644
--- a/include/asm-x86/bugs.h
+++ b/include/asm-x86/bugs.h
@@ -2,6 +2,11 @@
#define ASM_X86__BUGS_H
extern void check_bugs(void);
+
+#if defined(CONFIG_CPU_SUP_INTEL) && defined(CONFIG_X86_32)
int ppro_with_ram_bug(void);
+#else
+static inline int ppro_with_ram_bug(void) { return 0; }
+#endif
#endif /* ASM_X86__BUGS_H */
diff --git a/include/asm-x86/cpufeature.h b/include/asm-x86/cpufeature.h
index 250fa0c..adfeae6 100644
--- a/include/asm-x86/cpufeature.h
+++ b/include/asm-x86/cpufeature.h
@@ -6,7 +6,13 @@
#include <asm/required-features.h>
-#define NCAPINTS 8 /* N 32-bit words worth of info */
+#define NCAPINTS 9 /* N 32-bit words worth of info */
+
+/*
+ * Note: If the comment begins with a quoted string, that string is used
+ * in /proc/cpuinfo instead of the macro name. If the string is "",
+ * this feature bit is not displayed in /proc/cpuinfo at all.
+ */
/* Intel-defined CPU features, CPUID level 0x00000001 (edx), word 0 */
#define X86_FEATURE_FPU (0*32+ 0) /* Onboard FPU */
@@ -14,7 +20,7 @@
#define X86_FEATURE_DE (0*32+ 2) /* Debugging Extensions */
#define X86_FEATURE_PSE (0*32+ 3) /* Page Size Extensions */
#define X86_FEATURE_TSC (0*32+ 4) /* Time Stamp Counter */
-#define X86_FEATURE_MSR (0*32+ 5) /* Model-Specific Registers, RDMSR, WRMSR */
+#define X86_FEATURE_MSR (0*32+ 5) /* Model-Specific Registers */
#define X86_FEATURE_PAE (0*32+ 6) /* Physical Address Extensions */
#define X86_FEATURE_MCE (0*32+ 7) /* Machine Check Architecture */
#define X86_FEATURE_CX8 (0*32+ 8) /* CMPXCHG8 instruction */
@@ -23,22 +29,23 @@
#define X86_FEATURE_MTRR (0*32+12) /* Memory Type Range Registers */
#define X86_FEATURE_PGE (0*32+13) /* Page Global Enable */
#define X86_FEATURE_MCA (0*32+14) /* Machine Check Architecture */
-#define X86_FEATURE_CMOV (0*32+15) /* CMOV instruction (FCMOVCC and FCOMI too if FPU present) */
+#define X86_FEATURE_CMOV (0*32+15) /* CMOV instructions */
+ /* (plus FCMOVcc, FCOMI with FPU) */
#define X86_FEATURE_PAT (0*32+16) /* Page Attribute Table */
#define X86_FEATURE_PSE36 (0*32+17) /* 36-bit PSEs */
#define X86_FEATURE_PN (0*32+18) /* Processor serial number */
-#define X86_FEATURE_CLFLSH (0*32+19) /* Supports the CLFLUSH instruction */
-#define X86_FEATURE_DS (0*32+21) /* Debug Store */
+#define X86_FEATURE_CLFLSH (0*32+19) /* "clflush" CLFLUSH instruction */
+#define X86_FEATURE_DS (0*32+21) /* "dts" Debug Store */
#define X86_FEATURE_ACPI (0*32+22) /* ACPI via MSR */
#define X86_FEATURE_MMX (0*32+23) /* Multimedia Extensions */
-#define X86_FEATURE_FXSR (0*32+24) /* FXSAVE and FXRSTOR instructions (fast save and restore */
- /* of FPU context), and CR4.OSFXSR available */
-#define X86_FEATURE_XMM (0*32+25) /* Streaming SIMD Extensions */
-#define X86_FEATURE_XMM2 (0*32+26) /* Streaming SIMD Extensions-2 */
-#define X86_FEATURE_SELFSNOOP (0*32+27) /* CPU self snoop */
+#define X86_FEATURE_FXSR (0*32+24) /* FXSAVE/FXRSTOR, CR4.OSFXSR */
+#define X86_FEATURE_XMM (0*32+25) /* "sse" */
+#define X86_FEATURE_XMM2 (0*32+26) /* "sse2" */
+#define X86_FEATURE_SELFSNOOP (0*32+27) /* "ss" CPU self snoop */
#define X86_FEATURE_HT (0*32+28) /* Hyper-Threading */
-#define X86_FEATURE_ACC (0*32+29) /* Automatic clock control */
+#define X86_FEATURE_ACC (0*32+29) /* "tm" Automatic clock control */
#define X86_FEATURE_IA64 (0*32+30) /* IA-64 processor */
+#define X86_FEATURE_PBE (0*32+31) /* Pending Break Enable */
/* AMD-defined CPU features, CPUID level 0x80000001, word 1 */
/* Don't duplicate feature flags which are redundant with Intel! */
@@ -46,7 +53,8 @@
#define X86_FEATURE_MP (1*32+19) /* MP Capable. */
#define X86_FEATURE_NX (1*32+20) /* Execute Disable */
#define X86_FEATURE_MMXEXT (1*32+22) /* AMD MMX extensions */
-#define X86_FEATURE_GBPAGES (1*32+26) /* GB pages */
+#define X86_FEATURE_FXSR_OPT (1*32+25) /* FXSAVE/FXRSTOR optimizations */
+#define X86_FEATURE_GBPAGES (1*32+26) /* "pdpe1gb" GB pages */
#define X86_FEATURE_RDTSCP (1*32+27) /* RDTSCP */
#define X86_FEATURE_LM (1*32+29) /* Long Mode (x86-64) */
#define X86_FEATURE_3DNOWEXT (1*32+30) /* AMD 3DNow! extensions */
@@ -64,53 +72,79 @@
#define X86_FEATURE_CYRIX_ARR (3*32+ 2) /* Cyrix ARRs (= MTRRs) */
#define X86_FEATURE_CENTAUR_MCR (3*32+ 3) /* Centaur MCRs (= MTRRs) */
/* cpu types for specific tunings: */
-#define X86_FEATURE_K8 (3*32+ 4) /* Opteron, Athlon64 */
-#define X86_FEATURE_K7 (3*32+ 5) /* Athlon */
-#define X86_FEATURE_P3 (3*32+ 6) /* P3 */
-#define X86_FEATURE_P4 (3*32+ 7) /* P4 */
+#define X86_FEATURE_K8 (3*32+ 4) /* "" Opteron, Athlon64 */
+#define X86_FEATURE_K7 (3*32+ 5) /* "" Athlon */
+#define X86_FEATURE_P3 (3*32+ 6) /* "" P3 */
+#define X86_FEATURE_P4 (3*32+ 7) /* "" P4 */
#define X86_FEATURE_CONSTANT_TSC (3*32+ 8) /* TSC ticks at a constant rate */
#define X86_FEATURE_UP (3*32+ 9) /* smp kernel running on up */
-#define X86_FEATURE_FXSAVE_LEAK (3*32+10) /* FXSAVE leaks FOP/FIP/FOP */
+#define X86_FEATURE_FXSAVE_LEAK (3*32+10) /* "" FXSAVE leaks FOP/FIP/FOP */
#define X86_FEATURE_ARCH_PERFMON (3*32+11) /* Intel Architectural PerfMon */
+#define X86_FEATURE_NOPL (3*32+20) /* The NOPL (0F 1F) instructions */
#define X86_FEATURE_PEBS (3*32+12) /* Precise-Event Based Sampling */
#define X86_FEATURE_BTS (3*32+13) /* Branch Trace Store */
-#define X86_FEATURE_SYSCALL32 (3*32+14) /* syscall in ia32 userspace */
-#define X86_FEATURE_SYSENTER32 (3*32+15) /* sysenter in ia32 userspace */
-#define X86_FEATURE_REP_GOOD (3*32+16) /* rep microcode works well on this CPU */
-#define X86_FEATURE_MFENCE_RDTSC (3*32+17) /* Mfence synchronizes RDTSC */
-#define X86_FEATURE_LFENCE_RDTSC (3*32+18) /* Lfence synchronizes RDTSC */
-#define X86_FEATURE_11AP (3*32+19) /* Bad local APIC aka 11AP */
+#define X86_FEATURE_SYSCALL32 (3*32+14) /* "" syscall in ia32 userspace */
+#define X86_FEATURE_SYSENTER32 (3*32+15) /* "" sysenter in ia32 userspace */
+#define X86_FEATURE_REP_GOOD (3*32+16) /* rep microcode works well */
+#define X86_FEATURE_MFENCE_RDTSC (3*32+17) /* "" Mfence synchronizes RDTSC */
+#define X86_FEATURE_LFENCE_RDTSC (3*32+18) /* "" Lfence synchronizes RDTSC */
+#define X86_FEATURE_11AP (3*32+19) /* "" Bad local APIC aka 11AP */
#define X86_FEATURE_NOPL (3*32+20) /* The NOPL (0F 1F) instructions */
#define X86_FEATURE_AMDC1E (3*32+21) /* AMD C1E detected */
+#define X86_FEATURE_XTOPOLOGY (3*32+21) /* cpu topology enum extensions */
/* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */
-#define X86_FEATURE_XMM3 (4*32+ 0) /* Streaming SIMD Extensions-3 */
-#define X86_FEATURE_MWAIT (4*32+ 3) /* Monitor/Mwait support */
-#define X86_FEATURE_DSCPL (4*32+ 4) /* CPL Qualified Debug Store */
+#define X86_FEATURE_XMM3 (4*32+ 0) /* "pni" SSE-3 */
+#define X86_FEATURE_PCLMULQDQ (4*32+ 1) /* PCLMULQDQ instruction */
+#define X86_FEATURE_DTES64 (4*32+ 2) /* 64-bit Debug Store */
+#define X86_FEATURE_MWAIT (4*32+ 3) /* "monitor" Monitor/Mwait support */
+#define X86_FEATURE_DSCPL (4*32+ 4) /* "ds_cpl" CPL Qual. Debug Store */
+#define X86_FEATURE_VMX (4*32+ 5) /* Hardware virtualization */
+#define X86_FEATURE_SMX (4*32+ 6) /* Safer mode */
#define X86_FEATURE_EST (4*32+ 7) /* Enhanced SpeedStep */
#define X86_FEATURE_TM2 (4*32+ 8) /* Thermal Monitor 2 */
+#define X86_FEATURE_SSSE3 (4*32+ 9) /* Supplemental SSE-3 */
#define X86_FEATURE_CID (4*32+10) /* Context ID */
+#define X86_FEATURE_FMA (4*32+12) /* Fused multiply-add */
#define X86_FEATURE_CX16 (4*32+13) /* CMPXCHG16B */
#define X86_FEATURE_XTPR (4*32+14) /* Send Task Priority Messages */
+#define X86_FEATURE_PDCM (4*32+15) /* Performance Capabilities */
#define X86_FEATURE_DCA (4*32+18) /* Direct Cache Access */
-#define X86_FEATURE_XMM4_2 (4*32+20) /* Streaming SIMD Extensions-4.2 */
+#define X86_FEATURE_XMM4_1 (4*32+19) /* "sse4_1" SSE-4.1 */
+#define X86_FEATURE_XMM4_2 (4*32+20) /* "sse4_2" SSE-4.2 */
+#define X86_FEATURE_X2APIC (4*32+21) /* x2APIC */
+#define X86_FEATURE_AES (4*32+25) /* AES instructions */
+#define X86_FEATURE_XSAVE (4*32+26) /* XSAVE/XRSTOR/XSETBV/XGETBV */
+#define X86_FEATURE_OSXSAVE (4*32+27) /* "" XSAVE enabled in the OS */
+#define X86_FEATURE_AVX (4*32+28) /* Advanced Vector Extensions */
/* VIA/Cyrix/Centaur-defined CPU features, CPUID level 0xC0000001, word 5 */
-#define X86_FEATURE_XSTORE (5*32+ 2) /* on-CPU RNG present (xstore insn) */
-#define X86_FEATURE_XSTORE_EN (5*32+ 3) /* on-CPU RNG enabled */
-#define X86_FEATURE_XCRYPT (5*32+ 6) /* on-CPU crypto (xcrypt insn) */
-#define X86_FEATURE_XCRYPT_EN (5*32+ 7) /* on-CPU crypto enabled */
+#define X86_FEATURE_XSTORE (5*32+ 2) /* "rng" RNG present (xstore) */
+#define X86_FEATURE_XSTORE_EN (5*32+ 3) /* "rng_en" RNG enabled */
+#define X86_FEATURE_XCRYPT (5*32+ 6) /* "ace" on-CPU crypto (xcrypt) */
+#define X86_FEATURE_XCRYPT_EN (5*32+ 7) /* "ace_en" on-CPU crypto enabled */
#define X86_FEATURE_ACE2 (5*32+ 8) /* Advanced Cryptography Engine v2 */
#define X86_FEATURE_ACE2_EN (5*32+ 9) /* ACE v2 enabled */
-#define X86_FEATURE_PHE (5*32+ 10) /* PadLock Hash Engine */
-#define X86_FEATURE_PHE_EN (5*32+ 11) /* PHE enabled */
-#define X86_FEATURE_PMM (5*32+ 12) /* PadLock Montgomery Multiplier */
-#define X86_FEATURE_PMM_EN (5*32+ 13) /* PMM enabled */
+#define X86_FEATURE_PHE (5*32+10) /* PadLock Hash Engine */
+#define X86_FEATURE_PHE_EN (5*32+11) /* PHE enabled */
+#define X86_FEATURE_PMM (5*32+12) /* PadLock Montgomery Multiplier */
+#define X86_FEATURE_PMM_EN (5*32+13) /* PMM enabled */
/* More extended AMD flags: CPUID level 0x80000001, ecx, word 6 */
#define X86_FEATURE_LAHF_LM (6*32+ 0) /* LAHF/SAHF in long mode */
#define X86_FEATURE_CMP_LEGACY (6*32+ 1) /* If yes HyperThreading not valid */
-#define X86_FEATURE_IBS (6*32+ 10) /* Instruction Based Sampling */
+#define X86_FEATURE_SVM (6*32+ 2) /* Secure virtual machine */
+#define X86_FEATURE_EXTAPIC (6*32+ 3) /* Extended APIC space */
+#define X86_FEATURE_CR8_LEGACY (6*32+ 4) /* CR8 in 32-bit mode */
+#define X86_FEATURE_ABM (6*32+ 5) /* Advanced bit manipulation */
+#define X86_FEATURE_SSE4A (6*32+ 6) /* SSE-4A */
+#define X86_FEATURE_MISALIGNSSE (6*32+ 7) /* Misaligned SSE mode */
+#define X86_FEATURE_3DNOWPREFETCH (6*32+ 8) /* 3DNow prefetch instructions */
+#define X86_FEATURE_OSVW (6*32+ 9) /* OS Visible Workaround */
+#define X86_FEATURE_IBS (6*32+10) /* Instruction Based Sampling */
+#define X86_FEATURE_SSE5 (6*32+11) /* SSE-5 */
+#define X86_FEATURE_SKINIT (6*32+12) /* SKINIT/STGI instructions */
+#define X86_FEATURE_WDT (6*32+13) /* Watchdog timer */
/*
* Auxiliary flags: Linux defined - For features scattered in various
@@ -118,6 +152,13 @@
*/
#define X86_FEATURE_IDA (7*32+ 0) /* Intel Dynamic Acceleration */
+/* Virtualization flags: Linux defined */
+#define X86_FEATURE_TPR_SHADOW (8*32+ 0) /* Intel TPR Shadow */
+#define X86_FEATURE_VNMI (8*32+ 1) /* Intel Virtual NMI */
+#define X86_FEATURE_FLEXPRIORITY (8*32+ 2) /* Intel FlexPriority */
+#define X86_FEATURE_EPT (8*32+ 3) /* Intel Extended Page Table */
+#define X86_FEATURE_VPID (8*32+ 4) /* Intel Virtual Processor ID */
+
#if defined(__KERNEL__) && !defined(__ASSEMBLY__)
#include <linux/bitops.h>
@@ -151,7 +192,7 @@
} while (0)
#define setup_force_cpu_cap(bit) do { \
set_cpu_cap(&boot_cpu_data, bit); \
- clear_bit(bit, (unsigned long *)cleared_cpu_caps); \
+ clear_bit(bit, (unsigned long *)cleared_cpu_caps); \
} while (0)
#define cpu_has_fpu boot_cpu_has(X86_FEATURE_FPU)
@@ -192,7 +233,10 @@
#define cpu_has_gbpages boot_cpu_has(X86_FEATURE_GBPAGES)
#define cpu_has_arch_perfmon boot_cpu_has(X86_FEATURE_ARCH_PERFMON)
#define cpu_has_pat boot_cpu_has(X86_FEATURE_PAT)
+#define cpu_has_xmm4_1 boot_cpu_has(X86_FEATURE_XMM4_1)
#define cpu_has_xmm4_2 boot_cpu_has(X86_FEATURE_XMM4_2)
+#define cpu_has_x2apic boot_cpu_has(X86_FEATURE_X2APIC)
+#define cpu_has_xsave boot_cpu_has(X86_FEATURE_XSAVE)
#if defined(CONFIG_X86_INVLPG) || defined(CONFIG_X86_64)
# define cpu_has_invlpg 1
diff --git a/include/asm-x86/desc.h b/include/asm-x86/desc.h
index b73fea5..f06adac 100644
--- a/include/asm-x86/desc.h
+++ b/include/asm-x86/desc.h
@@ -24,6 +24,11 @@
desc->d = info->seg_32bit;
desc->g = info->limit_in_pages;
desc->base2 = (info->base_addr & 0xff000000) >> 24;
+ /*
+ * Don't allow setting of the lm bit. It is useless anyway
+ * because 64bit system calls require __USER_CS:
+ */
+ desc->l = 0;
}
extern struct desc_ptr idt_descr;
@@ -97,7 +102,15 @@
native_write_gdt_entry(dt, entry, desc, type)
#define write_idt_entry(dt, entry, g) \
native_write_idt_entry(dt, entry, g)
-#endif
+
+static inline void paravirt_alloc_ldt(struct desc_struct *ldt, unsigned entries)
+{
+}
+
+static inline void paravirt_free_ldt(struct desc_struct *ldt, unsigned entries)
+{
+}
+#endif /* CONFIG_PARAVIRT */
static inline void native_write_idt_entry(gate_desc *idt, int entry,
const gate_desc *gate)
@@ -338,22 +351,18 @@
_set_gate(n, GATE_INTERRUPT, addr, 0x3, 0, __KERNEL_CS);
}
+static inline void set_system_trap_gate(unsigned int n, void *addr)
+{
+ BUG_ON((unsigned)n > 0xFF);
+ _set_gate(n, GATE_TRAP, addr, 0x3, 0, __KERNEL_CS);
+}
+
static inline void set_trap_gate(unsigned int n, void *addr)
{
BUG_ON((unsigned)n > 0xFF);
_set_gate(n, GATE_TRAP, addr, 0, 0, __KERNEL_CS);
}
-static inline void set_system_gate(unsigned int n, void *addr)
-{
- BUG_ON((unsigned)n > 0xFF);
-#ifdef CONFIG_X86_32
- _set_gate(n, GATE_TRAP, addr, 0x3, 0, __KERNEL_CS);
-#else
- _set_gate(n, GATE_INTERRUPT, addr, 0x3, 0, __KERNEL_CS);
-#endif
-}
-
static inline void set_task_gate(unsigned int n, unsigned int gdt_entry)
{
BUG_ON((unsigned)n > 0xFF);
@@ -366,7 +375,7 @@
_set_gate(n, GATE_INTERRUPT, addr, 0, ist, __KERNEL_CS);
}
-static inline void set_system_gate_ist(int n, void *addr, unsigned ist)
+static inline void set_system_intr_gate_ist(int n, void *addr, unsigned ist)
{
BUG_ON((unsigned)n > 0xFF);
_set_gate(n, GATE_INTERRUPT, addr, 0x3, ist, __KERNEL_CS);
diff --git a/include/asm-x86/e820.h b/include/asm-x86/e820.h
index f52daf1..5abbdec 100644
--- a/include/asm-x86/e820.h
+++ b/include/asm-x86/e820.h
@@ -43,6 +43,7 @@
#define E820_RESERVED 2
#define E820_ACPI 3
#define E820_NVS 4
+#define E820_UNUSABLE 5
/* reserved RAM used by kernel itself */
#define E820_RESERVED_KERN 128
@@ -121,6 +122,7 @@
extern u64 e820_hole_size(u64 start, u64 end);
extern void finish_e820_parsing(void);
extern void e820_reserve_resources(void);
+extern void e820_reserve_resources_late(void);
extern void setup_memory_map(void);
extern char *default_machine_specific_memory_setup(void);
extern char *machine_specific_memory_setup(void);
diff --git a/include/asm-x86/mach-es7000/mach_apic.h b/include/asm-x86/es7000/apic.h
similarity index 90%
rename from include/asm-x86/mach-es7000/mach_apic.h
rename to include/asm-x86/es7000/apic.h
index c1f6f68..bd2c44d 100644
--- a/include/asm-x86/mach-es7000/mach_apic.h
+++ b/include/asm-x86/es7000/apic.h
@@ -1,5 +1,5 @@
-#ifndef ASM_X86__MACH_ES7000__MACH_APIC_H
-#define ASM_X86__MACH_ES7000__MACH_APIC_H
+#ifndef __ASM_ES7000_APIC_H
+#define __ASM_ES7000_APIC_H
#define xapic_phys_to_log_apicid(cpu) per_cpu(x86_bios_cpu_apicid, cpu)
#define esr_disable (1)
@@ -10,7 +10,7 @@
}
static inline cpumask_t target_cpus(void)
-{
+{
#if defined CONFIG_ES7000_CLUSTERED_APIC
return CPU_MASK_ALL;
#else
@@ -23,24 +23,24 @@
#define APIC_DFR_VALUE (APIC_DFR_CLUSTER)
#define INT_DELIVERY_MODE (dest_LowestPrio)
#define INT_DEST_MODE (1) /* logical delivery broadcast to all procs */
-#define NO_BALANCE_IRQ (1)
+#define NO_BALANCE_IRQ (1)
#undef WAKE_SECONDARY_VIA_INIT
#define WAKE_SECONDARY_VIA_MIP
#else
#define APIC_DFR_VALUE (APIC_DFR_FLAT)
#define INT_DELIVERY_MODE (dest_Fixed)
#define INT_DEST_MODE (0) /* phys delivery to target procs */
-#define NO_BALANCE_IRQ (0)
+#define NO_BALANCE_IRQ (0)
#undef APIC_DEST_LOGICAL
#define APIC_DEST_LOGICAL 0x0
#define WAKE_SECONDARY_VIA_INIT
#endif
static inline unsigned long check_apicid_used(physid_mask_t bitmap, int apicid)
-{
+{
return 0;
-}
-static inline unsigned long check_apicid_present(int bit)
+}
+static inline unsigned long check_apicid_present(int bit)
{
return physid_isset(bit, phys_cpu_present_map);
}
@@ -80,7 +80,7 @@
{
int apic = per_cpu(x86_bios_cpu_apicid, smp_processor_id());
printk("Enabling APIC mode: %s. Using %d I/O APICs, target cpus %lx\n",
- (apic_version[apic] == 0x14) ?
+ (apic_version[apic] == 0x14) ?
"Physical Cluster" : "Logical Cluster", nr_ioapics, cpus_addr(TARGET_CPUS)[0]);
}
@@ -141,7 +141,7 @@
extern unsigned int boot_cpu_physical_apicid;
static inline int check_phys_apicid_present(int cpu_physical_apicid)
{
- boot_cpu_physical_apicid = GET_APIC_ID(read_apic_id());
+ boot_cpu_physical_apicid = read_apic_id();
return (1);
}
@@ -150,7 +150,7 @@
int num_bits_set;
int cpus_found = 0;
int cpu;
- int apicid;
+ int apicid;
num_bits_set = cpus_weight(cpumask);
/* Return id to all */
@@ -160,16 +160,16 @@
#else
return cpu_to_logical_apicid(0);
#endif
- /*
- * The cpus in the mask must all be on the apic cluster. If are not
- * on the same apicid cluster return default value of TARGET_CPUS.
+ /*
+ * The cpus in the mask must all be on the apic cluster. If are not
+ * on the same apicid cluster return default value of TARGET_CPUS.
*/
cpu = first_cpu(cpumask);
apicid = cpu_to_logical_apicid(cpu);
while (cpus_found < num_bits_set) {
if (cpu_isset(cpu, cpumask)) {
int new_apicid = cpu_to_logical_apicid(cpu);
- if (apicid_cluster(apicid) !=
+ if (apicid_cluster(apicid) !=
apicid_cluster(new_apicid)){
printk ("%s: Not a valid mask!\n",__FUNCTION__);
#if defined CONFIG_ES7000_CLUSTERED_APIC
@@ -191,4 +191,4 @@
return cpuid_apic >> index_msb;
}
-#endif /* ASM_X86__MACH_ES7000__MACH_APIC_H */
+#endif /* __ASM_ES7000_APIC_H */
diff --git a/include/asm-x86/es7000/apicdef.h b/include/asm-x86/es7000/apicdef.h
new file mode 100644
index 0000000..8b234a3
--- /dev/null
+++ b/include/asm-x86/es7000/apicdef.h
@@ -0,0 +1,13 @@
+#ifndef __ASM_ES7000_APICDEF_H
+#define __ASM_ES7000_APICDEF_H
+
+#define APIC_ID_MASK (0xFF<<24)
+
+static inline unsigned get_apic_id(unsigned long x)
+{
+ return (((x)>>24)&0xFF);
+}
+
+#define GET_APIC_ID(x) get_apic_id(x)
+
+#endif
diff --git a/include/asm-x86/mach-es7000/mach_ipi.h b/include/asm-x86/es7000/ipi.h
similarity index 76%
rename from include/asm-x86/mach-es7000/mach_ipi.h
rename to include/asm-x86/es7000/ipi.h
index 3a21240..632a955 100644
--- a/include/asm-x86/mach-es7000/mach_ipi.h
+++ b/include/asm-x86/es7000/ipi.h
@@ -1,5 +1,5 @@
-#ifndef ASM_X86__MACH_ES7000__MACH_IPI_H
-#define ASM_X86__MACH_ES7000__MACH_IPI_H
+#ifndef __ASM_ES7000_IPI_H
+#define __ASM_ES7000_IPI_H
void send_IPI_mask_sequence(cpumask_t mask, int vector);
@@ -21,4 +21,4 @@
send_IPI_mask(cpu_online_map, vector);
}
-#endif /* ASM_X86__MACH_ES7000__MACH_IPI_H */
+#endif /* __ASM_ES7000_IPI_H */
diff --git a/include/asm-x86/mach-es7000/mach_mpparse.h b/include/asm-x86/es7000/mpparse.h
similarity index 78%
rename from include/asm-x86/mach-es7000/mach_mpparse.h
rename to include/asm-x86/es7000/mpparse.h
index befde24..ed5a3ca 100644
--- a/include/asm-x86/mach-es7000/mach_mpparse.h
+++ b/include/asm-x86/es7000/mpparse.h
@@ -1,10 +1,11 @@
-#ifndef ASM_X86__MACH_ES7000__MACH_MPPARSE_H
-#define ASM_X86__MACH_ES7000__MACH_MPPARSE_H
+#ifndef __ASM_ES7000_MPPARSE_H
+#define __ASM_ES7000_MPPARSE_H
#include <linux/acpi.h>
extern int parse_unisys_oem (char *oemptr);
extern int find_unisys_acpi_oem_table(unsigned long *oem_addr);
+extern void unmap_unisys_acpi_oem_table(unsigned long oem_addr);
extern void setup_unisys(void);
#ifndef CONFIG_X86_GENERICARCH
@@ -26,4 +27,4 @@
}
#endif
-#endif /* ASM_X86__MACH_ES7000__MACH_MPPARSE_H */
+#endif /* __ASM_MACH_MPPARSE_H */
diff --git a/include/asm-x86/mach-es7000/mach_wakecpu.h b/include/asm-x86/es7000/wakecpu.h
similarity index 89%
rename from include/asm-x86/mach-es7000/mach_wakecpu.h
rename to include/asm-x86/es7000/wakecpu.h
index 97c776c..3ffc5a7 100644
--- a/include/asm-x86/mach-es7000/mach_wakecpu.h
+++ b/include/asm-x86/es7000/wakecpu.h
@@ -1,7 +1,7 @@
-#ifndef ASM_X86__MACH_ES7000__MACH_WAKECPU_H
-#define ASM_X86__MACH_ES7000__MACH_WAKECPU_H
+#ifndef __ASM_ES7000_WAKECPU_H
+#define __ASM_ES7000_WAKECPU_H
-/*
+/*
* This file copes with machines that wakeup secondary CPUs by the
* INIT, INIT, STARTUP sequence.
*/
@@ -56,4 +56,4 @@
#define inquire_remote_apic(apicid) {}
#endif
-#endif /* ASM_X86__MACH_ES7000__MACH_WAKECPU_H */
+#endif /* __ASM_MACH_WAKECPU_H */
diff --git a/include/asm-x86/fixmap_32.h b/include/asm-x86/fixmap_32.h
index 784e3e7..8844002 100644
--- a/include/asm-x86/fixmap_32.h
+++ b/include/asm-x86/fixmap_32.h
@@ -94,10 +94,10 @@
* can have a single pgd entry and a single pte table:
*/
#define NR_FIX_BTMAPS 64
-#define FIX_BTMAPS_NESTING 4
+#define FIX_BTMAPS_SLOTS 4
FIX_BTMAP_END = __end_of_permanent_fixed_addresses + 256 -
(__end_of_permanent_fixed_addresses & 255),
- FIX_BTMAP_BEGIN = FIX_BTMAP_END + NR_FIX_BTMAPS*FIX_BTMAPS_NESTING - 1,
+ FIX_BTMAP_BEGIN = FIX_BTMAP_END + NR_FIX_BTMAPS*FIX_BTMAPS_SLOTS - 1,
FIX_WP_TEST,
#ifdef CONFIG_ACPI
FIX_ACPI_BEGIN,
diff --git a/include/asm-x86/fixmap_64.h b/include/asm-x86/fixmap_64.h
index dafb24b..dab4751 100644
--- a/include/asm-x86/fixmap_64.h
+++ b/include/asm-x86/fixmap_64.h
@@ -49,6 +49,7 @@
#ifdef CONFIG_PARAVIRT
FIX_PARAVIRT_BOOTMAP,
#endif
+ __end_of_permanent_fixed_addresses,
#ifdef CONFIG_ACPI
FIX_ACPI_BEGIN,
FIX_ACPI_END = FIX_ACPI_BEGIN + FIX_ACPI_PAGES - 1,
@@ -56,19 +57,18 @@
#ifdef CONFIG_PROVIDE_OHCI1394_DMA_INIT
FIX_OHCI1394_BASE,
#endif
- __end_of_permanent_fixed_addresses,
/*
* 256 temporary boot-time mappings, used by early_ioremap(),
* before ioremap() is functional.
*
- * We round it up to the next 512 pages boundary so that we
+ * We round it up to the next 256 pages boundary so that we
* can have a single pgd entry and a single pte table:
*/
#define NR_FIX_BTMAPS 64
-#define FIX_BTMAPS_NESTING 4
- FIX_BTMAP_END = __end_of_permanent_fixed_addresses + 512 -
- (__end_of_permanent_fixed_addresses & 511),
- FIX_BTMAP_BEGIN = FIX_BTMAP_END + NR_FIX_BTMAPS*FIX_BTMAPS_NESTING - 1,
+#define FIX_BTMAPS_SLOTS 4
+ FIX_BTMAP_END = __end_of_permanent_fixed_addresses + 256 -
+ (__end_of_permanent_fixed_addresses & 255),
+ FIX_BTMAP_BEGIN = FIX_BTMAP_END + NR_FIX_BTMAPS*FIX_BTMAPS_SLOTS - 1,
__end_of_fixed_addresses
};
diff --git a/include/asm-x86/genapic_64.h b/include/asm-x86/genapic_64.h
index 25097a8..ed6a488 100644
--- a/include/asm-x86/genapic_64.h
+++ b/include/asm-x86/genapic_64.h
@@ -14,6 +14,7 @@
struct genapic {
char *name;
+ int (*acpi_madt_oem_check)(char *oem_id, char *oem_table_id);
u32 int_delivery_mode;
u32 int_dest_mode;
int (*apic_id_registered)(void);
@@ -24,17 +25,24 @@
void (*send_IPI_mask)(cpumask_t mask, int vector);
void (*send_IPI_allbutself)(int vector);
void (*send_IPI_all)(int vector);
+ void (*send_IPI_self)(int vector);
/* */
unsigned int (*cpu_mask_to_apicid)(cpumask_t cpumask);
unsigned int (*phys_pkg_id)(int index_msb);
+ unsigned int (*get_apic_id)(unsigned long x);
+ unsigned long (*set_apic_id)(unsigned int id);
+ unsigned long apic_id_mask;
};
extern struct genapic *genapic;
extern struct genapic apic_flat;
extern struct genapic apic_physflat;
+extern struct genapic apic_x2apic_cluster;
+extern struct genapic apic_x2apic_phys;
extern int acpi_madt_oem_check(char *, char *);
+extern void apic_send_IPI_self(int vector);
enum uv_system_type {UV_NONE, UV_LEGACY_APIC, UV_X2APIC, UV_NON_UNIQUE_APIC};
extern enum uv_system_type get_uv_system_type(void);
extern int is_uv_system(void);
diff --git a/include/asm-x86/hw_irq.h b/include/asm-x86/hw_irq.h
index 65997b1..50f6e03 100644
--- a/include/asm-x86/hw_irq.h
+++ b/include/asm-x86/hw_irq.h
@@ -64,7 +64,6 @@
extern void init_VISWS_APIC_irqs(void);
extern void setup_IO_APIC(void);
extern void disable_IO_APIC(void);
-extern void print_IO_APIC(void);
extern int IO_APIC_get_PCI_irq_vector(int bus, int slot, int fn);
extern void setup_ioapic_dest(void);
@@ -73,7 +72,9 @@
#endif
/* IPI functions */
+#ifdef CONFIG_X86_32
extern void send_IPI_self(int vector);
+#endif
extern void send_IPI(int dest, int vector);
/* Statistics */
diff --git a/include/asm-x86/i387.h b/include/asm-x86/i387.h
index 1ecdc3e..9ba862a 100644
--- a/include/asm-x86/i387.h
+++ b/include/asm-x86/i387.h
@@ -19,7 +19,9 @@
#include <asm/sigcontext.h>
#include <asm/user.h>
#include <asm/uaccess.h>
+#include <asm/xsave.h>
+extern unsigned int sig_xstate_size;
extern void fpu_init(void);
extern void mxcsr_feature_mask_init(void);
extern int init_fpu(struct task_struct *child);
@@ -31,12 +33,18 @@
extern user_regset_get_fn fpregs_get, xfpregs_get, fpregs_soft_get;
extern user_regset_set_fn fpregs_set, xfpregs_set, fpregs_soft_set;
+extern struct _fpx_sw_bytes fx_sw_reserved;
#ifdef CONFIG_IA32_EMULATION
+extern unsigned int sig_xstate_ia32_size;
+extern struct _fpx_sw_bytes fx_sw_reserved_ia32;
struct _fpstate_ia32;
-extern int save_i387_ia32(struct _fpstate_ia32 __user *buf);
-extern int restore_i387_ia32(struct _fpstate_ia32 __user *buf);
+struct _xstate_ia32;
+extern int save_i387_xstate_ia32(void __user *buf);
+extern int restore_i387_xstate_ia32(void __user *buf);
#endif
+#define X87_FSW_ES (1 << 7) /* Exception Summary */
+
#ifdef CONFIG_X86_64
/* Ignore delayed exceptions from user space */
@@ -47,7 +55,7 @@
_ASM_EXTABLE(1b, 2b));
}
-static inline int restore_fpu_checking(struct i387_fxsave_struct *fx)
+static inline int fxrstor_checking(struct i387_fxsave_struct *fx)
{
int err;
@@ -67,15 +75,31 @@
return err;
}
-#define X87_FSW_ES (1 << 7) /* Exception Summary */
+static inline int restore_fpu_checking(struct task_struct *tsk)
+{
+ if (task_thread_info(tsk)->status & TS_XSAVE)
+ return xrstor_checking(&tsk->thread.xstate->xsave);
+ else
+ return fxrstor_checking(&tsk->thread.xstate->fxsave);
+}
/* AMD CPUs don't save/restore FDP/FIP/FOP unless an exception
is pending. Clear the x87 state here by setting it to fixed
values. The kernel data segment can be sometimes 0 and sometimes
new user value. Both should be ok.
Use the PDA as safe address because it should be already in L1. */
-static inline void clear_fpu_state(struct i387_fxsave_struct *fx)
+static inline void clear_fpu_state(struct task_struct *tsk)
{
+ struct xsave_struct *xstate = &tsk->thread.xstate->xsave;
+ struct i387_fxsave_struct *fx = &tsk->thread.xstate->fxsave;
+
+ /*
+ * xsave header may indicate the init state of the FP.
+ */
+ if ((task_thread_info(tsk)->status & TS_XSAVE) &&
+ !(xstate->xsave_hdr.xstate_bv & XSTATE_FP))
+ return;
+
if (unlikely(fx->swd & X87_FSW_ES))
asm volatile("fnclex");
alternative_input(ASM_NOP8 ASM_NOP2,
@@ -84,7 +108,7 @@
X86_FEATURE_FXSAVE_LEAK);
}
-static inline int save_i387_checking(struct i387_fxsave_struct __user *fx)
+static inline int fxsave_user(struct i387_fxsave_struct __user *fx)
{
int err;
@@ -108,7 +132,7 @@
return err;
}
-static inline void __save_init_fpu(struct task_struct *tsk)
+static inline void fxsave(struct task_struct *tsk)
{
/* Using "rex64; fxsave %0" is broken because, if the memory operand
uses any extended registers for addressing, a second REX prefix
@@ -133,7 +157,16 @@
: "=m" (tsk->thread.xstate->fxsave)
: "cdaSDb" (&tsk->thread.xstate->fxsave));
#endif
- clear_fpu_state(&tsk->thread.xstate->fxsave);
+}
+
+static inline void __save_init_fpu(struct task_struct *tsk)
+{
+ if (task_thread_info(tsk)->status & TS_XSAVE)
+ xsave(tsk);
+ else
+ fxsave(tsk);
+
+ clear_fpu_state(tsk);
task_thread_info(tsk)->status &= ~TS_USEDFPU;
}
@@ -148,6 +181,10 @@
static inline void restore_fpu(struct task_struct *tsk)
{
+ if (task_thread_info(tsk)->status & TS_XSAVE) {
+ xrstor_checking(&tsk->thread.xstate->xsave);
+ return;
+ }
/*
* The "nop" is needed to make the instructions the same
* length.
@@ -173,6 +210,27 @@
*/
static inline void __save_init_fpu(struct task_struct *tsk)
{
+ if (task_thread_info(tsk)->status & TS_XSAVE) {
+ struct xsave_struct *xstate = &tsk->thread.xstate->xsave;
+ struct i387_fxsave_struct *fx = &tsk->thread.xstate->fxsave;
+
+ xsave(tsk);
+
+ /*
+ * xsave header may indicate the init state of the FP.
+ */
+ if (!(xstate->xsave_hdr.xstate_bv & XSTATE_FP))
+ goto end;
+
+ if (unlikely(fx->swd & X87_FSW_ES))
+ asm volatile("fnclex");
+
+ /*
+ * we can do a simple return here or be paranoid :)
+ */
+ goto clear_state;
+ }
+
/* Use more nops than strictly needed in case the compiler
varies code */
alternative_input(
@@ -182,6 +240,7 @@
X86_FEATURE_FXSR,
[fx] "m" (tsk->thread.xstate->fxsave),
[fsw] "m" (tsk->thread.xstate->fxsave.swd) : "memory");
+clear_state:
/* AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception
is pending. Clear the x87 state here by setting it to fixed
values. safe_address is a random variable that should be in L1 */
@@ -191,16 +250,17 @@
"fildl %[addr]", /* set F?P to defined value */
X86_FEATURE_FXSAVE_LEAK,
[addr] "m" (safe_address));
+end:
task_thread_info(tsk)->status &= ~TS_USEDFPU;
}
+#endif /* CONFIG_X86_64 */
+
/*
* Signal frame handlers...
*/
-extern int save_i387(struct _fpstate __user *buf);
-extern int restore_i387(struct _fpstate __user *buf);
-
-#endif /* CONFIG_X86_64 */
+extern int save_i387_xstate(void __user *buf);
+extern int restore_i387_xstate(void __user *buf);
static inline void __unlazy_fpu(struct task_struct *tsk)
{
diff --git a/include/asm-x86/i8259.h b/include/asm-x86/i8259.h
index c586559..23c1b3b 100644
--- a/include/asm-x86/i8259.h
+++ b/include/asm-x86/i8259.h
@@ -57,4 +57,7 @@
extern struct irq_chip i8259A_chip;
+extern void mask_8259A(void);
+extern void unmask_8259A(void);
+
#endif /* ASM_X86__I8259_H */
diff --git a/include/asm-x86/io.h b/include/asm-x86/io.h
index 72b7719..a233f83 100644
--- a/include/asm-x86/io.h
+++ b/include/asm-x86/io.h
@@ -5,20 +5,6 @@
#include <linux/compiler.h>
-/*
- * early_ioremap() and early_iounmap() are for temporary early boot-time
- * mappings, before the real ioremap() is functional.
- * A boot-time mapping is currently limited to at most 16 pages.
- */
-#ifndef __ASSEMBLY__
-extern void early_ioremap_init(void);
-extern void early_ioremap_clear(void);
-extern void early_ioremap_reset(void);
-extern void *early_ioremap(unsigned long offset, unsigned long size);
-extern void early_iounmap(void *addr, unsigned long size);
-extern void __iomem *fix_ioremap(unsigned idx, unsigned long phys);
-#endif
-
#define build_mmio_read(name, size, type, reg, barrier) \
static inline type name(const volatile void __iomem *addr) \
{ type ret; asm volatile("mov" size " %1,%0":reg (ret) \
@@ -97,6 +83,7 @@
extern void early_ioremap_clear(void);
extern void early_ioremap_reset(void);
extern void *early_ioremap(unsigned long offset, unsigned long size);
+extern void *early_memremap(unsigned long offset, unsigned long size);
extern void early_iounmap(void *addr, unsigned long size);
extern void __iomem *fix_ioremap(unsigned idx, unsigned long phys);
diff --git a/include/asm-x86/io_64.h b/include/asm-x86/io_64.h
index 64429e9..ee6e086 100644
--- a/include/asm-x86/io_64.h
+++ b/include/asm-x86/io_64.h
@@ -165,9 +165,6 @@
#include <asm-generic/iomap.h>
-extern void *early_ioremap(unsigned long addr, unsigned long size);
-extern void early_iounmap(void *addr, unsigned long size);
-
/*
* This one maps high address device memory and turns off caching for that area.
* it's useful if some control registers are in such an area and write combining
diff --git a/include/asm-x86/io_apic.h b/include/asm-x86/io_apic.h
index be62847..8ec68a5 100644
--- a/include/asm-x86/io_apic.h
+++ b/include/asm-x86/io_apic.h
@@ -107,6 +107,20 @@
} __attribute__ ((packed));
+struct IR_IO_APIC_route_entry {
+ __u64 vector : 8,
+ zero : 3,
+ index2 : 1,
+ delivery_status : 1,
+ polarity : 1,
+ irr : 1,
+ trigger : 1,
+ mask : 1,
+ reserved : 31,
+ format : 1,
+ index : 15;
+} __attribute__ ((packed));
+
#ifdef CONFIG_X86_IO_APIC
/*
@@ -183,6 +197,12 @@
extern int (*ioapic_renumber_irq)(int ioapic, int irq);
extern void ioapic_init_mappings(void);
+#ifdef CONFIG_X86_64
+extern int save_mask_IO_APIC_setup(void);
+extern void restore_IO_APIC_setup(void);
+extern void reinit_intr_remapped_IO_APIC(int);
+#endif
+
#else /* !CONFIG_X86_IO_APIC */
#define io_apic_assign_pci_irqs 0
static const int timer_through_8259 = 0;
diff --git a/include/asm-x86/ioctls.h b/include/asm-x86/ioctls.h
index 3366035..06752a6 100644
--- a/include/asm-x86/ioctls.h
+++ b/include/asm-x86/ioctls.h
@@ -51,9 +51,15 @@
#define TCSETS2 _IOW('T', 0x2B, struct termios2)
#define TCSETSW2 _IOW('T', 0x2C, struct termios2)
#define TCSETSF2 _IOW('T', 0x2D, struct termios2)
+#define TIOCGRS485 0x542E
+#define TIOCSRS485 0x542F
#define TIOCGPTN _IOR('T', 0x30, unsigned int)
/* Get Pty Number (of pty-mux device) */
#define TIOCSPTLCK _IOW('T', 0x31, int) /* Lock/unlock Pty */
+#define TCGETX 0x5432 /* SYS5 TCGETX compatibility */
+#define TCSETX 0x5433
+#define TCSETXF 0x5434
+#define TCSETXW 0x5435
#define FIONCLEX 0x5450
#define FIOCLEX 0x5451
diff --git a/include/asm-x86/ipi.h b/include/asm-x86/ipi.h
index c1b2267..30a692c 100644
--- a/include/asm-x86/ipi.h
+++ b/include/asm-x86/ipi.h
@@ -49,6 +49,12 @@
return SET_APIC_DEST_FIELD(mask);
}
+static inline void __xapic_wait_icr_idle(void)
+{
+ while (native_apic_mem_read(APIC_ICR) & APIC_ICR_BUSY)
+ cpu_relax();
+}
+
static inline void __send_IPI_shortcut(unsigned int shortcut, int vector,
unsigned int dest)
{
@@ -64,7 +70,7 @@
/*
* Wait for idle.
*/
- apic_wait_icr_idle();
+ __xapic_wait_icr_idle();
/*
* No need to touch the target chip field
@@ -74,7 +80,7 @@
/*
* Send the IPI. The write to APIC_ICR fires this off.
*/
- apic_write(APIC_ICR, cfg);
+ native_apic_mem_write(APIC_ICR, cfg);
}
/*
@@ -92,13 +98,13 @@
if (unlikely(vector == NMI_VECTOR))
safe_apic_wait_icr_idle();
else
- apic_wait_icr_idle();
+ __xapic_wait_icr_idle();
/*
* prepare target chip field
*/
cfg = __prepare_ICR2(mask);
- apic_write(APIC_ICR2, cfg);
+ native_apic_mem_write(APIC_ICR2, cfg);
/*
* program the ICR
@@ -108,7 +114,7 @@
/*
* Send the IPI. The write to APIC_ICR fires this off.
*/
- apic_write(APIC_ICR, cfg);
+ native_apic_mem_write(APIC_ICR, cfg);
}
static inline void send_IPI_mask_sequence(cpumask_t mask, int vector)
diff --git a/include/asm-x86/irq_remapping.h b/include/asm-x86/irq_remapping.h
new file mode 100644
index 0000000..78242c6
--- /dev/null
+++ b/include/asm-x86/irq_remapping.h
@@ -0,0 +1,8 @@
+#ifndef _ASM_IRQ_REMAPPING_H
+#define _ASM_IRQ_REMAPPING_H
+
+extern int x2apic;
+
+#define IRTE_DEST(dest) ((x2apic) ? dest : dest << 8)
+
+#endif
diff --git a/include/asm-x86/irqflags.h b/include/asm-x86/irqflags.h
index 424acb4..2bdab21 100644
--- a/include/asm-x86/irqflags.h
+++ b/include/asm-x86/irqflags.h
@@ -166,27 +166,6 @@
return raw_irqs_disabled_flags(flags);
}
-/*
- * makes the traced hardirq state match with the machine state
- *
- * should be a rarely used function, only in places where its
- * otherwise impossible to know the irq state, like in traps.
- */
-static inline void trace_hardirqs_fixup_flags(unsigned long flags)
-{
- if (raw_irqs_disabled_flags(flags))
- trace_hardirqs_off();
- else
- trace_hardirqs_on();
-}
-
-static inline void trace_hardirqs_fixup(void)
-{
- unsigned long flags = __raw_local_save_flags();
-
- trace_hardirqs_fixup_flags(flags);
-}
-
#else
#ifdef CONFIG_X86_64
diff --git a/include/asm-x86/kdebug.h b/include/asm-x86/kdebug.h
index 5ec3ad3..fbbab66 100644
--- a/include/asm-x86/kdebug.h
+++ b/include/asm-x86/kdebug.h
@@ -27,10 +27,9 @@
extern void die(const char *, struct pt_regs *,long);
extern int __must_check __die(const char *, struct pt_regs *, long);
extern void show_registers(struct pt_regs *regs);
-extern void __show_registers(struct pt_regs *, int all);
extern void show_trace(struct task_struct *t, struct pt_regs *regs,
unsigned long *sp, unsigned long bp);
-extern void __show_regs(struct pt_regs *regs);
+extern void __show_regs(struct pt_regs *regs, int all);
extern void show_regs(struct pt_regs *regs);
extern unsigned long oops_begin(void);
extern void oops_end(unsigned long, struct pt_regs *, int signr);
diff --git a/include/asm-x86/kprobes.h b/include/asm-x86/kprobes.h
index bd84078..8a0748d 100644
--- a/include/asm-x86/kprobes.h
+++ b/include/asm-x86/kprobes.h
@@ -82,15 +82,6 @@
struct prev_kprobe prev_kprobe;
};
-/* trap3/1 are intr gates for kprobes. So, restore the status of IF,
- * if necessary, before executing the original int3/1 (trap) handler.
- */
-static inline void restore_interrupts(struct pt_regs *regs)
-{
- if (regs->flags & X86_EFLAGS_IF)
- local_irq_enable();
-}
-
extern int kprobe_fault_handler(struct pt_regs *regs, int trapnr);
extern int kprobe_exceptions_notify(struct notifier_block *self,
unsigned long val, void *data);
diff --git a/include/asm-x86/mach-bigsmp/mach_apicdef.h b/include/asm-x86/mach-bigsmp/mach_apicdef.h
deleted file mode 100644
index 811935d..0000000
--- a/include/asm-x86/mach-bigsmp/mach_apicdef.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef ASM_X86__MACH_BIGSMP__MACH_APICDEF_H
-#define ASM_X86__MACH_BIGSMP__MACH_APICDEF_H
-
-#define APIC_ID_MASK (0xFF<<24)
-
-static inline unsigned get_apic_id(unsigned long x)
-{
- return (((x)>>24)&0xFF);
-}
-
-#define GET_APIC_ID(x) get_apic_id(x)
-
-#endif /* ASM_X86__MACH_BIGSMP__MACH_APICDEF_H */
diff --git a/include/asm-x86/mach-default/mach_apic.h b/include/asm-x86/mach-default/mach_apic.h
index b615f40..2a330a4 100644
--- a/include/asm-x86/mach-default/mach_apic.h
+++ b/include/asm-x86/mach-default/mach_apic.h
@@ -30,6 +30,8 @@
#define cpu_mask_to_apicid (genapic->cpu_mask_to_apicid)
#define phys_pkg_id (genapic->phys_pkg_id)
#define vector_allocation_domain (genapic->vector_allocation_domain)
+#define read_apic_id() (GET_APIC_ID(apic_read(APIC_ID)))
+#define send_IPI_self (genapic->send_IPI_self)
extern void setup_apic_routing(void);
#else
#define INT_DELIVERY_MODE dest_LowestPrio
@@ -54,7 +56,7 @@
static inline int apic_id_registered(void)
{
- return physid_isset(GET_APIC_ID(read_apic_id()), phys_cpu_present_map);
+ return physid_isset(read_apic_id(), phys_cpu_present_map);
}
static inline unsigned int cpu_mask_to_apicid(cpumask_t cpumask)
diff --git a/include/asm-x86/mach-default/mach_apicdef.h b/include/asm-x86/mach-default/mach_apicdef.h
index 936704f..0c2d41c 100644
--- a/include/asm-x86/mach-default/mach_apicdef.h
+++ b/include/asm-x86/mach-default/mach_apicdef.h
@@ -4,9 +4,9 @@
#include <asm/apic.h>
#ifdef CONFIG_X86_64
-#define APIC_ID_MASK (0xFFu<<24)
-#define GET_APIC_ID(x) (((x)>>24)&0xFFu)
-#define SET_APIC_ID(x) (((x)<<24))
+#define APIC_ID_MASK (genapic->apic_id_mask)
+#define GET_APIC_ID(x) (genapic->get_apic_id(x))
+#define SET_APIC_ID(x) (genapic->set_apic_id(x))
#else
#define APIC_ID_MASK (0xF<<24)
static inline unsigned get_apic_id(unsigned long x)
diff --git a/include/asm-x86/mach-default/mach_traps.h b/include/asm-x86/mach-default/mach_traps.h
index de9ac3f..ff8778f 100644
--- a/include/asm-x86/mach-default/mach_traps.h
+++ b/include/asm-x86/mach-default/mach_traps.h
@@ -7,12 +7,6 @@
#include <asm/mc146818rtc.h>
-static inline void clear_mem_error(unsigned char reason)
-{
- reason = (reason & 0xf) | 4;
- outb(reason, 0x61);
-}
-
static inline unsigned char get_nmi_reason(void)
{
return inb(0x61);
diff --git a/include/asm-x86/mach-es7000/mach_apicdef.h b/include/asm-x86/mach-es7000/mach_apicdef.h
deleted file mode 100644
index a07e567..0000000
--- a/include/asm-x86/mach-es7000/mach_apicdef.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef ASM_X86__MACH_ES7000__MACH_APICDEF_H
-#define ASM_X86__MACH_ES7000__MACH_APICDEF_H
-
-#define APIC_ID_MASK (0xFF<<24)
-
-static inline unsigned get_apic_id(unsigned long x)
-{
- return (((x)>>24)&0xFF);
-}
-
-#define GET_APIC_ID(x) get_apic_id(x)
-
-#endif /* ASM_X86__MACH_ES7000__MACH_APICDEF_H */
diff --git a/include/asm-x86/mach-numaq/mach_apicdef.h b/include/asm-x86/mach-numaq/mach_apicdef.h
deleted file mode 100644
index f870ec5..0000000
--- a/include/asm-x86/mach-numaq/mach_apicdef.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef ASM_X86__MACH_NUMAQ__MACH_APICDEF_H
-#define ASM_X86__MACH_NUMAQ__MACH_APICDEF_H
-
-
-#define APIC_ID_MASK (0xF<<24)
-
-static inline unsigned get_apic_id(unsigned long x)
-{
- return (((x)>>24)&0x0F);
-}
-
-#define GET_APIC_ID(x) get_apic_id(x)
-
-#endif /* ASM_X86__MACH_NUMAQ__MACH_APICDEF_H */
diff --git a/include/asm-x86/mach-numaq/mach_mpparse.h b/include/asm-x86/mach-numaq/mach_mpparse.h
deleted file mode 100644
index 74ade18..0000000
--- a/include/asm-x86/mach-numaq/mach_mpparse.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef ASM_X86__MACH_NUMAQ__MACH_MPPARSE_H
-#define ASM_X86__MACH_NUMAQ__MACH_MPPARSE_H
-
-extern void numaq_mps_oem_check(struct mp_config_table *mpc, char *oem,
- char *productid);
-
-#endif /* ASM_X86__MACH_NUMAQ__MACH_MPPARSE_H */
diff --git a/include/asm-x86/mach-summit/mach_apicdef.h b/include/asm-x86/mach-summit/mach_apicdef.h
deleted file mode 100644
index d4bc859..0000000
--- a/include/asm-x86/mach-summit/mach_apicdef.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef ASM_X86__MACH_SUMMIT__MACH_APICDEF_H
-#define ASM_X86__MACH_SUMMIT__MACH_APICDEF_H
-
-#define APIC_ID_MASK (0xFF<<24)
-
-static inline unsigned get_apic_id(unsigned long x)
-{
- return (((x)>>24)&0xFF);
-}
-
-#define GET_APIC_ID(x) get_apic_id(x)
-
-#endif /* ASM_X86__MACH_SUMMIT__MACH_APICDEF_H */
diff --git a/include/asm-x86/microcode.h b/include/asm-x86/microcode.h
new file mode 100644
index 0000000..62c793b
--- /dev/null
+++ b/include/asm-x86/microcode.h
@@ -0,0 +1,47 @@
+#ifndef ASM_X86__MICROCODE_H
+#define ASM_X86__MICROCODE_H
+
+struct cpu_signature {
+ unsigned int sig;
+ unsigned int pf;
+ unsigned int rev;
+};
+
+struct device;
+
+struct microcode_ops {
+ int (*request_microcode_user) (int cpu, const void __user *buf, size_t size);
+ int (*request_microcode_fw) (int cpu, struct device *device);
+
+ void (*apply_microcode) (int cpu);
+
+ int (*collect_cpu_info) (int cpu, struct cpu_signature *csig);
+ void (*microcode_fini_cpu) (int cpu);
+};
+
+struct ucode_cpu_info {
+ struct cpu_signature cpu_sig;
+ int valid;
+ void *mc;
+};
+extern struct ucode_cpu_info ucode_cpu_info[];
+
+#ifdef CONFIG_MICROCODE_INTEL
+extern struct microcode_ops * __init init_intel_microcode(void);
+#else
+static inline struct microcode_ops * __init init_intel_microcode(void)
+{
+ return NULL;
+}
+#endif /* CONFIG_MICROCODE_INTEL */
+
+#ifdef CONFIG_MICROCODE_AMD
+extern struct microcode_ops * __init init_amd_microcode(void);
+#else
+static inline struct microcode_ops * __init init_amd_microcode(void)
+{
+ return NULL;
+}
+#endif
+
+#endif /* ASM_X86__MICROCODE_H */
diff --git a/include/asm-x86/mmzone_64.h b/include/asm-x86/mmzone_64.h
index 626b03a..6480f33 100644
--- a/include/asm-x86/mmzone_64.h
+++ b/include/asm-x86/mmzone_64.h
@@ -7,7 +7,7 @@
#ifdef CONFIG_NUMA
-#define VIRTUAL_BUG_ON(x)
+#include <linux/mmdebug.h>
#include <asm/smp.h>
@@ -29,7 +29,6 @@
{
unsigned nid;
VIRTUAL_BUG_ON(!memnodemap);
- VIRTUAL_BUG_ON((addr >> memnode_shift) >= memnodemapsize);
nid = memnodemap[addr >> memnode_shift];
VIRTUAL_BUG_ON(nid >= MAX_NUMNODES || !node_data[nid]);
return nid;
diff --git a/include/asm-x86/module.h b/include/asm-x86/module.h
index 48dc3e0..864f200 100644
--- a/include/asm-x86/module.h
+++ b/include/asm-x86/module.h
@@ -52,8 +52,6 @@
#define MODULE_PROC_FAMILY "EFFICEON "
#elif defined CONFIG_MWINCHIPC6
#define MODULE_PROC_FAMILY "WINCHIPC6 "
-#elif defined CONFIG_MWINCHIP2
-#define MODULE_PROC_FAMILY "WINCHIP2 "
#elif defined CONFIG_MWINCHIP3D
#define MODULE_PROC_FAMILY "WINCHIP3D "
#elif defined CONFIG_MCYRIXIII
diff --git a/include/asm-x86/mpspec.h b/include/asm-x86/mpspec.h
index 118da36..be2241a 100644
--- a/include/asm-x86/mpspec.h
+++ b/include/asm-x86/mpspec.h
@@ -5,11 +5,12 @@
#include <asm/mpspec_def.h>
+extern int apic_version[MAX_APICS];
+
#ifdef CONFIG_X86_32
#include <mach_mpspec.h>
extern unsigned int def_to_bigsmp;
-extern int apic_version[MAX_APICS];
extern u8 apicid_2_node[];
extern int pic_mode;
diff --git a/include/asm-x86/msidef.h b/include/asm-x86/msidef.h
index 3139666..ed91902 100644
--- a/include/asm-x86/msidef.h
+++ b/include/asm-x86/msidef.h
@@ -48,4 +48,8 @@
#define MSI_ADDR_DEST_ID(dest) (((dest) << MSI_ADDR_DEST_ID_SHIFT) & \
MSI_ADDR_DEST_ID_MASK)
+#define MSI_ADDR_IR_EXT_INT (1 << 4)
+#define MSI_ADDR_IR_SHV (1 << 3)
+#define MSI_ADDR_IR_INDEX1(index) ((index & 0x8000) >> 13)
+#define MSI_ADDR_IR_INDEX2(index) ((index & 0x7fff) << 5)
#endif /* ASM_X86__MSIDEF_H */
diff --git a/include/asm-x86/msr-index.h b/include/asm-x86/msr-index.h
index 3052f05..0bb4330 100644
--- a/include/asm-x86/msr-index.h
+++ b/include/asm-x86/msr-index.h
@@ -176,6 +176,7 @@
#define MSR_IA32_TSC 0x00000010
#define MSR_IA32_PLATFORM_ID 0x00000017
#define MSR_IA32_EBL_CR_POWERON 0x0000002a
+#define MSR_IA32_FEATURE_CONTROL 0x0000003a
#define MSR_IA32_APICBASE 0x0000001b
#define MSR_IA32_APICBASE_BSP (1<<8)
@@ -310,4 +311,19 @@
/* Geode defined MSRs */
#define MSR_GEODE_BUSCONT_CONF0 0x00001900
+/* Intel VT MSRs */
+#define MSR_IA32_VMX_BASIC 0x00000480
+#define MSR_IA32_VMX_PINBASED_CTLS 0x00000481
+#define MSR_IA32_VMX_PROCBASED_CTLS 0x00000482
+#define MSR_IA32_VMX_EXIT_CTLS 0x00000483
+#define MSR_IA32_VMX_ENTRY_CTLS 0x00000484
+#define MSR_IA32_VMX_MISC 0x00000485
+#define MSR_IA32_VMX_CR0_FIXED0 0x00000486
+#define MSR_IA32_VMX_CR0_FIXED1 0x00000487
+#define MSR_IA32_VMX_CR4_FIXED0 0x00000488
+#define MSR_IA32_VMX_CR4_FIXED1 0x00000489
+#define MSR_IA32_VMX_VMCS_ENUM 0x0000048a
+#define MSR_IA32_VMX_PROCBASED_CTLS2 0x0000048b
+#define MSR_IA32_VMX_EPT_VPID_CAP 0x0000048c
+
#endif /* ASM_X86__MSR_INDEX_H */
diff --git a/include/asm-x86/nmi.h b/include/asm-x86/nmi.h
index d5e715f..a53f829 100644
--- a/include/asm-x86/nmi.h
+++ b/include/asm-x86/nmi.h
@@ -15,10 +15,6 @@
*/
int do_nmi_callback(struct pt_regs *regs, int cpu);
-#ifdef CONFIG_X86_64
-extern void default_do_nmi(struct pt_regs *);
-#endif
-
extern void die_nmi(char *str, struct pt_regs *regs, int do_panic);
extern int check_nmi_watchdog(void);
extern int nmi_watchdog_enabled;
diff --git a/include/asm-x86/mach-numaq/mach_apic.h b/include/asm-x86/numaq/apic.h
similarity index 95%
rename from include/asm-x86/mach-numaq/mach_apic.h
rename to include/asm-x86/numaq/apic.h
index 7a0d39e..a8344ba 100644
--- a/include/asm-x86/mach-numaq/mach_apic.h
+++ b/include/asm-x86/numaq/apic.h
@@ -1,5 +1,5 @@
-#ifndef ASM_X86__MACH_NUMAQ__MACH_APIC_H
-#define ASM_X86__MACH_NUMAQ__MACH_APIC_H
+#ifndef __ASM_NUMAQ_APIC_H
+#define __ASM_NUMAQ_APIC_H
#include <asm/io.h>
#include <linux/mmzone.h>
@@ -135,4 +135,4 @@
return cpuid_apic >> index_msb;
}
-#endif /* ASM_X86__MACH_NUMAQ__MACH_APIC_H */
+#endif /* __ASM_NUMAQ_APIC_H */
diff --git a/include/asm-x86/numaq/apicdef.h b/include/asm-x86/numaq/apicdef.h
new file mode 100644
index 0000000..e012a46
--- /dev/null
+++ b/include/asm-x86/numaq/apicdef.h
@@ -0,0 +1,14 @@
+#ifndef __ASM_NUMAQ_APICDEF_H
+#define __ASM_NUMAQ_APICDEF_H
+
+
+#define APIC_ID_MASK (0xF<<24)
+
+static inline unsigned get_apic_id(unsigned long x)
+{
+ return (((x)>>24)&0x0F);
+}
+
+#define GET_APIC_ID(x) get_apic_id(x)
+
+#endif
diff --git a/include/asm-x86/mach-numaq/mach_ipi.h b/include/asm-x86/numaq/ipi.h
similarity index 77%
rename from include/asm-x86/mach-numaq/mach_ipi.h
rename to include/asm-x86/numaq/ipi.h
index 1e83582..935588d 100644
--- a/include/asm-x86/mach-numaq/mach_ipi.h
+++ b/include/asm-x86/numaq/ipi.h
@@ -1,5 +1,5 @@
-#ifndef ASM_X86__MACH_NUMAQ__MACH_IPI_H
-#define ASM_X86__MACH_NUMAQ__MACH_IPI_H
+#ifndef __ASM_NUMAQ_IPI_H
+#define __ASM_NUMAQ_IPI_H
void send_IPI_mask_sequence(cpumask_t, int vector);
@@ -22,4 +22,4 @@
send_IPI_mask(cpu_online_map, vector);
}
-#endif /* ASM_X86__MACH_NUMAQ__MACH_IPI_H */
+#endif /* __ASM_NUMAQ_IPI_H */
diff --git a/include/asm-x86/numaq/mpparse.h b/include/asm-x86/numaq/mpparse.h
new file mode 100644
index 0000000..252292e
--- /dev/null
+++ b/include/asm-x86/numaq/mpparse.h
@@ -0,0 +1,7 @@
+#ifndef __ASM_NUMAQ_MPPARSE_H
+#define __ASM_NUMAQ_MPPARSE_H
+
+extern void numaq_mps_oem_check(struct mp_config_table *mpc, char *oem,
+ char *productid);
+
+#endif /* __ASM_NUMAQ_MPPARSE_H */
diff --git a/include/asm-x86/mach-numaq/mach_wakecpu.h b/include/asm-x86/numaq/wakecpu.h
similarity index 88%
rename from include/asm-x86/mach-numaq/mach_wakecpu.h
rename to include/asm-x86/numaq/wakecpu.h
index 0db8cea..c577bda 100644
--- a/include/asm-x86/mach-numaq/mach_wakecpu.h
+++ b/include/asm-x86/numaq/wakecpu.h
@@ -1,5 +1,5 @@
-#ifndef ASM_X86__MACH_NUMAQ__MACH_WAKECPU_H
-#define ASM_X86__MACH_NUMAQ__MACH_WAKECPU_H
+#ifndef __ASM_NUMAQ_WAKECPU_H
+#define __ASM_NUMAQ_WAKECPU_H
/* This file copes with machines that wakeup secondary CPUs by NMIs */
@@ -40,4 +40,4 @@
#define inquire_remote_apic(apicid) {}
-#endif /* ASM_X86__MACH_NUMAQ__MACH_WAKECPU_H */
+#endif /* __ASM_NUMAQ_WAKECPU_H */
diff --git a/include/asm-x86/page.h b/include/asm-x86/page.h
index c915747..d4f1d57 100644
--- a/include/asm-x86/page.h
+++ b/include/asm-x86/page.h
@@ -179,6 +179,7 @@
#endif /* CONFIG_PARAVIRT */
#define __pa(x) __phys_addr((unsigned long)(x))
+#define __pa_nodebug(x) __phys_addr_nodebug((unsigned long)(x))
/* __pa_symbol should be used for C visible symbols.
This seems to be the official gcc blessed way to do such arithmetic. */
#define __pa_symbol(x) __pa(__phys_reloc_hide((unsigned long)(x)))
@@ -188,9 +189,14 @@
#define __boot_va(x) __va(x)
#define __boot_pa(x) __pa(x)
+/*
+ * virt_to_page(kaddr) returns a valid pointer if and only if
+ * virt_addr_valid(kaddr) returns true.
+ */
#define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
#define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT)
-#define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT)
+extern bool __virt_addr_valid(unsigned long kaddr);
+#define virt_addr_valid(kaddr) __virt_addr_valid((unsigned long) (kaddr))
#endif /* __ASSEMBLY__ */
diff --git a/include/asm-x86/page_32.h b/include/asm-x86/page_32.h
index 72f7305..e8d80d1 100644
--- a/include/asm-x86/page_32.h
+++ b/include/asm-x86/page_32.h
@@ -20,6 +20,12 @@
#endif
#define THREAD_SIZE (PAGE_SIZE << THREAD_ORDER)
+#define STACKFAULT_STACK 0
+#define DOUBLEFAULT_STACK 1
+#define NMI_STACK 0
+#define DEBUG_STACK 0
+#define MCE_STACK 0
+#define N_EXCEPTION_STACKS 1
#ifdef CONFIG_X86_PAE
/* 44=32+12, the limit we can fit into an unsigned long pfn */
@@ -73,7 +79,12 @@
#endif
#ifndef __ASSEMBLY__
-#define __phys_addr(x) ((x) - PAGE_OFFSET)
+#define __phys_addr_nodebug(x) ((x) - PAGE_OFFSET)
+#ifdef CONFIG_DEBUG_VIRTUAL
+extern unsigned long __phys_addr(unsigned long);
+#else
+#define __phys_addr(x) __phys_addr_nodebug(x)
+#endif
#define __phys_reloc_hide(x) RELOC_HIDE((x), 0)
#ifdef CONFIG_FLATMEM
diff --git a/include/asm-x86/paravirt.h b/include/asm-x86/paravirt.h
index 891971f..8d6ae2f 100644
--- a/include/asm-x86/paravirt.h
+++ b/include/asm-x86/paravirt.h
@@ -124,6 +124,9 @@
int entrynum, const void *desc, int size);
void (*write_idt_entry)(gate_desc *,
int entrynum, const gate_desc *gate);
+ void (*alloc_ldt)(struct desc_struct *ldt, unsigned entries);
+ void (*free_ldt)(struct desc_struct *ldt, unsigned entries);
+
void (*load_sp0)(struct tss_struct *tss, struct thread_struct *t);
void (*set_iopl_mask)(unsigned mask);
@@ -201,12 +204,6 @@
struct pv_apic_ops {
#ifdef CONFIG_X86_LOCAL_APIC
- /*
- * Direct APIC operations, principally for VMI. Ideally
- * these shouldn't be in this interface.
- */
- void (*apic_write)(unsigned long reg, u32 v);
- u32 (*apic_read)(unsigned long reg);
void (*setup_boot_clock)(void);
void (*setup_secondary_clock)(void);
@@ -331,6 +328,7 @@
int (*spin_is_locked)(struct raw_spinlock *lock);
int (*spin_is_contended)(struct raw_spinlock *lock);
void (*spin_lock)(struct raw_spinlock *lock);
+ void (*spin_lock_flags)(struct raw_spinlock *lock, unsigned long flags);
int (*spin_trylock)(struct raw_spinlock *lock);
void (*spin_unlock)(struct raw_spinlock *lock);
};
@@ -836,6 +834,16 @@
(aux) = __aux; \
} while (0)
+static inline void paravirt_alloc_ldt(struct desc_struct *ldt, unsigned entries)
+{
+ PVOP_VCALL2(pv_cpu_ops.alloc_ldt, ldt, entries);
+}
+
+static inline void paravirt_free_ldt(struct desc_struct *ldt, unsigned entries)
+{
+ PVOP_VCALL2(pv_cpu_ops.free_ldt, ldt, entries);
+}
+
static inline void load_TR_desc(void)
{
PVOP_VCALL0(pv_cpu_ops.load_tr_desc);
@@ -910,19 +918,6 @@
}
#ifdef CONFIG_X86_LOCAL_APIC
-/*
- * Basic functions accessing APICs.
- */
-static inline void apic_write(unsigned long reg, u32 v)
-{
- PVOP_VCALL2(pv_apic_ops.apic_write, reg, v);
-}
-
-static inline u32 apic_read(unsigned long reg)
-{
- return PVOP_CALL1(unsigned long, pv_apic_ops.apic_read, reg);
-}
-
static inline void setup_boot_clock(void)
{
PVOP_VCALL0(pv_apic_ops.setup_boot_clock);
@@ -1413,6 +1408,12 @@
PVOP_VCALL1(pv_lock_ops.spin_lock, lock);
}
+static __always_inline void __raw_spin_lock_flags(struct raw_spinlock *lock,
+ unsigned long flags)
+{
+ PVOP_VCALL2(pv_lock_ops.spin_lock_flags, lock, flags);
+}
+
static __always_inline int __raw_spin_trylock(struct raw_spinlock *lock)
{
return PVOP_CALL1(int, pv_lock_ops.spin_trylock, lock);
diff --git a/include/asm-x86/pgtable.h b/include/asm-x86/pgtable.h
index ed93245..182f9d4 100644
--- a/include/asm-x86/pgtable.h
+++ b/include/asm-x86/pgtable.h
@@ -15,7 +15,7 @@
#define _PAGE_BIT_PAT 7 /* on 4KB pages */
#define _PAGE_BIT_GLOBAL 8 /* Global TLB entry PPro+ */
#define _PAGE_BIT_UNUSED1 9 /* available for programmer */
-#define _PAGE_BIT_UNUSED2 10
+#define _PAGE_BIT_IOMAP 10 /* flag used to indicate IO mapping */
#define _PAGE_BIT_UNUSED3 11
#define _PAGE_BIT_PAT_LARGE 12 /* On 2MB or 1GB pages */
#define _PAGE_BIT_SPECIAL _PAGE_BIT_UNUSED1
@@ -32,7 +32,7 @@
#define _PAGE_PSE (_AT(pteval_t, 1) << _PAGE_BIT_PSE)
#define _PAGE_GLOBAL (_AT(pteval_t, 1) << _PAGE_BIT_GLOBAL)
#define _PAGE_UNUSED1 (_AT(pteval_t, 1) << _PAGE_BIT_UNUSED1)
-#define _PAGE_UNUSED2 (_AT(pteval_t, 1) << _PAGE_BIT_UNUSED2)
+#define _PAGE_IOMAP (_AT(pteval_t, 1) << _PAGE_BIT_IOMAP)
#define _PAGE_UNUSED3 (_AT(pteval_t, 1) << _PAGE_BIT_UNUSED3)
#define _PAGE_PAT (_AT(pteval_t, 1) << _PAGE_BIT_PAT)
#define _PAGE_PAT_LARGE (_AT(pteval_t, 1) << _PAGE_BIT_PAT_LARGE)
@@ -99,6 +99,11 @@
#define __PAGE_KERNEL_LARGE_NOCACHE (__PAGE_KERNEL | _PAGE_CACHE_UC | _PAGE_PSE)
#define __PAGE_KERNEL_LARGE_EXEC (__PAGE_KERNEL_EXEC | _PAGE_PSE)
+#define __PAGE_KERNEL_IO (__PAGE_KERNEL | _PAGE_IOMAP)
+#define __PAGE_KERNEL_IO_NOCACHE (__PAGE_KERNEL_NOCACHE | _PAGE_IOMAP)
+#define __PAGE_KERNEL_IO_UC_MINUS (__PAGE_KERNEL_UC_MINUS | _PAGE_IOMAP)
+#define __PAGE_KERNEL_IO_WC (__PAGE_KERNEL_WC | _PAGE_IOMAP)
+
#define PAGE_KERNEL __pgprot(__PAGE_KERNEL)
#define PAGE_KERNEL_RO __pgprot(__PAGE_KERNEL_RO)
#define PAGE_KERNEL_EXEC __pgprot(__PAGE_KERNEL_EXEC)
@@ -113,6 +118,11 @@
#define PAGE_KERNEL_VSYSCALL __pgprot(__PAGE_KERNEL_VSYSCALL)
#define PAGE_KERNEL_VSYSCALL_NOCACHE __pgprot(__PAGE_KERNEL_VSYSCALL_NOCACHE)
+#define PAGE_KERNEL_IO __pgprot(__PAGE_KERNEL_IO)
+#define PAGE_KERNEL_IO_NOCACHE __pgprot(__PAGE_KERNEL_IO_NOCACHE)
+#define PAGE_KERNEL_IO_UC_MINUS __pgprot(__PAGE_KERNEL_IO_UC_MINUS)
+#define PAGE_KERNEL_IO_WC __pgprot(__PAGE_KERNEL_IO_WC)
+
/* xwr */
#define __P000 PAGE_NONE
#define __P001 PAGE_READONLY
@@ -196,7 +206,7 @@
static inline int pte_special(pte_t pte)
{
- return pte_val(pte) & _PAGE_SPECIAL;
+ return pte_flags(pte) & _PAGE_SPECIAL;
}
static inline unsigned long pte_pfn(pte_t pte)
diff --git a/include/asm-x86/processor-cyrix.h b/include/asm-x86/processor-cyrix.h
index 97568ad..1198f2a 100644
--- a/include/asm-x86/processor-cyrix.h
+++ b/include/asm-x86/processor-cyrix.h
@@ -28,3 +28,11 @@
outb(reg, 0x22);
outb(data, 0x23);
}
+
+#define getCx86_old(reg) ({ outb((reg), 0x22); inb(0x23); })
+
+#define setCx86_old(reg, data) do { \
+ outb((reg), 0x22); \
+ outb((data), 0x23); \
+} while (0)
+
diff --git a/include/asm-x86/processor-flags.h b/include/asm-x86/processor-flags.h
index 5dd7977..dc5f071 100644
--- a/include/asm-x86/processor-flags.h
+++ b/include/asm-x86/processor-flags.h
@@ -59,6 +59,7 @@
#define X86_CR4_OSFXSR 0x00000200 /* enable fast FPU save and restore */
#define X86_CR4_OSXMMEXCPT 0x00000400 /* enable unmasked SSE exceptions */
#define X86_CR4_VMXE 0x00002000 /* enable VMX virtualization */
+#define X86_CR4_OSXSAVE 0x00040000 /* enable xsave and xrestore */
/*
* x86-64 Task Priority Register, CR8
diff --git a/include/asm-x86/processor.h b/include/asm-x86/processor.h
index 5eaf9bf..ee7cbb3 100644
--- a/include/asm-x86/processor.h
+++ b/include/asm-x86/processor.h
@@ -76,11 +76,11 @@
int x86_tlbsize;
__u8 x86_virt_bits;
__u8 x86_phys_bits;
+#endif
/* CPUID returned core id bits: */
__u8 x86_coreid_bits;
/* Max extended CPUID function supported: */
__u32 extended_cpuid_level;
-#endif
/* Maximum supported CPUID level, -1=no CPUID: */
int cpuid_level;
__u32 x86_capability[NCAPINTS];
@@ -166,11 +166,8 @@
extern unsigned int init_intel_cacheinfo(struct cpuinfo_x86 *c);
extern unsigned short num_cache_leaves;
-#if defined(CONFIG_X86_HT) || defined(CONFIG_X86_64)
+extern void detect_extended_topology(struct cpuinfo_x86 *c);
extern void detect_ht(struct cpuinfo_x86 *c);
-#else
-static inline void detect_ht(struct cpuinfo_x86 *c) {}
-#endif
static inline void native_cpuid(unsigned int *eax, unsigned int *ebx,
unsigned int *ecx, unsigned int *edx)
@@ -327,7 +324,12 @@
/* 16*16 bytes for each XMM-reg = 256 bytes: */
u32 xmm_space[64];
- u32 padding[24];
+ u32 padding[12];
+
+ union {
+ u32 padding1[12];
+ u32 sw_reserved[12];
+ };
} __attribute__((aligned(16)));
@@ -351,10 +353,23 @@
u32 entry_eip;
};
+struct xsave_hdr_struct {
+ u64 xstate_bv;
+ u64 reserved1[2];
+ u64 reserved2[5];
+} __attribute__((packed));
+
+struct xsave_struct {
+ struct i387_fxsave_struct i387;
+ struct xsave_hdr_struct xsave_hdr;
+ /* new processor state extensions will go here */
+} __attribute__ ((packed, aligned (64)));
+
union thread_xstate {
struct i387_fsave_struct fsave;
struct i387_fxsave_struct fxsave;
struct i387_soft_struct soft;
+ struct xsave_struct xsave;
};
#ifdef CONFIG_X86_64
@@ -571,41 +586,6 @@
write_cr4(cr4);
}
-struct microcode_header {
- unsigned int hdrver;
- unsigned int rev;
- unsigned int date;
- unsigned int sig;
- unsigned int cksum;
- unsigned int ldrver;
- unsigned int pf;
- unsigned int datasize;
- unsigned int totalsize;
- unsigned int reserved[3];
-};
-
-struct microcode {
- struct microcode_header hdr;
- unsigned int bits[0];
-};
-
-typedef struct microcode microcode_t;
-typedef struct microcode_header microcode_header_t;
-
-/* microcode format is extended from prescott processors */
-struct extended_signature {
- unsigned int sig;
- unsigned int pf;
- unsigned int cksum;
-};
-
-struct extended_sigtable {
- unsigned int count;
- unsigned int cksum;
- unsigned int reserved[3];
- struct extended_signature sigs[0];
-};
-
typedef struct {
unsigned long seg;
} mm_segment_t;
diff --git a/include/asm-x86/ptrace.h b/include/asm-x86/ptrace.h
index d64a610..a202552 100644
--- a/include/asm-x86/ptrace.h
+++ b/include/asm-x86/ptrace.h
@@ -174,13 +174,9 @@
extern unsigned long
convert_ip_to_linear(struct task_struct *child, struct pt_regs *regs);
-
-#ifdef CONFIG_X86_32
extern void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs,
- int error_code);
-#else
+ int error_code, int si_code);
void signal_fault(struct pt_regs *regs, void __user *frame, char *where);
-#endif
extern long syscall_trace_enter(struct pt_regs *);
extern void syscall_trace_leave(struct pt_regs *);
diff --git a/include/asm-x86/segment.h b/include/asm-x86/segment.h
index ea5f0a8..5d6e694 100644
--- a/include/asm-x86/segment.h
+++ b/include/asm-x86/segment.h
@@ -131,12 +131,6 @@
* Matching rules for certain types of segments.
*/
-/* Matches only __KERNEL_CS, ignoring PnP / USER / APM segments */
-#define SEGMENT_IS_KERNEL_CODE(x) (((x) & 0xfc) == GDT_ENTRY_KERNEL_CS * 8)
-
-/* Matches __KERNEL_CS and __USER_CS (they must be 2 entries apart) */
-#define SEGMENT_IS_FLAT_CODE(x) (((x) & 0xec) == GDT_ENTRY_KERNEL_CS * 8)
-
/* Matches PNP_CS32 and PNP_CS16 (they must be consecutive) */
#define SEGMENT_IS_PNP_CODE(x) (((x) & 0xf4) == GDT_ENTRY_PNPBIOS_BASE * 8)
diff --git a/include/asm-x86/setup.h b/include/asm-x86/setup.h
index 9030cb7..11b6cc1 100644
--- a/include/asm-x86/setup.h
+++ b/include/asm-x86/setup.h
@@ -38,6 +38,7 @@
void (*mpc_oem_pci_bus)(struct mpc_config_bus *m);
void (*smp_read_mpc_oem)(struct mp_config_oemtable *oemtable,
unsigned short oemsize);
+ int (*setup_ioapic_ids)(void);
};
extern struct x86_quirks *x86_quirks;
diff --git a/include/asm-x86/sigcontext.h b/include/asm-x86/sigcontext.h
index 24879c8..ee813f4 100644
--- a/include/asm-x86/sigcontext.h
+++ b/include/asm-x86/sigcontext.h
@@ -4,6 +4,40 @@
#include <linux/compiler.h>
#include <asm/types.h>
+#define FP_XSTATE_MAGIC1 0x46505853U
+#define FP_XSTATE_MAGIC2 0x46505845U
+#define FP_XSTATE_MAGIC2_SIZE sizeof(FP_XSTATE_MAGIC2)
+
+/*
+ * bytes 464..511 in the current 512byte layout of fxsave/fxrstor frame
+ * are reserved for SW usage. On cpu's supporting xsave/xrstor, these bytes
+ * are used to extended the fpstate pointer in the sigcontext, which now
+ * includes the extended state information along with fpstate information.
+ *
+ * Presence of FP_XSTATE_MAGIC1 at the beginning of this SW reserved
+ * area and FP_XSTATE_MAGIC2 at the end of memory layout
+ * (extended_size - FP_XSTATE_MAGIC2_SIZE) indicates the presence of the
+ * extended state information in the memory layout pointed by the fpstate
+ * pointer in sigcontext.
+ */
+struct _fpx_sw_bytes {
+ __u32 magic1; /* FP_XSTATE_MAGIC1 */
+ __u32 extended_size; /* total size of the layout referred by
+ * fpstate pointer in the sigcontext.
+ */
+ __u64 xstate_bv;
+ /* feature bit mask (including fp/sse/extended
+ * state) that is present in the memory
+ * layout.
+ */
+ __u32 xstate_size; /* actual xsave state size, based on the
+ * features saved in the layout.
+ * 'extended_size' will be greater than
+ * 'xstate_size'.
+ */
+ __u32 padding[7]; /* for future use. */
+};
+
#ifdef __i386__
/*
* As documented in the iBCS2 standard..
@@ -53,7 +87,13 @@
unsigned long reserved;
struct _fpxreg _fxsr_st[8]; /* FXSR FPU reg data is ignored */
struct _xmmreg _xmm[8];
- unsigned long padding[56];
+ unsigned long padding1[44];
+
+ union {
+ unsigned long padding2[12];
+ struct _fpx_sw_bytes sw_reserved; /* represents the extended
+ * state info */
+ };
};
#define X86_FXSR_MAGIC 0x0000
@@ -79,7 +119,15 @@
unsigned long flags;
unsigned long sp_at_signal;
unsigned short ss, __ssh;
- struct _fpstate __user *fpstate;
+
+ /*
+ * fpstate is really (struct _fpstate *) or (struct _xstate *)
+ * depending on the FP_XSTATE_MAGIC1 encoded in the SW reserved
+ * bytes of (struct _fpstate) and FP_XSTATE_MAGIC2 present at the end
+ * of extended memory layout. See comments at the defintion of
+ * (struct _fpx_sw_bytes)
+ */
+ void __user *fpstate; /* zero when no FPU/extended context */
unsigned long oldmask;
unsigned long cr2;
};
@@ -130,7 +178,12 @@
__u32 mxcsr_mask;
__u32 st_space[32]; /* 8*16 bytes for each FP-reg */
__u32 xmm_space[64]; /* 16*16 bytes for each XMM-reg */
- __u32 reserved2[24];
+ __u32 reserved2[12];
+ union {
+ __u32 reserved3[12];
+ struct _fpx_sw_bytes sw_reserved; /* represents the extended
+ * state information */
+ };
};
#ifdef __KERNEL__
@@ -161,7 +214,15 @@
unsigned long trapno;
unsigned long oldmask;
unsigned long cr2;
- struct _fpstate __user *fpstate; /* zero when no FPU context */
+
+ /*
+ * fpstate is really (struct _fpstate *) or (struct _xstate *)
+ * depending on the FP_XSTATE_MAGIC1 encoded in the SW reserved
+ * bytes of (struct _fpstate) and FP_XSTATE_MAGIC2 present at the end
+ * of extended memory layout. See comments at the defintion of
+ * (struct _fpx_sw_bytes)
+ */
+ void __user *fpstate; /* zero when no FPU/extended context */
unsigned long reserved1[8];
};
#else /* __KERNEL__ */
@@ -202,4 +263,22 @@
#endif /* !__i386__ */
+struct _xsave_hdr {
+ __u64 xstate_bv;
+ __u64 reserved1[2];
+ __u64 reserved2[5];
+};
+
+/*
+ * Extended state pointed by the fpstate pointer in the sigcontext.
+ * In addition to the fpstate, information encoded in the xstate_hdr
+ * indicates the presence of other extended state information
+ * supported by the processor and OS.
+ */
+struct _xstate {
+ struct _fpstate fpstate;
+ struct _xsave_hdr xstate_hdr;
+ /* new processor state extensions go here */
+};
+
#endif /* ASM_X86__SIGCONTEXT_H */
diff --git a/include/asm-x86/sigcontext32.h b/include/asm-x86/sigcontext32.h
index 4e2ec73..8c34703 100644
--- a/include/asm-x86/sigcontext32.h
+++ b/include/asm-x86/sigcontext32.h
@@ -40,7 +40,11 @@
__u32 reserved;
struct _fpxreg _fxsr_st[8];
struct _xmmreg _xmm[8]; /* It's actually 16 */
- __u32 padding[56];
+ __u32 padding[44];
+ union {
+ __u32 padding2[12];
+ struct _fpx_sw_bytes sw_reserved;
+ };
};
struct sigcontext_ia32 {
diff --git a/include/asm-x86/smp.h b/include/asm-x86/smp.h
index 04f84f4..a6afc29 100644
--- a/include/asm-x86/smp.h
+++ b/include/asm-x86/smp.h
@@ -50,12 +50,16 @@
struct smp_ops {
void (*smp_prepare_boot_cpu)(void);
void (*smp_prepare_cpus)(unsigned max_cpus);
- int (*cpu_up)(unsigned cpu);
void (*smp_cpus_done)(unsigned max_cpus);
void (*smp_send_stop)(void);
void (*smp_send_reschedule)(int cpu);
+ int (*cpu_up)(unsigned cpu);
+ int (*cpu_disable)(void);
+ void (*cpu_die)(unsigned int cpu);
+ void (*play_dead)(void);
+
void (*send_call_func_ipi)(cpumask_t mask);
void (*send_call_func_single_ipi)(int cpu);
};
@@ -94,6 +98,21 @@
return smp_ops.cpu_up(cpu);
}
+static inline int __cpu_disable(void)
+{
+ return smp_ops.cpu_disable();
+}
+
+static inline void __cpu_die(unsigned int cpu)
+{
+ smp_ops.cpu_die(cpu);
+}
+
+static inline void play_dead(void)
+{
+ smp_ops.play_dead();
+}
+
static inline void smp_send_reschedule(int cpu)
{
smp_ops.smp_send_reschedule(cpu);
@@ -109,15 +128,20 @@
smp_ops.send_call_func_ipi(mask);
}
+void cpu_disable_common(void);
void native_smp_prepare_boot_cpu(void);
void native_smp_prepare_cpus(unsigned int max_cpus);
void native_smp_cpus_done(unsigned int max_cpus);
int native_cpu_up(unsigned int cpunum);
+int native_cpu_disable(void);
+void native_cpu_die(unsigned int cpu);
+void native_play_dead(void);
+void play_dead_common(void);
+
void native_send_call_func_ipi(cpumask_t mask);
void native_send_call_func_single_ipi(int cpu);
-extern int __cpu_disable(void);
-extern void __cpu_die(unsigned int cpu);
+extern void prefill_possible_map(void);
void smp_store_cpu_info(int id);
#define cpu_physical_id(cpu) per_cpu(x86_cpu_to_apicid, cpu)
@@ -127,15 +151,11 @@
{
return cpus_weight(cpu_callout_map);
}
-#endif /* CONFIG_SMP */
-
-#if defined(CONFIG_SMP) && defined(CONFIG_HOTPLUG_CPU)
-extern void prefill_possible_map(void);
#else
static inline void prefill_possible_map(void)
{
}
-#endif
+#endif /* CONFIG_SMP */
extern unsigned disabled_cpus __cpuinitdata;
@@ -167,30 +187,33 @@
#ifdef CONFIG_X86_LOCAL_APIC
+#ifndef CONFIG_X86_64
static inline int logical_smp_processor_id(void)
{
/* we don't want to mark this access volatile - bad code generation */
return GET_APIC_LOGICAL_ID(*(u32 *)(APIC_BASE + APIC_LDR));
}
-#ifndef CONFIG_X86_64
+#include <mach_apicdef.h>
static inline unsigned int read_apic_id(void)
{
- return *(u32 *)(APIC_BASE + APIC_ID);
+ unsigned int reg;
+
+ reg = *(u32 *)(APIC_BASE + APIC_ID);
+
+ return GET_APIC_ID(reg);
}
-#else
-extern unsigned int read_apic_id(void);
#endif
-# ifdef APIC_DEFINITION
+# if defined(APIC_DEFINITION) || defined(CONFIG_X86_64)
extern int hard_smp_processor_id(void);
# else
-# include <mach_apicdef.h>
+#include <mach_apicdef.h>
static inline int hard_smp_processor_id(void)
{
/* we don't want to mark this access volatile - bad code generation */
- return GET_APIC_ID(read_apic_id());
+ return read_apic_id();
}
# endif /* APIC_DEFINITION */
@@ -202,9 +225,5 @@
#endif /* CONFIG_X86_LOCAL_APIC */
-#ifdef CONFIG_HOTPLUG_CPU
-extern void cpu_uninit(void);
-#endif
-
#endif /* __ASSEMBLY__ */
#endif /* ASM_X86__SMP_H */
diff --git a/include/asm-x86/spinlock.h b/include/asm-x86/spinlock.h
index 93adae3..157ff7f 100644
--- a/include/asm-x86/spinlock.h
+++ b/include/asm-x86/spinlock.h
@@ -21,8 +21,10 @@
#ifdef CONFIG_X86_32
# define LOCK_PTR_REG "a"
+# define REG_PTR_MODE "k"
#else
# define LOCK_PTR_REG "D"
+# define REG_PTR_MODE "q"
#endif
#if defined(CONFIG_X86_32) && \
@@ -54,19 +56,7 @@
* much between them in performance though, especially as locks are out of line.
*/
#if (NR_CPUS < 256)
-static inline int __ticket_spin_is_locked(raw_spinlock_t *lock)
-{
- int tmp = ACCESS_ONCE(lock->slock);
-
- return (((tmp >> 8) & 0xff) != (tmp & 0xff));
-}
-
-static inline int __ticket_spin_is_contended(raw_spinlock_t *lock)
-{
- int tmp = ACCESS_ONCE(lock->slock);
-
- return (((tmp >> 8) - tmp) & 0xff) > 1;
-}
+#define TICKET_SHIFT 8
static __always_inline void __ticket_spin_lock(raw_spinlock_t *lock)
{
@@ -89,19 +79,17 @@
static __always_inline int __ticket_spin_trylock(raw_spinlock_t *lock)
{
- int tmp;
- short new;
+ int tmp, new;
- asm volatile("movw %2,%w0\n\t"
+ asm volatile("movzwl %2, %0\n\t"
"cmpb %h0,%b0\n\t"
+ "leal 0x100(%" REG_PTR_MODE "0), %1\n\t"
"jne 1f\n\t"
- "movw %w0,%w1\n\t"
- "incb %h1\n\t"
LOCK_PREFIX "cmpxchgw %w1,%2\n\t"
"1:"
"sete %b1\n\t"
"movzbl %b1,%0\n\t"
- : "=&a" (tmp), "=Q" (new), "+m" (lock->slock)
+ : "=&a" (tmp), "=&q" (new), "+m" (lock->slock)
:
: "memory", "cc");
@@ -116,19 +104,7 @@
: "memory", "cc");
}
#else
-static inline int __ticket_spin_is_locked(raw_spinlock_t *lock)
-{
- int tmp = ACCESS_ONCE(lock->slock);
-
- return (((tmp >> 16) & 0xffff) != (tmp & 0xffff));
-}
-
-static inline int __ticket_spin_is_contended(raw_spinlock_t *lock)
-{
- int tmp = ACCESS_ONCE(lock->slock);
-
- return (((tmp >> 16) - tmp) & 0xffff) > 1;
-}
+#define TICKET_SHIFT 16
static __always_inline void __ticket_spin_lock(raw_spinlock_t *lock)
{
@@ -146,7 +122,7 @@
/* don't need lfence here, because loads are in-order */
"jmp 1b\n"
"2:"
- : "+Q" (inc), "+m" (lock->slock), "=r" (tmp)
+ : "+r" (inc), "+m" (lock->slock), "=&r" (tmp)
:
: "memory", "cc");
}
@@ -160,13 +136,13 @@
"movl %0,%1\n\t"
"roll $16, %0\n\t"
"cmpl %0,%1\n\t"
+ "leal 0x00010000(%" REG_PTR_MODE "0), %1\n\t"
"jne 1f\n\t"
- "addl $0x00010000, %1\n\t"
LOCK_PREFIX "cmpxchgl %1,%2\n\t"
"1:"
"sete %b1\n\t"
"movzbl %b1,%0\n\t"
- : "=&a" (tmp), "=r" (new), "+m" (lock->slock)
+ : "=&a" (tmp), "=&q" (new), "+m" (lock->slock)
:
: "memory", "cc");
@@ -182,7 +158,19 @@
}
#endif
-#define __raw_spin_lock_flags(lock, flags) __raw_spin_lock(lock)
+static inline int __ticket_spin_is_locked(raw_spinlock_t *lock)
+{
+ int tmp = ACCESS_ONCE(lock->slock);
+
+ return !!(((tmp >> TICKET_SHIFT) ^ tmp) & ((1 << TICKET_SHIFT) - 1));
+}
+
+static inline int __ticket_spin_is_contended(raw_spinlock_t *lock)
+{
+ int tmp = ACCESS_ONCE(lock->slock);
+
+ return (((tmp >> TICKET_SHIFT) - tmp) & ((1 << TICKET_SHIFT) - 1)) > 1;
+}
#ifdef CONFIG_PARAVIRT
/*
@@ -272,6 +260,13 @@
{
__ticket_spin_unlock(lock);
}
+
+static __always_inline void __raw_spin_lock_flags(raw_spinlock_t *lock,
+ unsigned long flags)
+{
+ __raw_spin_lock(lock);
+}
+
#endif /* CONFIG_PARAVIRT */
static inline void __raw_spin_unlock_wait(raw_spinlock_t *lock)
diff --git a/include/asm-x86/statfs.h b/include/asm-x86/statfs.h
index 3f005bc..ca5dc19 100644
--- a/include/asm-x86/statfs.h
+++ b/include/asm-x86/statfs.h
@@ -1,63 +1,12 @@
#ifndef ASM_X86__STATFS_H
#define ASM_X86__STATFS_H
-#ifdef __i386__
-#include <asm-generic/statfs.h>
-#else
-
-#ifndef __KERNEL_STRICT_NAMES
-
-#include <linux/types.h>
-
-typedef __kernel_fsid_t fsid_t;
-
-#endif
-
/*
- * This is ugly -- we're already 64-bit clean, so just duplicate the
- * definitions.
+ * We need compat_statfs64 to be packed, because the i386 ABI won't
+ * add padding at the end to bring it to a multiple of 8 bytes, but
+ * the x86_64 ABI will.
*/
-struct statfs {
- long f_type;
- long f_bsize;
- long f_blocks;
- long f_bfree;
- long f_bavail;
- long f_files;
- long f_ffree;
- __kernel_fsid_t f_fsid;
- long f_namelen;
- long f_frsize;
- long f_spare[5];
-};
+#define ARCH_PACK_COMPAT_STATFS64 __attribute__((packed,aligned(4)))
-struct statfs64 {
- long f_type;
- long f_bsize;
- long f_blocks;
- long f_bfree;
- long f_bavail;
- long f_files;
- long f_ffree;
- __kernel_fsid_t f_fsid;
- long f_namelen;
- long f_frsize;
- long f_spare[5];
-};
-
-struct compat_statfs64 {
- __u32 f_type;
- __u32 f_bsize;
- __u64 f_blocks;
- __u64 f_bfree;
- __u64 f_bavail;
- __u64 f_files;
- __u64 f_ffree;
- __kernel_fsid_t f_fsid;
- __u32 f_namelen;
- __u32 f_frsize;
- __u32 f_spare[5];
-} __attribute__((packed));
-
-#endif /* !__i386__ */
+#include <asm-generic/statfs.h>
#endif /* ASM_X86__STATFS_H */
diff --git a/include/asm-x86/mach-summit/mach_apic.h b/include/asm-x86/summit/apic.h
similarity index 92%
rename from include/asm-x86/mach-summit/mach_apic.h
rename to include/asm-x86/summit/apic.h
index 7a66758..c5b2e4b 100644
--- a/include/asm-x86/mach-summit/mach_apic.h
+++ b/include/asm-x86/summit/apic.h
@@ -1,5 +1,5 @@
-#ifndef ASM_X86__MACH_SUMMIT__MACH_APIC_H
-#define ASM_X86__MACH_SUMMIT__MACH_APIC_H
+#ifndef __ASM_SUMMIT_APIC_H
+#define __ASM_SUMMIT_APIC_H
#include <asm/smp.h>
@@ -21,7 +21,7 @@
* Just start on cpu 0. IRQ balancing will spread load
*/
return cpumask_of_cpu(0);
-}
+}
#define TARGET_CPUS (target_cpus())
#define INT_DELIVERY_MODE (dest_LowestPrio)
@@ -30,10 +30,10 @@
static inline unsigned long check_apicid_used(physid_mask_t bitmap, int apicid)
{
return 0;
-}
+}
/* we don't use the phys_cpu_present_map to indicate apicid presence */
-static inline unsigned long check_apicid_present(int bit)
+static inline unsigned long check_apicid_present(int bit)
{
return 1;
}
@@ -122,7 +122,7 @@
static inline physid_mask_t apicid_to_cpu_present(int apicid)
{
- return physid_mask_of_physid(apicid);
+ return physid_mask_of_physid(0);
}
static inline void setup_portio_remap(void)
@@ -143,22 +143,22 @@
int num_bits_set;
int cpus_found = 0;
int cpu;
- int apicid;
+ int apicid;
num_bits_set = cpus_weight(cpumask);
/* Return id to all */
if (num_bits_set == NR_CPUS)
return (int) 0xFF;
- /*
- * The cpus in the mask must all be on the apic cluster. If are not
- * on the same apicid cluster return default value of TARGET_CPUS.
+ /*
+ * The cpus in the mask must all be on the apic cluster. If are not
+ * on the same apicid cluster return default value of TARGET_CPUS.
*/
cpu = first_cpu(cpumask);
apicid = cpu_to_logical_apicid(cpu);
while (cpus_found < num_bits_set) {
if (cpu_isset(cpu, cpumask)) {
int new_apicid = cpu_to_logical_apicid(cpu);
- if (apicid_cluster(apicid) !=
+ if (apicid_cluster(apicid) !=
apicid_cluster(new_apicid)){
printk ("%s: Not a valid mask!\n",__FUNCTION__);
return 0xFF;
@@ -182,4 +182,4 @@
return hard_smp_processor_id() >> index_msb;
}
-#endif /* ASM_X86__MACH_SUMMIT__MACH_APIC_H */
+#endif /* __ASM_SUMMIT_APIC_H */
diff --git a/include/asm-x86/summit/apicdef.h b/include/asm-x86/summit/apicdef.h
new file mode 100644
index 0000000..f3fbca1
--- /dev/null
+++ b/include/asm-x86/summit/apicdef.h
@@ -0,0 +1,13 @@
+#ifndef __ASM_SUMMIT_APICDEF_H
+#define __ASM_SUMMIT_APICDEF_H
+
+#define APIC_ID_MASK (0xFF<<24)
+
+static inline unsigned get_apic_id(unsigned long x)
+{
+ return (x>>24)&0xFF;
+}
+
+#define GET_APIC_ID(x) get_apic_id(x)
+
+#endif
diff --git a/include/asm-x86/mach-summit/mach_ipi.h b/include/asm-x86/summit/ipi.h
similarity index 77%
rename from include/asm-x86/mach-summit/mach_ipi.h
rename to include/asm-x86/summit/ipi.h
index a3b31c5..53bd1e7 100644
--- a/include/asm-x86/mach-summit/mach_ipi.h
+++ b/include/asm-x86/summit/ipi.h
@@ -1,5 +1,5 @@
-#ifndef ASM_X86__MACH_SUMMIT__MACH_IPI_H
-#define ASM_X86__MACH_SUMMIT__MACH_IPI_H
+#ifndef __ASM_SUMMIT_IPI_H
+#define __ASM_SUMMIT_IPI_H
void send_IPI_mask_sequence(cpumask_t mask, int vector);
@@ -22,4 +22,4 @@
send_IPI_mask(cpu_online_map, vector);
}
-#endif /* ASM_X86__MACH_SUMMIT__MACH_IPI_H */
+#endif /* __ASM_SUMMIT_IPI_H */
diff --git a/include/asm-x86/mach-summit/irq_vectors_limits.h b/include/asm-x86/summit/irq_vectors_limits.h
similarity index 65%
rename from include/asm-x86/mach-summit/irq_vectors_limits.h
rename to include/asm-x86/summit/irq_vectors_limits.h
index 22f376a..890ce3f 100644
--- a/include/asm-x86/mach-summit/irq_vectors_limits.h
+++ b/include/asm-x86/summit/irq_vectors_limits.h
@@ -1,5 +1,5 @@
-#ifndef ASM_X86__MACH_SUMMIT__IRQ_VECTORS_LIMITS_H
-#define ASM_X86__MACH_SUMMIT__IRQ_VECTORS_LIMITS_H
+#ifndef _ASM_IRQ_VECTORS_LIMITS_H
+#define _ASM_IRQ_VECTORS_LIMITS_H
/*
* For Summit or generic (i.e. installer) kernels, we have lots of I/O APICs,
@@ -11,4 +11,4 @@
#define NR_IRQS 224
#define NR_IRQ_VECTORS 1024
-#endif /* ASM_X86__MACH_SUMMIT__IRQ_VECTORS_LIMITS_H */
+#endif /* _ASM_IRQ_VECTORS_LIMITS_H */
diff --git a/include/asm-x86/mach-summit/mach_mpparse.h b/include/asm-x86/summit/mpparse.h
similarity index 94%
rename from include/asm-x86/mach-summit/mach_mpparse.h
rename to include/asm-x86/summit/mpparse.h
index 92396f2..013ce6f 100644
--- a/include/asm-x86/mach-summit/mach_mpparse.h
+++ b/include/asm-x86/summit/mpparse.h
@@ -1,7 +1,6 @@
-#ifndef ASM_X86__MACH_SUMMIT__MACH_MPPARSE_H
-#define ASM_X86__MACH_SUMMIT__MACH_MPPARSE_H
+#ifndef __ASM_SUMMIT_MPPARSE_H
+#define __ASM_SUMMIT_MPPARSE_H
-#include <mach_apic.h>
#include <asm/tsc.h>
extern int use_cyclone;
@@ -12,11 +11,11 @@
#define setup_summit() {}
#endif
-static inline int mps_oem_check(struct mp_config_table *mpc, char *oem,
+static inline int mps_oem_check(struct mp_config_table *mpc, char *oem,
char *productid)
{
- if (!strncmp(oem, "IBM ENSW", 8) &&
- (!strncmp(productid, "VIGIL SMP", 9)
+ if (!strncmp(oem, "IBM ENSW", 8) &&
+ (!strncmp(productid, "VIGIL SMP", 9)
|| !strncmp(productid, "EXA", 3)
|| !strncmp(productid, "RUTHLESS SMP", 12))){
mark_tsc_unstable("Summit based system");
@@ -107,4 +106,4 @@
rio->type == LookOutAWPEG || rio->type == LookOutBWPEG);
}
-#endif /* ASM_X86__MACH_SUMMIT__MACH_MPPARSE_H */
+#endif /* __ASM_SUMMIT_MPPARSE_H */
diff --git a/include/asm-x86/system.h b/include/asm-x86/system.h
index 34505dd..b20c894 100644
--- a/include/asm-x86/system.h
+++ b/include/asm-x86/system.h
@@ -64,7 +64,10 @@
\
/* regparm parameters for __switch_to(): */ \
[prev] "a" (prev), \
- [next] "d" (next)); \
+ [next] "d" (next) \
+ \
+ : /* reloaded segment registers */ \
+ "memory"); \
} while (0)
/*
diff --git a/include/asm-x86/thread_info.h b/include/asm-x86/thread_info.h
index 4db0066..3f4e52b 100644
--- a/include/asm-x86/thread_info.h
+++ b/include/asm-x86/thread_info.h
@@ -241,6 +241,7 @@
#define TS_POLLING 0x0004 /* true if in idle loop
and not sleeping */
#define TS_RESTORE_SIGMASK 0x0008 /* restore signal mask in do_signal() */
+#define TS_XSAVE 0x0010 /* Use xsave/xrstor */
#define tsk_is_polling(t) (task_thread_info(t)->status & TS_POLLING)
diff --git a/include/asm-x86/tlbflush.h b/include/asm-x86/tlbflush.h
index ef68b76..3cdd08b 100644
--- a/include/asm-x86/tlbflush.h
+++ b/include/asm-x86/tlbflush.h
@@ -119,6 +119,10 @@
{
}
+static inline void reset_lazy_tlbstate(void)
+{
+}
+
#else /* SMP */
#include <asm/smp.h>
@@ -151,6 +155,12 @@
char __cacheline_padding[L1_CACHE_BYTES-8];
};
DECLARE_PER_CPU(struct tlb_state, cpu_tlbstate);
+
+void reset_lazy_tlbstate(void);
+#else
+static inline void reset_lazy_tlbstate(void)
+{
+}
#endif
#endif /* SMP */
diff --git a/include/asm-x86/traps.h b/include/asm-x86/traps.h
index 2ccebc6..6c3dc2c 100644
--- a/include/asm-x86/traps.h
+++ b/include/asm-x86/traps.h
@@ -1,7 +1,14 @@
#ifndef ASM_X86__TRAPS_H
#define ASM_X86__TRAPS_H
-/* Common in X86_32 and X86_64 */
+#include <asm/debugreg.h>
+
+#ifdef CONFIG_X86_32
+#define dotraplinkage
+#else
+#define dotraplinkage asmlinkage
+#endif
+
asmlinkage void divide_error(void);
asmlinkage void debug(void);
asmlinkage void nmi(void);
@@ -10,61 +17,65 @@
asmlinkage void bounds(void);
asmlinkage void invalid_op(void);
asmlinkage void device_not_available(void);
+#ifdef CONFIG_X86_64
+asmlinkage void double_fault(void);
+#endif
asmlinkage void coprocessor_segment_overrun(void);
asmlinkage void invalid_TSS(void);
asmlinkage void segment_not_present(void);
asmlinkage void stack_segment(void);
asmlinkage void general_protection(void);
asmlinkage void page_fault(void);
-asmlinkage void coprocessor_error(void);
-asmlinkage void simd_coprocessor_error(void);
-asmlinkage void alignment_check(void);
asmlinkage void spurious_interrupt_bug(void);
+asmlinkage void coprocessor_error(void);
+asmlinkage void alignment_check(void);
#ifdef CONFIG_X86_MCE
asmlinkage void machine_check(void);
#endif /* CONFIG_X86_MCE */
+asmlinkage void simd_coprocessor_error(void);
-void do_divide_error(struct pt_regs *, long);
-void do_overflow(struct pt_regs *, long);
-void do_bounds(struct pt_regs *, long);
-void do_coprocessor_segment_overrun(struct pt_regs *, long);
-void do_invalid_TSS(struct pt_regs *, long);
-void do_segment_not_present(struct pt_regs *, long);
-void do_stack_segment(struct pt_regs *, long);
-void do_alignment_check(struct pt_regs *, long);
-void do_invalid_op(struct pt_regs *, long);
-void do_general_protection(struct pt_regs *, long);
-void do_nmi(struct pt_regs *, long);
+dotraplinkage void do_divide_error(struct pt_regs *, long);
+dotraplinkage void do_debug(struct pt_regs *, long);
+dotraplinkage void do_nmi(struct pt_regs *, long);
+dotraplinkage void do_int3(struct pt_regs *, long);
+dotraplinkage void do_overflow(struct pt_regs *, long);
+dotraplinkage void do_bounds(struct pt_regs *, long);
+dotraplinkage void do_invalid_op(struct pt_regs *, long);
+dotraplinkage void do_device_not_available(struct pt_regs *, long);
+dotraplinkage void do_coprocessor_segment_overrun(struct pt_regs *, long);
+dotraplinkage void do_invalid_TSS(struct pt_regs *, long);
+dotraplinkage void do_segment_not_present(struct pt_regs *, long);
+dotraplinkage void do_stack_segment(struct pt_regs *, long);
+dotraplinkage void do_general_protection(struct pt_regs *, long);
+dotraplinkage void do_page_fault(struct pt_regs *, unsigned long);
+dotraplinkage void do_spurious_interrupt_bug(struct pt_regs *, long);
+dotraplinkage void do_coprocessor_error(struct pt_regs *, long);
+dotraplinkage void do_alignment_check(struct pt_regs *, long);
+#ifdef CONFIG_X86_MCE
+dotraplinkage void do_machine_check(struct pt_regs *, long);
+#endif
+dotraplinkage void do_simd_coprocessor_error(struct pt_regs *, long);
+#ifdef CONFIG_X86_32
+dotraplinkage void do_iret_error(struct pt_regs *, long);
+#endif
+
+static inline int get_si_code(unsigned long condition)
+{
+ if (condition & DR_STEP)
+ return TRAP_TRACE;
+ else if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3))
+ return TRAP_HWBKPT;
+ else
+ return TRAP_BRKPT;
+}
extern int panic_on_unrecovered_nmi;
extern int kstack_depth_to_print;
#ifdef CONFIG_X86_32
-
-void do_iret_error(struct pt_regs *, long);
-void do_int3(struct pt_regs *, long);
-void do_debug(struct pt_regs *, long);
void math_error(void __user *);
-void do_coprocessor_error(struct pt_regs *, long);
-void do_simd_coprocessor_error(struct pt_regs *, long);
-void do_spurious_interrupt_bug(struct pt_regs *, long);
unsigned long patch_espfix_desc(unsigned long, unsigned long);
asmlinkage void math_emulate(long);
+#endif
-void do_page_fault(struct pt_regs *regs, unsigned long error_code);
-
-#else /* CONFIG_X86_32 */
-
-asmlinkage void double_fault(void);
-
-asmlinkage void do_int3(struct pt_regs *, long);
-asmlinkage void do_stack_segment(struct pt_regs *, long);
-asmlinkage void do_debug(struct pt_regs *, unsigned long);
-asmlinkage void do_coprocessor_error(struct pt_regs *);
-asmlinkage void do_simd_coprocessor_error(struct pt_regs *);
-asmlinkage void do_spurious_interrupt_bug(struct pt_regs *);
-
-asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code);
-
-#endif /* CONFIG_X86_32 */
#endif /* ASM_X86__TRAPS_H */
diff --git a/include/asm-x86/ucontext.h b/include/asm-x86/ucontext.h
index 9948dd3..89eaa54 100644
--- a/include/asm-x86/ucontext.h
+++ b/include/asm-x86/ucontext.h
@@ -1,6 +1,12 @@
#ifndef ASM_X86__UCONTEXT_H
#define ASM_X86__UCONTEXT_H
+#define UC_FP_XSTATE 0x1 /* indicates the presence of extended state
+ * information in the memory layout pointed
+ * by the fpstate pointer in the ucontext's
+ * sigcontext struct (uc_mcontext).
+ */
+
struct ucontext {
unsigned long uc_flags;
struct ucontext *uc_link;
diff --git a/include/asm-x86/xcr.h b/include/asm-x86/xcr.h
new file mode 100644
index 0000000..f2cba4e
--- /dev/null
+++ b/include/asm-x86/xcr.h
@@ -0,0 +1,49 @@
+/* -*- linux-c -*- ------------------------------------------------------- *
+ *
+ * Copyright 2008 rPath, Inc. - All Rights Reserved
+ *
+ * This file is part of the Linux kernel, and is made available under
+ * the terms of the GNU General Public License version 2 or (at your
+ * option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * asm-x86/xcr.h
+ *
+ * Definitions for the eXtended Control Register instructions
+ */
+
+#ifndef _ASM_X86_XCR_H
+#define _ASM_X86_XCR_H
+
+#define XCR_XFEATURE_ENABLED_MASK 0x00000000
+
+#ifdef __KERNEL__
+# ifndef __ASSEMBLY__
+
+#include <linux/types.h>
+
+static inline u64 xgetbv(u32 index)
+{
+ u32 eax, edx;
+
+ asm volatile(".byte 0x0f,0x01,0xd0" /* xgetbv */
+ : "=a" (eax), "=d" (edx)
+ : "c" (index));
+ return eax + ((u64)edx << 32);
+}
+
+static inline void xsetbv(u32 index, u64 value)
+{
+ u32 eax = value;
+ u32 edx = value >> 32;
+
+ asm volatile(".byte 0x0f,0x01,0xd1" /* xsetbv */
+ : : "a" (eax), "d" (edx), "c" (index));
+}
+
+# endif /* __ASSEMBLY__ */
+#endif /* __KERNEL__ */
+
+#endif /* _ASM_X86_XCR_H */
diff --git a/include/asm-x86/xen/hypervisor.h b/include/asm-x86/xen/hypervisor.h
index 0ef3a88..445a247 100644
--- a/include/asm-x86/xen/hypervisor.h
+++ b/include/asm-x86/xen/hypervisor.h
@@ -54,7 +54,6 @@
/* arch/i386/kernel/setup.c */
extern struct shared_info *HYPERVISOR_shared_info;
extern struct start_info *xen_start_info;
-#define is_initial_xendomain() (xen_start_info->flags & SIF_INITDOMAIN)
/* arch/i386/mach-xen/evtchn.c */
/* Force a proper event-channel callback from Xen. */
@@ -67,6 +66,17 @@
#define MULTI_UVMFLAGS_INDEX 3
#define MULTI_UVMDOMID_INDEX 4
-#define is_running_on_xen() (xen_start_info ? 1 : 0)
+enum xen_domain_type {
+ XEN_NATIVE,
+ XEN_PV_DOMAIN,
+ XEN_HVM_DOMAIN,
+};
+
+extern enum xen_domain_type xen_domain_type;
+
+#define xen_domain() (xen_domain_type != XEN_NATIVE)
+#define xen_pv_domain() (xen_domain_type == XEN_PV_DOMAIN)
+#define xen_initial_domain() (xen_pv_domain() && xen_start_info->flags & SIF_INITDOMAIN)
+#define xen_hvm_domain() (xen_domain_type == XEN_HVM_DOMAIN)
#endif /* ASM_X86__XEN__HYPERVISOR_H */
diff --git a/include/asm-x86/xsave.h b/include/asm-x86/xsave.h
new file mode 100644
index 0000000..08e9a1a
--- /dev/null
+++ b/include/asm-x86/xsave.h
@@ -0,0 +1,118 @@
+#ifndef __ASM_X86_XSAVE_H
+#define __ASM_X86_XSAVE_H
+
+#include <linux/types.h>
+#include <asm/processor.h>
+#include <asm/i387.h>
+
+#define XSTATE_FP 0x1
+#define XSTATE_SSE 0x2
+
+#define XSTATE_FPSSE (XSTATE_FP | XSTATE_SSE)
+
+#define FXSAVE_SIZE 512
+
+/*
+ * These are the features that the OS can handle currently.
+ */
+#define XCNTXT_MASK (XSTATE_FP | XSTATE_SSE)
+
+#ifdef CONFIG_X86_64
+#define REX_PREFIX "0x48, "
+#else
+#define REX_PREFIX
+#endif
+
+extern unsigned int xstate_size;
+extern u64 pcntxt_mask;
+extern struct xsave_struct *init_xstate_buf;
+
+extern void xsave_cntxt_init(void);
+extern void xsave_init(void);
+extern int init_fpu(struct task_struct *child);
+extern int check_for_xstate(struct i387_fxsave_struct __user *buf,
+ void __user *fpstate,
+ struct _fpx_sw_bytes *sw);
+
+static inline int xrstor_checking(struct xsave_struct *fx)
+{
+ int err;
+
+ asm volatile("1: .byte " REX_PREFIX "0x0f,0xae,0x2f\n\t"
+ "2:\n"
+ ".section .fixup,\"ax\"\n"
+ "3: movl $-1,%[err]\n"
+ " jmp 2b\n"
+ ".previous\n"
+ _ASM_EXTABLE(1b, 3b)
+ : [err] "=r" (err)
+ : "D" (fx), "m" (*fx), "a" (-1), "d" (-1), "0" (0)
+ : "memory");
+
+ return err;
+}
+
+static inline int xsave_user(struct xsave_struct __user *buf)
+{
+ int err;
+ __asm__ __volatile__("1: .byte " REX_PREFIX "0x0f,0xae,0x27\n"
+ "2:\n"
+ ".section .fixup,\"ax\"\n"
+ "3: movl $-1,%[err]\n"
+ " jmp 2b\n"
+ ".previous\n"
+ ".section __ex_table,\"a\"\n"
+ _ASM_ALIGN "\n"
+ _ASM_PTR "1b,3b\n"
+ ".previous"
+ : [err] "=r" (err)
+ : "D" (buf), "a" (-1), "d" (-1), "0" (0)
+ : "memory");
+ if (unlikely(err) && __clear_user(buf, xstate_size))
+ err = -EFAULT;
+ /* No need to clear here because the caller clears USED_MATH */
+ return err;
+}
+
+static inline int xrestore_user(struct xsave_struct __user *buf, u64 mask)
+{
+ int err;
+ struct xsave_struct *xstate = ((__force struct xsave_struct *)buf);
+ u32 lmask = mask;
+ u32 hmask = mask >> 32;
+
+ __asm__ __volatile__("1: .byte " REX_PREFIX "0x0f,0xae,0x2f\n"
+ "2:\n"
+ ".section .fixup,\"ax\"\n"
+ "3: movl $-1,%[err]\n"
+ " jmp 2b\n"
+ ".previous\n"
+ ".section __ex_table,\"a\"\n"
+ _ASM_ALIGN "\n"
+ _ASM_PTR "1b,3b\n"
+ ".previous"
+ : [err] "=r" (err)
+ : "D" (xstate), "a" (lmask), "d" (hmask), "0" (0)
+ : "memory"); /* memory required? */
+ return err;
+}
+
+static inline void xrstor_state(struct xsave_struct *fx, u64 mask)
+{
+ u32 lmask = mask;
+ u32 hmask = mask >> 32;
+
+ asm volatile(".byte " REX_PREFIX "0x0f,0xae,0x2f\n\t"
+ : : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask)
+ : "memory");
+}
+
+static inline void xsave(struct task_struct *tsk)
+{
+ /* This, however, we can work around by forcing the compiler to select
+ an addressing mode that doesn't require extended registers. */
+ __asm__ __volatile__(".byte " REX_PREFIX "0x0f,0xae,0x27"
+ : : "D" (&(tsk->thread.xstate->xsave)),
+ "a" (-1), "d"(-1) : "memory");
+}
+#endif
diff --git a/include/asm-xtensa/a.out.h b/include/asm-xtensa/a.out.h
deleted file mode 100644
index fdf1370..0000000
--- a/include/asm-xtensa/a.out.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * include/asm-xtensa/a.out.h
- *
- * Dummy a.out file. Xtensa does not support the a.out format, but the kernel
- * seems to depend on it.
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2001 - 2005 Tensilica Inc.
- */
-
-#ifndef _XTENSA_A_OUT_H
-#define _XTENSA_A_OUT_H
-
-struct exec
-{
- unsigned long a_info;
- unsigned a_text;
- unsigned a_data;
- unsigned a_bss;
- unsigned a_syms;
- unsigned a_entry;
- unsigned a_trsize;
- unsigned a_drsize;
-};
-
-#endif /* _XTENSA_A_OUT_H */
diff --git a/include/linux/ata.h b/include/linux/ata.h
index be00973..a53318b 100644
--- a/include/linux/ata.h
+++ b/include/linux/ata.h
@@ -30,6 +30,7 @@
#define __LINUX_ATA_H__
#include <linux/types.h>
+#include <asm/byteorder.h>
/* defines only for the constants which don't work well as enums */
#define ATA_DMA_BOUNDARY 0xffffUL
@@ -558,6 +559,15 @@
return id[ATA_ID_COMMAND_SET_2] & (1 << 12);
}
+static inline int ata_id_flush_enabled(const u16 *id)
+{
+ if (ata_id_has_flush(id) == 0)
+ return 0;
+ if ((id[ATA_ID_CSF_DEFAULT] & 0xC000) != 0x4000)
+ return 0;
+ return id[ATA_ID_CFS_ENABLE_2] & (1 << 12);
+}
+
static inline int ata_id_has_flush_ext(const u16 *id)
{
if ((id[ATA_ID_COMMAND_SET_2] & 0xC000) != 0x4000)
@@ -565,6 +575,19 @@
return id[ATA_ID_COMMAND_SET_2] & (1 << 13);
}
+static inline int ata_id_flush_ext_enabled(const u16 *id)
+{
+ if (ata_id_has_flush_ext(id) == 0)
+ return 0;
+ if ((id[ATA_ID_CSF_DEFAULT] & 0xC000) != 0x4000)
+ return 0;
+ /*
+ * some Maxtor disks have bit 13 defined incorrectly
+ * so check bit 10 too
+ */
+ return (id[ATA_ID_CFS_ENABLE_2] & 0x2400) == 0x2400;
+}
+
static inline int ata_id_has_lba48(const u16 *id)
{
if ((id[ATA_ID_COMMAND_SET_2] & 0xC000) != 0x4000)
@@ -574,6 +597,15 @@
return id[ATA_ID_COMMAND_SET_2] & (1 << 10);
}
+static inline int ata_id_lba48_enabled(const u16 *id)
+{
+ if (ata_id_has_lba48(id) == 0)
+ return 0;
+ if ((id[ATA_ID_CSF_DEFAULT] & 0xC000) != 0x4000)
+ return 0;
+ return id[ATA_ID_CFS_ENABLE_2] & (1 << 10);
+}
+
static inline int ata_id_hpa_enabled(const u16 *id)
{
/* Yes children, word 83 valid bits cover word 82 data */
@@ -645,7 +677,15 @@
static inline int ata_id_is_sata(const u16 *id)
{
- return ata_id_major_version(id) >= 5 && id[ATA_ID_HW_CONFIG] == 0;
+ /*
+ * See if word 93 is 0 AND drive is at least ATA-5 compatible
+ * verifying that word 80 by casting it to a signed type --
+ * this trick allows us to filter out the reserved values of
+ * 0x0000 and 0xffff along with the earlier ATA revisions...
+ */
+ if (id[ATA_ID_HW_CONFIG] == 0 && (short)id[ATA_ID_MAJOR_VER] >= 0x0020)
+ return 1;
+ return 0;
}
static inline int ata_id_has_tpm(const u16 *id)
@@ -742,6 +782,76 @@
return ata_id_major_version(dev_id) >= 7 && (dev_id[62] & 0x8000);
}
+/*
+ * ata_id_is_lba_capacity_ok() performs a sanity check on
+ * the claimed LBA capacity value for the device.
+ *
+ * Returns 1 if LBA capacity looks sensible, 0 otherwise.
+ *
+ * It is called only once for each device.
+ */
+static inline int ata_id_is_lba_capacity_ok(u16 *id)
+{
+ unsigned long lba_sects, chs_sects, head, tail;
+
+ /* No non-LBA info .. so valid! */
+ if (id[ATA_ID_CYLS] == 0)
+ return 1;
+
+ lba_sects = ata_id_u32(id, ATA_ID_LBA_CAPACITY);
+
+ /*
+ * The ATA spec tells large drives to return
+ * C/H/S = 16383/16/63 independent of their size.
+ * Some drives can be jumpered to use 15 heads instead of 16.
+ * Some drives can be jumpered to use 4092 cyls instead of 16383.
+ */
+ if ((id[ATA_ID_CYLS] == 16383 ||
+ (id[ATA_ID_CYLS] == 4092 && id[ATA_ID_CUR_CYLS] == 16383)) &&
+ id[ATA_ID_SECTORS] == 63 &&
+ (id[ATA_ID_HEADS] == 15 || id[ATA_ID_HEADS] == 16) &&
+ (lba_sects >= 16383 * 63 * id[ATA_ID_HEADS]))
+ return 1;
+
+ chs_sects = id[ATA_ID_CYLS] * id[ATA_ID_HEADS] * id[ATA_ID_SECTORS];
+
+ /* perform a rough sanity check on lba_sects: within 10% is OK */
+ if (lba_sects - chs_sects < chs_sects/10)
+ return 1;
+
+ /* some drives have the word order reversed */
+ head = (lba_sects >> 16) & 0xffff;
+ tail = lba_sects & 0xffff;
+ lba_sects = head | (tail << 16);
+
+ if (lba_sects - chs_sects < chs_sects/10) {
+ *(__le32 *)&id[ATA_ID_LBA_CAPACITY] = __cpu_to_le32(lba_sects);
+ return 1; /* LBA capacity is (now) good */
+ }
+
+ return 0; /* LBA capacity value may be bad */
+}
+
+static inline void ata_id_to_hd_driveid(u16 *id)
+{
+#ifdef __BIG_ENDIAN
+ /* accessed in struct hd_driveid as 8-bit values */
+ id[ATA_ID_MAX_MULTSECT] = __cpu_to_le16(id[ATA_ID_MAX_MULTSECT]);
+ id[ATA_ID_CAPABILITY] = __cpu_to_le16(id[ATA_ID_CAPABILITY]);
+ id[ATA_ID_OLD_PIO_MODES] = __cpu_to_le16(id[ATA_ID_OLD_PIO_MODES]);
+ id[ATA_ID_OLD_DMA_MODES] = __cpu_to_le16(id[ATA_ID_OLD_DMA_MODES]);
+ id[ATA_ID_MULTSECT] = __cpu_to_le16(id[ATA_ID_MULTSECT]);
+
+ /* as 32-bit values */
+ *(u32 *)&id[ATA_ID_LBA_CAPACITY] = ata_id_u32(id, ATA_ID_LBA_CAPACITY);
+ *(u32 *)&id[ATA_ID_SPG] = ata_id_u32(id, ATA_ID_SPG);
+
+ /* as 64-bit value */
+ *(u64 *)&id[ATA_ID_LBA_CAPACITY_2] =
+ ata_id_u64(id, ATA_ID_LBA_CAPACITY_2);
+#endif
+}
+
static inline int is_multi_taskfile(struct ata_taskfile *tf)
{
return (tf->command == ATA_CMD_READ_MULTI) ||
diff --git a/include/linux/devpts_fs.h b/include/linux/devpts_fs.h
index 154769c..5ce0e5f 100644
--- a/include/linux/devpts_fs.h
+++ b/include/linux/devpts_fs.h
@@ -17,20 +17,31 @@
#ifdef CONFIG_UNIX98_PTYS
-int devpts_new_index(void);
-void devpts_kill_index(int idx);
-int devpts_pty_new(struct tty_struct *tty); /* mknod in devpts */
-struct tty_struct *devpts_get_tty(int number); /* get tty structure */
-void devpts_pty_kill(int number); /* unlink */
+int devpts_new_index(struct inode *ptmx_inode);
+void devpts_kill_index(struct inode *ptmx_inode, int idx);
+/* mknod in devpts */
+int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty);
+/* get tty structure */
+struct tty_struct *devpts_get_tty(struct inode *pts_inode, int number);
+/* unlink */
+void devpts_pty_kill(struct tty_struct *tty);
#else
/* Dummy stubs in the no-pty case */
-static inline int devpts_new_index(void) { return -EINVAL; }
-static inline void devpts_kill_index(int idx) { }
-static inline int devpts_pty_new(struct tty_struct *tty) { return -EINVAL; }
-static inline struct tty_struct *devpts_get_tty(int number) { return NULL; }
-static inline void devpts_pty_kill(int number) { }
+static inline int devpts_new_index(struct inode *ptmx_inode) { return -EINVAL; }
+static inline void devpts_kill_index(struct inode *ptmx_inode, int idx) { }
+static inline int devpts_pty_new(struct inode *ptmx_inode,
+ struct tty_struct *tty)
+{
+ return -EINVAL;
+}
+static inline struct tty_struct *devpts_get_tty(struct inode *pts_inode,
+ int number)
+{
+ return NULL;
+}
+static inline void devpts_pty_kill(struct tty_struct *tty) { }
#endif
diff --git a/include/linux/dmar.h b/include/linux/dmar.h
index 56c73b8..c360c55 100644
--- a/include/linux/dmar.h
+++ b/include/linux/dmar.h
@@ -25,9 +25,99 @@
#include <linux/types.h>
#include <linux/msi.h>
-#ifdef CONFIG_DMAR
+#if defined(CONFIG_DMAR) || defined(CONFIG_INTR_REMAP)
struct intel_iommu;
+struct dmar_drhd_unit {
+ struct list_head list; /* list of drhd units */
+ struct acpi_dmar_header *hdr; /* ACPI header */
+ u64 reg_base_addr; /* register base address*/
+ struct pci_dev **devices; /* target device array */
+ int devices_cnt; /* target device count */
+ u8 ignored:1; /* ignore drhd */
+ u8 include_all:1;
+ struct intel_iommu *iommu;
+};
+
+extern struct list_head dmar_drhd_units;
+
+#define for_each_drhd_unit(drhd) \
+ list_for_each_entry(drhd, &dmar_drhd_units, list)
+
+extern int dmar_table_init(void);
+extern int early_dmar_detect(void);
+extern int dmar_dev_scope_init(void);
+
+/* Intel IOMMU detection */
+extern void detect_intel_iommu(void);
+
+
+extern int parse_ioapics_under_ir(void);
+extern int alloc_iommu(struct dmar_drhd_unit *);
+#else
+static inline void detect_intel_iommu(void)
+{
+ return;
+}
+
+static inline int dmar_table_init(void)
+{
+ return -ENODEV;
+}
+#endif /* !CONFIG_DMAR && !CONFIG_INTR_REMAP */
+
+#ifdef CONFIG_INTR_REMAP
+extern int intr_remapping_enabled;
+extern int enable_intr_remapping(int);
+
+struct irte {
+ union {
+ struct {
+ __u64 present : 1,
+ fpd : 1,
+ dst_mode : 1,
+ redir_hint : 1,
+ trigger_mode : 1,
+ dlvry_mode : 3,
+ avail : 4,
+ __reserved_1 : 4,
+ vector : 8,
+ __reserved_2 : 8,
+ dest_id : 32;
+ };
+ __u64 low;
+ };
+
+ union {
+ struct {
+ __u64 sid : 16,
+ sq : 2,
+ svt : 2,
+ __reserved_3 : 44;
+ };
+ __u64 high;
+ };
+};
+extern int get_irte(int irq, struct irte *entry);
+extern int modify_irte(int irq, struct irte *irte_modified);
+extern int alloc_irte(struct intel_iommu *iommu, int irq, u16 count);
+extern int set_irte_irq(int irq, struct intel_iommu *iommu, u16 index,
+ u16 sub_handle);
+extern int map_irq_to_irte_handle(int irq, u16 *sub_handle);
+extern int clear_irte_irq(int irq, struct intel_iommu *iommu, u16 index);
+extern int flush_irte(int irq);
+extern int free_irte(int irq);
+
+extern int irq_remapped(int irq);
+extern struct intel_iommu *map_dev_to_ir(struct pci_dev *dev);
+extern struct intel_iommu *map_ioapic_to_ir(int apic);
+#else
+#define irq_remapped(irq) (0)
+#define enable_intr_remapping(mode) (-1)
+#define intr_remapping_enabled (0)
+#endif
+
+#ifdef CONFIG_DMAR
extern const char *dmar_get_fault_reason(u8 fault_reason);
/* Can't use the common MSI interrupt functions
@@ -40,47 +130,30 @@
extern int dmar_set_interrupt(struct intel_iommu *iommu);
extern int arch_setup_dmar_msi(unsigned int irq);
-/* Intel IOMMU detection and initialization functions */
-extern void detect_intel_iommu(void);
-extern int intel_iommu_init(void);
-
-extern int dmar_table_init(void);
-extern int early_dmar_detect(void);
-
-extern struct list_head dmar_drhd_units;
+extern int iommu_detected, no_iommu;
extern struct list_head dmar_rmrr_units;
-
-struct dmar_drhd_unit {
- struct list_head list; /* list of drhd units */
- u64 reg_base_addr; /* register base address*/
- struct pci_dev **devices; /* target device array */
- int devices_cnt; /* target device count */
- u8 ignored:1; /* ignore drhd */
- u8 include_all:1;
- struct intel_iommu *iommu;
-};
-
struct dmar_rmrr_unit {
struct list_head list; /* list of rmrr units */
+ struct acpi_dmar_header *hdr; /* ACPI header */
u64 base_address; /* reserved base address*/
u64 end_address; /* reserved end address */
struct pci_dev **devices; /* target devices */
int devices_cnt; /* target device count */
};
-#define for_each_drhd_unit(drhd) \
- list_for_each_entry(drhd, &dmar_drhd_units, list)
#define for_each_rmrr_units(rmrr) \
list_for_each_entry(rmrr, &dmar_rmrr_units, list)
+/* Intel DMAR initialization functions */
+extern int intel_iommu_init(void);
+extern int dmar_disabled;
#else
-static inline void detect_intel_iommu(void)
-{
- return;
-}
static inline int intel_iommu_init(void)
{
+#ifdef CONFIG_INTR_REMAP
+ return dmar_dev_scope_init();
+#else
return -ENODEV;
+#endif
}
-
#endif /* !CONFIG_DMAR */
#endif /* __DMAR_H__ */
diff --git a/include/linux/dmi.h b/include/linux/dmi.h
index 2a063b6..e5084eb 100644
--- a/include/linux/dmi.h
+++ b/include/linux/dmi.h
@@ -2,29 +2,9 @@
#define __DMI_H__
#include <linux/list.h>
+#include <linux/mod_devicetable.h>
-enum dmi_field {
- DMI_NONE,
- DMI_BIOS_VENDOR,
- DMI_BIOS_VERSION,
- DMI_BIOS_DATE,
- DMI_SYS_VENDOR,
- DMI_PRODUCT_NAME,
- DMI_PRODUCT_VERSION,
- DMI_PRODUCT_SERIAL,
- DMI_PRODUCT_UUID,
- DMI_BOARD_VENDOR,
- DMI_BOARD_NAME,
- DMI_BOARD_VERSION,
- DMI_BOARD_SERIAL,
- DMI_BOARD_ASSET_TAG,
- DMI_CHASSIS_VENDOR,
- DMI_CHASSIS_TYPE,
- DMI_CHASSIS_VERSION,
- DMI_CHASSIS_SERIAL,
- DMI_CHASSIS_ASSET_TAG,
- DMI_STRING_MAX,
-};
+/* enum dmi_field is in mod_devicetable.h */
enum dmi_device_type {
DMI_DEV_TYPE_ANY = 0,
@@ -48,23 +28,6 @@
u16 handle;
};
-/*
- * DMI callbacks for problem boards
- */
-struct dmi_strmatch {
- u8 slot;
- char *substr;
-};
-
-struct dmi_system_id {
- int (*callback)(const struct dmi_system_id *);
- const char *ident;
- struct dmi_strmatch matches[4];
- void *driver_data;
-};
-
-#define DMI_MATCH(a, b) { a, b }
-
struct dmi_device {
struct list_head list;
int type;
diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h
index c8cbd90..6e4ace2 100644
--- a/include/linux/dvb/frontend.h
+++ b/include/linux/dvb/frontend.h
@@ -62,6 +62,7 @@
FE_CAN_HIERARCHY_AUTO = 0x100000,
FE_CAN_8VSB = 0x200000,
FE_CAN_16VSB = 0x400000,
+ FE_HAS_EXTENDED_CAPS = 0x800000, // We need more bitspace for newer APIs, indicate this.
FE_NEEDS_BENDING = 0x20000000, // not supported anymore, don't use (frontend requires frequency bending)
FE_CAN_RECOVER = 0x40000000, // frontend can recover from a cable unplug automatically
FE_CAN_MUTE_TS = 0x80000000 // frontend can stop spurious TS data output
@@ -147,7 +148,9 @@
FEC_6_7,
FEC_7_8,
FEC_8_9,
- FEC_AUTO
+ FEC_AUTO,
+ FEC_3_5,
+ FEC_9_10,
} fe_code_rate_t;
@@ -160,7 +163,10 @@
QAM_256,
QAM_AUTO,
VSB_8,
- VSB_16
+ VSB_16,
+ PSK_8,
+ APSK_16,
+ DQPSK,
} fe_modulation_t;
typedef enum fe_transmit_mode {
@@ -239,6 +245,106 @@
struct dvb_frontend_parameters parameters;
};
+/* S2API Commands */
+#define DTV_UNDEFINED 0
+#define DTV_TUNE 1
+#define DTV_CLEAR 2
+#define DTV_FREQUENCY 3
+#define DTV_MODULATION 4
+#define DTV_BANDWIDTH_HZ 5
+#define DTV_INVERSION 6
+#define DTV_DISEQC_MASTER 7
+#define DTV_SYMBOL_RATE 8
+#define DTV_INNER_FEC 9
+#define DTV_VOLTAGE 10
+#define DTV_TONE 11
+#define DTV_PILOT 12
+#define DTV_ROLLOFF 13
+#define DTV_DISEQC_SLAVE_REPLY 14
+
+/* Basic enumeration set for querying unlimited capabilities */
+#define DTV_FE_CAPABILITY_COUNT 15
+#define DTV_FE_CAPABILITY 16
+#define DTV_DELIVERY_SYSTEM 17
+
+#define DTV_API_VERSION 35
+#define DTV_API_VERSION 35
+#define DTV_CODE_RATE_HP 36
+#define DTV_CODE_RATE_LP 37
+#define DTV_GUARD_INTERVAL 38
+#define DTV_TRANSMISSION_MODE 39
+#define DTV_HIERARCHY 40
+
+#define DTV_MAX_COMMAND DTV_HIERARCHY
+
+typedef enum fe_pilot {
+ PILOT_ON,
+ PILOT_OFF,
+ PILOT_AUTO,
+} fe_pilot_t;
+
+typedef enum fe_rolloff {
+ ROLLOFF_35, /* Implied value in DVB-S, default for DVB-S2 */
+ ROLLOFF_20,
+ ROLLOFF_25,
+ ROLLOFF_AUTO,
+} fe_rolloff_t;
+
+typedef enum fe_delivery_system {
+ SYS_UNDEFINED,
+ SYS_DVBC_ANNEX_AC,
+ SYS_DVBC_ANNEX_B,
+ SYS_DVBT,
+ SYS_DVBS,
+ SYS_DVBS2,
+ SYS_DVBH,
+ SYS_ISDBT,
+ SYS_ISDBS,
+ SYS_ISDBC,
+ SYS_ATSC,
+ SYS_ATSCMH,
+ SYS_DMBTH,
+ SYS_CMMB,
+ SYS_DAB,
+} fe_delivery_system_t;
+
+struct dtv_cmds_h {
+ char *name; /* A display name for debugging purposes */
+
+ __u32 cmd; /* A unique ID */
+
+ /* Flags */
+ __u32 set:1; /* Either a set or get property */
+ __u32 buffer:1; /* Does this property use the buffer? */
+ __u32 reserved:30; /* Align */
+};
+
+struct dtv_property {
+ __u32 cmd;
+ __u32 reserved[3];
+ union {
+ __u32 data;
+ struct {
+ __u8 data[32];
+ __u32 len;
+ __u32 reserved1[3];
+ void *reserved2;
+ } buffer;
+ } u;
+ int result;
+} __attribute__ ((packed));
+
+/* num of properties cannot exceed DTV_IOCTL_MAX_MSGS per ioctl */
+#define DTV_IOCTL_MAX_MSGS 64
+
+struct dtv_properties {
+ __u32 num;
+ struct dtv_property *props;
+};
+
+#define FE_SET_PROPERTY _IOW('o', 82, struct dtv_properties)
+#define FE_GET_PROPERTY _IOR('o', 83, struct dtv_properties)
+
/**
* When set, this flag will disable any zigzagging or other "normal" tuning
diff --git a/include/linux/dvb/version.h b/include/linux/dvb/version.h
index 126e0c2..25b823b 100644
--- a/include/linux/dvb/version.h
+++ b/include/linux/dvb/version.h
@@ -23,7 +23,7 @@
#ifndef _DVBVERSION_H_
#define _DVBVERSION_H_
-#define DVB_API_VERSION 3
-#define DVB_API_VERSION_MINOR 2
+#define DVB_API_VERSION 5
+#define DVB_API_VERSION_MINOR 0
#endif /*_DVBVERSION_H_*/
diff --git a/include/linux/elf.h b/include/linux/elf.h
index edc3dac..0b61ca4 100644
--- a/include/linux/elf.h
+++ b/include/linux/elf.h
@@ -360,6 +360,7 @@
#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */
#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */
#define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */
+#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */
/* Note header in a PT_NOTE section */
diff --git a/include/linux/ext3_fs.h b/include/linux/ext3_fs.h
index 80171ee..8120fa1 100644
--- a/include/linux/ext3_fs.h
+++ b/include/linux/ext3_fs.h
@@ -837,6 +837,8 @@
extern void ext3_set_inode_flags(struct inode *);
extern void ext3_get_inode_flags(struct ext3_inode_info *);
extern void ext3_set_aops(struct inode *inode);
+extern int ext3_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
+ u64 start, u64 len);
/* ioctl.c */
extern int ext3_ioctl (struct inode *, struct file *, unsigned int,
diff --git a/include/linux/fiemap.h b/include/linux/fiemap.h
new file mode 100644
index 0000000..671decb
--- /dev/null
+++ b/include/linux/fiemap.h
@@ -0,0 +1,64 @@
+/*
+ * FS_IOC_FIEMAP ioctl infrastructure.
+ *
+ * Some portions copyright (C) 2007 Cluster File Systems, Inc
+ *
+ * Authors: Mark Fasheh <mfasheh@suse.com>
+ * Kalpak Shah <kalpak.shah@sun.com>
+ * Andreas Dilger <adilger@sun.com>
+ */
+
+#ifndef _LINUX_FIEMAP_H
+#define _LINUX_FIEMAP_H
+
+struct fiemap_extent {
+ __u64 fe_logical; /* logical offset in bytes for the start of
+ * the extent from the beginning of the file */
+ __u64 fe_physical; /* physical offset in bytes for the start
+ * of the extent from the beginning of the disk */
+ __u64 fe_length; /* length in bytes for this extent */
+ __u64 fe_reserved64[2];
+ __u32 fe_flags; /* FIEMAP_EXTENT_* flags for this extent */
+ __u32 fe_reserved[3];
+};
+
+struct fiemap {
+ __u64 fm_start; /* logical offset (inclusive) at
+ * which to start mapping (in) */
+ __u64 fm_length; /* logical length of mapping which
+ * userspace wants (in) */
+ __u32 fm_flags; /* FIEMAP_FLAG_* flags for request (in/out) */
+ __u32 fm_mapped_extents;/* number of extents that were mapped (out) */
+ __u32 fm_extent_count; /* size of fm_extents array (in) */
+ __u32 fm_reserved;
+ struct fiemap_extent fm_extents[0]; /* array of mapped extents (out) */
+};
+
+#define FIEMAP_MAX_OFFSET (~0ULL)
+
+#define FIEMAP_FLAG_SYNC 0x00000001 /* sync file data before map */
+#define FIEMAP_FLAG_XATTR 0x00000002 /* map extended attribute tree */
+
+#define FIEMAP_FLAGS_COMPAT (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR)
+
+#define FIEMAP_EXTENT_LAST 0x00000001 /* Last extent in file. */
+#define FIEMAP_EXTENT_UNKNOWN 0x00000002 /* Data location unknown. */
+#define FIEMAP_EXTENT_DELALLOC 0x00000004 /* Location still pending.
+ * Sets EXTENT_UNKNOWN. */
+#define FIEMAP_EXTENT_ENCODED 0x00000008 /* Data can not be read
+ * while fs is unmounted */
+#define FIEMAP_EXTENT_DATA_ENCRYPTED 0x00000080 /* Data is encrypted by fs.
+ * Sets EXTENT_NO_BYPASS. */
+#define FIEMAP_EXTENT_NOT_ALIGNED 0x00000100 /* Extent offsets may not be
+ * block aligned. */
+#define FIEMAP_EXTENT_DATA_INLINE 0x00000200 /* Data mixed with metadata.
+ * Sets EXTENT_NOT_ALIGNED.*/
+#define FIEMAP_EXTENT_DATA_TAIL 0x00000400 /* Multiple files in block.
+ * Sets EXTENT_NOT_ALIGNED.*/
+#define FIEMAP_EXTENT_UNWRITTEN 0x00000800 /* Space allocated, but
+ * no data (i.e. zero). */
+#define FIEMAP_EXTENT_MERGED 0x00001000 /* File does not natively
+ * support extents. Result
+ * merged for efficiency. */
+
+#endif /* _LINUX_FIEMAP_H */
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 32477e8..44e3cb2 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -234,6 +234,7 @@
#define FS_IOC_SETFLAGS _IOW('f', 2, long)
#define FS_IOC_GETVERSION _IOR('v', 1, long)
#define FS_IOC_SETVERSION _IOW('v', 2, long)
+#define FS_IOC_FIEMAP _IOWR('f', 11, struct fiemap)
#define FS_IOC32_GETFLAGS _IOR('f', 1, int)
#define FS_IOC32_SETFLAGS _IOW('f', 2, int)
#define FS_IOC32_GETVERSION _IOR('v', 1, int)
@@ -294,6 +295,7 @@
#include <linux/mutex.h>
#include <linux/capability.h>
#include <linux/semaphore.h>
+#include <linux/fiemap.h>
#include <asm/atomic.h>
#include <asm/byteorder.h>
@@ -1182,6 +1184,20 @@
extern int file_permission(struct file *, int);
/*
+ * VFS FS_IOC_FIEMAP helper definitions.
+ */
+struct fiemap_extent_info {
+ unsigned int fi_flags; /* Flags as passed from user */
+ unsigned int fi_extents_mapped; /* Number of mapped extents */
+ unsigned int fi_extents_max; /* Size of fiemap_extent array */
+ struct fiemap_extent *fi_extents_start; /* Start of fiemap_extent
+ * array */
+};
+int fiemap_fill_next_extent(struct fiemap_extent_info *info, u64 logical,
+ u64 phys, u64 len, u32 flags);
+int fiemap_check_flags(struct fiemap_extent_info *fieinfo, u32 fs_flags);
+
+/*
* File types
*
* NOTE! These match bits 12..15 of stat.st_mode
@@ -1290,6 +1306,8 @@
void (*truncate_range)(struct inode *, loff_t, loff_t);
long (*fallocate)(struct inode *inode, int mode, loff_t offset,
loff_t len);
+ int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
+ u64 len);
};
struct seq_file;
@@ -1987,6 +2005,9 @@
extern int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd,
unsigned long arg);
+extern int generic_block_fiemap(struct inode *inode,
+ struct fiemap_extent_info *fieinfo, u64 start,
+ u64 len, get_block_t *get_block);
extern void get_filesystem(struct file_system_type *fs);
extern void put_filesystem(struct file_system_type *fs);
diff --git a/include/linux/hpet.h b/include/linux/hpet.h
index 2dc29ce..79f63a2 100644
--- a/include/linux/hpet.h
+++ b/include/linux/hpet.h
@@ -37,6 +37,7 @@
#define hpet_compare _u1._hpet_compare
#define HPET_MAX_TIMERS (32)
+#define HPET_MAX_IRQ (32)
/*
* HPET general capabilities register
@@ -64,7 +65,7 @@
*/
#define Tn_INT_ROUTE_CAP_MASK (0xffffffff00000000ULL)
-#define Tn_INI_ROUTE_CAP_SHIFT (32UL)
+#define Tn_INT_ROUTE_CAP_SHIFT (32UL)
#define Tn_FSB_INT_DELCAP_MASK (0x8000UL)
#define Tn_FSB_INT_DELCAP_SHIFT (15)
#define Tn_FSB_EN_CNF_MASK (0x4000UL)
@@ -91,23 +92,14 @@
* exported interfaces
*/
-struct hpet_task {
- void (*ht_func) (void *);
- void *ht_data;
- void *ht_opaque;
-};
-
struct hpet_data {
unsigned long hd_phys_address;
void __iomem *hd_address;
unsigned short hd_nirqs;
- unsigned short hd_flags;
unsigned int hd_state; /* timer allocated */
unsigned int hd_irq[HPET_MAX_TIMERS];
};
-#define HPET_DATA_PLATFORM 0x0001 /* platform call to hpet_alloc */
-
static inline void hpet_reserve_timer(struct hpet_data *hd, int timer)
{
hd->hd_state |= (1 << timer);
@@ -125,7 +117,7 @@
unsigned short hi_timer;
};
-#define HPET_INFO_PERIODIC 0x0001 /* timer is periodic */
+#define HPET_INFO_PERIODIC 0x0010 /* periodic-capable comparator */
#define HPET_IE_ON _IO('h', 0x01) /* interrupt on */
#define HPET_IE_OFF _IO('h', 0x02) /* interrupt off */
diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h
index bf34c5f..493435b 100644
--- a/include/linux/i2c-id.h
+++ b/include/linux/i2c-id.h
@@ -41,7 +41,6 @@
#define I2C_DRIVERID_SAA7110 22 /* video decoder */
#define I2C_DRIVERID_SAA5249 24 /* SAA5249 and compatibles */
#define I2C_DRIVERID_PCF8583 25 /* real time clock */
-#define I2C_DRIVERID_SAB3036 26 /* SAB3036 tuner */
#define I2C_DRIVERID_TDA7432 27 /* Stereo sound processor */
#define I2C_DRIVERID_TVMIXER 28 /* Mixer driver for tv cards */
#define I2C_DRIVERID_TVAUDIO 29 /* Generic TV sound driver */
diff --git a/include/linux/ide.h b/include/linux/ide.h
index 6514db8..c47e371 100644
--- a/include/linux/ide.h
+++ b/include/linux/ide.h
@@ -8,7 +8,7 @@
#include <linux/init.h>
#include <linux/ioport.h>
-#include <linux/hdreg.h>
+#include <linux/ata.h>
#include <linux/blkdev.h>
#include <linux/proc_fs.h>
#include <linux/interrupt.h>
@@ -17,6 +17,7 @@
#include <linux/device.h>
#include <linux/pci.h>
#include <linux/completion.h>
+#include <linux/pm.h>
#ifdef CONFIG_BLK_DEV_IDEACPI
#include <acpi/acpi.h>
#endif
@@ -47,12 +48,6 @@
#define ERROR_RESET 3 /* Reset controller every 4th retry */
#define ERROR_RECAL 1 /* Recalibrate every 2nd retry */
-/*
- * state flags
- */
-
-#define DMA_PIO_RETRY 1 /* retrying in PIO */
-
#define HWIF(drive) ((ide_hwif_t *)((drive)->hwif))
#define HWGROUP(drive) ((ide_hwgroup_t *)(HWIF(drive)->hwgroup))
@@ -87,12 +82,13 @@
};
#define OK_STAT(stat,good,bad) (((stat)&((good)|(bad)))==(good))
-#define BAD_R_STAT (BUSY_STAT | ERR_STAT)
-#define BAD_W_STAT (BAD_R_STAT | WRERR_STAT)
-#define BAD_STAT (BAD_R_STAT | DRQ_STAT)
-#define DRIVE_READY (READY_STAT | SEEK_STAT)
-#define BAD_CRC (ABRT_ERR | ICRC_ERR)
+#define BAD_R_STAT (ATA_BUSY | ATA_ERR)
+#define BAD_W_STAT (BAD_R_STAT | ATA_DF)
+#define BAD_STAT (BAD_R_STAT | ATA_DRQ)
+#define DRIVE_READY (ATA_DRDY | ATA_DSC)
+
+#define BAD_CRC (ATA_ABORTED | ATA_ICRC)
#define SATA_NR_PORTS (3) /* 16 possible ?? */
@@ -125,24 +121,43 @@
#define PARTN_BITS 6 /* number of minor dev bits for partitions */
#define MAX_DRIVES 2 /* per interface; 2 assumed by lots of code */
#define SECTOR_SIZE 512
-#define SECTOR_WORDS (SECTOR_SIZE / 4) /* number of 32bit words per sector */
+
#define IDE_LARGE_SEEK(b1,b2,t) (((b1) > (b2) + (t)) || ((b2) > (b1) + (t)))
/*
* Timeouts for various operations:
*/
-#define WAIT_DRQ (HZ/10) /* 100msec - spec allows up to 20ms */
-#define WAIT_READY (5*HZ) /* 5sec - some laptops are very slow */
-#define WAIT_PIDENTIFY (10*HZ) /* 10sec - should be less than 3ms (?), if all ATAPI CD is closed at boot */
-#define WAIT_WORSTCASE (30*HZ) /* 30sec - worst case when spinning up */
-#define WAIT_CMD (10*HZ) /* 10sec - maximum wait for an IRQ to happen */
-#define WAIT_MIN_SLEEP (2*HZ/100) /* 20msec - minimum sleep time */
+enum {
+ /* spec allows up to 20ms */
+ WAIT_DRQ = HZ / 10, /* 100ms */
+ /* some laptops are very slow */
+ WAIT_READY = 5 * HZ, /* 5s */
+ /* should be less than 3ms (?), if all ATAPI CD is closed at boot */
+ WAIT_PIDENTIFY = 10 * HZ, /* 10s */
+ /* worst case when spinning up */
+ WAIT_WORSTCASE = 30 * HZ, /* 30s */
+ /* maximum wait for an IRQ to happen */
+ WAIT_CMD = 10 * HZ, /* 10s */
+ /* Some drives require a longer IRQ timeout. */
+ WAIT_FLOPPY_CMD = 50 * HZ, /* 50s */
+ /*
+ * Some drives (for example, Seagate STT3401A Travan) require a very
+ * long timeout, because they don't return an interrupt or clear their
+ * BSY bit until after the command completes (even retension commands).
+ */
+ WAIT_TAPE_CMD = 900 * HZ, /* 900s */
+ /* minimum sleep time */
+ WAIT_MIN_SLEEP = HZ / 50, /* 20ms */
+};
/*
* Op codes for special requests to be handled by ide_special_rq().
* Values should be in the range of 0x20 to 0x3f.
*/
#define REQ_DRIVE_RESET 0x20
+#define REQ_DEVSET_EXEC 0x21
+#define REQ_PARK_HEADS 0x22
+#define REQ_UNPARK_HEADS 0x23
/*
* Check for an interrupt and acknowledge the interrupt status
@@ -249,8 +264,6 @@
* set_geometry : respecify drive geometry
* recalibrate : seek to cyl 0
* set_multmode : set multmode count
- * set_tune : tune interface for drive
- * serviced : service command
* reserved : unused
*/
typedef union {
@@ -259,43 +272,11 @@
unsigned set_geometry : 1;
unsigned recalibrate : 1;
unsigned set_multmode : 1;
- unsigned set_tune : 1;
- unsigned serviced : 1;
- unsigned reserved : 3;
+ unsigned reserved : 5;
} b;
} special_t;
/*
- * ATA-IDE Select Register, aka Device-Head
- *
- * head : always zeros here
- * unit : drive select number: 0/1
- * bit5 : always 1
- * lba : using LBA instead of CHS
- * bit7 : always 1
- */
-typedef union {
- unsigned all : 8;
- struct {
-#if defined(__LITTLE_ENDIAN_BITFIELD)
- unsigned head : 4;
- unsigned unit : 1;
- unsigned bit5 : 1;
- unsigned lba : 1;
- unsigned bit7 : 1;
-#elif defined(__BIG_ENDIAN_BITFIELD)
- unsigned bit7 : 1;
- unsigned lba : 1;
- unsigned bit5 : 1;
- unsigned unit : 1;
- unsigned head : 4;
-#else
-#error "Please fix <asm/byteorder.h>"
-#endif
- } b;
-} select_t, ata_select_t;
-
-/*
* Status returned from various ide_ functions
*/
typedef enum {
@@ -303,635 +284,6 @@
ide_started, /* a drive operation was started, handler was set */
} ide_startstop_t;
-struct ide_driver_s;
-struct ide_settings_s;
-
-#ifdef CONFIG_BLK_DEV_IDEACPI
-struct ide_acpi_drive_link;
-struct ide_acpi_hwif_link;
-#endif
-
-/* ATAPI device flags */
-enum {
- IDE_AFLAG_DRQ_INTERRUPT = (1 << 0),
- IDE_AFLAG_MEDIA_CHANGED = (1 << 1),
-
- /* ide-cd */
- /* Drive cannot lock the door. */
- IDE_AFLAG_NO_DOORLOCK = (1 << 2),
- /* Drive cannot eject the disc. */
- IDE_AFLAG_NO_EJECT = (1 << 3),
- /* Drive is a pre ATAPI 1.2 drive. */
- IDE_AFLAG_PRE_ATAPI12 = (1 << 4),
- /* TOC addresses are in BCD. */
- IDE_AFLAG_TOCADDR_AS_BCD = (1 << 5),
- /* TOC track numbers are in BCD. */
- IDE_AFLAG_TOCTRACKS_AS_BCD = (1 << 6),
- /*
- * Drive does not provide data in multiples of SECTOR_SIZE
- * when more than one interrupt is needed.
- */
- IDE_AFLAG_LIMIT_NFRAMES = (1 << 7),
- /* Seeking in progress. */
- IDE_AFLAG_SEEKING = (1 << 8),
- /* Saved TOC information is current. */
- IDE_AFLAG_TOC_VALID = (1 << 9),
- /* We think that the drive door is locked. */
- IDE_AFLAG_DOOR_LOCKED = (1 << 10),
- /* SET_CD_SPEED command is unsupported. */
- IDE_AFLAG_NO_SPEED_SELECT = (1 << 11),
- IDE_AFLAG_VERTOS_300_SSD = (1 << 12),
- IDE_AFLAG_VERTOS_600_ESD = (1 << 13),
- IDE_AFLAG_SANYO_3CD = (1 << 14),
- IDE_AFLAG_FULL_CAPS_PAGE = (1 << 15),
- IDE_AFLAG_PLAY_AUDIO_OK = (1 << 16),
- IDE_AFLAG_LE_SPEED_FIELDS = (1 << 17),
-
- /* ide-floppy */
- /* Format in progress */
- IDE_AFLAG_FORMAT_IN_PROGRESS = (1 << 18),
- /* Avoid commands not supported in Clik drive */
- IDE_AFLAG_CLIK_DRIVE = (1 << 19),
- /* Requires BH algorithm for packets */
- IDE_AFLAG_ZIP_DRIVE = (1 << 20),
-
- /* ide-tape */
- IDE_AFLAG_IGNORE_DSC = (1 << 21),
- /* 0 When the tape position is unknown */
- IDE_AFLAG_ADDRESS_VALID = (1 << 22),
- /* Device already opened */
- IDE_AFLAG_BUSY = (1 << 23),
- /* Attempt to auto-detect the current user block size */
- IDE_AFLAG_DETECT_BS = (1 << 24),
- /* Currently on a filemark */
- IDE_AFLAG_FILEMARK = (1 << 25),
- /* 0 = no tape is loaded, so we don't rewind after ejecting */
- IDE_AFLAG_MEDIUM_PRESENT = (1 << 26),
-
- IDE_AFLAG_NO_AUTOCLOSE = (1 << 27),
-};
-
-struct ide_drive_s {
- char name[4]; /* drive name, such as "hda" */
- char driver_req[10]; /* requests specific driver */
-
- struct request_queue *queue; /* request queue */
-
- struct request *rq; /* current request */
- struct ide_drive_s *next; /* circular list of hwgroup drives */
- void *driver_data; /* extra driver data */
- struct hd_driveid *id; /* drive model identification info */
-#ifdef CONFIG_IDE_PROC_FS
- struct proc_dir_entry *proc; /* /proc/ide/ directory entry */
- struct ide_settings_s *settings;/* /proc/ide/ drive settings */
-#endif
- struct hwif_s *hwif; /* actually (ide_hwif_t *) */
-
- unsigned long sleep; /* sleep until this time */
- unsigned long service_start; /* time we started last request */
- unsigned long service_time; /* service time of last request */
- unsigned long timeout; /* max time to wait for irq */
-
- special_t special; /* special action flags */
- select_t select; /* basic drive/head select reg value */
-
- u8 keep_settings; /* restore settings after drive reset */
- u8 using_dma; /* disk is using dma for read/write */
- u8 retry_pio; /* retrying dma capable host in pio */
- u8 state; /* retry state */
- u8 waiting_for_dma; /* dma currently in progress */
- u8 unmask; /* okay to unmask other irqs */
- u8 noflush; /* don't attempt flushes */
- u8 dsc_overlap; /* DSC overlap */
- u8 nice1; /* give potential excess bandwidth */
-
- unsigned present : 1; /* drive is physically present */
- unsigned dead : 1; /* device ejected hint */
- unsigned id_read : 1; /* 1=id read from disk 0 = synthetic */
- unsigned noprobe : 1; /* from: hdx=noprobe */
- unsigned removable : 1; /* 1 if need to do check_media_change */
- unsigned attach : 1; /* needed for removable devices */
- unsigned forced_geom : 1; /* 1 if hdx=c,h,s was given at boot */
- unsigned no_unmask : 1; /* disallow setting unmask bit */
- unsigned no_io_32bit : 1; /* disallow enabling 32bit I/O */
- unsigned atapi_overlap : 1; /* ATAPI overlap (not supported) */
- unsigned doorlocking : 1; /* for removable only: door lock/unlock works */
- unsigned nodma : 1; /* disallow DMA */
- unsigned remap_0_to_1 : 1; /* 0=noremap, 1=remap 0->1 (for EZDrive) */
- unsigned blocked : 1; /* 1=powermanagment told us not to do anything, so sleep nicely */
- unsigned scsi : 1; /* 0=default, 1=ide-scsi emulation */
- unsigned sleeping : 1; /* 1=sleeping & sleep field valid */
- unsigned post_reset : 1;
- unsigned udma33_warned : 1;
-
- u8 addressing; /* 0=28-bit, 1=48-bit, 2=48-bit doing 28-bit */
- u8 quirk_list; /* considered quirky, set for a specific host */
- u8 init_speed; /* transfer rate set at boot */
- u8 current_speed; /* current transfer rate set */
- u8 desired_speed; /* desired transfer rate set */
- u8 dn; /* now wide spread use */
- u8 wcache; /* status of write cache */
- u8 acoustic; /* acoustic management */
- u8 media; /* disk, cdrom, tape, floppy, ... */
- u8 ready_stat; /* min status value for drive ready */
- u8 mult_count; /* current multiple sector setting */
- u8 mult_req; /* requested multiple sector setting */
- u8 tune_req; /* requested drive tuning setting */
- u8 io_32bit; /* 0=16-bit, 1=32-bit, 2/3=32bit+sync */
- u8 bad_wstat; /* used for ignoring WRERR_STAT */
- u8 nowerr; /* used for ignoring WRERR_STAT */
- u8 sect0; /* offset of first sector for DM6:DDO */
- u8 head; /* "real" number of heads */
- u8 sect; /* "real" sectors per track */
- u8 bios_head; /* BIOS/fdisk/LILO number of heads */
- u8 bios_sect; /* BIOS/fdisk/LILO sectors per track */
-
- unsigned int bios_cyl; /* BIOS/fdisk/LILO number of cyls */
- unsigned int cyl; /* "real" number of cyls */
- unsigned int drive_data; /* used by set_pio_mode/selectproc */
- unsigned int failures; /* current failure count */
- unsigned int max_failures; /* maximum allowed failure count */
- u64 probed_capacity;/* initial reported media capacity (ide-cd only currently) */
-
- u64 capacity64; /* total number of sectors */
-
- int lun; /* logical unit */
- int crc_count; /* crc counter to reduce drive speed */
-#ifdef CONFIG_BLK_DEV_IDEACPI
- struct ide_acpi_drive_link *acpidata;
-#endif
- struct list_head list;
- struct device gendev;
- struct completion gendev_rel_comp; /* to deal with device release() */
-
- /* callback for packet commands */
- void (*pc_callback)(struct ide_drive_s *);
-
- unsigned long atapi_flags;
-};
-
-typedef struct ide_drive_s ide_drive_t;
-
-#define to_ide_device(dev)container_of(dev, ide_drive_t, gendev)
-
-#define IDE_CHIPSET_PCI_MASK \
- ((1<<ide_pci)|(1<<ide_cmd646)|(1<<ide_ali14xx))
-#define IDE_CHIPSET_IS_PCI(c) ((IDE_CHIPSET_PCI_MASK >> (c)) & 1)
-
-struct ide_task_s;
-struct ide_port_info;
-
-struct ide_tp_ops {
- void (*exec_command)(struct hwif_s *, u8);
- u8 (*read_status)(struct hwif_s *);
- u8 (*read_altstatus)(struct hwif_s *);
- u8 (*read_sff_dma_status)(struct hwif_s *);
-
- void (*set_irq)(struct hwif_s *, int);
-
- void (*tf_load)(ide_drive_t *, struct ide_task_s *);
- void (*tf_read)(ide_drive_t *, struct ide_task_s *);
-
- void (*input_data)(ide_drive_t *, struct request *, void *,
- unsigned int);
- void (*output_data)(ide_drive_t *, struct request *, void *,
- unsigned int);
-};
-
-extern const struct ide_tp_ops default_tp_ops;
-
-/**
- * struct ide_port_ops - IDE port operations
- *
- * @init_dev: host specific initialization of a device
- * @set_pio_mode: routine to program host for PIO mode
- * @set_dma_mode: routine to program host for DMA mode
- * @selectproc: tweaks hardware to select drive
- * @reset_poll: chipset polling based on hba specifics
- * @pre_reset: chipset specific changes to default for device-hba resets
- * @resetproc: routine to reset controller after a disk reset
- * @maskproc: special host masking for drive selection
- * @quirkproc: check host's drive quirk list
- *
- * @mdma_filter: filter MDMA modes
- * @udma_filter: filter UDMA modes
- *
- * @cable_detect: detect cable type
- */
-struct ide_port_ops {
- void (*init_dev)(ide_drive_t *);
- void (*set_pio_mode)(ide_drive_t *, const u8);
- void (*set_dma_mode)(ide_drive_t *, const u8);
- void (*selectproc)(ide_drive_t *);
- int (*reset_poll)(ide_drive_t *);
- void (*pre_reset)(ide_drive_t *);
- void (*resetproc)(ide_drive_t *);
- void (*maskproc)(ide_drive_t *, int);
- void (*quirkproc)(ide_drive_t *);
-
- u8 (*mdma_filter)(ide_drive_t *);
- u8 (*udma_filter)(ide_drive_t *);
-
- u8 (*cable_detect)(struct hwif_s *);
-};
-
-struct ide_dma_ops {
- void (*dma_host_set)(struct ide_drive_s *, int);
- int (*dma_setup)(struct ide_drive_s *);
- void (*dma_exec_cmd)(struct ide_drive_s *, u8);
- void (*dma_start)(struct ide_drive_s *);
- int (*dma_end)(struct ide_drive_s *);
- int (*dma_test_irq)(struct ide_drive_s *);
- void (*dma_lost_irq)(struct ide_drive_s *);
- void (*dma_timeout)(struct ide_drive_s *);
-};
-
-struct ide_host;
-
-typedef struct hwif_s {
- struct hwif_s *next; /* for linked-list in ide_hwgroup_t */
- struct hwif_s *mate; /* other hwif from same PCI chip */
- struct hwgroup_s *hwgroup; /* actually (ide_hwgroup_t *) */
- struct proc_dir_entry *proc; /* /proc/ide/ directory entry */
-
- struct ide_host *host;
-
- char name[6]; /* name of interface, eg. "ide0" */
-
- struct ide_io_ports io_ports;
-
- unsigned long sata_scr[SATA_NR_PORTS];
-
- ide_drive_t drives[MAX_DRIVES]; /* drive info */
-
- u8 major; /* our major number */
- u8 index; /* 0 for ide0; 1 for ide1; ... */
- u8 channel; /* for dual-port chips: 0=primary, 1=secondary */
- u8 bus_state; /* power state of the IDE bus */
-
- u32 host_flags;
-
- u8 pio_mask;
-
- u8 ultra_mask;
- u8 mwdma_mask;
- u8 swdma_mask;
-
- u8 cbl; /* cable type */
-
- hwif_chipset_t chipset; /* sub-module for tuning.. */
-
- struct device *dev;
-
- ide_ack_intr_t *ack_intr;
-
- void (*rw_disk)(ide_drive_t *, struct request *);
-
- const struct ide_tp_ops *tp_ops;
- const struct ide_port_ops *port_ops;
- const struct ide_dma_ops *dma_ops;
-
- void (*ide_dma_clear_irq)(ide_drive_t *drive);
-
- /* dma physical region descriptor table (cpu view) */
- unsigned int *dmatable_cpu;
- /* dma physical region descriptor table (dma view) */
- dma_addr_t dmatable_dma;
- /* Scatter-gather list used to build the above */
- struct scatterlist *sg_table;
- int sg_max_nents; /* Maximum number of entries in it */
- int sg_nents; /* Current number of entries in it */
- int sg_dma_direction; /* dma transfer direction */
-
- /* data phase of the active command (currently only valid for PIO/DMA) */
- int data_phase;
-
- unsigned int nsect;
- unsigned int nleft;
- struct scatterlist *cursg;
- unsigned int cursg_ofs;
-
- int rqsize; /* max sectors per request */
- int irq; /* our irq number */
-
- unsigned long dma_base; /* base addr for dma ports */
-
- unsigned long config_data; /* for use by chipset-specific code */
- unsigned long select_data; /* for use by chipset-specific code */
-
- unsigned long extra_base; /* extra addr for dma ports */
- unsigned extra_ports; /* number of extra dma ports */
-
- unsigned present : 1; /* this interface exists */
- unsigned serialized : 1; /* serialized all channel operation */
- unsigned sharing_irq: 1; /* 1 = sharing irq with another hwif */
- unsigned sg_mapped : 1; /* sg_table and sg_nents are ready */
-
- struct device gendev;
- struct device *portdev;
-
- struct completion gendev_rel_comp; /* To deal with device release() */
-
- void *hwif_data; /* extra hwif data */
-
- unsigned dma;
-
-#ifdef CONFIG_BLK_DEV_IDEACPI
- struct ide_acpi_hwif_link *acpidata;
-#endif
-} ____cacheline_internodealigned_in_smp ide_hwif_t;
-
-struct ide_host {
- ide_hwif_t *ports[MAX_HWIFS];
- unsigned int n_ports;
- struct device *dev[2];
- unsigned long host_flags;
- void *host_priv;
-};
-
-/*
- * internal ide interrupt handler type
- */
-typedef ide_startstop_t (ide_handler_t)(ide_drive_t *);
-typedef int (ide_expiry_t)(ide_drive_t *);
-
-/* used by ide-cd, ide-floppy, etc. */
-typedef void (xfer_func_t)(ide_drive_t *, struct request *rq, void *, unsigned);
-
-typedef struct hwgroup_s {
- /* irq handler, if active */
- ide_startstop_t (*handler)(ide_drive_t *);
-
- /* BOOL: protects all fields below */
- volatile int busy;
- /* BOOL: wake us up on timer expiry */
- unsigned int sleeping : 1;
- /* BOOL: polling active & poll_timeout field valid */
- unsigned int polling : 1;
-
- /* current drive */
- ide_drive_t *drive;
- /* ptr to current hwif in linked-list */
- ide_hwif_t *hwif;
-
- /* current request */
- struct request *rq;
-
- /* failsafe timer */
- struct timer_list timer;
- /* timeout value during long polls */
- unsigned long poll_timeout;
- /* queried upon timeouts */
- int (*expiry)(ide_drive_t *);
-
- int req_gen;
- int req_gen_timer;
-} ide_hwgroup_t;
-
-typedef struct ide_driver_s ide_driver_t;
-
-extern struct mutex ide_setting_mtx;
-
-int set_io_32bit(ide_drive_t *, int);
-int set_pio_mode(ide_drive_t *, int);
-int set_using_dma(ide_drive_t *, int);
-
-/* ATAPI packet command flags */
-enum {
- /* set when an error is considered normal - no retry (ide-tape) */
- PC_FLAG_ABORT = (1 << 0),
- PC_FLAG_SUPPRESS_ERROR = (1 << 1),
- PC_FLAG_WAIT_FOR_DSC = (1 << 2),
- PC_FLAG_DMA_OK = (1 << 3),
- PC_FLAG_DMA_IN_PROGRESS = (1 << 4),
- PC_FLAG_DMA_ERROR = (1 << 5),
- PC_FLAG_WRITING = (1 << 6),
- /* command timed out */
- PC_FLAG_TIMEDOUT = (1 << 7),
-};
-
-struct ide_atapi_pc {
- /* actual packet bytes */
- u8 c[12];
- /* incremented on each retry */
- int retries;
- int error;
-
- /* bytes to transfer */
- int req_xfer;
- /* bytes actually transferred */
- int xferred;
-
- /* data buffer */
- u8 *buf;
- /* current buffer position */
- u8 *cur_pos;
- int buf_size;
- /* missing/available data on the current buffer */
- int b_count;
-
- /* the corresponding request */
- struct request *rq;
-
- unsigned long flags;
-
- /*
- * those are more or less driver-specific and some of them are subject
- * to change/removal later.
- */
- u8 pc_buf[256];
-
- /* idetape only */
- struct idetape_bh *bh;
- char *b_data;
-
- /* idescsi only for now */
- struct scatterlist *sg;
- unsigned int sg_cnt;
-
- struct scsi_cmnd *scsi_cmd;
- void (*done) (struct scsi_cmnd *);
-
- unsigned long timeout;
-};
-
-#ifdef CONFIG_IDE_PROC_FS
-/*
- * configurable drive settings
- */
-
-#define TYPE_INT 0
-#define TYPE_BYTE 1
-#define TYPE_SHORT 2
-
-#define SETTING_READ (1 << 0)
-#define SETTING_WRITE (1 << 1)
-#define SETTING_RW (SETTING_READ | SETTING_WRITE)
-
-typedef int (ide_procset_t)(ide_drive_t *, int);
-typedef struct ide_settings_s {
- char *name;
- int rw;
- int data_type;
- int min;
- int max;
- int mul_factor;
- int div_factor;
- void *data;
- ide_procset_t *set;
- int auto_remove;
- struct ide_settings_s *next;
-} ide_settings_t;
-
-int ide_add_setting(ide_drive_t *, const char *, int, int, int, int, int, int, void *, ide_procset_t *set);
-
-/*
- * /proc/ide interface
- */
-typedef struct {
- const char *name;
- mode_t mode;
- read_proc_t *read_proc;
- write_proc_t *write_proc;
-} ide_proc_entry_t;
-
-void proc_ide_create(void);
-void proc_ide_destroy(void);
-void ide_proc_register_port(ide_hwif_t *);
-void ide_proc_port_register_devices(ide_hwif_t *);
-void ide_proc_unregister_device(ide_drive_t *);
-void ide_proc_unregister_port(ide_hwif_t *);
-void ide_proc_register_driver(ide_drive_t *, ide_driver_t *);
-void ide_proc_unregister_driver(ide_drive_t *, ide_driver_t *);
-
-void ide_add_generic_settings(ide_drive_t *);
-
-read_proc_t proc_ide_read_capacity;
-read_proc_t proc_ide_read_geometry;
-
-/*
- * Standard exit stuff:
- */
-#define PROC_IDE_READ_RETURN(page,start,off,count,eof,len) \
-{ \
- len -= off; \
- if (len < count) { \
- *eof = 1; \
- if (len <= 0) \
- return 0; \
- } else \
- len = count; \
- *start = page + off; \
- return len; \
-}
-#else
-static inline void proc_ide_create(void) { ; }
-static inline void proc_ide_destroy(void) { ; }
-static inline void ide_proc_register_port(ide_hwif_t *hwif) { ; }
-static inline void ide_proc_port_register_devices(ide_hwif_t *hwif) { ; }
-static inline void ide_proc_unregister_device(ide_drive_t *drive) { ; }
-static inline void ide_proc_unregister_port(ide_hwif_t *hwif) { ; }
-static inline void ide_proc_register_driver(ide_drive_t *drive, ide_driver_t *driver) { ; }
-static inline void ide_proc_unregister_driver(ide_drive_t *drive, ide_driver_t *driver) { ; }
-static inline void ide_add_generic_settings(ide_drive_t *drive) { ; }
-#define PROC_IDE_READ_RETURN(page,start,off,count,eof,len) return 0;
-#endif
-
-/*
- * Power Management step value (rq->pm->pm_step).
- *
- * The step value starts at 0 (ide_pm_state_start_suspend) for a
- * suspend operation or 1000 (ide_pm_state_start_resume) for a
- * resume operation.
- *
- * For each step, the core calls the subdriver start_power_step() first.
- * This can return:
- * - ide_stopped : In this case, the core calls us back again unless
- * step have been set to ide_power_state_completed.
- * - ide_started : In this case, the channel is left busy until an
- * async event (interrupt) occurs.
- * Typically, start_power_step() will issue a taskfile request with
- * do_rw_taskfile().
- *
- * Upon reception of the interrupt, the core will call complete_power_step()
- * with the error code if any. This routine should update the step value
- * and return. It should not start a new request. The core will call
- * start_power_step for the new step value, unless step have been set to
- * ide_power_state_completed.
- *
- * Subdrivers are expected to define their own additional power
- * steps from 1..999 for suspend and from 1001..1999 for resume,
- * other values are reserved for future use.
- */
-
-enum {
- ide_pm_state_completed = -1,
- ide_pm_state_start_suspend = 0,
- ide_pm_state_start_resume = 1000,
-};
-
-/*
- * Subdrivers support.
- *
- * The gendriver.owner field should be set to the module owner of this driver.
- * The gendriver.name field should be set to the name of this driver
- */
-struct ide_driver_s {
- const char *version;
- u8 media;
- unsigned supports_dsc_overlap : 1;
- ide_startstop_t (*do_request)(ide_drive_t *, struct request *, sector_t);
- int (*end_request)(ide_drive_t *, int, int);
- ide_startstop_t (*error)(ide_drive_t *, struct request *rq, u8, u8);
- struct device_driver gen_driver;
- int (*probe)(ide_drive_t *);
- void (*remove)(ide_drive_t *);
- void (*resume)(ide_drive_t *);
- void (*shutdown)(ide_drive_t *);
-#ifdef CONFIG_IDE_PROC_FS
- ide_proc_entry_t *proc;
-#endif
-};
-
-#define to_ide_driver(drv) container_of(drv, ide_driver_t, gen_driver)
-
-int ide_device_get(ide_drive_t *);
-void ide_device_put(ide_drive_t *);
-
-int generic_ide_ioctl(ide_drive_t *, struct file *, struct block_device *, unsigned, unsigned long);
-
-extern int ide_vlb_clk;
-extern int ide_pci_clk;
-
-extern int ide_end_request (ide_drive_t *drive, int uptodate, int nrsecs);
-int ide_end_dequeued_request(ide_drive_t *drive, struct request *rq,
- int uptodate, int nr_sectors);
-
-extern void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry);
-
-void ide_execute_command(ide_drive_t *, u8, ide_handler_t *, unsigned int,
- ide_expiry_t *);
-
-void ide_execute_pkt_cmd(ide_drive_t *);
-
-void ide_pad_transfer(ide_drive_t *, int, int);
-
-ide_startstop_t __ide_error(ide_drive_t *, struct request *, u8, u8);
-
-ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, byte stat);
-
-extern void ide_fix_driveid(struct hd_driveid *);
-
-extern void ide_fixstring(u8 *, const int, const int);
-
-int ide_wait_stat(ide_startstop_t *, ide_drive_t *, u8, u8, unsigned long);
-
-extern ide_startstop_t ide_do_reset (ide_drive_t *);
-
-extern void ide_do_drive_cmd(ide_drive_t *, struct request *);
-
-extern void ide_end_drive_cmd(ide_drive_t *, u8, u8);
-
enum {
IDE_TFLAG_LBA48 = (1 << 0),
IDE_TFLAG_FLAGGED = (1 << 2),
@@ -1036,6 +388,800 @@
void *special; /* valid_t generally */
} ide_task_t;
+/* ATAPI packet command flags */
+enum {
+ /* set when an error is considered normal - no retry (ide-tape) */
+ PC_FLAG_ABORT = (1 << 0),
+ PC_FLAG_SUPPRESS_ERROR = (1 << 1),
+ PC_FLAG_WAIT_FOR_DSC = (1 << 2),
+ PC_FLAG_DMA_OK = (1 << 3),
+ PC_FLAG_DMA_IN_PROGRESS = (1 << 4),
+ PC_FLAG_DMA_ERROR = (1 << 5),
+ PC_FLAG_WRITING = (1 << 6),
+ /* command timed out */
+ PC_FLAG_TIMEDOUT = (1 << 7),
+};
+
+/*
+ * With each packet command, we allocate a buffer of IDE_PC_BUFFER_SIZE bytes.
+ * This is used for several packet commands (not for READ/WRITE commands).
+ */
+#define IDE_PC_BUFFER_SIZE 256
+
+struct ide_atapi_pc {
+ /* actual packet bytes */
+ u8 c[12];
+ /* incremented on each retry */
+ int retries;
+ int error;
+
+ /* bytes to transfer */
+ int req_xfer;
+ /* bytes actually transferred */
+ int xferred;
+
+ /* data buffer */
+ u8 *buf;
+ /* current buffer position */
+ u8 *cur_pos;
+ int buf_size;
+ /* missing/available data on the current buffer */
+ int b_count;
+
+ /* the corresponding request */
+ struct request *rq;
+
+ unsigned long flags;
+
+ /*
+ * those are more or less driver-specific and some of them are subject
+ * to change/removal later.
+ */
+ u8 pc_buf[IDE_PC_BUFFER_SIZE];
+
+ /* idetape only */
+ struct idetape_bh *bh;
+ char *b_data;
+
+ /* idescsi only for now */
+ struct scatterlist *sg;
+ unsigned int sg_cnt;
+
+ struct scsi_cmnd *scsi_cmd;
+ void (*done) (struct scsi_cmnd *);
+
+ unsigned long timeout;
+};
+
+struct ide_devset;
+struct ide_driver_s;
+
+#ifdef CONFIG_BLK_DEV_IDEACPI
+struct ide_acpi_drive_link;
+struct ide_acpi_hwif_link;
+#endif
+
+/* ATAPI device flags */
+enum {
+ IDE_AFLAG_DRQ_INTERRUPT = (1 << 0),
+ IDE_AFLAG_MEDIA_CHANGED = (1 << 1),
+ /* Drive cannot lock the door. */
+ IDE_AFLAG_NO_DOORLOCK = (1 << 2),
+
+ /* ide-cd */
+ /* Drive cannot eject the disc. */
+ IDE_AFLAG_NO_EJECT = (1 << 3),
+ /* Drive is a pre ATAPI 1.2 drive. */
+ IDE_AFLAG_PRE_ATAPI12 = (1 << 4),
+ /* TOC addresses are in BCD. */
+ IDE_AFLAG_TOCADDR_AS_BCD = (1 << 5),
+ /* TOC track numbers are in BCD. */
+ IDE_AFLAG_TOCTRACKS_AS_BCD = (1 << 6),
+ /*
+ * Drive does not provide data in multiples of SECTOR_SIZE
+ * when more than one interrupt is needed.
+ */
+ IDE_AFLAG_LIMIT_NFRAMES = (1 << 7),
+ /* Seeking in progress. */
+ IDE_AFLAG_SEEKING = (1 << 8),
+ /* Saved TOC information is current. */
+ IDE_AFLAG_TOC_VALID = (1 << 9),
+ /* We think that the drive door is locked. */
+ IDE_AFLAG_DOOR_LOCKED = (1 << 10),
+ /* SET_CD_SPEED command is unsupported. */
+ IDE_AFLAG_NO_SPEED_SELECT = (1 << 11),
+ IDE_AFLAG_VERTOS_300_SSD = (1 << 12),
+ IDE_AFLAG_VERTOS_600_ESD = (1 << 13),
+ IDE_AFLAG_SANYO_3CD = (1 << 14),
+ IDE_AFLAG_FULL_CAPS_PAGE = (1 << 15),
+ IDE_AFLAG_PLAY_AUDIO_OK = (1 << 16),
+ IDE_AFLAG_LE_SPEED_FIELDS = (1 << 17),
+
+ /* ide-floppy */
+ /* Format in progress */
+ IDE_AFLAG_FORMAT_IN_PROGRESS = (1 << 18),
+ /* Avoid commands not supported in Clik drive */
+ IDE_AFLAG_CLIK_DRIVE = (1 << 19),
+ /* Requires BH algorithm for packets */
+ IDE_AFLAG_ZIP_DRIVE = (1 << 20),
+ /* Write protect */
+ IDE_AFLAG_WP = (1 << 21),
+ /* Supports format progress report */
+ IDE_AFLAG_SRFP = (1 << 22),
+
+ /* ide-tape */
+ IDE_AFLAG_IGNORE_DSC = (1 << 23),
+ /* 0 When the tape position is unknown */
+ IDE_AFLAG_ADDRESS_VALID = (1 << 24),
+ /* Device already opened */
+ IDE_AFLAG_BUSY = (1 << 25),
+ /* Attempt to auto-detect the current user block size */
+ IDE_AFLAG_DETECT_BS = (1 << 26),
+ /* Currently on a filemark */
+ IDE_AFLAG_FILEMARK = (1 << 27),
+ /* 0 = no tape is loaded, so we don't rewind after ejecting */
+ IDE_AFLAG_MEDIUM_PRESENT = (1 << 28),
+
+ IDE_AFLAG_NO_AUTOCLOSE = (1 << 29),
+};
+
+/* device flags */
+enum {
+ /* restore settings after device reset */
+ IDE_DFLAG_KEEP_SETTINGS = (1 << 0),
+ /* device is using DMA for read/write */
+ IDE_DFLAG_USING_DMA = (1 << 1),
+ /* okay to unmask other IRQs */
+ IDE_DFLAG_UNMASK = (1 << 2),
+ /* don't attempt flushes */
+ IDE_DFLAG_NOFLUSH = (1 << 3),
+ /* DSC overlap */
+ IDE_DFLAG_DSC_OVERLAP = (1 << 4),
+ /* give potential excess bandwidth */
+ IDE_DFLAG_NICE1 = (1 << 5),
+ /* device is physically present */
+ IDE_DFLAG_PRESENT = (1 << 6),
+ /* device ejected hint */
+ IDE_DFLAG_DEAD = (1 << 7),
+ /* id read from device (synthetic if not set) */
+ IDE_DFLAG_ID_READ = (1 << 8),
+ IDE_DFLAG_NOPROBE = (1 << 9),
+ /* need to do check_media_change() */
+ IDE_DFLAG_REMOVABLE = (1 << 10),
+ /* needed for removable devices */
+ IDE_DFLAG_ATTACH = (1 << 11),
+ IDE_DFLAG_FORCED_GEOM = (1 << 12),
+ /* disallow setting unmask bit */
+ IDE_DFLAG_NO_UNMASK = (1 << 13),
+ /* disallow enabling 32-bit I/O */
+ IDE_DFLAG_NO_IO_32BIT = (1 << 14),
+ /* for removable only: door lock/unlock works */
+ IDE_DFLAG_DOORLOCKING = (1 << 15),
+ /* disallow DMA */
+ IDE_DFLAG_NODMA = (1 << 16),
+ /* powermanagment told us not to do anything, so sleep nicely */
+ IDE_DFLAG_BLOCKED = (1 << 17),
+ /* ide-scsi emulation */
+ IDE_DFLAG_SCSI = (1 << 18),
+ /* sleeping & sleep field valid */
+ IDE_DFLAG_SLEEPING = (1 << 19),
+ IDE_DFLAG_POST_RESET = (1 << 20),
+ IDE_DFLAG_UDMA33_WARNED = (1 << 21),
+ IDE_DFLAG_LBA48 = (1 << 22),
+ /* status of write cache */
+ IDE_DFLAG_WCACHE = (1 << 23),
+ /* used for ignoring ATA_DF */
+ IDE_DFLAG_NOWERR = (1 << 24),
+ /* retrying in PIO */
+ IDE_DFLAG_DMA_PIO_RETRY = (1 << 25),
+ IDE_DFLAG_LBA = (1 << 26),
+ /* don't unload heads */
+ IDE_DFLAG_NO_UNLOAD = (1 << 27),
+ /* heads unloaded, please don't reset port */
+ IDE_DFLAG_PARKED = (1 << 28)
+};
+
+struct ide_drive_s {
+ char name[4]; /* drive name, such as "hda" */
+ char driver_req[10]; /* requests specific driver */
+
+ struct request_queue *queue; /* request queue */
+
+ struct request *rq; /* current request */
+ struct ide_drive_s *next; /* circular list of hwgroup drives */
+ void *driver_data; /* extra driver data */
+ u16 *id; /* identification info */
+#ifdef CONFIG_IDE_PROC_FS
+ struct proc_dir_entry *proc; /* /proc/ide/ directory entry */
+ const struct ide_proc_devset *settings; /* /proc/ide/ drive settings */
+#endif
+ struct hwif_s *hwif; /* actually (ide_hwif_t *) */
+
+ unsigned long dev_flags;
+
+ unsigned long sleep; /* sleep until this time */
+ unsigned long service_start; /* time we started last request */
+ unsigned long service_time; /* service time of last request */
+ unsigned long timeout; /* max time to wait for irq */
+
+ special_t special; /* special action flags */
+
+ u8 select; /* basic drive/head select reg value */
+ u8 retry_pio; /* retrying dma capable host in pio */
+ u8 waiting_for_dma; /* dma currently in progress */
+ u8 dma; /* atapi dma flag */
+
+ u8 quirk_list; /* considered quirky, set for a specific host */
+ u8 init_speed; /* transfer rate set at boot */
+ u8 current_speed; /* current transfer rate set */
+ u8 desired_speed; /* desired transfer rate set */
+ u8 dn; /* now wide spread use */
+ u8 acoustic; /* acoustic management */
+ u8 media; /* disk, cdrom, tape, floppy, ... */
+ u8 ready_stat; /* min status value for drive ready */
+ u8 mult_count; /* current multiple sector setting */
+ u8 mult_req; /* requested multiple sector setting */
+ u8 io_32bit; /* 0=16-bit, 1=32-bit, 2/3=32bit+sync */
+ u8 bad_wstat; /* used for ignoring ATA_DF */
+ u8 head; /* "real" number of heads */
+ u8 sect; /* "real" sectors per track */
+ u8 bios_head; /* BIOS/fdisk/LILO number of heads */
+ u8 bios_sect; /* BIOS/fdisk/LILO sectors per track */
+
+ /* delay this long before sending packet command */
+ u8 pc_delay;
+
+ unsigned int bios_cyl; /* BIOS/fdisk/LILO number of cyls */
+ unsigned int cyl; /* "real" number of cyls */
+ unsigned int drive_data; /* used by set_pio_mode/selectproc */
+ unsigned int failures; /* current failure count */
+ unsigned int max_failures; /* maximum allowed failure count */
+ u64 probed_capacity;/* initial reported media capacity (ide-cd only currently) */
+
+ u64 capacity64; /* total number of sectors */
+
+ int lun; /* logical unit */
+ int crc_count; /* crc counter to reduce drive speed */
+
+ unsigned long debug_mask; /* debugging levels switch */
+
+#ifdef CONFIG_BLK_DEV_IDEACPI
+ struct ide_acpi_drive_link *acpidata;
+#endif
+ struct list_head list;
+ struct device gendev;
+ struct completion gendev_rel_comp; /* to deal with device release() */
+
+ /* current packet command */
+ struct ide_atapi_pc *pc;
+
+ /* callback for packet commands */
+ void (*pc_callback)(struct ide_drive_s *, int);
+
+ void (*pc_update_buffers)(struct ide_drive_s *, struct ide_atapi_pc *);
+ int (*pc_io_buffers)(struct ide_drive_s *, struct ide_atapi_pc *,
+ unsigned int, int);
+
+ unsigned long atapi_flags;
+
+ struct ide_atapi_pc request_sense_pc;
+ struct request request_sense_rq;
+};
+
+typedef struct ide_drive_s ide_drive_t;
+
+#define to_ide_device(dev) container_of(dev, ide_drive_t, gendev)
+
+#define to_ide_drv(obj, cont_type) \
+ container_of(obj, struct cont_type, kref)
+
+#define ide_drv_g(disk, cont_type) \
+ container_of((disk)->private_data, struct cont_type, driver)
+
+struct ide_port_info;
+
+struct ide_tp_ops {
+ void (*exec_command)(struct hwif_s *, u8);
+ u8 (*read_status)(struct hwif_s *);
+ u8 (*read_altstatus)(struct hwif_s *);
+ u8 (*read_sff_dma_status)(struct hwif_s *);
+
+ void (*set_irq)(struct hwif_s *, int);
+
+ void (*tf_load)(ide_drive_t *, struct ide_task_s *);
+ void (*tf_read)(ide_drive_t *, struct ide_task_s *);
+
+ void (*input_data)(ide_drive_t *, struct request *, void *,
+ unsigned int);
+ void (*output_data)(ide_drive_t *, struct request *, void *,
+ unsigned int);
+};
+
+extern const struct ide_tp_ops default_tp_ops;
+
+/**
+ * struct ide_port_ops - IDE port operations
+ *
+ * @init_dev: host specific initialization of a device
+ * @set_pio_mode: routine to program host for PIO mode
+ * @set_dma_mode: routine to program host for DMA mode
+ * @selectproc: tweaks hardware to select drive
+ * @reset_poll: chipset polling based on hba specifics
+ * @pre_reset: chipset specific changes to default for device-hba resets
+ * @resetproc: routine to reset controller after a disk reset
+ * @maskproc: special host masking for drive selection
+ * @quirkproc: check host's drive quirk list
+ * @clear_irq: clear IRQ
+ *
+ * @mdma_filter: filter MDMA modes
+ * @udma_filter: filter UDMA modes
+ *
+ * @cable_detect: detect cable type
+ */
+struct ide_port_ops {
+ void (*init_dev)(ide_drive_t *);
+ void (*set_pio_mode)(ide_drive_t *, const u8);
+ void (*set_dma_mode)(ide_drive_t *, const u8);
+ void (*selectproc)(ide_drive_t *);
+ int (*reset_poll)(ide_drive_t *);
+ void (*pre_reset)(ide_drive_t *);
+ void (*resetproc)(ide_drive_t *);
+ void (*maskproc)(ide_drive_t *, int);
+ void (*quirkproc)(ide_drive_t *);
+ void (*clear_irq)(ide_drive_t *);
+
+ u8 (*mdma_filter)(ide_drive_t *);
+ u8 (*udma_filter)(ide_drive_t *);
+
+ u8 (*cable_detect)(struct hwif_s *);
+};
+
+struct ide_dma_ops {
+ void (*dma_host_set)(struct ide_drive_s *, int);
+ int (*dma_setup)(struct ide_drive_s *);
+ void (*dma_exec_cmd)(struct ide_drive_s *, u8);
+ void (*dma_start)(struct ide_drive_s *);
+ int (*dma_end)(struct ide_drive_s *);
+ int (*dma_test_irq)(struct ide_drive_s *);
+ void (*dma_lost_irq)(struct ide_drive_s *);
+ void (*dma_timeout)(struct ide_drive_s *);
+};
+
+struct ide_host;
+
+typedef struct hwif_s {
+ struct hwif_s *next; /* for linked-list in ide_hwgroup_t */
+ struct hwif_s *mate; /* other hwif from same PCI chip */
+ struct hwgroup_s *hwgroup; /* actually (ide_hwgroup_t *) */
+ struct proc_dir_entry *proc; /* /proc/ide/ directory entry */
+
+ struct ide_host *host;
+
+ char name[6]; /* name of interface, eg. "ide0" */
+
+ struct ide_io_ports io_ports;
+
+ unsigned long sata_scr[SATA_NR_PORTS];
+
+ ide_drive_t drives[MAX_DRIVES]; /* drive info */
+
+ u8 major; /* our major number */
+ u8 index; /* 0 for ide0; 1 for ide1; ... */
+ u8 channel; /* for dual-port chips: 0=primary, 1=secondary */
+
+ u32 host_flags;
+
+ u8 pio_mask;
+
+ u8 ultra_mask;
+ u8 mwdma_mask;
+ u8 swdma_mask;
+
+ u8 cbl; /* cable type */
+
+ hwif_chipset_t chipset; /* sub-module for tuning.. */
+
+ struct device *dev;
+
+ ide_ack_intr_t *ack_intr;
+
+ void (*rw_disk)(ide_drive_t *, struct request *);
+
+ const struct ide_tp_ops *tp_ops;
+ const struct ide_port_ops *port_ops;
+ const struct ide_dma_ops *dma_ops;
+
+ /* dma physical region descriptor table (cpu view) */
+ unsigned int *dmatable_cpu;
+ /* dma physical region descriptor table (dma view) */
+ dma_addr_t dmatable_dma;
+
+ /* maximum number of PRD table entries */
+ int prd_max_nents;
+ /* PRD entry size in bytes */
+ int prd_ent_size;
+
+ /* Scatter-gather list used to build the above */
+ struct scatterlist *sg_table;
+ int sg_max_nents; /* Maximum number of entries in it */
+ int sg_nents; /* Current number of entries in it */
+ int sg_dma_direction; /* dma transfer direction */
+
+ /* data phase of the active command (currently only valid for PIO/DMA) */
+ int data_phase;
+
+ struct ide_task_s task; /* current command */
+
+ unsigned int nsect;
+ unsigned int nleft;
+ struct scatterlist *cursg;
+ unsigned int cursg_ofs;
+
+ int rqsize; /* max sectors per request */
+ int irq; /* our irq number */
+
+ unsigned long dma_base; /* base addr for dma ports */
+
+ unsigned long config_data; /* for use by chipset-specific code */
+ unsigned long select_data; /* for use by chipset-specific code */
+
+ unsigned long extra_base; /* extra addr for dma ports */
+ unsigned extra_ports; /* number of extra dma ports */
+
+ unsigned present : 1; /* this interface exists */
+ unsigned serialized : 1; /* serialized all channel operation */
+ unsigned sharing_irq: 1; /* 1 = sharing irq with another hwif */
+ unsigned sg_mapped : 1; /* sg_table and sg_nents are ready */
+
+ struct device gendev;
+ struct device *portdev;
+
+ struct completion gendev_rel_comp; /* To deal with device release() */
+
+ void *hwif_data; /* extra hwif data */
+
+#ifdef CONFIG_BLK_DEV_IDEACPI
+ struct ide_acpi_hwif_link *acpidata;
+#endif
+} ____cacheline_internodealigned_in_smp ide_hwif_t;
+
+#define MAX_HOST_PORTS 4
+
+struct ide_host {
+ ide_hwif_t *ports[MAX_HOST_PORTS];
+ unsigned int n_ports;
+ struct device *dev[2];
+ unsigned int (*init_chipset)(struct pci_dev *);
+ unsigned long host_flags;
+ void *host_priv;
+};
+
+/*
+ * internal ide interrupt handler type
+ */
+typedef ide_startstop_t (ide_handler_t)(ide_drive_t *);
+typedef int (ide_expiry_t)(ide_drive_t *);
+
+/* used by ide-cd, ide-floppy, etc. */
+typedef void (xfer_func_t)(ide_drive_t *, struct request *rq, void *, unsigned);
+
+typedef struct hwgroup_s {
+ /* irq handler, if active */
+ ide_startstop_t (*handler)(ide_drive_t *);
+
+ /* BOOL: protects all fields below */
+ volatile int busy;
+ /* BOOL: wake us up on timer expiry */
+ unsigned int sleeping : 1;
+ /* BOOL: polling active & poll_timeout field valid */
+ unsigned int polling : 1;
+
+ /* current drive */
+ ide_drive_t *drive;
+ /* ptr to current hwif in linked-list */
+ ide_hwif_t *hwif;
+
+ /* current request */
+ struct request *rq;
+
+ /* failsafe timer */
+ struct timer_list timer;
+ /* timeout value during long polls */
+ unsigned long poll_timeout;
+ /* queried upon timeouts */
+ int (*expiry)(ide_drive_t *);
+
+ int req_gen;
+ int req_gen_timer;
+} ide_hwgroup_t;
+
+typedef struct ide_driver_s ide_driver_t;
+
+extern struct mutex ide_setting_mtx;
+
+/*
+ * configurable drive settings
+ */
+
+#define DS_SYNC (1 << 0)
+
+struct ide_devset {
+ int (*get)(ide_drive_t *);
+ int (*set)(ide_drive_t *, int);
+ unsigned int flags;
+};
+
+#define __DEVSET(_flags, _get, _set) { \
+ .flags = _flags, \
+ .get = _get, \
+ .set = _set, \
+}
+
+#define ide_devset_get(name, field) \
+static int get_##name(ide_drive_t *drive) \
+{ \
+ return drive->field; \
+}
+
+#define ide_devset_set(name, field) \
+static int set_##name(ide_drive_t *drive, int arg) \
+{ \
+ drive->field = arg; \
+ return 0; \
+}
+
+#define ide_devset_get_flag(name, flag) \
+static int get_##name(ide_drive_t *drive) \
+{ \
+ return !!(drive->dev_flags & flag); \
+}
+
+#define ide_devset_set_flag(name, flag) \
+static int set_##name(ide_drive_t *drive, int arg) \
+{ \
+ if (arg) \
+ drive->dev_flags |= flag; \
+ else \
+ drive->dev_flags &= ~flag; \
+ return 0; \
+}
+
+#define __IDE_DEVSET(_name, _flags, _get, _set) \
+const struct ide_devset ide_devset_##_name = \
+ __DEVSET(_flags, _get, _set)
+
+#define IDE_DEVSET(_name, _flags, _get, _set) \
+static __IDE_DEVSET(_name, _flags, _get, _set)
+
+#define ide_devset_rw(_name, _func) \
+IDE_DEVSET(_name, 0, get_##_func, set_##_func)
+
+#define ide_devset_w(_name, _func) \
+IDE_DEVSET(_name, 0, NULL, set_##_func)
+
+#define ide_ext_devset_rw(_name, _func) \
+__IDE_DEVSET(_name, 0, get_##_func, set_##_func)
+
+#define ide_ext_devset_rw_sync(_name, _func) \
+__IDE_DEVSET(_name, DS_SYNC, get_##_func, set_##_func)
+
+#define ide_decl_devset(_name) \
+extern const struct ide_devset ide_devset_##_name
+
+ide_decl_devset(io_32bit);
+ide_decl_devset(keepsettings);
+ide_decl_devset(pio_mode);
+ide_decl_devset(unmaskirq);
+ide_decl_devset(using_dma);
+
+#ifdef CONFIG_IDE_PROC_FS
+/*
+ * /proc/ide interface
+ */
+
+#define ide_devset_rw_field(_name, _field) \
+ide_devset_get(_name, _field); \
+ide_devset_set(_name, _field); \
+IDE_DEVSET(_name, DS_SYNC, get_##_name, set_##_name)
+
+#define ide_devset_rw_flag(_name, _field) \
+ide_devset_get_flag(_name, _field); \
+ide_devset_set_flag(_name, _field); \
+IDE_DEVSET(_name, DS_SYNC, get_##_name, set_##_name)
+
+struct ide_proc_devset {
+ const char *name;
+ const struct ide_devset *setting;
+ int min, max;
+ int (*mulf)(ide_drive_t *);
+ int (*divf)(ide_drive_t *);
+};
+
+#define __IDE_PROC_DEVSET(_name, _min, _max, _mulf, _divf) { \
+ .name = __stringify(_name), \
+ .setting = &ide_devset_##_name, \
+ .min = _min, \
+ .max = _max, \
+ .mulf = _mulf, \
+ .divf = _divf, \
+}
+
+#define IDE_PROC_DEVSET(_name, _min, _max) \
+__IDE_PROC_DEVSET(_name, _min, _max, NULL, NULL)
+
+typedef struct {
+ const char *name;
+ mode_t mode;
+ read_proc_t *read_proc;
+ write_proc_t *write_proc;
+} ide_proc_entry_t;
+
+void proc_ide_create(void);
+void proc_ide_destroy(void);
+void ide_proc_register_port(ide_hwif_t *);
+void ide_proc_port_register_devices(ide_hwif_t *);
+void ide_proc_unregister_device(ide_drive_t *);
+void ide_proc_unregister_port(ide_hwif_t *);
+void ide_proc_register_driver(ide_drive_t *, ide_driver_t *);
+void ide_proc_unregister_driver(ide_drive_t *, ide_driver_t *);
+
+read_proc_t proc_ide_read_capacity;
+read_proc_t proc_ide_read_geometry;
+
+/*
+ * Standard exit stuff:
+ */
+#define PROC_IDE_READ_RETURN(page,start,off,count,eof,len) \
+{ \
+ len -= off; \
+ if (len < count) { \
+ *eof = 1; \
+ if (len <= 0) \
+ return 0; \
+ } else \
+ len = count; \
+ *start = page + off; \
+ return len; \
+}
+#else
+static inline void proc_ide_create(void) { ; }
+static inline void proc_ide_destroy(void) { ; }
+static inline void ide_proc_register_port(ide_hwif_t *hwif) { ; }
+static inline void ide_proc_port_register_devices(ide_hwif_t *hwif) { ; }
+static inline void ide_proc_unregister_device(ide_drive_t *drive) { ; }
+static inline void ide_proc_unregister_port(ide_hwif_t *hwif) { ; }
+static inline void ide_proc_register_driver(ide_drive_t *drive, ide_driver_t *driver) { ; }
+static inline void ide_proc_unregister_driver(ide_drive_t *drive, ide_driver_t *driver) { ; }
+#define PROC_IDE_READ_RETURN(page,start,off,count,eof,len) return 0;
+#endif
+
+enum {
+ /* enter/exit functions */
+ IDE_DBG_FUNC = (1 << 0),
+ /* sense key/asc handling */
+ IDE_DBG_SENSE = (1 << 1),
+ /* packet commands handling */
+ IDE_DBG_PC = (1 << 2),
+ /* request handling */
+ IDE_DBG_RQ = (1 << 3),
+ /* driver probing/setup */
+ IDE_DBG_PROBE = (1 << 4),
+};
+
+/* DRV_NAME has to be defined in the driver before using the macro below */
+#define __ide_debug_log(lvl, fmt, args...) \
+{ \
+ if (unlikely(drive->debug_mask & lvl)) \
+ printk(KERN_INFO DRV_NAME ": " fmt, ## args); \
+}
+
+/*
+ * Power Management state machine (rq->pm->pm_step).
+ *
+ * For each step, the core calls ide_start_power_step() first.
+ * This can return:
+ * - ide_stopped : In this case, the core calls us back again unless
+ * step have been set to ide_power_state_completed.
+ * - ide_started : In this case, the channel is left busy until an
+ * async event (interrupt) occurs.
+ * Typically, ide_start_power_step() will issue a taskfile request with
+ * do_rw_taskfile().
+ *
+ * Upon reception of the interrupt, the core will call ide_complete_power_step()
+ * with the error code if any. This routine should update the step value
+ * and return. It should not start a new request. The core will call
+ * ide_start_power_step() for the new step value, unless step have been
+ * set to IDE_PM_COMPLETED.
+ */
+enum {
+ IDE_PM_START_SUSPEND,
+ IDE_PM_FLUSH_CACHE = IDE_PM_START_SUSPEND,
+ IDE_PM_STANDBY,
+
+ IDE_PM_START_RESUME,
+ IDE_PM_RESTORE_PIO = IDE_PM_START_RESUME,
+ IDE_PM_IDLE,
+ IDE_PM_RESTORE_DMA,
+
+ IDE_PM_COMPLETED,
+};
+
+/*
+ * Subdrivers support.
+ *
+ * The gendriver.owner field should be set to the module owner of this driver.
+ * The gendriver.name field should be set to the name of this driver
+ */
+struct ide_driver_s {
+ const char *version;
+ ide_startstop_t (*do_request)(ide_drive_t *, struct request *, sector_t);
+ int (*end_request)(ide_drive_t *, int, int);
+ ide_startstop_t (*error)(ide_drive_t *, struct request *rq, u8, u8);
+ struct device_driver gen_driver;
+ int (*probe)(ide_drive_t *);
+ void (*remove)(ide_drive_t *);
+ void (*resume)(ide_drive_t *);
+ void (*shutdown)(ide_drive_t *);
+#ifdef CONFIG_IDE_PROC_FS
+ ide_proc_entry_t *proc;
+ const struct ide_proc_devset *settings;
+#endif
+};
+
+#define to_ide_driver(drv) container_of(drv, ide_driver_t, gen_driver)
+
+int ide_device_get(ide_drive_t *);
+void ide_device_put(ide_drive_t *);
+
+struct ide_ioctl_devset {
+ unsigned int get_ioctl;
+ unsigned int set_ioctl;
+ const struct ide_devset *setting;
+};
+
+int ide_setting_ioctl(ide_drive_t *, struct block_device *, unsigned int,
+ unsigned long, const struct ide_ioctl_devset *);
+
+int generic_ide_ioctl(ide_drive_t *, struct file *, struct block_device *,
+ unsigned, unsigned long);
+
+extern int ide_vlb_clk;
+extern int ide_pci_clk;
+
+extern int ide_end_request (ide_drive_t *drive, int uptodate, int nrsecs);
+int ide_end_dequeued_request(ide_drive_t *drive, struct request *rq,
+ int uptodate, int nr_sectors);
+
+extern void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry);
+
+void ide_execute_command(ide_drive_t *, u8, ide_handler_t *, unsigned int,
+ ide_expiry_t *);
+
+void ide_execute_pkt_cmd(ide_drive_t *);
+
+void ide_pad_transfer(ide_drive_t *, int, int);
+
+ide_startstop_t __ide_error(ide_drive_t *, struct request *, u8, u8);
+
+ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, byte stat);
+
+void ide_fix_driveid(u16 *);
+
+extern void ide_fixstring(u8 *, const int, const int);
+
+int ide_busy_sleep(ide_hwif_t *, unsigned long, int);
+
+int ide_wait_stat(ide_startstop_t *, ide_drive_t *, u8, u8, unsigned long);
+
+extern ide_startstop_t ide_do_reset (ide_drive_t *);
+
+extern int ide_devset_execute(ide_drive_t *drive,
+ const struct ide_devset *setting, int arg);
+
+extern void ide_do_drive_cmd(ide_drive_t *, struct request *);
+
+extern void ide_end_drive_cmd(ide_drive_t *, u8, u8);
+
void ide_tf_dump(const char *, struct ide_taskfile *);
void ide_exec_command(ide_hwif_t *, u8);
@@ -1051,6 +1197,8 @@
void ide_input_data(ide_drive_t *, struct request *, void *, unsigned int);
void ide_output_data(ide_drive_t *, struct request *, void *, unsigned int);
+int ide_io_buffers(ide_drive_t *, struct ide_atapi_pc *, unsigned int, int);
+
extern void SELECT_DRIVE(ide_drive_t *);
void SELECT_MASK(ide_drive_t *, int);
@@ -1061,16 +1209,46 @@
void ide_pktcmd_tf_load(ide_drive_t *, u32, u16, u8);
-ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc,
- ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry,
- void (*update_buffers)(ide_drive_t *, struct ide_atapi_pc *),
- void (*retry_pc)(ide_drive_t *), void (*dsc_handle)(ide_drive_t *),
- void (*io_buffers)(ide_drive_t *, struct ide_atapi_pc *, unsigned int,
- int));
-ide_startstop_t ide_transfer_pc(ide_drive_t *, struct ide_atapi_pc *,
- ide_handler_t *, unsigned int, ide_expiry_t *);
-ide_startstop_t ide_issue_pc(ide_drive_t *, struct ide_atapi_pc *,
- ide_handler_t *, unsigned int, ide_expiry_t *);
+int ide_check_atapi_device(ide_drive_t *, const char *);
+
+void ide_init_pc(struct ide_atapi_pc *);
+
+/* Disk head parking */
+extern wait_queue_head_t ide_park_wq;
+ssize_t ide_park_show(struct device *dev, struct device_attribute *attr,
+ char *buf);
+ssize_t ide_park_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t len);
+
+/*
+ * Special requests for ide-tape block device strategy routine.
+ *
+ * In order to service a character device command, we add special requests to
+ * the tail of our block device request queue and wait for their completion.
+ */
+enum {
+ REQ_IDETAPE_PC1 = (1 << 0), /* packet command (first stage) */
+ REQ_IDETAPE_PC2 = (1 << 1), /* packet command (second stage) */
+ REQ_IDETAPE_READ = (1 << 2),
+ REQ_IDETAPE_WRITE = (1 << 3),
+};
+
+int ide_queue_pc_tail(ide_drive_t *, struct gendisk *, struct ide_atapi_pc *);
+
+int ide_do_test_unit_ready(ide_drive_t *, struct gendisk *);
+int ide_do_start_stop(ide_drive_t *, struct gendisk *, int);
+int ide_set_media_lock(ide_drive_t *, struct gendisk *, int);
+void ide_create_request_sense_cmd(ide_drive_t *, struct ide_atapi_pc *);
+void ide_retry_pc(ide_drive_t *, struct gendisk *);
+
+static inline unsigned long ide_scsi_get_timeout(struct ide_atapi_pc *pc)
+{
+ return max_t(unsigned long, WAIT_CMD, pc->timeout - jiffies);
+}
+
+int ide_scsi_expiry(ide_drive_t *);
+
+ide_startstop_t ide_issue_pc(ide_drive_t *, unsigned int, ide_expiry_t *);
ide_startstop_t do_rw_taskfile(ide_drive_t *, ide_task_t *);
@@ -1080,8 +1258,6 @@
int ide_no_data_taskfile(ide_drive_t *, ide_task_t *);
int ide_taskfile_ioctl(ide_drive_t *, unsigned int, unsigned long);
-int ide_cmd_ioctl(ide_drive_t *, unsigned int, unsigned long);
-int ide_task_ioctl(ide_drive_t *, unsigned int, unsigned long);
extern int ide_driveid_update(ide_drive_t *);
extern int ide_config_drive_speed(ide_drive_t *, u8);
@@ -1092,7 +1268,6 @@
extern void ide_stall_queue(ide_drive_t *drive, unsigned long timeout);
-extern int ide_spin_wait_hwgroup(ide_drive_t *);
extern void ide_timer_expiry(unsigned long);
extern irqreturn_t ide_intr(int irq, void *dev_id);
extern void do_ide_request(struct request_queue *);
@@ -1229,6 +1404,14 @@
const struct ide_port_info *, void *);
void ide_pci_remove(struct pci_dev *);
+#ifdef CONFIG_PM
+int ide_pci_suspend(struct pci_dev *, pm_message_t);
+int ide_pci_resume(struct pci_dev *);
+#else
+#define ide_pci_suspend NULL
+#define ide_pci_resume NULL
+#endif
+
void ide_map_sg(ide_drive_t *, struct request *);
void ide_init_sg_cmd(ide_drive_t *, struct request *);
@@ -1240,9 +1423,10 @@
const char *id_firmware;
};
-int ide_in_drive_list(struct hd_driveid *, const struct drive_list_entry *);
+int ide_in_drive_list(u16 *, const struct drive_list_entry *);
#ifdef CONFIG_BLK_DEV_IDEDMA
+int ide_dma_good_drive(ide_drive_t *);
int __ide_dma_bad_drive(ide_drive_t *);
int ide_id_dma_bug(ide_drive_t *);
@@ -1260,25 +1444,29 @@
void ide_check_dma_crc(ide_drive_t *);
ide_startstop_t ide_dma_intr(ide_drive_t *);
+int ide_allocate_dma_engine(ide_hwif_t *);
+void ide_release_dma_engine(ide_hwif_t *);
+
int ide_build_sglist(ide_drive_t *, struct request *);
void ide_destroy_dmatable(ide_drive_t *);
#ifdef CONFIG_BLK_DEV_IDEDMA_SFF
+int config_drive_for_dma(ide_drive_t *);
extern int ide_build_dmatable(ide_drive_t *, struct request *);
-int ide_allocate_dma_engine(ide_hwif_t *);
-void ide_release_dma_engine(ide_hwif_t *);
-
void ide_dma_host_set(ide_drive_t *, int);
extern int ide_dma_setup(ide_drive_t *);
void ide_dma_exec_cmd(ide_drive_t *, u8);
extern void ide_dma_start(ide_drive_t *);
-extern int __ide_dma_end(ide_drive_t *);
+int ide_dma_end(ide_drive_t *);
int ide_dma_test_irq(ide_drive_t *);
-extern void ide_dma_lost_irq(ide_drive_t *);
-extern void ide_dma_timeout(ide_drive_t *);
extern const struct ide_dma_ops sff_dma_ops;
+#else
+static inline int config_drive_for_dma(ide_drive_t *drive) { return 0; }
#endif /* CONFIG_BLK_DEV_IDEDMA_SFF */
+void ide_dma_lost_irq(ide_drive_t *);
+void ide_dma_timeout(ide_drive_t *);
+
#else
static inline int ide_id_dma_bug(ide_drive_t *drive) { return 0; }
static inline u8 ide_find_dma_mode(ide_drive_t *drive, u8 speed) { return 0; }
@@ -1289,11 +1477,8 @@
static inline void ide_dma_verbose(ide_drive_t *drive) { ; }
static inline int ide_set_dma(ide_drive_t *drive) { return 1; }
static inline void ide_check_dma_crc(ide_drive_t *drive) { ; }
-#endif /* CONFIG_BLK_DEV_IDEDMA */
-
-#ifndef CONFIG_BLK_DEV_IDEDMA_SFF
static inline void ide_release_dma_engine(ide_hwif_t *hwif) { ; }
-#endif
+#endif /* CONFIG_BLK_DEV_IDEDMA */
#ifdef CONFIG_BLK_DEV_IDEACPI
extern int ide_acpi_exec_tfs(ide_drive_t *drive);
@@ -1321,7 +1506,6 @@
void ide_port_apply_params(ide_hwif_t *);
-struct ide_host *ide_host_alloc_all(const struct ide_port_info *, hw_regs_t **);
struct ide_host *ide_host_alloc(const struct ide_port_info *, hw_regs_t **);
void ide_host_free(struct ide_host *);
int ide_host_register(struct ide_host *, const struct ide_port_info *,
@@ -1347,24 +1531,6 @@
extern void ide_toggle_bounce(ide_drive_t *drive, int on);
extern int ide_set_xfer_rate(ide_drive_t *drive, u8 rate);
-static inline int ide_dev_has_iordy(struct hd_driveid *id)
-{
- return ((id->field_valid & 2) && (id->capability & 8)) ? 1 : 0;
-}
-
-static inline int ide_dev_is_sata(struct hd_driveid *id)
-{
- /*
- * See if word 93 is 0 AND drive is at least ATA-5 compatible
- * verifying that word 80 by casting it to a signed type --
- * this trick allows us to filter out the reserved values of
- * 0x0000 and 0xffff along with the earlier ATA revisions...
- */
- if (id->hw_config == 0 && (short)id->major_rev_num >= 0x0020)
- return 1;
- return 0;
-}
-
u64 ide_get_lba_addr(struct ide_taskfile *, int);
u8 ide_dump_status(ide_drive_t *, const char *, u8);
@@ -1436,13 +1602,6 @@
extern struct bus_type ide_bus_type;
extern struct class *ide_port_class;
-/* check if CACHE FLUSH (EXT) command is supported (bits defined in ATA-6) */
-#define ide_id_has_flush_cache(id) ((id)->cfs_enable_2 & 0x3000)
-
-/* some Maxtor disks have bit 13 defined incorrectly so check bit 10 too */
-#define ide_id_has_flush_cache_ext(id) \
- (((id)->cfs_enable_2 & 0x2400) == 0x2400)
-
static inline void ide_dump_identify(u8 *id)
{
print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 2, id, 512, 0);
@@ -1453,10 +1612,10 @@
return hwif->dev ? dev_to_node(hwif->dev) : -1;
}
-static inline ide_drive_t *ide_get_paired_drive(ide_drive_t *drive)
+static inline ide_drive_t *ide_get_pair_dev(ide_drive_t *drive)
{
- ide_hwif_t *hwif = HWIF(drive);
+ ide_drive_t *peer = &drive->hwif->drives[(drive->dn ^ 1) & 1];
- return &hwif->drives[(drive->dn ^ 1) & 1];
+ return (peer->dev_flags & IDE_DFLAG_PRESENT) ? peer : NULL;
}
#endif /* _IDE_H */
diff --git a/include/linux/ioport.h b/include/linux/ioport.h
index 350033e..ee9bcc6 100644
--- a/include/linux/ioport.h
+++ b/include/linux/ioport.h
@@ -108,6 +108,9 @@
extern int request_resource(struct resource *root, struct resource *new);
extern int release_resource(struct resource *new);
+extern void reserve_region_with_split(struct resource *root,
+ resource_size_t start, resource_size_t end,
+ const char *name);
extern int insert_resource(struct resource *parent, struct resource *new);
extern void insert_resource_expand_to_fit(struct resource *root, struct resource *new);
extern int allocate_resource(struct resource *root, struct resource *new,
diff --git a/include/linux/irq.h b/include/linux/irq.h
index 8ccb462..8d9411b 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -62,6 +62,7 @@
#define IRQ_MOVE_PENDING 0x00200000 /* need to re-target IRQ destination */
#define IRQ_NO_BALANCING 0x00400000 /* IRQ is excluded from balancing */
#define IRQ_SPURIOUS_DISABLED 0x00800000 /* IRQ was disabled by the spurious trap */
+#define IRQ_MOVE_PCNTXT 0x01000000 /* IRQ migration from process context */
#ifdef CONFIG_IRQ_PER_CPU
# define CHECK_IRQ_PER_CPU(var) ((var) & IRQ_PER_CPU)
diff --git a/include/linux/ivtv.h b/include/linux/ivtv.h
index 17ca64b..f272028 100644
--- a/include/linux/ivtv.h
+++ b/include/linux/ivtv.h
@@ -23,6 +23,7 @@
#include <linux/compiler.h>
#include <linux/types.h>
+#include <linux/videodev2.h>
/* ivtv knows several distinct output modes: MPEG streaming,
YUV streaming, YUV updates through user DMA and the passthrough
diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h
index 3dd2090..d2e91ea 100644
--- a/include/linux/jbd2.h
+++ b/include/linux/jbd2.h
@@ -850,7 +850,8 @@
*/
struct block_device *j_dev;
int j_blocksize;
- unsigned long long j_blk_offset;
+ unsigned long long j_blk_offset;
+ char j_devname[BDEVNAME_SIZE+24];
/*
* Device which holds the client fs. For internal journal this will be
@@ -966,6 +967,9 @@
#define JBD2_FLUSHED 0x008 /* The journal superblock has been flushed */
#define JBD2_LOADED 0x010 /* The journal superblock has been loaded */
#define JBD2_BARRIER 0x020 /* Use IDE barriers */
+#define JBD2_ABORT_ON_SYNCDATA_ERR 0x040 /* Abort the journal on file
+ * data write error in ordered
+ * mode */
/*
* Function declarations for the journaling transaction and buffer
@@ -1059,7 +1063,7 @@
(journal_t *, unsigned long, unsigned long, unsigned long);
extern int jbd2_journal_create (journal_t *);
extern int jbd2_journal_load (journal_t *journal);
-extern void jbd2_journal_destroy (journal_t *);
+extern int jbd2_journal_destroy (journal_t *);
extern int jbd2_journal_recover (journal_t *journal);
extern int jbd2_journal_wipe (journal_t *, int);
extern int jbd2_journal_skip_recovery (journal_t *);
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 2651f80..75d81f1 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -182,7 +182,7 @@
extern int get_option(char **str, int *pint);
extern char *get_options(const char *str, int nints, int *ints);
-extern unsigned long long memparse(char *ptr, char **retptr);
+extern unsigned long long memparse(const char *ptr, char **retptr);
extern int core_kernel_text(unsigned long addr);
extern int __kernel_text_address(unsigned long addr);
diff --git a/include/linux/key.h b/include/linux/key.h
index c45c962..1b70e35 100644
--- a/include/linux/key.h
+++ b/include/linux/key.h
@@ -299,6 +299,7 @@
#define key_validate(k) 0
#define key_serial(k) 0
#define key_get(k) ({ NULL; })
+#define key_revoke(k) do { } while(0)
#define key_put(k) do { } while(0)
#define key_ref_put(k) do { } while(0)
#define make_key_ref(k, p) ({ NULL; })
diff --git a/include/linux/magic.h b/include/linux/magic.h
index 1fa0c2c..f7f3fdd 100644
--- a/include/linux/magic.h
+++ b/include/linux/magic.h
@@ -6,6 +6,10 @@
#define AFS_SUPER_MAGIC 0x5346414F
#define AUTOFS_SUPER_MAGIC 0x0187
#define CODA_SUPER_MAGIC 0x73757245
+#define DEBUGFS_MAGIC 0x64626720
+#define SYSFS_MAGIC 0x62656572
+#define SECURITYFS_MAGIC 0x73636673
+#define TMPFS_MAGIC 0x01021994
#define EFS_SUPER_MAGIC 0x414A53
#define EXT2_SUPER_MAGIC 0xEF53
#define EXT3_SUPER_MAGIC 0xEF53
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 72a15dc..c61ba10 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -7,6 +7,7 @@
#include <linux/gfp.h>
#include <linux/list.h>
+#include <linux/mmdebug.h>
#include <linux/mmzone.h>
#include <linux/rbtree.h>
#include <linux/prio_tree.h>
@@ -219,12 +220,6 @@
*/
#include <linux/page-flags.h>
-#ifdef CONFIG_DEBUG_VM
-#define VM_BUG_ON(cond) BUG_ON(cond)
-#else
-#define VM_BUG_ON(condition) do { } while(0)
-#endif
-
/*
* Methods to modify the page usage count.
*
@@ -919,7 +914,7 @@
}
#endif /* CONFIG_MMU && !__ARCH_HAS_4LEVEL_HACK */
-#if NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS
+#if USE_SPLIT_PTLOCKS
/*
* We tuck a spinlock to guard each pagetable page into its struct page,
* at page->private, with BUILD_BUG_ON to make sure that this will not
@@ -932,14 +927,14 @@
} while (0)
#define pte_lock_deinit(page) ((page)->mapping = NULL)
#define pte_lockptr(mm, pmd) ({(void)(mm); __pte_lockptr(pmd_page(*(pmd)));})
-#else
+#else /* !USE_SPLIT_PTLOCKS */
/*
* We use mm->page_table_lock to guard all pagetable pages of the mm.
*/
#define pte_lock_init(page) do {} while (0)
#define pte_lock_deinit(page) do {} while (0)
#define pte_lockptr(mm, pmd) ({(void)(pmd); &(mm)->page_table_lock;})
-#endif /* NR_CPUS < CONFIG_SPLIT_PTLOCK_CPUS */
+#endif /* USE_SPLIT_PTLOCKS */
static inline void pgtable_page_ctor(struct page *page)
{
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index bf33413..9d49fa3 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -21,11 +21,13 @@
struct address_space;
-#if NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS
+#define USE_SPLIT_PTLOCKS (NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS)
+
+#if USE_SPLIT_PTLOCKS
typedef atomic_long_t mm_counter_t;
-#else /* NR_CPUS < CONFIG_SPLIT_PTLOCK_CPUS */
+#else /* !USE_SPLIT_PTLOCKS */
typedef unsigned long mm_counter_t;
-#endif /* NR_CPUS < CONFIG_SPLIT_PTLOCK_CPUS */
+#endif /* !USE_SPLIT_PTLOCKS */
/*
* Each physical page in the system has a struct page associated with
@@ -65,7 +67,7 @@
* see PAGE_MAPPING_ANON below.
*/
};
-#if NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS
+#if USE_SPLIT_PTLOCKS
spinlock_t ptl;
#endif
struct kmem_cache *slab; /* SLUB: Pointer to slab */
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 9c288c9..bde891f 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -65,7 +65,7 @@
* -ENOSYS when not supported (equal to NULL callback)
* or a negative errno value when something bad happened
*
- * Return values for the get_ro callback should be:
+ * Return values for the get_cd callback should be:
* 0 for a absent card
* 1 for a present card
* -ENOSYS when not supported (equal to NULL callback)
diff --git a/include/linux/mmdebug.h b/include/linux/mmdebug.h
new file mode 100644
index 0000000..8a55098
--- /dev/null
+++ b/include/linux/mmdebug.h
@@ -0,0 +1,18 @@
+#ifndef LINUX_MM_DEBUG_H
+#define LINUX_MM_DEBUG_H 1
+
+#include <linux/autoconf.h>
+
+#ifdef CONFIG_DEBUG_VM
+#define VM_BUG_ON(cond) BUG_ON(cond)
+#else
+#define VM_BUG_ON(cond) do { } while (0)
+#endif
+
+#ifdef CONFIG_DEBUG_VIRTUAL
+#define VIRTUAL_BUG_ON(cond) BUG_ON(cond)
+#else
+#define VIRTUAL_BUG_ON(cond) do { } while (0)
+#endif
+
+#endif
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index c4db582..3481a7d 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -388,5 +388,52 @@
__attribute__((aligned(sizeof(kernel_ulong_t))));
};
+/* dmi */
+enum dmi_field {
+ DMI_NONE,
+ DMI_BIOS_VENDOR,
+ DMI_BIOS_VERSION,
+ DMI_BIOS_DATE,
+ DMI_SYS_VENDOR,
+ DMI_PRODUCT_NAME,
+ DMI_PRODUCT_VERSION,
+ DMI_PRODUCT_SERIAL,
+ DMI_PRODUCT_UUID,
+ DMI_BOARD_VENDOR,
+ DMI_BOARD_NAME,
+ DMI_BOARD_VERSION,
+ DMI_BOARD_SERIAL,
+ DMI_BOARD_ASSET_TAG,
+ DMI_CHASSIS_VENDOR,
+ DMI_CHASSIS_TYPE,
+ DMI_CHASSIS_VERSION,
+ DMI_CHASSIS_SERIAL,
+ DMI_CHASSIS_ASSET_TAG,
+ DMI_STRING_MAX,
+};
+
+struct dmi_strmatch {
+ unsigned char slot;
+ char substr[79];
+};
+
+#ifndef __KERNEL__
+struct dmi_system_id {
+ kernel_ulong_t callback;
+ kernel_ulong_t ident;
+ struct dmi_strmatch matches[4];
+ kernel_ulong_t driver_data
+ __attribute__((aligned(sizeof(kernel_ulong_t))));
+};
+#else
+struct dmi_system_id {
+ int (*callback)(const struct dmi_system_id *);
+ const char *ident;
+ struct dmi_strmatch matches[4];
+ void *driver_data;
+};
+#endif
+
+#define DMI_MATCH(a, b) { a, b }
#endif /* LINUX_MOD_DEVICETABLE_H */
diff --git a/include/linux/oprofile.h b/include/linux/oprofile.h
index 041bb31..bcb8f72 100644
--- a/include/linux/oprofile.h
+++ b/include/linux/oprofile.h
@@ -36,6 +36,8 @@
#define XEN_ENTER_SWITCH_CODE 10
#define SPU_PROFILING_CODE 11
#define SPU_CTX_SWITCH_CODE 12
+#define IBS_FETCH_CODE 13
+#define IBS_OP_CODE 14
struct super_block;
struct dentry;
diff --git a/include/linux/parser.h b/include/linux/parser.h
index 7dcd050..ea2281e 100644
--- a/include/linux/parser.h
+++ b/include/linux/parser.h
@@ -25,7 +25,7 @@
char *to;
} substring_t;
-int match_token(char *, match_table_t table, substring_t args[]);
+int match_token(char *, const match_table_t table, substring_t args[]);
int match_int(substring_t *, int *result);
int match_octal(substring_t *, int *result);
int match_hex(substring_t *, int *result);
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index f63b545..1176f1f 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -1533,7 +1533,9 @@
#define PCI_DEVICE_ID_MARVELL_GT64260 0x6430
#define PCI_DEVICE_ID_MARVELL_MV64360 0x6460
#define PCI_DEVICE_ID_MARVELL_MV64460 0x6480
-#define PCI_DEVICE_ID_MARVELL_CAFE_SD 0x4101
+#define PCI_DEVICE_ID_MARVELL_88ALP01_NAND 0x4100
+#define PCI_DEVICE_ID_MARVELL_88ALP01_SD 0x4101
+#define PCI_DEVICE_ID_MARVELL_88ALP01_CCIC 0x4102
#define PCI_VENDOR_ID_V3 0x11b0
#define PCI_DEVICE_ID_V3_V960 0x0001
diff --git a/include/linux/percpu.h b/include/linux/percpu.h
index fac3337..9f2a375 100644
--- a/include/linux/percpu.h
+++ b/include/linux/percpu.h
@@ -23,12 +23,19 @@
__attribute__((__section__(SHARED_ALIGNED_SECTION))) \
PER_CPU_ATTRIBUTES __typeof__(type) per_cpu__##name \
____cacheline_aligned_in_smp
+
+#define DEFINE_PER_CPU_PAGE_ALIGNED(type, name) \
+ __attribute__((__section__(".data.percpu.page_aligned"))) \
+ PER_CPU_ATTRIBUTES __typeof__(type) per_cpu__##name
#else
#define DEFINE_PER_CPU(type, name) \
PER_CPU_ATTRIBUTES __typeof__(type) per_cpu__##name
#define DEFINE_PER_CPU_SHARED_ALIGNED(type, name) \
DEFINE_PER_CPU(type, name)
+
+#define DEFINE_PER_CPU_PAGE_ALIGNED(type, name) \
+ DEFINE_PER_CPU(type, name)
#endif
#define EXPORT_PER_CPU_SYMBOL(var) EXPORT_SYMBOL(per_cpu__##var)
diff --git a/include/linux/percpu_counter.h b/include/linux/percpu_counter.h
index 2083888..9007ccd 100644
--- a/include/linux/percpu_counter.h
+++ b/include/linux/percpu_counter.h
@@ -35,7 +35,7 @@
void percpu_counter_destroy(struct percpu_counter *fbc);
void percpu_counter_set(struct percpu_counter *fbc, s64 amount);
void __percpu_counter_add(struct percpu_counter *fbc, s64 amount, s32 batch);
-s64 __percpu_counter_sum(struct percpu_counter *fbc, int set);
+s64 __percpu_counter_sum(struct percpu_counter *fbc);
static inline void percpu_counter_add(struct percpu_counter *fbc, s64 amount)
{
@@ -44,19 +44,13 @@
static inline s64 percpu_counter_sum_positive(struct percpu_counter *fbc)
{
- s64 ret = __percpu_counter_sum(fbc, 0);
+ s64 ret = __percpu_counter_sum(fbc);
return ret < 0 ? 0 : ret;
}
-static inline s64 percpu_counter_sum_and_set(struct percpu_counter *fbc)
-{
- return __percpu_counter_sum(fbc, 1);
-}
-
-
static inline s64 percpu_counter_sum(struct percpu_counter *fbc)
{
- return __percpu_counter_sum(fbc, 0);
+ return __percpu_counter_sum(fbc);
}
static inline s64 percpu_counter_read(struct percpu_counter *fbc)
diff --git a/include/linux/rtc/m48t59.h b/include/linux/rtc/m48t59.h
index e8c7c21..6fc9614 100644
--- a/include/linux/rtc/m48t59.h
+++ b/include/linux/rtc/m48t59.h
@@ -18,40 +18,47 @@
/*
* M48T59 Register Offset
*/
-#define M48T59_YEAR 0x1fff
-#define M48T59_MONTH 0x1ffe
-#define M48T59_MDAY 0x1ffd /* Day of Month */
-#define M48T59_WDAY 0x1ffc /* Day of Week */
+#define M48T59_YEAR 0xf
+#define M48T59_MONTH 0xe
+#define M48T59_MDAY 0xd /* Day of Month */
+#define M48T59_WDAY 0xc /* Day of Week */
#define M48T59_WDAY_CB 0x20 /* Century Bit */
#define M48T59_WDAY_CEB 0x10 /* Century Enable Bit */
-#define M48T59_HOUR 0x1ffb
-#define M48T59_MIN 0x1ffa
-#define M48T59_SEC 0x1ff9
-#define M48T59_CNTL 0x1ff8
+#define M48T59_HOUR 0xb
+#define M48T59_MIN 0xa
+#define M48T59_SEC 0x9
+#define M48T59_CNTL 0x8
#define M48T59_CNTL_READ 0x40
#define M48T59_CNTL_WRITE 0x80
-#define M48T59_WATCHDOG 0x1ff7
-#define M48T59_INTR 0x1ff6
+#define M48T59_WATCHDOG 0x7
+#define M48T59_INTR 0x6
#define M48T59_INTR_AFE 0x80 /* Alarm Interrupt Enable */
#define M48T59_INTR_ABE 0x20
-#define M48T59_ALARM_DATE 0x1ff5
-#define M48T59_ALARM_HOUR 0x1ff4
-#define M48T59_ALARM_MIN 0x1ff3
-#define M48T59_ALARM_SEC 0x1ff2
-#define M48T59_UNUSED 0x1ff1
-#define M48T59_FLAGS 0x1ff0
+#define M48T59_ALARM_DATE 0x5
+#define M48T59_ALARM_HOUR 0x4
+#define M48T59_ALARM_MIN 0x3
+#define M48T59_ALARM_SEC 0x2
+#define M48T59_UNUSED 0x1
+#define M48T59_FLAGS 0x0
#define M48T59_FLAGS_WDT 0x80 /* watchdog timer expired */
#define M48T59_FLAGS_AF 0x40 /* alarm */
#define M48T59_FLAGS_BF 0x10 /* low battery */
-#define M48T59_NVRAM_SIZE 0x1ff0
+#define M48T59RTC_TYPE_M48T59 0 /* to keep compatibility */
+#define M48T59RTC_TYPE_M48T02 1
+#define M48T59RTC_TYPE_M48T08 2
struct m48t59_plat_data {
- /* The method to access M48T59 registers,
- * NOTE: The 'ofs' should be 0x00~0x1fff
- */
+ /* The method to access M48T59 registers */
void (*write_byte)(struct device *dev, u32 ofs, u8 val);
unsigned char (*read_byte)(struct device *dev, u32 ofs);
+
+ int type; /* RTC model */
+
+ /* ioaddr mapped externally */
+ void __iomem *ioaddr;
+ /* offset to RTC registers, automatically set according to the type */
+ unsigned int offset;
};
#endif /* _LINUX_RTC_M48T59_H_ */
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 5d0819e..c226c7b 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -352,7 +352,7 @@
extern void arch_unmap_area(struct mm_struct *, unsigned long);
extern void arch_unmap_area_topdown(struct mm_struct *, unsigned long);
-#if NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS
+#if USE_SPLIT_PTLOCKS
/*
* The mm counters are not protected by its page_table_lock,
* so must be incremented atomically.
@@ -363,7 +363,7 @@
#define inc_mm_counter(mm, member) atomic_long_inc(&(mm)->_##member)
#define dec_mm_counter(mm, member) atomic_long_dec(&(mm)->_##member)
-#else /* NR_CPUS < CONFIG_SPLIT_PTLOCK_CPUS */
+#else /* !USE_SPLIT_PTLOCKS */
/*
* The mm counters are protected by its page_table_lock,
* so can be incremented directly.
@@ -374,7 +374,7 @@
#define inc_mm_counter(mm, member) (mm)->_##member++
#define dec_mm_counter(mm, member) (mm)->_##member--
-#endif /* NR_CPUS < CONFIG_SPLIT_PTLOCK_CPUS */
+#endif /* !USE_SPLIT_PTLOCKS */
#define get_mm_rss(mm) \
(get_mm_counter(mm, file_rss) + get_mm_counter(mm, anon_rss))
diff --git a/include/linux/serial.h b/include/linux/serial.h
index deb7143..1ea8d92 100644
--- a/include/linux/serial.h
+++ b/include/linux/serial.h
@@ -173,6 +173,22 @@
int reserved[9];
};
+/*
+ * Serial interface for controlling RS485 settings on chips with suitable
+ * support. Set with TIOCSRS485 and get with TIOCGRS485 if supported by your
+ * platform. The set function returns the new state, with any unsupported bits
+ * reverted appropriately.
+ */
+
+struct serial_rs485 {
+ __u32 flags; /* RS485 feature flags */
+#define SER_RS485_ENABLED (1 << 0)
+#define SER_RS485_RTS_ON_SEND (1 << 1)
+#define SER_RS485_RTS_AFTER_SEND (1 << 2)
+ __u32 delay_rts_before_send; /* Milliseconds */
+ __u32 padding[6]; /* Memory is cheap, new structs
+ are a royal PITA .. */
+};
#ifdef __KERNEL__
#include <linux/compiler.h>
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 3b2f6c0..e27f216 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -241,7 +241,7 @@
struct uart_port {
spinlock_t lock; /* port lock */
- unsigned int iobase; /* in/out[bwl] */
+ unsigned long iobase; /* in/out[bwl] */
unsigned char __iomem *membase; /* read/write[bwl] */
unsigned int irq; /* irq number */
unsigned int uartclk; /* base uart clock */
diff --git a/include/linux/termios.h b/include/linux/termios.h
index 4786628..2acd0c1 100644
--- a/include/linux/termios.h
+++ b/include/linux/termios.h
@@ -4,4 +4,19 @@
#include <linux/types.h>
#include <asm/termios.h>
+#define NFF 5
+
+struct termiox
+{
+ __u16 x_hflag;
+ __u16 x_cflag;
+ __u16 x_rflag[NFF];
+ __u16 x_sflag;
+};
+
+#define RTSXOFF 0x0001 /* RTS flow control on input */
+#define CTSXON 0x0002 /* CTS flow control on output */
+#define DTRXOFF 0x0004 /* DTR flow control on input */
+#define DSRXON 0x0008 /* DCD flow control on output */
+
#endif
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 0cbec74..3b8121d 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -23,7 +23,7 @@
*/
#define NR_UNIX98_PTY_DEFAULT 4096 /* Default maximum for Unix98 ptys */
#define NR_UNIX98_PTY_MAX (1 << MINORBITS) /* Absolute limit */
-#define NR_LDISCS 18
+#define NR_LDISCS 19
/* line disciplines */
#define N_TTY 0
@@ -45,6 +45,7 @@
#define N_HCI 15 /* Bluetooth HCI UART */
#define N_GIGASET_M101 16 /* Siemens Gigaset M101 serial DECT adapter */
#define N_SLCAN 17 /* Serial / USB serial CAN Adaptors */
+#define N_PPS 18 /* Pulse per Second */
/*
* This character is the same as _POSIX_VDISABLE: it cannot be used as
@@ -181,6 +182,7 @@
struct tty_port {
struct tty_struct *tty; /* Back pointer */
+ spinlock_t lock; /* Lock protecting tty field */
int blocked_open; /* Waiting to open */
int count; /* Usage count */
wait_queue_head_t open_wait; /* Open waiters */
@@ -208,6 +210,7 @@
struct tty_struct {
int magic;
+ struct kref kref;
struct tty_driver *driver;
const struct tty_operations *ops;
int index;
@@ -217,6 +220,7 @@
spinlock_t ctrl_lock;
/* Termios values are protected by the termios mutex */
struct ktermios *termios, *termios_locked;
+ struct termiox *termiox; /* May be NULL for unsupported */
char name[64];
struct pid *pgrp; /* Protected by ctrl lock */
struct pid *session;
@@ -310,6 +314,25 @@
extern void console_init(void);
extern int vcs_init(void);
+extern struct class *tty_class;
+
+/**
+ * tty_kref_get - get a tty reference
+ * @tty: tty device
+ *
+ * Return a new reference to a tty object. The caller must hold
+ * sufficient locks/counts to ensure that their existing reference cannot
+ * go away
+ */
+
+extern inline struct tty_struct *tty_kref_get(struct tty_struct *tty)
+{
+ if (tty)
+ kref_get(&tty->kref);
+ return tty;
+}
+extern void tty_kref_put(struct tty_struct *tty);
+
extern int tty_paranoia_check(struct tty_struct *tty, struct inode *inode,
const char *routine);
extern char *tty_name(struct tty_struct *tty, char *buf);
@@ -333,13 +356,15 @@
extern void tty_unthrottle(struct tty_struct *tty);
extern int tty_do_resize(struct tty_struct *tty, struct tty_struct *real_tty,
struct winsize *ws);
-
+extern void tty_shutdown(struct tty_struct *tty);
+extern void tty_free_termios(struct tty_struct *tty);
extern int is_current_pgrp_orphaned(void);
extern struct pid *tty_get_pgrp(struct tty_struct *tty);
extern int is_ignored(int sig);
extern int tty_signal(int sig, struct tty_struct *tty);
extern void tty_hangup(struct tty_struct *tty);
extern void tty_vhangup(struct tty_struct *tty);
+extern void tty_vhangup_self(void);
extern void tty_unhangup(struct file *filp);
extern int tty_hung_up_p(struct file *filp);
extern void do_SAK(struct tty_struct *tty);
@@ -347,6 +372,9 @@
extern void disassociate_ctty(int priv);
extern void no_tty(void);
extern void tty_flip_buffer_push(struct tty_struct *tty);
+extern void tty_buffer_free_all(struct tty_struct *tty);
+extern void tty_buffer_flush(struct tty_struct *tty);
+extern void tty_buffer_init(struct tty_struct *tty);
extern speed_t tty_get_baud_rate(struct tty_struct *tty);
extern speed_t tty_termios_baud_rate(struct ktermios *termios);
extern speed_t tty_termios_input_baud_rate(struct ktermios *termios);
@@ -372,6 +400,15 @@
extern dev_t tty_devnum(struct tty_struct *tty);
extern void proc_clear_tty(struct task_struct *p);
extern struct tty_struct *get_current_tty(void);
+extern void tty_default_fops(struct file_operations *fops);
+extern struct tty_struct *alloc_tty_struct(void);
+extern void free_tty_struct(struct tty_struct *tty);
+extern void initialize_tty_struct(struct tty_struct *tty,
+ struct tty_driver *driver, int idx);
+extern struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
+ int first_ok);
+extern void tty_release_dev(struct file *filp);
+extern int tty_init_termios(struct tty_struct *tty);
extern struct mutex tty_mutex;
@@ -382,6 +419,8 @@
extern void tty_port_init(struct tty_port *port);
extern int tty_port_alloc_xmit_buf(struct tty_port *port);
extern void tty_port_free_xmit_buf(struct tty_port *port);
+extern struct tty_struct *tty_port_tty_get(struct tty_port *port);
+extern void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty);
extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc);
extern int tty_unregister_ldisc(int disc);
@@ -427,7 +466,7 @@
#endif
/* tty_ioctl.c */
-extern int n_tty_ioctl(struct tty_struct *tty, struct file *file,
+extern int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg);
/* serial.c */
diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h
index 16d2794..78416b9 100644
--- a/include/linux/tty_driver.h
+++ b/include/linux/tty_driver.h
@@ -7,6 +7,28 @@
* defined; unless noted otherwise, they are optional, and can be
* filled in with a null pointer.
*
+ * struct tty_struct * (*lookup)(struct tty_driver *self, int idx)
+ *
+ * Return the tty device corresponding to idx, NULL if there is not
+ * one currently in use and an ERR_PTR value on error. Called under
+ * tty_mutex (for now!)
+ *
+ * Optional method. Default behaviour is to use the ttys array
+ *
+ * int (*install)(struct tty_driver *self, struct tty_struct *tty)
+ *
+ * Install a new tty into the tty driver internal tables. Used in
+ * conjunction with lookup and remove methods.
+ *
+ * Optional method. Default behaviour is to use the ttys array
+ *
+ * void (*remove)(struct tty_driver *self, struct tty_struct *tty)
+ *
+ * Remove a closed tty from the tty driver internal tables. Used in
+ * conjunction with lookup and remove methods.
+ *
+ * Optional method. Default behaviour is to use the ttys array
+ *
* int (*open)(struct tty_struct * tty, struct file * filp);
*
* This routine is called when a particular tty device is opened.
@@ -21,6 +43,11 @@
*
* Required method.
*
+ * void (*shutdown)(struct tty_struct * tty);
+ *
+ * This routine is called when a particular tty device is closed for
+ * the last time freeing up the resources.
+ *
* int (*write)(struct tty_struct * tty,
* const unsigned char *buf, int count);
*
@@ -180,6 +207,14 @@
* not force errors here if they are not resizable objects (eg a serial
* line). See tty_do_resize() if you need to wrap the standard method
* in your own logic - the usual case.
+ *
+ * void (*set_termiox)(struct tty_struct *tty, struct termiox *new);
+ *
+ * Called when the device receives a termiox based ioctl. Passes down
+ * the requested data from user space. This method will not be invoked
+ * unless the tty also has a valid tty->termiox pointer.
+ *
+ * Optional: Called under the termios lock
*/
#include <linux/fs.h>
@@ -190,8 +225,13 @@
struct tty_driver;
struct tty_operations {
+ struct tty_struct * (*lookup)(struct tty_driver *driver,
+ struct inode *inode, int idx);
+ int (*install)(struct tty_driver *driver, struct tty_struct *tty);
+ void (*remove)(struct tty_driver *driver, struct tty_struct *tty);
int (*open)(struct tty_struct * tty, struct file * filp);
void (*close)(struct tty_struct * tty, struct file * filp);
+ void (*shutdown)(struct tty_struct *tty);
int (*write)(struct tty_struct * tty,
const unsigned char *buf, int count);
int (*put_char)(struct tty_struct *tty, unsigned char ch);
@@ -220,6 +260,7 @@
unsigned int set, unsigned int clear);
int (*resize)(struct tty_struct *tty, struct tty_struct *real_tty,
struct winsize *ws);
+ int (*set_termiox)(struct tty_struct *tty, struct termiox *tnew);
#ifdef CONFIG_CONSOLE_POLL
int (*poll_init)(struct tty_driver *driver, int line, char *options);
int (*poll_get_char)(struct tty_driver *driver, int line);
@@ -229,6 +270,7 @@
struct tty_driver {
int magic; /* magic number for this structure */
+ struct kref kref; /* Reference management */
struct cdev cdev;
struct module *owner;
const char *driver_name;
@@ -242,7 +284,6 @@
short subtype; /* subtype of tty driver */
struct ktermios init_termios; /* Initial termios */
int flags; /* tty driver flags */
- int refcount; /* for loadable tty drivers */
struct proc_dir_entry *proc_entry; /* /proc fs entry */
struct tty_driver *other; /* only used for the PTY driver */
@@ -264,12 +305,19 @@
extern struct list_head tty_drivers;
-struct tty_driver *alloc_tty_driver(int lines);
-void put_tty_driver(struct tty_driver *driver);
-void tty_set_operations(struct tty_driver *driver,
+extern struct tty_driver *alloc_tty_driver(int lines);
+extern void put_tty_driver(struct tty_driver *driver);
+extern void tty_set_operations(struct tty_driver *driver,
const struct tty_operations *op);
extern struct tty_driver *tty_find_polling_driver(char *name, int *line);
+extern void tty_driver_kref_put(struct tty_driver *driver);
+extern inline struct tty_driver *tty_driver_kref_get(struct tty_driver *d)
+{
+ kref_get(&d->kref);
+ return d;
+}
+
/* tty driver magic number */
#define TTY_DRIVER_MAGIC 0x5402
diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h
new file mode 100644
index 0000000..5b88e36
--- /dev/null
+++ b/include/linux/usb/ehci_def.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2001-2002 by David Brownell
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __LINUX_USB_EHCI_DEF_H
+#define __LINUX_USB_EHCI_DEF_H
+
+/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */
+
+/* Section 2.2 Host Controller Capability Registers */
+struct ehci_caps {
+ /* these fields are specified as 8 and 16 bit registers,
+ * but some hosts can't perform 8 or 16 bit PCI accesses.
+ */
+ u32 hc_capbase;
+#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */
+#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */
+ u32 hcs_params; /* HCSPARAMS - offset 0x4 */
+#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */
+#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */
+#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */
+#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */
+#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */
+#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */
+#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */
+
+ u32 hcc_params; /* HCCPARAMS - offset 0x8 */
+#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */
+#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */
+#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */
+#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */
+#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/
+#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */
+ u8 portroute [8]; /* nibbles for routing - offset 0xC */
+} __attribute__ ((packed));
+
+
+/* Section 2.3 Host Controller Operational Registers */
+struct ehci_regs {
+
+ /* USBCMD: offset 0x00 */
+ u32 command;
+/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */
+#define CMD_PARK (1<<11) /* enable "park" on async qh */
+#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */
+#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */
+#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */
+#define CMD_ASE (1<<5) /* async schedule enable */
+#define CMD_PSE (1<<4) /* periodic schedule enable */
+/* 3:2 is periodic frame list size */
+#define CMD_RESET (1<<1) /* reset HC not bus */
+#define CMD_RUN (1<<0) /* start/stop HC */
+
+ /* USBSTS: offset 0x04 */
+ u32 status;
+#define STS_ASS (1<<15) /* Async Schedule Status */
+#define STS_PSS (1<<14) /* Periodic Schedule Status */
+#define STS_RECL (1<<13) /* Reclamation */
+#define STS_HALT (1<<12) /* Not running (any reason) */
+/* some bits reserved */
+ /* these STS_* flags are also intr_enable bits (USBINTR) */
+#define STS_IAA (1<<5) /* Interrupted on async advance */
+#define STS_FATAL (1<<4) /* such as some PCI access errors */
+#define STS_FLR (1<<3) /* frame list rolled over */
+#define STS_PCD (1<<2) /* port change detect */
+#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */
+#define STS_INT (1<<0) /* "normal" completion (short, ...) */
+
+ /* USBINTR: offset 0x08 */
+ u32 intr_enable;
+
+ /* FRINDEX: offset 0x0C */
+ u32 frame_index; /* current microframe number */
+ /* CTRLDSSEGMENT: offset 0x10 */
+ u32 segment; /* address bits 63:32 if needed */
+ /* PERIODICLISTBASE: offset 0x14 */
+ u32 frame_list; /* points to periodic list */
+ /* ASYNCLISTADDR: offset 0x18 */
+ u32 async_next; /* address of next async queue head */
+
+ u32 reserved [9];
+
+ /* CONFIGFLAG: offset 0x40 */
+ u32 configured_flag;
+#define FLAG_CF (1<<0) /* true: we'll support "high speed" */
+
+ /* PORTSC: offset 0x44 */
+ u32 port_status [0]; /* up to N_PORTS */
+/* 31:23 reserved */
+#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */
+#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */
+#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */
+/* 19:16 for port testing */
+#define PORT_LED_OFF (0<<14)
+#define PORT_LED_AMBER (1<<14)
+#define PORT_LED_GREEN (2<<14)
+#define PORT_LED_MASK (3<<14)
+#define PORT_OWNER (1<<13) /* true: companion hc owns this port */
+#define PORT_POWER (1<<12) /* true: has power (see PPC) */
+#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */
+/* 11:10 for detecting lowspeed devices (reset vs release ownership) */
+/* 9 reserved */
+#define PORT_RESET (1<<8) /* reset port */
+#define PORT_SUSPEND (1<<7) /* suspend port */
+#define PORT_RESUME (1<<6) /* resume it */
+#define PORT_OCC (1<<5) /* over current change */
+#define PORT_OC (1<<4) /* over current active */
+#define PORT_PEC (1<<3) /* port enable change */
+#define PORT_PE (1<<2) /* port enable */
+#define PORT_CSC (1<<1) /* connect status change */
+#define PORT_CONNECT (1<<0) /* device connected */
+#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC)
+} __attribute__ ((packed));
+
+#define USBMODE 0x68 /* USB Device mode */
+#define USBMODE_SDIS (1<<3) /* Stream disable */
+#define USBMODE_BE (1<<2) /* BE/LE endianness select */
+#define USBMODE_CM_HC (3<<0) /* host controller mode */
+#define USBMODE_CM_IDLE (0<<0) /* idle state */
+
+/* Appendix C, Debug port ... intended for use with special "debug devices"
+ * that can help if there's no serial console. (nonstandard enumeration.)
+ */
+struct ehci_dbg_port {
+ u32 control;
+#define DBGP_OWNER (1<<30)
+#define DBGP_ENABLED (1<<28)
+#define DBGP_DONE (1<<16)
+#define DBGP_INUSE (1<<10)
+#define DBGP_ERRCODE(x) (((x)>>7)&0x07)
+# define DBGP_ERR_BAD 1
+# define DBGP_ERR_SIGNAL 2
+#define DBGP_ERROR (1<<6)
+#define DBGP_GO (1<<5)
+#define DBGP_OUT (1<<4)
+#define DBGP_LEN(x) (((x)>>0)&0x0f)
+ u32 pids;
+#define DBGP_PID_GET(x) (((x)>>16)&0xff)
+#define DBGP_PID_SET(data, tok) (((data)<<8)|(tok))
+ u32 data03;
+ u32 data47;
+ u32 address;
+#define DBGP_EPADDR(dev, ep) (((dev)<<8)|(ep))
+} __attribute__ ((packed));
+
+#endif /* __LINUX_USB_EHCI_DEF_H */
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 303d93f..d4b0303 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -910,6 +910,8 @@
V4L2_MPEG_AUDIO_ENCODING_LAYER_1 = 0,
V4L2_MPEG_AUDIO_ENCODING_LAYER_2 = 1,
V4L2_MPEG_AUDIO_ENCODING_LAYER_3 = 2,
+ V4L2_MPEG_AUDIO_ENCODING_AAC = 3,
+ V4L2_MPEG_AUDIO_ENCODING_AC3 = 4,
};
#define V4L2_CID_MPEG_AUDIO_L1_BITRATE (V4L2_CID_MPEG_BASE+102)
enum v4l2_mpeg_audio_l1_bitrate {
@@ -988,12 +990,36 @@
V4L2_MPEG_AUDIO_CRC_CRC16 = 1,
};
#define V4L2_CID_MPEG_AUDIO_MUTE (V4L2_CID_MPEG_BASE+109)
+#define V4L2_CID_MPEG_AUDIO_AAC_BITRATE (V4L2_CID_MPEG_BASE+110)
+#define V4L2_CID_MPEG_AUDIO_AC3_BITRATE (V4L2_CID_MPEG_BASE+111)
+enum v4l2_mpeg_audio_ac3_bitrate {
+ V4L2_MPEG_AUDIO_AC3_BITRATE_32K = 0,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_40K = 1,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_48K = 2,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_56K = 3,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_64K = 4,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_80K = 5,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_96K = 6,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_112K = 7,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_128K = 8,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_160K = 9,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_192K = 10,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_224K = 11,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_256K = 12,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_320K = 13,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_384K = 14,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_448K = 15,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_512K = 16,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_576K = 17,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_640K = 18,
+};
/* MPEG video */
#define V4L2_CID_MPEG_VIDEO_ENCODING (V4L2_CID_MPEG_BASE+200)
enum v4l2_mpeg_video_encoding {
- V4L2_MPEG_VIDEO_ENCODING_MPEG_1 = 0,
- V4L2_MPEG_VIDEO_ENCODING_MPEG_2 = 1,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_1 = 0,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2 = 1,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC = 2,
};
#define V4L2_CID_MPEG_VIDEO_ASPECT (V4L2_CID_MPEG_BASE+201)
enum v4l2_mpeg_video_aspect {
diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h
index 1cbd0a7..2f11134 100644
--- a/include/linux/vt_kern.h
+++ b/include/linux/vt_kern.h
@@ -96,7 +96,7 @@
void reset_vc(struct vc_data *vc);
extern int unbind_con_driver(const struct consw *csw, int first, int last,
int deflt);
-int vty_init(void);
+int vty_init(const struct file_operations *console_fops);
/*
* vc_screen.c shares this temporary buffer with the console write code so that
diff --git a/include/media/ir-common.h b/include/media/ir-common.h
index b8e8aa9..38f2d93 100644
--- a/include/media/ir-common.h
+++ b/include/media/ir-common.h
@@ -25,6 +25,7 @@
#include <linux/input.h>
#include <linux/workqueue.h>
+#include <linux/interrupt.h>
#define IR_TYPE_RC5 1
#define IR_TYPE_PD 2 /* Pulse distance encoded IR */
@@ -85,6 +86,10 @@
u32 code; /* raw code under construction */
struct timeval base_time; /* time of last seen code */
int active; /* building raw code */
+
+ /* NEC decoding */
+ u32 nec_gpio;
+ struct tasklet_struct tlet;
};
void ir_input_init(struct input_dev *dev, struct ir_input_state *ir,
@@ -105,6 +110,7 @@
extern IR_KEYTAB_TYPE ir_codes_empty[IR_KEYTAB_SIZE];
extern IR_KEYTAB_TYPE ir_codes_avermedia[IR_KEYTAB_SIZE];
extern IR_KEYTAB_TYPE ir_codes_avermedia_dvbt[IR_KEYTAB_SIZE];
+extern IR_KEYTAB_TYPE ir_codes_avermedia_m135a[IR_KEYTAB_SIZE];
extern IR_KEYTAB_TYPE ir_codes_apac_viewcomp[IR_KEYTAB_SIZE];
extern IR_KEYTAB_TYPE ir_codes_pixelview[IR_KEYTAB_SIZE];
extern IR_KEYTAB_TYPE ir_codes_pixelview_new[IR_KEYTAB_SIZE];
@@ -139,6 +145,7 @@
extern IR_KEYTAB_TYPE ir_codes_budget_ci_old[IR_KEYTAB_SIZE];
extern IR_KEYTAB_TYPE ir_codes_asus_pc39[IR_KEYTAB_SIZE];
extern IR_KEYTAB_TYPE ir_codes_encore_enltv[IR_KEYTAB_SIZE];
+extern IR_KEYTAB_TYPE ir_codes_encore_enltv2[IR_KEYTAB_SIZE];
extern IR_KEYTAB_TYPE ir_codes_tt_1500[IR_KEYTAB_SIZE];
extern IR_KEYTAB_TYPE ir_codes_fusionhdtv_mce[IR_KEYTAB_SIZE];
extern IR_KEYTAB_TYPE ir_codes_behold[IR_KEYTAB_SIZE];
@@ -147,7 +154,9 @@
extern IR_KEYTAB_TYPE ir_codes_genius_tvgo_a11mce[IR_KEYTAB_SIZE];
extern IR_KEYTAB_TYPE ir_codes_powercolor_real_angel[IR_KEYTAB_SIZE];
extern IR_KEYTAB_TYPE ir_codes_avermedia_a16d[IR_KEYTAB_SIZE];
-
+extern IR_KEYTAB_TYPE ir_codes_encore_enltv_fm53[IR_KEYTAB_SIZE];
+extern IR_KEYTAB_TYPE ir_codes_real_audio_220_32_keys[IR_KEYTAB_SIZE];
+extern IR_KEYTAB_TYPE ir_codes_msi_tvanywhere_plus[IR_KEYTAB_SIZE];
#endif
/*
diff --git a/include/media/saa7115.h b/include/media/saa7115.h
index f677dfb..bab2127 100644
--- a/include/media/saa7115.h
+++ b/include/media/saa7115.h
@@ -1,5 +1,5 @@
/*
- saa7115.h - definition for saa7113/4/5 inputs and frequency flags
+ saa7115.h - definition for saa7111/3/4/5 inputs and frequency flags
Copyright (C) 2006 Hans Verkuil (hverkuil@xs4all.nl)
@@ -21,13 +21,13 @@
#ifndef _SAA7115_H_
#define _SAA7115_H_
-/* SAA7113/4/5 HW inputs */
+/* SAA7111/3/4/5 HW inputs */
#define SAA7115_COMPOSITE0 0
#define SAA7115_COMPOSITE1 1
#define SAA7115_COMPOSITE2 2
#define SAA7115_COMPOSITE3 3
-#define SAA7115_COMPOSITE4 4 /* not available for the saa7113 */
-#define SAA7115_COMPOSITE5 5 /* not available for the saa7113 */
+#define SAA7115_COMPOSITE4 4 /* not available for the saa7111/3 */
+#define SAA7115_COMPOSITE5 5 /* not available for the saa7111/3 */
#define SAA7115_SVIDEO0 6
#define SAA7115_SVIDEO1 7
#define SAA7115_SVIDEO2 8
@@ -42,8 +42,15 @@
#define SAA7115_FREQ_FL_CGCDIV (1 << 1) /* SA 3A[6], CGCDIV, SAA7115 only */
#define SAA7115_FREQ_FL_APLL (1 << 2) /* SA 3A[3], APLL, SAA7114/5 only */
-#define SAA7115_IPORT_ON 1
-#define SAA7115_IPORT_OFF 0
+#define SAA7115_IPORT_ON 1
+#define SAA7115_IPORT_OFF 0
+
+/* SAA7111 specific output flags */
+#define SAA7111_VBI_BYPASS 2
+#define SAA7111_FMT_YUV422 0x00
+#define SAA7111_FMT_RGB 0x40
+#define SAA7111_FMT_CCIR 0x80
+#define SAA7111_FMT_YUV411 0xc0
#endif
diff --git a/include/media/saa7146.h b/include/media/saa7146.h
index 2f68f4c..64a2ec7 100644
--- a/include/media/saa7146.h
+++ b/include/media/saa7146.h
@@ -30,7 +30,7 @@
#define DEBUG_VARIABLE saa7146_debug
#endif
-#define DEBUG_PROLOG printk("%s: %s(): ",KBUILD_MODNAME,__FUNCTION__)
+#define DEBUG_PROLOG printk("%s: %s(): ",KBUILD_MODNAME, __func__)
#define INFO(x) { printk("%s: ",KBUILD_MODNAME); printk x; }
#define ERR(x) { DEBUG_PROLOG; printk x; }
diff --git a/include/media/sh_mobile_ceu.h b/include/media/sh_mobile_ceu.h
index 234a471..b5dbefe 100644
--- a/include/media/sh_mobile_ceu.h
+++ b/include/media/sh_mobile_ceu.h
@@ -5,8 +5,6 @@
struct sh_mobile_ceu_info {
unsigned long flags; /* SOCAM_... */
- void (*enable_camera)(void);
- void (*disable_camera)(void);
};
#endif /* __ASM_SH_MOBILE_CEU_H__ */
diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h
index d548de3..c5de7bb 100644
--- a/include/media/soc_camera.h
+++ b/include/media/soc_camera.h
@@ -83,6 +83,9 @@
int bus_id;
/* GPIO number to switch between 8 and 10 bit modes */
unsigned int gpio;
+ /* Optional callbacks to power on or off and reset the sensor */
+ int (*power)(struct device *, int);
+ int (*reset)(struct device *);
};
static inline struct soc_camera_device *to_soc_camera_dev(struct device *dev)
diff --git a/include/media/tuner.h b/include/media/tuner.h
index 77068fc..67c1f51 100644
--- a/include/media/tuner.h
+++ b/include/media/tuner.h
@@ -122,6 +122,7 @@
#define TUNER_TDA9887 74 /* This tuner should be used only internally */
#define TUNER_TEA5761 75 /* Only FM Radio Tuner */
#define TUNER_XC5000 76 /* Xceive Silicon Tuner */
+#define TUNER_TCL_MF02GIP_5N 77 /* TCL MF02GIP_5N */
/* tv card specific */
#define TDA9887_PRESENT (1<<0)
@@ -178,7 +179,7 @@
unsigned int type; /* Tuner type */
unsigned int mode_mask; /* Allowed tuner modes */
unsigned int config; /* configuraion for more complex tuners */
- int (*tuner_callback) (void *dev, int command,int arg);
+ int (*tuner_callback) (void *dev, int component, int cmd, int arg);
};
#endif /* __KERNEL__ */
diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h
index 41b509b..d73a8e9 100644
--- a/include/media/v4l2-chip-ident.h
+++ b/include/media/v4l2-chip-ident.h
@@ -72,6 +72,10 @@
/* module cs5345: just ident 5345 */
V4L2_IDENT_CS5345 = 5345,
+ /* module saa6752hs: reserved range 6750-6759 */
+ V4L2_IDENT_SAA6752HS = 6752,
+ V4L2_IDENT_SAA6752HS_AC3 = 6753,
+
/* module wm8739: just ident 8739 */
V4L2_IDENT_WM8739 = 8739,
@@ -161,6 +165,7 @@
/* Micron CMOS sensor chips: 45000-45099 */
V4L2_IDENT_MT9M001C12ST = 45000,
V4L2_IDENT_MT9M001C12STM = 45005,
+ V4L2_IDENT_MT9M111 = 45007,
V4L2_IDENT_MT9V022IX7ATC = 45010, /* No way to detect "normal" I77ATx */
V4L2_IDENT_MT9V022IX7ATM = 45015, /* and "lead free" IA7ATx chips */
};
diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
index 07d3a9a..2f8719a 100644
--- a/include/media/v4l2-common.h
+++ b/include/media/v4l2-common.h
@@ -76,11 +76,14 @@
int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl,
const char **menu_items);
+const char *v4l2_ctrl_get_name(u32 id);
const char **v4l2_ctrl_get_menu(u32 id);
int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def);
int v4l2_ctrl_query_fill_std(struct v4l2_queryctrl *qctrl);
int v4l2_ctrl_query_menu(struct v4l2_querymenu *qmenu,
struct v4l2_queryctrl *qctrl, const char **menu_items);
+#define V4L2_CTRL_MENU_IDS_END (0xffffffff)
+int v4l2_ctrl_query_menu_valid_items(struct v4l2_querymenu *qmenu, const u32 *ids);
u32 v4l2_ctrl_next(const u32 * const *ctrl_classes, u32 id);
/* ------------------------------------------------------------------------- */
@@ -222,18 +225,22 @@
An extra flags field allows device specific configuration regarding
clock frequency dividers, etc. If not used, then set flags to 0.
If the frequency is not supported, then -EINVAL is returned. */
-#define VIDIOC_INT_S_CRYSTAL_FREQ _IOW ('d', 113, struct v4l2_crystal_freq)
+#define VIDIOC_INT_S_CRYSTAL_FREQ _IOW('d', 113, struct v4l2_crystal_freq)
/* Initialize the sensor registors to some sort of reasonable
default values. */
-#define VIDIOC_INT_INIT _IOW ('d', 114, u32)
+#define VIDIOC_INT_INIT _IOW('d', 114, u32)
/* Set v4l2_std_id for video OUTPUT devices. This is ignored by
video input devices. */
-#define VIDIOC_INT_S_STD_OUTPUT _IOW ('d', 115, v4l2_std_id)
+#define VIDIOC_INT_S_STD_OUTPUT _IOW('d', 115, v4l2_std_id)
/* Get v4l2_std_id for video OUTPUT devices. This is ignored by
video input devices. */
-#define VIDIOC_INT_G_STD_OUTPUT _IOW ('d', 116, v4l2_std_id)
+#define VIDIOC_INT_G_STD_OUTPUT _IOW('d', 116, v4l2_std_id)
+
+/* Set GPIO pins. Very simple right now, might need to be extended with
+ a v4l2_gpio struct if a direction is also needed. */
+#define VIDIOC_INT_S_GPIO _IOW('d', 117, u32)
#endif /* V4L2_COMMON_H_ */
diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h
index 2745e1a..a0a6b41 100644
--- a/include/media/v4l2-dev.h
+++ b/include/media/v4l2-dev.h
@@ -9,30 +9,20 @@
#ifndef _V4L2_DEV_H
#define _V4L2_DEV_H
-#define OBSOLETE_DEVDATA 1 /* to be removed soon */
-
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/device.h>
+#include <linux/cdev.h>
#include <linux/mutex.h>
-#include <linux/compiler.h> /* need __user */
#include <linux/videodev2.h>
#define VIDEO_MAJOR 81
-/* Minor device allocation */
-#define MINOR_VFL_TYPE_GRABBER_MIN 0
-#define MINOR_VFL_TYPE_GRABBER_MAX 63
-#define MINOR_VFL_TYPE_RADIO_MIN 64
-#define MINOR_VFL_TYPE_RADIO_MAX 127
-#define MINOR_VFL_TYPE_VTX_MIN 192
-#define MINOR_VFL_TYPE_VTX_MAX 223
-#define MINOR_VFL_TYPE_VBI_MIN 224
-#define MINOR_VFL_TYPE_VBI_MAX 255
#define VFL_TYPE_GRABBER 0
#define VFL_TYPE_VBI 1
#define VFL_TYPE_RADIO 2
#define VFL_TYPE_VTX 3
+#define VFL_TYPE_MAX 4
struct v4l2_ioctl_callbacks;
@@ -49,12 +39,15 @@
/* sysfs */
struct device dev; /* v4l device */
+ struct cdev cdev; /* character device */
+ void (*cdev_release)(struct kobject *kobj);
struct device *parent; /* device parent */
/* device info */
char name[32];
int vfl_type;
int minor;
+ u16 num;
/* attribute to differentiate multiple indices on one physical device */
int index;
@@ -69,50 +62,50 @@
/* ioctl callbacks */
const struct v4l2_ioctl_ops *ioctl_ops;
-
-#ifdef OBSOLETE_DEVDATA /* to be removed soon */
- /* dev->driver_data will be used instead some day.
- * Use the video_{get|set}_drvdata() helper functions,
- * so the switch over will be transparent for you.
- * Or use {pci|usb}_{get|set}_drvdata() directly. */
- void *priv;
-#endif
-
- /* for videodev.c internal usage -- please don't touch */
- int users; /* video_exclusive_{open|close} ... */
- struct mutex lock; /* ... helper function uses these */
};
-/* Class-dev to video-device */
+/* dev to video-device */
#define to_video_device(cd) container_of(cd, struct video_device, dev)
-/* Version 2 functions */
-extern int video_register_device(struct video_device *vfd, int type, int nr);
-int video_register_device_index(struct video_device *vfd, int type, int nr,
- int index);
-void video_unregister_device(struct video_device *);
+/* Register and unregister devices. Note that if video_register_device fails,
+ the release() callback of the video_device structure is *not* called, so
+ the caller is responsible for freeing any data. Usually that means that
+ you call video_device_release() on failure. */
+int __must_check video_register_device(struct video_device *vfd, int type, int nr);
+int __must_check video_register_device_index(struct video_device *vfd,
+ int type, int nr, int index);
+void video_unregister_device(struct video_device *vfd);
-/* helper functions to alloc / release struct video_device, the
- later can be used for video_device->release() */
-struct video_device *video_device_alloc(void);
+/* helper functions to alloc/release struct video_device, the
+ latter can also be used for video_device->release(). */
+struct video_device * __must_check video_device_alloc(void);
+
+/* this release function frees the vfd pointer */
void video_device_release(struct video_device *vfd);
-#ifdef OBSOLETE_DEVDATA /* to be removed soon */
+/* this release function does nothing, use when the video_device is a
+ static global struct. Note that having a static video_device is
+ a dubious construction at best. */
+void video_device_release_empty(struct video_device *vfd);
+
/* helper functions to access driver private data. */
static inline void *video_get_drvdata(struct video_device *dev)
{
- return dev->priv;
+ return dev_get_drvdata(&dev->dev);
}
static inline void video_set_drvdata(struct video_device *dev, void *data)
{
- dev->priv = data;
+ dev_set_drvdata(&dev->dev, data);
}
-/* Obsolete stuff - Still needed for radio devices and obsolete drivers */
-extern struct video_device* video_devdata(struct file*);
-extern int video_exclusive_open(struct inode *inode, struct file *file);
-extern int video_exclusive_release(struct inode *inode, struct file *file);
-#endif
+struct video_device *video_devdata(struct file *file);
+
+/* Combine video_get_drvdata and video_devdata as this is
+ used very often. */
+static inline void *video_drvdata(struct file *file)
+{
+ return video_get_drvdata(video_devdata(file));
+}
#endif /* _V4L2_DEV_H */
diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h
index dc64046..0bef03a 100644
--- a/include/media/v4l2-ioctl.h
+++ b/include/media/v4l2-ioctl.h
@@ -39,11 +39,6 @@
struct v4l2_fmtdesc *f);
int (*vidioc_enum_fmt_vid_out) (struct file *file, void *fh,
struct v4l2_fmtdesc *f);
-#if 1
- /* deprecated, will be removed in 2.6.28 */
- int (*vidioc_enum_fmt_vbi_cap) (struct file *file, void *fh,
- struct v4l2_fmtdesc *f);
-#endif
int (*vidioc_enum_fmt_type_private)(struct file *file, void *fh,
struct v4l2_fmtdesc *f);
diff --git a/include/net/cipso_ipv4.h b/include/net/cipso_ipv4.h
index a6bb945..9909774 100644
--- a/include/net/cipso_ipv4.h
+++ b/include/net/cipso_ipv4.h
@@ -40,11 +40,12 @@
#include <linux/net.h>
#include <linux/skbuff.h>
#include <net/netlabel.h>
+#include <asm/atomic.h>
/* known doi values */
#define CIPSO_V4_DOI_UNKNOWN 0x00000000
-/* tag types */
+/* standard tag types */
#define CIPSO_V4_TAG_INVALID 0
#define CIPSO_V4_TAG_RBITMAP 1
#define CIPSO_V4_TAG_ENUM 2
@@ -52,10 +53,14 @@
#define CIPSO_V4_TAG_PBITMAP 6
#define CIPSO_V4_TAG_FREEFORM 7
+/* non-standard tag types (tags > 127) */
+#define CIPSO_V4_TAG_LOCAL 128
+
/* doi mapping types */
#define CIPSO_V4_MAP_UNKNOWN 0
-#define CIPSO_V4_MAP_STD 1
+#define CIPSO_V4_MAP_TRANS 1
#define CIPSO_V4_MAP_PASS 2
+#define CIPSO_V4_MAP_LOCAL 3
/* limits */
#define CIPSO_V4_MAX_REM_LVLS 255
@@ -79,10 +84,9 @@
} map;
u8 tags[CIPSO_V4_TAG_MAXCNT];
- u32 valid;
+ atomic_t refcount;
struct list_head list;
struct rcu_head rcu;
- struct list_head dom_list;
};
/* Standard CIPSO mapping table */
@@ -128,25 +132,26 @@
#ifdef CONFIG_NETLABEL
int cipso_v4_doi_add(struct cipso_v4_doi *doi_def);
-int cipso_v4_doi_remove(u32 doi,
- struct netlbl_audit *audit_info,
- void (*callback) (struct rcu_head * head));
+void cipso_v4_doi_free(struct cipso_v4_doi *doi_def);
+int cipso_v4_doi_remove(u32 doi, struct netlbl_audit *audit_info);
struct cipso_v4_doi *cipso_v4_doi_getdef(u32 doi);
+void cipso_v4_doi_putdef(struct cipso_v4_doi *doi_def);
int cipso_v4_doi_walk(u32 *skip_cnt,
int (*callback) (struct cipso_v4_doi *doi_def, void *arg),
void *cb_arg);
-int cipso_v4_doi_domhsh_add(struct cipso_v4_doi *doi_def, const char *domain);
-int cipso_v4_doi_domhsh_remove(struct cipso_v4_doi *doi_def,
- const char *domain);
#else
static inline int cipso_v4_doi_add(struct cipso_v4_doi *doi_def)
{
return -ENOSYS;
}
+static inline void cipso_v4_doi_free(struct cipso_v4_doi *doi_def)
+{
+ return;
+}
+
static inline int cipso_v4_doi_remove(u32 doi,
- struct netlbl_audit *audit_info,
- void (*callback) (struct rcu_head * head))
+ struct netlbl_audit *audit_info)
{
return 0;
}
@@ -206,10 +211,15 @@
int cipso_v4_sock_setattr(struct sock *sk,
const struct cipso_v4_doi *doi_def,
const struct netlbl_lsm_secattr *secattr);
+void cipso_v4_sock_delattr(struct sock *sk);
int cipso_v4_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr);
+int cipso_v4_skbuff_setattr(struct sk_buff *skb,
+ const struct cipso_v4_doi *doi_def,
+ const struct netlbl_lsm_secattr *secattr);
+int cipso_v4_skbuff_delattr(struct sk_buff *skb);
int cipso_v4_skbuff_getattr(const struct sk_buff *skb,
struct netlbl_lsm_secattr *secattr);
-int cipso_v4_validate(unsigned char **option);
+int cipso_v4_validate(const struct sk_buff *skb, unsigned char **option);
#else
static inline void cipso_v4_error(struct sk_buff *skb,
int error,
@@ -225,19 +235,36 @@
return -ENOSYS;
}
+static inline void cipso_v4_sock_delattr(struct sock *sk)
+{
+}
+
static inline int cipso_v4_sock_getattr(struct sock *sk,
struct netlbl_lsm_secattr *secattr)
{
return -ENOSYS;
}
+static inline int cipso_v4_skbuff_setattr(struct sk_buff *skb,
+ const struct cipso_v4_doi *doi_def,
+ const struct netlbl_lsm_secattr *secattr)
+{
+ return -ENOSYS;
+}
+
+static inline int cipso_v4_skbuff_delattr(struct sk_buff *skb)
+{
+ return -ENOSYS;
+}
+
static inline int cipso_v4_skbuff_getattr(const struct sk_buff *skb,
struct netlbl_lsm_secattr *secattr)
{
return -ENOSYS;
}
-static inline int cipso_v4_validate(unsigned char **option)
+static inline int cipso_v4_validate(const struct sk_buff *skb,
+ unsigned char **option)
{
return -ENOSYS;
}
diff --git a/include/net/netlabel.h b/include/net/netlabel.h
index e4d2d6b..17c442a 100644
--- a/include/net/netlabel.h
+++ b/include/net/netlabel.h
@@ -9,7 +9,7 @@
*/
/*
- * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
*
* 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
@@ -72,8 +72,10 @@
/* NetLabel NETLINK protocol version
* 1: initial version
* 2: added static labels for unlabeled connections
+ * 3: network selectors added to the NetLabel/LSM domain mapping and the
+ * CIPSO_V4_MAP_LOCAL CIPSO mapping was added
*/
-#define NETLBL_PROTO_VERSION 2
+#define NETLBL_PROTO_VERSION 3
/* NetLabel NETLINK types/families */
#define NETLBL_NLTYPE_NONE 0
@@ -87,6 +89,8 @@
#define NETLBL_NLTYPE_CIPSOV6_NAME "NLBL_CIPSOv6"
#define NETLBL_NLTYPE_UNLABELED 5
#define NETLBL_NLTYPE_UNLABELED_NAME "NLBL_UNLBL"
+#define NETLBL_NLTYPE_ADDRSELECT 6
+#define NETLBL_NLTYPE_ADDRSELECT_NAME "NLBL_ADRSEL"
/*
* NetLabel - Kernel API for accessing the network packet label mappings.
@@ -200,7 +204,7 @@
u32 type;
char *domain;
struct netlbl_lsm_cache *cache;
- union {
+ struct {
struct {
struct netlbl_lsm_secattr_catmap *cat;
u32 lvl;
@@ -352,12 +356,9 @@
int netlbl_cfg_map_del(const char *domain, struct netlbl_audit *audit_info);
int netlbl_cfg_unlbl_add_map(const char *domain,
struct netlbl_audit *audit_info);
-int netlbl_cfg_cipsov4_add(struct cipso_v4_doi *doi_def,
- struct netlbl_audit *audit_info);
int netlbl_cfg_cipsov4_add_map(struct cipso_v4_doi *doi_def,
const char *domain,
struct netlbl_audit *audit_info);
-int netlbl_cfg_cipsov4_del(u32 doi, struct netlbl_audit *audit_info);
/*
* LSM security attribute operations
@@ -380,12 +381,19 @@
int netlbl_enabled(void);
int netlbl_sock_setattr(struct sock *sk,
const struct netlbl_lsm_secattr *secattr);
+void netlbl_sock_delattr(struct sock *sk);
int netlbl_sock_getattr(struct sock *sk,
struct netlbl_lsm_secattr *secattr);
+int netlbl_conn_setattr(struct sock *sk,
+ struct sockaddr *addr,
+ const struct netlbl_lsm_secattr *secattr);
+int netlbl_skbuff_setattr(struct sk_buff *skb,
+ u16 family,
+ const struct netlbl_lsm_secattr *secattr);
int netlbl_skbuff_getattr(const struct sk_buff *skb,
u16 family,
struct netlbl_lsm_secattr *secattr);
-void netlbl_skbuff_err(struct sk_buff *skb, int error);
+void netlbl_skbuff_err(struct sk_buff *skb, int error, int gateway);
/*
* LSM label mapping cache operations
@@ -404,22 +412,12 @@
{
return -ENOSYS;
}
-static inline int netlbl_cfg_cipsov4_add(struct cipso_v4_doi *doi_def,
- struct netlbl_audit *audit_info)
-{
- return -ENOSYS;
-}
static inline int netlbl_cfg_cipsov4_add_map(struct cipso_v4_doi *doi_def,
const char *domain,
struct netlbl_audit *audit_info)
{
return -ENOSYS;
}
-static inline int netlbl_cfg_cipsov4_del(u32 doi,
- struct netlbl_audit *audit_info)
-{
- return -ENOSYS;
-}
static inline int netlbl_secattr_catmap_walk(
struct netlbl_lsm_secattr_catmap *catmap,
u32 offset)
@@ -456,18 +454,35 @@
{
return -ENOSYS;
}
+static inline void netlbl_sock_delattr(struct sock *sk)
+{
+}
static inline int netlbl_sock_getattr(struct sock *sk,
struct netlbl_lsm_secattr *secattr)
{
return -ENOSYS;
}
+static inline int netlbl_conn_setattr(struct sock *sk,
+ struct sockaddr *addr,
+ const struct netlbl_lsm_secattr *secattr)
+{
+ return -ENOSYS;
+}
+static inline int netlbl_skbuff_setattr(struct sk_buff *skb,
+ u16 family,
+ const struct netlbl_lsm_secattr *secattr)
+{
+ return -ENOSYS;
+}
static inline int netlbl_skbuff_getattr(const struct sk_buff *skb,
u16 family,
struct netlbl_lsm_secattr *secattr)
{
return -ENOSYS;
}
-static inline void netlbl_skbuff_err(struct sk_buff *skb, int error)
+static inline void netlbl_skbuff_err(struct sk_buff *skb,
+ int error,
+ int gateway)
{
return;
}
diff --git a/include/pcmcia/ciscode.h b/include/pcmcia/ciscode.h
index ad6e278..b417985 100644
--- a/include/pcmcia/ciscode.h
+++ b/include/pcmcia/ciscode.h
@@ -119,7 +119,7 @@
#define MANFID_TOSHIBA 0x0098
-#define MANFID_UNGERMANN 0x02c0
+#define MANFID_UNGERMANN 0x02c0
#define MANFID_XIRCOM 0x0105
diff --git a/include/pcmcia/cistpl.h b/include/pcmcia/cistpl.h
index e2e10c1..cfdd5af 100644
--- a/include/pcmcia/cistpl.h
+++ b/include/pcmcia/cistpl.h
@@ -573,44 +573,6 @@
#define TUPLE_RETURN_LINK 0x01
#define TUPLE_RETURN_COMMON 0x02
-/* For ValidateCIS */
-typedef struct cisinfo_t {
- u_int Chains;
-} cisinfo_t;
-
#define CISTPL_MAX_CIS_SIZE 0x200
-/* For ReplaceCIS */
-typedef struct cisdump_t {
- u_int Length;
- cisdata_t Data[CISTPL_MAX_CIS_SIZE];
-} cisdump_t;
-
-
-int pcmcia_replace_cis(struct pcmcia_socket *s, cisdump_t *cis);
-
-/* don't use outside of PCMCIA core yet */
-int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int func, tuple_t *tuple);
-int pccard_get_first_tuple(struct pcmcia_socket *s, unsigned int function, tuple_t *tuple);
-int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple);
-int pccard_parse_tuple(tuple_t *tuple, cisparse_t *parse);
-
-int pccard_validate_cis(struct pcmcia_socket *s, unsigned int function, unsigned int *count);
-
-/* ... but use these wrappers instead */
-#define pcmcia_get_first_tuple(p_dev, tuple) \
- pccard_get_first_tuple(p_dev->socket, p_dev->func, tuple)
-
-#define pcmcia_get_next_tuple(p_dev, tuple) \
- pccard_get_next_tuple(p_dev->socket, p_dev->func, tuple)
-
-#define pcmcia_get_tuple_data(p_dev, tuple) \
- pccard_get_tuple_data(p_dev->socket, tuple)
-
-#define pcmcia_parse_tuple(p_dev, tuple, parse) \
- pccard_parse_tuple(tuple, parse)
-
-#define pcmcia_validate_cis(p_dev, info) \
- pccard_validate_cis(p_dev->socket, p_dev->func, info)
-
#endif /* LINUX_CISTPL_H */
diff --git a/include/pcmcia/cs.h b/include/pcmcia/cs.h
index 45d84b2..904468a 100644
--- a/include/pcmcia/cs.h
+++ b/include/pcmcia/cs.h
@@ -28,72 +28,16 @@
#define CS_WRITE 2
/* for AdjustResourceInfo */
-typedef struct adjust_t {
- u_int Action;
- u_int Resource;
- u_int Attributes;
- union {
- struct memory {
- u_long Base;
- u_long Size;
- } memory;
- struct io {
- ioaddr_t BasePort;
- ioaddr_t NumPorts;
- u_int IOAddrLines;
- } io;
- struct irq {
- u_int IRQ;
- } irq;
- } resource;
-} adjust_t;
-
/* Action field */
#define REMOVE_MANAGED_RESOURCE 1
#define ADD_MANAGED_RESOURCE 2
-#define GET_FIRST_MANAGED_RESOURCE 3
-#define GET_NEXT_MANAGED_RESOURCE 4
-/* Resource field */
-#define RES_MEMORY_RANGE 1
-#define RES_IO_RANGE 2
-#define RES_IRQ 3
-/* Attribute field */
-#define RES_IRQ_TYPE 0x03
-#define RES_IRQ_TYPE_EXCLUSIVE 0
-#define RES_IRQ_TYPE_TIME 1
-#define RES_IRQ_TYPE_DYNAMIC 2
-#define RES_IRQ_CSC 0x04
-#define RES_SHARED 0x08
-#define RES_RESERVED 0x10
-#define RES_ALLOCATED 0x20
-#define RES_REMOVED 0x40
+
typedef struct event_callback_args_t {
struct pcmcia_device *client_handle;
void *client_data;
} event_callback_args_t;
-/* for GetConfigurationInfo */
-typedef struct config_info_t {
- u_char Function;
- u_int Attributes;
- u_int Vcc, Vpp1, Vpp2;
- u_int IntType;
- u_int ConfigBase;
- u_char Status, Pin, Copy, Option, ExtStatus;
- u_int Present;
- u_int CardValues;
- u_int AssignedIRQ;
- u_int IRQAttributes;
- ioaddr_t BasePort1;
- ioaddr_t NumPorts1;
- u_int Attributes1;
- ioaddr_t BasePort2;
- ioaddr_t NumPorts2;
- u_int Attributes2;
- u_int IOAddrLines;
-} config_info_t;
-
/* For CardValues field */
#define CV_OPTION_VALUE 0x01
#define CV_STATUS_VALUE 0x02
@@ -257,22 +201,6 @@
#define WIN_BAR_MASK 0xe000
#define WIN_BAR_SHIFT 13
-/* Attributes for RegisterClient -- UNUSED -- */
-#define INFO_MASTER_CLIENT 0x01
-#define INFO_IO_CLIENT 0x02
-#define INFO_MTD_CLIENT 0x04
-#define INFO_MEM_CLIENT 0x08
-#define MAX_NUM_CLIENTS 3
-
-#define INFO_CARD_SHARE 0x10
-#define INFO_CARD_EXCL 0x20
-
-typedef struct cs_status_t {
- u_char Function;
- event_t CardState;
- event_t SocketState;
-} cs_status_t;
-
typedef struct error_info_t {
int func;
int retcode;
@@ -308,95 +236,4 @@
#define CS_EVENT_3VCARD 0x200000
#define CS_EVENT_XVCARD 0x400000
-/* Return codes */
-#define CS_SUCCESS 0x00
-#define CS_BAD_ADAPTER 0x01
-#define CS_BAD_ATTRIBUTE 0x02
-#define CS_BAD_BASE 0x03
-#define CS_BAD_EDC 0x04
-#define CS_BAD_IRQ 0x06
-#define CS_BAD_OFFSET 0x07
-#define CS_BAD_PAGE 0x08
-#define CS_READ_FAILURE 0x09
-#define CS_BAD_SIZE 0x0a
-#define CS_BAD_SOCKET 0x0b
-#define CS_BAD_TYPE 0x0d
-#define CS_BAD_VCC 0x0e
-#define CS_BAD_VPP 0x0f
-#define CS_BAD_WINDOW 0x11
-#define CS_WRITE_FAILURE 0x12
-#define CS_NO_CARD 0x14
-#define CS_UNSUPPORTED_FUNCTION 0x15
-#define CS_UNSUPPORTED_MODE 0x16
-#define CS_BAD_SPEED 0x17
-#define CS_BUSY 0x18
-#define CS_GENERAL_FAILURE 0x19
-#define CS_WRITE_PROTECTED 0x1a
-#define CS_BAD_ARG_LENGTH 0x1b
-#define CS_BAD_ARGS 0x1c
-#define CS_CONFIGURATION_LOCKED 0x1d
-#define CS_IN_USE 0x1e
-#define CS_NO_MORE_ITEMS 0x1f
-#define CS_OUT_OF_RESOURCE 0x20
-#define CS_BAD_HANDLE 0x21
-
-#define CS_BAD_TUPLE 0x40
-
-#ifdef __KERNEL__
-
-/*
- * The main Card Services entry point
- */
-
-enum service {
- AccessConfigurationRegister, AddSocketServices,
- AdjustResourceInfo, CheckEraseQueue, CloseMemory, CopyMemory,
- DeregisterClient, DeregisterEraseQueue, GetCardServicesInfo,
- GetClientInfo, GetConfigurationInfo, GetEventMask,
- GetFirstClient, GetFirstPartion, GetFirstRegion, GetFirstTuple,
- GetNextClient, GetNextPartition, GetNextRegion, GetNextTuple,
- GetStatus, GetTupleData, MapLogSocket, MapLogWindow, MapMemPage,
- MapPhySocket, MapPhyWindow, ModifyConfiguration, ModifyWindow,
- OpenMemory, ParseTuple, ReadMemory, RegisterClient,
- RegisterEraseQueue, RegisterMTD, RegisterTimer,
- ReleaseConfiguration, ReleaseExclusive, ReleaseIO, ReleaseIRQ,
- ReleaseSocketMask, ReleaseWindow, ReplaceSocketServices,
- RequestConfiguration, RequestExclusive, RequestIO, RequestIRQ,
- RequestSocketMask, RequestWindow, ResetCard, ReturnSSEntry,
- SetEventMask, SetRegion, ValidateCIS, VendorSpecific,
- WriteMemory, BindDevice, BindMTD, ReportError,
- SuspendCard, ResumeCard, EjectCard, InsertCard, ReplaceCIS,
- GetFirstWindow, GetNextWindow, GetMemPage
-};
-
-struct pcmcia_socket;
-
-int pcmcia_access_configuration_register(struct pcmcia_device *p_dev, conf_reg_t *reg);
-int pcmcia_get_configuration_info(struct pcmcia_device *p_dev, config_info_t *config);
-int pcmcia_get_mem_page(window_handle_t win, memreq_t *req);
-int pcmcia_map_mem_page(window_handle_t win, memreq_t *req);
-int pcmcia_modify_configuration(struct pcmcia_device *p_dev, modconf_t *mod);
-int pcmcia_release_window(window_handle_t win);
-int pcmcia_request_configuration(struct pcmcia_device *p_dev, config_req_t *req);
-int pcmcia_request_io(struct pcmcia_device *p_dev, io_req_t *req);
-int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req);
-int pcmcia_request_window(struct pcmcia_device **p_dev, win_req_t *req, window_handle_t *wh);
-int pcmcia_suspend_card(struct pcmcia_socket *skt);
-int pcmcia_resume_card(struct pcmcia_socket *skt);
-int pcmcia_eject_card(struct pcmcia_socket *skt);
-int pcmcia_insert_card(struct pcmcia_socket *skt);
-int pccard_reset_card(struct pcmcia_socket *skt);
-
-struct pcmcia_device * pcmcia_dev_present(struct pcmcia_device *p_dev);
-void pcmcia_disable_device(struct pcmcia_device *p_dev);
-
-struct pcmcia_socket * pcmcia_get_socket(struct pcmcia_socket *skt);
-void pcmcia_put_socket(struct pcmcia_socket *skt);
-
-/* compatibility functions */
-#define pcmcia_reset_card(p_dev, req) \
- pccard_reset_card(p_dev->socket)
-
-#endif /* __KERNEL__ */
-
#endif /* _LINUX_CS_H */
diff --git a/include/pcmcia/cs_types.h b/include/pcmcia/cs_types.h
index f402a0f..315965a 100644
--- a/include/pcmcia/cs_types.h
+++ b/include/pcmcia/cs_types.h
@@ -21,14 +21,6 @@
#include <sys/types.h>
#endif
-#if defined(__arm__) || defined(__mips__) || defined(__avr32__) || \
- defined(__bfin__)
-/* This (ioaddr_t) is exposed to userspace & hence cannot be changed. */
-typedef u_int ioaddr_t;
-#else
-typedef u_short ioaddr_t;
-#endif
-
typedef u_short socket_t;
typedef u_int event_t;
typedef u_char cisdata_t;
diff --git a/include/pcmcia/device_id.h b/include/pcmcia/device_id.h
index e04e0b0..c33ea08 100644
--- a/include/pcmcia/device_id.h
+++ b/include/pcmcia/device_id.h
@@ -1,10 +1,19 @@
/*
- * Copyright (2003-2004) Dominik Brodowski <linux@brodo.de>
- * David Woodhouse
+ * device_id.h -- PCMCIA driver matching helpers
*
- * License: GPL v2
+ * 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.
+ *
+ * (C) 2003 - 2004 David Woodhouse
+ * (C) 2003 - 2004 Dominik Brodowski
*/
+#ifndef _LINUX_PCMCIA_DEVICE_ID_H
+#define _LINUX_PCMCIA_DEVICE_ID_H
+
+#ifdef __KERNEL__
+
#define PCMCIA_DEVICE_MANF_CARD(manf, card) { \
.match_flags = PCMCIA_DEV_ID_MATCH_MANF_ID| \
PCMCIA_DEV_ID_MATCH_CARD_ID, \
@@ -256,3 +265,6 @@
#define PCMCIA_DEVICE_NULL { .match_flags = 0, }
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_PCMCIA_DEVICE_ID_H */
diff --git a/include/pcmcia/ds.h b/include/pcmcia/ds.h
index b316027..a2be80b 100644
--- a/include/pcmcia/ds.h
+++ b/include/pcmcia/ds.h
@@ -10,7 +10,7 @@
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
*
* (C) 1999 David A. Hinds
- * (C) 2003 - 2004 Dominik Brodowski
+ * (C) 2003 - 2008 Dominik Brodowski
*/
#ifndef _LINUX_DS_H
@@ -23,108 +23,21 @@
#include <pcmcia/cs_types.h>
#include <pcmcia/device_id.h>
-typedef struct tuple_parse_t {
- tuple_t tuple;
- cisdata_t data[255];
- cisparse_t parse;
-} tuple_parse_t;
-
-typedef struct win_info_t {
- window_handle_t handle;
- win_req_t window;
- memreq_t map;
-} win_info_t;
-
-typedef struct bind_info_t {
- dev_info_t dev_info;
- u_char function;
- struct pcmcia_device *instance;
- char name[DEV_NAME_LEN];
- u_short major, minor;
- void *next;
-} bind_info_t;
-
-typedef struct mtd_info_t {
- dev_info_t dev_info;
- u_int Attributes;
- u_int CardOffset;
-} mtd_info_t;
-
-typedef struct region_info_t {
- u_int Attributes;
- u_int CardOffset;
- u_int RegionSize;
- u_int AccessSpeed;
- u_int BlockSize;
- u_int PartMultiple;
- u_char JedecMfr, JedecInfo;
- memory_handle_t next;
-} region_info_t;
-#define REGION_TYPE 0x0001
-#define REGION_TYPE_CM 0x0000
-#define REGION_TYPE_AM 0x0001
-#define REGION_PREFETCH 0x0008
-#define REGION_CACHEABLE 0x0010
-#define REGION_BAR_MASK 0xe000
-#define REGION_BAR_SHIFT 13
-
-typedef union ds_ioctl_arg_t {
- adjust_t adjust;
- config_info_t config;
- tuple_t tuple;
- tuple_parse_t tuple_parse;
- client_req_t client_req;
- cs_status_t status;
- conf_reg_t conf_reg;
- cisinfo_t cisinfo;
- region_info_t region;
- bind_info_t bind_info;
- mtd_info_t mtd_info;
- win_info_t win_info;
- cisdump_t cisdump;
-} ds_ioctl_arg_t;
-
-#define DS_ADJUST_RESOURCE_INFO _IOWR('d', 2, adjust_t)
-#define DS_GET_CONFIGURATION_INFO _IOWR('d', 3, config_info_t)
-#define DS_GET_FIRST_TUPLE _IOWR('d', 4, tuple_t)
-#define DS_GET_NEXT_TUPLE _IOWR('d', 5, tuple_t)
-#define DS_GET_TUPLE_DATA _IOWR('d', 6, tuple_parse_t)
-#define DS_PARSE_TUPLE _IOWR('d', 7, tuple_parse_t)
-#define DS_RESET_CARD _IO ('d', 8)
-#define DS_GET_STATUS _IOWR('d', 9, cs_status_t)
-#define DS_ACCESS_CONFIGURATION_REGISTER _IOWR('d', 10, conf_reg_t)
-#define DS_VALIDATE_CIS _IOR ('d', 11, cisinfo_t)
-#define DS_SUSPEND_CARD _IO ('d', 12)
-#define DS_RESUME_CARD _IO ('d', 13)
-#define DS_EJECT_CARD _IO ('d', 14)
-#define DS_INSERT_CARD _IO ('d', 15)
-#define DS_GET_FIRST_REGION _IOWR('d', 16, region_info_t)
-#define DS_GET_NEXT_REGION _IOWR('d', 17, region_info_t)
-#define DS_REPLACE_CIS _IOWR('d', 18, cisdump_t)
-#define DS_GET_FIRST_WINDOW _IOR ('d', 19, win_info_t)
-#define DS_GET_NEXT_WINDOW _IOWR('d', 20, win_info_t)
-#define DS_GET_MEM_PAGE _IOWR('d', 21, win_info_t)
-
-#define DS_BIND_REQUEST _IOWR('d', 60, bind_info_t)
-#define DS_GET_DEVICE_INFO _IOWR('d', 61, bind_info_t)
-#define DS_GET_NEXT_DEVICE _IOWR('d', 62, bind_info_t)
-#define DS_UNBIND_REQUEST _IOW ('d', 63, bind_info_t)
-#define DS_BIND_MTD _IOWR('d', 64, mtd_info_t)
-
#ifdef __KERNEL__
#include <linux/device.h>
#include <pcmcia/ss.h>
-typedef struct dev_node_t {
- char dev_name[DEV_NAME_LEN];
- u_short major, minor;
- struct dev_node_t *next;
-} dev_node_t;
-
-
+/*
+ * PCMCIA device drivers (16-bit cards only; 32-bit cards require CardBus
+ * a.k.a. PCI drivers
+ */
struct pcmcia_socket;
+struct pcmcia_device;
struct config_t;
+/* dynamic device IDs for PCMCIA device drivers. See
+ * Documentation/pcmcia/driver.txt for details.
+*/
struct pcmcia_dynids {
spinlock_t lock;
struct list_head list;
@@ -147,6 +60,14 @@
int pcmcia_register_driver(struct pcmcia_driver *driver);
void pcmcia_unregister_driver(struct pcmcia_driver *driver);
+/* Some drivers use dev_node_t to store char or block device information.
+ * Don't use this in new drivers, though.
+ */
+typedef struct dev_node_t {
+ char dev_name[DEV_NAME_LEN];
+ u_short major, minor;
+ struct dev_node_t *next;
+} dev_node_t;
struct pcmcia_device {
/* the socket and the device_no [for multifunction devices]
@@ -216,10 +137,304 @@
#define to_pcmcia_dev(n) container_of(n, struct pcmcia_device, dev)
#define to_pcmcia_drv(n) container_of(n, struct pcmcia_driver, drv)
+/* deprecated -- don't use! */
#define handle_to_dev(handle) (handle->dev)
-/* error reporting */
-void cs_error(struct pcmcia_device *handle, int func, int ret);
+
+/* (deprecated) error reporting by PCMCIA devices. Use dev_printk()
+ * or dev_dbg() directly in the driver, without referring to pcmcia_error_func()
+ * and/or pcmcia_error_ret() for those functions will go away soon.
+ */
+enum service {
+ AccessConfigurationRegister, AddSocketServices,
+ AdjustResourceInfo, CheckEraseQueue, CloseMemory, CopyMemory,
+ DeregisterClient, DeregisterEraseQueue, GetCardServicesInfo,
+ GetClientInfo, GetConfigurationInfo, GetEventMask,
+ GetFirstClient, GetFirstPartion, GetFirstRegion, GetFirstTuple,
+ GetNextClient, GetNextPartition, GetNextRegion, GetNextTuple,
+ GetStatus, GetTupleData, MapLogSocket, MapLogWindow, MapMemPage,
+ MapPhySocket, MapPhyWindow, ModifyConfiguration, ModifyWindow,
+ OpenMemory, ParseTuple, ReadMemory, RegisterClient,
+ RegisterEraseQueue, RegisterMTD, RegisterTimer,
+ ReleaseConfiguration, ReleaseExclusive, ReleaseIO, ReleaseIRQ,
+ ReleaseSocketMask, ReleaseWindow, ReplaceSocketServices,
+ RequestConfiguration, RequestExclusive, RequestIO, RequestIRQ,
+ RequestSocketMask, RequestWindow, ResetCard, ReturnSSEntry,
+ SetEventMask, SetRegion, ValidateCIS, VendorSpecific,
+ WriteMemory, BindDevice, BindMTD, ReportError,
+ SuspendCard, ResumeCard, EjectCard, InsertCard, ReplaceCIS,
+ GetFirstWindow, GetNextWindow, GetMemPage
+};
+const char *pcmcia_error_func(int func);
+const char *pcmcia_error_ret(int ret);
+
+#define cs_error(p_dev, func, ret) \
+ { \
+ dev_printk(KERN_NOTICE, &p_dev->dev, \
+ "%s : %s\n", \
+ pcmcia_error_func(func), \
+ pcmcia_error_ret(ret)); \
+ }
+
+/* CIS access.
+ * Use the pcmcia_* versions in PCMCIA drivers
+ */
+int pcmcia_parse_tuple(tuple_t *tuple, cisparse_t *parse);
+
+int pccard_get_first_tuple(struct pcmcia_socket *s, unsigned int function,
+ tuple_t *tuple);
+#define pcmcia_get_first_tuple(p_dev, tuple) \
+ pccard_get_first_tuple(p_dev->socket, p_dev->func, tuple)
+
+int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function,
+ tuple_t *tuple);
+#define pcmcia_get_next_tuple(p_dev, tuple) \
+ pccard_get_next_tuple(p_dev->socket, p_dev->func, tuple)
+
+int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple);
+#define pcmcia_get_tuple_data(p_dev, tuple) \
+ pccard_get_tuple_data(p_dev->socket, tuple)
+
+
+/* loop CIS entries for valid configuration */
+int pcmcia_loop_config(struct pcmcia_device *p_dev,
+ int (*conf_check) (struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data),
+ void *priv_data);
+
+/* is the device still there? */
+struct pcmcia_device *pcmcia_dev_present(struct pcmcia_device *p_dev);
+
+/* low-level interface reset */
+int pcmcia_reset_card(struct pcmcia_socket *skt);
+
+/* CIS config */
+int pcmcia_access_configuration_register(struct pcmcia_device *p_dev,
+ conf_reg_t *reg);
+
+/* device configuration */
+int pcmcia_request_io(struct pcmcia_device *p_dev, io_req_t *req);
+int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req);
+int pcmcia_request_configuration(struct pcmcia_device *p_dev,
+ config_req_t *req);
+
+int pcmcia_request_window(struct pcmcia_device **p_dev, win_req_t *req,
+ window_handle_t *wh);
+int pcmcia_release_window(window_handle_t win);
+
+int pcmcia_get_mem_page(window_handle_t win, memreq_t *req);
+int pcmcia_map_mem_page(window_handle_t win, memreq_t *req);
+
+int pcmcia_modify_configuration(struct pcmcia_device *p_dev, modconf_t *mod);
+void pcmcia_disable_device(struct pcmcia_device *p_dev);
#endif /* __KERNEL__ */
+
+
+
+/* Below, there are only definitions which are used by
+ * - the PCMCIA ioctl
+ * - deprecated PCMCIA userspace tools only
+ *
+ * here be dragons ... here be dragons ... here be dragons ... here be drag
+ */
+
+#if defined(CONFIG_PCMCIA_IOCTL) || !defined(__KERNEL__)
+
+#if defined(__arm__) || defined(__mips__) || defined(__avr32__) || \
+ defined(__bfin__)
+/* This (ioaddr_t) is exposed to userspace & hence cannot be changed. */
+typedef u_int ioaddr_t;
+#else
+typedef u_short ioaddr_t;
+#endif
+
+/* for AdjustResourceInfo */
+typedef struct adjust_t {
+ u_int Action;
+ u_int Resource;
+ u_int Attributes;
+ union {
+ struct memory {
+ u_long Base;
+ u_long Size;
+ } memory;
+ struct io {
+ ioaddr_t BasePort;
+ ioaddr_t NumPorts;
+ u_int IOAddrLines;
+ } io;
+ struct irq {
+ u_int IRQ;
+ } irq;
+ } resource;
+} adjust_t;
+
+/* Action field */
+#define REMOVE_MANAGED_RESOURCE 1
+#define ADD_MANAGED_RESOURCE 2
+#define GET_FIRST_MANAGED_RESOURCE 3
+#define GET_NEXT_MANAGED_RESOURCE 4
+/* Resource field */
+#define RES_MEMORY_RANGE 1
+#define RES_IO_RANGE 2
+#define RES_IRQ 3
+/* Attribute field */
+#define RES_IRQ_TYPE 0x03
+#define RES_IRQ_TYPE_EXCLUSIVE 0
+#define RES_IRQ_TYPE_TIME 1
+#define RES_IRQ_TYPE_DYNAMIC 2
+#define RES_IRQ_CSC 0x04
+#define RES_SHARED 0x08
+#define RES_RESERVED 0x10
+#define RES_ALLOCATED 0x20
+#define RES_REMOVED 0x40
+
+
+typedef struct tuple_parse_t {
+ tuple_t tuple;
+ cisdata_t data[255];
+ cisparse_t parse;
+} tuple_parse_t;
+
+typedef struct win_info_t {
+ window_handle_t handle;
+ win_req_t window;
+ memreq_t map;
+} win_info_t;
+
+typedef struct bind_info_t {
+ dev_info_t dev_info;
+ u_char function;
+ struct pcmcia_device *instance;
+ char name[DEV_NAME_LEN];
+ u_short major, minor;
+ void *next;
+} bind_info_t;
+
+typedef struct mtd_info_t {
+ dev_info_t dev_info;
+ u_int Attributes;
+ u_int CardOffset;
+} mtd_info_t;
+
+typedef struct region_info_t {
+ u_int Attributes;
+ u_int CardOffset;
+ u_int RegionSize;
+ u_int AccessSpeed;
+ u_int BlockSize;
+ u_int PartMultiple;
+ u_char JedecMfr, JedecInfo;
+ memory_handle_t next;
+} region_info_t;
+
+#define REGION_TYPE 0x0001
+#define REGION_TYPE_CM 0x0000
+#define REGION_TYPE_AM 0x0001
+#define REGION_PREFETCH 0x0008
+#define REGION_CACHEABLE 0x0010
+#define REGION_BAR_MASK 0xe000
+#define REGION_BAR_SHIFT 13
+
+/* For ReplaceCIS */
+typedef struct cisdump_t {
+ u_int Length;
+ cisdata_t Data[CISTPL_MAX_CIS_SIZE];
+} cisdump_t;
+
+/* for GetConfigurationInfo */
+typedef struct config_info_t {
+ u_char Function;
+ u_int Attributes;
+ u_int Vcc, Vpp1, Vpp2;
+ u_int IntType;
+ u_int ConfigBase;
+ u_char Status, Pin, Copy, Option, ExtStatus;
+ u_int Present;
+ u_int CardValues;
+ u_int AssignedIRQ;
+ u_int IRQAttributes;
+ ioaddr_t BasePort1;
+ ioaddr_t NumPorts1;
+ u_int Attributes1;
+ ioaddr_t BasePort2;
+ ioaddr_t NumPorts2;
+ u_int Attributes2;
+ u_int IOAddrLines;
+} config_info_t;
+
+/* For ValidateCIS */
+typedef struct cisinfo_t {
+ u_int Chains;
+} cisinfo_t;
+
+typedef struct cs_status_t {
+ u_char Function;
+ event_t CardState;
+ event_t SocketState;
+} cs_status_t;
+
+typedef union ds_ioctl_arg_t {
+ adjust_t adjust;
+ config_info_t config;
+ tuple_t tuple;
+ tuple_parse_t tuple_parse;
+ client_req_t client_req;
+ cs_status_t status;
+ conf_reg_t conf_reg;
+ cisinfo_t cisinfo;
+ region_info_t region;
+ bind_info_t bind_info;
+ mtd_info_t mtd_info;
+ win_info_t win_info;
+ cisdump_t cisdump;
+} ds_ioctl_arg_t;
+
+#define DS_ADJUST_RESOURCE_INFO _IOWR('d', 2, adjust_t)
+#define DS_GET_CONFIGURATION_INFO _IOWR('d', 3, config_info_t)
+#define DS_GET_FIRST_TUPLE _IOWR('d', 4, tuple_t)
+#define DS_GET_NEXT_TUPLE _IOWR('d', 5, tuple_t)
+#define DS_GET_TUPLE_DATA _IOWR('d', 6, tuple_parse_t)
+#define DS_PARSE_TUPLE _IOWR('d', 7, tuple_parse_t)
+#define DS_RESET_CARD _IO ('d', 8)
+#define DS_GET_STATUS _IOWR('d', 9, cs_status_t)
+#define DS_ACCESS_CONFIGURATION_REGISTER _IOWR('d', 10, conf_reg_t)
+#define DS_VALIDATE_CIS _IOR ('d', 11, cisinfo_t)
+#define DS_SUSPEND_CARD _IO ('d', 12)
+#define DS_RESUME_CARD _IO ('d', 13)
+#define DS_EJECT_CARD _IO ('d', 14)
+#define DS_INSERT_CARD _IO ('d', 15)
+#define DS_GET_FIRST_REGION _IOWR('d', 16, region_info_t)
+#define DS_GET_NEXT_REGION _IOWR('d', 17, region_info_t)
+#define DS_REPLACE_CIS _IOWR('d', 18, cisdump_t)
+#define DS_GET_FIRST_WINDOW _IOR ('d', 19, win_info_t)
+#define DS_GET_NEXT_WINDOW _IOWR('d', 20, win_info_t)
+#define DS_GET_MEM_PAGE _IOWR('d', 21, win_info_t)
+
+#define DS_BIND_REQUEST _IOWR('d', 60, bind_info_t)
+#define DS_GET_DEVICE_INFO _IOWR('d', 61, bind_info_t)
+#define DS_GET_NEXT_DEVICE _IOWR('d', 62, bind_info_t)
+#define DS_UNBIND_REQUEST _IOW ('d', 63, bind_info_t)
+#define DS_BIND_MTD _IOWR('d', 64, mtd_info_t)
+
+
+/* used in userspace only */
+#define CS_IN_USE 0x1e
+
+#define INFO_MASTER_CLIENT 0x01
+#define INFO_IO_CLIENT 0x02
+#define INFO_MTD_CLIENT 0x04
+#define INFO_MEM_CLIENT 0x08
+#define MAX_NUM_CLIENTS 3
+
+#define INFO_CARD_SHARE 0x10
+#define INFO_CARD_EXCL 0x20
+
+
+#endif /* !defined(__KERNEL__) || defined(CONFIG_PCMCIA_IOCTL) */
+
#endif /* _LINUX_DS_H */
diff --git a/include/pcmcia/ss.h b/include/pcmcia/ss.h
index ed919dd..9b4ac93 100644
--- a/include/pcmcia/ss.h
+++ b/include/pcmcia/ss.h
@@ -53,10 +53,10 @@
/* for GetSocket, SetSocket */
typedef struct socket_state_t {
- u_int flags;
- u_int csc_mask;
- u_char Vcc, Vpp;
- u_char io_irq;
+ u_int flags;
+ u_int csc_mask;
+ u_char Vcc, Vpp;
+ u_char io_irq;
} socket_state_t;
extern socket_state_t dead_socket;
@@ -86,79 +86,22 @@
#define HOOK_POWER_PRE 0x01
#define HOOK_POWER_POST 0x02
-
typedef struct pccard_io_map {
- u_char map;
- u_char flags;
- u_short speed;
- u_int start, stop;
+ u_char map;
+ u_char flags;
+ u_short speed;
+ u_int start, stop;
} pccard_io_map;
typedef struct pccard_mem_map {
- u_char map;
- u_char flags;
- u_short speed;
- u_long static_start;
- u_int card_start;
- struct resource *res;
+ u_char map;
+ u_char flags;
+ u_short speed;
+ u_long static_start;
+ u_int card_start;
+ struct resource *res;
} pccard_mem_map;
-typedef struct cb_bridge_map {
- u_char map;
- u_char flags;
- u_int start, stop;
-} cb_bridge_map;
-
-/*
- * Socket operations.
- */
-struct pcmcia_socket;
-
-struct pccard_operations {
- int (*init)(struct pcmcia_socket *sock);
- int (*suspend)(struct pcmcia_socket *sock);
- int (*get_status)(struct pcmcia_socket *sock, u_int *value);
- int (*set_socket)(struct pcmcia_socket *sock, socket_state_t *state);
- int (*set_io_map)(struct pcmcia_socket *sock, struct pccard_io_map *io);
- int (*set_mem_map)(struct pcmcia_socket *sock, struct pccard_mem_map *mem);
-};
-
-struct pccard_resource_ops {
- int (*validate_mem) (struct pcmcia_socket *s);
- int (*adjust_io_region) (struct resource *res,
- unsigned long r_start,
- unsigned long r_end,
- struct pcmcia_socket *s);
- struct resource* (*find_io) (unsigned long base, int num,
- unsigned long align,
- struct pcmcia_socket *s);
- struct resource* (*find_mem) (unsigned long base, unsigned long num,
- unsigned long align, int low,
- struct pcmcia_socket *s);
- int (*add_io) (struct pcmcia_socket *s,
- unsigned int action,
- unsigned long r_start,
- unsigned long r_end);
- int (*add_mem) (struct pcmcia_socket *s,
- unsigned int action,
- unsigned long r_start,
- unsigned long r_end);
- int (*init) (struct pcmcia_socket *s);
- void (*exit) (struct pcmcia_socket *s);
-};
-/* SS_CAP_STATIC_MAP */
-extern struct pccard_resource_ops pccard_static_ops;
-/* !SS_CAP_STATIC_MAP */
-extern struct pccard_resource_ops pccard_nonstatic_ops;
-
-/* static mem, dynamic IO sockets */
-extern struct pccard_resource_ops pccard_iodyn_ops;
-
-/*
- * Calls to set up low-level "Socket Services" drivers
- */
-struct pcmcia_socket;
-
typedef struct io_window_t {
u_int InUse, Config;
struct resource *res;
@@ -179,10 +122,25 @@
/* Maximum number of memory windows per socket */
#define MAX_WIN 4
+
+/*
+ * Socket operations.
+ */
+struct pcmcia_socket;
+struct pccard_resource_ops;
struct config_t;
struct pcmcia_callback;
struct user_info_t;
+struct pccard_operations {
+ int (*init)(struct pcmcia_socket *s);
+ int (*suspend)(struct pcmcia_socket *s);
+ int (*get_status)(struct pcmcia_socket *s, u_int *value);
+ int (*set_socket)(struct pcmcia_socket *s, socket_state_t *state);
+ int (*set_io_map)(struct pcmcia_socket *s, struct pccard_io_map *io);
+ int (*set_mem_map)(struct pcmcia_socket *s, struct pccard_mem_map *mem);
+};
+
struct pcmcia_socket {
struct module *owner;
spinlock_t lock;
@@ -199,8 +157,8 @@
io_window_t io[MAX_IO_WIN];
window_t win[MAX_WIN];
struct list_head cis_cache;
- u_int fake_cis_len;
- char *fake_cis;
+ size_t fake_cis_len;
+ u8 *fake_cis;
struct list_head socket_list;
struct completion socket_released;
@@ -218,12 +176,12 @@
struct pci_dev * cb_dev;
- /* socket setup is done so resources should be able to be allocated. Only
- * if set to 1, calls to find_{io,mem}_region are handled, and insertion
- * events are actually managed by the PCMCIA layer.*/
+ /* socket setup is done so resources should be able to be allocated.
+ * Only if set to 1, calls to find_{io,mem}_region are handled, and
+ * insertio events are actually managed by the PCMCIA layer.*/
u8 resource_setup_done:1;
- /* is set to one if resource setup is done using adjust_resource_info() */
+ /* It's old if resource setup is done using adjust_resource_info() */
u8 resource_setup_old:1;
u8 resource_setup_new:1;
@@ -236,75 +194,101 @@
/* Zoom video behaviour is so chip specific its not worth adding
this to _ops */
- void (*zoom_video)(struct pcmcia_socket *, int);
+ void (*zoom_video)(struct pcmcia_socket *,
+ int);
/* so is power hook */
int (*power_hook)(struct pcmcia_socket *sock, int operation);
-#ifdef CONFIG_CARDBUS
+
/* allows tuning the CB bridge before loading driver for the CB card */
+#ifdef CONFIG_CARDBUS
void (*tune_bridge)(struct pcmcia_socket *sock, struct pci_bus *bus);
#endif
/* state thread */
- struct mutex skt_mutex; /* protects socket h/w state */
-
struct task_struct *thread;
struct completion thread_done;
- spinlock_t thread_lock; /* protects thread_events */
unsigned int thread_events;
+ /* protects socket h/w state */
+ struct mutex skt_mutex;
+ /* protects thread_events */
+ spinlock_t thread_lock;
/* pcmcia (16-bit) */
struct pcmcia_callback *callback;
#if defined(CONFIG_PCMCIA) || defined(CONFIG_PCMCIA_MODULE)
- struct list_head devices_list; /* PCMCIA devices */
- u8 device_count; /* the number of devices, used
- * only internally and subject
- * to incorrectness and change */
+ /* The following elements refer to 16-bit PCMCIA devices inserted
+ * into the socket */
+ struct list_head devices_list;
+ /* the number of devices, used only internally and subject to
+ * incorrectness and change */
+ u8 device_count;
+
+ /* 16-bit state: */
struct {
- u8 present:1, /* PCMCIA card is present in socket */
- busy:1, /* "master" ioctl is used */
- dead:1, /* pcmcia module is being unloaded */
- device_add_pending:1, /* a multifunction-device
- * add event is pending */
- mfc_pfc:1, /* the pending event adds a mfc (1) or pfc (0) */
- reserved:3;
- } pcmcia_state;
+ /* PCMCIA card is present in socket */
+ u8 present:1;
+ /* "master" ioctl is used */
+ u8 busy:1;
+ /* pcmcia module is being unloaded */
+ u8 dead:1;
+ /* a multifunction-device add event is pending */
+ u8 device_add_pending:1;
+ /* the pending event adds a mfc (1) or pfc (0) */
+ u8 mfc_pfc:1;
- struct work_struct device_add; /* for adding further pseudo-multifunction
- * devices */
+ u8 reserved:3;
+ } pcmcia_state;
+
+
+ /* for adding further pseudo-multifunction devices */
+ struct work_struct device_add;
#ifdef CONFIG_PCMCIA_IOCTL
struct user_info_t *user;
wait_queue_head_t queue;
-#endif
-#endif
+#endif /* CONFIG_PCMCIA_IOCTL */
+#endif /* CONFIG_PCMCIA */
/* cardbus (32-bit) */
#ifdef CONFIG_CARDBUS
struct resource * cb_cis_res;
void __iomem *cb_cis_virt;
-#endif
+#endif /* CONFIG_CARDBUS */
/* socket device */
struct device dev;
- void *driver_data; /* data internal to the socket driver */
-
+ /* data internal to the socket driver */
+ void *driver_data;
};
-struct pcmcia_socket * pcmcia_get_socket_by_nr(unsigned int nr);
-
-
-extern void pcmcia_parse_events(struct pcmcia_socket *socket, unsigned int events);
-extern int pcmcia_register_socket(struct pcmcia_socket *socket);
-extern void pcmcia_unregister_socket(struct pcmcia_socket *socket);
-
-extern struct class pcmcia_socket_class;
+/* socket drivers must define the resource operations type they use. There
+ * are three options:
+ * - pccard_static_ops iomem and ioport areas are assigned statically
+ * - pccard_iodyn_ops iomem areas is assigned statically, ioport
+ * areas dynamically
+ * - pccard_nonstatic_ops iomem and ioport areas are assigned dynamically.
+ * If this option is selected, use
+ * "select PCCARD_NONSTATIC" in Kconfig.
+ */
+extern struct pccard_resource_ops pccard_static_ops;
+extern struct pccard_resource_ops pccard_iodyn_ops;
+extern struct pccard_resource_ops pccard_nonstatic_ops;
/* socket drivers are expected to use these callbacks in their .drv struct */
extern int pcmcia_socket_dev_suspend(struct device *dev, pm_message_t state);
extern int pcmcia_socket_dev_resume(struct device *dev);
+/* socket drivers use this callback in their IRQ handler */
+extern void pcmcia_parse_events(struct pcmcia_socket *socket,
+ unsigned int events);
+
+/* to register and unregister a socket */
+extern int pcmcia_register_socket(struct pcmcia_socket *socket);
+extern void pcmcia_unregister_socket(struct pcmcia_socket *socket);
+
+
#endif /* _LINUX_SS_H */
diff --git a/include/sound/core.h b/include/sound/core.h
index e5eec5f..35424a9 100644
--- a/include/sound/core.h
+++ b/include/sound/core.h
@@ -43,9 +43,6 @@
#ifdef CONFIG_PCI
struct pci_dev;
#endif
-#ifdef CONFIG_SBUS
-struct sbus_dev;
-#endif
/* device allocation stuff */
diff --git a/include/sound/memalloc.h b/include/sound/memalloc.h
index d787a6b..7ccce94 100644
--- a/include/sound/memalloc.h
+++ b/include/sound/memalloc.h
@@ -37,7 +37,6 @@
#ifndef snd_dma_pci_data
#define snd_dma_pci_data(pci) (&(pci)->dev)
#define snd_dma_isa_data() NULL
-#define snd_dma_sbus_data(sbus) ((struct device *)(sbus))
#define snd_dma_continuous_data(x) ((struct device *)(unsigned long)(x))
#endif
@@ -49,7 +48,6 @@
#define SNDRV_DMA_TYPE_CONTINUOUS 1 /* continuous no-DMA memory */
#define SNDRV_DMA_TYPE_DEV 2 /* generic device continuous */
#define SNDRV_DMA_TYPE_DEV_SG 3 /* generic device SG-buffer */
-#define SNDRV_DMA_TYPE_SBUS 4 /* SBUS continuous */
/*
* info for buffer allocation
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index c1b26fc..ca699a3 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -240,6 +240,7 @@
/* dapm audio pin control and status */
int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, char *pin);
int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, char *pin);
+int snd_soc_dapm_nc_pin(struct snd_soc_codec *codec, char *pin);
int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, char *pin);
int snd_soc_dapm_sync(struct snd_soc_codec *codec);
diff --git a/include/sound/tea575x-tuner.h b/include/sound/tea575x-tuner.h
index b62ce3e..b6870cb 100644
--- a/include/sound/tea575x-tuner.h
+++ b/include/sound/tea575x-tuner.h
@@ -43,6 +43,7 @@
unsigned int freq_fixup; /* crystal onboard */
unsigned int val; /* hw value */
unsigned long freq; /* frequency */
+ unsigned long in_use; /* set if the device is in use */
struct snd_tea575x_ops *ops;
void *private_data;
};
diff --git a/include/xen/balloon.h b/include/xen/balloon.h
deleted file mode 100644
index fe43b0f..0000000
--- a/include/xen/balloon.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/******************************************************************************
- * balloon.h
- *
- * Xen balloon driver - enables returning/claiming memory to/from Xen.
- *
- * Copyright (c) 2003, B Dragovic
- * Copyright (c) 2003-2004, M Williamson, K Fraser
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation; or, when distributed
- * separately from the Linux kernel or incorporated into other
- * software packages, subject to the following license:
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this source file (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy, modify,
- * merge, publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
-#ifndef __XEN_BALLOON_H__
-#define __XEN_BALLOON_H__
-
-#include <linux/spinlock.h>
-
-#if 0
-/*
- * Inform the balloon driver that it should allow some slop for device-driver
- * memory activities.
- */
-void balloon_update_driver_allowance(long delta);
-
-/* Allocate/free a set of empty pages in low memory (i.e., no RAM mapped). */
-struct page **alloc_empty_pages_and_pagevec(int nr_pages);
-void free_empty_pages_and_pagevec(struct page **pagevec, int nr_pages);
-
-void balloon_release_driver_page(struct page *page);
-
-/*
- * Prevent the balloon driver from changing the memory reservation during
- * a driver critical region.
- */
-extern spinlock_t balloon_lock;
-#define balloon_lock(__flags) spin_lock_irqsave(&balloon_lock, __flags)
-#define balloon_unlock(__flags) spin_unlock_irqrestore(&balloon_lock, __flags)
-#endif
-
-#endif /* __XEN_BALLOON_H__ */
diff --git a/include/xen/events.h b/include/xen/events.h
index 4680ff3..0d5f1ad 100644
--- a/include/xen/events.h
+++ b/include/xen/events.h
@@ -46,6 +46,8 @@
/* Clear an irq's pending state, in preparation for polling on it */
void xen_clear_irq_pending(int irq);
+void xen_set_irq_pending(int irq);
+bool xen_test_irq_pending(int irq);
/* Poll waiting for an irq to become pending. In the usual case, the
irq will be disabled so it won't deliver an interrupt. */
diff --git a/init/Kconfig b/init/Kconfig
index c11da38..8a8e2d0 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -779,16 +779,6 @@
source "arch/Kconfig"
-config PROC_PAGE_MONITOR
- default y
- depends on PROC_FS && MMU
- bool "Enable /proc page monitoring" if EMBEDDED
- help
- Various /proc files exist to monitor process memory utilization:
- /proc/pid/smaps, /proc/pid/clear_refs, /proc/pid/pagemap,
- /proc/kpagecount, and /proc/kpageflags. Disabling these
- interfaces will reduce the size of the kernel by approximately 4kb.
-
endmenu # General setup
config HAVE_GENERIC_DMA_COHERENT
diff --git a/kernel/acct.c b/kernel/acct.c
index dd68b90..f6006a6 100644
--- a/kernel/acct.c
+++ b/kernel/acct.c
@@ -548,7 +548,7 @@
#endif
spin_lock_irq(¤t->sighand->siglock);
- tty = current->signal->tty;
+ tty = current->signal->tty; /* Safe as we hold the siglock */
ac.ac_tty = tty ? old_encode_dev(tty_devnum(tty)) : 0;
ac.ac_utime = encode_comp_t(jiffies_to_AHZ(cputime_to_jiffies(pacct->ac_utime)));
ac.ac_stime = encode_comp_t(jiffies_to_AHZ(cputime_to_jiffies(pacct->ac_stime)));
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 59cedfb..cf5bc2f 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -246,8 +246,8 @@
unsigned n;
if (unlikely(!ctx))
return 0;
-
n = ctx->major;
+
switch (audit_classify_syscall(ctx->arch, n)) {
case 0: /* native */
if ((mask & AUDIT_PERM_WRITE) &&
@@ -1204,13 +1204,13 @@
(context->return_valid==AUDITSC_SUCCESS)?"yes":"no",
context->return_code);
- mutex_lock(&tty_mutex);
- read_lock(&tasklist_lock);
+ spin_lock_irq(&tsk->sighand->siglock);
if (tsk->signal && tsk->signal->tty && tsk->signal->tty->name)
tty = tsk->signal->tty->name;
else
tty = "(none)";
- read_unlock(&tasklist_lock);
+ spin_unlock_irq(&tsk->sighand->siglock);
+
audit_log_format(ab,
" a0=%lx a1=%lx a2=%lx a3=%lx items=%d"
" ppid=%d pid=%d auid=%u uid=%u gid=%u"
@@ -1230,7 +1230,6 @@
context->egid, context->sgid, context->fsgid, tty,
tsk->sessionid);
- mutex_unlock(&tty_mutex);
audit_log_task_info(ab, tsk);
if (context->filterkey) {
diff --git a/kernel/fork.c b/kernel/fork.c
index 7ce2ebe..30de644 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -802,6 +802,7 @@
sig->leader = 0; /* session leadership doesn't inherit */
sig->tty_old_pgrp = NULL;
+ sig->tty = NULL;
sig->utime = sig->stime = sig->cutime = sig->cstime = cputime_zero;
sig->gtime = cputime_zero;
@@ -838,6 +839,7 @@
void __cleanup_signal(struct signal_struct *sig)
{
exit_thread_group_keys(sig);
+ tty_kref_put(sig->tty);
kmem_cache_free(signal_cachep, sig);
}
@@ -1227,7 +1229,8 @@
p->nsproxy->pid_ns->child_reaper = p;
p->signal->leader_pid = pid;
- p->signal->tty = current->signal->tty;
+ tty_kref_put(p->signal->tty);
+ p->signal->tty = tty_kref_get(current->signal->tty);
set_task_pgrp(p, task_pgrp_nr(current));
set_task_session(p, task_session_nr(current));
attach_pid(p, PIDTYPE_PGID, task_pgrp(current));
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index 0314074..60c49e3 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -89,7 +89,14 @@
set_balance_irq_affinity(irq, cpumask);
#ifdef CONFIG_GENERIC_PENDING_IRQ
- set_pending_irq(irq, cpumask);
+ if (desc->status & IRQ_MOVE_PCNTXT) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&desc->lock, flags);
+ desc->chip->set_affinity(irq, cpumask);
+ spin_unlock_irqrestore(&desc->lock, flags);
+ } else
+ set_pending_irq(irq, cpumask);
#else
desc->affinity = cpumask;
desc->chip->set_affinity(irq, cpumask);
diff --git a/kernel/printk.c b/kernel/printk.c
index b51b156..a430fd0 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -1291,22 +1291,6 @@
}
late_initcall(disable_boot_consoles);
-/**
- * tty_write_message - write a message to a certain tty, not just the console.
- * @tty: the destination tty_struct
- * @msg: the message to write
- *
- * This is used for messages that need to be redirected to a specific tty.
- * We don't put it into the syslog queue right now maybe in the future if
- * really needed.
- */
-void tty_write_message(struct tty_struct *tty, char *msg)
-{
- if (tty && tty->ops->write)
- tty->ops->write(tty, msg, strlen(msg));
- return;
-}
-
#if defined CONFIG_PRINTK
/*
diff --git a/kernel/resource.c b/kernel/resource.c
index 03d796c..414d6fc 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -516,6 +516,74 @@
return result;
}
+static void __init __reserve_region_with_split(struct resource *root,
+ resource_size_t start, resource_size_t end,
+ const char *name)
+{
+ struct resource *parent = root;
+ struct resource *conflict;
+ struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL);
+
+ if (!res)
+ return;
+
+ res->name = name;
+ res->start = start;
+ res->end = end;
+ res->flags = IORESOURCE_BUSY;
+
+ for (;;) {
+ conflict = __request_resource(parent, res);
+ if (!conflict)
+ break;
+ if (conflict != parent) {
+ parent = conflict;
+ if (!(conflict->flags & IORESOURCE_BUSY))
+ continue;
+ }
+
+ /* Uhhuh, that didn't work out.. */
+ kfree(res);
+ res = NULL;
+ break;
+ }
+
+ if (!res) {
+ printk(KERN_DEBUG " __reserve_region_with_split: (%s) [%llx, %llx], res: (%s) [%llx, %llx]\n",
+ conflict->name, conflict->start, conflict->end,
+ name, start, end);
+
+ /* failed, split and try again */
+
+ /* conflict coverred whole area */
+ if (conflict->start <= start && conflict->end >= end)
+ return;
+
+ if (conflict->start > start)
+ __reserve_region_with_split(root, start, conflict->start-1, name);
+ if (!(conflict->flags & IORESOURCE_BUSY)) {
+ resource_size_t common_start, common_end;
+
+ common_start = max(conflict->start, start);
+ common_end = min(conflict->end, end);
+ if (common_start < common_end)
+ __reserve_region_with_split(root, common_start, common_end, name);
+ }
+ if (conflict->end < end)
+ __reserve_region_with_split(root, conflict->end+1, end, name);
+ }
+
+}
+
+void reserve_region_with_split(struct resource *root,
+ resource_size_t start, resource_size_t end,
+ const char *name)
+{
+ write_lock(&resource_lock);
+ __reserve_region_with_split(root, start, end, name);
+ write_unlock(&resource_lock);
+}
+
EXPORT_SYMBOL(adjust_resource);
/**
diff --git a/kernel/sched_debug.c b/kernel/sched_debug.c
index bbe6b31..ad958c1 100644
--- a/kernel/sched_debug.c
+++ b/kernel/sched_debug.c
@@ -333,12 +333,10 @@
unsigned long flags;
int num_threads = 1;
- rcu_read_lock();
if (lock_task_sighand(p, &flags)) {
num_threads = atomic_read(&p->signal->count);
unlock_task_sighand(p, &flags);
}
- rcu_read_unlock();
SEQ_printf(m, "%s (%d, #threads: %d)\n", p->comm, p->pid, num_threads);
SEQ_printf(m,
diff --git a/kernel/sys.c b/kernel/sys.c
index 038a7bc..234d945 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -1060,9 +1060,7 @@
group_leader->signal->leader = 1;
__set_special_pids(sid);
- spin_lock(&group_leader->sighand->siglock);
- group_leader->signal->tty = NULL;
- spin_unlock(&group_leader->sighand->siglock);
+ proc_clear_tty(group_leader);
err = session;
out:
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 50ec088..c468c3c 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -80,7 +80,6 @@
extern int sysctl_drop_caches;
extern int percpu_pagelist_fraction;
extern int compat_log;
-extern int maps_protect;
extern int latencytop_enabled;
extern int sysctl_nr_open_min, sysctl_nr_open_max;
#ifdef CONFIG_RCU_TORTURE_TEST
@@ -118,10 +117,8 @@
extern int sg_big_buff;
#endif
-#ifdef __sparc__
-extern char reboot_command [];
-extern int stop_a_enabled;
-extern int scons_pwroff;
+#ifdef CONFIG_SPARC
+#include <asm/system.h>
#endif
#ifdef __hppa__
@@ -415,7 +412,7 @@
.mode = 0644,
.proc_handler = &proc_dointvec,
},
-#ifdef __sparc__
+#ifdef CONFIG_SPARC
{
.ctl_name = KERN_SPARC_REBOOT,
.procname = "reboot-cmd",
@@ -810,16 +807,6 @@
.proc_handler = &proc_dointvec,
},
#endif
-#ifdef CONFIG_PROC_FS
- {
- .ctl_name = CTL_UNNUMBERED,
- .procname = "maps_protect",
- .data = &maps_protect,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = &proc_dointvec,
- },
-#endif
{
.ctl_name = CTL_UNNUMBERED,
.procname = "poweroff_cmd",
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index ce697e0..aa81d28 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -495,6 +495,15 @@
If unsure, say N.
+config DEBUG_VIRTUAL
+ bool "Debug VM translations"
+ depends on DEBUG_KERNEL && X86
+ help
+ Enable some costly sanity checks in virtual to page code. This can
+ catch mistakes with virt_to_page() and friends.
+
+ If unsure, say N.
+
config DEBUG_WRITECOUNT
bool "Debug filesystem writers count"
depends on DEBUG_KERNEL
diff --git a/lib/cmdline.c b/lib/cmdline.c
index 5ba8a94..f5f3ad8 100644
--- a/lib/cmdline.c
+++ b/lib/cmdline.c
@@ -126,7 +126,7 @@
* megabyte, or one gigabyte, respectively.
*/
-unsigned long long memparse(char *ptr, char **retptr)
+unsigned long long memparse(const char *ptr, char **retptr)
{
char *endptr; /* local pointer to end of parsed string */
diff --git a/lib/parser.c b/lib/parser.c
index 4f0cbc0..b00d020 100644
--- a/lib/parser.c
+++ b/lib/parser.c
@@ -100,7 +100,7 @@
* format identifiers which will be taken into account when matching the
* tokens, and whose locations will be returned in the @args array.
*/
-int match_token(char *s, match_table_t table, substring_t args[])
+int match_token(char *s, const match_table_t table, substring_t args[])
{
const struct match_token *p;
diff --git a/lib/percpu_counter.c b/lib/percpu_counter.c
index 4a8ba4b..a866389 100644
--- a/lib/percpu_counter.c
+++ b/lib/percpu_counter.c
@@ -52,7 +52,7 @@
* Add up all the per-cpu counts, return the result. This is a more accurate
* but much slower version of percpu_counter_read_positive()
*/
-s64 __percpu_counter_sum(struct percpu_counter *fbc, int set)
+s64 __percpu_counter_sum(struct percpu_counter *fbc)
{
s64 ret;
int cpu;
@@ -62,11 +62,9 @@
for_each_online_cpu(cpu) {
s32 *pcount = per_cpu_ptr(fbc->counters, cpu);
ret += *pcount;
- if (set)
- *pcount = 0;
+ *pcount = 0;
}
- if (set)
- fbc->count = ret;
+ fbc->count = ret;
spin_unlock(&fbc->lock);
return ret;
diff --git a/mm/shmem.c b/mm/shmem.c
index 04fb4f1..bf66d01 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -50,14 +50,12 @@
#include <linux/migrate.h>
#include <linux/highmem.h>
#include <linux/seq_file.h>
+#include <linux/magic.h>
#include <asm/uaccess.h>
#include <asm/div64.h>
#include <asm/pgtable.h>
-/* This magic number is used in glibc for posix shared memory */
-#define TMPFS_MAGIC 0x01021994
-
#define ENTRIES_PER_PAGE (PAGE_CACHE_SIZE/sizeof(unsigned long))
#define ENTRIES_PER_PAGEPAGE (ENTRIES_PER_PAGE*ENTRIES_PER_PAGE)
#define BLOCKS_PER_PAGE (PAGE_CACHE_SIZE/512)
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index 85b9a0d..bba06c4 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -180,6 +180,13 @@
pmd_t *pmd;
pte_t *ptep, pte;
+ /*
+ * XXX we might need to change this if we add VIRTUAL_BUG_ON for
+ * architectures that do not vmalloc module space
+ */
+ VIRTUAL_BUG_ON(!is_vmalloc_addr(vmalloc_addr) &&
+ !is_module_address(addr));
+
if (!pgd_none(*pgd)) {
pud = pud_offset(pgd, addr);
if (!pud_none(*pud)) {
diff --git a/net/9p/client.c b/net/9p/client.c
index 10e3203..e053e06 100644
--- a/net/9p/client.c
+++ b/net/9p/client.c
@@ -52,7 +52,7 @@
Opt_err,
};
-static match_table_t tokens = {
+static const match_table_t tokens = {
{Opt_msize, "msize=%u"},
{Opt_legacy, "noextend"},
{Opt_trans, "trans=%s"},
diff --git a/net/9p/trans_fd.c b/net/9p/trans_fd.c
index d652baf..6dabbdb 100644
--- a/net/9p/trans_fd.c
+++ b/net/9p/trans_fd.c
@@ -86,7 +86,7 @@
Opt_port, Opt_rfdno, Opt_wfdno, Opt_err,
};
-static match_table_t tokens = {
+static const match_table_t tokens = {
{Opt_port, "port=%u"},
{Opt_rfdno, "rfdno=%u"},
{Opt_wfdno, "wfdno=%u"},
diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c
index 2c0e457..490e035 100644
--- a/net/ipv4/cipso_ipv4.c
+++ b/net/ipv4/cipso_ipv4.c
@@ -13,7 +13,7 @@
*/
/*
- * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
*
* 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
@@ -47,17 +47,7 @@
#include <asm/bug.h>
#include <asm/unaligned.h>
-struct cipso_v4_domhsh_entry {
- char *domain;
- u32 valid;
- struct list_head list;
- struct rcu_head rcu;
-};
-
/* List of available DOI definitions */
-/* XXX - Updates should be minimal so having a single lock for the
- * cipso_v4_doi_list and the cipso_v4_doi_list->dom_list should be
- * okay. */
/* XXX - This currently assumes a minimal number of different DOIs in use,
* if in practice there are a lot of different DOIs this list should
* probably be turned into a hash table or something similar so we
@@ -119,6 +109,19 @@
* be omitted. */
#define CIPSO_V4_TAG_RNG_CAT_MAX 8
+/* Base length of the local tag (non-standard tag).
+ * Tag definition (may change between kernel versions)
+ *
+ * 0 8 16 24 32
+ * +----------+----------+----------+----------+
+ * | 10000000 | 00000110 | 32-bit secid value |
+ * +----------+----------+----------+----------+
+ * | in (host byte order)|
+ * +----------+----------+
+ *
+ */
+#define CIPSO_V4_TAG_LOC_BLEN 6
+
/*
* Helper Functions
*/
@@ -194,25 +197,6 @@
}
/**
- * cipso_v4_doi_domhsh_free - Frees a domain list entry
- * @entry: the entry's RCU field
- *
- * Description:
- * This function is designed to be used as a callback to the call_rcu()
- * function so that the memory allocated to a domain list entry can be released
- * safely.
- *
- */
-static void cipso_v4_doi_domhsh_free(struct rcu_head *entry)
-{
- struct cipso_v4_domhsh_entry *ptr;
-
- ptr = container_of(entry, struct cipso_v4_domhsh_entry, rcu);
- kfree(ptr->domain);
- kfree(ptr);
-}
-
-/**
* cipso_v4_cache_entry_free - Frees a cache entry
* @entry: the entry to free
*
@@ -457,7 +441,7 @@
struct cipso_v4_doi *iter;
list_for_each_entry_rcu(iter, &cipso_v4_doi_list, list)
- if (iter->doi == doi && iter->valid)
+ if (iter->doi == doi && atomic_read(&iter->refcount))
return iter;
return NULL;
}
@@ -496,14 +480,17 @@
if (doi_def->type != CIPSO_V4_MAP_PASS)
return -EINVAL;
break;
+ case CIPSO_V4_TAG_LOCAL:
+ if (doi_def->type != CIPSO_V4_MAP_LOCAL)
+ return -EINVAL;
+ break;
default:
return -EINVAL;
}
}
- doi_def->valid = 1;
+ atomic_set(&doi_def->refcount, 1);
INIT_RCU_HEAD(&doi_def->rcu);
- INIT_LIST_HEAD(&doi_def->dom_list);
spin_lock(&cipso_v4_doi_list_lock);
if (cipso_v4_doi_search(doi_def->doi) != NULL)
@@ -519,59 +506,129 @@
}
/**
- * cipso_v4_doi_remove - Remove an existing DOI from the CIPSO protocol engine
- * @doi: the DOI value
- * @audit_secid: the LSM secid to use in the audit message
- * @callback: the DOI cleanup/free callback
+ * cipso_v4_doi_free - Frees a DOI definition
+ * @entry: the entry's RCU field
*
* Description:
- * Removes a DOI definition from the CIPSO engine, @callback is called to
- * free any memory. The NetLabel routines will be called to release their own
- * LSM domain mappings as well as our own domain list. Returns zero on
- * success and negative values on failure.
+ * This function frees all of the memory associated with a DOI definition.
*
*/
-int cipso_v4_doi_remove(u32 doi,
- struct netlbl_audit *audit_info,
- void (*callback) (struct rcu_head * head))
+void cipso_v4_doi_free(struct cipso_v4_doi *doi_def)
{
- struct cipso_v4_doi *doi_def;
- struct cipso_v4_domhsh_entry *dom_iter;
+ if (doi_def == NULL)
+ return;
- spin_lock(&cipso_v4_doi_list_lock);
- doi_def = cipso_v4_doi_search(doi);
- if (doi_def != NULL) {
- doi_def->valid = 0;
- list_del_rcu(&doi_def->list);
- spin_unlock(&cipso_v4_doi_list_lock);
- rcu_read_lock();
- list_for_each_entry_rcu(dom_iter, &doi_def->dom_list, list)
- if (dom_iter->valid)
- netlbl_cfg_map_del(dom_iter->domain,
- audit_info);
- rcu_read_unlock();
- cipso_v4_cache_invalidate();
- call_rcu(&doi_def->rcu, callback);
- return 0;
+ switch (doi_def->type) {
+ case CIPSO_V4_MAP_TRANS:
+ kfree(doi_def->map.std->lvl.cipso);
+ kfree(doi_def->map.std->lvl.local);
+ kfree(doi_def->map.std->cat.cipso);
+ kfree(doi_def->map.std->cat.local);
+ break;
}
- spin_unlock(&cipso_v4_doi_list_lock);
-
- return -ENOENT;
+ kfree(doi_def);
}
/**
- * cipso_v4_doi_getdef - Returns a pointer to a valid DOI definition
+ * cipso_v4_doi_free_rcu - Frees a DOI definition via the RCU pointer
+ * @entry: the entry's RCU field
+ *
+ * Description:
+ * This function is designed to be used as a callback to the call_rcu()
+ * function so that the memory allocated to the DOI definition can be released
+ * safely.
+ *
+ */
+static void cipso_v4_doi_free_rcu(struct rcu_head *entry)
+{
+ struct cipso_v4_doi *doi_def;
+
+ doi_def = container_of(entry, struct cipso_v4_doi, rcu);
+ cipso_v4_doi_free(doi_def);
+}
+
+/**
+ * cipso_v4_doi_remove - Remove an existing DOI from the CIPSO protocol engine
+ * @doi: the DOI value
+ * @audit_secid: the LSM secid to use in the audit message
+ *
+ * Description:
+ * Removes a DOI definition from the CIPSO engine. The NetLabel routines will
+ * be called to release their own LSM domain mappings as well as our own
+ * domain list. Returns zero on success and negative values on failure.
+ *
+ */
+int cipso_v4_doi_remove(u32 doi, struct netlbl_audit *audit_info)
+{
+ struct cipso_v4_doi *doi_def;
+
+ spin_lock(&cipso_v4_doi_list_lock);
+ doi_def = cipso_v4_doi_search(doi);
+ if (doi_def == NULL) {
+ spin_unlock(&cipso_v4_doi_list_lock);
+ return -ENOENT;
+ }
+ if (!atomic_dec_and_test(&doi_def->refcount)) {
+ spin_unlock(&cipso_v4_doi_list_lock);
+ return -EBUSY;
+ }
+ list_del_rcu(&doi_def->list);
+ spin_unlock(&cipso_v4_doi_list_lock);
+
+ cipso_v4_cache_invalidate();
+ call_rcu(&doi_def->rcu, cipso_v4_doi_free_rcu);
+
+ return 0;
+}
+
+/**
+ * cipso_v4_doi_getdef - Returns a reference to a valid DOI definition
* @doi: the DOI value
*
* Description:
* Searches for a valid DOI definition and if one is found it is returned to
* the caller. Otherwise NULL is returned. The caller must ensure that
- * rcu_read_lock() is held while accessing the returned definition.
+ * rcu_read_lock() is held while accessing the returned definition and the DOI
+ * definition reference count is decremented when the caller is done.
*
*/
struct cipso_v4_doi *cipso_v4_doi_getdef(u32 doi)
{
- return cipso_v4_doi_search(doi);
+ struct cipso_v4_doi *doi_def;
+
+ rcu_read_lock();
+ doi_def = cipso_v4_doi_search(doi);
+ if (doi_def == NULL)
+ goto doi_getdef_return;
+ if (!atomic_inc_not_zero(&doi_def->refcount))
+ doi_def = NULL;
+
+doi_getdef_return:
+ rcu_read_unlock();
+ return doi_def;
+}
+
+/**
+ * cipso_v4_doi_putdef - Releases a reference for the given DOI definition
+ * @doi_def: the DOI definition
+ *
+ * Description:
+ * Releases a DOI definition reference obtained from cipso_v4_doi_getdef().
+ *
+ */
+void cipso_v4_doi_putdef(struct cipso_v4_doi *doi_def)
+{
+ if (doi_def == NULL)
+ return;
+
+ if (!atomic_dec_and_test(&doi_def->refcount))
+ return;
+ spin_lock(&cipso_v4_doi_list_lock);
+ list_del_rcu(&doi_def->list);
+ spin_unlock(&cipso_v4_doi_list_lock);
+
+ cipso_v4_cache_invalidate();
+ call_rcu(&doi_def->rcu, cipso_v4_doi_free_rcu);
}
/**
@@ -597,7 +654,7 @@
rcu_read_lock();
list_for_each_entry_rcu(iter_doi, &cipso_v4_doi_list, list)
- if (iter_doi->valid) {
+ if (atomic_read(&iter_doi->refcount) > 0) {
if (doi_cnt++ < *skip_cnt)
continue;
ret_val = callback(iter_doi, cb_arg);
@@ -613,85 +670,6 @@
return ret_val;
}
-/**
- * cipso_v4_doi_domhsh_add - Adds a domain entry to a DOI definition
- * @doi_def: the DOI definition
- * @domain: the domain to add
- *
- * Description:
- * Adds the @domain to the DOI specified by @doi_def, this function
- * should only be called by external functions (i.e. NetLabel). This function
- * does allocate memory. Returns zero on success, negative values on failure.
- *
- */
-int cipso_v4_doi_domhsh_add(struct cipso_v4_doi *doi_def, const char *domain)
-{
- struct cipso_v4_domhsh_entry *iter;
- struct cipso_v4_domhsh_entry *new_dom;
-
- new_dom = kzalloc(sizeof(*new_dom), GFP_KERNEL);
- if (new_dom == NULL)
- return -ENOMEM;
- if (domain) {
- new_dom->domain = kstrdup(domain, GFP_KERNEL);
- if (new_dom->domain == NULL) {
- kfree(new_dom);
- return -ENOMEM;
- }
- }
- new_dom->valid = 1;
- INIT_RCU_HEAD(&new_dom->rcu);
-
- spin_lock(&cipso_v4_doi_list_lock);
- list_for_each_entry(iter, &doi_def->dom_list, list)
- if (iter->valid &&
- ((domain != NULL && iter->domain != NULL &&
- strcmp(iter->domain, domain) == 0) ||
- (domain == NULL && iter->domain == NULL))) {
- spin_unlock(&cipso_v4_doi_list_lock);
- kfree(new_dom->domain);
- kfree(new_dom);
- return -EEXIST;
- }
- list_add_tail_rcu(&new_dom->list, &doi_def->dom_list);
- spin_unlock(&cipso_v4_doi_list_lock);
-
- return 0;
-}
-
-/**
- * cipso_v4_doi_domhsh_remove - Removes a domain entry from a DOI definition
- * @doi_def: the DOI definition
- * @domain: the domain to remove
- *
- * Description:
- * Removes the @domain from the DOI specified by @doi_def, this function
- * should only be called by external functions (i.e. NetLabel). Returns zero
- * on success and negative values on error.
- *
- */
-int cipso_v4_doi_domhsh_remove(struct cipso_v4_doi *doi_def,
- const char *domain)
-{
- struct cipso_v4_domhsh_entry *iter;
-
- spin_lock(&cipso_v4_doi_list_lock);
- list_for_each_entry(iter, &doi_def->dom_list, list)
- if (iter->valid &&
- ((domain != NULL && iter->domain != NULL &&
- strcmp(iter->domain, domain) == 0) ||
- (domain == NULL && iter->domain == NULL))) {
- iter->valid = 0;
- list_del_rcu(&iter->list);
- spin_unlock(&cipso_v4_doi_list_lock);
- call_rcu(&iter->rcu, cipso_v4_doi_domhsh_free);
- return 0;
- }
- spin_unlock(&cipso_v4_doi_list_lock);
-
- return -ENOENT;
-}
-
/*
* Label Mapping Functions
*/
@@ -712,7 +690,7 @@
switch (doi_def->type) {
case CIPSO_V4_MAP_PASS:
return 0;
- case CIPSO_V4_MAP_STD:
+ case CIPSO_V4_MAP_TRANS:
if (doi_def->map.std->lvl.cipso[level] < CIPSO_V4_INV_LVL)
return 0;
break;
@@ -741,7 +719,7 @@
case CIPSO_V4_MAP_PASS:
*net_lvl = host_lvl;
return 0;
- case CIPSO_V4_MAP_STD:
+ case CIPSO_V4_MAP_TRANS:
if (host_lvl < doi_def->map.std->lvl.local_size &&
doi_def->map.std->lvl.local[host_lvl] < CIPSO_V4_INV_LVL) {
*net_lvl = doi_def->map.std->lvl.local[host_lvl];
@@ -775,7 +753,7 @@
case CIPSO_V4_MAP_PASS:
*host_lvl = net_lvl;
return 0;
- case CIPSO_V4_MAP_STD:
+ case CIPSO_V4_MAP_TRANS:
map_tbl = doi_def->map.std;
if (net_lvl < map_tbl->lvl.cipso_size &&
map_tbl->lvl.cipso[net_lvl] < CIPSO_V4_INV_LVL) {
@@ -812,7 +790,7 @@
switch (doi_def->type) {
case CIPSO_V4_MAP_PASS:
return 0;
- case CIPSO_V4_MAP_STD:
+ case CIPSO_V4_MAP_TRANS:
cipso_cat_size = doi_def->map.std->cat.cipso_size;
cipso_array = doi_def->map.std->cat.cipso;
for (;;) {
@@ -860,7 +838,7 @@
u32 host_cat_size = 0;
u32 *host_cat_array = NULL;
- if (doi_def->type == CIPSO_V4_MAP_STD) {
+ if (doi_def->type == CIPSO_V4_MAP_TRANS) {
host_cat_size = doi_def->map.std->cat.local_size;
host_cat_array = doi_def->map.std->cat.local;
}
@@ -875,7 +853,7 @@
case CIPSO_V4_MAP_PASS:
net_spot = host_spot;
break;
- case CIPSO_V4_MAP_STD:
+ case CIPSO_V4_MAP_TRANS:
if (host_spot >= host_cat_size)
return -EPERM;
net_spot = host_cat_array[host_spot];
@@ -921,7 +899,7 @@
u32 net_cat_size = 0;
u32 *net_cat_array = NULL;
- if (doi_def->type == CIPSO_V4_MAP_STD) {
+ if (doi_def->type == CIPSO_V4_MAP_TRANS) {
net_cat_size = doi_def->map.std->cat.cipso_size;
net_cat_array = doi_def->map.std->cat.cipso;
}
@@ -941,7 +919,7 @@
case CIPSO_V4_MAP_PASS:
host_spot = net_spot;
break;
- case CIPSO_V4_MAP_STD:
+ case CIPSO_V4_MAP_TRANS:
if (net_spot >= net_cat_size)
return -EPERM;
host_spot = net_cat_array[net_spot];
@@ -1277,7 +1255,7 @@
} else
tag_len = 4;
- buffer[0] = 0x01;
+ buffer[0] = CIPSO_V4_TAG_RBITMAP;
buffer[1] = tag_len;
buffer[3] = level;
@@ -1373,7 +1351,7 @@
} else
tag_len = 4;
- buffer[0] = 0x02;
+ buffer[0] = CIPSO_V4_TAG_ENUM;
buffer[1] = tag_len;
buffer[3] = level;
@@ -1469,7 +1447,7 @@
} else
tag_len = 4;
- buffer[0] = 0x05;
+ buffer[0] = CIPSO_V4_TAG_RANGE;
buffer[1] = tag_len;
buffer[3] = level;
@@ -1523,6 +1501,54 @@
}
/**
+ * cipso_v4_gentag_loc - Generate a CIPSO local tag (non-standard)
+ * @doi_def: the DOI definition
+ * @secattr: the security attributes
+ * @buffer: the option buffer
+ * @buffer_len: length of buffer in bytes
+ *
+ * Description:
+ * Generate a CIPSO option using the local tag. Returns the size of the tag
+ * on success, negative values on failure.
+ *
+ */
+static int cipso_v4_gentag_loc(const struct cipso_v4_doi *doi_def,
+ const struct netlbl_lsm_secattr *secattr,
+ unsigned char *buffer,
+ u32 buffer_len)
+{
+ if (!(secattr->flags & NETLBL_SECATTR_SECID))
+ return -EPERM;
+
+ buffer[0] = CIPSO_V4_TAG_LOCAL;
+ buffer[1] = CIPSO_V4_TAG_LOC_BLEN;
+ *(u32 *)&buffer[2] = secattr->attr.secid;
+
+ return CIPSO_V4_TAG_LOC_BLEN;
+}
+
+/**
+ * cipso_v4_parsetag_loc - Parse a CIPSO local tag
+ * @doi_def: the DOI definition
+ * @tag: the CIPSO tag
+ * @secattr: the security attributes
+ *
+ * Description:
+ * Parse a CIPSO local tag and return the security attributes in @secattr.
+ * Return zero on success, negatives values on failure.
+ *
+ */
+static int cipso_v4_parsetag_loc(const struct cipso_v4_doi *doi_def,
+ const unsigned char *tag,
+ struct netlbl_lsm_secattr *secattr)
+{
+ secattr->attr.secid = *(u32 *)&tag[2];
+ secattr->flags |= NETLBL_SECATTR_SECID;
+
+ return 0;
+}
+
+/**
* cipso_v4_validate - Validate a CIPSO option
* @option: the start of the option, on error it is set to point to the error
*
@@ -1541,7 +1567,7 @@
* that is unrecognized."
*
*/
-int cipso_v4_validate(unsigned char **option)
+int cipso_v4_validate(const struct sk_buff *skb, unsigned char **option)
{
unsigned char *opt = *option;
unsigned char *tag;
@@ -1566,7 +1592,7 @@
goto validate_return_locked;
}
- opt_iter = 6;
+ opt_iter = CIPSO_V4_HDR_LEN;
tag = opt + opt_iter;
while (opt_iter < opt_len) {
for (tag_iter = 0; doi_def->tags[tag_iter] != tag[0];)
@@ -1584,7 +1610,7 @@
switch (tag[0]) {
case CIPSO_V4_TAG_RBITMAP:
- if (tag_len < 4) {
+ if (tag_len < CIPSO_V4_TAG_RBM_BLEN) {
err_offset = opt_iter + 1;
goto validate_return_locked;
}
@@ -1602,7 +1628,7 @@
err_offset = opt_iter + 3;
goto validate_return_locked;
}
- if (tag_len > 4 &&
+ if (tag_len > CIPSO_V4_TAG_RBM_BLEN &&
cipso_v4_map_cat_rbm_valid(doi_def,
&tag[4],
tag_len - 4) < 0) {
@@ -1612,7 +1638,7 @@
}
break;
case CIPSO_V4_TAG_ENUM:
- if (tag_len < 4) {
+ if (tag_len < CIPSO_V4_TAG_ENUM_BLEN) {
err_offset = opt_iter + 1;
goto validate_return_locked;
}
@@ -1622,7 +1648,7 @@
err_offset = opt_iter + 3;
goto validate_return_locked;
}
- if (tag_len > 4 &&
+ if (tag_len > CIPSO_V4_TAG_ENUM_BLEN &&
cipso_v4_map_cat_enum_valid(doi_def,
&tag[4],
tag_len - 4) < 0) {
@@ -1631,7 +1657,7 @@
}
break;
case CIPSO_V4_TAG_RANGE:
- if (tag_len < 4) {
+ if (tag_len < CIPSO_V4_TAG_RNG_BLEN) {
err_offset = opt_iter + 1;
goto validate_return_locked;
}
@@ -1641,7 +1667,7 @@
err_offset = opt_iter + 3;
goto validate_return_locked;
}
- if (tag_len > 4 &&
+ if (tag_len > CIPSO_V4_TAG_RNG_BLEN &&
cipso_v4_map_cat_rng_valid(doi_def,
&tag[4],
tag_len - 4) < 0) {
@@ -1649,6 +1675,19 @@
goto validate_return_locked;
}
break;
+ case CIPSO_V4_TAG_LOCAL:
+ /* This is a non-standard tag that we only allow for
+ * local connections, so if the incoming interface is
+ * not the loopback device drop the packet. */
+ if (!(skb->dev->flags & IFF_LOOPBACK)) {
+ err_offset = opt_iter;
+ goto validate_return_locked;
+ }
+ if (tag_len != CIPSO_V4_TAG_LOC_BLEN) {
+ err_offset = opt_iter + 1;
+ goto validate_return_locked;
+ }
+ break;
default:
err_offset = opt_iter;
goto validate_return_locked;
@@ -1704,48 +1743,27 @@
}
/**
- * cipso_v4_sock_setattr - Add a CIPSO option to a socket
- * @sk: the socket
+ * cipso_v4_genopt - Generate a CIPSO option
+ * @buf: the option buffer
+ * @buf_len: the size of opt_buf
* @doi_def: the CIPSO DOI to use
- * @secattr: the specific security attributes of the socket
+ * @secattr: the security attributes
*
* Description:
- * Set the CIPSO option on the given socket using the DOI definition and
- * security attributes passed to the function. This function requires
- * exclusive access to @sk, which means it either needs to be in the
- * process of being created or locked. Returns zero on success and negative
- * values on failure.
+ * Generate a CIPSO option using the DOI definition and security attributes
+ * passed to the function. Returns the length of the option on success and
+ * negative values on failure.
*
*/
-int cipso_v4_sock_setattr(struct sock *sk,
- const struct cipso_v4_doi *doi_def,
- const struct netlbl_lsm_secattr *secattr)
+static int cipso_v4_genopt(unsigned char *buf, u32 buf_len,
+ const struct cipso_v4_doi *doi_def,
+ const struct netlbl_lsm_secattr *secattr)
{
- int ret_val = -EPERM;
+ int ret_val;
u32 iter;
- unsigned char *buf;
- u32 buf_len = 0;
- u32 opt_len;
- struct ip_options *opt = NULL;
- struct inet_sock *sk_inet;
- struct inet_connection_sock *sk_conn;
- /* In the case of sock_create_lite(), the sock->sk field is not
- * defined yet but it is not a problem as the only users of these
- * "lite" PF_INET sockets are functions which do an accept() call
- * afterwards so we will label the socket as part of the accept(). */
- if (sk == NULL)
- return 0;
-
- /* We allocate the maximum CIPSO option size here so we are probably
- * being a little wasteful, but it makes our life _much_ easier later
- * on and after all we are only talking about 40 bytes. */
- buf_len = CIPSO_V4_OPT_LEN_MAX;
- buf = kmalloc(buf_len, GFP_ATOMIC);
- if (buf == NULL) {
- ret_val = -ENOMEM;
- goto socket_setattr_failure;
- }
+ if (buf_len <= CIPSO_V4_HDR_LEN)
+ return -ENOSPC;
/* XXX - This code assumes only one tag per CIPSO option which isn't
* really a good assumption to make but since we only support the MAC
@@ -1772,9 +1790,14 @@
&buf[CIPSO_V4_HDR_LEN],
buf_len - CIPSO_V4_HDR_LEN);
break;
+ case CIPSO_V4_TAG_LOCAL:
+ ret_val = cipso_v4_gentag_loc(doi_def,
+ secattr,
+ &buf[CIPSO_V4_HDR_LEN],
+ buf_len - CIPSO_V4_HDR_LEN);
+ break;
default:
- ret_val = -EPERM;
- goto socket_setattr_failure;
+ return -EPERM;
}
iter++;
@@ -1782,9 +1805,58 @@
iter < CIPSO_V4_TAG_MAXCNT &&
doi_def->tags[iter] != CIPSO_V4_TAG_INVALID);
if (ret_val < 0)
- goto socket_setattr_failure;
+ return ret_val;
cipso_v4_gentag_hdr(doi_def, buf, ret_val);
- buf_len = CIPSO_V4_HDR_LEN + ret_val;
+ return CIPSO_V4_HDR_LEN + ret_val;
+}
+
+/**
+ * cipso_v4_sock_setattr - Add a CIPSO option to a socket
+ * @sk: the socket
+ * @doi_def: the CIPSO DOI to use
+ * @secattr: the specific security attributes of the socket
+ *
+ * Description:
+ * Set the CIPSO option on the given socket using the DOI definition and
+ * security attributes passed to the function. This function requires
+ * exclusive access to @sk, which means it either needs to be in the
+ * process of being created or locked. Returns zero on success and negative
+ * values on failure.
+ *
+ */
+int cipso_v4_sock_setattr(struct sock *sk,
+ const struct cipso_v4_doi *doi_def,
+ const struct netlbl_lsm_secattr *secattr)
+{
+ int ret_val = -EPERM;
+ unsigned char *buf = NULL;
+ u32 buf_len;
+ u32 opt_len;
+ struct ip_options *opt = NULL;
+ struct inet_sock *sk_inet;
+ struct inet_connection_sock *sk_conn;
+
+ /* In the case of sock_create_lite(), the sock->sk field is not
+ * defined yet but it is not a problem as the only users of these
+ * "lite" PF_INET sockets are functions which do an accept() call
+ * afterwards so we will label the socket as part of the accept(). */
+ if (sk == NULL)
+ return 0;
+
+ /* We allocate the maximum CIPSO option size here so we are probably
+ * being a little wasteful, but it makes our life _much_ easier later
+ * on and after all we are only talking about 40 bytes. */
+ buf_len = CIPSO_V4_OPT_LEN_MAX;
+ buf = kmalloc(buf_len, GFP_ATOMIC);
+ if (buf == NULL) {
+ ret_val = -ENOMEM;
+ goto socket_setattr_failure;
+ }
+
+ ret_val = cipso_v4_genopt(buf, buf_len, doi_def, secattr);
+ if (ret_val < 0)
+ goto socket_setattr_failure;
+ buf_len = ret_val;
/* We can't use ip_options_get() directly because it makes a call to
* ip_options_get_alloc() which allocates memory with GFP_KERNEL and
@@ -1822,6 +1894,80 @@
}
/**
+ * cipso_v4_sock_delattr - Delete the CIPSO option from a socket
+ * @sk: the socket
+ *
+ * Description:
+ * Removes the CIPSO option from a socket, if present.
+ *
+ */
+void cipso_v4_sock_delattr(struct sock *sk)
+{
+ u8 hdr_delta;
+ struct ip_options *opt;
+ struct inet_sock *sk_inet;
+
+ sk_inet = inet_sk(sk);
+ opt = sk_inet->opt;
+ if (opt == NULL || opt->cipso == 0)
+ return;
+
+ if (opt->srr || opt->rr || opt->ts || opt->router_alert) {
+ u8 cipso_len;
+ u8 cipso_off;
+ unsigned char *cipso_ptr;
+ int iter;
+ int optlen_new;
+
+ cipso_off = opt->cipso - sizeof(struct iphdr);
+ cipso_ptr = &opt->__data[cipso_off];
+ cipso_len = cipso_ptr[1];
+
+ if (opt->srr > opt->cipso)
+ opt->srr -= cipso_len;
+ if (opt->rr > opt->cipso)
+ opt->rr -= cipso_len;
+ if (opt->ts > opt->cipso)
+ opt->ts -= cipso_len;
+ if (opt->router_alert > opt->cipso)
+ opt->router_alert -= cipso_len;
+ opt->cipso = 0;
+
+ memmove(cipso_ptr, cipso_ptr + cipso_len,
+ opt->optlen - cipso_off - cipso_len);
+
+ /* determining the new total option length is tricky because of
+ * the padding necessary, the only thing i can think to do at
+ * this point is walk the options one-by-one, skipping the
+ * padding at the end to determine the actual option size and
+ * from there we can determine the new total option length */
+ iter = 0;
+ optlen_new = 0;
+ while (iter < opt->optlen)
+ if (opt->__data[iter] != IPOPT_NOP) {
+ iter += opt->__data[iter + 1];
+ optlen_new = iter;
+ } else
+ iter++;
+ hdr_delta = opt->optlen;
+ opt->optlen = (optlen_new + 3) & ~3;
+ hdr_delta -= opt->optlen;
+ } else {
+ /* only the cipso option was present on the socket so we can
+ * remove the entire option struct */
+ sk_inet->opt = NULL;
+ hdr_delta = opt->optlen;
+ kfree(opt);
+ }
+
+ if (sk_inet->is_icsk && hdr_delta > 0) {
+ struct inet_connection_sock *sk_conn = inet_csk(sk);
+ sk_conn->icsk_ext_hdr_len -= hdr_delta;
+ sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie);
+ }
+}
+
+/**
* cipso_v4_getattr - Helper function for the cipso_v4_*_getattr functions
* @cipso: the CIPSO v4 option
* @secattr: the security attributes
@@ -1859,6 +2005,9 @@
case CIPSO_V4_TAG_RANGE:
ret_val = cipso_v4_parsetag_rng(doi_def, &cipso[6], secattr);
break;
+ case CIPSO_V4_TAG_LOCAL:
+ ret_val = cipso_v4_parsetag_loc(doi_def, &cipso[6], secattr);
+ break;
}
if (ret_val == 0)
secattr->type = NETLBL_NLTYPE_CIPSOV4;
@@ -1893,6 +2042,123 @@
}
/**
+ * cipso_v4_skbuff_setattr - Set the CIPSO option on a packet
+ * @skb: the packet
+ * @secattr: the security attributes
+ *
+ * Description:
+ * Set the CIPSO option on the given packet based on the security attributes.
+ * Returns a pointer to the IP header on success and NULL on failure.
+ *
+ */
+int cipso_v4_skbuff_setattr(struct sk_buff *skb,
+ const struct cipso_v4_doi *doi_def,
+ const struct netlbl_lsm_secattr *secattr)
+{
+ int ret_val;
+ struct iphdr *iph;
+ struct ip_options *opt = &IPCB(skb)->opt;
+ unsigned char buf[CIPSO_V4_OPT_LEN_MAX];
+ u32 buf_len = CIPSO_V4_OPT_LEN_MAX;
+ u32 opt_len;
+ int len_delta;
+
+ buf_len = cipso_v4_genopt(buf, buf_len, doi_def, secattr);
+ if (buf_len < 0)
+ return buf_len;
+ opt_len = (buf_len + 3) & ~3;
+
+ /* we overwrite any existing options to ensure that we have enough
+ * room for the CIPSO option, the reason is that we _need_ to guarantee
+ * that the security label is applied to the packet - we do the same
+ * thing when using the socket options and it hasn't caused a problem,
+ * if we need to we can always revisit this choice later */
+
+ len_delta = opt_len - opt->optlen;
+ /* if we don't ensure enough headroom we could panic on the skb_push()
+ * call below so make sure we have enough, we are also "mangling" the
+ * packet so we should probably do a copy-on-write call anyway */
+ ret_val = skb_cow(skb, skb_headroom(skb) + len_delta);
+ if (ret_val < 0)
+ return ret_val;
+
+ if (len_delta > 0) {
+ /* we assume that the header + opt->optlen have already been
+ * "pushed" in ip_options_build() or similar */
+ iph = ip_hdr(skb);
+ skb_push(skb, len_delta);
+ memmove((char *)iph - len_delta, iph, iph->ihl << 2);
+ skb_reset_network_header(skb);
+ iph = ip_hdr(skb);
+ } else if (len_delta < 0) {
+ iph = ip_hdr(skb);
+ memset(iph + 1, IPOPT_NOP, opt->optlen);
+ } else
+ iph = ip_hdr(skb);
+
+ if (opt->optlen > 0)
+ memset(opt, 0, sizeof(*opt));
+ opt->optlen = opt_len;
+ opt->cipso = sizeof(struct iphdr);
+ opt->is_changed = 1;
+
+ /* we have to do the following because we are being called from a
+ * netfilter hook which means the packet already has had the header
+ * fields populated and the checksum calculated - yes this means we
+ * are doing more work than needed but we do it to keep the core
+ * stack clean and tidy */
+ memcpy(iph + 1, buf, buf_len);
+ if (opt_len > buf_len)
+ memset((char *)(iph + 1) + buf_len, 0, opt_len - buf_len);
+ if (len_delta != 0) {
+ iph->ihl = 5 + (opt_len >> 2);
+ iph->tot_len = htons(skb->len);
+ }
+ ip_send_check(iph);
+
+ return 0;
+}
+
+/**
+ * cipso_v4_skbuff_delattr - Delete any CIPSO options from a packet
+ * @skb: the packet
+ *
+ * Description:
+ * Removes any and all CIPSO options from the given packet. Returns zero on
+ * success, negative values on failure.
+ *
+ */
+int cipso_v4_skbuff_delattr(struct sk_buff *skb)
+{
+ int ret_val;
+ struct iphdr *iph;
+ struct ip_options *opt = &IPCB(skb)->opt;
+ unsigned char *cipso_ptr;
+
+ if (opt->cipso == 0)
+ return 0;
+
+ /* since we are changing the packet we should make a copy */
+ ret_val = skb_cow(skb, skb_headroom(skb));
+ if (ret_val < 0)
+ return ret_val;
+
+ /* the easiest thing to do is just replace the cipso option with noop
+ * options since we don't change the size of the packet, although we
+ * still need to recalculate the checksum */
+
+ iph = ip_hdr(skb);
+ cipso_ptr = (unsigned char *)iph + opt->cipso;
+ memset(cipso_ptr, IPOPT_NOOP, cipso_ptr[1]);
+ opt->cipso = 0;
+ opt->is_changed = 1;
+
+ ip_send_check(iph);
+
+ return 0;
+}
+
+/**
* cipso_v4_skbuff_getattr - Get the security attributes from the CIPSO option
* @skb: the packet
* @secattr: the security attributes
diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c
index be3f18a..2c88da6 100644
--- a/net/ipv4/ip_options.c
+++ b/net/ipv4/ip_options.c
@@ -438,7 +438,7 @@
goto error;
}
opt->cipso = optptr - iph;
- if (cipso_v4_validate(&optptr)) {
+ if (cipso_v4_validate(skb, &optptr)) {
pp_ptr = optptr;
goto error;
}
diff --git a/net/netlabel/Makefile b/net/netlabel/Makefile
index 8af18c0..ea750e9 100644
--- a/net/netlabel/Makefile
+++ b/net/netlabel/Makefile
@@ -5,7 +5,8 @@
#
# base objects
-obj-y := netlabel_user.o netlabel_kapi.o netlabel_domainhash.o
+obj-y := netlabel_user.o netlabel_kapi.o
+obj-y += netlabel_domainhash.o netlabel_addrlist.o
# management objects
obj-y += netlabel_mgmt.o
diff --git a/net/netlabel/netlabel_addrlist.c b/net/netlabel/netlabel_addrlist.c
new file mode 100644
index 0000000..b0925a3
--- /dev/null
+++ b/net/netlabel/netlabel_addrlist.c
@@ -0,0 +1,388 @@
+/*
+ * NetLabel Network Address Lists
+ *
+ * This file contains network address list functions used to manage ordered
+ * lists of network addresses for use by the NetLabel subsystem. The NetLabel
+ * system manages static and dynamic label mappings for network protocols such
+ * as CIPSO and RIPSO.
+ *
+ * Author: Paul Moore <paul.moore@hp.com>
+ *
+ */
+
+/*
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2008
+ *
+ * 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
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/rcupdate.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <linux/audit.h>
+
+#include "netlabel_addrlist.h"
+
+/*
+ * Address List Functions
+ */
+
+/**
+ * netlbl_af4list_search - Search for a matching IPv4 address entry
+ * @addr: IPv4 address
+ * @head: the list head
+ *
+ * Description:
+ * Searches the IPv4 address list given by @head. If a matching address entry
+ * is found it is returned, otherwise NULL is returned. The caller is
+ * responsible for calling the rcu_read_[un]lock() functions.
+ *
+ */
+struct netlbl_af4list *netlbl_af4list_search(__be32 addr,
+ struct list_head *head)
+{
+ struct netlbl_af4list *iter;
+
+ list_for_each_entry_rcu(iter, head, list)
+ if (iter->valid && (addr & iter->mask) == iter->addr)
+ return iter;
+
+ return NULL;
+}
+
+/**
+ * netlbl_af4list_search_exact - Search for an exact IPv4 address entry
+ * @addr: IPv4 address
+ * @mask: IPv4 address mask
+ * @head: the list head
+ *
+ * Description:
+ * Searches the IPv4 address list given by @head. If an exact match if found
+ * it is returned, otherwise NULL is returned. The caller is responsible for
+ * calling the rcu_read_[un]lock() functions.
+ *
+ */
+struct netlbl_af4list *netlbl_af4list_search_exact(__be32 addr,
+ __be32 mask,
+ struct list_head *head)
+{
+ struct netlbl_af4list *iter;
+
+ list_for_each_entry_rcu(iter, head, list)
+ if (iter->valid && iter->addr == addr && iter->mask == mask)
+ return iter;
+
+ return NULL;
+}
+
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+/**
+ * netlbl_af6list_search - Search for a matching IPv6 address entry
+ * @addr: IPv6 address
+ * @head: the list head
+ *
+ * Description:
+ * Searches the IPv6 address list given by @head. If a matching address entry
+ * is found it is returned, otherwise NULL is returned. The caller is
+ * responsible for calling the rcu_read_[un]lock() functions.
+ *
+ */
+struct netlbl_af6list *netlbl_af6list_search(const struct in6_addr *addr,
+ struct list_head *head)
+{
+ struct netlbl_af6list *iter;
+
+ list_for_each_entry_rcu(iter, head, list)
+ if (iter->valid &&
+ ipv6_masked_addr_cmp(&iter->addr, &iter->mask, addr) == 0)
+ return iter;
+
+ return NULL;
+}
+
+/**
+ * netlbl_af6list_search_exact - Search for an exact IPv6 address entry
+ * @addr: IPv6 address
+ * @mask: IPv6 address mask
+ * @head: the list head
+ *
+ * Description:
+ * Searches the IPv6 address list given by @head. If an exact match if found
+ * it is returned, otherwise NULL is returned. The caller is responsible for
+ * calling the rcu_read_[un]lock() functions.
+ *
+ */
+struct netlbl_af6list *netlbl_af6list_search_exact(const struct in6_addr *addr,
+ const struct in6_addr *mask,
+ struct list_head *head)
+{
+ struct netlbl_af6list *iter;
+
+ list_for_each_entry_rcu(iter, head, list)
+ if (iter->valid &&
+ ipv6_addr_equal(&iter->addr, addr) &&
+ ipv6_addr_equal(&iter->mask, mask))
+ return iter;
+
+ return NULL;
+}
+#endif /* IPv6 */
+
+/**
+ * netlbl_af4list_add - Add a new IPv4 address entry to a list
+ * @entry: address entry
+ * @head: the list head
+ *
+ * Description:
+ * Add a new address entry to the list pointed to by @head. On success zero is
+ * returned, otherwise a negative value is returned. The caller is responsible
+ * for calling the necessary locking functions.
+ *
+ */
+int netlbl_af4list_add(struct netlbl_af4list *entry, struct list_head *head)
+{
+ struct netlbl_af4list *iter;
+
+ iter = netlbl_af4list_search(entry->addr, head);
+ if (iter != NULL &&
+ iter->addr == entry->addr && iter->mask == entry->mask)
+ return -EEXIST;
+
+ /* in order to speed up address searches through the list (the common
+ * case) we need to keep the list in order based on the size of the
+ * address mask such that the entry with the widest mask (smallest
+ * numerical value) appears first in the list */
+ list_for_each_entry_rcu(iter, head, list)
+ if (iter->valid &&
+ ntohl(entry->mask) > ntohl(iter->mask)) {
+ __list_add_rcu(&entry->list,
+ iter->list.prev,
+ &iter->list);
+ return 0;
+ }
+ list_add_tail_rcu(&entry->list, head);
+ return 0;
+}
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+/**
+ * netlbl_af6list_add - Add a new IPv6 address entry to a list
+ * @entry: address entry
+ * @head: the list head
+ *
+ * Description:
+ * Add a new address entry to the list pointed to by @head. On success zero is
+ * returned, otherwise a negative value is returned. The caller is responsible
+ * for calling the necessary locking functions.
+ *
+ */
+int netlbl_af6list_add(struct netlbl_af6list *entry, struct list_head *head)
+{
+ struct netlbl_af6list *iter;
+
+ iter = netlbl_af6list_search(&entry->addr, head);
+ if (iter != NULL &&
+ ipv6_addr_equal(&iter->addr, &entry->addr) &&
+ ipv6_addr_equal(&iter->mask, &entry->mask))
+ return -EEXIST;
+
+ /* in order to speed up address searches through the list (the common
+ * case) we need to keep the list in order based on the size of the
+ * address mask such that the entry with the widest mask (smallest
+ * numerical value) appears first in the list */
+ list_for_each_entry_rcu(iter, head, list)
+ if (iter->valid &&
+ ipv6_addr_cmp(&entry->mask, &iter->mask) > 0) {
+ __list_add_rcu(&entry->list,
+ iter->list.prev,
+ &iter->list);
+ return 0;
+ }
+ list_add_tail_rcu(&entry->list, head);
+ return 0;
+}
+#endif /* IPv6 */
+
+/**
+ * netlbl_af4list_remove_entry - Remove an IPv4 address entry
+ * @entry: address entry
+ *
+ * Description:
+ * Remove the specified IP address entry. The caller is responsible for
+ * calling the necessary locking functions.
+ *
+ */
+void netlbl_af4list_remove_entry(struct netlbl_af4list *entry)
+{
+ entry->valid = 0;
+ list_del_rcu(&entry->list);
+}
+
+/**
+ * netlbl_af4list_remove - Remove an IPv4 address entry
+ * @addr: IP address
+ * @mask: IP address mask
+ * @head: the list head
+ *
+ * Description:
+ * Remove an IP address entry from the list pointed to by @head. Returns the
+ * entry on success, NULL on failure. The caller is responsible for calling
+ * the necessary locking functions.
+ *
+ */
+struct netlbl_af4list *netlbl_af4list_remove(__be32 addr, __be32 mask,
+ struct list_head *head)
+{
+ struct netlbl_af4list *entry;
+
+ entry = netlbl_af4list_search(addr, head);
+ if (entry != NULL && entry->addr == addr && entry->mask == mask) {
+ netlbl_af4list_remove_entry(entry);
+ return entry;
+ }
+
+ return NULL;
+}
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+/**
+ * netlbl_af6list_remove_entry - Remove an IPv6 address entry
+ * @entry: address entry
+ *
+ * Description:
+ * Remove the specified IP address entry. The caller is responsible for
+ * calling the necessary locking functions.
+ *
+ */
+void netlbl_af6list_remove_entry(struct netlbl_af6list *entry)
+{
+ entry->valid = 0;
+ list_del_rcu(&entry->list);
+}
+
+/**
+ * netlbl_af6list_remove - Remove an IPv6 address entry
+ * @addr: IP address
+ * @mask: IP address mask
+ * @head: the list head
+ *
+ * Description:
+ * Remove an IP address entry from the list pointed to by @head. Returns the
+ * entry on success, NULL on failure. The caller is responsible for calling
+ * the necessary locking functions.
+ *
+ */
+struct netlbl_af6list *netlbl_af6list_remove(const struct in6_addr *addr,
+ const struct in6_addr *mask,
+ struct list_head *head)
+{
+ struct netlbl_af6list *entry;
+
+ entry = netlbl_af6list_search(addr, head);
+ if (entry != NULL &&
+ ipv6_addr_equal(&entry->addr, addr) &&
+ ipv6_addr_equal(&entry->mask, mask)) {
+ netlbl_af6list_remove_entry(entry);
+ return entry;
+ }
+
+ return NULL;
+}
+#endif /* IPv6 */
+
+/*
+ * Audit Helper Functions
+ */
+
+/**
+ * netlbl_af4list_audit_addr - Audit an IPv4 address
+ * @audit_buf: audit buffer
+ * @src: true if source address, false if destination
+ * @dev: network interface
+ * @addr: IP address
+ * @mask: IP address mask
+ *
+ * Description:
+ * Write the IPv4 address and address mask, if necessary, to @audit_buf.
+ *
+ */
+void netlbl_af4list_audit_addr(struct audit_buffer *audit_buf,
+ int src, const char *dev,
+ __be32 addr, __be32 mask)
+{
+ u32 mask_val = ntohl(mask);
+ char *dir = (src ? "src" : "dst");
+
+ if (dev != NULL)
+ audit_log_format(audit_buf, " netif=%s", dev);
+ audit_log_format(audit_buf, " %s=" NIPQUAD_FMT, dir, NIPQUAD(addr));
+ if (mask_val != 0xffffffff) {
+ u32 mask_len = 0;
+ while (mask_val > 0) {
+ mask_val <<= 1;
+ mask_len++;
+ }
+ audit_log_format(audit_buf, " %s_prefixlen=%d", dir, mask_len);
+ }
+}
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+/**
+ * netlbl_af6list_audit_addr - Audit an IPv6 address
+ * @audit_buf: audit buffer
+ * @src: true if source address, false if destination
+ * @dev: network interface
+ * @addr: IP address
+ * @mask: IP address mask
+ *
+ * Description:
+ * Write the IPv6 address and address mask, if necessary, to @audit_buf.
+ *
+ */
+void netlbl_af6list_audit_addr(struct audit_buffer *audit_buf,
+ int src,
+ const char *dev,
+ const struct in6_addr *addr,
+ const struct in6_addr *mask)
+{
+ char *dir = (src ? "src" : "dst");
+
+ if (dev != NULL)
+ audit_log_format(audit_buf, " netif=%s", dev);
+ audit_log_format(audit_buf, " %s=" NIP6_FMT, dir, NIP6(*addr));
+ if (ntohl(mask->s6_addr32[3]) != 0xffffffff) {
+ u32 mask_len = 0;
+ u32 mask_val;
+ int iter = -1;
+ while (ntohl(mask->s6_addr32[++iter]) == 0xffffffff)
+ mask_len += 32;
+ mask_val = ntohl(mask->s6_addr32[iter]);
+ while (mask_val > 0) {
+ mask_val <<= 1;
+ mask_len++;
+ }
+ audit_log_format(audit_buf, " %s_prefixlen=%d", dir, mask_len);
+ }
+}
+#endif /* IPv6 */
diff --git a/net/netlabel/netlabel_addrlist.h b/net/netlabel/netlabel_addrlist.h
new file mode 100644
index 0000000..0242bea
--- /dev/null
+++ b/net/netlabel/netlabel_addrlist.h
@@ -0,0 +1,189 @@
+/*
+ * NetLabel Network Address Lists
+ *
+ * This file contains network address list functions used to manage ordered
+ * lists of network addresses for use by the NetLabel subsystem. The NetLabel
+ * system manages static and dynamic label mappings for network protocols such
+ * as CIPSO and RIPSO.
+ *
+ * Author: Paul Moore <paul.moore@hp.com>
+ *
+ */
+
+/*
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2008
+ *
+ * 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 _NETLABEL_ADDRLIST_H
+#define _NETLABEL_ADDRLIST_H
+
+#include <linux/types.h>
+#include <linux/rcupdate.h>
+#include <linux/list.h>
+#include <linux/in6.h>
+#include <linux/audit.h>
+
+/**
+ * struct netlbl_af4list - NetLabel IPv4 address list
+ * @addr: IPv4 address
+ * @mask: IPv4 address mask
+ * @valid: valid flag
+ * @list: list structure, used internally
+ */
+struct netlbl_af4list {
+ __be32 addr;
+ __be32 mask;
+
+ u32 valid;
+ struct list_head list;
+};
+
+/**
+ * struct netlbl_af6list - NetLabel IPv6 address list
+ * @addr: IPv6 address
+ * @mask: IPv6 address mask
+ * @valid: valid flag
+ * @list: list structure, used internally
+ */
+struct netlbl_af6list {
+ struct in6_addr addr;
+ struct in6_addr mask;
+
+ u32 valid;
+ struct list_head list;
+};
+
+#define __af4list_entry(ptr) container_of(ptr, struct netlbl_af4list, list)
+
+static inline struct netlbl_af4list *__af4list_valid(struct list_head *s,
+ struct list_head *h)
+{
+ struct list_head *i = s;
+ struct netlbl_af4list *n = __af4list_entry(s);
+ while (i != h && !n->valid) {
+ i = i->next;
+ n = __af4list_entry(i);
+ }
+ return n;
+}
+
+static inline struct netlbl_af4list *__af4list_valid_rcu(struct list_head *s,
+ struct list_head *h)
+{
+ struct list_head *i = s;
+ struct netlbl_af4list *n = __af4list_entry(s);
+ while (i != h && !n->valid) {
+ i = rcu_dereference(i->next);
+ n = __af4list_entry(i);
+ }
+ return n;
+}
+
+#define netlbl_af4list_foreach(iter, head) \
+ for (iter = __af4list_valid((head)->next, head); \
+ prefetch(iter->list.next), &iter->list != (head); \
+ iter = __af4list_valid(iter->list.next, head))
+
+#define netlbl_af4list_foreach_rcu(iter, head) \
+ for (iter = __af4list_valid_rcu((head)->next, head); \
+ prefetch(iter->list.next), &iter->list != (head); \
+ iter = __af4list_valid_rcu(iter->list.next, head))
+
+#define netlbl_af4list_foreach_safe(iter, tmp, head) \
+ for (iter = __af4list_valid((head)->next, head), \
+ tmp = __af4list_valid(iter->list.next, head); \
+ &iter->list != (head); \
+ iter = tmp, tmp = __af4list_valid(iter->list.next, head))
+
+int netlbl_af4list_add(struct netlbl_af4list *entry,
+ struct list_head *head);
+struct netlbl_af4list *netlbl_af4list_remove(__be32 addr, __be32 mask,
+ struct list_head *head);
+void netlbl_af4list_remove_entry(struct netlbl_af4list *entry);
+struct netlbl_af4list *netlbl_af4list_search(__be32 addr,
+ struct list_head *head);
+struct netlbl_af4list *netlbl_af4list_search_exact(__be32 addr,
+ __be32 mask,
+ struct list_head *head);
+void netlbl_af4list_audit_addr(struct audit_buffer *audit_buf,
+ int src, const char *dev,
+ __be32 addr, __be32 mask);
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+
+#define __af6list_entry(ptr) container_of(ptr, struct netlbl_af6list, list)
+
+static inline struct netlbl_af6list *__af6list_valid(struct list_head *s,
+ struct list_head *h)
+{
+ struct list_head *i = s;
+ struct netlbl_af6list *n = __af6list_entry(s);
+ while (i != h && !n->valid) {
+ i = i->next;
+ n = __af6list_entry(i);
+ }
+ return n;
+}
+
+static inline struct netlbl_af6list *__af6list_valid_rcu(struct list_head *s,
+ struct list_head *h)
+{
+ struct list_head *i = s;
+ struct netlbl_af6list *n = __af6list_entry(s);
+ while (i != h && !n->valid) {
+ i = rcu_dereference(i->next);
+ n = __af6list_entry(i);
+ }
+ return n;
+}
+
+#define netlbl_af6list_foreach(iter, head) \
+ for (iter = __af6list_valid((head)->next, head); \
+ prefetch(iter->list.next), &iter->list != (head); \
+ iter = __af6list_valid(iter->list.next, head))
+
+#define netlbl_af6list_foreach_rcu(iter, head) \
+ for (iter = __af6list_valid_rcu((head)->next, head); \
+ prefetch(iter->list.next), &iter->list != (head); \
+ iter = __af6list_valid_rcu(iter->list.next, head))
+
+#define netlbl_af6list_foreach_safe(iter, tmp, head) \
+ for (iter = __af6list_valid((head)->next, head), \
+ tmp = __af6list_valid(iter->list.next, head); \
+ &iter->list != (head); \
+ iter = tmp, tmp = __af6list_valid(iter->list.next, head))
+
+int netlbl_af6list_add(struct netlbl_af6list *entry,
+ struct list_head *head);
+struct netlbl_af6list *netlbl_af6list_remove(const struct in6_addr *addr,
+ const struct in6_addr *mask,
+ struct list_head *head);
+void netlbl_af6list_remove_entry(struct netlbl_af6list *entry);
+struct netlbl_af6list *netlbl_af6list_search(const struct in6_addr *addr,
+ struct list_head *head);
+struct netlbl_af6list *netlbl_af6list_search_exact(const struct in6_addr *addr,
+ const struct in6_addr *mask,
+ struct list_head *head);
+void netlbl_af6list_audit_addr(struct audit_buffer *audit_buf,
+ int src,
+ const char *dev,
+ const struct in6_addr *addr,
+ const struct in6_addr *mask);
+#endif /* IPV6 */
+
+#endif
diff --git a/net/netlabel/netlabel_cipso_v4.c b/net/netlabel/netlabel_cipso_v4.c
index 0aec318..fff32b7 100644
--- a/net/netlabel/netlabel_cipso_v4.c
+++ b/net/netlabel/netlabel_cipso_v4.c
@@ -43,6 +43,7 @@
#include "netlabel_user.h"
#include "netlabel_cipso_v4.h"
#include "netlabel_mgmt.h"
+#include "netlabel_domainhash.h"
/* Argument struct for cipso_v4_doi_walk() */
struct netlbl_cipsov4_doiwalk_arg {
@@ -51,6 +52,12 @@
u32 seq;
};
+/* Argument struct for netlbl_domhsh_walk() */
+struct netlbl_domhsh_walk_arg {
+ struct netlbl_audit *audit_info;
+ u32 doi;
+};
+
/* NetLabel Generic NETLINK CIPSOv4 family */
static struct genl_family netlbl_cipsov4_gnl_family = {
.id = GENL_ID_GENERATE,
@@ -81,32 +88,6 @@
*/
/**
- * netlbl_cipsov4_doi_free - Frees a CIPSO V4 DOI definition
- * @entry: the entry's RCU field
- *
- * Description:
- * This function is designed to be used as a callback to the call_rcu()
- * function so that the memory allocated to the DOI definition can be released
- * safely.
- *
- */
-void netlbl_cipsov4_doi_free(struct rcu_head *entry)
-{
- struct cipso_v4_doi *ptr;
-
- ptr = container_of(entry, struct cipso_v4_doi, rcu);
- switch (ptr->type) {
- case CIPSO_V4_MAP_STD:
- kfree(ptr->map.std->lvl.cipso);
- kfree(ptr->map.std->lvl.local);
- kfree(ptr->map.std->cat.cipso);
- kfree(ptr->map.std->cat.local);
- break;
- }
- kfree(ptr);
-}
-
-/**
* netlbl_cipsov4_add_common - Parse the common sections of a ADD message
* @info: the Generic NETLINK info block
* @doi_def: the CIPSO V4 DOI definition
@@ -151,9 +132,9 @@
* @info: the Generic NETLINK info block
*
* Description:
- * Create a new CIPSO_V4_MAP_STD DOI definition based on the given ADD message
- * and add it to the CIPSO V4 engine. Return zero on success and non-zero on
- * error.
+ * Create a new CIPSO_V4_MAP_TRANS DOI definition based on the given ADD
+ * message and add it to the CIPSO V4 engine. Return zero on success and
+ * non-zero on error.
*
*/
static int netlbl_cipsov4_add_std(struct genl_info *info)
@@ -183,7 +164,7 @@
ret_val = -ENOMEM;
goto add_std_failure;
}
- doi_def->type = CIPSO_V4_MAP_STD;
+ doi_def->type = CIPSO_V4_MAP_TRANS;
ret_val = netlbl_cipsov4_add_common(info, doi_def);
if (ret_val != 0)
@@ -342,7 +323,7 @@
add_std_failure:
if (doi_def)
- netlbl_cipsov4_doi_free(&doi_def->rcu);
+ cipso_v4_doi_free(doi_def);
return ret_val;
}
@@ -379,7 +360,44 @@
return 0;
add_pass_failure:
- netlbl_cipsov4_doi_free(&doi_def->rcu);
+ cipso_v4_doi_free(doi_def);
+ return ret_val;
+}
+
+/**
+ * netlbl_cipsov4_add_local - Adds a CIPSO V4 DOI definition
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Create a new CIPSO_V4_MAP_LOCAL DOI definition based on the given ADD
+ * message and add it to the CIPSO V4 engine. Return zero on success and
+ * non-zero on error.
+ *
+ */
+static int netlbl_cipsov4_add_local(struct genl_info *info)
+{
+ int ret_val;
+ struct cipso_v4_doi *doi_def = NULL;
+
+ if (!info->attrs[NLBL_CIPSOV4_A_TAGLST])
+ return -EINVAL;
+
+ doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL);
+ if (doi_def == NULL)
+ return -ENOMEM;
+ doi_def->type = CIPSO_V4_MAP_LOCAL;
+
+ ret_val = netlbl_cipsov4_add_common(info, doi_def);
+ if (ret_val != 0)
+ goto add_local_failure;
+
+ ret_val = cipso_v4_doi_add(doi_def);
+ if (ret_val != 0)
+ goto add_local_failure;
+ return 0;
+
+add_local_failure:
+ cipso_v4_doi_free(doi_def);
return ret_val;
}
@@ -412,14 +430,18 @@
type = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_MTYPE]);
switch (type) {
- case CIPSO_V4_MAP_STD:
- type_str = "std";
+ case CIPSO_V4_MAP_TRANS:
+ type_str = "trans";
ret_val = netlbl_cipsov4_add_std(info);
break;
case CIPSO_V4_MAP_PASS:
type_str = "pass";
ret_val = netlbl_cipsov4_add_pass(info);
break;
+ case CIPSO_V4_MAP_LOCAL:
+ type_str = "local";
+ ret_val = netlbl_cipsov4_add_local(info);
+ break;
}
if (ret_val == 0)
atomic_inc(&netlabel_mgmt_protocount);
@@ -491,7 +513,7 @@
doi_def = cipso_v4_doi_getdef(doi);
if (doi_def == NULL) {
ret_val = -EINVAL;
- goto list_failure;
+ goto list_failure_lock;
}
ret_val = nla_put_u32(ans_skb, NLBL_CIPSOV4_A_MTYPE, doi_def->type);
@@ -516,7 +538,7 @@
nla_nest_end(ans_skb, nla_a);
switch (doi_def->type) {
- case CIPSO_V4_MAP_STD:
+ case CIPSO_V4_MAP_TRANS:
nla_a = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSLVLLST);
if (nla_a == NULL) {
ret_val = -ENOMEM;
@@ -655,7 +677,7 @@
struct netlink_callback *cb)
{
struct netlbl_cipsov4_doiwalk_arg cb_arg;
- int doi_skip = cb->args[0];
+ u32 doi_skip = cb->args[0];
cb_arg.nl_cb = cb;
cb_arg.skb = skb;
@@ -668,6 +690,29 @@
}
/**
+ * netlbl_cipsov4_remove_cb - netlbl_cipsov4_remove() callback for REMOVE
+ * @entry: LSM domain mapping entry
+ * @arg: the netlbl_domhsh_walk_arg structure
+ *
+ * Description:
+ * This function is intended for use by netlbl_cipsov4_remove() as the callback
+ * for the netlbl_domhsh_walk() function; it removes LSM domain map entries
+ * which are associated with the CIPSO DOI specified in @arg. Returns zero on
+ * success, negative values on failure.
+ *
+ */
+static int netlbl_cipsov4_remove_cb(struct netlbl_dom_map *entry, void *arg)
+{
+ struct netlbl_domhsh_walk_arg *cb_arg = arg;
+
+ if (entry->type == NETLBL_NLTYPE_CIPSOV4 &&
+ entry->type_def.cipsov4->doi == cb_arg->doi)
+ return netlbl_domhsh_remove_entry(entry, cb_arg->audit_info);
+
+ return 0;
+}
+
+/**
* netlbl_cipsov4_remove - Handle a REMOVE message
* @skb: the NETLINK buffer
* @info: the Generic NETLINK info block
@@ -681,8 +726,11 @@
{
int ret_val = -EINVAL;
u32 doi = 0;
+ struct netlbl_domhsh_walk_arg cb_arg;
struct audit_buffer *audit_buf;
struct netlbl_audit audit_info;
+ u32 skip_bkt = 0;
+ u32 skip_chain = 0;
if (!info->attrs[NLBL_CIPSOV4_A_DOI])
return -EINVAL;
@@ -690,11 +738,15 @@
doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]);
netlbl_netlink_auditinfo(skb, &audit_info);
- ret_val = cipso_v4_doi_remove(doi,
- &audit_info,
- netlbl_cipsov4_doi_free);
- if (ret_val == 0)
- atomic_dec(&netlabel_mgmt_protocount);
+ cb_arg.doi = doi;
+ cb_arg.audit_info = &audit_info;
+ ret_val = netlbl_domhsh_walk(&skip_bkt, &skip_chain,
+ netlbl_cipsov4_remove_cb, &cb_arg);
+ if (ret_val == 0 || ret_val == -ENOENT) {
+ ret_val = cipso_v4_doi_remove(doi, &audit_info);
+ if (ret_val == 0)
+ atomic_dec(&netlabel_mgmt_protocount);
+ }
audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_DEL,
&audit_info);
diff --git a/net/netlabel/netlabel_cipso_v4.h b/net/netlabel/netlabel_cipso_v4.h
index 220cb9d..c8a4079 100644
--- a/net/netlabel/netlabel_cipso_v4.h
+++ b/net/netlabel/netlabel_cipso_v4.h
@@ -45,12 +45,13 @@
* NLBL_CIPSOV4_A_MTYPE
* NLBL_CIPSOV4_A_TAGLST
*
- * If using CIPSO_V4_MAP_STD the following attributes are required:
+ * If using CIPSO_V4_MAP_TRANS the following attributes are required:
*
* NLBL_CIPSOV4_A_MLSLVLLST
* NLBL_CIPSOV4_A_MLSCATLST
*
- * If using CIPSO_V4_MAP_PASS no additional attributes are required.
+ * If using CIPSO_V4_MAP_PASS or CIPSO_V4_MAP_LOCAL no additional attributes
+ * are required.
*
* o REMOVE:
* Sent by an application to remove a specific DOI mapping table from the
@@ -76,12 +77,13 @@
* NLBL_CIPSOV4_A_MTYPE
* NLBL_CIPSOV4_A_TAGLST
*
- * If using CIPSO_V4_MAP_STD the following attributes are required:
+ * If using CIPSO_V4_MAP_TRANS the following attributes are required:
*
* NLBL_CIPSOV4_A_MLSLVLLST
* NLBL_CIPSOV4_A_MLSCATLST
*
- * If using CIPSO_V4_MAP_PASS no additional attributes are required.
+ * If using CIPSO_V4_MAP_PASS or CIPSO_V4_MAP_LOCAL no additional attributes
+ * are required.
*
* o LISTALL:
* This message is sent by an application to list the valid DOIs on the
diff --git a/net/netlabel/netlabel_domainhash.c b/net/netlabel/netlabel_domainhash.c
index 643c032..5fadf10 100644
--- a/net/netlabel/netlabel_domainhash.c
+++ b/net/netlabel/netlabel_domainhash.c
@@ -11,7 +11,7 @@
*/
/*
- * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
*
* 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
@@ -40,6 +40,7 @@
#include <asm/bug.h>
#include "netlabel_mgmt.h"
+#include "netlabel_addrlist.h"
#include "netlabel_domainhash.h"
#include "netlabel_user.h"
@@ -72,8 +73,28 @@
static void netlbl_domhsh_free_entry(struct rcu_head *entry)
{
struct netlbl_dom_map *ptr;
+ struct netlbl_af4list *iter4;
+ struct netlbl_af4list *tmp4;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ struct netlbl_af6list *iter6;
+ struct netlbl_af6list *tmp6;
+#endif /* IPv6 */
ptr = container_of(entry, struct netlbl_dom_map, rcu);
+ if (ptr->type == NETLBL_NLTYPE_ADDRSELECT) {
+ netlbl_af4list_foreach_safe(iter4, tmp4,
+ &ptr->type_def.addrsel->list4) {
+ netlbl_af4list_remove_entry(iter4);
+ kfree(netlbl_domhsh_addr4_entry(iter4));
+ }
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ netlbl_af6list_foreach_safe(iter6, tmp6,
+ &ptr->type_def.addrsel->list6) {
+ netlbl_af6list_remove_entry(iter6);
+ kfree(netlbl_domhsh_addr6_entry(iter6));
+ }
+#endif /* IPv6 */
+ }
kfree(ptr->domain);
kfree(ptr);
}
@@ -115,13 +136,13 @@
static struct netlbl_dom_map *netlbl_domhsh_search(const char *domain)
{
u32 bkt;
+ struct list_head *bkt_list;
struct netlbl_dom_map *iter;
if (domain != NULL) {
bkt = netlbl_domhsh_hash(domain);
- list_for_each_entry_rcu(iter,
- &rcu_dereference(netlbl_domhsh)->tbl[bkt],
- list)
+ bkt_list = &rcu_dereference(netlbl_domhsh)->tbl[bkt];
+ list_for_each_entry_rcu(iter, bkt_list, list)
if (iter->valid && strcmp(iter->domain, domain) == 0)
return iter;
}
@@ -156,6 +177,69 @@
return entry;
}
+/**
+ * netlbl_domhsh_audit_add - Generate an audit entry for an add event
+ * @entry: the entry being added
+ * @addr4: the IPv4 address information
+ * @addr6: the IPv6 address information
+ * @result: the result code
+ * @audit_info: NetLabel audit information
+ *
+ * Description:
+ * Generate an audit record for adding a new NetLabel/LSM mapping entry with
+ * the given information. Caller is responsibile for holding the necessary
+ * locks.
+ *
+ */
+static void netlbl_domhsh_audit_add(struct netlbl_dom_map *entry,
+ struct netlbl_af4list *addr4,
+ struct netlbl_af6list *addr6,
+ int result,
+ struct netlbl_audit *audit_info)
+{
+ struct audit_buffer *audit_buf;
+ struct cipso_v4_doi *cipsov4 = NULL;
+ u32 type;
+
+ audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_ADD, audit_info);
+ if (audit_buf != NULL) {
+ audit_log_format(audit_buf, " nlbl_domain=%s",
+ entry->domain ? entry->domain : "(default)");
+ if (addr4 != NULL) {
+ struct netlbl_domaddr4_map *map4;
+ map4 = netlbl_domhsh_addr4_entry(addr4);
+ type = map4->type;
+ cipsov4 = map4->type_def.cipsov4;
+ netlbl_af4list_audit_addr(audit_buf, 0, NULL,
+ addr4->addr, addr4->mask);
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ } else if (addr6 != NULL) {
+ struct netlbl_domaddr6_map *map6;
+ map6 = netlbl_domhsh_addr6_entry(addr6);
+ type = map6->type;
+ netlbl_af6list_audit_addr(audit_buf, 0, NULL,
+ &addr6->addr, &addr6->mask);
+#endif /* IPv6 */
+ } else {
+ type = entry->type;
+ cipsov4 = entry->type_def.cipsov4;
+ }
+ switch (type) {
+ case NETLBL_NLTYPE_UNLABELED:
+ audit_log_format(audit_buf, " nlbl_protocol=unlbl");
+ break;
+ case NETLBL_NLTYPE_CIPSOV4:
+ BUG_ON(cipsov4 == NULL);
+ audit_log_format(audit_buf,
+ " nlbl_protocol=cipsov4 cipso_doi=%u",
+ cipsov4->doi);
+ break;
+ }
+ audit_log_format(audit_buf, " res=%u", result == 0 ? 1 : 0);
+ audit_log_end(audit_buf);
+ }
+}
+
/*
* Domain Hash Table Functions
*/
@@ -213,74 +297,106 @@
int netlbl_domhsh_add(struct netlbl_dom_map *entry,
struct netlbl_audit *audit_info)
{
- int ret_val;
- u32 bkt;
- struct audit_buffer *audit_buf;
-
- switch (entry->type) {
- case NETLBL_NLTYPE_UNLABELED:
- ret_val = 0;
- break;
- case NETLBL_NLTYPE_CIPSOV4:
- ret_val = cipso_v4_doi_domhsh_add(entry->type_def.cipsov4,
- entry->domain);
- break;
- default:
- return -EINVAL;
- }
- if (ret_val != 0)
- return ret_val;
-
- entry->valid = 1;
- INIT_RCU_HEAD(&entry->rcu);
+ int ret_val = 0;
+ struct netlbl_dom_map *entry_old;
+ struct netlbl_af4list *iter4;
+ struct netlbl_af4list *tmp4;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ struct netlbl_af6list *iter6;
+ struct netlbl_af6list *tmp6;
+#endif /* IPv6 */
rcu_read_lock();
+
spin_lock(&netlbl_domhsh_lock);
- if (entry->domain != NULL) {
- bkt = netlbl_domhsh_hash(entry->domain);
- if (netlbl_domhsh_search(entry->domain) == NULL)
+ if (entry->domain != NULL)
+ entry_old = netlbl_domhsh_search(entry->domain);
+ else
+ entry_old = netlbl_domhsh_search_def(entry->domain);
+ if (entry_old == NULL) {
+ entry->valid = 1;
+ INIT_RCU_HEAD(&entry->rcu);
+
+ if (entry->domain != NULL) {
+ u32 bkt = netlbl_domhsh_hash(entry->domain);
list_add_tail_rcu(&entry->list,
&rcu_dereference(netlbl_domhsh)->tbl[bkt]);
- else
- ret_val = -EEXIST;
- } else {
- INIT_LIST_HEAD(&entry->list);
- if (rcu_dereference(netlbl_domhsh_def) == NULL)
+ } else {
+ INIT_LIST_HEAD(&entry->list);
rcu_assign_pointer(netlbl_domhsh_def, entry);
- else
- ret_val = -EEXIST;
- }
+ }
+
+ if (entry->type == NETLBL_NLTYPE_ADDRSELECT) {
+ netlbl_af4list_foreach_rcu(iter4,
+ &entry->type_def.addrsel->list4)
+ netlbl_domhsh_audit_add(entry, iter4, NULL,
+ ret_val, audit_info);
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ netlbl_af6list_foreach_rcu(iter6,
+ &entry->type_def.addrsel->list6)
+ netlbl_domhsh_audit_add(entry, NULL, iter6,
+ ret_val, audit_info);
+#endif /* IPv6 */
+ } else
+ netlbl_domhsh_audit_add(entry, NULL, NULL,
+ ret_val, audit_info);
+ } else if (entry_old->type == NETLBL_NLTYPE_ADDRSELECT &&
+ entry->type == NETLBL_NLTYPE_ADDRSELECT) {
+ struct list_head *old_list4;
+ struct list_head *old_list6;
+
+ old_list4 = &entry_old->type_def.addrsel->list4;
+ old_list6 = &entry_old->type_def.addrsel->list6;
+
+ /* we only allow the addition of address selectors if all of
+ * the selectors do not exist in the existing domain map */
+ netlbl_af4list_foreach_rcu(iter4,
+ &entry->type_def.addrsel->list4)
+ if (netlbl_af4list_search_exact(iter4->addr,
+ iter4->mask,
+ old_list4)) {
+ ret_val = -EEXIST;
+ goto add_return;
+ }
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ netlbl_af6list_foreach_rcu(iter6,
+ &entry->type_def.addrsel->list6)
+ if (netlbl_af6list_search_exact(&iter6->addr,
+ &iter6->mask,
+ old_list6)) {
+ ret_val = -EEXIST;
+ goto add_return;
+ }
+#endif /* IPv6 */
+
+ netlbl_af4list_foreach_safe(iter4, tmp4,
+ &entry->type_def.addrsel->list4) {
+ netlbl_af4list_remove_entry(iter4);
+ iter4->valid = 1;
+ ret_val = netlbl_af4list_add(iter4, old_list4);
+ netlbl_domhsh_audit_add(entry_old, iter4, NULL,
+ ret_val, audit_info);
+ if (ret_val != 0)
+ goto add_return;
+ }
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ netlbl_af6list_foreach_safe(iter6, tmp6,
+ &entry->type_def.addrsel->list6) {
+ netlbl_af6list_remove_entry(iter6);
+ iter6->valid = 1;
+ ret_val = netlbl_af6list_add(iter6, old_list6);
+ netlbl_domhsh_audit_add(entry_old, NULL, iter6,
+ ret_val, audit_info);
+ if (ret_val != 0)
+ goto add_return;
+ }
+#endif /* IPv6 */
+ } else
+ ret_val = -EINVAL;
+
+add_return:
spin_unlock(&netlbl_domhsh_lock);
- audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_ADD, audit_info);
- if (audit_buf != NULL) {
- audit_log_format(audit_buf,
- " nlbl_domain=%s",
- entry->domain ? entry->domain : "(default)");
- switch (entry->type) {
- case NETLBL_NLTYPE_UNLABELED:
- audit_log_format(audit_buf, " nlbl_protocol=unlbl");
- break;
- case NETLBL_NLTYPE_CIPSOV4:
- audit_log_format(audit_buf,
- " nlbl_protocol=cipsov4 cipso_doi=%u",
- entry->type_def.cipsov4->doi);
- break;
- }
- audit_log_format(audit_buf, " res=%u", ret_val == 0 ? 1 : 0);
- audit_log_end(audit_buf);
- }
rcu_read_unlock();
-
- if (ret_val != 0) {
- switch (entry->type) {
- case NETLBL_NLTYPE_CIPSOV4:
- if (cipso_v4_doi_domhsh_remove(entry->type_def.cipsov4,
- entry->domain) != 0)
- BUG();
- break;
- }
- }
-
return ret_val;
}
@@ -302,6 +418,71 @@
}
/**
+ * netlbl_domhsh_remove_entry - Removes a given entry from the domain table
+ * @entry: the entry to remove
+ * @audit_info: NetLabel audit information
+ *
+ * Description:
+ * Removes an entry from the domain hash table and handles any updates to the
+ * lower level protocol handler (i.e. CIPSO). Caller is responsible for
+ * ensuring that the RCU read lock is held. Returns zero on success, negative
+ * on failure.
+ *
+ */
+int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry,
+ struct netlbl_audit *audit_info)
+{
+ int ret_val = 0;
+ struct audit_buffer *audit_buf;
+
+ if (entry == NULL)
+ return -ENOENT;
+
+ spin_lock(&netlbl_domhsh_lock);
+ if (entry->valid) {
+ entry->valid = 0;
+ if (entry != rcu_dereference(netlbl_domhsh_def))
+ list_del_rcu(&entry->list);
+ else
+ rcu_assign_pointer(netlbl_domhsh_def, NULL);
+ } else
+ ret_val = -ENOENT;
+ spin_unlock(&netlbl_domhsh_lock);
+
+ audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_DEL, audit_info);
+ if (audit_buf != NULL) {
+ audit_log_format(audit_buf,
+ " nlbl_domain=%s res=%u",
+ entry->domain ? entry->domain : "(default)",
+ ret_val == 0 ? 1 : 0);
+ audit_log_end(audit_buf);
+ }
+
+ if (ret_val == 0) {
+ struct netlbl_af4list *iter4;
+ struct netlbl_domaddr4_map *map4;
+
+ switch (entry->type) {
+ case NETLBL_NLTYPE_ADDRSELECT:
+ netlbl_af4list_foreach_rcu(iter4,
+ &entry->type_def.addrsel->list4) {
+ map4 = netlbl_domhsh_addr4_entry(iter4);
+ cipso_v4_doi_putdef(map4->type_def.cipsov4);
+ }
+ /* no need to check the IPv6 list since we currently
+ * support only unlabeled protocols for IPv6 */
+ break;
+ case NETLBL_NLTYPE_CIPSOV4:
+ cipso_v4_doi_putdef(entry->type_def.cipsov4);
+ break;
+ }
+ call_rcu(&entry->rcu, netlbl_domhsh_free_entry);
+ }
+
+ return ret_val;
+}
+
+/**
* netlbl_domhsh_remove - Removes an entry from the domain hash table
* @domain: the domain to remove
* @audit_info: NetLabel audit information
@@ -314,47 +495,17 @@
*/
int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info)
{
- int ret_val = -ENOENT;
+ int ret_val;
struct netlbl_dom_map *entry;
- struct audit_buffer *audit_buf;
rcu_read_lock();
if (domain)
entry = netlbl_domhsh_search(domain);
else
entry = netlbl_domhsh_search_def(domain);
- if (entry == NULL)
- goto remove_return;
- switch (entry->type) {
- case NETLBL_NLTYPE_CIPSOV4:
- cipso_v4_doi_domhsh_remove(entry->type_def.cipsov4,
- entry->domain);
- break;
- }
- spin_lock(&netlbl_domhsh_lock);
- if (entry->valid) {
- entry->valid = 0;
- if (entry != rcu_dereference(netlbl_domhsh_def))
- list_del_rcu(&entry->list);
- else
- rcu_assign_pointer(netlbl_domhsh_def, NULL);
- ret_val = 0;
- }
- spin_unlock(&netlbl_domhsh_lock);
-
- audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_DEL, audit_info);
- if (audit_buf != NULL) {
- audit_log_format(audit_buf,
- " nlbl_domain=%s res=%u",
- entry->domain ? entry->domain : "(default)",
- ret_val == 0 ? 1 : 0);
- audit_log_end(audit_buf);
- }
-
-remove_return:
+ ret_val = netlbl_domhsh_remove_entry(entry, audit_info);
rcu_read_unlock();
- if (ret_val == 0)
- call_rcu(&entry->rcu, netlbl_domhsh_free_entry);
+
return ret_val;
}
@@ -389,6 +540,70 @@
}
/**
+ * netlbl_domhsh_getentry_af4 - Get an entry from the domain hash table
+ * @domain: the domain name to search for
+ * @addr: the IP address to search for
+ *
+ * Description:
+ * Look through the domain hash table searching for an entry to match @domain
+ * and @addr, return a pointer to a copy of the entry or NULL. The caller is
+ * responsible for ensuring that rcu_read_[un]lock() is called.
+ *
+ */
+struct netlbl_domaddr4_map *netlbl_domhsh_getentry_af4(const char *domain,
+ __be32 addr)
+{
+ struct netlbl_dom_map *dom_iter;
+ struct netlbl_af4list *addr_iter;
+
+ dom_iter = netlbl_domhsh_search_def(domain);
+ if (dom_iter == NULL)
+ return NULL;
+ if (dom_iter->type != NETLBL_NLTYPE_ADDRSELECT)
+ return NULL;
+
+ addr_iter = netlbl_af4list_search(addr,
+ &dom_iter->type_def.addrsel->list4);
+ if (addr_iter == NULL)
+ return NULL;
+
+ return netlbl_domhsh_addr4_entry(addr_iter);
+}
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+/**
+ * netlbl_domhsh_getentry_af6 - Get an entry from the domain hash table
+ * @domain: the domain name to search for
+ * @addr: the IP address to search for
+ *
+ * Description:
+ * Look through the domain hash table searching for an entry to match @domain
+ * and @addr, return a pointer to a copy of the entry or NULL. The caller is
+ * responsible for ensuring that rcu_read_[un]lock() is called.
+ *
+ */
+struct netlbl_domaddr6_map *netlbl_domhsh_getentry_af6(const char *domain,
+ const struct in6_addr *addr)
+{
+ struct netlbl_dom_map *dom_iter;
+ struct netlbl_af6list *addr_iter;
+
+ dom_iter = netlbl_domhsh_search_def(domain);
+ if (dom_iter == NULL)
+ return NULL;
+ if (dom_iter->type != NETLBL_NLTYPE_ADDRSELECT)
+ return NULL;
+
+ addr_iter = netlbl_af6list_search(addr,
+ &dom_iter->type_def.addrsel->list6);
+ if (addr_iter == NULL)
+ return NULL;
+
+ return netlbl_domhsh_addr6_entry(addr_iter);
+}
+#endif /* IPv6 */
+
+/**
* netlbl_domhsh_walk - Iterate through the domain mapping hash table
* @skip_bkt: the number of buckets to skip at the start
* @skip_chain: the number of entries to skip in the first iterated bucket
@@ -410,6 +625,7 @@
{
int ret_val = -ENOENT;
u32 iter_bkt;
+ struct list_head *iter_list;
struct netlbl_dom_map *iter_entry;
u32 chain_cnt = 0;
@@ -417,9 +633,8 @@
for (iter_bkt = *skip_bkt;
iter_bkt < rcu_dereference(netlbl_domhsh)->size;
iter_bkt++, chain_cnt = 0) {
- list_for_each_entry_rcu(iter_entry,
- &rcu_dereference(netlbl_domhsh)->tbl[iter_bkt],
- list)
+ iter_list = &rcu_dereference(netlbl_domhsh)->tbl[iter_bkt];
+ list_for_each_entry_rcu(iter_entry, iter_list, list)
if (iter_entry->valid) {
if (chain_cnt++ < *skip_chain)
continue;
diff --git a/net/netlabel/netlabel_domainhash.h b/net/netlabel/netlabel_domainhash.h
index 8220990..bfcb676 100644
--- a/net/netlabel/netlabel_domainhash.h
+++ b/net/netlabel/netlabel_domainhash.h
@@ -11,7 +11,7 @@
*/
/*
- * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
*
* 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
@@ -36,16 +36,43 @@
#include <linux/rcupdate.h>
#include <linux/list.h>
+#include "netlabel_addrlist.h"
+
/* Domain hash table size */
/* XXX - currently this number is an uneducated guess */
#define NETLBL_DOMHSH_BITSIZE 7
-/* Domain mapping definition struct */
+/* Domain mapping definition structures */
+#define netlbl_domhsh_addr4_entry(iter) \
+ container_of(iter, struct netlbl_domaddr4_map, list)
+struct netlbl_domaddr4_map {
+ u32 type;
+ union {
+ struct cipso_v4_doi *cipsov4;
+ } type_def;
+
+ struct netlbl_af4list list;
+};
+#define netlbl_domhsh_addr6_entry(iter) \
+ container_of(iter, struct netlbl_domaddr6_map, list)
+struct netlbl_domaddr6_map {
+ u32 type;
+
+ /* NOTE: no 'type_def' union needed at present since we don't currently
+ * support any IPv6 labeling protocols */
+
+ struct netlbl_af6list list;
+};
+struct netlbl_domaddr_map {
+ struct list_head list4;
+ struct list_head list6;
+};
struct netlbl_dom_map {
char *domain;
u32 type;
union {
struct cipso_v4_doi *cipsov4;
+ struct netlbl_domaddr_map *addrsel;
} type_def;
u32 valid;
@@ -61,12 +88,21 @@
struct netlbl_audit *audit_info);
int netlbl_domhsh_add_default(struct netlbl_dom_map *entry,
struct netlbl_audit *audit_info);
+int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry,
+ struct netlbl_audit *audit_info);
int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info);
int netlbl_domhsh_remove_default(struct netlbl_audit *audit_info);
struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain);
+struct netlbl_domaddr4_map *netlbl_domhsh_getentry_af4(const char *domain,
+ __be32 addr);
int netlbl_domhsh_walk(u32 *skip_bkt,
u32 *skip_chain,
int (*callback) (struct netlbl_dom_map *entry, void *arg),
void *cb_arg);
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+struct netlbl_domaddr6_map *netlbl_domhsh_getentry_af6(const char *domain,
+ const struct in6_addr *addr);
+#endif /* IPv6 */
+
#endif
diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c
index 39793a1..b32eceb 100644
--- a/net/netlabel/netlabel_kapi.c
+++ b/net/netlabel/netlabel_kapi.c
@@ -10,7 +10,7 @@
*/
/*
- * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
*
* 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
@@ -82,7 +82,7 @@
entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
if (entry == NULL)
- goto cfg_unlbl_add_map_failure;
+ return -ENOMEM;
if (domain != NULL) {
entry->domain = kstrdup(domain, GFP_ATOMIC);
if (entry->domain == NULL)
@@ -104,49 +104,6 @@
}
/**
- * netlbl_cfg_cipsov4_add - Add a new CIPSOv4 DOI definition
- * @doi_def: the DOI definition
- * @audit_info: NetLabel audit information
- *
- * Description:
- * Add a new CIPSOv4 DOI definition to the NetLabel subsystem. Returns zero on
- * success, negative values on failure.
- *
- */
-int netlbl_cfg_cipsov4_add(struct cipso_v4_doi *doi_def,
- struct netlbl_audit *audit_info)
-{
- int ret_val;
- const char *type_str;
- struct audit_buffer *audit_buf;
-
- ret_val = cipso_v4_doi_add(doi_def);
-
- audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_ADD,
- audit_info);
- if (audit_buf != NULL) {
- switch (doi_def->type) {
- case CIPSO_V4_MAP_STD:
- type_str = "std";
- break;
- case CIPSO_V4_MAP_PASS:
- type_str = "pass";
- break;
- default:
- type_str = "(unknown)";
- }
- audit_log_format(audit_buf,
- " cipso_doi=%u cipso_type=%s res=%u",
- doi_def->doi,
- type_str,
- ret_val == 0 ? 1 : 0);
- audit_log_end(audit_buf);
- }
-
- return ret_val;
-}
-
-/**
* netlbl_cfg_cipsov4_add_map - Add a new CIPSOv4 DOI definition and mapping
* @doi_def: the DOI definition
* @domain: the domain mapping to add
@@ -164,58 +121,71 @@
struct netlbl_audit *audit_info)
{
int ret_val = -ENOMEM;
+ u32 doi;
+ u32 doi_type;
struct netlbl_dom_map *entry;
+ const char *type_str;
+ struct audit_buffer *audit_buf;
+
+ doi = doi_def->doi;
+ doi_type = doi_def->type;
entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
if (entry == NULL)
- goto cfg_cipsov4_add_map_failure;
+ return -ENOMEM;
if (domain != NULL) {
entry->domain = kstrdup(domain, GFP_ATOMIC);
if (entry->domain == NULL)
goto cfg_cipsov4_add_map_failure;
}
- entry->type = NETLBL_NLTYPE_CIPSOV4;
- entry->type_def.cipsov4 = doi_def;
- /* Grab a RCU read lock here so nothing happens to the doi_def variable
- * between adding it to the CIPSOv4 protocol engine and adding a
- * domain mapping for it. */
-
- rcu_read_lock();
- ret_val = netlbl_cfg_cipsov4_add(doi_def, audit_info);
- if (ret_val != 0)
- goto cfg_cipsov4_add_map_failure_unlock;
- ret_val = netlbl_domhsh_add(entry, audit_info);
+ ret_val = cipso_v4_doi_add(doi_def);
if (ret_val != 0)
goto cfg_cipsov4_add_map_failure_remove_doi;
- rcu_read_unlock();
+ entry->type = NETLBL_NLTYPE_CIPSOV4;
+ entry->type_def.cipsov4 = cipso_v4_doi_getdef(doi);
+ if (entry->type_def.cipsov4 == NULL) {
+ ret_val = -ENOENT;
+ goto cfg_cipsov4_add_map_failure_remove_doi;
+ }
+ ret_val = netlbl_domhsh_add(entry, audit_info);
+ if (ret_val != 0)
+ goto cfg_cipsov4_add_map_failure_release_doi;
- return 0;
+cfg_cipsov4_add_map_return:
+ audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_ADD,
+ audit_info);
+ if (audit_buf != NULL) {
+ switch (doi_type) {
+ case CIPSO_V4_MAP_TRANS:
+ type_str = "trans";
+ break;
+ case CIPSO_V4_MAP_PASS:
+ type_str = "pass";
+ break;
+ case CIPSO_V4_MAP_LOCAL:
+ type_str = "local";
+ break;
+ default:
+ type_str = "(unknown)";
+ }
+ audit_log_format(audit_buf,
+ " cipso_doi=%u cipso_type=%s res=%u",
+ doi, type_str, ret_val == 0 ? 1 : 0);
+ audit_log_end(audit_buf);
+ }
+ return ret_val;
+
+cfg_cipsov4_add_map_failure_release_doi:
+ cipso_v4_doi_putdef(doi_def);
cfg_cipsov4_add_map_failure_remove_doi:
- cipso_v4_doi_remove(doi_def->doi, audit_info, netlbl_cipsov4_doi_free);
-cfg_cipsov4_add_map_failure_unlock:
- rcu_read_unlock();
+ cipso_v4_doi_remove(doi, audit_info);
cfg_cipsov4_add_map_failure:
if (entry != NULL)
kfree(entry->domain);
kfree(entry);
- return ret_val;
-}
-
-/**
- * netlbl_cfg_cipsov4_del - Removean existing CIPSOv4 DOI definition
- * @doi: the CIPSO DOI value
- * @audit_info: NetLabel audit information
- *
- * Description:
- * Removes an existing CIPSOv4 DOI definition from the NetLabel subsystem.
- * Returns zero on success, negative values on failure.
- *
- */
-int netlbl_cfg_cipsov4_del(u32 doi, struct netlbl_audit *audit_info)
-{
- return cipso_v4_doi_remove(doi, audit_info, netlbl_cipsov4_doi_free);
+ goto cfg_cipsov4_add_map_return;
}
/*
@@ -452,7 +422,9 @@
* Attach the correct label to the given socket using the security attributes
* specified in @secattr. This function requires exclusive access to @sk,
* which means it either needs to be in the process of being created or locked.
- * Returns zero on success, negative values on failure.
+ * Returns zero on success, -EDESTADDRREQ if the domain is configured to use
+ * network address selectors (can't blindly label the socket), and negative
+ * values on all other failures.
*
*/
int netlbl_sock_setattr(struct sock *sk,
@@ -466,6 +438,9 @@
if (dom_entry == NULL)
goto socket_setattr_return;
switch (dom_entry->type) {
+ case NETLBL_NLTYPE_ADDRSELECT:
+ ret_val = -EDESTADDRREQ;
+ break;
case NETLBL_NLTYPE_CIPSOV4:
ret_val = cipso_v4_sock_setattr(sk,
dom_entry->type_def.cipsov4,
@@ -484,6 +459,20 @@
}
/**
+ * netlbl_sock_delattr - Delete all the NetLabel labels on a socket
+ * @sk: the socket
+ *
+ * Description:
+ * Remove all the NetLabel labeling from @sk. The caller is responsible for
+ * ensuring that @sk is locked.
+ *
+ */
+void netlbl_sock_delattr(struct sock *sk)
+{
+ cipso_v4_sock_delattr(sk);
+}
+
+/**
* netlbl_sock_getattr - Determine the security attributes of a sock
* @sk: the sock
* @secattr: the security attributes
@@ -501,6 +490,128 @@
}
/**
+ * netlbl_conn_setattr - Label a connected socket using the correct protocol
+ * @sk: the socket to label
+ * @addr: the destination address
+ * @secattr: the security attributes
+ *
+ * Description:
+ * Attach the correct label to the given connected socket using the security
+ * attributes specified in @secattr. The caller is responsible for ensuring
+ * that @sk is locked. Returns zero on success, negative values on failure.
+ *
+ */
+int netlbl_conn_setattr(struct sock *sk,
+ struct sockaddr *addr,
+ const struct netlbl_lsm_secattr *secattr)
+{
+ int ret_val;
+ struct sockaddr_in *addr4;
+ struct netlbl_domaddr4_map *af4_entry;
+
+ rcu_read_lock();
+ switch (addr->sa_family) {
+ case AF_INET:
+ addr4 = (struct sockaddr_in *)addr;
+ af4_entry = netlbl_domhsh_getentry_af4(secattr->domain,
+ addr4->sin_addr.s_addr);
+ if (af4_entry == NULL) {
+ ret_val = -ENOENT;
+ goto conn_setattr_return;
+ }
+ switch (af4_entry->type) {
+ case NETLBL_NLTYPE_CIPSOV4:
+ ret_val = cipso_v4_sock_setattr(sk,
+ af4_entry->type_def.cipsov4,
+ secattr);
+ break;
+ case NETLBL_NLTYPE_UNLABELED:
+ /* just delete the protocols we support for right now
+ * but we could remove other protocols if needed */
+ cipso_v4_sock_delattr(sk);
+ ret_val = 0;
+ break;
+ default:
+ ret_val = -ENOENT;
+ }
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case AF_INET6:
+ /* since we don't support any IPv6 labeling protocols right
+ * now we can optimize everything away until we do */
+ ret_val = 0;
+ break;
+#endif /* IPv6 */
+ default:
+ ret_val = 0;
+ }
+
+conn_setattr_return:
+ rcu_read_unlock();
+ return ret_val;
+}
+
+/**
+ * netlbl_skbuff_setattr - Label a packet using the correct protocol
+ * @skb: the packet
+ * @family: protocol family
+ * @secattr: the security attributes
+ *
+ * Description:
+ * Attach the correct label to the given packet using the security attributes
+ * specified in @secattr. Returns zero on success, negative values on failure.
+ *
+ */
+int netlbl_skbuff_setattr(struct sk_buff *skb,
+ u16 family,
+ const struct netlbl_lsm_secattr *secattr)
+{
+ int ret_val;
+ struct iphdr *hdr4;
+ struct netlbl_domaddr4_map *af4_entry;
+
+ rcu_read_lock();
+ switch (family) {
+ case AF_INET:
+ hdr4 = ip_hdr(skb);
+ af4_entry = netlbl_domhsh_getentry_af4(secattr->domain,
+ hdr4->daddr);
+ if (af4_entry == NULL) {
+ ret_val = -ENOENT;
+ goto skbuff_setattr_return;
+ }
+ switch (af4_entry->type) {
+ case NETLBL_NLTYPE_CIPSOV4:
+ ret_val = cipso_v4_skbuff_setattr(skb,
+ af4_entry->type_def.cipsov4,
+ secattr);
+ break;
+ case NETLBL_NLTYPE_UNLABELED:
+ /* just delete the protocols we support for right now
+ * but we could remove other protocols if needed */
+ ret_val = cipso_v4_skbuff_delattr(skb);
+ break;
+ default:
+ ret_val = -ENOENT;
+ }
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case AF_INET6:
+ /* since we don't support any IPv6 labeling protocols right
+ * now we can optimize everything away until we do */
+ ret_val = 0;
+ break;
+#endif /* IPv6 */
+ default:
+ ret_val = 0;
+ }
+
+skbuff_setattr_return:
+ rcu_read_unlock();
+ return ret_val;
+}
+
+/**
* netlbl_skbuff_getattr - Determine the security attributes of a packet
* @skb: the packet
* @family: protocol family
@@ -528,6 +639,7 @@
* netlbl_skbuff_err - Handle a LSM error on a sk_buff
* @skb: the packet
* @error: the error code
+ * @gateway: true if host is acting as a gateway, false otherwise
*
* Description:
* Deal with a LSM problem when handling the packet in @skb, typically this is
@@ -535,10 +647,10 @@
* according to the packet's labeling protocol.
*
*/
-void netlbl_skbuff_err(struct sk_buff *skb, int error)
+void netlbl_skbuff_err(struct sk_buff *skb, int error, int gateway)
{
if (CIPSO_V4_OPTEXIST(skb))
- cipso_v4_error(skb, error, 0);
+ cipso_v4_error(skb, error, gateway);
}
/**
diff --git a/net/netlabel/netlabel_mgmt.c b/net/netlabel/netlabel_mgmt.c
index 44be5d5..ee769ec 100644
--- a/net/netlabel/netlabel_mgmt.c
+++ b/net/netlabel/netlabel_mgmt.c
@@ -10,7 +10,7 @@
*/
/*
- * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008
*
* 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
@@ -32,9 +32,13 @@
#include <linux/socket.h>
#include <linux/string.h>
#include <linux/skbuff.h>
+#include <linux/in.h>
+#include <linux/in6.h>
#include <net/sock.h>
#include <net/netlink.h>
#include <net/genetlink.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
#include <net/netlabel.h>
#include <net/cipso_ipv4.h>
#include <asm/atomic.h>
@@ -71,6 +75,301 @@
};
/*
+ * Helper Functions
+ */
+
+/**
+ * netlbl_mgmt_add - Handle an ADD message
+ * @info: the Generic NETLINK info block
+ * @audit_info: NetLabel audit information
+ *
+ * Description:
+ * Helper function for the ADD and ADDDEF messages to add the domain mappings
+ * from the message to the hash table. See netlabel.h for a description of the
+ * message format. Returns zero on success, negative values on failure.
+ *
+ */
+static int netlbl_mgmt_add_common(struct genl_info *info,
+ struct netlbl_audit *audit_info)
+{
+ int ret_val = -EINVAL;
+ struct netlbl_dom_map *entry = NULL;
+ struct netlbl_domaddr_map *addrmap = NULL;
+ struct cipso_v4_doi *cipsov4 = NULL;
+ u32 tmp_val;
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (entry == NULL) {
+ ret_val = -ENOMEM;
+ goto add_failure;
+ }
+ entry->type = nla_get_u32(info->attrs[NLBL_MGMT_A_PROTOCOL]);
+ if (info->attrs[NLBL_MGMT_A_DOMAIN]) {
+ size_t tmp_size = nla_len(info->attrs[NLBL_MGMT_A_DOMAIN]);
+ entry->domain = kmalloc(tmp_size, GFP_KERNEL);
+ if (entry->domain == NULL) {
+ ret_val = -ENOMEM;
+ goto add_failure;
+ }
+ nla_strlcpy(entry->domain,
+ info->attrs[NLBL_MGMT_A_DOMAIN], tmp_size);
+ }
+
+ /* NOTE: internally we allow/use a entry->type value of
+ * NETLBL_NLTYPE_ADDRSELECT but we don't currently allow users
+ * to pass that as a protocol value because we need to know the
+ * "real" protocol */
+
+ switch (entry->type) {
+ case NETLBL_NLTYPE_UNLABELED:
+ break;
+ case NETLBL_NLTYPE_CIPSOV4:
+ if (!info->attrs[NLBL_MGMT_A_CV4DOI])
+ goto add_failure;
+
+ tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CV4DOI]);
+ cipsov4 = cipso_v4_doi_getdef(tmp_val);
+ if (cipsov4 == NULL)
+ goto add_failure;
+ entry->type_def.cipsov4 = cipsov4;
+ break;
+ default:
+ goto add_failure;
+ }
+
+ if (info->attrs[NLBL_MGMT_A_IPV4ADDR]) {
+ struct in_addr *addr;
+ struct in_addr *mask;
+ struct netlbl_domaddr4_map *map;
+
+ addrmap = kzalloc(sizeof(*addrmap), GFP_KERNEL);
+ if (addrmap == NULL) {
+ ret_val = -ENOMEM;
+ goto add_failure;
+ }
+ INIT_LIST_HEAD(&addrmap->list4);
+ INIT_LIST_HEAD(&addrmap->list6);
+
+ if (nla_len(info->attrs[NLBL_MGMT_A_IPV4ADDR]) !=
+ sizeof(struct in_addr)) {
+ ret_val = -EINVAL;
+ goto add_failure;
+ }
+ if (nla_len(info->attrs[NLBL_MGMT_A_IPV4MASK]) !=
+ sizeof(struct in_addr)) {
+ ret_val = -EINVAL;
+ goto add_failure;
+ }
+ addr = nla_data(info->attrs[NLBL_MGMT_A_IPV4ADDR]);
+ mask = nla_data(info->attrs[NLBL_MGMT_A_IPV4MASK]);
+
+ map = kzalloc(sizeof(*map), GFP_KERNEL);
+ if (map == NULL) {
+ ret_val = -ENOMEM;
+ goto add_failure;
+ }
+ map->list.addr = addr->s_addr & mask->s_addr;
+ map->list.mask = mask->s_addr;
+ map->list.valid = 1;
+ map->type = entry->type;
+ if (cipsov4)
+ map->type_def.cipsov4 = cipsov4;
+
+ ret_val = netlbl_af4list_add(&map->list, &addrmap->list4);
+ if (ret_val != 0) {
+ kfree(map);
+ goto add_failure;
+ }
+
+ entry->type = NETLBL_NLTYPE_ADDRSELECT;
+ entry->type_def.addrsel = addrmap;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ } else if (info->attrs[NLBL_MGMT_A_IPV6ADDR]) {
+ struct in6_addr *addr;
+ struct in6_addr *mask;
+ struct netlbl_domaddr6_map *map;
+
+ addrmap = kzalloc(sizeof(*addrmap), GFP_KERNEL);
+ if (addrmap == NULL) {
+ ret_val = -ENOMEM;
+ goto add_failure;
+ }
+ INIT_LIST_HEAD(&addrmap->list4);
+ INIT_LIST_HEAD(&addrmap->list6);
+
+ if (nla_len(info->attrs[NLBL_MGMT_A_IPV6ADDR]) !=
+ sizeof(struct in6_addr)) {
+ ret_val = -EINVAL;
+ goto add_failure;
+ }
+ if (nla_len(info->attrs[NLBL_MGMT_A_IPV6MASK]) !=
+ sizeof(struct in6_addr)) {
+ ret_val = -EINVAL;
+ goto add_failure;
+ }
+ addr = nla_data(info->attrs[NLBL_MGMT_A_IPV6ADDR]);
+ mask = nla_data(info->attrs[NLBL_MGMT_A_IPV6MASK]);
+
+ map = kzalloc(sizeof(*map), GFP_KERNEL);
+ if (map == NULL) {
+ ret_val = -ENOMEM;
+ goto add_failure;
+ }
+ ipv6_addr_copy(&map->list.addr, addr);
+ map->list.addr.s6_addr32[0] &= mask->s6_addr32[0];
+ map->list.addr.s6_addr32[1] &= mask->s6_addr32[1];
+ map->list.addr.s6_addr32[2] &= mask->s6_addr32[2];
+ map->list.addr.s6_addr32[3] &= mask->s6_addr32[3];
+ ipv6_addr_copy(&map->list.mask, mask);
+ map->list.valid = 1;
+ map->type = entry->type;
+
+ ret_val = netlbl_af6list_add(&map->list, &addrmap->list6);
+ if (ret_val != 0) {
+ kfree(map);
+ goto add_failure;
+ }
+
+ entry->type = NETLBL_NLTYPE_ADDRSELECT;
+ entry->type_def.addrsel = addrmap;
+#endif /* IPv6 */
+ }
+
+ ret_val = netlbl_domhsh_add(entry, audit_info);
+ if (ret_val != 0)
+ goto add_failure;
+
+ return 0;
+
+add_failure:
+ if (cipsov4)
+ cipso_v4_doi_putdef(cipsov4);
+ if (entry)
+ kfree(entry->domain);
+ kfree(addrmap);
+ kfree(entry);
+ return ret_val;
+}
+
+/**
+ * netlbl_mgmt_listentry - List a NetLabel/LSM domain map entry
+ * @skb: the NETLINK buffer
+ * @entry: the map entry
+ *
+ * Description:
+ * This function is a helper function used by the LISTALL and LISTDEF command
+ * handlers. The caller is responsibile for ensuring that the RCU read lock
+ * is held. Returns zero on success, negative values on failure.
+ *
+ */
+static int netlbl_mgmt_listentry(struct sk_buff *skb,
+ struct netlbl_dom_map *entry)
+{
+ int ret_val;
+ struct nlattr *nla_a;
+ struct nlattr *nla_b;
+ struct netlbl_af4list *iter4;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ struct netlbl_af6list *iter6;
+#endif
+
+ if (entry->domain != NULL) {
+ ret_val = nla_put_string(skb,
+ NLBL_MGMT_A_DOMAIN, entry->domain);
+ if (ret_val != 0)
+ return ret_val;
+ }
+
+ switch (entry->type) {
+ case NETLBL_NLTYPE_ADDRSELECT:
+ nla_a = nla_nest_start(skb, NLBL_MGMT_A_SELECTORLIST);
+ if (nla_a == NULL)
+ return -ENOMEM;
+
+ netlbl_af4list_foreach_rcu(iter4,
+ &entry->type_def.addrsel->list4) {
+ struct netlbl_domaddr4_map *map4;
+ struct in_addr addr_struct;
+
+ nla_b = nla_nest_start(skb, NLBL_MGMT_A_ADDRSELECTOR);
+ if (nla_b == NULL)
+ return -ENOMEM;
+
+ addr_struct.s_addr = iter4->addr;
+ ret_val = nla_put(skb, NLBL_MGMT_A_IPV4ADDR,
+ sizeof(struct in_addr),
+ &addr_struct);
+ if (ret_val != 0)
+ return ret_val;
+ addr_struct.s_addr = iter4->mask;
+ ret_val = nla_put(skb, NLBL_MGMT_A_IPV4MASK,
+ sizeof(struct in_addr),
+ &addr_struct);
+ if (ret_val != 0)
+ return ret_val;
+ map4 = netlbl_domhsh_addr4_entry(iter4);
+ ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL,
+ map4->type);
+ if (ret_val != 0)
+ return ret_val;
+ switch (map4->type) {
+ case NETLBL_NLTYPE_CIPSOV4:
+ ret_val = nla_put_u32(skb, NLBL_MGMT_A_CV4DOI,
+ map4->type_def.cipsov4->doi);
+ if (ret_val != 0)
+ return ret_val;
+ break;
+ }
+
+ nla_nest_end(skb, nla_b);
+ }
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ netlbl_af6list_foreach_rcu(iter6,
+ &entry->type_def.addrsel->list6) {
+ struct netlbl_domaddr6_map *map6;
+
+ nla_b = nla_nest_start(skb, NLBL_MGMT_A_ADDRSELECTOR);
+ if (nla_b == NULL)
+ return -ENOMEM;
+
+ ret_val = nla_put(skb, NLBL_MGMT_A_IPV6ADDR,
+ sizeof(struct in6_addr),
+ &iter6->addr);
+ if (ret_val != 0)
+ return ret_val;
+ ret_val = nla_put(skb, NLBL_MGMT_A_IPV6MASK,
+ sizeof(struct in6_addr),
+ &iter6->mask);
+ if (ret_val != 0)
+ return ret_val;
+ map6 = netlbl_domhsh_addr6_entry(iter6);
+ ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL,
+ map6->type);
+ if (ret_val != 0)
+ return ret_val;
+
+ nla_nest_end(skb, nla_b);
+ }
+#endif /* IPv6 */
+
+ nla_nest_end(skb, nla_a);
+ break;
+ case NETLBL_NLTYPE_UNLABELED:
+ ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, entry->type);
+ break;
+ case NETLBL_NLTYPE_CIPSOV4:
+ ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, entry->type);
+ if (ret_val != 0)
+ return ret_val;
+ ret_val = nla_put_u32(skb, NLBL_MGMT_A_CV4DOI,
+ entry->type_def.cipsov4->doi);
+ break;
+ }
+
+ return ret_val;
+}
+
+/*
* NetLabel Command Handlers
*/
@@ -87,67 +386,23 @@
*/
static int netlbl_mgmt_add(struct sk_buff *skb, struct genl_info *info)
{
- int ret_val = -EINVAL;
- struct netlbl_dom_map *entry = NULL;
- size_t tmp_size;
- u32 tmp_val;
struct netlbl_audit audit_info;
- if (!info->attrs[NLBL_MGMT_A_DOMAIN] ||
- !info->attrs[NLBL_MGMT_A_PROTOCOL])
- goto add_failure;
+ if ((!info->attrs[NLBL_MGMT_A_DOMAIN]) ||
+ (!info->attrs[NLBL_MGMT_A_PROTOCOL]) ||
+ (info->attrs[NLBL_MGMT_A_IPV4ADDR] &&
+ info->attrs[NLBL_MGMT_A_IPV6ADDR]) ||
+ (info->attrs[NLBL_MGMT_A_IPV4MASK] &&
+ info->attrs[NLBL_MGMT_A_IPV6MASK]) ||
+ ((info->attrs[NLBL_MGMT_A_IPV4ADDR] != NULL) ^
+ (info->attrs[NLBL_MGMT_A_IPV4MASK] != NULL)) ||
+ ((info->attrs[NLBL_MGMT_A_IPV6ADDR] != NULL) ^
+ (info->attrs[NLBL_MGMT_A_IPV6MASK] != NULL)))
+ return -EINVAL;
netlbl_netlink_auditinfo(skb, &audit_info);
- entry = kzalloc(sizeof(*entry), GFP_KERNEL);
- if (entry == NULL) {
- ret_val = -ENOMEM;
- goto add_failure;
- }
- tmp_size = nla_len(info->attrs[NLBL_MGMT_A_DOMAIN]);
- entry->domain = kmalloc(tmp_size, GFP_KERNEL);
- if (entry->domain == NULL) {
- ret_val = -ENOMEM;
- goto add_failure;
- }
- entry->type = nla_get_u32(info->attrs[NLBL_MGMT_A_PROTOCOL]);
- nla_strlcpy(entry->domain, info->attrs[NLBL_MGMT_A_DOMAIN], tmp_size);
-
- switch (entry->type) {
- case NETLBL_NLTYPE_UNLABELED:
- ret_val = netlbl_domhsh_add(entry, &audit_info);
- break;
- case NETLBL_NLTYPE_CIPSOV4:
- if (!info->attrs[NLBL_MGMT_A_CV4DOI])
- goto add_failure;
-
- tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CV4DOI]);
- /* We should be holding a rcu_read_lock() here while we hold
- * the result but since the entry will always be deleted when
- * the CIPSO DOI is deleted we aren't going to keep the
- * lock. */
- rcu_read_lock();
- entry->type_def.cipsov4 = cipso_v4_doi_getdef(tmp_val);
- if (entry->type_def.cipsov4 == NULL) {
- rcu_read_unlock();
- goto add_failure;
- }
- ret_val = netlbl_domhsh_add(entry, &audit_info);
- rcu_read_unlock();
- break;
- default:
- goto add_failure;
- }
- if (ret_val != 0)
- goto add_failure;
-
- return 0;
-
-add_failure:
- if (entry)
- kfree(entry->domain);
- kfree(entry);
- return ret_val;
+ return netlbl_mgmt_add_common(info, &audit_info);
}
/**
@@ -198,23 +453,9 @@
if (data == NULL)
goto listall_cb_failure;
- ret_val = nla_put_string(cb_arg->skb,
- NLBL_MGMT_A_DOMAIN,
- entry->domain);
+ ret_val = netlbl_mgmt_listentry(cb_arg->skb, entry);
if (ret_val != 0)
goto listall_cb_failure;
- ret_val = nla_put_u32(cb_arg->skb, NLBL_MGMT_A_PROTOCOL, entry->type);
- if (ret_val != 0)
- goto listall_cb_failure;
- switch (entry->type) {
- case NETLBL_NLTYPE_CIPSOV4:
- ret_val = nla_put_u32(cb_arg->skb,
- NLBL_MGMT_A_CV4DOI,
- entry->type_def.cipsov4->doi);
- if (ret_val != 0)
- goto listall_cb_failure;
- break;
- }
cb_arg->seq++;
return genlmsg_end(cb_arg->skb, data);
@@ -268,56 +509,22 @@
*/
static int netlbl_mgmt_adddef(struct sk_buff *skb, struct genl_info *info)
{
- int ret_val = -EINVAL;
- struct netlbl_dom_map *entry = NULL;
- u32 tmp_val;
struct netlbl_audit audit_info;
- if (!info->attrs[NLBL_MGMT_A_PROTOCOL])
- goto adddef_failure;
+ if ((!info->attrs[NLBL_MGMT_A_PROTOCOL]) ||
+ (info->attrs[NLBL_MGMT_A_IPV4ADDR] &&
+ info->attrs[NLBL_MGMT_A_IPV6ADDR]) ||
+ (info->attrs[NLBL_MGMT_A_IPV4MASK] &&
+ info->attrs[NLBL_MGMT_A_IPV6MASK]) ||
+ ((info->attrs[NLBL_MGMT_A_IPV4ADDR] != NULL) ^
+ (info->attrs[NLBL_MGMT_A_IPV4MASK] != NULL)) ||
+ ((info->attrs[NLBL_MGMT_A_IPV6ADDR] != NULL) ^
+ (info->attrs[NLBL_MGMT_A_IPV6MASK] != NULL)))
+ return -EINVAL;
netlbl_netlink_auditinfo(skb, &audit_info);
- entry = kzalloc(sizeof(*entry), GFP_KERNEL);
- if (entry == NULL) {
- ret_val = -ENOMEM;
- goto adddef_failure;
- }
- entry->type = nla_get_u32(info->attrs[NLBL_MGMT_A_PROTOCOL]);
-
- switch (entry->type) {
- case NETLBL_NLTYPE_UNLABELED:
- ret_val = netlbl_domhsh_add_default(entry, &audit_info);
- break;
- case NETLBL_NLTYPE_CIPSOV4:
- if (!info->attrs[NLBL_MGMT_A_CV4DOI])
- goto adddef_failure;
-
- tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CV4DOI]);
- /* We should be holding a rcu_read_lock() here while we hold
- * the result but since the entry will always be deleted when
- * the CIPSO DOI is deleted we aren't going to keep the
- * lock. */
- rcu_read_lock();
- entry->type_def.cipsov4 = cipso_v4_doi_getdef(tmp_val);
- if (entry->type_def.cipsov4 == NULL) {
- rcu_read_unlock();
- goto adddef_failure;
- }
- ret_val = netlbl_domhsh_add_default(entry, &audit_info);
- rcu_read_unlock();
- break;
- default:
- goto adddef_failure;
- }
- if (ret_val != 0)
- goto adddef_failure;
-
- return 0;
-
-adddef_failure:
- kfree(entry);
- return ret_val;
+ return netlbl_mgmt_add_common(info, &audit_info);
}
/**
@@ -371,19 +578,10 @@
ret_val = -ENOENT;
goto listdef_failure_lock;
}
- ret_val = nla_put_u32(ans_skb, NLBL_MGMT_A_PROTOCOL, entry->type);
- if (ret_val != 0)
- goto listdef_failure_lock;
- switch (entry->type) {
- case NETLBL_NLTYPE_CIPSOV4:
- ret_val = nla_put_u32(ans_skb,
- NLBL_MGMT_A_CV4DOI,
- entry->type_def.cipsov4->doi);
- if (ret_val != 0)
- goto listdef_failure_lock;
- break;
- }
+ ret_val = netlbl_mgmt_listentry(ans_skb, entry);
rcu_read_unlock();
+ if (ret_val != 0)
+ goto listdef_failure;
genlmsg_end(ans_skb, data);
return genlmsg_reply(ans_skb, info);
diff --git a/net/netlabel/netlabel_mgmt.h b/net/netlabel/netlabel_mgmt.h
index a43bff1..05d9643 100644
--- a/net/netlabel/netlabel_mgmt.h
+++ b/net/netlabel/netlabel_mgmt.h
@@ -45,6 +45,16 @@
* NLBL_MGMT_A_DOMAIN
* NLBL_MGMT_A_PROTOCOL
*
+ * If IPv4 is specified the following attributes are required:
+ *
+ * NLBL_MGMT_A_IPV4ADDR
+ * NLBL_MGMT_A_IPV4MASK
+ *
+ * If IPv6 is specified the following attributes are required:
+ *
+ * NLBL_MGMT_A_IPV6ADDR
+ * NLBL_MGMT_A_IPV6MASK
+ *
* If using NETLBL_NLTYPE_CIPSOV4 the following attributes are required:
*
* NLBL_MGMT_A_CV4DOI
@@ -68,13 +78,24 @@
* Required attributes:
*
* NLBL_MGMT_A_DOMAIN
+ *
+ * If the IP address selectors are not used the following attribute is
+ * required:
+ *
* NLBL_MGMT_A_PROTOCOL
*
- * If using NETLBL_NLTYPE_CIPSOV4 the following attributes are required:
+ * If the IP address selectors are used then the following attritbute is
+ * required:
+ *
+ * NLBL_MGMT_A_SELECTORLIST
+ *
+ * If the mapping is using the NETLBL_NLTYPE_CIPSOV4 type then the following
+ * attributes are required:
*
* NLBL_MGMT_A_CV4DOI
*
- * If using NETLBL_NLTYPE_UNLABELED no other attributes are required.
+ * If the mapping is using the NETLBL_NLTYPE_UNLABELED type no other
+ * attributes are required.
*
* o ADDDEF:
* Sent by an application to set the default domain mapping for the NetLabel
@@ -100,15 +121,23 @@
* application there is no payload. On success the kernel should send a
* response using the following format.
*
- * Required attributes:
+ * If the IP address selectors are not used the following attribute is
+ * required:
*
* NLBL_MGMT_A_PROTOCOL
*
- * If using NETLBL_NLTYPE_CIPSOV4 the following attributes are required:
+ * If the IP address selectors are used then the following attritbute is
+ * required:
+ *
+ * NLBL_MGMT_A_SELECTORLIST
+ *
+ * If the mapping is using the NETLBL_NLTYPE_CIPSOV4 type then the following
+ * attributes are required:
*
* NLBL_MGMT_A_CV4DOI
*
- * If using NETLBL_NLTYPE_UNLABELED no other attributes are required.
+ * If the mapping is using the NETLBL_NLTYPE_UNLABELED type no other
+ * attributes are required.
*
* o PROTOCOLS:
* Sent by an application to request a list of configured NetLabel protocols
@@ -162,6 +191,26 @@
NLBL_MGMT_A_CV4DOI,
/* (NLA_U32)
* the CIPSOv4 DOI value */
+ NLBL_MGMT_A_IPV6ADDR,
+ /* (NLA_BINARY, struct in6_addr)
+ * an IPv6 address */
+ NLBL_MGMT_A_IPV6MASK,
+ /* (NLA_BINARY, struct in6_addr)
+ * an IPv6 address mask */
+ NLBL_MGMT_A_IPV4ADDR,
+ /* (NLA_BINARY, struct in_addr)
+ * an IPv4 address */
+ NLBL_MGMT_A_IPV4MASK,
+ /* (NLA_BINARY, struct in_addr)
+ * and IPv4 address mask */
+ NLBL_MGMT_A_ADDRSELECTOR,
+ /* (NLA_NESTED)
+ * an IP address selector, must contain an address, mask, and protocol
+ * attribute plus any protocol specific attributes */
+ NLBL_MGMT_A_SELECTORLIST,
+ /* (NLA_NESTED)
+ * the selector list, there must be at least one
+ * NLBL_MGMT_A_ADDRSELECTOR attribute */
__NLBL_MGMT_A_MAX,
};
#define NLBL_MGMT_A_MAX (__NLBL_MGMT_A_MAX - 1)
diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c
index 921c118..e8a5c32 100644
--- a/net/netlabel/netlabel_unlabeled.c
+++ b/net/netlabel/netlabel_unlabeled.c
@@ -10,7 +10,7 @@
*/
/*
- * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 - 2007
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 - 2008
*
* 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
@@ -54,6 +54,7 @@
#include <asm/atomic.h>
#include "netlabel_user.h"
+#include "netlabel_addrlist.h"
#include "netlabel_domainhash.h"
#include "netlabel_unlabeled.h"
#include "netlabel_mgmt.h"
@@ -76,22 +77,20 @@
struct list_head *tbl;
u32 size;
};
+#define netlbl_unlhsh_addr4_entry(iter) \
+ container_of(iter, struct netlbl_unlhsh_addr4, list)
struct netlbl_unlhsh_addr4 {
- __be32 addr;
- __be32 mask;
u32 secid;
- u32 valid;
- struct list_head list;
+ struct netlbl_af4list list;
struct rcu_head rcu;
};
+#define netlbl_unlhsh_addr6_entry(iter) \
+ container_of(iter, struct netlbl_unlhsh_addr6, list)
struct netlbl_unlhsh_addr6 {
- struct in6_addr addr;
- struct in6_addr mask;
u32 secid;
- u32 valid;
- struct list_head list;
+ struct netlbl_af6list list;
struct rcu_head rcu;
};
struct netlbl_unlhsh_iface {
@@ -147,76 +146,6 @@
};
/*
- * Audit Helper Functions
- */
-
-/**
- * netlbl_unlabel_audit_addr4 - Audit an IPv4 address
- * @audit_buf: audit buffer
- * @dev: network interface
- * @addr: IP address
- * @mask: IP address mask
- *
- * Description:
- * Write the IPv4 address and address mask, if necessary, to @audit_buf.
- *
- */
-static void netlbl_unlabel_audit_addr4(struct audit_buffer *audit_buf,
- const char *dev,
- __be32 addr, __be32 mask)
-{
- u32 mask_val = ntohl(mask);
-
- if (dev != NULL)
- audit_log_format(audit_buf, " netif=%s", dev);
- audit_log_format(audit_buf, " src=" NIPQUAD_FMT, NIPQUAD(addr));
- if (mask_val != 0xffffffff) {
- u32 mask_len = 0;
- while (mask_val > 0) {
- mask_val <<= 1;
- mask_len++;
- }
- audit_log_format(audit_buf, " src_prefixlen=%d", mask_len);
- }
-}
-
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-/**
- * netlbl_unlabel_audit_addr6 - Audit an IPv6 address
- * @audit_buf: audit buffer
- * @dev: network interface
- * @addr: IP address
- * @mask: IP address mask
- *
- * Description:
- * Write the IPv6 address and address mask, if necessary, to @audit_buf.
- *
- */
-static void netlbl_unlabel_audit_addr6(struct audit_buffer *audit_buf,
- const char *dev,
- const struct in6_addr *addr,
- const struct in6_addr *mask)
-{
- if (dev != NULL)
- audit_log_format(audit_buf, " netif=%s", dev);
- audit_log_format(audit_buf, " src=" NIP6_FMT, NIP6(*addr));
- if (ntohl(mask->s6_addr32[3]) != 0xffffffff) {
- u32 mask_len = 0;
- u32 mask_val;
- int iter = -1;
- while (ntohl(mask->s6_addr32[++iter]) == 0xffffffff)
- mask_len += 32;
- mask_val = ntohl(mask->s6_addr32[iter]);
- while (mask_val > 0) {
- mask_val <<= 1;
- mask_len++;
- }
- audit_log_format(audit_buf, " src_prefixlen=%d", mask_len);
- }
-}
-#endif /* IPv6 */
-
-/*
* Unlabeled Connection Hash Table Functions
*/
@@ -274,26 +203,28 @@
static void netlbl_unlhsh_free_iface(struct rcu_head *entry)
{
struct netlbl_unlhsh_iface *iface;
- struct netlbl_unlhsh_addr4 *iter4;
- struct netlbl_unlhsh_addr4 *tmp4;
- struct netlbl_unlhsh_addr6 *iter6;
- struct netlbl_unlhsh_addr6 *tmp6;
+ struct netlbl_af4list *iter4;
+ struct netlbl_af4list *tmp4;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ struct netlbl_af6list *iter6;
+ struct netlbl_af6list *tmp6;
+#endif /* IPv6 */
iface = container_of(entry, struct netlbl_unlhsh_iface, rcu);
/* no need for locks here since we are the only one with access to this
* structure */
- list_for_each_entry_safe(iter4, tmp4, &iface->addr4_list, list)
- if (iter4->valid) {
- list_del_rcu(&iter4->list);
- kfree(iter4);
- }
- list_for_each_entry_safe(iter6, tmp6, &iface->addr6_list, list)
- if (iter6->valid) {
- list_del_rcu(&iter6->list);
- kfree(iter6);
- }
+ netlbl_af4list_foreach_safe(iter4, tmp4, &iface->addr4_list) {
+ netlbl_af4list_remove_entry(iter4);
+ kfree(netlbl_unlhsh_addr4_entry(iter4));
+ }
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ netlbl_af6list_foreach_safe(iter6, tmp6, &iface->addr6_list) {
+ netlbl_af6list_remove_entry(iter6);
+ kfree(netlbl_unlhsh_addr6_entry(iter6));
+ }
+#endif /* IPv6 */
kfree(iface);
}
@@ -316,59 +247,6 @@
}
/**
- * netlbl_unlhsh_search_addr4 - Search for a matching IPv4 address entry
- * @addr: IPv4 address
- * @iface: the network interface entry
- *
- * Description:
- * Searches the IPv4 address list of the network interface specified by @iface.
- * If a matching address entry is found it is returned, otherwise NULL is
- * returned. The caller is responsible for calling the rcu_read_[un]lock()
- * functions.
- *
- */
-static struct netlbl_unlhsh_addr4 *netlbl_unlhsh_search_addr4(
- __be32 addr,
- const struct netlbl_unlhsh_iface *iface)
-{
- struct netlbl_unlhsh_addr4 *iter;
-
- list_for_each_entry_rcu(iter, &iface->addr4_list, list)
- if (iter->valid && (addr & iter->mask) == iter->addr)
- return iter;
-
- return NULL;
-}
-
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-/**
- * netlbl_unlhsh_search_addr6 - Search for a matching IPv6 address entry
- * @addr: IPv6 address
- * @iface: the network interface entry
- *
- * Description:
- * Searches the IPv6 address list of the network interface specified by @iface.
- * If a matching address entry is found it is returned, otherwise NULL is
- * returned. The caller is responsible for calling the rcu_read_[un]lock()
- * functions.
- *
- */
-static struct netlbl_unlhsh_addr6 *netlbl_unlhsh_search_addr6(
- const struct in6_addr *addr,
- const struct netlbl_unlhsh_iface *iface)
-{
- struct netlbl_unlhsh_addr6 *iter;
-
- list_for_each_entry_rcu(iter, &iface->addr6_list, list)
- if (iter->valid &&
- ipv6_masked_addr_cmp(&iter->addr, &iter->mask, addr) == 0)
- return iter;
-
- return NULL;
-}
-#endif /* IPv6 */
-
-/**
* netlbl_unlhsh_search_iface - Search for a matching interface entry
* @ifindex: the network interface
*
@@ -381,12 +259,12 @@
static struct netlbl_unlhsh_iface *netlbl_unlhsh_search_iface(int ifindex)
{
u32 bkt;
+ struct list_head *bkt_list;
struct netlbl_unlhsh_iface *iter;
bkt = netlbl_unlhsh_hash(ifindex);
- list_for_each_entry_rcu(iter,
- &rcu_dereference(netlbl_unlhsh)->tbl[bkt],
- list)
+ bkt_list = &rcu_dereference(netlbl_unlhsh)->tbl[bkt];
+ list_for_each_entry_rcu(iter, bkt_list, list)
if (iter->valid && iter->ifindex == ifindex)
return iter;
@@ -439,43 +317,26 @@
const struct in_addr *mask,
u32 secid)
{
+ int ret_val;
struct netlbl_unlhsh_addr4 *entry;
- struct netlbl_unlhsh_addr4 *iter;
entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
if (entry == NULL)
return -ENOMEM;
- entry->addr = addr->s_addr & mask->s_addr;
- entry->mask = mask->s_addr;
- entry->secid = secid;
- entry->valid = 1;
+ entry->list.addr = addr->s_addr & mask->s_addr;
+ entry->list.mask = mask->s_addr;
+ entry->list.valid = 1;
INIT_RCU_HEAD(&entry->rcu);
+ entry->secid = secid;
spin_lock(&netlbl_unlhsh_lock);
- iter = netlbl_unlhsh_search_addr4(entry->addr, iface);
- if (iter != NULL &&
- iter->addr == addr->s_addr && iter->mask == mask->s_addr) {
- spin_unlock(&netlbl_unlhsh_lock);
- kfree(entry);
- return -EEXIST;
- }
- /* in order to speed up address searches through the list (the common
- * case) we need to keep the list in order based on the size of the
- * address mask such that the entry with the widest mask (smallest
- * numerical value) appears first in the list */
- list_for_each_entry_rcu(iter, &iface->addr4_list, list)
- if (iter->valid &&
- ntohl(entry->mask) > ntohl(iter->mask)) {
- __list_add_rcu(&entry->list,
- iter->list.prev,
- &iter->list);
- spin_unlock(&netlbl_unlhsh_lock);
- return 0;
- }
- list_add_tail_rcu(&entry->list, &iface->addr4_list);
+ ret_val = netlbl_af4list_add(&entry->list, &iface->addr4_list);
spin_unlock(&netlbl_unlhsh_lock);
- return 0;
+
+ if (ret_val != 0)
+ kfree(entry);
+ return ret_val;
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
@@ -498,47 +359,29 @@
const struct in6_addr *mask,
u32 secid)
{
+ int ret_val;
struct netlbl_unlhsh_addr6 *entry;
- struct netlbl_unlhsh_addr6 *iter;
entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
if (entry == NULL)
return -ENOMEM;
- ipv6_addr_copy(&entry->addr, addr);
- entry->addr.s6_addr32[0] &= mask->s6_addr32[0];
- entry->addr.s6_addr32[1] &= mask->s6_addr32[1];
- entry->addr.s6_addr32[2] &= mask->s6_addr32[2];
- entry->addr.s6_addr32[3] &= mask->s6_addr32[3];
- ipv6_addr_copy(&entry->mask, mask);
- entry->secid = secid;
- entry->valid = 1;
+ ipv6_addr_copy(&entry->list.addr, addr);
+ entry->list.addr.s6_addr32[0] &= mask->s6_addr32[0];
+ entry->list.addr.s6_addr32[1] &= mask->s6_addr32[1];
+ entry->list.addr.s6_addr32[2] &= mask->s6_addr32[2];
+ entry->list.addr.s6_addr32[3] &= mask->s6_addr32[3];
+ ipv6_addr_copy(&entry->list.mask, mask);
+ entry->list.valid = 1;
INIT_RCU_HEAD(&entry->rcu);
+ entry->secid = secid;
spin_lock(&netlbl_unlhsh_lock);
- iter = netlbl_unlhsh_search_addr6(&entry->addr, iface);
- if (iter != NULL &&
- (ipv6_addr_equal(&iter->addr, addr) &&
- ipv6_addr_equal(&iter->mask, mask))) {
- spin_unlock(&netlbl_unlhsh_lock);
- kfree(entry);
- return -EEXIST;
- }
- /* in order to speed up address searches through the list (the common
- * case) we need to keep the list in order based on the size of the
- * address mask such that the entry with the widest mask (smallest
- * numerical value) appears first in the list */
- list_for_each_entry_rcu(iter, &iface->addr6_list, list)
- if (iter->valid &&
- ipv6_addr_cmp(&entry->mask, &iter->mask) > 0) {
- __list_add_rcu(&entry->list,
- iter->list.prev,
- &iter->list);
- spin_unlock(&netlbl_unlhsh_lock);
- return 0;
- }
- list_add_tail_rcu(&entry->list, &iface->addr6_list);
+ ret_val = netlbl_af6list_add(&entry->list, &iface->addr6_list);
spin_unlock(&netlbl_unlhsh_lock);
+
+ if (ret_val != 0)
+ kfree(entry);
return 0;
}
#endif /* IPv6 */
@@ -658,10 +501,10 @@
mask4 = (struct in_addr *)mask;
ret_val = netlbl_unlhsh_add_addr4(iface, addr4, mask4, secid);
if (audit_buf != NULL)
- netlbl_unlabel_audit_addr4(audit_buf,
- dev_name,
- addr4->s_addr,
- mask4->s_addr);
+ netlbl_af4list_audit_addr(audit_buf, 1,
+ dev_name,
+ addr4->s_addr,
+ mask4->s_addr);
break;
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
@@ -672,9 +515,9 @@
mask6 = (struct in6_addr *)mask;
ret_val = netlbl_unlhsh_add_addr6(iface, addr6, mask6, secid);
if (audit_buf != NULL)
- netlbl_unlabel_audit_addr6(audit_buf,
- dev_name,
- addr6, mask6);
+ netlbl_af6list_audit_addr(audit_buf, 1,
+ dev_name,
+ addr6, mask6);
break;
}
#endif /* IPv6 */
@@ -719,35 +562,34 @@
const struct in_addr *mask,
struct netlbl_audit *audit_info)
{
- int ret_val = -ENOENT;
+ int ret_val = 0;
+ struct netlbl_af4list *list_entry;
struct netlbl_unlhsh_addr4 *entry;
- struct audit_buffer *audit_buf = NULL;
+ struct audit_buffer *audit_buf;
struct net_device *dev;
- char *secctx = NULL;
+ char *secctx;
u32 secctx_len;
spin_lock(&netlbl_unlhsh_lock);
- entry = netlbl_unlhsh_search_addr4(addr->s_addr, iface);
- if (entry != NULL &&
- entry->addr == addr->s_addr && entry->mask == mask->s_addr) {
- entry->valid = 0;
- list_del_rcu(&entry->list);
- ret_val = 0;
- }
+ list_entry = netlbl_af4list_remove(addr->s_addr, mask->s_addr,
+ &iface->addr4_list);
spin_unlock(&netlbl_unlhsh_lock);
+ if (list_entry == NULL)
+ ret_val = -ENOENT;
+ entry = netlbl_unlhsh_addr4_entry(list_entry);
audit_buf = netlbl_audit_start_common(AUDIT_MAC_UNLBL_STCDEL,
audit_info);
if (audit_buf != NULL) {
dev = dev_get_by_index(net, iface->ifindex);
- netlbl_unlabel_audit_addr4(audit_buf,
- (dev != NULL ? dev->name : NULL),
- entry->addr, entry->mask);
+ netlbl_af4list_audit_addr(audit_buf, 1,
+ (dev != NULL ? dev->name : NULL),
+ addr->s_addr, mask->s_addr);
if (dev != NULL)
dev_put(dev);
- if (security_secid_to_secctx(entry->secid,
- &secctx,
- &secctx_len) == 0) {
+ if (entry && security_secid_to_secctx(entry->secid,
+ &secctx,
+ &secctx_len) == 0) {
audit_log_format(audit_buf, " sec_obj=%s", secctx);
security_release_secctx(secctx, secctx_len);
}
@@ -781,36 +623,33 @@
const struct in6_addr *mask,
struct netlbl_audit *audit_info)
{
- int ret_val = -ENOENT;
+ int ret_val = 0;
+ struct netlbl_af6list *list_entry;
struct netlbl_unlhsh_addr6 *entry;
- struct audit_buffer *audit_buf = NULL;
+ struct audit_buffer *audit_buf;
struct net_device *dev;
- char *secctx = NULL;
+ char *secctx;
u32 secctx_len;
spin_lock(&netlbl_unlhsh_lock);
- entry = netlbl_unlhsh_search_addr6(addr, iface);
- if (entry != NULL &&
- (ipv6_addr_equal(&entry->addr, addr) &&
- ipv6_addr_equal(&entry->mask, mask))) {
- entry->valid = 0;
- list_del_rcu(&entry->list);
- ret_val = 0;
- }
+ list_entry = netlbl_af6list_remove(addr, mask, &iface->addr6_list);
spin_unlock(&netlbl_unlhsh_lock);
+ if (list_entry == NULL)
+ ret_val = -ENOENT;
+ entry = netlbl_unlhsh_addr6_entry(list_entry);
audit_buf = netlbl_audit_start_common(AUDIT_MAC_UNLBL_STCDEL,
audit_info);
if (audit_buf != NULL) {
dev = dev_get_by_index(net, iface->ifindex);
- netlbl_unlabel_audit_addr6(audit_buf,
- (dev != NULL ? dev->name : NULL),
- addr, mask);
+ netlbl_af6list_audit_addr(audit_buf, 1,
+ (dev != NULL ? dev->name : NULL),
+ addr, mask);
if (dev != NULL)
dev_put(dev);
- if (security_secid_to_secctx(entry->secid,
- &secctx,
- &secctx_len) == 0) {
+ if (entry && security_secid_to_secctx(entry->secid,
+ &secctx,
+ &secctx_len) == 0) {
audit_log_format(audit_buf, " sec_obj=%s", secctx);
security_release_secctx(secctx, secctx_len);
}
@@ -836,16 +675,18 @@
*/
static void netlbl_unlhsh_condremove_iface(struct netlbl_unlhsh_iface *iface)
{
- struct netlbl_unlhsh_addr4 *iter4;
- struct netlbl_unlhsh_addr6 *iter6;
+ struct netlbl_af4list *iter4;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ struct netlbl_af6list *iter6;
+#endif /* IPv6 */
spin_lock(&netlbl_unlhsh_lock);
- list_for_each_entry_rcu(iter4, &iface->addr4_list, list)
- if (iter4->valid)
- goto unlhsh_condremove_failure;
- list_for_each_entry_rcu(iter6, &iface->addr6_list, list)
- if (iter6->valid)
- goto unlhsh_condremove_failure;
+ netlbl_af4list_foreach_rcu(iter4, &iface->addr4_list)
+ goto unlhsh_condremove_failure;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ netlbl_af6list_foreach_rcu(iter6, &iface->addr6_list)
+ goto unlhsh_condremove_failure;
+#endif /* IPv6 */
iface->valid = 0;
if (iface->ifindex > 0)
list_del_rcu(&iface->list);
@@ -1349,7 +1190,7 @@
if (addr4) {
struct in_addr addr_struct;
- addr_struct.s_addr = addr4->addr;
+ addr_struct.s_addr = addr4->list.addr;
ret_val = nla_put(cb_arg->skb,
NLBL_UNLABEL_A_IPV4ADDR,
sizeof(struct in_addr),
@@ -1357,7 +1198,7 @@
if (ret_val != 0)
goto list_cb_failure;
- addr_struct.s_addr = addr4->mask;
+ addr_struct.s_addr = addr4->list.mask;
ret_val = nla_put(cb_arg->skb,
NLBL_UNLABEL_A_IPV4MASK,
sizeof(struct in_addr),
@@ -1370,14 +1211,14 @@
ret_val = nla_put(cb_arg->skb,
NLBL_UNLABEL_A_IPV6ADDR,
sizeof(struct in6_addr),
- &addr6->addr);
+ &addr6->list.addr);
if (ret_val != 0)
goto list_cb_failure;
ret_val = nla_put(cb_arg->skb,
NLBL_UNLABEL_A_IPV6MASK,
sizeof(struct in6_addr),
- &addr6->mask);
+ &addr6->list.mask);
if (ret_val != 0)
goto list_cb_failure;
@@ -1425,8 +1266,11 @@
u32 iter_bkt;
u32 iter_chain = 0, iter_addr4 = 0, iter_addr6 = 0;
struct netlbl_unlhsh_iface *iface;
- struct netlbl_unlhsh_addr4 *addr4;
- struct netlbl_unlhsh_addr6 *addr6;
+ struct list_head *iter_list;
+ struct netlbl_af4list *addr4;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ struct netlbl_af6list *addr6;
+#endif
cb_arg.nl_cb = cb;
cb_arg.skb = skb;
@@ -1436,44 +1280,43 @@
for (iter_bkt = skip_bkt;
iter_bkt < rcu_dereference(netlbl_unlhsh)->size;
iter_bkt++, iter_chain = 0, iter_addr4 = 0, iter_addr6 = 0) {
- list_for_each_entry_rcu(iface,
- &rcu_dereference(netlbl_unlhsh)->tbl[iter_bkt],
- list) {
+ iter_list = &rcu_dereference(netlbl_unlhsh)->tbl[iter_bkt];
+ list_for_each_entry_rcu(iface, iter_list, list) {
if (!iface->valid ||
iter_chain++ < skip_chain)
continue;
- list_for_each_entry_rcu(addr4,
- &iface->addr4_list,
- list) {
- if (!addr4->valid || iter_addr4++ < skip_addr4)
+ netlbl_af4list_foreach_rcu(addr4,
+ &iface->addr4_list) {
+ if (iter_addr4++ < skip_addr4)
continue;
if (netlbl_unlabel_staticlist_gen(
- NLBL_UNLABEL_C_STATICLIST,
- iface,
- addr4,
- NULL,
- &cb_arg) < 0) {
+ NLBL_UNLABEL_C_STATICLIST,
+ iface,
+ netlbl_unlhsh_addr4_entry(addr4),
+ NULL,
+ &cb_arg) < 0) {
iter_addr4--;
iter_chain--;
goto unlabel_staticlist_return;
}
}
- list_for_each_entry_rcu(addr6,
- &iface->addr6_list,
- list) {
- if (!addr6->valid || iter_addr6++ < skip_addr6)
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ netlbl_af6list_foreach_rcu(addr6,
+ &iface->addr6_list) {
+ if (iter_addr6++ < skip_addr6)
continue;
if (netlbl_unlabel_staticlist_gen(
- NLBL_UNLABEL_C_STATICLIST,
- iface,
- NULL,
- addr6,
- &cb_arg) < 0) {
+ NLBL_UNLABEL_C_STATICLIST,
+ iface,
+ NULL,
+ netlbl_unlhsh_addr6_entry(addr6),
+ &cb_arg) < 0) {
iter_addr6--;
iter_chain--;
goto unlabel_staticlist_return;
}
}
+#endif /* IPv6 */
}
}
@@ -1504,9 +1347,12 @@
struct netlbl_unlhsh_iface *iface;
u32 skip_addr4 = cb->args[0];
u32 skip_addr6 = cb->args[1];
- u32 iter_addr4 = 0, iter_addr6 = 0;
- struct netlbl_unlhsh_addr4 *addr4;
- struct netlbl_unlhsh_addr6 *addr6;
+ u32 iter_addr4 = 0;
+ struct netlbl_af4list *addr4;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ u32 iter_addr6 = 0;
+ struct netlbl_af6list *addr6;
+#endif
cb_arg.nl_cb = cb;
cb_arg.skb = skb;
@@ -1517,30 +1363,32 @@
if (iface == NULL || !iface->valid)
goto unlabel_staticlistdef_return;
- list_for_each_entry_rcu(addr4, &iface->addr4_list, list) {
- if (!addr4->valid || iter_addr4++ < skip_addr4)
+ netlbl_af4list_foreach_rcu(addr4, &iface->addr4_list) {
+ if (iter_addr4++ < skip_addr4)
continue;
if (netlbl_unlabel_staticlist_gen(NLBL_UNLABEL_C_STATICLISTDEF,
- iface,
- addr4,
- NULL,
- &cb_arg) < 0) {
+ iface,
+ netlbl_unlhsh_addr4_entry(addr4),
+ NULL,
+ &cb_arg) < 0) {
iter_addr4--;
goto unlabel_staticlistdef_return;
}
}
- list_for_each_entry_rcu(addr6, &iface->addr6_list, list) {
- if (!addr6->valid || iter_addr6++ < skip_addr6)
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ netlbl_af6list_foreach_rcu(addr6, &iface->addr6_list) {
+ if (iter_addr6++ < skip_addr6)
continue;
if (netlbl_unlabel_staticlist_gen(NLBL_UNLABEL_C_STATICLISTDEF,
- iface,
- NULL,
- addr6,
- &cb_arg) < 0) {
+ iface,
+ NULL,
+ netlbl_unlhsh_addr6_entry(addr6),
+ &cb_arg) < 0) {
iter_addr6--;
goto unlabel_staticlistdef_return;
}
}
+#endif /* IPv6 */
unlabel_staticlistdef_return:
rcu_read_unlock();
@@ -1718,25 +1566,27 @@
switch (family) {
case PF_INET: {
struct iphdr *hdr4;
- struct netlbl_unlhsh_addr4 *addr4;
+ struct netlbl_af4list *addr4;
hdr4 = ip_hdr(skb);
- addr4 = netlbl_unlhsh_search_addr4(hdr4->saddr, iface);
+ addr4 = netlbl_af4list_search(hdr4->saddr,
+ &iface->addr4_list);
if (addr4 == NULL)
goto unlabel_getattr_nolabel;
- secattr->attr.secid = addr4->secid;
+ secattr->attr.secid = netlbl_unlhsh_addr4_entry(addr4)->secid;
break;
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case PF_INET6: {
struct ipv6hdr *hdr6;
- struct netlbl_unlhsh_addr6 *addr6;
+ struct netlbl_af6list *addr6;
hdr6 = ipv6_hdr(skb);
- addr6 = netlbl_unlhsh_search_addr6(&hdr6->saddr, iface);
+ addr6 = netlbl_af6list_search(&hdr6->saddr,
+ &iface->addr6_list);
if (addr6 == NULL)
goto unlabel_getattr_nolabel;
- secattr->attr.secid = addr6->secid;
+ secattr->attr.secid = netlbl_unlhsh_addr6_entry(addr6)->secid;
break;
}
#endif /* IPv6 */
diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c
index e5b6955..21124ec 100644
--- a/net/rfkill/rfkill-input.c
+++ b/net/rfkill/rfkill-input.c
@@ -16,6 +16,7 @@
#include <linux/workqueue.h>
#include <linux/init.h>
#include <linux/rfkill.h>
+#include <linux/sched.h>
#include "rfkill-input.h"
diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include
index d64e6ba..982dcae 100644
--- a/scripts/Kbuild.include
+++ b/scripts/Kbuild.include
@@ -105,12 +105,12 @@
# Usage: cflags-y += $(call cc-option,-march=winchip-c6,-march=i586)
cc-option = $(call try-run,\
- $(CC) $(KBUILD_CFLAGS) $(1) -S -xc /dev/null -o "$$TMP",$(1),$(2))
+ $(CC) $(KBUILD_CFLAGS) $(1) -c -xc /dev/null -o "$$TMP",$(1),$(2))
# cc-option-yn
# Usage: flag := $(call cc-option-yn,-march=winchip-c6)
cc-option-yn = $(call try-run,\
- $(CC) $(KBUILD_CFLAGS) $(1) -S -xc /dev/null -o "$$TMP",y,n)
+ $(CC) $(KBUILD_CFLAGS) $(1) -c -xc /dev/null -o "$$TMP",y,n)
# cc-option-align
# Prefix align with either -falign or -malign
diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c
index 4c9890e..473f94e 100644
--- a/scripts/mod/file2alias.c
+++ b/scripts/mod/file2alias.c
@@ -629,6 +629,59 @@
return 1;
}
+static const struct dmifield {
+ const char *prefix;
+ int field;
+} dmi_fields[] = {
+ { "bvn", DMI_BIOS_VENDOR },
+ { "bvr", DMI_BIOS_VERSION },
+ { "bd", DMI_BIOS_DATE },
+ { "svn", DMI_SYS_VENDOR },
+ { "pn", DMI_PRODUCT_NAME },
+ { "pvr", DMI_PRODUCT_VERSION },
+ { "rvn", DMI_BOARD_VENDOR },
+ { "rn", DMI_BOARD_NAME },
+ { "rvr", DMI_BOARD_VERSION },
+ { "cvn", DMI_CHASSIS_VENDOR },
+ { "ct", DMI_CHASSIS_TYPE },
+ { "cvr", DMI_CHASSIS_VERSION },
+ { NULL, DMI_NONE }
+};
+
+static void dmi_ascii_filter(char *d, const char *s)
+{
+ /* Filter out characters we don't want to see in the modalias string */
+ for (; *s; s++)
+ if (*s > ' ' && *s < 127 && *s != ':')
+ *(d++) = *s;
+
+ *d = 0;
+}
+
+
+static int do_dmi_entry(const char *filename, struct dmi_system_id *id,
+ char *alias)
+{
+ int i, j;
+
+ sprintf(alias, "dmi*");
+
+ for (i = 0; i < ARRAY_SIZE(dmi_fields); i++) {
+ for (j = 0; j < 4; j++) {
+ if (id->matches[j].slot &&
+ id->matches[j].slot == dmi_fields[i].field) {
+ sprintf(alias + strlen(alias), ":%s*",
+ dmi_fields[i].prefix);
+ dmi_ascii_filter(alias + strlen(alias),
+ id->matches[j].substr);
+ strcat(alias, "*");
+ }
+ }
+ }
+
+ strcat(alias, ":");
+ return 1;
+}
/* Ignore any prefix, eg. some architectures prepend _ */
static inline int sym_is(const char *symbol, const char *name)
{
@@ -760,6 +813,10 @@
do_table(symval, sym->st_size,
sizeof(struct i2c_device_id), "i2c",
do_i2c_entry, mod);
+ else if (sym_is(symname, "__mod_dmi_device_table"))
+ do_table(symval, sym->st_size,
+ sizeof(struct dmi_system_id), "dmi",
+ do_dmi_entry, mod);
free(zeros);
}
diff --git a/security/inode.c b/security/inode.c
index ca4958e..efea5a6 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -20,8 +20,7 @@
#include <linux/init.h>
#include <linux/namei.h>
#include <linux/security.h>
-
-#define SECURITYFS_MAGIC 0x73636673
+#include <linux/magic.h>
static struct vfsmount *mount;
static int mount_count;
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 4a7374c..576e511 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -291,6 +291,7 @@
struct sk_security_struct *ssec = sk->sk_security;
sk->sk_security = NULL;
+ selinux_netlbl_sk_security_free(ssec);
kfree(ssec);
}
@@ -324,7 +325,7 @@
Opt_rootcontext = 4,
};
-static match_table_t tokens = {
+static const match_table_t tokens = {
{Opt_context, CONTEXT_STR "%s"},
{Opt_fscontext, FSCONTEXT_STR "%s"},
{Opt_defcontext, DEFCONTEXT_STR "%s"},
@@ -2121,7 +2122,6 @@
long j = -1;
int drop_tty = 0;
- mutex_lock(&tty_mutex);
tty = get_current_tty();
if (tty) {
file_list_lock();
@@ -2139,8 +2139,8 @@
}
}
file_list_unlock();
+ tty_kref_put(tty);
}
- mutex_unlock(&tty_mutex);
/* Reset controlling tty. */
if (drop_tty)
no_tty();
@@ -3801,6 +3801,7 @@
static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen)
{
+ struct sock *sk = sock->sk;
struct inode_security_struct *isec;
int err;
@@ -3814,7 +3815,6 @@
isec = SOCK_INODE(sock)->i_security;
if (isec->sclass == SECCLASS_TCP_SOCKET ||
isec->sclass == SECCLASS_DCCP_SOCKET) {
- struct sock *sk = sock->sk;
struct avc_audit_data ad;
struct sockaddr_in *addr4 = NULL;
struct sockaddr_in6 *addr6 = NULL;
@@ -3848,6 +3848,8 @@
goto out;
}
+ err = selinux_netlbl_socket_connect(sk, address);
+
out:
return err;
}
@@ -4077,20 +4079,28 @@
}
static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
- struct avc_audit_data *ad,
- u16 family, char *addrp)
+ u16 family)
{
int err;
struct sk_security_struct *sksec = sk->sk_security;
u32 peer_sid;
u32 sk_sid = sksec->sid;
+ struct avc_audit_data ad;
+ char *addrp;
+
+ AVC_AUDIT_DATA_INIT(&ad, NET);
+ ad.u.net.netif = skb->iif;
+ ad.u.net.family = family;
+ err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL);
+ if (err)
+ return err;
if (selinux_compat_net)
- err = selinux_sock_rcv_skb_iptables_compat(sk, skb, ad,
+ err = selinux_sock_rcv_skb_iptables_compat(sk, skb, &ad,
family, addrp);
else
err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET,
- PACKET__RECV, ad);
+ PACKET__RECV, &ad);
if (err)
return err;
@@ -4099,12 +4109,14 @@
if (err)
return err;
err = avc_has_perm(sk_sid, peer_sid,
- SECCLASS_PEER, PEER__RECV, ad);
+ SECCLASS_PEER, PEER__RECV, &ad);
+ if (err)
+ selinux_netlbl_err(skb, err, 0);
} else {
- err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, ad);
+ err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, &ad);
if (err)
return err;
- err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, ad);
+ err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, &ad);
}
return err;
@@ -4118,6 +4130,8 @@
u32 sk_sid = sksec->sid;
struct avc_audit_data ad;
char *addrp;
+ u8 secmark_active;
+ u8 peerlbl_active;
if (family != PF_INET && family != PF_INET6)
return 0;
@@ -4126,6 +4140,18 @@
if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
family = PF_INET;
+ /* If any sort of compatibility mode is enabled then handoff processing
+ * to the selinux_sock_rcv_skb_compat() function to deal with the
+ * special handling. We do this in an attempt to keep this function
+ * as fast and as clean as possible. */
+ if (selinux_compat_net || !selinux_policycap_netpeer)
+ return selinux_sock_rcv_skb_compat(sk, skb, family);
+
+ secmark_active = selinux_secmark_enabled();
+ peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled();
+ if (!secmark_active && !peerlbl_active)
+ return 0;
+
AVC_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.netif = skb->iif;
ad.u.net.family = family;
@@ -4133,15 +4159,7 @@
if (err)
return err;
- /* If any sort of compatibility mode is enabled then handoff processing
- * to the selinux_sock_rcv_skb_compat() function to deal with the
- * special handling. We do this in an attempt to keep this function
- * as fast and as clean as possible. */
- if (selinux_compat_net || !selinux_policycap_netpeer)
- return selinux_sock_rcv_skb_compat(sk, skb, &ad,
- family, addrp);
-
- if (netlbl_enabled() || selinux_xfrm_enabled()) {
+ if (peerlbl_active) {
u32 peer_sid;
err = selinux_skb_peerlbl_sid(skb, family, &peer_sid);
@@ -4149,13 +4167,17 @@
return err;
err = selinux_inet_sys_rcv_skb(skb->iif, addrp, family,
peer_sid, &ad);
- if (err)
+ if (err) {
+ selinux_netlbl_err(skb, err, 0);
return err;
+ }
err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER,
PEER__RECV, &ad);
+ if (err)
+ selinux_netlbl_err(skb, err, 0);
}
- if (selinux_secmark_enabled()) {
+ if (secmark_active) {
err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET,
PACKET__RECV, &ad);
if (err)
@@ -4214,10 +4236,12 @@
u32 peer_secid = SECSID_NULL;
u16 family;
- if (sock)
+ if (skb && skb->protocol == htons(ETH_P_IP))
+ family = PF_INET;
+ else if (skb && skb->protocol == htons(ETH_P_IPV6))
+ family = PF_INET6;
+ else if (sock)
family = sock->sk->sk_family;
- else if (skb && skb->sk)
- family = skb->sk->sk_family;
else
goto out;
@@ -4275,8 +4299,6 @@
sk->sk_family == PF_UNIX)
isec->sid = sksec->sid;
sksec->sclass = isec->sclass;
-
- selinux_netlbl_sock_graft(sk, parent);
}
static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
@@ -4284,10 +4306,15 @@
{
struct sk_security_struct *sksec = sk->sk_security;
int err;
+ u16 family = sk->sk_family;
u32 newsid;
u32 peersid;
- err = selinux_skb_peerlbl_sid(skb, sk->sk_family, &peersid);
+ /* handle mapped IPv4 packets arriving via IPv6 sockets */
+ if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
+ family = PF_INET;
+
+ err = selinux_skb_peerlbl_sid(skb, family, &peersid);
if (err)
return err;
if (peersid == SECSID_NULL) {
@@ -4322,12 +4349,18 @@
selinux_netlbl_sk_security_reset(newsksec, req->rsk_ops->family);
}
-static void selinux_inet_conn_established(struct sock *sk,
- struct sk_buff *skb)
+static void selinux_inet_conn_established(struct sock *sk, struct sk_buff *skb)
{
+ u16 family = sk->sk_family;
struct sk_security_struct *sksec = sk->sk_security;
- selinux_skb_peerlbl_sid(skb, sk->sk_family, &sksec->peer_sid);
+ /* handle mapped IPv4 packets arriving via IPv6 sockets */
+ if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
+ family = PF_INET;
+
+ selinux_skb_peerlbl_sid(skb, family, &sksec->peer_sid);
+
+ selinux_netlbl_inet_conn_established(sk, family);
}
static void selinux_req_classify_flow(const struct request_sock *req,
@@ -4377,39 +4410,54 @@
static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex,
u16 family)
{
+ int err;
char *addrp;
u32 peer_sid;
struct avc_audit_data ad;
u8 secmark_active;
+ u8 netlbl_active;
u8 peerlbl_active;
if (!selinux_policycap_netpeer)
return NF_ACCEPT;
secmark_active = selinux_secmark_enabled();
- peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled();
+ netlbl_active = netlbl_enabled();
+ peerlbl_active = netlbl_active || selinux_xfrm_enabled();
if (!secmark_active && !peerlbl_active)
return NF_ACCEPT;
+ if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0)
+ return NF_DROP;
+
AVC_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.netif = ifindex;
ad.u.net.family = family;
if (selinux_parse_skb(skb, &ad, &addrp, 1, NULL) != 0)
return NF_DROP;
- if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0)
- return NF_DROP;
-
- if (peerlbl_active)
- if (selinux_inet_sys_rcv_skb(ifindex, addrp, family,
- peer_sid, &ad) != 0)
+ if (peerlbl_active) {
+ err = selinux_inet_sys_rcv_skb(ifindex, addrp, family,
+ peer_sid, &ad);
+ if (err) {
+ selinux_netlbl_err(skb, err, 1);
return NF_DROP;
+ }
+ }
if (secmark_active)
if (avc_has_perm(peer_sid, skb->secmark,
SECCLASS_PACKET, PACKET__FORWARD_IN, &ad))
return NF_DROP;
+ if (netlbl_active)
+ /* we do this in the FORWARD path and not the POST_ROUTING
+ * path because we want to make sure we apply the necessary
+ * labeling before IPsec is applied so we can leverage AH
+ * protection */
+ if (selinux_netlbl_skbuff_setsid(skb, family, peer_sid) != 0)
+ return NF_DROP;
+
return NF_ACCEPT;
}
@@ -4433,6 +4481,37 @@
}
#endif /* IPV6 */
+static unsigned int selinux_ip_output(struct sk_buff *skb,
+ u16 family)
+{
+ u32 sid;
+
+ if (!netlbl_enabled())
+ return NF_ACCEPT;
+
+ /* we do this in the LOCAL_OUT path and not the POST_ROUTING path
+ * because we want to make sure we apply the necessary labeling
+ * before IPsec is applied so we can leverage AH protection */
+ if (skb->sk) {
+ struct sk_security_struct *sksec = skb->sk->sk_security;
+ sid = sksec->sid;
+ } else
+ sid = SECINITSID_KERNEL;
+ if (selinux_netlbl_skbuff_setsid(skb, family, sid) != 0)
+ return NF_DROP;
+
+ return NF_ACCEPT;
+}
+
+static unsigned int selinux_ipv4_output(unsigned int hooknum,
+ struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ return selinux_ip_output(skb, PF_INET);
+}
+
static int selinux_ip_postroute_iptables_compat(struct sock *sk,
int ifindex,
struct avc_audit_data *ad,
@@ -4500,30 +4579,36 @@
static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
int ifindex,
- struct avc_audit_data *ad,
- u16 family,
- char *addrp,
- u8 proto)
+ u16 family)
{
struct sock *sk = skb->sk;
struct sk_security_struct *sksec;
+ struct avc_audit_data ad;
+ char *addrp;
+ u8 proto;
if (sk == NULL)
return NF_ACCEPT;
sksec = sk->sk_security;
+ AVC_AUDIT_DATA_INIT(&ad, NET);
+ ad.u.net.netif = ifindex;
+ ad.u.net.family = family;
+ if (selinux_parse_skb(skb, &ad, &addrp, 0, &proto))
+ return NF_DROP;
+
if (selinux_compat_net) {
if (selinux_ip_postroute_iptables_compat(skb->sk, ifindex,
- ad, family, addrp))
+ &ad, family, addrp))
return NF_DROP;
} else {
if (avc_has_perm(sksec->sid, skb->secmark,
- SECCLASS_PACKET, PACKET__SEND, ad))
+ SECCLASS_PACKET, PACKET__SEND, &ad))
return NF_DROP;
}
if (selinux_policycap_netpeer)
- if (selinux_xfrm_postroute_last(sksec->sid, skb, ad, proto))
+ if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto))
return NF_DROP;
return NF_ACCEPT;
@@ -4537,23 +4622,15 @@
struct sock *sk;
struct avc_audit_data ad;
char *addrp;
- u8 proto;
u8 secmark_active;
u8 peerlbl_active;
- AVC_AUDIT_DATA_INIT(&ad, NET);
- ad.u.net.netif = ifindex;
- ad.u.net.family = family;
- if (selinux_parse_skb(skb, &ad, &addrp, 0, &proto))
- return NF_DROP;
-
/* If any sort of compatibility mode is enabled then handoff processing
* to the selinux_ip_postroute_compat() function to deal with the
* special handling. We do this in an attempt to keep this function
* as fast and as clean as possible. */
if (selinux_compat_net || !selinux_policycap_netpeer)
- return selinux_ip_postroute_compat(skb, ifindex, &ad,
- family, addrp, proto);
+ return selinux_ip_postroute_compat(skb, ifindex, family);
/* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec
* packet transformation so allow the packet to pass without any checks
@@ -4569,21 +4646,45 @@
if (!secmark_active && !peerlbl_active)
return NF_ACCEPT;
- /* if the packet is locally generated (skb->sk != NULL) then use the
- * socket's label as the peer label, otherwise the packet is being
- * forwarded through this system and we need to fetch the peer label
- * directly from the packet */
+ /* if the packet is being forwarded then get the peer label from the
+ * packet itself; otherwise check to see if it is from a local
+ * application or the kernel, if from an application get the peer label
+ * from the sending socket, otherwise use the kernel's sid */
sk = skb->sk;
- if (sk) {
+ if (sk == NULL) {
+ switch (family) {
+ case PF_INET:
+ if (IPCB(skb)->flags & IPSKB_FORWARDED)
+ secmark_perm = PACKET__FORWARD_OUT;
+ else
+ secmark_perm = PACKET__SEND;
+ break;
+ case PF_INET6:
+ if (IP6CB(skb)->flags & IP6SKB_FORWARDED)
+ secmark_perm = PACKET__FORWARD_OUT;
+ else
+ secmark_perm = PACKET__SEND;
+ break;
+ default:
+ return NF_DROP;
+ }
+ if (secmark_perm == PACKET__FORWARD_OUT) {
+ if (selinux_skb_peerlbl_sid(skb, family, &peer_sid))
+ return NF_DROP;
+ } else
+ peer_sid = SECINITSID_KERNEL;
+ } else {
struct sk_security_struct *sksec = sk->sk_security;
peer_sid = sksec->sid;
secmark_perm = PACKET__SEND;
- } else {
- if (selinux_skb_peerlbl_sid(skb, family, &peer_sid))
- return NF_DROP;
- secmark_perm = PACKET__FORWARD_OUT;
}
+ AVC_AUDIT_DATA_INIT(&ad, NET);
+ ad.u.net.netif = ifindex;
+ ad.u.net.family = family;
+ if (selinux_parse_skb(skb, &ad, &addrp, 0, NULL))
+ return NF_DROP;
+
if (secmark_active)
if (avc_has_perm(peer_sid, skb->secmark,
SECCLASS_PACKET, secmark_perm, &ad))
@@ -5657,6 +5758,13 @@
.pf = PF_INET,
.hooknum = NF_INET_FORWARD,
.priority = NF_IP_PRI_SELINUX_FIRST,
+ },
+ {
+ .hook = selinux_ipv4_output,
+ .owner = THIS_MODULE,
+ .pf = PF_INET,
+ .hooknum = NF_INET_LOCAL_OUT,
+ .priority = NF_IP_PRI_SELINUX_FIRST,
}
};
diff --git a/security/selinux/include/netlabel.h b/security/selinux/include/netlabel.h
index 487a7d8..b913c8d 100644
--- a/security/selinux/include/netlabel.h
+++ b/security/selinux/include/netlabel.h
@@ -39,6 +39,9 @@
#ifdef CONFIG_NETLABEL
void selinux_netlbl_cache_invalidate(void);
+void selinux_netlbl_err(struct sk_buff *skb, int error, int gateway);
+
+void selinux_netlbl_sk_security_free(struct sk_security_struct *ssec);
void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec,
int family);
@@ -46,8 +49,11 @@
u16 family,
u32 *type,
u32 *sid);
+int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
+ u16 family,
+ u32 sid);
-void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock);
+void selinux_netlbl_inet_conn_established(struct sock *sk, u16 family);
int selinux_netlbl_socket_post_create(struct socket *sock);
int selinux_netlbl_inode_permission(struct inode *inode, int mask);
int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
@@ -57,12 +63,27 @@
int selinux_netlbl_socket_setsockopt(struct socket *sock,
int level,
int optname);
+int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr);
+
#else
static inline void selinux_netlbl_cache_invalidate(void)
{
return;
}
+static inline void selinux_netlbl_err(struct sk_buff *skb,
+ int error,
+ int gateway)
+{
+ return;
+}
+
+static inline void selinux_netlbl_sk_security_free(
+ struct sk_security_struct *ssec)
+{
+ return;
+}
+
static inline void selinux_netlbl_sk_security_reset(
struct sk_security_struct *ssec,
int family)
@@ -79,9 +100,21 @@
*sid = SECSID_NULL;
return 0;
}
+static inline int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
+ u16 family,
+ u32 sid)
+{
+ return 0;
+}
-static inline void selinux_netlbl_sock_graft(struct sock *sk,
- struct socket *sock)
+static inline int selinux_netlbl_conn_setsid(struct sock *sk,
+ struct sockaddr *addr)
+{
+ return 0;
+}
+
+static inline void selinux_netlbl_inet_conn_established(struct sock *sk,
+ u16 family)
{
return;
}
@@ -107,6 +140,11 @@
{
return 0;
}
+static inline int selinux_netlbl_socket_connect(struct sock *sk,
+ struct sockaddr *addr)
+{
+ return 0;
+}
#endif /* CONFIG_NETLABEL */
#endif
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 91070ab..f8be8d7 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -109,16 +109,19 @@
};
struct sk_security_struct {
- u32 sid; /* SID of this object */
- u32 peer_sid; /* SID of peer */
- u16 sclass; /* sock security class */
#ifdef CONFIG_NETLABEL
enum { /* NetLabel state */
NLBL_UNSET = 0,
NLBL_REQUIRE,
NLBL_LABELED,
+ NLBL_REQSKB,
+ NLBL_CONNLABELED,
} nlbl_state;
+ struct netlbl_lsm_secattr *nlbl_secattr; /* NetLabel sec attributes */
#endif
+ u32 sid; /* SID of this object */
+ u32 peer_sid; /* SID of peer */
+ u16 sclass; /* sock security class */
};
struct key_security_struct {
diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c
index 89b4183..f58701a 100644
--- a/security/selinux/netlabel.c
+++ b/security/selinux/netlabel.c
@@ -9,7 +9,7 @@
*/
/*
- * (c) Copyright Hewlett-Packard Development Company, L.P., 2007
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2007, 2008
*
* 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
@@ -29,8 +29,12 @@
#include <linux/spinlock.h>
#include <linux/rcupdate.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
#include <net/sock.h>
#include <net/netlabel.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
#include "objsec.h"
#include "security.h"
@@ -64,32 +68,69 @@
}
/**
- * selinux_netlbl_sock_setsid - Label a socket using the NetLabel mechanism
- * @sk: the socket to label
- * @sid: the SID to use
+ * selinux_netlbl_sock_genattr - Generate the NetLabel socket secattr
+ * @sk: the socket
*
* Description:
- * Attempt to label a socket using the NetLabel mechanism using the given
- * SID. Returns zero values on success, negative values on failure.
+ * Generate the NetLabel security attributes for a socket, making full use of
+ * the socket's attribute cache. Returns a pointer to the security attributes
+ * on success, NULL on failure.
*
*/
-static int selinux_netlbl_sock_setsid(struct sock *sk, u32 sid)
+static struct netlbl_lsm_secattr *selinux_netlbl_sock_genattr(struct sock *sk)
{
int rc;
struct sk_security_struct *sksec = sk->sk_security;
- struct netlbl_lsm_secattr secattr;
+ struct netlbl_lsm_secattr *secattr;
- netlbl_secattr_init(&secattr);
+ if (sksec->nlbl_secattr != NULL)
+ return sksec->nlbl_secattr;
- rc = security_netlbl_sid_to_secattr(sid, &secattr);
- if (rc != 0)
- goto sock_setsid_return;
- rc = netlbl_sock_setattr(sk, &secattr);
- if (rc == 0)
+ secattr = netlbl_secattr_alloc(GFP_ATOMIC);
+ if (secattr == NULL)
+ return NULL;
+ rc = security_netlbl_sid_to_secattr(sksec->sid, secattr);
+ if (rc != 0) {
+ netlbl_secattr_free(secattr);
+ return NULL;
+ }
+ sksec->nlbl_secattr = secattr;
+
+ return secattr;
+}
+
+/**
+ * selinux_netlbl_sock_setsid - Label a socket using the NetLabel mechanism
+ * @sk: the socket to label
+ *
+ * Description:
+ * Attempt to label a socket using the NetLabel mechanism. Returns zero values
+ * on success, negative values on failure.
+ *
+ */
+static int selinux_netlbl_sock_setsid(struct sock *sk)
+{
+ int rc;
+ struct sk_security_struct *sksec = sk->sk_security;
+ struct netlbl_lsm_secattr *secattr;
+
+ if (sksec->nlbl_state != NLBL_REQUIRE)
+ return 0;
+
+ secattr = selinux_netlbl_sock_genattr(sk);
+ if (secattr == NULL)
+ return -ENOMEM;
+ rc = netlbl_sock_setattr(sk, secattr);
+ switch (rc) {
+ case 0:
sksec->nlbl_state = NLBL_LABELED;
+ break;
+ case -EDESTADDRREQ:
+ sksec->nlbl_state = NLBL_REQSKB;
+ rc = 0;
+ break;
+ }
-sock_setsid_return:
- netlbl_secattr_destroy(&secattr);
return rc;
}
@@ -106,6 +147,38 @@
}
/**
+ * selinux_netlbl_err - Handle a NetLabel packet error
+ * @skb: the packet
+ * @error: the error code
+ * @gateway: true if host is acting as a gateway, false otherwise
+ *
+ * Description:
+ * When a packet is dropped due to a call to avc_has_perm() pass the error
+ * code to the NetLabel subsystem so any protocol specific processing can be
+ * done. This is safe to call even if you are unsure if NetLabel labeling is
+ * present on the packet, NetLabel is smart enough to only act when it should.
+ *
+ */
+void selinux_netlbl_err(struct sk_buff *skb, int error, int gateway)
+{
+ netlbl_skbuff_err(skb, error, gateway);
+}
+
+/**
+ * selinux_netlbl_sk_security_free - Free the NetLabel fields
+ * @sssec: the sk_security_struct
+ *
+ * Description:
+ * Free all of the memory in the NetLabel fields of a sk_security_struct.
+ *
+ */
+void selinux_netlbl_sk_security_free(struct sk_security_struct *ssec)
+{
+ if (ssec->nlbl_secattr != NULL)
+ netlbl_secattr_free(ssec->nlbl_secattr);
+}
+
+/**
* selinux_netlbl_sk_security_reset - Reset the NetLabel fields
* @ssec: the sk_security_struct
* @family: the socket family
@@ -163,35 +236,118 @@
}
/**
- * selinux_netlbl_sock_graft - Netlabel the new socket
- * @sk: the new connection
- * @sock: the new socket
+ * selinux_netlbl_skbuff_setsid - Set the NetLabel on a packet given a sid
+ * @skb: the packet
+ * @family: protocol family
+ * @sid: the SID
*
- * Description:
- * The connection represented by @sk is being grafted onto @sock so set the
- * socket's NetLabel to match the SID of @sk.
+ * Description
+ * Call the NetLabel mechanism to set the label of a packet using @sid.
+ * Returns zero on auccess, negative values on failure.
*
*/
-void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock)
+int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
+ u16 family,
+ u32 sid)
{
+ int rc;
+ struct netlbl_lsm_secattr secattr_storage;
+ struct netlbl_lsm_secattr *secattr = NULL;
+ struct sock *sk;
+
+ /* if this is a locally generated packet check to see if it is already
+ * being labeled by it's parent socket, if it is just exit */
+ sk = skb->sk;
+ if (sk != NULL) {
+ struct sk_security_struct *sksec = sk->sk_security;
+ if (sksec->nlbl_state != NLBL_REQSKB)
+ return 0;
+ secattr = sksec->nlbl_secattr;
+ }
+ if (secattr == NULL) {
+ secattr = &secattr_storage;
+ netlbl_secattr_init(secattr);
+ rc = security_netlbl_sid_to_secattr(sid, secattr);
+ if (rc != 0)
+ goto skbuff_setsid_return;
+ }
+
+ rc = netlbl_skbuff_setattr(skb, family, secattr);
+
+skbuff_setsid_return:
+ if (secattr == &secattr_storage)
+ netlbl_secattr_destroy(secattr);
+ return rc;
+}
+
+/**
+ * selinux_netlbl_inet_conn_established - Netlabel the newly accepted connection
+ * @sk: the new connection
+ *
+ * Description:
+ * A new connection has been established on @sk so make sure it is labeled
+ * correctly with the NetLabel susbsystem.
+ *
+ */
+void selinux_netlbl_inet_conn_established(struct sock *sk, u16 family)
+{
+ int rc;
struct sk_security_struct *sksec = sk->sk_security;
- struct netlbl_lsm_secattr secattr;
- u32 nlbl_peer_sid;
+ struct netlbl_lsm_secattr *secattr;
+ struct inet_sock *sk_inet = inet_sk(sk);
+ struct sockaddr_in addr;
if (sksec->nlbl_state != NLBL_REQUIRE)
return;
- netlbl_secattr_init(&secattr);
- if (netlbl_sock_getattr(sk, &secattr) == 0 &&
- secattr.flags != NETLBL_SECATTR_NONE &&
- security_netlbl_secattr_to_sid(&secattr, &nlbl_peer_sid) == 0)
- sksec->peer_sid = nlbl_peer_sid;
- netlbl_secattr_destroy(&secattr);
+ secattr = selinux_netlbl_sock_genattr(sk);
+ if (secattr == NULL)
+ return;
- /* Try to set the NetLabel on the socket to save time later, if we fail
- * here we will pick up the pieces in later calls to
- * selinux_netlbl_inode_permission(). */
- selinux_netlbl_sock_setsid(sk, sksec->sid);
+ rc = netlbl_sock_setattr(sk, secattr);
+ switch (rc) {
+ case 0:
+ sksec->nlbl_state = NLBL_LABELED;
+ break;
+ case -EDESTADDRREQ:
+ /* no PF_INET6 support yet because we don't support any IPv6
+ * labeling protocols */
+ if (family != PF_INET) {
+ sksec->nlbl_state = NLBL_UNSET;
+ return;
+ }
+
+ addr.sin_family = family;
+ addr.sin_addr.s_addr = sk_inet->daddr;
+ if (netlbl_conn_setattr(sk, (struct sockaddr *)&addr,
+ secattr) != 0) {
+ /* we failed to label the connected socket (could be
+ * for a variety of reasons, the actual "why" isn't
+ * important here) so we have to go to our backup plan,
+ * labeling the packets individually in the netfilter
+ * local output hook. this is okay but we need to
+ * adjust the MSS of the connection to take into
+ * account any labeling overhead, since we don't know
+ * the exact overhead at this point we'll use the worst
+ * case value which is 40 bytes for IPv4 */
+ struct inet_connection_sock *sk_conn = inet_csk(sk);
+ sk_conn->icsk_ext_hdr_len += 40 -
+ (sk_inet->opt ? sk_inet->opt->optlen : 0);
+ sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie);
+
+ sksec->nlbl_state = NLBL_REQSKB;
+ } else
+ sksec->nlbl_state = NLBL_CONNLABELED;
+ break;
+ default:
+ /* note that we are failing to label the socket which could be
+ * a bad thing since it means traffic could leave the system
+ * without the desired labeling, however, all is not lost as
+ * we have a check in selinux_netlbl_inode_permission() to
+ * pick up the pieces that we might drop here because we can't
+ * return an error code */
+ break;
+ }
}
/**
@@ -205,13 +361,7 @@
*/
int selinux_netlbl_socket_post_create(struct socket *sock)
{
- struct sock *sk = sock->sk;
- struct sk_security_struct *sksec = sk->sk_security;
-
- if (sksec->nlbl_state != NLBL_REQUIRE)
- return 0;
-
- return selinux_netlbl_sock_setsid(sk, sksec->sid);
+ return selinux_netlbl_sock_setsid(sock->sk);
}
/**
@@ -246,7 +396,7 @@
local_bh_disable();
bh_lock_sock_nested(sk);
if (likely(sksec->nlbl_state == NLBL_REQUIRE))
- rc = selinux_netlbl_sock_setsid(sk, sksec->sid);
+ rc = selinux_netlbl_sock_setsid(sk);
else
rc = 0;
bh_unlock_sock(sk);
@@ -307,7 +457,7 @@
return 0;
if (nlbl_sid != SECINITSID_UNLABELED)
- netlbl_skbuff_err(skb, rc);
+ netlbl_skbuff_err(skb, rc, 0);
return rc;
}
@@ -334,7 +484,8 @@
struct netlbl_lsm_secattr secattr;
if (level == IPPROTO_IP && optname == IP_OPTIONS &&
- sksec->nlbl_state == NLBL_LABELED) {
+ (sksec->nlbl_state == NLBL_LABELED ||
+ sksec->nlbl_state == NLBL_CONNLABELED)) {
netlbl_secattr_init(&secattr);
lock_sock(sk);
rc = netlbl_sock_getattr(sk, &secattr);
@@ -346,3 +497,50 @@
return rc;
}
+
+/**
+ * selinux_netlbl_socket_connect - Label a client-side socket on connect
+ * @sk: the socket to label
+ * @addr: the destination address
+ *
+ * Description:
+ * Attempt to label a connected socket with NetLabel using the given address.
+ * Returns zero values on success, negative values on failure.
+ *
+ */
+int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr)
+{
+ int rc;
+ struct sk_security_struct *sksec = sk->sk_security;
+ struct netlbl_lsm_secattr *secattr;
+
+ if (sksec->nlbl_state != NLBL_REQSKB &&
+ sksec->nlbl_state != NLBL_CONNLABELED)
+ return 0;
+
+ local_bh_disable();
+ bh_lock_sock_nested(sk);
+
+ /* connected sockets are allowed to disconnect when the address family
+ * is set to AF_UNSPEC, if that is what is happening we want to reset
+ * the socket */
+ if (addr->sa_family == AF_UNSPEC) {
+ netlbl_sock_delattr(sk);
+ sksec->nlbl_state = NLBL_REQSKB;
+ rc = 0;
+ goto socket_connect_return;
+ }
+ secattr = selinux_netlbl_sock_genattr(sk);
+ if (secattr == NULL) {
+ rc = -ENOMEM;
+ goto socket_connect_return;
+ }
+ rc = netlbl_conn_setattr(sk, addr, secattr);
+ if (rc == 0)
+ sksec->nlbl_state = NLBL_CONNLABELED;
+
+socket_connect_return:
+ bh_unlock_sock(sk);
+ local_bh_enable();
+ return rc;
+}
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index ab0cc0c..343c8ab 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -2955,7 +2955,7 @@
*/
int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr)
{
- int rc = -ENOENT;
+ int rc;
struct context *ctx;
if (!ss_initialized)
@@ -2963,11 +2963,18 @@
read_lock(&policy_rwlock);
ctx = sidtab_search(&sidtab, sid);
- if (ctx == NULL)
+ if (ctx == NULL) {
+ rc = -ENOENT;
goto netlbl_sid_to_secattr_failure;
+ }
secattr->domain = kstrdup(policydb.p_type_val_to_name[ctx->type - 1],
GFP_ATOMIC);
- secattr->flags |= NETLBL_SECATTR_DOMAIN_CPY;
+ if (secattr->domain == NULL) {
+ rc = -ENOMEM;
+ goto netlbl_sid_to_secattr_failure;
+ }
+ secattr->attr.secid = sid;
+ secattr->flags |= NETLBL_SECATTR_DOMAIN_CPY | NETLBL_SECATTR_SECID;
mls_export_netlbl_lvl(ctx, secattr);
rc = mls_export_netlbl_cat(ctx, secattr);
if (rc != 0)
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 87d7541..6e2dc0b 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -2179,7 +2179,10 @@
* This is the simplist possible security model
* for networking.
*/
- return smk_access(smack, ssp->smk_in, MAY_WRITE);
+ rc = smk_access(smack, ssp->smk_in, MAY_WRITE);
+ if (rc != 0)
+ netlbl_skbuff_err(skb, rc, 0);
+ return rc;
}
/**
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index e7c6424..c21d8c8 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -354,9 +354,11 @@
doip->tags[rc] = CIPSO_V4_TAG_INVALID;
rc = netlbl_cfg_cipsov4_add_map(doip, NULL, &audit_info);
- if (rc != 0)
+ if (rc != 0) {
printk(KERN_WARNING "%s:%d add rc = %d\n",
__func__, __LINE__, rc);
+ kfree(doip);
+ }
}
/**
diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c
index a7b46ec..1b3534d 100644
--- a/sound/core/memalloc.c
+++ b/sound/core/memalloc.c
@@ -33,9 +33,6 @@
#include <linux/moduleparam.h>
#include <linux/mutex.h>
#include <sound/memalloc.h>
-#ifdef CONFIG_SBUS
-#include <asm/sbus.h>
-#endif
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>, Jaroslav Kysela <perex@perex.cz>");
@@ -162,39 +159,6 @@
}
#endif /* CONFIG_HAS_DMA */
-#ifdef CONFIG_SBUS
-
-static void *snd_malloc_sbus_pages(struct device *dev, size_t size,
- dma_addr_t *dma_addr)
-{
- struct sbus_dev *sdev = (struct sbus_dev *)dev;
- int pg;
- void *res;
-
- if (WARN_ON(!dma_addr))
- return NULL;
- pg = get_order(size);
- res = sbus_alloc_consistent(sdev, PAGE_SIZE * (1 << pg), dma_addr);
- if (res != NULL)
- inc_snd_pages(pg);
- return res;
-}
-
-static void snd_free_sbus_pages(struct device *dev, size_t size,
- void *ptr, dma_addr_t dma_addr)
-{
- struct sbus_dev *sdev = (struct sbus_dev *)dev;
- int pg;
-
- if (ptr == NULL)
- return;
- pg = get_order(size);
- dec_snd_pages(pg);
- sbus_free_consistent(sdev, PAGE_SIZE * (1 << pg), ptr, dma_addr);
-}
-
-#endif /* CONFIG_SBUS */
-
/*
*
* ALSA generic memory management
@@ -231,11 +195,6 @@
dmab->area = snd_malloc_pages(size, (unsigned long)device);
dmab->addr = 0;
break;
-#ifdef CONFIG_SBUS
- case SNDRV_DMA_TYPE_SBUS:
- dmab->area = snd_malloc_sbus_pages(device, size, &dmab->addr);
- break;
-#endif
#ifdef CONFIG_HAS_DMA
case SNDRV_DMA_TYPE_DEV:
dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr);
@@ -306,11 +265,6 @@
case SNDRV_DMA_TYPE_CONTINUOUS:
snd_free_pages(dmab->area, dmab->bytes);
break;
-#ifdef CONFIG_SBUS
- case SNDRV_DMA_TYPE_SBUS:
- snd_free_sbus_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
- break;
-#endif
#ifdef CONFIG_HAS_DMA
case SNDRV_DMA_TYPE_DEV:
snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
@@ -419,7 +373,7 @@
long pages = snd_allocated_pages >> (PAGE_SHIFT-12);
struct snd_mem_list *mem;
int devno;
- static char *types[] = { "UNKNOWN", "CONT", "DEV", "DEV-SG", "SBUS" };
+ static char *types[] = { "UNKNOWN", "CONT", "DEV", "DEV-SG" };
mutex_lock(&list_mutex);
seq_printf(seq, "pages : %li bytes (%li pages per %likB)\n",
diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c
index 83e9005..c13a178 100644
--- a/sound/i2c/other/tea575x-tuner.c
+++ b/sound/i2c/other/tea575x-tuner.c
@@ -87,8 +87,7 @@
static int snd_tea575x_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long data)
{
- struct video_device *dev = video_devdata(file);
- struct snd_tea575x *tea = video_get_drvdata(dev);
+ struct snd_tea575x *tea = video_drvdata(file);
void __user *arg = (void __user *)data;
switch(cmd) {
@@ -175,6 +174,21 @@
{
}
+static int snd_tea575x_exclusive_open(struct inode *inode, struct file *file)
+{
+ struct snd_tea575x *tea = video_drvdata(file);
+
+ return test_and_set_bit(0, &tea->in_use) ? -EBUSY : 0;
+}
+
+static int snd_tea575x_exclusive_release(struct inode *inode, struct file *file)
+{
+ struct snd_tea575x *tea = video_drvdata(file);
+
+ clear_bit(0, &tea->in_use);
+ return 0;
+}
+
/*
* initialize all the tea575x chips
*/
@@ -193,9 +207,10 @@
tea->vd.release = snd_tea575x_release;
video_set_drvdata(&tea->vd, tea);
tea->vd.fops = &tea->fops;
+ tea->in_use = 0;
tea->fops.owner = tea->card->module;
- tea->fops.open = video_exclusive_open;
- tea->fops.release = video_exclusive_release;
+ tea->fops.open = snd_tea575x_exclusive_open;
+ tea->fops.release = snd_tea575x_exclusive_release;
tea->fops.ioctl = snd_tea575x_ioctl;
if (video_register_device(&tea->vd, VFL_TYPE_RADIO, tea->dev_nr - 1) < 0) {
snd_printk(KERN_ERR "unable to register tea575x tuner\n");
diff --git a/sound/oss/ac97_codec.c b/sound/oss/ac97_codec.c
index b63839e..456a1b4 100644
--- a/sound/oss/ac97_codec.c
+++ b/sound/oss/ac97_codec.c
@@ -30,7 +30,7 @@
**************************************************************************
*
* History
- * May 02, 2003 Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ * May 02, 2003 Liam Girdwood <lrg@slimlogic.co.uk>
* Removed non existant WM9700
* Added support for WM9705, WM9708, WM9709, WM9710, WM9711
* WM9712 and WM9717
diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
index 6ce3cbe..6e831af 100644
--- a/sound/pci/ac97/ac97_patch.c
+++ b/sound/pci/ac97/ac97_patch.c
@@ -476,7 +476,7 @@
}
/*
- * May 2, 2003 Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ * May 2, 2003 Liam Girdwood <lrg@slimlogic.co.uk>
* removed broken wolfson00 patch.
* added support for WM9705,WM9708,WM9709,WM9710,WM9711,WM9712 and WM9717.
*/
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index c461baa..c590655 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -322,8 +322,8 @@
0x1a, 0x1b
};
-static hda_nid_t stac92hd71bxx_dmux_nids[1] = {
- 0x1c,
+static hda_nid_t stac92hd71bxx_dmux_nids[2] = {
+ 0x1c, 0x1d,
};
static hda_nid_t stac92hd71bxx_smux_nids[2] = {
@@ -861,20 +861,18 @@
{ 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
/* connect headphone jack to dac1 */
{ 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
- { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */
/* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */
{ 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{ 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{ 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
};
-#define HD_DISABLE_PORTF 3
+#define HD_DISABLE_PORTF 2
static struct hda_verb stac92hd71bxx_analog_core_init[] = {
/* start of config #1 */
/* connect port 0f to audio mixer */
{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2},
- { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */
/* unmute right and left channels for node 0x0f */
{ 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
/* start of config #2 */
@@ -883,10 +881,6 @@
{ 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
/* connect headphone jack to dac1 */
{ 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
- /* connect port 0d to audio mixer */
- { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x2},
- /* unmute dac0 input in audio mixer */
- { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0x701f},
/* unmute right and left channels for nodes 0x0a, 0xd */
{ 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{ 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
@@ -1107,6 +1101,7 @@
static struct snd_kcontrol_new stac92hd71bxx_analog_mixer[] = {
STAC_INPUT_SOURCE(2),
+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2),
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT),
@@ -1119,8 +1114,17 @@
HDA_CODEC_MUTE("PC Beep Switch", 0x17, 0x2, HDA_INPUT),
*/
- HDA_CODEC_MUTE("Analog Loopback 1", 0x17, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("Analog Loopback 2", 0x17, 0x4, HDA_INPUT),
+ HDA_CODEC_MUTE("Import0 Mux Capture Switch", 0x17, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Import0 Mux Capture Volume", 0x17, 0x0, HDA_INPUT),
+
+ HDA_CODEC_MUTE("Import1 Mux Capture Switch", 0x17, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("Import1 Mux Capture Volume", 0x17, 0x1, HDA_INPUT),
+
+ HDA_CODEC_MUTE("DAC0 Capture Switch", 0x17, 0x3, HDA_INPUT),
+ HDA_CODEC_VOLUME("DAC0 Capture Volume", 0x17, 0x3, HDA_INPUT),
+
+ HDA_CODEC_MUTE("DAC1 Capture Switch", 0x17, 0x4, HDA_INPUT),
+ HDA_CODEC_VOLUME("DAC1 Capture Volume", 0x17, 0x4, HDA_INPUT),
{ } /* end */
};
@@ -1649,7 +1653,7 @@
static unsigned int ref92hd71bxx_pin_configs[11] = {
0x02214030, 0x02a19040, 0x01a19020, 0x01014010,
- 0x0181302e, 0x01114010, 0x01019020, 0x90a000f0,
+ 0x0181302e, 0x01014010, 0x01019020, 0x90a000f0,
0x90a000f0, 0x01452050, 0x01452050,
};
@@ -3000,7 +3004,7 @@
/* labels for amp mux outputs */
static const char *stac92xx_amp_labels[3] = {
- "Front Microphone", "Microphone", "Line In"
+ "Front Microphone", "Microphone", "Line In",
};
/* create amp out controls mux on capable codecs */
@@ -4327,6 +4331,16 @@
#endif
};
+static struct hda_input_mux stac92hd71bxx_dmux = {
+ .num_items = 4,
+ .items = {
+ { "Analog Inputs", 0x00 },
+ { "Mixer", 0x01 },
+ { "Digital Mic 1", 0x02 },
+ { "Digital Mic 2", 0x03 },
+ }
+};
+
static int patch_stac92hd71bxx(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
@@ -4341,6 +4355,8 @@
spec->num_pins = ARRAY_SIZE(stac92hd71bxx_pin_nids);
spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids);
spec->pin_nids = stac92hd71bxx_pin_nids;
+ memcpy(&spec->private_dimux, &stac92hd71bxx_dmux,
+ sizeof(stac92hd71bxx_dmux));
spec->board_config = snd_hda_check_board_config(codec,
STAC_92HD71BXX_MODELS,
stac92hd71bxx_models,
@@ -4392,6 +4408,7 @@
/* no output amps */
spec->num_pwrs = 0;
spec->mixer = stac92hd71bxx_analog_mixer;
+ spec->dinput_mux = &spec->private_dimux;
/* disable VSW */
spec->init = &stac92hd71bxx_analog_core_init[HD_DISABLE_PORTF];
@@ -4409,12 +4426,13 @@
spec->num_pwrs = 0;
/* fallthru */
default:
+ spec->dinput_mux = &spec->private_dimux;
spec->mixer = stac92hd71bxx_analog_mixer;
spec->init = stac92hd71bxx_analog_core_init;
codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
}
- spec->aloopback_mask = 0x20;
+ spec->aloopback_mask = 0x50;
spec->aloopback_shift = 0;
if (spec->board_config > STAC_92HD71BXX_REF) {
@@ -4456,6 +4474,10 @@
spec->multiout.num_dacs = 1;
spec->multiout.hp_nid = 0x11;
spec->multiout.dac_nids = stac92hd71bxx_dac_nids;
+ if (spec->dinput_mux)
+ spec->private_dimux.num_items +=
+ spec->num_dmics -
+ (ARRAY_SIZE(stac92hd71bxx_dmic_nids) - 1);
err = stac92xx_parse_auto_config(codec, 0x21, 0x23);
if (!err) {
diff --git a/sound/soc/at91/Kconfig b/sound/soc/at91/Kconfig
index 9051865..85a8832 100644
--- a/sound/soc/at91/Kconfig
+++ b/sound/soc/at91/Kconfig
@@ -8,20 +8,3 @@
config SND_AT91_SOC_SSC
tristate
-
-config SND_AT91_SOC_ETI_B1_WM8731
- tristate "SoC Audio support for WM8731-based Endrelia ETI-B1 boards"
- depends on SND_AT91_SOC && (MACH_ETI_B1 || MACH_ETI_C1)
- select SND_AT91_SOC_SSC
- select SND_SOC_WM8731
- help
- Say Y if you want to add support for SoC audio on WM8731-based
- Endrelia Technologies Inc ETI-B1 or ETI-C1 boards.
-
-config SND_AT91_SOC_ETI_SLAVE
- bool "Run codec in slave Mode on Endrelia boards"
- depends on SND_AT91_SOC_ETI_B1_WM8731
- default n
- help
- Say Y if you want to run with the AT91 SSC generating the BCLK
- and LRC signals on Endrelia boards.
diff --git a/sound/soc/at91/Makefile b/sound/soc/at91/Makefile
index f23da17..b817f11 100644
--- a/sound/soc/at91/Makefile
+++ b/sound/soc/at91/Makefile
@@ -4,8 +4,3 @@
obj-$(CONFIG_SND_AT91_SOC) += snd-soc-at91.o
obj-$(CONFIG_SND_AT91_SOC_SSC) += snd-soc-at91-ssc.o
-
-# AT91 Machine Support
-snd-soc-eti-b1-wm8731-objs := eti_b1_wm8731.o
-
-obj-$(CONFIG_SND_AT91_SOC_ETI_B1_WM8731) += snd-soc-eti-b1-wm8731.o
diff --git a/sound/soc/at91/at91-ssc.c b/sound/soc/at91/at91-ssc.c
index a5b1a79..1b61cc4 100644
--- a/sound/soc/at91/at91-ssc.c
+++ b/sound/soc/at91/at91-ssc.c
@@ -5,7 +5,7 @@
* Endrelia Technologies Inc.
*
* Based on pxa2xx Platform drivers by
- * Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ * Liam Girdwood <lrg@slimlogic.co.uk>
*
* 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
diff --git a/sound/soc/at91/eti_b1_wm8731.c b/sound/soc/at91/eti_b1_wm8731.c
deleted file mode 100644
index 684781e..0000000
--- a/sound/soc/at91/eti_b1_wm8731.c
+++ /dev/null
@@ -1,349 +0,0 @@
-/*
- * eti_b1_wm8731 -- SoC audio for AT91RM9200-based Endrelia ETI_B1 board.
- *
- * Author: Frank Mandarino <fmandarino@endrelia.com>
- * Endrelia Technologies Inc.
- * Created: Mar 29, 2006
- *
- * Based on corgi.c by:
- *
- * Copyright 2005 Wolfson Microelectronics PLC.
- * Copyright 2005 Openedhand Ltd.
- *
- * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
- * Richard Purdie <richard@openedhand.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.
- *
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/clk.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/soc-dapm.h>
-
-#include <mach/hardware.h>
-#include <mach/gpio.h>
-
-#include "../codecs/wm8731.h"
-#include "at91-pcm.h"
-#include "at91-ssc.h"
-
-#if 0
-#define DBG(x...) printk(KERN_INFO "eti_b1_wm8731: " x)
-#else
-#define DBG(x...)
-#endif
-
-static struct clk *pck1_clk;
-static struct clk *pllb_clk;
-
-
-static int eti_b1_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
- int ret;
-
- /* cpu clock is the AT91 master clock sent to the SSC */
- ret = snd_soc_dai_set_sysclk(cpu_dai, AT91_SYSCLK_MCK,
- 60000000, SND_SOC_CLOCK_IN);
- if (ret < 0)
- return ret;
-
- /* codec system clock is supplied by PCK1, set to 12MHz */
- ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK,
- 12000000, SND_SOC_CLOCK_IN);
- if (ret < 0)
- return ret;
-
- /* Start PCK1 clock. */
- clk_enable(pck1_clk);
- DBG("pck1 started\n");
-
- return 0;
-}
-
-static void eti_b1_shutdown(struct snd_pcm_substream *substream)
-{
- /* Stop PCK1 clock. */
- clk_disable(pck1_clk);
- DBG("pck1 stopped\n");
-}
-
-static int eti_b1_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
- int ret;
-
-#ifdef CONFIG_SND_AT91_SOC_ETI_SLAVE
- unsigned int rate;
- int cmr_div, period;
-
- /* set codec DAI configuration */
- ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
- if (ret < 0)
- return ret;
-
- /* set cpu DAI configuration */
- ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
- if (ret < 0)
- return ret;
-
- /*
- * The SSC clock dividers depend on the sample rate. The CMR.DIV
- * field divides the system master clock MCK to drive the SSC TK
- * signal which provides the codec BCLK. The TCMR.PERIOD and
- * RCMR.PERIOD fields further divide the BCLK signal to drive
- * the SSC TF and RF signals which provide the codec DACLRC and
- * ADCLRC clocks.
- *
- * The dividers were determined through trial and error, where a
- * CMR.DIV value is chosen such that the resulting BCLK value is
- * divisible, or almost divisible, by (2 * sample rate), and then
- * the TCMR.PERIOD or RCMR.PERIOD is BCLK / (2 * sample rate) - 1.
- */
- rate = params_rate(params);
-
- switch (rate) {
- case 8000:
- cmr_div = 25; /* BCLK = 60MHz/(2*25) = 1.2MHz */
- period = 74; /* LRC = BCLK/(2*(74+1)) = 8000Hz */
- break;
- case 32000:
- cmr_div = 7; /* BCLK = 60MHz/(2*7) ~= 4.28571428MHz */
- period = 66; /* LRC = BCLK/(2*(66+1)) = 31982.942Hz */
- break;
- case 48000:
- cmr_div = 13; /* BCLK = 60MHz/(2*13) ~= 2.3076923MHz */
- period = 23; /* LRC = BCLK/(2*(23+1)) = 48076.923Hz */
- break;
- default:
- printk(KERN_WARNING "unsupported rate %d on ETI-B1 board\n", rate);
- return -EINVAL;
- }
-
- /* set the MCK divider for BCLK */
- ret = snd_soc_dai_set_clkdiv(cpu_dai, AT91SSC_CMR_DIV, cmr_div);
- if (ret < 0)
- return ret;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- /* set the BCLK divider for DACLRC */
- ret = snd_soc_dai_set_clkdiv(cpu_dai,
- AT91SSC_TCMR_PERIOD, period);
- } else {
- /* set the BCLK divider for ADCLRC */
- ret = snd_soc_dai_set_clkdiv(cpu_dai,
- AT91SSC_RCMR_PERIOD, period);
- }
- if (ret < 0)
- return ret;
-
-#else /* CONFIG_SND_AT91_SOC_ETI_SLAVE */
- /*
- * Codec in Master Mode.
- */
-
- /* set codec DAI configuration */
- ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
- if (ret < 0)
- return ret;
-
- /* set cpu DAI configuration */
- ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
- if (ret < 0)
- return ret;
-
-#endif /* CONFIG_SND_AT91_SOC_ETI_SLAVE */
-
- return 0;
-}
-
-static struct snd_soc_ops eti_b1_ops = {
- .startup = eti_b1_startup,
- .hw_params = eti_b1_hw_params,
- .shutdown = eti_b1_shutdown,
-};
-
-
-static const struct snd_soc_dapm_widget eti_b1_dapm_widgets[] = {
- SND_SOC_DAPM_MIC("Int Mic", NULL),
- SND_SOC_DAPM_SPK("Ext Spk", NULL),
-};
-
-static const struct snd_soc_dapm_route intercon[] = {
-
- /* speaker connected to LHPOUT */
- {"Ext Spk", NULL, "LHPOUT"},
-
- /* mic is connected to Mic Jack, with WM8731 Mic Bias */
- {"MICIN", NULL, "Mic Bias"},
- {"Mic Bias", NULL, "Int Mic"},
-};
-
-/*
- * Logic for a wm8731 as connected on a Endrelia ETI-B1 board.
- */
-static int eti_b1_wm8731_init(struct snd_soc_codec *codec)
-{
- DBG("eti_b1_wm8731_init() called\n");
-
- /* Add specific widgets */
- snd_soc_dapm_new_controls(codec, eti_b1_dapm_widgets,
- ARRAY_SIZE(eti_b1_dapm_widgets));
-
- /* Set up specific audio path interconnects */
- snd_soc_dapm_add_route(codec, intercon, ARRAY_SIZE(intercon));
-
- /* not connected */
- snd_soc_dapm_disable_pin(codec, "RLINEIN");
- snd_soc_dapm_disable_pin(codec, "LLINEIN");
-
- /* always connected */
- snd_soc_dapm_enable_pin(codec, "Int Mic");
- snd_soc_dapm_enable_pin(codec, "Ext Spk");
-
- snd_soc_dapm_sync(codec);
-
- return 0;
-}
-
-static struct snd_soc_dai_link eti_b1_dai = {
- .name = "WM8731",
- .stream_name = "WM8731 PCM",
- .cpu_dai = &at91_ssc_dai[1],
- .codec_dai = &wm8731_dai,
- .init = eti_b1_wm8731_init,
- .ops = &eti_b1_ops,
-};
-
-static struct snd_soc_machine snd_soc_machine_eti_b1 = {
- .name = "ETI_B1_WM8731",
- .dai_link = &eti_b1_dai,
- .num_links = 1,
-};
-
-static struct wm8731_setup_data eti_b1_wm8731_setup = {
- .i2c_bus = 0,
- .i2c_address = 0x1a,
-};
-
-static struct snd_soc_device eti_b1_snd_devdata = {
- .machine = &snd_soc_machine_eti_b1,
- .platform = &at91_soc_platform,
- .codec_dev = &soc_codec_dev_wm8731,
- .codec_data = &eti_b1_wm8731_setup,
-};
-
-static struct platform_device *eti_b1_snd_device;
-
-static int __init eti_b1_init(void)
-{
- int ret;
- struct at91_ssc_periph *ssc = eti_b1_dai.cpu_dai->private_data;
-
- if (!request_mem_region(AT91RM9200_BASE_SSC1, SZ_16K, "soc-audio")) {
- DBG("SSC1 memory region is busy\n");
- return -EBUSY;
- }
-
- ssc->base = ioremap(AT91RM9200_BASE_SSC1, SZ_16K);
- if (!ssc->base) {
- DBG("SSC1 memory ioremap failed\n");
- ret = -ENOMEM;
- goto fail_release_mem;
- }
-
- ssc->pid = AT91RM9200_ID_SSC1;
-
- eti_b1_snd_device = platform_device_alloc("soc-audio", -1);
- if (!eti_b1_snd_device) {
- DBG("platform device allocation failed\n");
- ret = -ENOMEM;
- goto fail_io_unmap;
- }
-
- platform_set_drvdata(eti_b1_snd_device, &eti_b1_snd_devdata);
- eti_b1_snd_devdata.dev = &eti_b1_snd_device->dev;
-
- ret = platform_device_add(eti_b1_snd_device);
- if (ret) {
- DBG("platform device add failed\n");
- platform_device_put(eti_b1_snd_device);
- goto fail_io_unmap;
- }
-
- at91_set_A_periph(AT91_PIN_PB6, 0); /* TF1 */
- at91_set_A_periph(AT91_PIN_PB7, 0); /* TK1 */
- at91_set_A_periph(AT91_PIN_PB8, 0); /* TD1 */
- at91_set_A_periph(AT91_PIN_PB9, 0); /* RD1 */
-/* at91_set_A_periph(AT91_PIN_PB10, 0);*/ /* RK1 */
- at91_set_A_periph(AT91_PIN_PB11, 0); /* RF1 */
-
- /*
- * Set PCK1 parent to PLLB and its rate to 12 Mhz.
- */
- pllb_clk = clk_get(NULL, "pllb");
- pck1_clk = clk_get(NULL, "pck1");
-
- clk_set_parent(pck1_clk, pllb_clk);
- clk_set_rate(pck1_clk, 12000000);
-
- DBG("MCLK rate %luHz\n", clk_get_rate(pck1_clk));
-
- /* assign the GPIO pin to PCK1 */
- at91_set_B_periph(AT91_PIN_PA24, 0);
-
-#ifdef CONFIG_SND_AT91_SOC_ETI_SLAVE
- printk(KERN_INFO "eti_b1_wm8731: Codec in Slave Mode\n");
-#else
- printk(KERN_INFO "eti_b1_wm8731: Codec in Master Mode\n");
-#endif
- return ret;
-
-fail_io_unmap:
- iounmap(ssc->base);
-fail_release_mem:
- release_mem_region(AT91RM9200_BASE_SSC1, SZ_16K);
- return ret;
-}
-
-static void __exit eti_b1_exit(void)
-{
- struct at91_ssc_periph *ssc = eti_b1_dai.cpu_dai->private_data;
-
- clk_put(pck1_clk);
- clk_put(pllb_clk);
-
- platform_device_unregister(eti_b1_snd_device);
-
- iounmap(ssc->base);
- release_mem_region(AT91RM9200_BASE_SSC1, SZ_16K);
-}
-
-module_init(eti_b1_init);
-module_exit(eti_b1_exit);
-
-/* Module information */
-MODULE_AUTHOR("Frank Mandarino <fmandarino@endrelia.com>");
-MODULE_DESCRIPTION("ALSA SoC ETI-B1-WM8731");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig
index f98331d..dc00620 100644
--- a/sound/soc/blackfin/Kconfig
+++ b/sound/soc/blackfin/Kconfig
@@ -17,6 +17,22 @@
help
Say Y if you want to add support for SoC audio on BF527-EZKIT.
+config SND_BF5XX_SOC_AD73311
+ tristate "SoC AD73311 Audio support for Blackfin"
+ depends on SND_BF5XX_I2S
+ select SND_BF5XX_SOC_I2S
+ select SND_SOC_AD73311
+ help
+ Say Y if you want to add support for AD73311 codec on Blackfin.
+
+config SND_BFIN_AD73311_SE
+ int "PF pin for AD73311L Chip Select"
+ depends on SND_BF5XX_SOC_AD73311
+ default 4
+ help
+ Enter the GPIO used to control AD73311's SE pin. Acceptable
+ values are 0 to 7
+
config SND_BF5XX_AC97
tristate "SoC AC97 Audio for the ADI BF5xx chip"
depends on BLACKFIN && SND_SOC
diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile
index 9ea8bd9..97bb37a 100644
--- a/sound/soc/blackfin/Makefile
+++ b/sound/soc/blackfin/Makefile
@@ -14,7 +14,8 @@
# Blackfin Machine Support
snd-ad1980-objs := bf5xx-ad1980.o
snd-ssm2602-objs := bf5xx-ssm2602.o
-
+snd-ad73311-objs := bf5xx-ad73311.o
obj-$(CONFIG_SND_BF5XX_SOC_AD1980) += snd-ad1980.o
obj-$(CONFIG_SND_BF5XX_SOC_SSM2602) += snd-ssm2602.o
+obj-$(CONFIG_SND_BF5XX_SOC_AD73311) += snd-ad73311.o
diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c
index 51f4907..25e50d2 100644
--- a/sound/soc/blackfin/bf5xx-ac97-pcm.c
+++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c
@@ -56,6 +56,7 @@
sport->tx_pos += runtime->period_size;
if (sport->tx_pos >= runtime->buffer_size)
sport->tx_pos %= runtime->buffer_size;
+ sport->tx_delay_pos = sport->tx_pos;
} else {
bf5xx_ac97_to_pcm(
(struct ac97_frame *)sport->rx_dma_buf + sport->rx_pos,
@@ -72,7 +73,15 @@
struct snd_pcm_substream *pcm = data;
#if defined(CONFIG_SND_MMAP_SUPPORT)
struct snd_pcm_runtime *runtime = pcm->runtime;
+ struct sport_device *sport = runtime->private_data;
bf5xx_mmap_copy(pcm, runtime->period_size);
+ if (pcm->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (sport->once == 0) {
+ snd_pcm_period_elapsed(pcm);
+ bf5xx_mmap_copy(pcm, runtime->period_size);
+ sport->once = 1;
+ }
+ }
#endif
snd_pcm_period_elapsed(pcm);
}
@@ -114,6 +123,10 @@
static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream)
{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ memset(runtime->dma_area, 0, runtime->buffer_size);
snd_pcm_lib_free_pages(substream);
return 0;
}
@@ -127,16 +140,11 @@
* SPORT working in TMD mode(include AC97).
*/
#if defined(CONFIG_SND_MMAP_SUPPORT)
- size_t size = bf5xx_pcm_hardware.buffer_bytes_max
- * sizeof(struct ac97_frame) / 4;
- /*clean up intermediate buffer*/
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- memset(sport->tx_dma_buf, 0, size);
sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
sport_config_tx_dma(sport, sport->tx_dma_buf, runtime->periods,
runtime->period_size * sizeof(struct ac97_frame));
} else {
- memset(sport->rx_dma_buf, 0, size);
sport_set_rx_callback(sport, bf5xx_dma_irq, substream);
sport_config_rx_dma(sport, sport->rx_dma_buf, runtime->periods,
runtime->period_size * sizeof(struct ac97_frame));
@@ -164,8 +172,12 @@
pr_debug("%s enter\n", __func__);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ bf5xx_mmap_copy(substream, runtime->period_size);
+ snd_pcm_period_elapsed(substream);
+ sport->tx_delay_pos = 0;
sport_tx_start(sport);
+ }
else
sport_rx_start(sport);
break;
@@ -198,7 +210,7 @@
#if defined(CONFIG_SND_MMAP_SUPPORT)
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- curr = sport->tx_pos;
+ curr = sport->tx_delay_pos;
else
curr = sport->rx_pos;
#else
@@ -237,6 +249,21 @@
return ret;
}
+static int bf5xx_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct sport_device *sport = runtime->private_data;
+
+ pr_debug("%s enter\n", __func__);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ sport->once = 0;
+ memset(sport->tx_dma_buf, 0, runtime->buffer_size * sizeof(struct ac97_frame));
+ } else
+ memset(sport->rx_dma_buf, 0, runtime->buffer_size * sizeof(struct ac97_frame));
+
+ return 0;
+}
+
#ifdef CONFIG_SND_MMAP_SUPPORT
static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
@@ -272,6 +299,7 @@
struct snd_pcm_ops bf5xx_pcm_ac97_ops = {
.open = bf5xx_pcm_open,
+ .close = bf5xx_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = bf5xx_pcm_hw_params,
.hw_free = bf5xx_pcm_hw_free,
diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c
index c782e31..5e5aafb 100644
--- a/sound/soc/blackfin/bf5xx-ac97.c
+++ b/sound/soc/blackfin/bf5xx-ac97.c
@@ -129,7 +129,6 @@
struct ac97_frame *nextwrite;
sport_incfrag(sport, &nextfrag, 1);
- sport_incfrag(sport, &nextfrag, 1);
nextwrite = (struct ac97_frame *)(sport->tx_buf + \
nextfrag * sport->tx_fragsize);
diff --git a/sound/soc/blackfin/bf5xx-ad73311.c b/sound/soc/blackfin/bf5xx-ad73311.c
new file mode 100644
index 0000000..622c9b9
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-ad73311.c
@@ -0,0 +1,240 @@
+/*
+ * File: sound/soc/blackfin/bf5xx-ad73311.c
+ * Author: Cliff Cai <Cliff.Cai@analog.com>
+ *
+ * Created: Thur Sep 25 2008
+ * Description: Board driver for ad73311 sound chip
+ *
+ * Modified:
+ * Copyright 2008 Analog Devices Inc.
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm_params.h>
+
+#include <asm/blackfin.h>
+#include <asm/cacheflush.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <asm/portmux.h>
+
+#include "../codecs/ad73311.h"
+#include "bf5xx-sport.h"
+#include "bf5xx-i2s-pcm.h"
+#include "bf5xx-i2s.h"
+
+#if CONFIG_SND_BF5XX_SPORT_NUM == 0
+#define bfin_write_SPORT_TCR1 bfin_write_SPORT0_TCR1
+#define bfin_read_SPORT_TCR1 bfin_read_SPORT0_TCR1
+#define bfin_write_SPORT_TCR2 bfin_write_SPORT0_TCR2
+#define bfin_write_SPORT_TX16 bfin_write_SPORT0_TX16
+#define bfin_read_SPORT_STAT bfin_read_SPORT0_STAT
+#else
+#define bfin_write_SPORT_TCR1 bfin_write_SPORT1_TCR1
+#define bfin_read_SPORT_TCR1 bfin_read_SPORT1_TCR1
+#define bfin_write_SPORT_TCR2 bfin_write_SPORT1_TCR2
+#define bfin_write_SPORT_TX16 bfin_write_SPORT1_TX16
+#define bfin_read_SPORT_STAT bfin_read_SPORT1_STAT
+#endif
+
+#define GPIO_SE CONFIG_SND_BFIN_AD73311_SE
+
+static struct snd_soc_machine bf5xx_ad73311;
+
+static int snd_ad73311_startup(void)
+{
+ pr_debug("%s enter\n", __func__);
+
+ /* Pull up SE pin on AD73311L */
+ gpio_set_value(GPIO_SE, 1);
+ return 0;
+}
+
+static int snd_ad73311_configure(void)
+{
+ unsigned short ctrl_regs[6];
+ unsigned short status = 0;
+ int count = 0;
+
+ /* DMCLK = MCLK = 16.384 MHz
+ * SCLK = DMCLK/8 = 2.048 MHz
+ * Sample Rate = DMCLK/2048 = 8 KHz
+ */
+ ctrl_regs[0] = AD_CONTROL | AD_WRITE | CTRL_REG_B | REGB_MCDIV(0) | \
+ REGB_SCDIV(0) | REGB_DIRATE(0);
+ ctrl_regs[1] = AD_CONTROL | AD_WRITE | CTRL_REG_C | REGC_PUDEV | \
+ REGC_PUADC | REGC_PUDAC | REGC_PUREF | REGC_REFUSE ;
+ ctrl_regs[2] = AD_CONTROL | AD_WRITE | CTRL_REG_D | REGD_OGS(2) | \
+ REGD_IGS(2);
+ ctrl_regs[3] = AD_CONTROL | AD_WRITE | CTRL_REG_E | REGE_DA(0x1f);
+ ctrl_regs[4] = AD_CONTROL | AD_WRITE | CTRL_REG_F | REGF_SEEN ;
+ ctrl_regs[5] = AD_CONTROL | AD_WRITE | CTRL_REG_A | REGA_MODE_DATA;
+
+ local_irq_disable();
+ snd_ad73311_startup();
+ udelay(1);
+
+ bfin_write_SPORT_TCR1(TFSR);
+ bfin_write_SPORT_TCR2(0xF);
+ SSYNC();
+
+ /* SPORT Tx Register is a 8 x 16 FIFO, all the data can be put to
+ * FIFO before enable SPORT to transfer the data
+ */
+ for (count = 0; count < 6; count++)
+ bfin_write_SPORT_TX16(ctrl_regs[count]);
+ SSYNC();
+ bfin_write_SPORT_TCR1(bfin_read_SPORT_TCR1() | TSPEN);
+ SSYNC();
+
+ /* When TUVF is set, the data is already send out */
+ while (!(status & TUVF) && count++ < 10000) {
+ udelay(1);
+ status = bfin_read_SPORT_STAT();
+ SSYNC();
+ }
+ bfin_write_SPORT_TCR1(bfin_read_SPORT_TCR1() & ~TSPEN);
+ SSYNC();
+ local_irq_enable();
+
+ if (count == 10000) {
+ printk(KERN_ERR "ad73311: failed to configure codec\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int bf5xx_probe(struct platform_device *pdev)
+{
+ int err;
+ if (gpio_request(GPIO_SE, "AD73311_SE")) {
+ printk(KERN_ERR "%s: Failed ro request GPIO_%d\n", __func__, GPIO_SE);
+ return -EBUSY;
+ }
+
+ gpio_direction_output(GPIO_SE, 0);
+
+ err = snd_ad73311_configure();
+ if (err < 0)
+ return -EFAULT;
+
+ return 0;
+}
+
+static int bf5xx_ad73311_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+ pr_debug("%s enter\n", __func__);
+ cpu_dai->private_data = sport_handle;
+ return 0;
+}
+
+static int bf5xx_ad73311_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ int ret = 0;
+
+ pr_debug("%s rate %d format %x\n", __func__, params_rate(params),
+ params_format(params));
+
+ /* set cpu DAI configuration */
+ ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+
+static struct snd_soc_ops bf5xx_ad73311_ops = {
+ .startup = bf5xx_ad73311_startup,
+ .hw_params = bf5xx_ad73311_hw_params,
+};
+
+static struct snd_soc_dai_link bf5xx_ad73311_dai = {
+ .name = "ad73311",
+ .stream_name = "AD73311",
+ .cpu_dai = &bf5xx_i2s_dai,
+ .codec_dai = &ad73311_dai,
+ .ops = &bf5xx_ad73311_ops,
+};
+
+static struct snd_soc_machine bf5xx_ad73311 = {
+ .name = "bf5xx_ad73311",
+ .probe = bf5xx_probe,
+ .dai_link = &bf5xx_ad73311_dai,
+ .num_links = 1,
+};
+
+static struct snd_soc_device bf5xx_ad73311_snd_devdata = {
+ .machine = &bf5xx_ad73311,
+ .platform = &bf5xx_i2s_soc_platform,
+ .codec_dev = &soc_codec_dev_ad73311,
+};
+
+static struct platform_device *bf52x_ad73311_snd_device;
+
+static int __init bf5xx_ad73311_init(void)
+{
+ int ret;
+
+ pr_debug("%s enter\n", __func__);
+ bf52x_ad73311_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!bf52x_ad73311_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(bf52x_ad73311_snd_device, &bf5xx_ad73311_snd_devdata);
+ bf5xx_ad73311_snd_devdata.dev = &bf52x_ad73311_snd_device->dev;
+ ret = platform_device_add(bf52x_ad73311_snd_device);
+
+ if (ret)
+ platform_device_put(bf52x_ad73311_snd_device);
+
+ return ret;
+}
+
+static void __exit bf5xx_ad73311_exit(void)
+{
+ pr_debug("%s enter\n", __func__);
+ platform_device_unregister(bf52x_ad73311_snd_device);
+}
+
+module_init(bf5xx_ad73311_init);
+module_exit(bf5xx_ad73311_exit);
+
+/* Module information */
+MODULE_AUTHOR("Cliff Cai");
+MODULE_DESCRIPTION("ALSA SoC AD73311 Blackfin");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/blackfin/bf5xx-i2s.c b/sound/soc/blackfin/bf5xx-i2s.c
index 43a4092..827587f 100644
--- a/sound/soc/blackfin/bf5xx-i2s.c
+++ b/sound/soc/blackfin/bf5xx-i2s.c
@@ -70,6 +70,13 @@
}
};
+static u16 sport_req[][7] = {
+ { P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS,
+ P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0},
+ { P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS,
+ P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0},
+};
+
static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
@@ -78,6 +85,14 @@
/* interface format:support I2S,slave mode */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
+ bf5xx_i2s.tcr1 |= TFSR | TCKFE;
+ bf5xx_i2s.rcr1 |= RFSR | RCKFE;
+ bf5xx_i2s.tcr2 |= TSFSE;
+ bf5xx_i2s.rcr2 |= RSFSE;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ bf5xx_i2s.tcr1 |= TFSR;
+ bf5xx_i2s.rcr1 |= RFSR;
break;
case SND_SOC_DAIFMT_LEFT_J:
ret = -EINVAL;
@@ -127,14 +142,17 @@
case SNDRV_PCM_FORMAT_S16_LE:
bf5xx_i2s.tcr2 |= 15;
bf5xx_i2s.rcr2 |= 15;
+ sport_handle->wdsize = 2;
break;
case SNDRV_PCM_FORMAT_S24_LE:
bf5xx_i2s.tcr2 |= 23;
bf5xx_i2s.rcr2 |= 23;
+ sport_handle->wdsize = 3;
break;
case SNDRV_PCM_FORMAT_S32_LE:
bf5xx_i2s.tcr2 |= 31;
bf5xx_i2s.rcr2 |= 31;
+ sport_handle->wdsize = 4;
break;
}
@@ -145,17 +163,17 @@
* need to configure both of them at the time when the first
* stream is opened.
*
- * CPU DAI format:I2S, slave mode.
+ * CPU DAI:slave mode.
*/
- ret = sport_config_rx(sport_handle, RFSR | RCKFE,
- RSFSE|bf5xx_i2s.rcr2, 0, 0);
+ ret = sport_config_rx(sport_handle, bf5xx_i2s.rcr1,
+ bf5xx_i2s.rcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
}
- ret = sport_config_tx(sport_handle, TFSR | TCKFE,
- TSFSE|bf5xx_i2s.tcr2, 0, 0);
+ ret = sport_config_tx(sport_handle, bf5xx_i2s.tcr1,
+ bf5xx_i2s.tcr2, 0, 0);
if (ret) {
pr_err("SPORT is busy!\n");
return -EBUSY;
@@ -174,13 +192,6 @@
static int bf5xx_i2s_probe(struct platform_device *pdev,
struct snd_soc_dai *dai)
{
- u16 sport_req[][7] = {
- { P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS,
- P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0},
- { P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS,
- P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0},
- };
-
pr_debug("%s enter\n", __func__);
if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
pr_err("Requesting Peripherals failed\n");
@@ -198,6 +209,13 @@
return 0;
}
+static void bf5xx_i2s_remove(struct platform_device *pdev,
+ struct snd_soc_dai *dai)
+{
+ pr_debug("%s enter\n", __func__);
+ peripheral_free_list(&sport_req[sport_num][0]);
+}
+
#ifdef CONFIG_PM
static int bf5xx_i2s_suspend(struct platform_device *dev,
struct snd_soc_dai *dai)
@@ -263,15 +281,16 @@
.id = 0,
.type = SND_SOC_DAI_I2S,
.probe = bf5xx_i2s_probe,
+ .remove = bf5xx_i2s_remove,
.suspend = bf5xx_i2s_suspend,
.resume = bf5xx_i2s_resume,
.playback = {
- .channels_min = 2,
+ .channels_min = 1,
.channels_max = 2,
.rates = BF5XX_I2S_RATES,
.formats = BF5XX_I2S_FORMATS,},
.capture = {
- .channels_min = 2,
+ .channels_min = 1,
.channels_max = 2,
.rates = BF5XX_I2S_RATES,
.formats = BF5XX_I2S_FORMATS,},
diff --git a/sound/soc/blackfin/bf5xx-sport.h b/sound/soc/blackfin/bf5xx-sport.h
index 4c16345..fcadcc0 100644
--- a/sound/soc/blackfin/bf5xx-sport.h
+++ b/sound/soc/blackfin/bf5xx-sport.h
@@ -123,6 +123,8 @@
int rx_pos;
unsigned int tx_buffer_size;
unsigned int rx_buffer_size;
+ int tx_delay_pos;
+ int once;
#endif
void *private_data;
};
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index e0b9869..4975d85 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -3,9 +3,11 @@
depends on I2C
select SPI
select SPI_MASTER
+ select SND_SOC_AD73311
select SND_SOC_AK4535
select SND_SOC_CS4270
select SND_SOC_SSM2602
+ select SND_SOC_TLV320AIC23
select SND_SOC_TLV320AIC26
select SND_SOC_TLV320AIC3X
select SND_SOC_UDA1380
@@ -34,6 +36,9 @@
config SND_SOC_AD1980
tristate
+config SND_SOC_AD73311
+ tristate
+
config SND_SOC_AK4535
tristate
@@ -58,9 +63,13 @@
config SND_SOC_SSM2602
tristate
+config SND_SOC_TLV320AIC23
+ tristate
+ depends on I2C
+
config SND_SOC_TLV320AIC26
tristate "TI TLV320AIC26 Codec support"
- depends on SND_SOC && SPI
+ depends on SPI
config SND_SOC_TLV320AIC3X
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index f977978..90f0a58 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -1,8 +1,10 @@
snd-soc-ac97-objs := ac97.o
snd-soc-ad1980-objs := ad1980.o
+snd-soc-ad73311-objs := ad73311.o
snd-soc-ak4535-objs := ak4535.o
snd-soc-cs4270-objs := cs4270.o
snd-soc-ssm2602-objs := ssm2602.o
+snd-soc-tlv320aic23-objs := tlv320aic23.o
snd-soc-tlv320aic26-objs := tlv320aic26.o
snd-soc-tlv320aic3x-objs := tlv320aic3x.o
snd-soc-uda1380-objs := uda1380.o
@@ -20,9 +22,11 @@
obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o
+obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o
+obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o
obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index 61fd96c..bd1ebdc 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -2,8 +2,7 @@
* ac97.c -- ALSA Soc AC97 codec support
*
* Copyright 2005 Wolfson Microelectronics PLC.
- * Author: Liam Girdwood
- * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
*
* 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
diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c
index 4e09c1f..1397b8e 100644
--- a/sound/soc/codecs/ad1980.c
+++ b/sound/soc/codecs/ad1980.c
@@ -13,7 +13,6 @@
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <sound/core.h>
diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c
new file mode 100644
index 0000000..37af860
--- /dev/null
+++ b/sound/soc/codecs/ad73311.c
@@ -0,0 +1,107 @@
+/*
+ * ad73311.c -- ALSA Soc AD73311 codec support
+ *
+ * Copyright: Analog Device Inc.
+ * Author: Cliff Cai <cliff.cai@analog.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.
+ *
+ * Revision history
+ * 25th Sep 2008 Initial version.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "ad73311.h"
+
+struct snd_soc_dai ad73311_dai = {
+ .name = "AD73311",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, },
+};
+EXPORT_SYMBOL_GPL(ad73311_dai);
+
+static int ad73311_soc_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+ mutex_init(&codec->mutex);
+ codec->name = "AD73311";
+ codec->owner = THIS_MODULE;
+ codec->dai = &ad73311_dai;
+ codec->num_dai = 1;
+ socdev->codec = codec;
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ printk(KERN_ERR "ad73311: failed to create pcms\n");
+ goto pcm_err;
+ }
+
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "ad73311: failed to register card\n");
+ goto register_err;
+ }
+
+ return ret;
+
+register_err:
+ snd_soc_free_pcms(socdev);
+pcm_err:
+ kfree(socdev->codec);
+ socdev->codec = NULL;
+ return ret;
+}
+
+static int ad73311_soc_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ if (codec == NULL)
+ return 0;
+ snd_soc_free_pcms(socdev);
+ kfree(codec);
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_ad73311 = {
+ .probe = ad73311_soc_probe,
+ .remove = ad73311_soc_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ad73311);
+
+MODULE_DESCRIPTION("ASoC ad73311 driver");
+MODULE_AUTHOR("Cliff Cai ");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ad73311.h b/sound/soc/codecs/ad73311.h
new file mode 100644
index 0000000..507ce0c
--- /dev/null
+++ b/sound/soc/codecs/ad73311.h
@@ -0,0 +1,90 @@
+/*
+ * File: sound/soc/codec/ad73311.h
+ * Based on:
+ * Author: Cliff Cai <cliff.cai@analog.com>
+ *
+ * Created: Thur Sep 25, 2008
+ * Description: definitions for AD73311 registers
+ *
+ *
+ * Modified:
+ * Copyright 2006 Analog Devices Inc.
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __AD73311_H__
+#define __AD73311_H__
+
+#define AD_CONTROL 0x8000
+#define AD_DATA 0x0000
+#define AD_READ 0x4000
+#define AD_WRITE 0x0000
+
+/* Control register A */
+#define CTRL_REG_A (0 << 8)
+
+#define REGA_MODE_PRO 0x00
+#define REGA_MODE_DATA 0x01
+#define REGA_MODE_MIXED 0x03
+#define REGA_DLB 0x04
+#define REGA_SLB 0x08
+#define REGA_DEVC(x) ((x & 0x7) << 4)
+#define REGA_RESET 0x80
+
+/* Control register B */
+#define CTRL_REG_B (1 << 8)
+
+#define REGB_DIRATE(x) (x & 0x3)
+#define REGB_SCDIV(x) ((x & 0x3) << 2)
+#define REGB_MCDIV(x) ((x & 0x7) << 4)
+#define REGB_CEE (1 << 7)
+
+/* Control register C */
+#define CTRL_REG_C (2 << 8)
+
+#define REGC_PUDEV (1 << 0)
+#define REGC_PUADC (1 << 3)
+#define REGC_PUDAC (1 << 4)
+#define REGC_PUREF (1 << 5)
+#define REGC_REFUSE (1 << 6)
+
+/* Control register D */
+#define CTRL_REG_D (3 << 8)
+
+#define REGD_IGS(x) (x & 0x7)
+#define REGD_RMOD (1 << 3)
+#define REGD_OGS(x) ((x & 0x7) << 4)
+#define REGD_MUTE (x << 7)
+
+/* Control register E */
+#define CTRL_REG_E (4 << 8)
+
+#define REGE_DA(x) (x & 0x1f)
+#define REGE_IBYP (1 << 5)
+
+/* Control register F */
+#define CTRL_REG_F (5 << 8)
+
+#define REGF_SEEN (1 << 5)
+#define REGF_INV (1 << 6)
+#define REGF_ALB (1 << 7)
+
+extern struct snd_soc_dai ad73311_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ad73311;
+#endif
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
index 088cf99..2a89b58 100644
--- a/sound/soc/codecs/ak4535.c
+++ b/sound/soc/codecs/ak4535.c
@@ -28,7 +28,6 @@
#include "ak4535.h"
-#define AUDIO_NAME "ak4535"
#define AK4535_VERSION "0.3"
struct snd_soc_codec_device soc_codec_dev_ak4535;
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c
index 940ce1c..44ef0da 100644
--- a/sound/soc/codecs/ssm2602.c
+++ b/sound/soc/codecs/ssm2602.c
@@ -42,7 +42,6 @@
#include "ssm2602.h"
-#define AUDIO_NAME "ssm2602"
#define SSM2602_VERSION "0.1"
struct snd_soc_codec_device soc_codec_dev_ssm2602;
diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c
new file mode 100644
index 0000000..bac7815
--- /dev/null
+++ b/sound/soc/codecs/tlv320aic23.c
@@ -0,0 +1,714 @@
+/*
+ * ALSA SoC TLV320AIC23 codec driver
+ *
+ * Author: Arun KS, <arunks@mistralsolutions.com>
+ * Copyright: (C) 2008 Mistral Solutions Pvt Ltd.,
+ *
+ * Based on sound/soc/codecs/wm8731.c by Richard Purdie
+ *
+ * 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.
+ *
+ * Notes:
+ * The AIC23 is a driver for a low power stereo audio
+ * codec tlv320aic23
+ *
+ * The machine layer should disable unsupported inputs/outputs by
+ * snd_soc_dapm_disable_pin(codec, "LHPOUT"), etc.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <sound/initval.h>
+
+#include "tlv320aic23.h"
+
+#define AIC23_VERSION "0.1"
+
+struct tlv320aic23_srate_reg_info {
+ u32 sample_rate;
+ u8 control; /* SR3, SR2, SR1, SR0 and BOSR */
+ u8 divider; /* if 0 CLKIN = MCLK, if 1 CLKIN = MCLK/2 */
+};
+
+/*
+ * AIC23 register cache
+ */
+static const u16 tlv320aic23_reg[] = {
+ 0x0097, 0x0097, 0x00F9, 0x00F9, /* 0 */
+ 0x001A, 0x0004, 0x0007, 0x0001, /* 4 */
+ 0x0020, 0x0000, 0x0000, 0x0000, /* 8 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 12 */
+};
+
+/*
+ * read tlv320aic23 register cache
+ */
+static inline unsigned int tlv320aic23_read_reg_cache(struct snd_soc_codec
+ *codec, unsigned int reg)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg >= ARRAY_SIZE(tlv320aic23_reg))
+ return -1;
+ return cache[reg];
+}
+
+/*
+ * write tlv320aic23 register cache
+ */
+static inline void tlv320aic23_write_reg_cache(struct snd_soc_codec *codec,
+ u8 reg, u16 value)
+{
+ u16 *cache = codec->reg_cache;
+ if (reg >= ARRAY_SIZE(tlv320aic23_reg))
+ return;
+ cache[reg] = value;
+}
+
+/*
+ * write to the tlv320aic23 register space
+ */
+static int tlv320aic23_write(struct snd_soc_codec *codec, unsigned int reg,
+ unsigned int value)
+{
+
+ u8 data;
+
+ /* TLV320AIC23 has 7 bit address and 9 bits of data
+ * so we need to switch one data bit into reg and rest
+ * of data into val
+ */
+
+ if ((reg < 0 || reg > 9) && (reg != 15)) {
+ printk(KERN_WARNING "%s Invalid register R%d\n", __func__, reg);
+ return -1;
+ }
+
+ data = (reg << 1) | (value >> 8 & 0x01);
+
+ tlv320aic23_write_reg_cache(codec, reg, value);
+
+ if (codec->hw_write(codec->control_data, data,
+ (value & 0xff)) == 0)
+ return 0;
+
+ printk(KERN_ERR "%s cannot write %03x to register R%d\n", __func__,
+ value, reg);
+
+ return -EIO;
+}
+
+static const char *rec_src_text[] = { "Line", "Mic" };
+static const char *deemph_text[] = {"None", "32Khz", "44.1Khz", "48Khz"};
+
+static const struct soc_enum rec_src_enum =
+ SOC_ENUM_SINGLE(TLV320AIC23_ANLG, 2, 2, rec_src_text);
+
+static const struct snd_kcontrol_new tlv320aic23_rec_src_mux_controls =
+SOC_DAPM_ENUM("Input Select", rec_src_enum);
+
+static const struct soc_enum tlv320aic23_rec_src =
+ SOC_ENUM_SINGLE(TLV320AIC23_ANLG, 2, 2, rec_src_text);
+static const struct soc_enum tlv320aic23_deemph =
+ SOC_ENUM_SINGLE(TLV320AIC23_DIGT, 1, 4, deemph_text);
+
+static const DECLARE_TLV_DB_SCALE(out_gain_tlv, -12100, 100, 0);
+static const DECLARE_TLV_DB_SCALE(input_gain_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(sidetone_vol_tlv, -1800, 300, 0);
+
+static int snd_soc_tlv320aic23_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ u16 val, reg;
+
+ val = (ucontrol->value.integer.value[0] & 0x07);
+
+ /* linear conversion to userspace
+ * 000 = -6db
+ * 001 = -9db
+ * 010 = -12db
+ * 011 = -18db (Min)
+ * 100 = 0db (Max)
+ */
+ val = (val >= 4) ? 4 : (3 - val);
+
+ reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_ANLG) & (~0x1C0);
+ tlv320aic23_write(codec, TLV320AIC23_ANLG, reg | (val << 6));
+
+ return 0;
+}
+
+static int snd_soc_tlv320aic23_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ u16 val;
+
+ val = tlv320aic23_read_reg_cache(codec, TLV320AIC23_ANLG) & (0x1C0);
+ val = val >> 6;
+ val = (val >= 4) ? 4 : (3 - val);
+ ucontrol->value.integer.value[0] = val;
+ return 0;
+
+}
+
+#define SOC_TLV320AIC23_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+ SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+ .tlv.p = (tlv_array), \
+ .info = snd_soc_info_volsw, .get = snd_soc_tlv320aic23_get_volsw,\
+ .put = snd_soc_tlv320aic23_put_volsw, \
+ .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+
+static const struct snd_kcontrol_new tlv320aic23_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Digital Playback Volume", TLV320AIC23_LCHNVOL,
+ TLV320AIC23_RCHNVOL, 0, 127, 0, out_gain_tlv),
+ SOC_SINGLE("Digital Playback Switch", TLV320AIC23_DIGT, 3, 1, 1),
+ SOC_DOUBLE_R("Line Input Switch", TLV320AIC23_LINVOL,
+ TLV320AIC23_RINVOL, 7, 1, 0),
+ SOC_DOUBLE_R_TLV("Line Input Volume", TLV320AIC23_LINVOL,
+ TLV320AIC23_RINVOL, 0, 31, 0, input_gain_tlv),
+ SOC_SINGLE("Mic Input Switch", TLV320AIC23_ANLG, 1, 1, 1),
+ SOC_SINGLE("Mic Booster Switch", TLV320AIC23_ANLG, 0, 1, 0),
+ SOC_TLV320AIC23_SINGLE_TLV("Sidetone Volume", TLV320AIC23_ANLG,
+ 6, 4, 0, sidetone_vol_tlv),
+ SOC_ENUM("Playback De-emphasis", tlv320aic23_deemph),
+};
+
+/* add non dapm controls */
+static int tlv320aic23_add_controls(struct snd_soc_codec *codec)
+{
+
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(tlv320aic23_snd_controls); i++) {
+ err = snd_ctl_add(codec->card,
+ snd_soc_cnew(&tlv320aic23_snd_controls[i],
+ codec, NULL));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+
+}
+
+/* PGA Mixer controls for Line and Mic switch */
+static const struct snd_kcontrol_new tlv320aic23_output_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Line Bypass Switch", TLV320AIC23_ANLG, 3, 1, 0),
+ SOC_DAPM_SINGLE("Mic Sidetone Switch", TLV320AIC23_ANLG, 5, 1, 0),
+ SOC_DAPM_SINGLE("Playback Switch", TLV320AIC23_ANLG, 4, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", "Playback", TLV320AIC23_PWR, 3, 1),
+ SND_SOC_DAPM_ADC("ADC", "Capture", TLV320AIC23_PWR, 2, 1),
+ SND_SOC_DAPM_MUX("Capture Source", SND_SOC_NOPM, 0, 0,
+ &tlv320aic23_rec_src_mux_controls),
+ SND_SOC_DAPM_MIXER("Output Mixer", TLV320AIC23_PWR, 4, 1,
+ &tlv320aic23_output_mixer_controls[0],
+ ARRAY_SIZE(tlv320aic23_output_mixer_controls)),
+ SND_SOC_DAPM_PGA("Line Input", TLV320AIC23_PWR, 0, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Mic Input", TLV320AIC23_PWR, 1, 1, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("LHPOUT"),
+ SND_SOC_DAPM_OUTPUT("RHPOUT"),
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+ SND_SOC_DAPM_OUTPUT("ROUT"),
+
+ SND_SOC_DAPM_INPUT("LLINEIN"),
+ SND_SOC_DAPM_INPUT("RLINEIN"),
+
+ SND_SOC_DAPM_INPUT("MICIN"),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ /* Output Mixer */
+ {"Output Mixer", "Line Bypass Switch", "Line Input"},
+ {"Output Mixer", "Playback Switch", "DAC"},
+ {"Output Mixer", "Mic Sidetone Switch", "Mic Input"},
+
+ /* Outputs */
+ {"RHPOUT", NULL, "Output Mixer"},
+ {"LHPOUT", NULL, "Output Mixer"},
+ {"LOUT", NULL, "Output Mixer"},
+ {"ROUT", NULL, "Output Mixer"},
+
+ /* Inputs */
+ {"Line Input", "NULL", "LLINEIN"},
+ {"Line Input", "NULL", "RLINEIN"},
+
+ {"Mic Input", "NULL", "MICIN"},
+
+ /* input mux */
+ {"Capture Source", "Line", "Line Input"},
+ {"Capture Source", "Mic", "Mic Input"},
+ {"ADC", NULL, "Capture Source"},
+
+};
+
+/* tlv320aic23 related */
+static const struct tlv320aic23_srate_reg_info srate_reg_info[] = {
+ {4000, 0x06, 1}, /* 4000 */
+ {8000, 0x06, 0}, /* 8000 */
+ {16000, 0x0C, 1}, /* 16000 */
+ {22050, 0x11, 1}, /* 22050 */
+ {24000, 0x00, 1}, /* 24000 */
+ {32000, 0x0C, 0}, /* 32000 */
+ {44100, 0x11, 0}, /* 44100 */
+ {48000, 0x00, 0}, /* 48000 */
+ {88200, 0x1F, 0}, /* 88200 */
+ {96000, 0x0E, 0}, /* 96000 */
+};
+
+static int tlv320aic23_add_widgets(struct snd_soc_codec *codec)
+{
+ snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets,
+ ARRAY_SIZE(tlv320aic23_dapm_widgets));
+
+ /* set up audio path interconnects */
+ snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+ snd_soc_dapm_new_widgets(codec);
+ return 0;
+}
+
+static int tlv320aic23_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ u16 iface_reg, data;
+ u8 count = 0;
+
+ iface_reg =
+ tlv320aic23_read_reg_cache(codec,
+ TLV320AIC23_DIGT_FMT) & ~(0x03 << 2);
+
+ /* Search for the right sample rate */
+ /* Verify what happens if the rate is not supported
+ * now it goes to 96Khz */
+ while ((srate_reg_info[count].sample_rate != params_rate(params)) &&
+ (count < ARRAY_SIZE(srate_reg_info))) {
+ count++;
+ }
+
+ data = (srate_reg_info[count].divider << TLV320AIC23_CLKIN_SHIFT) |
+ (srate_reg_info[count]. control << TLV320AIC23_BOSR_SHIFT) |
+ TLV320AIC23_USB_CLK_ON;
+
+ tlv320aic23_write(codec, TLV320AIC23_SRATE, data);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ iface_reg |= (0x01 << 2);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ iface_reg |= (0x02 << 2);
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ iface_reg |= (0x03 << 2);
+ break;
+ }
+ tlv320aic23_write(codec, TLV320AIC23_DIGT_FMT, iface_reg);
+
+ return 0;
+}
+
+static int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+
+ /* set active */
+ tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0001);
+
+ return 0;
+}
+
+static void tlv320aic23_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_device *socdev = rtd->socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+
+ /* deactivate */
+ if (!codec->active) {
+ udelay(50);
+ tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0);
+ }
+}
+
+static int tlv320aic23_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ u16 reg;
+
+ reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_DIGT);
+ if (mute)
+ reg |= TLV320AIC23_DACM_MUTE;
+
+ else
+ reg &= ~TLV320AIC23_DACM_MUTE;
+
+ tlv320aic23_write(codec, TLV320AIC23_DIGT, reg);
+
+ return 0;
+}
+
+static int tlv320aic23_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u16 iface_reg;
+
+ iface_reg =
+ tlv320aic23_read_reg_cache(codec, TLV320AIC23_DIGT_FMT) & (~0x03);
+
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ iface_reg |= TLV320AIC23_MS_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface_reg |= TLV320AIC23_FOR_I2S;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ iface_reg |= TLV320AIC23_FOR_DSP;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface_reg |= TLV320AIC23_FOR_LJUST;
+ break;
+ default:
+ return -EINVAL;
+
+ }
+
+ tlv320aic23_write(codec, TLV320AIC23_DIGT_FMT, iface_reg);
+
+ return 0;
+}
+
+static int tlv320aic23_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+
+ switch (freq) {
+ case 12000000:
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int tlv320aic23_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ u16 reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_PWR) & 0xff7f;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ /* vref/mid, osc on, dac unmute */
+ tlv320aic23_write(codec, TLV320AIC23_PWR, reg);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ /* everything off except vref/vmid, */
+ tlv320aic23_write(codec, TLV320AIC23_PWR, reg | 0x0040);
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* everything off, dac mute, inactive */
+ tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0);
+ tlv320aic23_write(codec, TLV320AIC23_PWR, 0xffff);
+ break;
+ }
+ codec->bias_level = level;
+ return 0;
+}
+
+#define AIC23_RATES SNDRV_PCM_RATE_8000_96000
+#define AIC23_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct snd_soc_dai tlv320aic23_dai = {
+ .name = "tlv320aic23",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AIC23_RATES,
+ .formats = AIC23_FORMATS,},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AIC23_RATES,
+ .formats = AIC23_FORMATS,},
+ .ops = {
+ .prepare = tlv320aic23_pcm_prepare,
+ .hw_params = tlv320aic23_hw_params,
+ .shutdown = tlv320aic23_shutdown,
+ },
+ .dai_ops = {
+ .digital_mute = tlv320aic23_mute,
+ .set_fmt = tlv320aic23_set_dai_fmt,
+ .set_sysclk = tlv320aic23_set_dai_sysclk,
+ }
+};
+EXPORT_SYMBOL_GPL(tlv320aic23_dai);
+
+static int tlv320aic23_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0);
+ tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ return 0;
+}
+
+static int tlv320aic23_resume(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+ int i;
+ u16 reg;
+
+ /* Sync reg_cache with the hardware */
+ for (reg = 0; reg < ARRAY_SIZE(tlv320aic23_reg); i++) {
+ u16 val = tlv320aic23_read_reg_cache(codec, reg);
+ tlv320aic23_write(codec, reg, val);
+ }
+
+ tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ tlv320aic23_set_bias_level(codec, codec->suspend_bias_level);
+
+ return 0;
+}
+
+/*
+ * initialise the AIC23 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int tlv320aic23_init(struct snd_soc_device *socdev)
+{
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret = 0;
+ u16 reg;
+
+ codec->name = "tlv320aic23";
+ codec->owner = THIS_MODULE;
+ codec->read = tlv320aic23_read_reg_cache;
+ codec->write = tlv320aic23_write;
+ codec->set_bias_level = tlv320aic23_set_bias_level;
+ codec->dai = &tlv320aic23_dai;
+ codec->num_dai = 1;
+ codec->reg_cache_size = ARRAY_SIZE(tlv320aic23_reg);
+ codec->reg_cache =
+ kmemdup(tlv320aic23_reg, sizeof(tlv320aic23_reg), GFP_KERNEL);
+ if (codec->reg_cache == NULL)
+ return -ENOMEM;
+
+ /* Reset codec */
+ tlv320aic23_write(codec, TLV320AIC23_RESET, 0);
+
+ /* register pcms */
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ printk(KERN_ERR "tlv320aic23: failed to create pcms\n");
+ goto pcm_err;
+ }
+
+ /* power on device */
+ tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+ tlv320aic23_write(codec, TLV320AIC23_DIGT, TLV320AIC23_DEEMP_44K);
+
+ /* Unmute input */
+ reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_LINVOL);
+ tlv320aic23_write(codec, TLV320AIC23_LINVOL,
+ (reg & (~TLV320AIC23_LIM_MUTED)) |
+ (TLV320AIC23_LRS_ENABLED));
+
+ reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_RINVOL);
+ tlv320aic23_write(codec, TLV320AIC23_RINVOL,
+ (reg & (~TLV320AIC23_LIM_MUTED)) |
+ TLV320AIC23_LRS_ENABLED);
+
+ reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_ANLG);
+ tlv320aic23_write(codec, TLV320AIC23_ANLG,
+ (reg) & (~TLV320AIC23_BYPASS_ON) &
+ (~TLV320AIC23_MICM_MUTED));
+
+ /* Default output volume */
+ tlv320aic23_write(codec, TLV320AIC23_LCHNVOL,
+ TLV320AIC23_DEFAULT_OUT_VOL &
+ TLV320AIC23_OUT_VOL_MASK);
+ tlv320aic23_write(codec, TLV320AIC23_RCHNVOL,
+ TLV320AIC23_DEFAULT_OUT_VOL &
+ TLV320AIC23_OUT_VOL_MASK);
+
+ tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x1);
+
+ tlv320aic23_add_controls(codec);
+ tlv320aic23_add_widgets(codec);
+ ret = snd_soc_register_card(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "tlv320aic23: failed to register card\n");
+ goto card_err;
+ }
+
+ return ret;
+
+card_err:
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+pcm_err:
+ kfree(codec->reg_cache);
+ return ret;
+}
+static struct snd_soc_device *tlv320aic23_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+/*
+ * If the i2c layer weren't so broken, we could pass this kind of data
+ * around
+ */
+static int tlv320aic23_codec_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *i2c_id)
+{
+ struct snd_soc_device *socdev = tlv320aic23_socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret;
+
+ if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EINVAL;
+
+ i2c_set_clientdata(i2c, codec);
+ codec->control_data = i2c;
+
+ ret = tlv320aic23_init(socdev);
+ if (ret < 0) {
+ printk(KERN_ERR "tlv320aic23: failed to initialise AIC23\n");
+ goto err;
+ }
+ return ret;
+
+err:
+ kfree(codec);
+ kfree(i2c);
+ return ret;
+}
+static int __exit tlv320aic23_i2c_remove(struct i2c_client *i2c)
+{
+ put_device(&i2c->dev);
+ return 0;
+}
+
+static const struct i2c_device_id tlv320aic23_id[] = {
+ {"tlv320aic23", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, tlv320aic23_id);
+
+static struct i2c_driver tlv320aic23_i2c_driver = {
+ .driver = {
+ .name = "tlv320aic23",
+ },
+ .probe = tlv320aic23_codec_probe,
+ .remove = __exit_p(tlv320aic23_i2c_remove),
+ .id_table = tlv320aic23_id,
+};
+
+#endif
+
+static int tlv320aic23_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret = 0;
+
+ printk(KERN_INFO "AIC23 Audio Codec %s\n", AIC23_VERSION);
+
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+
+ socdev->codec = codec;
+ mutex_init(&codec->mutex);
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+ tlv320aic23_socdev = socdev;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ codec->hw_write = (hw_write_t) i2c_smbus_write_byte_data;
+ codec->hw_read = NULL;
+ ret = i2c_add_driver(&tlv320aic23_i2c_driver);
+ if (ret != 0)
+ printk(KERN_ERR "can't add i2c driver");
+#endif
+ return ret;
+}
+
+static int tlv320aic23_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ if (codec->control_data)
+ tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+ snd_soc_free_pcms(socdev);
+ snd_soc_dapm_free(socdev);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ i2c_del_driver(&tlv320aic23_i2c_driver);
+#endif
+ kfree(codec->reg_cache);
+ kfree(codec);
+
+ return 0;
+}
+struct snd_soc_codec_device soc_codec_dev_tlv320aic23 = {
+ .probe = tlv320aic23_probe,
+ .remove = tlv320aic23_remove,
+ .suspend = tlv320aic23_suspend,
+ .resume = tlv320aic23_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_tlv320aic23);
+
+MODULE_DESCRIPTION("ASoC TLV320AIC23 codec driver");
+MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tlv320aic23.h b/sound/soc/codecs/tlv320aic23.h
new file mode 100644
index 0000000..79d1faf
--- /dev/null
+++ b/sound/soc/codecs/tlv320aic23.h
@@ -0,0 +1,122 @@
+/*
+ * ALSA SoC TLV320AIC23 codec driver
+ *
+ * Author: Arun KS, <arunks@mistralsolutions.com>
+ * Copyright: (C) 2008 Mistral Solutions Pvt Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _TLV320AIC23_H
+#define _TLV320AIC23_H
+
+/* Codec TLV320AIC23 */
+#define TLV320AIC23_LINVOL 0x00
+#define TLV320AIC23_RINVOL 0x01
+#define TLV320AIC23_LCHNVOL 0x02
+#define TLV320AIC23_RCHNVOL 0x03
+#define TLV320AIC23_ANLG 0x04
+#define TLV320AIC23_DIGT 0x05
+#define TLV320AIC23_PWR 0x06
+#define TLV320AIC23_DIGT_FMT 0x07
+#define TLV320AIC23_SRATE 0x08
+#define TLV320AIC23_ACTIVE 0x09
+#define TLV320AIC23_RESET 0x0F
+
+/* Left (right) line input volume control register */
+#define TLV320AIC23_LRS_ENABLED 0x0100
+#define TLV320AIC23_LIM_MUTED 0x0080
+#define TLV320AIC23_LIV_DEFAULT 0x0017
+#define TLV320AIC23_LIV_MAX 0x001f
+#define TLV320AIC23_LIV_MIN 0x0000
+
+/* Left (right) channel headphone volume control register */
+#define TLV320AIC23_LZC_ON 0x0080
+#define TLV320AIC23_LHV_DEFAULT 0x0079
+#define TLV320AIC23_LHV_MAX 0x007f
+#define TLV320AIC23_LHV_MIN 0x0000
+
+/* Analog audio path control register */
+#define TLV320AIC23_STA_REG(x) ((x)<<6)
+#define TLV320AIC23_STE_ENABLED 0x0020
+#define TLV320AIC23_DAC_SELECTED 0x0010
+#define TLV320AIC23_BYPASS_ON 0x0008
+#define TLV320AIC23_INSEL_MIC 0x0004
+#define TLV320AIC23_MICM_MUTED 0x0002
+#define TLV320AIC23_MICB_20DB 0x0001
+
+/* Digital audio path control register */
+#define TLV320AIC23_DACM_MUTE 0x0008
+#define TLV320AIC23_DEEMP_32K 0x0002
+#define TLV320AIC23_DEEMP_44K 0x0004
+#define TLV320AIC23_DEEMP_48K 0x0006
+#define TLV320AIC23_ADCHP_ON 0x0001
+
+/* Power control down register */
+#define TLV320AIC23_DEVICE_PWR_OFF 0x0080
+#define TLV320AIC23_CLK_OFF 0x0040
+#define TLV320AIC23_OSC_OFF 0x0020
+#define TLV320AIC23_OUT_OFF 0x0010
+#define TLV320AIC23_DAC_OFF 0x0008
+#define TLV320AIC23_ADC_OFF 0x0004
+#define TLV320AIC23_MIC_OFF 0x0002
+#define TLV320AIC23_LINE_OFF 0x0001
+
+/* Digital audio interface register */
+#define TLV320AIC23_MS_MASTER 0x0040
+#define TLV320AIC23_LRSWAP_ON 0x0020
+#define TLV320AIC23_LRP_ON 0x0010
+#define TLV320AIC23_IWL_16 0x0000
+#define TLV320AIC23_IWL_20 0x0004
+#define TLV320AIC23_IWL_24 0x0008
+#define TLV320AIC23_IWL_32 0x000C
+#define TLV320AIC23_FOR_I2S 0x0002
+#define TLV320AIC23_FOR_DSP 0x0003
+#define TLV320AIC23_FOR_LJUST 0x0001
+
+/* Sample rate control register */
+#define TLV320AIC23_CLKOUT_HALF 0x0080
+#define TLV320AIC23_CLKIN_HALF 0x0040
+#define TLV320AIC23_BOSR_384fs 0x0002 /* BOSR_272fs in USB mode */
+#define TLV320AIC23_USB_CLK_ON 0x0001
+#define TLV320AIC23_SR_MASK 0xf
+#define TLV320AIC23_CLKOUT_SHIFT 7
+#define TLV320AIC23_CLKIN_SHIFT 6
+#define TLV320AIC23_SR_SHIFT 2
+#define TLV320AIC23_BOSR_SHIFT 1
+
+/* Digital interface register */
+#define TLV320AIC23_ACT_ON 0x0001
+
+/*
+ * AUDIO related MACROS
+ */
+
+#define TLV320AIC23_DEFAULT_OUT_VOL 0x70
+#define TLV320AIC23_DEFAULT_IN_VOLUME 0x10
+
+#define TLV320AIC23_OUT_VOL_MIN TLV320AIC23_LHV_MIN
+#define TLV320AIC23_OUT_VOL_MAX TLV320AIC23_LHV_MAX
+#define TLV320AIC23_OUT_VO_RANGE (TLV320AIC23_OUT_VOL_MAX - \
+ TLV320AIC23_OUT_VOL_MIN)
+#define TLV320AIC23_OUT_VOL_MASK TLV320AIC23_OUT_VOL_MAX
+
+#define TLV320AIC23_IN_VOL_MIN TLV320AIC23_LIV_MIN
+#define TLV320AIC23_IN_VOL_MAX TLV320AIC23_LIV_MAX
+#define TLV320AIC23_IN_VOL_RANGE (TLV320AIC23_IN_VOL_MAX - \
+ TLV320AIC23_IN_VOL_MIN)
+#define TLV320AIC23_IN_VOL_MASK TLV320AIC23_IN_VOL_MAX
+
+#define TLV320AIC23_SIDETONE_MASK 0x1c0
+#define TLV320AIC23_SIDETONE_0 0x100
+#define TLV320AIC23_SIDETONE_6 0x000
+#define TLV320AIC23_SIDETONE_9 0x040
+#define TLV320AIC23_SIDETONE_12 0x080
+#define TLV320AIC23_SIDETONE_18 0x0c0
+
+extern struct snd_soc_dai tlv320aic23_dai;
+extern struct snd_soc_codec_device soc_codec_dev_tlv320aic23;
+
+#endif /* _TLV320AIC23_H */
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 566a427..05336ed 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -48,7 +48,6 @@
#include "tlv320aic3x.h"
-#define AUDIO_NAME "aic3x"
#define AIC3X_VERSION "0.2"
/* codec private data */
@@ -991,7 +990,7 @@
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
struct snd_soc_dai aic3x_dai = {
- .name = "aic3x",
+ .name = "tlv320aic3x",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
@@ -1055,7 +1054,7 @@
struct aic3x_setup_data *setup = socdev->codec_data;
int reg, ret = 0;
- codec->name = "aic3x";
+ codec->name = "tlv320aic3x";
codec->owner = THIS_MODULE;
codec->read = aic3x_read_reg_cache;
codec->write = aic3x_write;
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index d206d7f..a69ee72 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -36,7 +36,6 @@
#include "uda1380.h"
#define UDA1380_VERSION "0.6"
-#define AUDIO_NAME "uda1380"
/*
* uda1380 register cache
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c
index 9a37c8d..d8ca2da 100644
--- a/sound/soc/codecs/wm8510.c
+++ b/sound/soc/codecs/wm8510.c
@@ -3,7 +3,7 @@
*
* Copyright 2006 Wolfson Microelectronics PLC.
*
- * Author: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
*
* 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
@@ -18,6 +18,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -27,7 +28,6 @@
#include "wm8510.h"
-#define AUDIO_NAME "wm8510"
#define WM8510_VERSION "0.6"
struct snd_soc_codec_device soc_codec_dev_wm8510;
@@ -55,6 +55,9 @@
0x0001,
};
+#define WM8510_POWER1_BIASEN 0x08
+#define WM8510_POWER1_BUFIOEN 0x10
+
/*
* read wm8510 register cache
*/
@@ -224,9 +227,9 @@
SND_SOC_DAPM_PGA("SpkP Out", WM8510_POWER3, 6, 0, NULL, 0),
SND_SOC_DAPM_PGA("Mono Out", WM8510_POWER3, 7, 0, NULL, 0),
-SND_SOC_DAPM_PGA("Mic PGA", WM8510_POWER2, 2, 0,
- &wm8510_micpga_controls[0],
- ARRAY_SIZE(wm8510_micpga_controls)),
+SND_SOC_DAPM_MIXER("Mic PGA", WM8510_POWER2, 2, 0,
+ &wm8510_micpga_controls[0],
+ ARRAY_SIZE(wm8510_micpga_controls)),
SND_SOC_DAPM_MIXER("Boost Mixer", WM8510_POWER2, 4, 0,
&wm8510_boost_controls[0],
ARRAY_SIZE(wm8510_boost_controls)),
@@ -526,23 +529,35 @@
static int wm8510_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
+ u16 power1 = wm8510_read_reg_cache(codec, WM8510_POWER1) & ~0x3;
switch (level) {
case SND_SOC_BIAS_ON:
- wm8510_write(codec, WM8510_POWER1, 0x1ff);
- wm8510_write(codec, WM8510_POWER2, 0x1ff);
- wm8510_write(codec, WM8510_POWER3, 0x1ff);
- break;
case SND_SOC_BIAS_PREPARE:
- case SND_SOC_BIAS_STANDBY:
+ power1 |= 0x1; /* VMID 50k */
+ wm8510_write(codec, WM8510_POWER1, power1);
break;
+
+ case SND_SOC_BIAS_STANDBY:
+ power1 |= WM8510_POWER1_BIASEN | WM8510_POWER1_BUFIOEN;
+
+ if (codec->bias_level == SND_SOC_BIAS_OFF) {
+ /* Initial cap charge at VMID 5k */
+ wm8510_write(codec, WM8510_POWER1, power1 | 0x3);
+ mdelay(100);
+ }
+
+ power1 |= 0x2; /* VMID 500k */
+ wm8510_write(codec, WM8510_POWER1, power1);
+ break;
+
case SND_SOC_BIAS_OFF:
- /* everything off, dac mute, inactive */
- wm8510_write(codec, WM8510_POWER1, 0x0);
- wm8510_write(codec, WM8510_POWER2, 0x0);
- wm8510_write(codec, WM8510_POWER3, 0x0);
+ wm8510_write(codec, WM8510_POWER1, 0);
+ wm8510_write(codec, WM8510_POWER2, 0);
+ wm8510_write(codec, WM8510_POWER3, 0);
break;
}
+
codec->bias_level = level;
return 0;
}
@@ -640,6 +655,7 @@
}
/* power on device */
+ codec->bias_level = SND_SOC_BIAS_OFF;
wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
wm8510_add_controls(codec);
wm8510_add_widgets(codec);
@@ -747,6 +763,62 @@
}
#endif
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit wm8510_spi_probe(struct spi_device *spi)
+{
+ struct snd_soc_device *socdev = wm8510_socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret;
+
+ codec->control_data = spi;
+
+ ret = wm8510_init(socdev);
+ if (ret < 0)
+ dev_err(&spi->dev, "failed to initialise WM8510\n");
+
+ return ret;
+}
+
+static int __devexit wm8510_spi_remove(struct spi_device *spi)
+{
+ return 0;
+}
+
+static struct spi_driver wm8510_spi_driver = {
+ .driver = {
+ .name = "wm8510",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8510_spi_probe,
+ .remove = __devexit_p(wm8510_spi_remove),
+};
+
+static int wm8510_spi_write(struct spi_device *spi, const char *data, int len)
+{
+ struct spi_transfer t;
+ struct spi_message m;
+ u8 msg[2];
+
+ if (len <= 0)
+ return 0;
+
+ msg[0] = data[0];
+ msg[1] = data[1];
+
+ spi_message_init(&m);
+ memset(&t, 0, (sizeof t));
+
+ t.tx_buf = &msg[0];
+ t.len = len;
+
+ spi_message_add_tail(&t, &m);
+ spi_sync(spi, &m);
+
+ return len;
+}
+#endif /* CONFIG_SPI_MASTER */
+
static int wm8510_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
@@ -772,8 +844,14 @@
codec->hw_write = (hw_write_t)i2c_master_send;
ret = wm8510_add_i2c_device(pdev, setup);
}
-#else
- /* Add other interfaces here */
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ if (setup->spi) {
+ codec->hw_write = (hw_write_t)wm8510_spi_write;
+ ret = spi_register_driver(&wm8510_spi_driver);
+ if (ret != 0)
+ printk(KERN_ERR "can't add spi driver");
+ }
#endif
if (ret != 0)
@@ -796,6 +874,9 @@
i2c_unregister_device(codec->control_data);
i2c_del_driver(&wm8510_i2c_driver);
#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&wm8510_spi_driver);
+#endif
kfree(codec);
return 0;
diff --git a/sound/soc/codecs/wm8510.h b/sound/soc/codecs/wm8510.h
index c536839..bdefcf5 100644
--- a/sound/soc/codecs/wm8510.h
+++ b/sound/soc/codecs/wm8510.h
@@ -94,6 +94,7 @@
#define WM8510_MCLKDIV_12 (7 << 5)
struct wm8510_setup_data {
+ int spi;
int i2c_bus;
unsigned short i2c_address;
};
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c
index df1ffbe..627ebfb 100644
--- a/sound/soc/codecs/wm8580.c
+++ b/sound/soc/codecs/wm8580.c
@@ -18,7 +18,6 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
@@ -36,7 +35,6 @@
#include "wm8580.h"
-#define AUDIO_NAME "wm8580"
#define WM8580_VERSION "0.1"
struct pll_state {
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index 7b64d9a..7f8a7e3 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -29,7 +29,6 @@
#include "wm8731.h"
-#define AUDIO_NAME "wm8731"
#define WM8731_VERSION "0.13"
struct snd_soc_codec_device soc_codec_dev_wm8731;
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index 4892e39..9b7296e 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -29,7 +29,6 @@
#include "wm8750.h"
-#define AUDIO_NAME "WM8750"
#define WM8750_VERSION "0.12"
/* codec private data */
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index 8c4df44..d426eaa 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -2,8 +2,7 @@
* wm8753.c -- WM8753 ALSA Soc Audio driver
*
* Copyright 2003 Wolfson Microelectronics PLC.
- * Author: Liam Girdwood
- * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
*
* 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
@@ -40,6 +39,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -51,7 +51,6 @@
#include "wm8753.h"
-#define AUDIO_NAME "wm8753"
#define WM8753_VERSION "0.16"
static int caps_charge = 2000;
@@ -1719,6 +1718,63 @@
}
#endif
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit wm8753_spi_probe(struct spi_device *spi)
+{
+ struct snd_soc_device *socdev = wm8753_socdev;
+ struct snd_soc_codec *codec = socdev->codec;
+ int ret;
+
+ codec->control_data = spi;
+
+ ret = wm8753_init(socdev);
+ if (ret < 0)
+ dev_err(&spi->dev, "failed to initialise WM8753\n");
+
+ return ret;
+}
+
+static int __devexit wm8753_spi_remove(struct spi_device *spi)
+{
+ return 0;
+}
+
+static struct spi_driver wm8753_spi_driver = {
+ .driver = {
+ .name = "wm8753",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8753_spi_probe,
+ .remove = __devexit_p(wm8753_spi_remove),
+};
+
+static int wm8753_spi_write(struct spi_device *spi, const char *data, int len)
+{
+ struct spi_transfer t;
+ struct spi_message m;
+ u8 msg[2];
+
+ if (len <= 0)
+ return 0;
+
+ msg[0] = data[0];
+ msg[1] = data[1];
+
+ spi_message_init(&m);
+ memset(&t, 0, (sizeof t));
+
+ t.tx_buf = &msg[0];
+ t.len = len;
+
+ spi_message_add_tail(&t, &m);
+ spi_sync(spi, &m);
+
+ return len;
+}
+#endif
+
+
static int wm8753_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
@@ -1753,8 +1809,14 @@
codec->hw_write = (hw_write_t)i2c_master_send;
ret = wm8753_add_i2c_device(pdev, setup);
}
-#else
- /* Add other interfaces here */
+#endif
+#if defined(CONFIG_SPI_MASTER)
+ if (setup->spi) {
+ codec->hw_write = (hw_write_t)wm8753_spi_write;
+ ret = spi_register_driver(&wm8753_spi_driver);
+ if (ret != 0)
+ printk(KERN_ERR "can't add spi driver");
+ }
#endif
if (ret != 0) {
@@ -1798,6 +1860,9 @@
i2c_unregister_device(codec->control_data);
i2c_del_driver(&wm8753_i2c_driver);
#endif
+#if defined(CONFIG_SPI_MASTER)
+ spi_unregister_driver(&wm8753_spi_driver);
+#endif
kfree(codec->private_data);
kfree(codec);
diff --git a/sound/soc/codecs/wm8753.h b/sound/soc/codecs/wm8753.h
index 7defde0..f55704c 100644
--- a/sound/soc/codecs/wm8753.h
+++ b/sound/soc/codecs/wm8753.h
@@ -2,8 +2,7 @@
* wm8753.h -- audio driver for WM8753
*
* Copyright 2003 Wolfson Microelectronics PLC.
- * Author: Liam Girdwood
- * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
*
* 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
@@ -79,6 +78,7 @@
#define WM8753_ADCTL2 0x3f
struct wm8753_setup_data {
+ int spi;
int i2c_bus;
unsigned short i2c_address;
};
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c
index 0b8c6d3..3b326c9 100644
--- a/sound/soc/codecs/wm8900.c
+++ b/sound/soc/codecs/wm8900.c
@@ -18,7 +18,6 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index a3f54ec..ce40d78 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -653,14 +653,14 @@
/* Input PGAs - No TLV since the scale depends on PGA mode */
SOC_SINGLE("Left Input PGA Switch", WM8903_ANALOGUE_LEFT_INPUT_0,
- 7, 1, 0),
+ 7, 1, 1),
SOC_SINGLE("Left Input PGA Volume", WM8903_ANALOGUE_LEFT_INPUT_0,
0, 31, 0),
SOC_SINGLE("Left Input PGA Common Mode Switch", WM8903_ANALOGUE_LEFT_INPUT_1,
6, 1, 0),
SOC_SINGLE("Right Input PGA Switch", WM8903_ANALOGUE_RIGHT_INPUT_0,
- 7, 1, 0),
+ 7, 1, 1),
SOC_SINGLE("Right Input PGA Volume", WM8903_ANALOGUE_RIGHT_INPUT_0,
0, 31, 0),
SOC_SINGLE("Right Input PGA Common Mode Switch", WM8903_ANALOGUE_RIGHT_INPUT_1,
diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c
index 974a4cd..f41a578 100644
--- a/sound/soc/codecs/wm8971.c
+++ b/sound/soc/codecs/wm8971.c
@@ -29,7 +29,6 @@
#include "wm8971.h"
-#define AUDIO_NAME "wm8971"
#define WM8971_VERSION "0.9"
#define WM8971_REG_COUNT 43
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index 63410d7..572d22b 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -30,7 +30,6 @@
#include "wm8990.h"
-#define AUDIO_NAME "wm8990"
#define WM8990_VERSION "0.2"
/* codec private data */
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index 2f1c91b..ffb471e 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -2,8 +2,7 @@
* wm9712.c -- ALSA Soc WM9712 codec support
*
* Copyright 2006 Wolfson Microelectronics PLC.
- * Author: Liam Girdwood
- * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
*
* 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
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index 441d058..aba402b 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -2,8 +2,7 @@
* wm9713.c -- ALSA Soc WM9713 codec support
*
* Copyright 2006 Wolfson Microelectronics PLC.
- * Author: Liam Girdwood
- * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
*
* 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
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index aea27e7..8b7766b 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -13,3 +13,11 @@
select SND_SOC_TLV320AIC3X
help
Say Y if you want to add support for SoC audio on Nokia N810.
+
+config SND_OMAP_SOC_OSK5912
+ tristate "SoC Audio support for omap osk5912"
+ depends on SND_OMAP_SOC && MACH_OMAP_OSK
+ select SND_OMAP_SOC_MCBSP
+ select SND_SOC_TLV320AIC23
+ help
+ Say Y if you want to add support for SoC audio on osk5912.
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
index d8d8d58..e09d1f2 100644
--- a/sound/soc/omap/Makefile
+++ b/sound/soc/omap/Makefile
@@ -7,5 +7,7 @@
# OMAP Machine Support
snd-soc-n810-objs := n810.o
+snd-soc-osk5912-objs := osk5912.o
obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
+obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o
diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c
index d166b6b..fae3ad3 100644
--- a/sound/soc/omap/n810.c
+++ b/sound/soc/omap/n810.c
@@ -247,9 +247,9 @@
int i, err;
/* Not connected */
- snd_soc_dapm_disable_pin(codec, "MONO_LOUT");
- snd_soc_dapm_disable_pin(codec, "HPLCOM");
- snd_soc_dapm_disable_pin(codec, "HPRCOM");
+ snd_soc_dapm_nc_pin(codec, "MONO_LOUT");
+ snd_soc_dapm_nc_pin(codec, "HPLCOM");
+ snd_soc_dapm_nc_pin(codec, "HPRCOM");
/* Add N810 specific controls */
for (i = 0; i < ARRAY_SIZE(aic33_n810_controls); i++) {
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c
index 35310e1..0a063a9 100644
--- a/sound/soc/omap/omap-mcbsp.c
+++ b/sound/soc/omap/omap-mcbsp.c
@@ -59,12 +59,7 @@
* Stream DMA parameters. DMA request line and port address are set runtime
* since they are different between OMAP1 and later OMAPs
*/
-static struct omap_pcm_dma_data omap_mcbsp_dai_dma_params[NUM_LINKS][2] = {
-{
- { .name = "I2S PCM Stereo out", },
- { .name = "I2S PCM Stereo in", },
-},
-};
+static struct omap_pcm_dma_data omap_mcbsp_dai_dma_params[NUM_LINKS][2];
#if defined(CONFIG_ARCH_OMAP15XX) || defined(CONFIG_ARCH_OMAP16XX)
static const int omap1_dma_reqs[][2] = {
@@ -84,11 +79,22 @@
static const int omap1_dma_reqs[][2] = {};
static const unsigned long omap1_mcbsp_port[][2] = {};
#endif
-#if defined(CONFIG_ARCH_OMAP2420)
-static const int omap2420_dma_reqs[][2] = {
+
+#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX)
+static const int omap24xx_dma_reqs[][2] = {
{ OMAP24XX_DMA_MCBSP1_TX, OMAP24XX_DMA_MCBSP1_RX },
{ OMAP24XX_DMA_MCBSP2_TX, OMAP24XX_DMA_MCBSP2_RX },
+#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX)
+ { OMAP24XX_DMA_MCBSP3_TX, OMAP24XX_DMA_MCBSP3_RX },
+ { OMAP24XX_DMA_MCBSP4_TX, OMAP24XX_DMA_MCBSP4_RX },
+ { OMAP24XX_DMA_MCBSP5_TX, OMAP24XX_DMA_MCBSP5_RX },
+#endif
};
+#else
+static const int omap24xx_dma_reqs[][2] = {};
+#endif
+
+#if defined(CONFIG_ARCH_OMAP2420)
static const unsigned long omap2420_mcbsp_port[][2] = {
{ OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR1,
OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR1 },
@@ -96,10 +102,43 @@
OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR1 },
};
#else
-static const int omap2420_dma_reqs[][2] = {};
static const unsigned long omap2420_mcbsp_port[][2] = {};
#endif
+#if defined(CONFIG_ARCH_OMAP2430)
+static const unsigned long omap2430_mcbsp_port[][2] = {
+ { OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR,
+ OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR },
+ { OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DXR,
+ OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR },
+ { OMAP2430_MCBSP3_BASE + OMAP_MCBSP_REG_DXR,
+ OMAP2430_MCBSP3_BASE + OMAP_MCBSP_REG_DRR },
+ { OMAP2430_MCBSP4_BASE + OMAP_MCBSP_REG_DXR,
+ OMAP2430_MCBSP4_BASE + OMAP_MCBSP_REG_DRR },
+ { OMAP2430_MCBSP5_BASE + OMAP_MCBSP_REG_DXR,
+ OMAP2430_MCBSP5_BASE + OMAP_MCBSP_REG_DRR },
+};
+#else
+static const unsigned long omap2430_mcbsp_port[][2] = {};
+#endif
+
+#if defined(CONFIG_ARCH_OMAP34XX)
+static const unsigned long omap34xx_mcbsp_port[][2] = {
+ { OMAP34XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR,
+ OMAP34XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR },
+ { OMAP34XX_MCBSP2_BASE + OMAP_MCBSP_REG_DXR,
+ OMAP34XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR },
+ { OMAP34XX_MCBSP3_BASE + OMAP_MCBSP_REG_DXR,
+ OMAP34XX_MCBSP3_BASE + OMAP_MCBSP_REG_DRR },
+ { OMAP34XX_MCBSP4_BASE + OMAP_MCBSP_REG_DXR,
+ OMAP34XX_MCBSP4_BASE + OMAP_MCBSP_REG_DRR },
+ { OMAP34XX_MCBSP5_BASE + OMAP_MCBSP_REG_DXR,
+ OMAP34XX_MCBSP5_BASE + OMAP_MCBSP_REG_DRR },
+};
+#else
+static const unsigned long omap34xx_mcbsp_port[][2] = {};
+#endif
+
static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -167,14 +206,19 @@
dma = omap1_dma_reqs[bus_id][substream->stream];
port = omap1_mcbsp_port[bus_id][substream->stream];
} else if (cpu_is_omap2420()) {
- dma = omap2420_dma_reqs[bus_id][substream->stream];
+ dma = omap24xx_dma_reqs[bus_id][substream->stream];
port = omap2420_mcbsp_port[bus_id][substream->stream];
+ } else if (cpu_is_omap2430()) {
+ dma = omap24xx_dma_reqs[bus_id][substream->stream];
+ port = omap2430_mcbsp_port[bus_id][substream->stream];
+ } else if (cpu_is_omap343x()) {
+ dma = omap24xx_dma_reqs[bus_id][substream->stream];
+ port = omap34xx_mcbsp_port[bus_id][substream->stream];
} else {
- /*
- * TODO: Add support for 2430 and 3430
- */
return -ENODEV;
}
+ omap_mcbsp_dai_dma_params[id][substream->stream].name =
+ substream->stream ? "Audio Capture" : "Audio Playback";
omap_mcbsp_dai_dma_params[id][substream->stream].dma_req = dma;
omap_mcbsp_dai_dma_params[id][substream->stream].port_addr = port;
cpu_dai->dma_data = &omap_mcbsp_dai_dma_params[id][substream->stream];
@@ -245,6 +289,11 @@
regs->rcr2 |= RDATDLY(1);
regs->xcr2 |= XDATDLY(1);
break;
+ case SND_SOC_DAIFMT_DSP_A:
+ /* 0-bit data delay */
+ regs->rcr2 |= RDATDLY(0);
+ regs->xcr2 |= XDATDLY(0);
+ break;
default:
/* Unsupported data format */
return -EINVAL;
@@ -310,7 +359,7 @@
int clk_id)
{
int sel_bit;
- u16 reg;
+ u16 reg, reg_devconf1 = OMAP243X_CONTROL_DEVCONF1;
if (cpu_class_is_omap1()) {
/* OMAP1's can use only external source clock */
@@ -320,6 +369,12 @@
return 0;
}
+ if (cpu_is_omap2420() && mcbsp_data->bus_id > 1)
+ return -EINVAL;
+
+ if (cpu_is_omap343x())
+ reg_devconf1 = OMAP343X_CONTROL_DEVCONF1;
+
switch (mcbsp_data->bus_id) {
case 0:
reg = OMAP2_CONTROL_DEVCONF0;
@@ -329,20 +384,26 @@
reg = OMAP2_CONTROL_DEVCONF0;
sel_bit = 6;
break;
- /* TODO: Support for ports 3 - 5 in OMAP2430 and OMAP34xx */
+ case 2:
+ reg = reg_devconf1;
+ sel_bit = 0;
+ break;
+ case 3:
+ reg = reg_devconf1;
+ sel_bit = 2;
+ break;
+ case 4:
+ reg = reg_devconf1;
+ sel_bit = 4;
+ break;
default:
return -EINVAL;
}
- if (cpu_class_is_omap2()) {
- if (clk_id == OMAP_MCBSP_SYSCLK_CLKS_FCLK) {
- omap_ctrl_writel(omap_ctrl_readl(reg) &
- ~(1 << sel_bit), reg);
- } else {
- omap_ctrl_writel(omap_ctrl_readl(reg) |
- (1 << sel_bit), reg);
- }
- }
+ if (clk_id == OMAP_MCBSP_SYSCLK_CLKS_FCLK)
+ omap_ctrl_writel(omap_ctrl_readl(reg) & ~(1 << sel_bit), reg);
+ else
+ omap_ctrl_writel(omap_ctrl_readl(reg) | (1 << sel_bit), reg);
return 0;
}
@@ -376,37 +437,49 @@
return err;
}
-struct snd_soc_dai omap_mcbsp_dai[NUM_LINKS] = {
-{
- .name = "omap-mcbsp-dai",
- .id = 0,
- .type = SND_SOC_DAI_I2S,
- .playback = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = OMAP_MCBSP_RATES,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = OMAP_MCBSP_RATES,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .ops = {
- .startup = omap_mcbsp_dai_startup,
- .shutdown = omap_mcbsp_dai_shutdown,
- .trigger = omap_mcbsp_dai_trigger,
- .hw_params = omap_mcbsp_dai_hw_params,
- },
- .dai_ops = {
- .set_fmt = omap_mcbsp_dai_set_dai_fmt,
- .set_clkdiv = omap_mcbsp_dai_set_clkdiv,
- .set_sysclk = omap_mcbsp_dai_set_dai_sysclk,
- },
- .private_data = &mcbsp_data[0].bus_id,
-},
+#define OMAP_MCBSP_DAI_BUILDER(link_id) \
+{ \
+ .name = "omap-mcbsp-dai-(link_id)", \
+ .id = (link_id), \
+ .type = SND_SOC_DAI_I2S, \
+ .playback = { \
+ .channels_min = 2, \
+ .channels_max = 2, \
+ .rates = OMAP_MCBSP_RATES, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, \
+ }, \
+ .capture = { \
+ .channels_min = 2, \
+ .channels_max = 2, \
+ .rates = OMAP_MCBSP_RATES, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, \
+ }, \
+ .ops = { \
+ .startup = omap_mcbsp_dai_startup, \
+ .shutdown = omap_mcbsp_dai_shutdown, \
+ .trigger = omap_mcbsp_dai_trigger, \
+ .hw_params = omap_mcbsp_dai_hw_params, \
+ }, \
+ .dai_ops = { \
+ .set_fmt = omap_mcbsp_dai_set_dai_fmt, \
+ .set_clkdiv = omap_mcbsp_dai_set_clkdiv, \
+ .set_sysclk = omap_mcbsp_dai_set_dai_sysclk, \
+ }, \
+ .private_data = &mcbsp_data[(link_id)].bus_id, \
+}
+
+struct snd_soc_dai omap_mcbsp_dai[] = {
+ OMAP_MCBSP_DAI_BUILDER(0),
+ OMAP_MCBSP_DAI_BUILDER(1),
+#if NUM_LINKS >= 3
+ OMAP_MCBSP_DAI_BUILDER(2),
+#endif
+#if NUM_LINKS == 5
+ OMAP_MCBSP_DAI_BUILDER(3),
+ OMAP_MCBSP_DAI_BUILDER(4),
+#endif
};
+
EXPORT_SYMBOL_GPL(omap_mcbsp_dai);
MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@nokia.com>");
diff --git a/sound/soc/omap/omap-mcbsp.h b/sound/soc/omap/omap-mcbsp.h
index ed8afb5..df7ad13 100644
--- a/sound/soc/omap/omap-mcbsp.h
+++ b/sound/soc/omap/omap-mcbsp.h
@@ -38,11 +38,17 @@
OMAP_MCBSP_CLKGDV, /* Sample rate generator divider */
};
-/*
- * REVISIT: Preparation for the ASoC v2. Let the number of available links to
- * be same than number of McBSP ports found in OMAP(s) we are compiling for.
- */
-#define NUM_LINKS 1
+#if defined(CONFIG_ARCH_OMAP2420)
+#define NUM_LINKS 2
+#endif
+#if defined(CONFIG_ARCH_OMAP15XX) || defined(CONFIG_ARCH_OMAP16XX)
+#undef NUM_LINKS
+#define NUM_LINKS 3
+#endif
+#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX)
+#undef NUM_LINKS
+#define NUM_LINKS 5
+#endif
extern struct snd_soc_dai omap_mcbsp_dai[NUM_LINKS];
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c
index 690bfea..e9084fd 100644
--- a/sound/soc/omap/omap-pcm.c
+++ b/sound/soc/omap/omap-pcm.c
@@ -97,7 +97,7 @@
prtd->dma_data = dma_data;
err = omap_request_dma(dma_data->dma_req, dma_data->name,
omap_pcm_dma_irq, substream, &prtd->dma_ch);
- if (!cpu_is_omap1510()) {
+ if (!err & !cpu_is_omap1510()) {
/*
* Link channel with itself so DMA doesn't need any
* reprogramming while looping the buffer
@@ -147,12 +147,14 @@
dma_params.src_or_dst_synch = OMAP_DMA_DST_SYNC;
dma_params.src_start = runtime->dma_addr;
dma_params.dst_start = dma_data->port_addr;
+ dma_params.dst_port = OMAP_DMA_PORT_MPUI;
} else {
dma_params.src_amode = OMAP_DMA_AMODE_CONSTANT;
dma_params.dst_amode = OMAP_DMA_AMODE_POST_INC;
dma_params.src_or_dst_synch = OMAP_DMA_SRC_SYNC;
dma_params.src_start = dma_data->port_addr;
dma_params.dst_start = runtime->dma_addr;
+ dma_params.src_port = OMAP_DMA_PORT_MPUI;
}
/*
* Set DMA transfer frame size equal to ALSA period size and frame
diff --git a/sound/soc/omap/osk5912.c b/sound/soc/omap/osk5912.c
new file mode 100644
index 0000000..0fe7337
--- /dev/null
+++ b/sound/soc/omap/osk5912.c
@@ -0,0 +1,232 @@
+/*
+ * osk5912.c -- SoC audio for OSK 5912
+ *
+ * Copyright (C) 2008 Mistral Solutions
+ *
+ * Contact: Arun KS <arunks@mistralsolutions.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <linux/gpio.h>
+#include <mach/mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+#include "../codecs/tlv320aic23.h"
+
+#define CODEC_CLOCK 12000000
+
+static struct clk *tlv320aic23_mclk;
+
+static int osk_startup(struct snd_pcm_substream *substream)
+{
+ return clk_enable(tlv320aic23_mclk);
+}
+
+static void osk_shutdown(struct snd_pcm_substream *substream)
+{
+ clk_disable(tlv320aic23_mclk);
+}
+
+static int osk_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ int err;
+
+ /* Set codec DAI configuration */
+ err = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_DSP_A |
+ SND_SOC_DAIFMT_NB_IF |
+ SND_SOC_DAIFMT_CBM_CFM);
+ if (err < 0) {
+ printk(KERN_ERR "can't set codec DAI configuration\n");
+ return err;
+ }
+
+ /* Set cpu DAI configuration */
+ err = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_DSP_A |
+ SND_SOC_DAIFMT_NB_IF |
+ SND_SOC_DAIFMT_CBM_CFM);
+ if (err < 0) {
+ printk(KERN_ERR "can't set cpu DAI configuration\n");
+ return err;
+ }
+
+ /* Set the codec system clock for DAC and ADC */
+ err =
+ snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK, SND_SOC_CLOCK_IN);
+
+ if (err < 0) {
+ printk(KERN_ERR "can't set codec system clock\n");
+ return err;
+ }
+
+ return err;
+}
+
+static struct snd_soc_ops osk_ops = {
+ .startup = osk_startup,
+ .hw_params = osk_hw_params,
+ .shutdown = osk_shutdown,
+};
+
+static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_LINE("Line In", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ {"Headphone Jack", NULL, "LHPOUT"},
+ {"Headphone Jack", NULL, "RHPOUT"},
+
+ {"LLINEIN", NULL, "Line In"},
+ {"RLINEIN", NULL, "Line In"},
+
+ {"MICIN", NULL, "Mic Jack"},
+};
+
+static int osk_tlv320aic23_init(struct snd_soc_codec *codec)
+{
+
+ /* Add osk5912 specific widgets */
+ snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets,
+ ARRAY_SIZE(tlv320aic23_dapm_widgets));
+
+ /* Set up osk5912 specific audio path audio_map */
+ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+ snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+ snd_soc_dapm_enable_pin(codec, "Line In");
+ snd_soc_dapm_enable_pin(codec, "Mic Jack");
+
+ snd_soc_dapm_sync(codec);
+
+ return 0;
+}
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link osk_dai = {
+ .name = "TLV320AIC23",
+ .stream_name = "AIC23",
+ .cpu_dai = &omap_mcbsp_dai[0],
+ .codec_dai = &tlv320aic23_dai,
+ .init = osk_tlv320aic23_init,
+ .ops = &osk_ops,
+};
+
+/* Audio machine driver */
+static struct snd_soc_machine snd_soc_machine_osk = {
+ .name = "OSK5912",
+ .dai_link = &osk_dai,
+ .num_links = 1,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device osk_snd_devdata = {
+ .machine = &snd_soc_machine_osk,
+ .platform = &omap_soc_platform,
+ .codec_dev = &soc_codec_dev_tlv320aic23,
+};
+
+static struct platform_device *osk_snd_device;
+
+static int __init osk_soc_init(void)
+{
+ int err;
+ u32 curRate;
+ struct device *dev;
+
+ if (!(machine_is_omap_osk()))
+ return -ENODEV;
+
+ osk_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!osk_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(osk_snd_device, &osk_snd_devdata);
+ osk_snd_devdata.dev = &osk_snd_device->dev;
+ *(unsigned int *)osk_dai.cpu_dai->private_data = 0; /* McBSP1 */
+ err = platform_device_add(osk_snd_device);
+ if (err)
+ goto err1;
+
+ dev = &osk_snd_device->dev;
+
+ tlv320aic23_mclk = clk_get(dev, "mclk");
+ if (IS_ERR(tlv320aic23_mclk)) {
+ printk(KERN_ERR "Could not get mclk clock\n");
+ return -ENODEV;
+ }
+
+ if (clk_get_usecount(tlv320aic23_mclk) > 0) {
+ /* MCLK is already in use */
+ printk(KERN_WARNING
+ "MCLK in use at %d Hz. We change it to %d Hz\n",
+ (uint) clk_get_rate(tlv320aic23_mclk), CODEC_CLOCK);
+ }
+
+ /*
+ * Configure 12 MHz output on MCLK.
+ */
+ curRate = (uint) clk_get_rate(tlv320aic23_mclk);
+ if (curRate != CODEC_CLOCK) {
+ if (clk_set_rate(tlv320aic23_mclk, CODEC_CLOCK)) {
+ printk(KERN_ERR "Cannot set MCLK for AIC23 CODEC\n");
+ err = -ECANCELED;
+ goto err1;
+ }
+ }
+
+ printk(KERN_INFO "MCLK = %d [%d], usecount = %d\n",
+ (uint) clk_get_rate(tlv320aic23_mclk), CODEC_CLOCK,
+ clk_get_usecount(tlv320aic23_mclk));
+
+ return 0;
+err1:
+ clk_put(tlv320aic23_mclk);
+ platform_device_del(osk_snd_device);
+ platform_device_put(osk_snd_device);
+
+ return err;
+
+}
+
+static void __exit osk_soc_exit(void)
+{
+ platform_device_unregister(osk_snd_device);
+}
+
+module_init(osk_soc_init);
+module_exit(osk_soc_exit);
+
+MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
+MODULE_DESCRIPTION("ALSA SoC OSK 5912");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c
index 1a8373d..2718eaf 100644
--- a/sound/soc/pxa/corgi.c
+++ b/sound/soc/pxa/corgi.c
@@ -4,7 +4,7 @@
* Copyright 2005 Wolfson Microelectronics PLC.
* Copyright 2005 Openedhand Ltd.
*
- * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
* Richard Purdie <richard@openedhand.com>
*
* This program is free software; you can redistribute it and/or modify it
@@ -281,8 +281,8 @@
{
int i, err;
- snd_soc_dapm_disable_pin(codec, "LLINEIN");
- snd_soc_dapm_disable_pin(codec, "RLINEIN");
+ snd_soc_dapm_nc_pin(codec, "LLINEIN");
+ snd_soc_dapm_nc_pin(codec, "RLINEIN");
/* Add corgi specific controls */
for (i = 0; i < ARRAY_SIZE(wm8731_corgi_controls); i++) {
diff --git a/sound/soc/pxa/em-x270.c b/sound/soc/pxa/em-x270.c
index d9c3f7b..e6ff692 100644
--- a/sound/soc/pxa/em-x270.c
+++ b/sound/soc/pxa/em-x270.c
@@ -9,7 +9,7 @@
* Copyright 2005 Wolfson Microelectronics PLC.
* Copyright 2005 Openedhand Ltd.
*
- * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
* Richard Purdie <richard@openedhand.com>
*
* This program is free software; you can redistribute it and/or modify it
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c
index f84f7d8..4d9930c 100644
--- a/sound/soc/pxa/poodle.c
+++ b/sound/soc/pxa/poodle.c
@@ -4,7 +4,7 @@
* Copyright 2005 Wolfson Microelectronics PLC.
* Copyright 2005 Openedhand Ltd.
*
- * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
* Richard Purdie <richard@openedhand.com>
*
* This program is free software; you can redistribute it and/or modify it
@@ -242,8 +242,8 @@
{
int i, err;
- snd_soc_dapm_disable_pin(codec, "LLINEIN");
- snd_soc_dapm_disable_pin(codec, "RLINEIN");
+ snd_soc_dapm_nc_pin(codec, "LLINEIN");
+ snd_soc_dapm_nc_pin(codec, "RLINEIN");
snd_soc_dapm_enable_pin(codec, "MICIN");
/* Add poodle specific controls */
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c
index 2fb5829..e758034 100644
--- a/sound/soc/pxa/pxa2xx-i2s.c
+++ b/sound/soc/pxa/pxa2xx-i2s.c
@@ -3,7 +3,7 @@
*
* Copyright 2005 Wolfson Microelectronics PLC.
* Author: Liam Girdwood
- * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ * lrg@slimlogic.co.uk
*
* 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
@@ -405,6 +405,6 @@
module_exit(pxa2xx_i2s_exit);
/* Module information */
-MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk");
MODULE_DESCRIPTION("pxa2xx I2S SoC Interface");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c
index 9a70b00..d307b67 100644
--- a/sound/soc/pxa/spitz.c
+++ b/sound/soc/pxa/spitz.c
@@ -4,7 +4,7 @@
* Copyright 2005 Wolfson Microelectronics PLC.
* Copyright 2005 Openedhand Ltd.
*
- * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
* Richard Purdie <richard@openedhand.com>
*
* This program is free software; you can redistribute it and/or modify it
@@ -281,13 +281,13 @@
int i, err;
/* NC codec pins */
- snd_soc_dapm_disable_pin(codec, "RINPUT1");
- snd_soc_dapm_disable_pin(codec, "LINPUT2");
- snd_soc_dapm_disable_pin(codec, "RINPUT2");
- snd_soc_dapm_disable_pin(codec, "LINPUT3");
- snd_soc_dapm_disable_pin(codec, "RINPUT3");
- snd_soc_dapm_disable_pin(codec, "OUT3");
- snd_soc_dapm_disable_pin(codec, "MONO1");
+ snd_soc_dapm_nc_pin(codec, "RINPUT1");
+ snd_soc_dapm_nc_pin(codec, "LINPUT2");
+ snd_soc_dapm_nc_pin(codec, "RINPUT2");
+ snd_soc_dapm_nc_pin(codec, "LINPUT3");
+ snd_soc_dapm_nc_pin(codec, "RINPUT3");
+ snd_soc_dapm_nc_pin(codec, "OUT3");
+ snd_soc_dapm_nc_pin(codec, "MONO1");
/* Add spitz specific controls */
for (i = 0; i < ARRAY_SIZE(wm8750_spitz_controls); i++) {
diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c
index 2baaa75..afefe41 100644
--- a/sound/soc/pxa/tosa.c
+++ b/sound/soc/pxa/tosa.c
@@ -4,7 +4,7 @@
* Copyright 2005 Wolfson Microelectronics PLC.
* Copyright 2005 Openedhand Ltd.
*
- * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com>
+ * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
* Richard Purdie <richard@openedhand.com>
*
* This program is free software; you can redistribute it and/or modify it
@@ -190,8 +190,8 @@
{
int i, err;
- snd_soc_dapm_disable_pin(codec, "OUT3");
- snd_soc_dapm_disable_pin(codec, "MONOOUT");
+ snd_soc_dapm_nc_pin(codec, "OUT3");
+ snd_soc_dapm_nc_pin(codec, "MONOOUT");
/* add tosa specific controls */
for (i = 0; i < ARRAY_SIZE(tosa_controls); i++) {
diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c
index 73a50e9..87ddfef 100644
--- a/sound/soc/s3c24xx/neo1973_wm8753.c
+++ b/sound/soc/s3c24xx/neo1973_wm8753.c
@@ -511,21 +511,20 @@
DBG("Entered %s\n", __func__);
/* set up NC codec pins */
- snd_soc_dapm_disable_pin(codec, "LOUT2");
- snd_soc_dapm_disable_pin(codec, "ROUT2");
- snd_soc_dapm_disable_pin(codec, "OUT3");
- snd_soc_dapm_disable_pin(codec, "OUT4");
- snd_soc_dapm_disable_pin(codec, "LINE1");
- snd_soc_dapm_disable_pin(codec, "LINE2");
-
-
- /* set endpoints to default mode */
- set_scenario_endpoints(codec, NEO_AUDIO_OFF);
+ snd_soc_dapm_nc_pin(codec, "LOUT2");
+ snd_soc_dapm_nc_pin(codec, "ROUT2");
+ snd_soc_dapm_nc_pin(codec, "OUT3");
+ snd_soc_dapm_nc_pin(codec, "OUT4");
+ snd_soc_dapm_nc_pin(codec, "LINE1");
+ snd_soc_dapm_nc_pin(codec, "LINE2");
/* Add neo1973 specific widgets */
snd_soc_dapm_new_controls(codec, wm8753_dapm_widgets,
ARRAY_SIZE(wm8753_dapm_widgets));
+ /* set endpoints to default mode */
+ set_scenario_endpoints(codec, NEO_AUDIO_OFF);
+
/* add neo1973 specific controls */
for (i = 0; i < ARRAY_SIZE(wm8753_neo1973_controls); i++) {
err = snd_ctl_add(codec->card,
@@ -603,6 +602,8 @@
{
DBG("Entered %s\n", __func__);
+ i2c = client;
+
lm4857_write_regs();
return 0;
}
@@ -611,6 +612,8 @@
{
DBG("Entered %s\n", __func__);
+ i2c = NULL;
+
return 0;
}
@@ -650,7 +653,7 @@
}
static const struct i2c_device_id lm4857_i2c_id[] = {
- { "neo1973_lm4857", 0 }
+ { "neo1973_lm4857", 0 },
{ }
};
@@ -668,48 +671,6 @@
};
static struct platform_device *neo1973_snd_device;
-static struct i2c_client *lm4857_client;
-
-static int __init neo1973_add_lm4857_device(struct platform_device *pdev,
- int i2c_bus,
- unsigned short i2c_address)
-{
- struct i2c_board_info info;
- struct i2c_adapter *adapter;
- struct i2c_client *client;
- int ret;
-
- ret = i2c_add_driver(&lm4857_i2c_driver);
- if (ret != 0) {
- dev_err(&pdev->dev, "can't add lm4857 driver\n");
- return ret;
- }
-
- memset(&info, 0, sizeof(struct i2c_board_info));
- info.addr = i2c_address;
- strlcpy(info.type, "neo1973_lm4857", I2C_NAME_SIZE);
-
- adapter = i2c_get_adapter(i2c_bus);
- if (!adapter) {
- dev_err(&pdev->dev, "can't get i2c adapter %d\n", i2c_bus);
- goto err_driver;
- }
-
- client = i2c_new_device(adapter, &info);
- i2c_put_adapter(adapter);
- if (!client) {
- dev_err(&pdev->dev, "can't add lm4857 device at 0x%x\n",
- (unsigned int)info.addr);
- goto err_driver;
- }
-
- lm4857_client = client;
- return 0;
-
-err_driver:
- i2c_del_driver(&lm4857_i2c_driver);
- return -ENODEV;
-}
static int __init neo1973_init(void)
{
@@ -736,8 +697,8 @@
return ret;
}
- ret = neo1973_add_lm4857_device(neo1973_snd_device,
- neo1973_wm8753_setup, 0x7C);
+ ret = i2c_add_driver(&lm4857_i2c_driver);
+
if (ret != 0)
platform_device_unregister(neo1973_snd_device);
@@ -748,7 +709,6 @@
{
DBG("Entered %s\n", __func__);
- i2c_unregister_device(lm4857_client);
i2c_del_driver(&lm4857_i2c_driver);
platform_device_unregister(neo1973_snd_device);
}
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index ad38113..462e635d 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -4,8 +4,7 @@
* Copyright 2005 Wolfson Microelectronics PLC.
* Copyright 2005 Openedhand Ltd.
*
- * Author: Liam Girdwood
- * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
* with code, comments and ideas from :-
* Richard Purdie <richard@openedhand.com>
*
@@ -1886,7 +1885,7 @@
module_exit(snd_soc_exit);
/* Module information */
-MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk");
MODULE_DESCRIPTION("ALSA SoC Core");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:soc-audio");
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 9ca9c08..efbd0b3 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -2,8 +2,7 @@
* soc-dapm.c -- ALSA SoC Dynamic Audio Power Management
*
* Copyright 2005 Wolfson Microelectronics PLC.
- * Author: Liam Girdwood
- * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
*
* 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
@@ -1484,6 +1483,26 @@
EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin);
/**
+ * snd_soc_dapm_nc_pin - permanently disable pin.
+ * @codec: SoC codec
+ * @pin: pin name
+ *
+ * Marks the specified pin as being not connected, disabling it along
+ * any parent or child widgets. At present this is identical to
+ * snd_soc_dapm_disable_pin() but in future it will be extended to do
+ * additional things such as disabling controls which only affect
+ * paths through the pin.
+ *
+ * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
+ * do any widget power switching.
+ */
+int snd_soc_dapm_nc_pin(struct snd_soc_codec *codec, char *pin)
+{
+ return snd_soc_dapm_set_pin(codec, pin, 0);
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin);
+
+/**
* snd_soc_dapm_get_pin_status - get audio pin status
* @codec: audio codec
* @pin: audio signal pin endpoint (or start point)
@@ -1521,6 +1540,6 @@
EXPORT_SYMBOL_GPL(snd_soc_dapm_free);
/* Module information */
-MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk");
MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC");
MODULE_LICENSE("GPL");
diff --git a/sound/sparc/amd7930.c b/sound/sparc/amd7930.c
index 49acee0..f87933e 100644
--- a/sound/sparc/amd7930.c
+++ b/sound/sparc/amd7930.c
@@ -1,6 +1,6 @@
/*
* Driver for AMD7930 sound chips found on Sparcs.
- * Copyright (C) 2002 David S. Miller <davem@redhat.com>
+ * Copyright (C) 2002, 2008 David S. Miller <davem@davemloft.net>
*
* Based entirely upon drivers/sbus/audio/amd7930.c which is:
* Copyright (C) 1996,1997 Thomas K. Dyas (tdyas@eden.rutgers.edu)
@@ -35,6 +35,8 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/moduleparam.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -44,7 +46,6 @@
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/sbus.h>
#include <asm/prom.h>
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
@@ -335,8 +336,8 @@
int pgain;
int mgain;
+ struct of_device *op;
unsigned int irq;
- unsigned int regs_size;
struct snd_amd7930 *next;
};
@@ -905,13 +906,16 @@
static int snd_amd7930_free(struct snd_amd7930 *amd)
{
+ struct of_device *op = amd->op;
+
amd7930_idle(amd);
if (amd->irq)
free_irq(amd->irq, amd);
if (amd->regs)
- sbus_iounmap(amd->regs, amd->regs_size);
+ of_iounmap(&op->resource[0], amd->regs,
+ resource_size(&op->resource[0]));
kfree(amd);
@@ -930,13 +934,12 @@
};
static int __devinit snd_amd7930_create(struct snd_card *card,
- struct resource *rp,
- unsigned int reg_size,
+ struct of_device *op,
int irq, int dev,
struct snd_amd7930 **ramd)
{
- unsigned long flags;
struct snd_amd7930 *amd;
+ unsigned long flags;
int err;
*ramd = NULL;
@@ -946,9 +949,10 @@
spin_lock_init(&amd->lock);
amd->card = card;
- amd->regs_size = reg_size;
+ amd->op = op;
- amd->regs = sbus_ioremap(rp, 0, amd->regs_size, "amd7930");
+ amd->regs = of_ioremap(&op->resource[0], 0,
+ resource_size(&op->resource[0]), "amd7930");
if (!amd->regs) {
snd_printk("amd7930-%d: Unable to map chip registers.\n", dev);
return -EIO;
@@ -997,12 +1001,15 @@
return 0;
}
-static int __devinit amd7930_attach_common(struct resource *rp, int irq)
+static int __devinit amd7930_sbus_probe(struct of_device *op, const struct of_device_id *match)
{
+ struct resource *rp = &op->resource[0];
static int dev_num;
struct snd_card *card;
struct snd_amd7930 *amd;
- int err;
+ int err, irq;
+
+ irq = op->irqs[0];
if (dev_num >= SNDRV_CARDS)
return -ENODEV;
@@ -1023,8 +1030,7 @@
(unsigned long long)rp->start,
irq);
- if ((err = snd_amd7930_create(card, rp,
- (rp->end - rp->start) + 1,
+ if ((err = snd_amd7930_create(card, op,
irq, dev_num, &amd)) < 0)
goto out_err;
@@ -1049,43 +1055,7 @@
return err;
}
-static int __devinit amd7930_obio_attach(struct device_node *dp)
-{
- const struct linux_prom_registers *regs;
- const struct linux_prom_irqs *irqp;
- struct resource res, *rp;
- int len;
-
- irqp = of_get_property(dp, "intr", &len);
- if (!irqp) {
- snd_printk("%s: Firmware node lacks IRQ property.\n",
- dp->full_name);
- return -ENODEV;
- }
-
- regs = of_get_property(dp, "reg", &len);
- if (!regs) {
- snd_printk("%s: Firmware node lacks register property.\n",
- dp->full_name);
- return -ENODEV;
- }
-
- rp = &res;
- rp->start = regs->phys_addr;
- rp->end = rp->start + regs->reg_size - 1;
- rp->flags = IORESOURCE_IO | (regs->which_io & 0xff);
-
- return amd7930_attach_common(rp, irqp->pri);
-}
-
-static int __devinit amd7930_sbus_probe(struct of_device *dev, const struct of_device_id *match)
-{
- struct sbus_dev *sdev = to_sbus_device(&dev->dev);
-
- return amd7930_attach_common(&sdev->resource[0], sdev->irqs[0]);
-}
-
-static struct of_device_id amd7930_match[] = {
+static const struct of_device_id amd7930_match[] = {
{
.name = "audio",
},
@@ -1100,20 +1070,7 @@
static int __init amd7930_init(void)
{
- struct device_node *dp;
-
- /* Try to find the sun4c "audio" node first. */
- dp = of_find_node_by_path("/");
- dp = dp->child;
- while (dp) {
- if (!strcmp(dp->name, "audio"))
- amd7930_obio_attach(dp);
-
- dp = dp->sibling;
- }
-
- /* Probe each SBUS for amd7930 chips. */
- return of_register_driver(&amd7930_sbus_driver, &sbus_bus_type);
+ return of_register_driver(&amd7930_sbus_driver, &of_bus_type);
}
static void __exit amd7930_exit(void)
diff --git a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c
index 791d2fb..d44bf98 100644
--- a/sound/sparc/cs4231.c
+++ b/sound/sparc/cs4231.c
@@ -1,6 +1,6 @@
/*
* Driver for CS4231 sound chips found on Sparcs.
- * Copyright (C) 2002 David S. Miller <davem@redhat.com>
+ * Copyright (C) 2002, 2008 David S. Miller <davem@davemloft.net>
*
* Based entirely upon drivers/sbus/audio/cs4231.c which is:
* Copyright (C) 1996, 1997, 1998 Derrick J Brashear (shadow@andrew.cmu.edu)
@@ -17,7 +17,8 @@
#include <linux/moduleparam.h>
#include <linux/irq.h>
#include <linux/io.h>
-
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -29,13 +30,12 @@
#ifdef CONFIG_SBUS
#define SBUS_SUPPORT
-#include <asm/sbus.h>
#endif
#if defined(CONFIG_PCI) && defined(CONFIG_SPARC64)
#define EBUS_SUPPORT
#include <linux/pci.h>
-#include <asm/ebus.h>
+#include <asm/ebus_dma.h>
#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
@@ -70,8 +70,6 @@
int (*request)(struct cs4231_dma_control *dma_cont,
dma_addr_t bus_addr, size_t len);
unsigned int (*address)(struct cs4231_dma_control *dma_cont);
- void (*preallocate)(struct snd_cs4231 *chip,
- struct snd_pcm *pcm);
#ifdef EBUS_SUPPORT
struct ebus_dma_info ebus_info;
#endif
@@ -114,21 +112,12 @@
struct mutex mce_mutex; /* mutex for mce register */
struct mutex open_mutex; /* mutex for ALSA open/close */
- union {
-#ifdef SBUS_SUPPORT
- struct sbus_dev *sdev;
-#endif
-#ifdef EBUS_SUPPORT
- struct pci_dev *pdev;
-#endif
- } dev_u;
+ struct of_device *op;
unsigned int irq[2];
unsigned int regs_size;
struct snd_cs4231 *next;
};
-static struct snd_cs4231 *cs4231_list;
-
/* Eventually we can use sound/isa/cs423x/cs4231_lib.c directly, but for
* now.... -DaveM
*/
@@ -267,27 +256,19 @@
static u8 __cs4231_readb(struct snd_cs4231 *cp, void __iomem *reg_addr)
{
-#ifdef EBUS_SUPPORT
if (cp->flags & CS4231_FLAG_EBUS)
return readb(reg_addr);
else
-#endif
-#ifdef SBUS_SUPPORT
return sbus_readb(reg_addr);
-#endif
}
static void __cs4231_writeb(struct snd_cs4231 *cp, u8 val,
void __iomem *reg_addr)
{
-#ifdef EBUS_SUPPORT
if (cp->flags & CS4231_FLAG_EBUS)
return writeb(val, reg_addr);
else
-#endif
-#ifdef SBUS_SUPPORT
return sbus_writeb(val, reg_addr);
-#endif
}
/*
@@ -1258,7 +1239,9 @@
pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
strcpy(pcm->name, "CS4231");
- chip->p_dma.preallocate(chip, pcm);
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->op->dev,
+ 64 * 1024, 128 * 1024);
chip->pcm = pcm;
@@ -1627,8 +1610,7 @@
if (err < 0)
goto out_err;
- chip->next = cs4231_list;
- cs4231_list = chip;
+ dev_set_drvdata(&chip->op->dev, chip);
dev++;
return 0;
@@ -1783,24 +1765,19 @@
return sbus_readl(base->regs + base->dir + APCVA);
}
-static void sbus_dma_preallocate(struct snd_cs4231 *chip, struct snd_pcm *pcm)
-{
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_SBUS,
- snd_dma_sbus_data(chip->dev_u.sdev),
- 64 * 1024, 128 * 1024);
-}
-
/*
* Init and exit routines
*/
static int snd_cs4231_sbus_free(struct snd_cs4231 *chip)
{
+ struct of_device *op = chip->op;
+
if (chip->irq[0])
free_irq(chip->irq[0], chip);
if (chip->port)
- sbus_iounmap(chip->port, chip->regs_size);
+ of_iounmap(&op->resource[0], chip->port, chip->regs_size);
return 0;
}
@@ -1817,7 +1794,7 @@
};
static int __init snd_cs4231_sbus_create(struct snd_card *card,
- struct sbus_dev *sdev,
+ struct of_device *op,
int dev)
{
struct snd_cs4231 *chip = card->private_data;
@@ -1828,13 +1805,13 @@
spin_lock_init(&chip->p_dma.sbus_info.lock);
mutex_init(&chip->mce_mutex);
mutex_init(&chip->open_mutex);
- chip->dev_u.sdev = sdev;
- chip->regs_size = sdev->reg_addrs[0].reg_size;
+ chip->op = op;
+ chip->regs_size = resource_size(&op->resource[0]);
memcpy(&chip->image, &snd_cs4231_original_image,
sizeof(snd_cs4231_original_image));
- chip->port = sbus_ioremap(&sdev->resource[0], 0,
- chip->regs_size, "cs4231");
+ chip->port = of_ioremap(&op->resource[0], 0,
+ chip->regs_size, "cs4231");
if (!chip->port) {
snd_printdd("cs4231-%d: Unable to map chip registers.\n", dev);
return -EIO;
@@ -1849,22 +1826,20 @@
chip->p_dma.enable = sbus_dma_enable;
chip->p_dma.request = sbus_dma_request;
chip->p_dma.address = sbus_dma_addr;
- chip->p_dma.preallocate = sbus_dma_preallocate;
chip->c_dma.prepare = sbus_dma_prepare;
chip->c_dma.enable = sbus_dma_enable;
chip->c_dma.request = sbus_dma_request;
chip->c_dma.address = sbus_dma_addr;
- chip->c_dma.preallocate = sbus_dma_preallocate;
- if (request_irq(sdev->irqs[0], snd_cs4231_sbus_interrupt,
+ if (request_irq(op->irqs[0], snd_cs4231_sbus_interrupt,
IRQF_SHARED, "cs4231", chip)) {
snd_printdd("cs4231-%d: Unable to grab SBUS IRQ %d\n",
- dev, sdev->irqs[0]);
+ dev, op->irqs[0]);
snd_cs4231_sbus_free(chip);
return -EBUSY;
}
- chip->irq[0] = sdev->irqs[0];
+ chip->irq[0] = op->irqs[0];
if (snd_cs4231_probe(chip) < 0) {
snd_cs4231_sbus_free(chip);
@@ -1881,9 +1856,9 @@
return 0;
}
-static int __init cs4231_sbus_attach(struct sbus_dev *sdev)
+static int __devinit cs4231_sbus_probe(struct of_device *op, const struct of_device_id *match)
{
- struct resource *rp = &sdev->resource[0];
+ struct resource *rp = &op->resource[0];
struct snd_card *card;
int err;
@@ -1895,9 +1870,9 @@
card->shortname,
rp->flags & 0xffL,
(unsigned long long)rp->start,
- sdev->irqs[0]);
+ op->irqs[0]);
- err = snd_cs4231_sbus_create(card, sdev, dev);
+ err = snd_cs4231_sbus_create(card, op, dev);
if (err < 0) {
snd_card_free(card);
return err;
@@ -1950,30 +1925,25 @@
return ebus_dma_addr(&dma_cont->ebus_info);
}
-static void _ebus_dma_preallocate(struct snd_cs4231 *chip, struct snd_pcm *pcm)
-{
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->dev_u.pdev),
- 64*1024, 128*1024);
-}
-
/*
* Init and exit routines
*/
static int snd_cs4231_ebus_free(struct snd_cs4231 *chip)
{
+ struct of_device *op = chip->op;
+
if (chip->c_dma.ebus_info.regs) {
ebus_dma_unregister(&chip->c_dma.ebus_info);
- iounmap(chip->c_dma.ebus_info.regs);
+ of_iounmap(&op->resource[2], chip->c_dma.ebus_info.regs, 0x10);
}
if (chip->p_dma.ebus_info.regs) {
ebus_dma_unregister(&chip->p_dma.ebus_info);
- iounmap(chip->p_dma.ebus_info.regs);
+ of_iounmap(&op->resource[1], chip->p_dma.ebus_info.regs, 0x10);
}
if (chip->port)
- iounmap(chip->port);
+ of_iounmap(&op->resource[0], chip->port, 0x10);
return 0;
}
@@ -1990,7 +1960,7 @@
};
static int __init snd_cs4231_ebus_create(struct snd_card *card,
- struct linux_ebus_device *edev,
+ struct of_device *op,
int dev)
{
struct snd_cs4231 *chip = card->private_data;
@@ -2002,35 +1972,35 @@
mutex_init(&chip->mce_mutex);
mutex_init(&chip->open_mutex);
chip->flags |= CS4231_FLAG_EBUS;
- chip->dev_u.pdev = edev->bus->self;
+ chip->op = op;
memcpy(&chip->image, &snd_cs4231_original_image,
sizeof(snd_cs4231_original_image));
strcpy(chip->c_dma.ebus_info.name, "cs4231(capture)");
chip->c_dma.ebus_info.flags = EBUS_DMA_FLAG_USE_EBDMA_HANDLER;
chip->c_dma.ebus_info.callback = snd_cs4231_ebus_capture_callback;
chip->c_dma.ebus_info.client_cookie = chip;
- chip->c_dma.ebus_info.irq = edev->irqs[0];
+ chip->c_dma.ebus_info.irq = op->irqs[0];
strcpy(chip->p_dma.ebus_info.name, "cs4231(play)");
chip->p_dma.ebus_info.flags = EBUS_DMA_FLAG_USE_EBDMA_HANDLER;
chip->p_dma.ebus_info.callback = snd_cs4231_ebus_play_callback;
chip->p_dma.ebus_info.client_cookie = chip;
- chip->p_dma.ebus_info.irq = edev->irqs[1];
+ chip->p_dma.ebus_info.irq = op->irqs[1];
chip->p_dma.prepare = _ebus_dma_prepare;
chip->p_dma.enable = _ebus_dma_enable;
chip->p_dma.request = _ebus_dma_request;
chip->p_dma.address = _ebus_dma_addr;
- chip->p_dma.preallocate = _ebus_dma_preallocate;
chip->c_dma.prepare = _ebus_dma_prepare;
chip->c_dma.enable = _ebus_dma_enable;
chip->c_dma.request = _ebus_dma_request;
chip->c_dma.address = _ebus_dma_addr;
- chip->c_dma.preallocate = _ebus_dma_preallocate;
- chip->port = ioremap(edev->resource[0].start, 0x10);
- chip->p_dma.ebus_info.regs = ioremap(edev->resource[1].start, 0x10);
- chip->c_dma.ebus_info.regs = ioremap(edev->resource[2].start, 0x10);
+ chip->port = of_ioremap(&op->resource[0], 0, 0x10, "cs4231");
+ chip->p_dma.ebus_info.regs =
+ of_ioremap(&op->resource[1], 0, 0x10, "cs4231_pdma");
+ chip->c_dma.ebus_info.regs =
+ of_ioremap(&op->resource[2], 0, 0x10, "cs4231_cdma");
if (!chip->port || !chip->p_dma.ebus_info.regs ||
!chip->c_dma.ebus_info.regs) {
snd_cs4231_ebus_free(chip);
@@ -2078,7 +2048,7 @@
return 0;
}
-static int __init cs4231_ebus_attach(struct linux_ebus_device *edev)
+static int __devinit cs4231_ebus_probe(struct of_device *op, const struct of_device_id *match)
{
struct snd_card *card;
int err;
@@ -2089,10 +2059,10 @@
sprintf(card->longname, "%s at 0x%lx, irq %d",
card->shortname,
- edev->resource[0].start,
- edev->irqs[0]);
+ op->resource[0].start,
+ op->irqs[0]);
- err = snd_cs4231_ebus_create(card, edev, dev);
+ err = snd_cs4231_ebus_create(card, op, dev);
if (err < 0) {
snd_card_free(card);
return err;
@@ -2102,68 +2072,57 @@
}
#endif
+static int __devinit cs4231_probe(struct of_device *op, const struct of_device_id *match)
+{
+#ifdef EBUS_SUPPORT
+ if (!strcmp(op->node->parent->name, "ebus"))
+ return cs4231_ebus_probe(op, match);
+#endif
+#ifdef SBUS_SUPPORT
+ if (!strcmp(op->node->parent->name, "sbus") ||
+ !strcmp(op->node->parent->name, "sbi"))
+ return cs4231_sbus_probe(op, match);
+#endif
+ return -ENODEV;
+}
+
+static int __devexit cs4231_remove(struct of_device *op)
+{
+ struct snd_cs4231 *chip = dev_get_drvdata(&op->dev);
+
+ snd_card_free(chip->card);
+
+ return 0;
+}
+
+static const struct of_device_id cs4231_match[] = {
+ {
+ .name = "SUNW,CS4231",
+ },
+ {
+ .name = "audio",
+ .compatible = "SUNW,CS4231",
+ },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, cs4231_match);
+
+static struct of_platform_driver cs4231_driver = {
+ .name = "audio",
+ .match_table = cs4231_match,
+ .probe = cs4231_probe,
+ .remove = __devexit_p(cs4231_remove),
+};
+
static int __init cs4231_init(void)
{
-#ifdef SBUS_SUPPORT
- struct sbus_bus *sbus;
- struct sbus_dev *sdev;
-#endif
-#ifdef EBUS_SUPPORT
- struct linux_ebus *ebus;
- struct linux_ebus_device *edev;
-#endif
- int found;
-
- found = 0;
-
-#ifdef SBUS_SUPPORT
- for_all_sbusdev(sdev, sbus) {
- if (!strcmp(sdev->prom_name, "SUNW,CS4231")) {
- if (cs4231_sbus_attach(sdev) == 0)
- found++;
- }
- }
-#endif
-#ifdef EBUS_SUPPORT
- for_each_ebus(ebus) {
- for_each_ebusdev(edev, ebus) {
- int match = 0;
-
- if (!strcmp(edev->prom_node->name, "SUNW,CS4231")) {
- match = 1;
- } else if (!strcmp(edev->prom_node->name, "audio")) {
- const char *compat;
-
- compat = of_get_property(edev->prom_node,
- "compatible", NULL);
- if (compat && !strcmp(compat, "SUNW,CS4231"))
- match = 1;
- }
-
- if (match &&
- cs4231_ebus_attach(edev) == 0)
- found++;
- }
- }
-#endif
-
-
- return (found > 0) ? 0 : -EIO;
+ return of_register_driver(&cs4231_driver, &of_bus_type);
}
static void __exit cs4231_exit(void)
{
- struct snd_cs4231 *p = cs4231_list;
-
- while (p != NULL) {
- struct snd_cs4231 *next = p->next;
-
- snd_card_free(p->card);
-
- p = next;
- }
-
- cs4231_list = NULL;
+ of_unregister_driver(&cs4231_driver);
}
module_init(cs4231_init);
diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c
index c534a2a..c257ad8 100644
--- a/sound/sparc/dbri.c
+++ b/sound/sparc/dbri.c
@@ -57,6 +57,7 @@
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/io.h>
+#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -66,7 +67,7 @@
#include <sound/initval.h>
#include <linux/of.h>
-#include <asm/sbus.h>
+#include <linux/of_device.h>
#include <asm/atomic.h>
MODULE_AUTHOR("Rudolf Koenig, Brent Baccala and Martin Habets");
@@ -297,7 +298,7 @@
/* This structure holds the information for both chips (DBRI & CS4215) */
struct snd_dbri {
int regs_size, irq; /* Needed for unload */
- struct sbus_dev *sdev; /* SBUS device info */
+ struct of_device *op; /* OF device info */
spinlock_t lock;
struct dbri_dma *dma; /* Pointer to our DMA block */
@@ -2093,14 +2094,15 @@
*/
if (info->dvma_buffer == 0) {
if (DBRI_STREAMNO(substream) == DBRI_PLAY)
- direction = SBUS_DMA_TODEVICE;
+ direction = DMA_TO_DEVICE;
else
- direction = SBUS_DMA_FROMDEVICE;
+ direction = DMA_FROM_DEVICE;
- info->dvma_buffer = sbus_map_single(dbri->sdev,
- runtime->dma_area,
- params_buffer_bytes(hw_params),
- direction);
+ info->dvma_buffer =
+ dma_map_single(&dbri->op->dev,
+ runtime->dma_area,
+ params_buffer_bytes(hw_params),
+ direction);
}
direction = params_buffer_bytes(hw_params);
@@ -2121,12 +2123,12 @@
*/
if (info->dvma_buffer) {
if (DBRI_STREAMNO(substream) == DBRI_PLAY)
- direction = SBUS_DMA_TODEVICE;
+ direction = DMA_TO_DEVICE;
else
- direction = SBUS_DMA_FROMDEVICE;
+ direction = DMA_FROM_DEVICE;
- sbus_unmap_single(dbri->sdev, info->dvma_buffer,
- substream->runtime->buffer_size, direction);
+ dma_unmap_single(&dbri->op->dev, info->dvma_buffer,
+ substream->runtime->buffer_size, direction);
info->dvma_buffer = 0;
}
if (info->pipe != -1) {
@@ -2519,31 +2521,32 @@
static void snd_dbri_free(struct snd_dbri *dbri);
static int __devinit snd_dbri_create(struct snd_card *card,
- struct sbus_dev *sdev,
- int irq, int dev)
+ struct of_device *op,
+ int irq, int dev)
{
struct snd_dbri *dbri = card->private_data;
int err;
spin_lock_init(&dbri->lock);
- dbri->sdev = sdev;
+ dbri->op = op;
dbri->irq = irq;
- dbri->dma = sbus_alloc_consistent(sdev, sizeof(struct dbri_dma),
- &dbri->dma_dvma);
+ dbri->dma = dma_alloc_coherent(&op->dev,
+ sizeof(struct dbri_dma),
+ &dbri->dma_dvma, GFP_ATOMIC);
memset((void *)dbri->dma, 0, sizeof(struct dbri_dma));
dprintk(D_GEN, "DMA Cmd Block 0x%p (0x%08x)\n",
dbri->dma, dbri->dma_dvma);
/* Map the registers into memory. */
- dbri->regs_size = sdev->reg_addrs[0].reg_size;
- dbri->regs = sbus_ioremap(&sdev->resource[0], 0,
- dbri->regs_size, "DBRI Registers");
+ dbri->regs_size = resource_size(&op->resource[0]);
+ dbri->regs = of_ioremap(&op->resource[0], 0,
+ dbri->regs_size, "DBRI Registers");
if (!dbri->regs) {
printk(KERN_ERR "DBRI: could not allocate registers\n");
- sbus_free_consistent(sdev, sizeof(struct dbri_dma),
- (void *)dbri->dma, dbri->dma_dvma);
+ dma_free_coherent(&op->dev, sizeof(struct dbri_dma),
+ (void *)dbri->dma, dbri->dma_dvma);
return -EIO;
}
@@ -2551,9 +2554,9 @@
"DBRI audio", dbri);
if (err) {
printk(KERN_ERR "DBRI: Can't get irq %d\n", dbri->irq);
- sbus_iounmap(dbri->regs, dbri->regs_size);
- sbus_free_consistent(sdev, sizeof(struct dbri_dma),
- (void *)dbri->dma, dbri->dma_dvma);
+ of_iounmap(&op->resource[0], dbri->regs, dbri->regs_size);
+ dma_free_coherent(&op->dev, sizeof(struct dbri_dma),
+ (void *)dbri->dma, dbri->dma_dvma);
return err;
}
@@ -2577,27 +2580,23 @@
free_irq(dbri->irq, dbri);
if (dbri->regs)
- sbus_iounmap(dbri->regs, dbri->regs_size);
+ of_iounmap(&dbri->op->resource[0], dbri->regs, dbri->regs_size);
if (dbri->dma)
- sbus_free_consistent(dbri->sdev, sizeof(struct dbri_dma),
- (void *)dbri->dma, dbri->dma_dvma);
+ dma_free_coherent(&dbri->op->dev,
+ sizeof(struct dbri_dma),
+ (void *)dbri->dma, dbri->dma_dvma);
}
-static int __devinit dbri_probe(struct of_device *of_dev,
- const struct of_device_id *match)
+static int __devinit dbri_probe(struct of_device *op, const struct of_device_id *match)
{
- struct sbus_dev *sdev = to_sbus_device(&of_dev->dev);
struct snd_dbri *dbri;
- int irq;
struct resource *rp;
struct snd_card *card;
static int dev = 0;
+ int irq;
int err;
- dprintk(D_GEN, "DBRI: Found %s in SBUS slot %d\n",
- sdev->prom_name, sdev->slot);
-
if (dev >= SNDRV_CARDS)
return -ENODEV;
if (!enable[dev]) {
@@ -2605,7 +2604,7 @@
return -ENOENT;
}
- irq = sdev->irqs[0];
+ irq = op->irqs[0];
if (irq <= 0) {
printk(KERN_ERR "DBRI-%d: No IRQ.\n", dev);
return -ENODEV;
@@ -2618,12 +2617,12 @@
strcpy(card->driver, "DBRI");
strcpy(card->shortname, "Sun DBRI");
- rp = &sdev->resource[0];
+ rp = &op->resource[0];
sprintf(card->longname, "%s at 0x%02lx:0x%016Lx, irq %d",
card->shortname,
rp->flags & 0xffL, (unsigned long long)rp->start, irq);
- err = snd_dbri_create(card, sdev, irq, dev);
+ err = snd_dbri_create(card, op, irq, dev);
if (err < 0) {
snd_card_free(card);
return err;
@@ -2640,7 +2639,7 @@
/* /proc file handling */
snd_dbri_proc(card);
- dev_set_drvdata(&of_dev->dev, card);
+ dev_set_drvdata(&op->dev, card);
err = snd_card_register(card);
if (err < 0)
@@ -2648,7 +2647,7 @@
printk(KERN_INFO "audio%d at %p (irq %d) is DBRI(%c)+CS4215(%d)\n",
dev, dbri->regs,
- dbri->irq, sdev->prom_name[9], dbri->mm.version);
+ dbri->irq, op->node->name[9], dbri->mm.version);
dev++;
return 0;
@@ -2659,19 +2658,19 @@
return err;
}
-static int __devexit dbri_remove(struct of_device *dev)
+static int __devexit dbri_remove(struct of_device *op)
{
- struct snd_card *card = dev_get_drvdata(&dev->dev);
+ struct snd_card *card = dev_get_drvdata(&op->dev);
snd_dbri_free(card->private_data);
snd_card_free(card);
- dev_set_drvdata(&dev->dev, NULL);
+ dev_set_drvdata(&op->dev, NULL);
return 0;
}
-static struct of_device_id dbri_match[] = {
+static const struct of_device_id dbri_match[] = {
{
.name = "SUNW,DBRIe",
},
@@ -2693,7 +2692,7 @@
/* Probe for the dbri chip and then attach the driver. */
static int __init dbri_init(void)
{
- return of_register_driver(&dbri_sbus_driver, &sbus_bus_type);
+ return of_register_driver(&dbri_sbus_driver, &of_bus_type);
}
static void __exit dbri_exit(void)