* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jwessel/linux-2.6-kgdb:
kgdb: Always process the whole breakpoint list on activate or deactivate
kgdb: continue and warn on signal passing from gdb
kgdb,x86: do not set kgdb_single_step on x86
kgdb: allow for cpu switch when single stepping
kgdb,i386: Fix corner case access to ss with NMI watch dog exception
kgdb: Replace strstr() by strchr() for single-character needles
kgdbts: Read buffer overflow
kgdb: Read buffer overflow
kgdb,x86: remove redundant test
NILFS2 supports the following mount options:
(*) == default
-barrier=on(*) This enables/disables barriers. barrier=off disables
- it, barrier=on enables it.
+nobarrier Disables barriers.
errors=continue(*) Keep going on a filesystem error.
errors=remount-ro Remount the filesystem read-only on an error.
errors=panic Panic and halt the machine if an error occurs.
blocks. That means, it is guaranteed that no
overtaking of events occurs in the recovered file
system after a crash.
+norecovery Disable recovery of the filesystem on mount.
+ This disables every write access on the device for
+ read-only mounts or snapshots. This option will fail
+ for r/w mounts on an unclean volume.
NILFS2 usage
============
unsigned long, prot, unsigned long, flags, unsigned long, fd,
unsigned long, off)
{
- struct file *file = NULL;
- unsigned long ret = -EBADF;
+ unsigned long ret = -EINVAL;
#if 0
if (flags & (_MAP_HASSEMAPHORE | _MAP_INHERIT | _MAP_UNALIGNED))
printk("%s: unimplemented OSF mmap flags %04lx\n",
current->comm, flags);
#endif
- if (!(flags & MAP_ANONYMOUS)) {
- file = fget(fd);
- if (!file)
- goto out;
- }
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
- down_write(¤t->mm->mmap_sem);
- ret = do_mmap(file, addr, len, prot, flags, off);
- up_write(¤t->mm->mmap_sem);
- if (file)
- fput(file);
+ if ((off + PAGE_ALIGN(len)) < off)
+ goto out;
+ if (off & ~PAGE_MASK)
+ goto out;
+ ret = sys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
out:
return ret;
}
#include <asm-generic/mman.h>
+
+#define arch_mmap_check(addr, len, flags) \
+ (((flags) & MAP_FIXED && (addr) < FIRST_USER_ADDRESS) ? -EINVAL : 0)
/* 160 */ CALL(sys_sched_get_priority_min)
CALL(sys_sched_rr_get_interval)
CALL(sys_nanosleep)
- CALL(sys_arm_mremap)
+ CALL(sys_mremap)
CALL(sys_setresuid16)
/* 165 */ CALL(sys_getresuid16)
CALL(sys_ni_syscall) /* vm86 */
tst r5, #PGOFF_MASK
moveq r5, r5, lsr #PAGE_SHIFT - 12
streq r5, [sp, #4]
- beq do_mmap2
+ beq sys_mmap_pgoff
mov r0, #-EINVAL
mov pc, lr
#else
str r5, [sp, #4]
- b do_mmap2
+ b sys_mmap_pgoff
#endif
ENDPROC(sys_mmap2)
#include <linux/ipc.h>
#include <linux/uaccess.h>
-extern unsigned long do_mremap(unsigned long addr, unsigned long old_len,
- unsigned long new_len, unsigned long flags,
- unsigned long new_addr);
-
-/* common code for old and new mmaps */
-inline long do_mmap2(
- unsigned long addr, unsigned long len,
- unsigned long prot, unsigned long flags,
- unsigned long fd, unsigned long pgoff)
-{
- int error = -EINVAL;
- struct file * file = NULL;
-
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-
- if (flags & MAP_FIXED && addr < FIRST_USER_ADDRESS)
- goto out;
-
- error = -EBADF;
- if (!(flags & MAP_ANONYMOUS)) {
- file = fget(fd);
- if (!file)
- goto out;
- }
-
- down_write(¤t->mm->mmap_sem);
- error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
- up_write(¤t->mm->mmap_sem);
-
- if (file)
- fput(file);
-out:
- return error;
-}
-
struct mmap_arg_struct {
unsigned long addr;
unsigned long len;
if (a.offset & ~PAGE_MASK)
goto out;
- error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT);
+ error = sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT);
out:
return error;
}
-asmlinkage unsigned long
-sys_arm_mremap(unsigned long addr, unsigned long old_len,
- unsigned long new_len, unsigned long flags,
- unsigned long new_addr)
-{
- unsigned long ret = -EINVAL;
-
- if (flags & MREMAP_FIXED && new_addr < FIRST_USER_ADDRESS)
- goto out;
-
- down_write(¤t->mm->mmap_sem);
- ret = do_mremap(addr, old_len, new_len, flags, new_addr);
- up_write(¤t->mm->mmap_sem);
-
-out:
- return ret;
-}
-
/*
* Perform the select(nd, in, out, ex, tv) and mmap() system
* calls.
* We enforce the MAP_FIXED case.
*/
if (flags & MAP_FIXED) {
- if (aliasing && flags & MAP_SHARED && addr & (SHMLBA - 1))
+ if (aliasing && flags & MAP_SHARED &&
+ (addr - (pgoff << PAGE_SHIFT)) & (SHMLBA - 1))
return -EINVAL;
return addr;
}
struct pt_regs *);
asmlinkage int sys_rt_sigreturn(struct pt_regs *);
-/* kernel/sys_avr32.c */
-asmlinkage long sys_mmap2(unsigned long, unsigned long, unsigned long,
- unsigned long, unsigned long, off_t);
-
/* mm/cache.c */
asmlinkage int sys_cacheflush(int, void __user *, size_t);
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/file.h>
-#include <linux/mm.h>
#include <linux/unistd.h>
-#include <asm/mman.h>
-#include <asm/uaccess.h>
-#include <asm/syscalls.h>
-
-asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
- unsigned long prot, unsigned long flags,
- unsigned long fd, off_t offset)
-{
- int error = -EBADF;
- struct file *file = NULL;
-
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
- if (!(flags & MAP_ANONYMOUS)) {
- file = fget(fd);
- if (!file)
- return error;
- }
-
- down_write(¤t->mm->mmap_sem);
- error = do_mmap_pgoff(file, addr, len, prot, flags, offset);
- up_write(¤t->mm->mmap_sem);
-
- if (file)
- fput(file);
- return error;
-}
-
int kernel_execve(const char *file, char **argv, char **envp)
{
register long scno asm("r8") = __NR_execve;
__sys_mmap2:
pushm lr
st.w --sp, ARG6
- call sys_mmap2
+ call sys_mmap_pgoff
sub sp, -4
popm pc
#include <asm/cacheflush.h>
#include <asm/dma.h>
-/* common code for old and new mmaps */
-static inline long
-do_mmap2(unsigned long addr, unsigned long len,
- unsigned long prot, unsigned long flags,
- unsigned long fd, unsigned long pgoff)
-{
- int error = -EBADF;
- struct file *file = NULL;
-
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
- if (!(flags & MAP_ANONYMOUS)) {
- file = fget(fd);
- if (!file)
- goto out;
- }
-
- down_write(¤t->mm->mmap_sem);
- error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
- up_write(¤t->mm->mmap_sem);
-
- if (file)
- fput(file);
- out:
- return error;
-}
-
-asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
- unsigned long prot, unsigned long flags,
- unsigned long fd, unsigned long pgoff)
-{
- return do_mmap2(addr, len, prot, flags, fd, pgoff);
-}
-
asmlinkage void *sys_sram_alloc(size_t size, unsigned long flags)
{
return sram_alloc_with_lsl(size, flags);
.long _sys_ni_syscall /* streams2 */
.long _sys_vfork /* 190 */
.long _sys_getrlimit
- .long _sys_mmap2
+ .long _sys_mmap_pgoff
.long _sys_truncate64
.long _sys_ftruncate64
.long _sys_stat64 /* 195 */
#include <asm/uaccess.h>
#include <asm/segment.h>
-/* common code for old and new mmaps */
-static inline long
-do_mmap2(unsigned long addr, unsigned long len, unsigned long prot,
- unsigned long flags, unsigned long fd, unsigned long pgoff)
-{
- int error = -EBADF;
- struct file * file = NULL;
-
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
- if (!(flags & MAP_ANONYMOUS)) {
- file = fget(fd);
- if (!file)
- goto out;
- }
-
- down_write(¤t->mm->mmap_sem);
- error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
- up_write(¤t->mm->mmap_sem);
-
- if (file)
- fput(file);
-out:
- return error;
-}
-
asmlinkage unsigned long old_mmap(unsigned long __user *args)
{
unsigned long buffer[6];
if (buffer[5] & ~PAGE_MASK) /* verify that offset is on page boundary */
goto out;
- err = do_mmap2(buffer[0], buffer[1], buffer[2], buffer[3],
+ err = sys_mmap_pgoff(buffer[0], buffer[1], buffer[2], buffer[3],
buffer[4], buffer[5] >> PAGE_SHIFT);
out:
return err;
sys_mmap2(unsigned long addr, unsigned long len, unsigned long prot,
unsigned long flags, unsigned long fd, unsigned long pgoff)
{
- return do_mmap2(addr, len, prot, flags, fd, pgoff);
+ /* bug(?): 8Kb pages here */
+ return sys_mmap_pgoff(addr, len, prot, flags, fd, pgoff);
}
/*
unsigned long prot, unsigned long flags,
unsigned long fd, unsigned long pgoff)
{
- int error = -EBADF;
- struct file * file = NULL;
-
/* As with sparc32, make sure the shift for mmap2 is constant
(12), no matter what PAGE_SIZE we have.... */
trying to map something we can't */
if (pgoff & ((1 << (PAGE_SHIFT - 12)) - 1))
return -EINVAL;
- pgoff >>= PAGE_SHIFT - 12;
-
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
- if (!(flags & MAP_ANONYMOUS)) {
- file = fget(fd);
- if (!file)
- goto out;
- }
-
- down_write(¤t->mm->mmap_sem);
- error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
- up_write(¤t->mm->mmap_sem);
-
- if (file)
- fput(file);
-out:
- return error;
-}
-
-#if 0 /* DAVIDM - do we want this */
-struct mmap_arg_struct64 {
- __u32 addr;
- __u32 len;
- __u32 prot;
- __u32 flags;
- __u64 offset; /* 64 bits */
- __u32 fd;
-};
-
-asmlinkage long sys_mmap64(struct mmap_arg_struct64 *arg)
-{
- int error = -EFAULT;
- struct file * file = NULL;
- struct mmap_arg_struct64 a;
- unsigned long pgoff;
-
- if (copy_from_user(&a, arg, sizeof(a)))
- return -EFAULT;
-
- if ((long)a.offset & ~PAGE_MASK)
- return -EINVAL;
-
- pgoff = a.offset >> PAGE_SHIFT;
- if ((a.offset >> PAGE_SHIFT) != pgoff)
- return -EINVAL;
-
- if (!(a.flags & MAP_ANONYMOUS)) {
- error = -EBADF;
- file = fget(a.fd);
- if (!file)
- goto out;
- }
- a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
- down_write(¤t->mm->mmap_sem);
- error = do_mmap_pgoff(file, a.addr, a.len, a.prot, a.flags, pgoff);
- up_write(¤t->mm->mmap_sem);
- if (file)
- fput(file);
-out:
- return error;
+ return sys_mmap_pgoff(addr, len, prot, flags, fd,
+ pgoff >> (PAGE_SHIFT - 12));
}
-#endif
/*
* sys_ipc() is the de-multiplexer for the SysV IPC calls..
#include <asm/traps.h>
#include <asm/unistd.h>
-/* common code for old and new mmaps */
-static inline long do_mmap2(
- unsigned long addr, unsigned long len,
- unsigned long prot, unsigned long flags,
- unsigned long fd, unsigned long pgoff)
-{
- int error = -EBADF;
- struct file * file = NULL;
-
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
- if (!(flags & MAP_ANONYMOUS)) {
- file = fget(fd);
- if (!file)
- goto out;
- }
-
- down_write(¤t->mm->mmap_sem);
- error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
- up_write(¤t->mm->mmap_sem);
-
- if (file)
- fput(file);
-out:
- return error;
-}
-
-asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
- unsigned long prot, unsigned long flags,
- unsigned long fd, unsigned long pgoff)
-{
- return do_mmap2(addr, len, prot, flags, fd, pgoff);
-}
-
/*
* Perform the select(nd, in, out, ex, tv) and mmap() system
* calls. Linux/m68k cloned Linux/i386, which didn't use to be able to
if (a.offset & ~PAGE_MASK)
goto out;
- a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-
- error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT);
-out:
- return error;
-}
-
-#if 0 /* DAVIDM - do we want this */
-struct mmap_arg_struct64 {
- __u32 addr;
- __u32 len;
- __u32 prot;
- __u32 flags;
- __u64 offset; /* 64 bits */
- __u32 fd;
-};
-
-asmlinkage long sys_mmap64(struct mmap_arg_struct64 *arg)
-{
- int error = -EFAULT;
- struct file * file = NULL;
- struct mmap_arg_struct64 a;
- unsigned long pgoff;
-
- if (copy_from_user(&a, arg, sizeof(a)))
- return -EFAULT;
-
- if ((long)a.offset & ~PAGE_MASK)
- return -EINVAL;
-
- pgoff = a.offset >> PAGE_SHIFT;
- if ((a.offset >> PAGE_SHIFT) != pgoff)
- return -EINVAL;
-
- if (!(a.flags & MAP_ANONYMOUS)) {
- error = -EBADF;
- file = fget(a.fd);
- if (!file)
- goto out;
- }
- a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-
- down_write(¤t->mm->mmap_sem);
- error = do_mmap_pgoff(file, a.addr, a.len, a.prot, a.flags, pgoff);
- up_write(¤t->mm->mmap_sem);
- if (file)
- fput(file);
+ error = sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd,
+ a.offset >> PAGE_SHIFT);
out:
return error;
}
-#endif
struct sel_arg_struct {
unsigned long n;
.long SYMBOL_NAME(sys_ni_syscall) /* streams2 */
.long SYMBOL_NAME(sys_vfork) /* 190 */
.long SYMBOL_NAME(sys_getrlimit)
- .long SYMBOL_NAME(sys_mmap2)
+ .long SYMBOL_NAME(sys_mmap_pgoff)
.long SYMBOL_NAME(sys_truncate64)
.long SYMBOL_NAME(sys_ftruncate64)
.long SYMBOL_NAME(sys_stat64) /* 195 */
prot = get_prot32(prot);
+ if (flags & MAP_HUGETLB)
+ return -ENOMEM;
+
#if PAGE_SHIFT > IA32_PAGE_SHIFT
mutex_lock(&ia32_mmap_mutex);
{
#include <xen/interface/xen.h>
#include <xen/interface/version.h> /* to compile feature.c */
#include <xen/features.h> /* to comiple xen-netfront.c */
+#include <xen/xen.h>
#include <asm/xen/hypercall.h>
-/* xen_domain_type is set before executing any C code by early_xen_setup */
-enum xen_domain_type {
- XEN_NATIVE, /* running on bare hardware */
- XEN_PV_DOMAIN, /* running in a PV domain */
- XEN_HVM_DOMAIN, /* running in a Xen hvm domain*/
-};
-
-#ifdef CONFIG_XEN
-extern enum xen_domain_type xen_domain_type;
-#else
-#define xen_domain_type XEN_NATIVE
-#endif
-
-#define xen_domain() (xen_domain_type != XEN_NATIVE)
-#define xen_pv_domain() (xen_domain() && \
- xen_domain_type == XEN_PV_DOMAIN)
-#define xen_hvm_domain() (xen_domain() && \
- xen_domain_type == XEN_HVM_DOMAIN)
-
-#ifdef CONFIG_XEN_DOM0
-#define xen_initial_domain() (xen_pv_domain() && \
- (xen_start_info->flags & SIF_INITDOMAIN))
-#else
-#define xen_initial_domain() (0)
-#endif
-
-
#ifdef CONFIG_XEN
extern struct shared_info *HYPERVISOR_shared_info;
extern struct start_info *xen_start_info;
asmlinkage unsigned long
ia64_brk (unsigned long brk)
{
- unsigned long rlim, retval, newbrk, oldbrk;
- struct mm_struct *mm = current->mm;
-
- /*
- * Most of this replicates the code in sys_brk() except for an additional safety
- * check and the clearing of r8. However, we can't call sys_brk() because we need
- * to acquire the mmap_sem before we can do the test...
- */
- down_write(&mm->mmap_sem);
-
- if (brk < mm->end_code)
- goto out;
- newbrk = PAGE_ALIGN(brk);
- oldbrk = PAGE_ALIGN(mm->brk);
- if (oldbrk == newbrk)
- goto set_brk;
-
- /* Always allow shrinking brk. */
- if (brk <= mm->brk) {
- if (!do_munmap(mm, newbrk, oldbrk-newbrk))
- goto set_brk;
- goto out;
- }
-
- /* Check against unimplemented/unmapped addresses: */
- if ((newbrk - oldbrk) > RGN_MAP_LIMIT || REGION_OFFSET(newbrk) > RGN_MAP_LIMIT)
- goto out;
-
- /* Check against rlimit.. */
- rlim = current->signal->rlim[RLIMIT_DATA].rlim_cur;
- if (rlim < RLIM_INFINITY && brk - mm->start_data > rlim)
- goto out;
-
- /* Check against existing mmap mappings. */
- if (find_vma_intersection(mm, oldbrk, newbrk+PAGE_SIZE))
- goto out;
-
- /* Ok, looks good - let it rip. */
- if (do_brk(oldbrk, newbrk-oldbrk) != oldbrk)
- goto out;
-set_brk:
- mm->brk = brk;
-out:
- retval = mm->brk;
- up_write(&mm->mmap_sem);
+ unsigned long retval = sys_brk(brk);
force_successful_syscall_return();
return retval;
}
return 0;
}
-static inline unsigned long
-do_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, unsigned long pgoff)
-{
- struct file *file = NULL;
-
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
- if (!(flags & MAP_ANONYMOUS)) {
- file = fget(fd);
- if (!file)
- return -EBADF;
-
- if (!file->f_op || !file->f_op->mmap) {
- addr = -ENODEV;
- goto out;
- }
- }
-
- /* Careful about overflows.. */
- len = PAGE_ALIGN(len);
- if (!len || len > TASK_SIZE) {
- addr = -EINVAL;
- goto out;
- }
-
- down_write(¤t->mm->mmap_sem);
- addr = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
- up_write(¤t->mm->mmap_sem);
-
-out: if (file)
- fput(file);
- return addr;
-}
-
/*
* mmap2() is like mmap() except that the offset is expressed in units
* of PAGE_SIZE (instead of bytes). This allows to mmap2() (pieces
asmlinkage unsigned long
sys_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, long pgoff)
{
- addr = do_mmap2(addr, len, prot, flags, fd, pgoff);
+ addr = sys_mmap_pgoff(addr, len, prot, flags, fd, pgoff);
if (!IS_ERR((void *) addr))
force_successful_syscall_return();
return addr;
if (offset_in_page(off) != 0)
return -EINVAL;
- addr = do_mmap2(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
+ addr = sys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
if (!IS_ERR((void *) addr))
force_successful_syscall_return();
return addr;
}
struct pci_root_info {
+ struct acpi_device *bridge;
struct pci_controller *controller;
char *name;
};
window->offset = offset;
if (insert_resource(root, &window->resource)) {
- printk(KERN_ERR "alloc 0x%llx-0x%llx from %s for %s failed\n",
- window->resource.start, window->resource.end,
- root->name, info->name);
+ dev_err(&info->bridge->dev,
+ "can't allocate host bridge window %pR\n",
+ &window->resource);
+ } else {
+ if (offset)
+ dev_info(&info->bridge->dev, "host bridge window %pR "
+ "(PCI address [%#llx-%#llx])\n",
+ &window->resource,
+ window->resource.start - offset,
+ window->resource.end - offset);
+ else
+ dev_info(&info->bridge->dev,
+ "host bridge window %pR\n",
+ &window->resource);
}
return AE_OK;
(res->end - res->start < 16))
continue;
if (j >= PCI_BUS_NUM_RESOURCES) {
- printk("Ignoring range [%#llx-%#llx] (%lx)\n",
- res->start, res->end, res->flags);
+ dev_warn(&bus->dev,
+ "ignoring host bridge window %pR (no space)\n",
+ res);
continue;
}
bus->resource[j++] = res;
goto out3;
sprintf(name, "PCI Bus %04x:%02x", domain, bus);
+ info.bridge = device;
info.controller = controller;
info.name = name;
acpi_walk_resources(device->handle, METHOD_NAME__CRS,
return ret;
}
-/* It's defined in drivers/pci/pci.c */
-extern u8 pci_cache_line_size;
-
/**
* set_pci_cacheline_size - determine cacheline size for PCI devices
*
*
* Code mostly taken from arch/ia64/kernel/palinfo.c:cache_info().
*/
-static void __init set_pci_cacheline_size(void)
+static void __init set_pci_dfl_cacheline_size(void)
{
unsigned long levels, unique_caches;
long status;
"(status=%ld)\n", __func__, status);
return;
}
- pci_cache_line_size = (1 << cci.pcci_line_size) / 4;
+ pci_dfl_cache_line_size = (1 << cci.pcci_line_size) / 4;
}
u64 ia64_dma_get_required_mask(struct device *dev)
static int __init pcibios_init(void)
{
- set_pci_cacheline_size();
+ set_pci_dfl_cacheline_size();
return 0;
}
return oldval;
}
-asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
- unsigned long prot, unsigned long flags,
- unsigned long fd, unsigned long pgoff)
-{
- int error = -EBADF;
- struct file *file = NULL;
-
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
- if (!(flags & MAP_ANONYMOUS)) {
- file = fget(fd);
- if (!file)
- goto out;
- }
-
- down_write(¤t->mm->mmap_sem);
- error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
- up_write(¤t->mm->mmap_sem);
-
- if (file)
- fput(file);
-out:
- return error;
-}
-
/*
* sys_ipc() is the de-multiplexer for the SysV IPC calls..
*
.long sys_ni_syscall /* streams2 */
.long sys_vfork /* 190 */
.long sys_getrlimit
- .long sys_mmap2
+ .long sys_mmap_pgoff
.long sys_truncate64
.long sys_ftruncate64
.long sys_stat64 /* 195 */
#include <asm/page.h>
#include <asm/unistd.h>
-/* common code for old and new mmaps */
-static inline long do_mmap2(
- unsigned long addr, unsigned long len,
- unsigned long prot, unsigned long flags,
- unsigned long fd, unsigned long pgoff)
-{
- int error = -EBADF;
- struct file * file = NULL;
-
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
- if (!(flags & MAP_ANONYMOUS)) {
- file = fget(fd);
- if (!file)
- goto out;
- }
-
- down_write(¤t->mm->mmap_sem);
- error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
- up_write(¤t->mm->mmap_sem);
-
- if (file)
- fput(file);
-out:
- return error;
-}
-
asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags,
unsigned long fd, unsigned long pgoff)
{
- return do_mmap2(addr, len, prot, flags, fd, pgoff);
+ /*
+ * This is wrong for sun3 - there PAGE_SIZE is 8Kb,
+ * so we need to shift the argument down by 1; m68k mmap64(3)
+ * (in libc) expects the last argument of mmap2 in 4Kb units.
+ */
+ return sys_mmap_pgoff(addr, len, prot, flags, fd, pgoff);
}
/*
if (a.offset & ~PAGE_MASK)
goto out;
- a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-
- error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT);
-out:
- return error;
-}
-
-#if 0
-struct mmap_arg_struct64 {
- __u32 addr;
- __u32 len;
- __u32 prot;
- __u32 flags;
- __u64 offset; /* 64 bits */
- __u32 fd;
-};
-
-asmlinkage long sys_mmap64(struct mmap_arg_struct64 *arg)
-{
- int error = -EFAULT;
- struct file * file = NULL;
- struct mmap_arg_struct64 a;
- unsigned long pgoff;
-
- if (copy_from_user(&a, arg, sizeof(a)))
- return -EFAULT;
-
- if ((long)a.offset & ~PAGE_MASK)
- return -EINVAL;
-
- pgoff = a.offset >> PAGE_SHIFT;
- if ((a.offset >> PAGE_SHIFT) != pgoff)
- return -EINVAL;
-
- if (!(a.flags & MAP_ANONYMOUS)) {
- error = -EBADF;
- file = fget(a.fd);
- if (!file)
- goto out;
- }
- a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-
- down_write(¤t->mm->mmap_sem);
- error = do_mmap_pgoff(file, a.addr, a.len, a.prot, a.flags, pgoff);
- up_write(¤t->mm->mmap_sem);
- if (file)
- fput(file);
+ error = sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd,
+ a.offset >> PAGE_SHIFT);
out:
return error;
}
-#endif
struct sel_arg_struct {
unsigned long n;
#include <asm/cacheflush.h>
#include <asm/unistd.h>
-/* common code for old and new mmaps */
-static inline long do_mmap2(
- unsigned long addr, unsigned long len,
- unsigned long prot, unsigned long flags,
- unsigned long fd, unsigned long pgoff)
-{
- int error = -EBADF;
- struct file * file = NULL;
-
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
- if (!(flags & MAP_ANONYMOUS)) {
- file = fget(fd);
- if (!file)
- goto out;
- }
-
- down_write(¤t->mm->mmap_sem);
- error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
- up_write(¤t->mm->mmap_sem);
-
- if (file)
- fput(file);
-out:
- return error;
-}
-
-asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
- unsigned long prot, unsigned long flags,
- unsigned long fd, unsigned long pgoff)
-{
- return do_mmap2(addr, len, prot, flags, fd, pgoff);
-}
-
/*
* Perform the select(nd, in, out, ex, tv) and mmap() system
* calls. Linux/m68k cloned Linux/i386, which didn't use to be able to
if (a.offset & ~PAGE_MASK)
goto out;
- a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-
- error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT);
+ error = sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd,
+ a.offset >> PAGE_SHIFT);
out:
return error;
}
.long sys_ni_syscall /* streams2 */
.long sys_vfork /* 190 */
.long sys_getrlimit
- .long sys_mmap2
+ .long sys_mmap_pgoff
.long sys_truncate64
.long sys_ftruncate64
.long sys_stat64 /* 195 */
return error;
}
-asmlinkage long
-sys_mmap2(unsigned long addr, unsigned long len,
- unsigned long prot, unsigned long flags,
- unsigned long fd, unsigned long pgoff)
-{
- struct file *file = NULL;
- int ret = -EBADF;
-
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
- if (!(flags & MAP_ANONYMOUS)) {
- file = fget(fd);
- if (!file) {
- printk(KERN_INFO "no fd in mmap\r\n");
- goto out;
- }
- }
-
- down_write(¤t->mm->mmap_sem);
- ret = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
- up_write(¤t->mm->mmap_sem);
- if (file)
- fput(file);
-out:
- return ret;
-}
-
asmlinkage long sys_mmap(unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags,
unsigned long fd, off_t pgoff)
{
- int err = -EINVAL;
-
- if (pgoff & ~PAGE_MASK) {
- printk(KERN_INFO "no pagemask in mmap\r\n");
- goto out;
- }
+ if (pgoff & ~PAGE_MASK)
+ return -EINVAL;
- err = sys_mmap2(addr, len, prot, flags, fd, pgoff >> PAGE_SHIFT);
-out:
- return err;
+ return sys_mmap_pgoff(addr, len, prot, flags, fd, pgoff >> PAGE_SHIFT);
}
/*
.long sys_ni_syscall /* reserved for streams2 */
.long sys_vfork /* 190 */
.long sys_getrlimit
- .long sys_mmap2 /* mmap2 */
+ .long sys_mmap_pgoff /* mmap2 */
.long sys_truncate64
.long sys_ftruncate64
.long sys_stat64 /* 195 */
unsigned long, prot, unsigned long, flags, unsigned long, fd,
unsigned long, pgoff)
{
- struct file * file = NULL;
unsigned long error;
error = -EINVAL;
if (pgoff & (~PAGE_MASK >> 12))
goto out;
- pgoff >>= PAGE_SHIFT-12;
-
- if (!(flags & MAP_ANONYMOUS)) {
- error = -EBADF;
- file = fget(fd);
- if (!file)
- goto out;
- }
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-
- down_write(¤t->mm->mmap_sem);
- error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
- up_write(¤t->mm->mmap_sem);
- if (file)
- fput(file);
-
+ error = sys_mmap_pgoff(addr, len, prot, flags, fd,
+ pgoff >> (PAGE_SHIFT-12));
out:
return error;
}
* We do not accept a shared mapping if it would violate
* cache aliasing constraints.
*/
- if ((flags & MAP_SHARED) && (addr & shm_align_mask))
+ if ((flags & MAP_SHARED) &&
+ ((addr - (pgoff << PAGE_SHIFT)) & shm_align_mask))
return -EINVAL;
return addr;
}
}
}
-/* common code for old and new mmaps */
-static inline unsigned long
-do_mmap2(unsigned long addr, unsigned long len, unsigned long prot,
- unsigned long flags, unsigned long fd, unsigned long pgoff)
-{
- unsigned long error = -EBADF;
- struct file * file = NULL;
-
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
- if (!(flags & MAP_ANONYMOUS)) {
- file = fget(fd);
- if (!file)
- goto out;
- }
-
- down_write(¤t->mm->mmap_sem);
- error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
- up_write(¤t->mm->mmap_sem);
-
- if (file)
- fput(file);
-out:
- return error;
-}
-
SYSCALL_DEFINE6(mips_mmap, unsigned long, addr, unsigned long, len,
unsigned long, prot, unsigned long, flags, unsigned long,
fd, off_t, offset)
if (offset & ~PAGE_MASK)
goto out;
- result = do_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT);
+ result = sys_mmap_pgoff(addr, len, prot, flags, fd, offset >> PAGE_SHIFT);
out:
return result;
if (pgoff & (~PAGE_MASK >> 12))
return -EINVAL;
- return do_mmap2(addr, len, prot, flags, fd, pgoff >> (PAGE_SHIFT-12));
+ return sys_mmap_pgoff(addr, len, prot, flags, fd, pgoff >> (PAGE_SHIFT-12));
}
save_static_function(sys_fork);
#include <asm-generic/mman.h>
+
+#define MIN_MAP_ADDR PAGE_SIZE /* minimum fixed mmap address */
+
+#define arch_mmap_check(addr, len, flags) \
+ (((flags) & MAP_FIXED && (addr) < MIN_MAP_ADDR) ? -EINVAL : 0)
.long sys_ni_syscall /* reserved for streams2 */
.long sys_vfork /* 190 */
.long sys_getrlimit
- .long sys_mmap2
+ .long sys_mmap_pgoff
.long sys_truncate64
.long sys_ftruncate64
.long sys_stat64 /* 195 */
#include <asm/uaccess.h>
-#define MIN_MAP_ADDR PAGE_SIZE /* minimum fixed mmap address */
-
-/*
- * memory mapping syscall
- */
-asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
- unsigned long prot, unsigned long flags,
- unsigned long fd, unsigned long pgoff)
-{
- struct file *file = NULL;
- long error = -EINVAL;
-
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-
- if (flags & MAP_FIXED && addr < MIN_MAP_ADDR)
- goto out;
-
- error = -EBADF;
- if (!(flags & MAP_ANONYMOUS)) {
- file = fget(fd);
- if (!file)
- goto out;
- }
-
- down_write(¤t->mm->mmap_sem);
- error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
- up_write(¤t->mm->mmap_sem);
-
- if (file)
- fput(file);
-out:
- return error;
-}
-
asmlinkage long old_mmap(unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags,
unsigned long fd, unsigned long offset)
{
if (offset & ~PAGE_MASK)
return -EINVAL;
- return sys_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT);
+ return sys_mmap_pgoff(addr, len, prot, flags, fd, offset >> PAGE_SHIFT);
}
struct sel_arg_struct {
return addr;
}
-static unsigned long do_mmap2(unsigned long addr, unsigned long len,
- unsigned long prot, unsigned long flags, unsigned long fd,
- unsigned long pgoff)
-{
- struct file * file = NULL;
- unsigned long error = -EBADF;
- if (!(flags & MAP_ANONYMOUS)) {
- file = fget(fd);
- if (!file)
- goto out;
- }
-
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-
- down_write(¤t->mm->mmap_sem);
- error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
- up_write(¤t->mm->mmap_sem);
-
- if (file != NULL)
- fput(file);
-out:
- return error;
-}
-
asmlinkage unsigned long sys_mmap2(unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags, unsigned long fd,
unsigned long pgoff)
{
/* Make sure the shift for mmap2 is constant (12), no matter what PAGE_SIZE
we have. */
- return do_mmap2(addr, len, prot, flags, fd, pgoff >> (PAGE_SHIFT - 12));
+ return sys_mmap_pgoff(addr, len, prot, flags, fd,
+ pgoff >> (PAGE_SHIFT - 12));
}
asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len,
unsigned long offset)
{
if (!(offset & ~PAGE_MASK)) {
- return do_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT);
+ return sys_mmap_pgoff(addr, len, prot, flags, fd,
+ offset >> PAGE_SHIFT);
} else {
return -EINVAL;
}
unsigned long prot, unsigned long flags,
unsigned long fd, unsigned long off, int shift)
{
- struct file * file = NULL;
unsigned long ret = -EINVAL;
if (!arch_validate_prot(prot))
goto out;
off >>= shift;
}
-
- ret = -EBADF;
- if (!(flags & MAP_ANONYMOUS)) {
- if (!(file = fget(fd)))
- goto out;
- }
-
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
- down_write(¤t->mm->mmap_sem);
- ret = do_mmap_pgoff(file, addr, len, prot, flags, off);
- up_write(¤t->mm->mmap_sem);
- if (file)
- fput(file);
+ ret = sys_mmap_pgoff(addr, len, prot, flags, fd, off);
out:
return ret;
}
u32 offset;
};
-/* common code for old and new mmaps */
-static inline long do_mmap2(
- unsigned long addr, unsigned long len,
- unsigned long prot, unsigned long flags,
- unsigned long fd, unsigned long pgoff)
-{
- struct file * file = NULL;
- unsigned long error = -EBADF;
-
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
- if (!(flags & MAP_ANONYMOUS)) {
- file = fget(fd);
- if (!file)
- goto out;
- }
-
- down_write(¤t->mm->mmap_sem);
- error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
- if (!IS_ERR((void *) error) && error + len >= 0x80000000ULL) {
- /* Result is out of bounds. */
- do_munmap(current->mm, addr, len);
- error = -ENOMEM;
- }
- up_write(¤t->mm->mmap_sem);
-
- if (file)
- fput(file);
-out:
- return error;
-}
-
-
asmlinkage unsigned long
old32_mmap(struct mmap_arg_struct_emu31 __user *arg)
{
if (a.offset & ~PAGE_MASK)
goto out;
- error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT);
+ error = sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd,
+ a.offset >> PAGE_SHIFT);
out:
return error;
}
if (copy_from_user(&a, arg, sizeof(a)))
goto out;
- error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset);
+ error = sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd, a.offset);
out:
return error;
}
#include <asm/uaccess.h>
#include "entry.h"
-/* common code for old and new mmaps */
-static inline long do_mmap2(
- unsigned long addr, unsigned long len,
- unsigned long prot, unsigned long flags,
- unsigned long fd, unsigned long pgoff)
-{
- long error = -EBADF;
- struct file * file = NULL;
-
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
- if (!(flags & MAP_ANONYMOUS)) {
- file = fget(fd);
- if (!file)
- goto out;
- }
-
- down_write(¤t->mm->mmap_sem);
- error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
- up_write(¤t->mm->mmap_sem);
-
- if (file)
- fput(file);
-out:
- return error;
-}
-
/*
* Perform the select(nd, in, out, ex, tv) and mmap() system
* calls. Linux for S/390 isn't able to handle more than 5
if (copy_from_user(&a, arg, sizeof(a)))
goto out;
- error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset);
+ error = sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd, a.offset);
out:
return error;
}
if (a.offset & ~PAGE_MASK)
goto out;
- error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT);
+ error = sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT);
out:
return error;
}
sys_mmap2(unsigned long addr, unsigned long len, unsigned long prot,
unsigned long flags, unsigned long fd, unsigned long pgoff)
{
- int error = -EBADF;
- struct file *file = NULL;
-
- if (pgoff & (~PAGE_MASK >> 12))
- return -EINVAL;
-
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
- if (!(flags & MAP_ANONYMOUS)) {
- file = fget(fd);
- if (!file)
- return error;
- }
-
- down_write(¤t->mm->mmap_sem);
- error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
- up_write(¤t->mm->mmap_sem);
-
- if (file)
- fput(file);
-
- return error;
+ return sys_mmap_pgoff(addr, len, prot, flags, fd, pgoff);
}
asmlinkage long
sys_mmap(unsigned long addr, unsigned long len, unsigned long prot,
- unsigned long flags, unsigned long fd, off_t pgoff)
+ unsigned long flags, unsigned long fd, off_t offset)
{
- return sys_mmap2(addr, len, prot, flags, fd, pgoff >> PAGE_SHIFT);
+ if (unlikely(offset & ~PAGE_MASK))
+ return -EINVAL;
+ return sys_mmap_pgoff(addr, len, prot, flags, fd, offset >> PAGE_SHIFT);
}
asmlinkage long
#include <asm/cacheflush.h>
#include <asm/cachectl.h>
-static inline long
-do_mmap2(unsigned long addr, unsigned long len, unsigned long prot,
- unsigned long flags, int fd, unsigned long pgoff)
-{
- int error = -EBADF;
- struct file *file = NULL;
-
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
- if (!(flags & MAP_ANONYMOUS)) {
- file = fget(fd);
- if (!file)
- goto out;
- }
-
- down_write(¤t->mm->mmap_sem);
- error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
- up_write(¤t->mm->mmap_sem);
-
- if (file)
- fput(file);
-out:
- return error;
-}
-
asmlinkage int old_mmap(unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags,
int fd, unsigned long off)
{
if (off & ~PAGE_MASK)
return -EINVAL;
- return do_mmap2(addr, len, prot, flags, fd, off>>PAGE_SHIFT);
+ return sys_mmap_pgoff(addr, len, prot, flags, fd, off>>PAGE_SHIFT);
}
asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
pgoff >>= PAGE_SHIFT - 12;
- return do_mmap2(addr, len, prot, flags, fd, pgoff);
+ return sys_mmap_pgoff(addr, len, prot, flags, fd, pgoff);
}
/*
/* We do not accept a shared mapping if it would violate
* cache aliasing constraints.
*/
- if ((flags & MAP_SHARED) && (addr & shm_align_mask))
+ if ((flags & MAP_SHARED) &&
+ ((addr - (pgoff << PAGE_SHIFT)) & shm_align_mask))
return -EINVAL;
return addr;
}
#define PCI_IRQ_NONE 0xffffffff
-#define PCI_CACHE_LINE_BYTES 64
-
static inline void pcibios_set_master(struct pci_dev *dev)
{
/* No special bus mastering setup handling */
*start = rp->start - offset;
*end = rp->end - offset;
}
+
+static int __init pcibios_init(void)
+{
+ pci_dfl_cache_line_size = 64 >> 2;
+ return 0;
+}
+subsys_initcall(pcibios_init);
return do_sys_open(AT_FDCWD, filename, flags, mode);
}
-extern unsigned long do_mremap(unsigned long addr,
- unsigned long old_len, unsigned long new_len,
- unsigned long flags, unsigned long new_addr);
-
-asmlinkage unsigned long sys32_mremap(unsigned long addr,
- unsigned long old_len, unsigned long new_len,
- unsigned long flags, u32 __new_addr)
-{
- unsigned long ret = -EINVAL;
- unsigned long new_addr = __new_addr;
-
- if (unlikely(sparc_mmap_check(addr, old_len)))
- goto out;
- if (unlikely(sparc_mmap_check(new_addr, new_len)))
- goto out;
- down_write(¤t->mm->mmap_sem);
- ret = do_mremap(addr, old_len, new_len, flags, new_addr);
- up_write(¤t->mm->mmap_sem);
-out:
- return ret;
-}
-
long sys32_lookup_dcookie(unsigned long cookie_high,
unsigned long cookie_low,
char __user *buf, size_t len)
/* We do not accept a shared mapping if it would violate
* cache aliasing constraints.
*/
- if ((flags & MAP_SHARED) && (addr & (SHMLBA - 1)))
+ if ((flags & MAP_SHARED) &&
+ ((addr - (pgoff << PAGE_SHIFT)) & (SHMLBA - 1)))
return -EINVAL;
return addr;
}
}
}
-asmlinkage unsigned long sparc_brk(unsigned long brk)
-{
- if(ARCH_SUN4C) {
- if ((brk & 0xe0000000) != (current->mm->brk & 0xe0000000))
- return current->mm->brk;
- }
- return sys_brk(brk);
-}
-
/*
* sys_pipe() is the normal C calling standard for creating
* a pipe. It's not the way unix traditionally does this, though.
}
/* Linux version of mmap */
-static unsigned long do_mmap2(unsigned long addr, unsigned long len,
- unsigned long prot, unsigned long flags, unsigned long fd,
- unsigned long pgoff)
-{
- struct file * file = NULL;
- unsigned long retval = -EBADF;
-
- if (!(flags & MAP_ANONYMOUS)) {
- file = fget(fd);
- if (!file)
- goto out;
- }
-
- len = PAGE_ALIGN(len);
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-
- down_write(¤t->mm->mmap_sem);
- retval = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
- up_write(¤t->mm->mmap_sem);
-
- if (file)
- fput(file);
-out:
- return retval;
-}
asmlinkage unsigned long sys_mmap2(unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags, unsigned long fd,
{
/* Make sure the shift for mmap2 is constant (12), no matter what PAGE_SIZE
we have. */
- return do_mmap2(addr, len, prot, flags, fd, pgoff >> (PAGE_SHIFT - 12));
+ return sys_mmap_pgoff(addr, len, prot, flags, fd,
+ pgoff >> (PAGE_SHIFT - 12));
}
asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags, unsigned long fd,
unsigned long off)
{
- return do_mmap2(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
+ /* no alignment check? */
+ return sys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
}
long sparc_remap_file_pages(unsigned long start, unsigned long size,
(pgoff >> (PAGE_SHIFT - 12)), flags);
}
-extern unsigned long do_mremap(unsigned long addr,
- unsigned long old_len, unsigned long new_len,
- unsigned long flags, unsigned long new_addr);
-
-asmlinkage unsigned long sparc_mremap(unsigned long addr,
- unsigned long old_len, unsigned long new_len,
- unsigned long flags, unsigned long new_addr)
-{
- unsigned long ret = -EINVAL;
-
- if (unlikely(sparc_mmap_check(addr, old_len)))
- goto out;
- if (unlikely(sparc_mmap_check(new_addr, new_len)))
- goto out;
- down_write(¤t->mm->mmap_sem);
- ret = do_mremap(addr, old_len, new_len, flags, new_addr);
- up_write(¤t->mm->mmap_sem);
-out:
- return ret;
-}
-
/* we come to here via sys_nis_syscall so it can setup the regs argument */
asmlinkage unsigned long
c_sys_nis_syscall (struct pt_regs *regs)
unsigned long get_fb_unmapped_area(struct file *filp, unsigned long orig_addr, unsigned long len, unsigned long pgoff, unsigned long flags)
{
unsigned long align_goal, addr = -ENOMEM;
+ unsigned long (*get_area)(struct file *, unsigned long,
+ unsigned long, unsigned long, unsigned long);
+
+ get_area = current->mm->get_unmapped_area;
if (flags & MAP_FIXED) {
/* Ok, don't mess with it. */
- return get_unmapped_area(NULL, orig_addr, len, pgoff, flags);
+ return get_area(NULL, orig_addr, len, pgoff, flags);
}
flags &= ~MAP_SHARED;
align_goal = (64UL * 1024);
do {
- addr = get_unmapped_area(NULL, orig_addr, len + (align_goal - PAGE_SIZE), pgoff, flags);
+ addr = get_area(NULL, orig_addr, len + (align_goal - PAGE_SIZE), pgoff, flags);
if (!(addr & ~PAGE_MASK)) {
addr = (addr + (align_goal - 1UL)) & ~(align_goal - 1UL);
break;
* be obtained.
*/
if (addr & ~PAGE_MASK)
- addr = get_unmapped_area(NULL, orig_addr, len, pgoff, flags);
+ addr = get_area(NULL, orig_addr, len, pgoff, flags);
return addr;
}
}
}
-SYSCALL_DEFINE1(sparc_brk, unsigned long, brk)
-{
- /* People could try to be nasty and use ta 0x6d in 32bit programs */
- if (test_thread_flag(TIF_32BIT) && brk >= STACK_TOP32)
- return current->mm->brk;
-
- if (unlikely(straddles_64bit_va_hole(current->mm->brk, brk)))
- return current->mm->brk;
-
- return sys_brk(brk);
-}
-
/*
* sys_pipe() is the normal C calling standard for creating
* a pipe. It's not the way unix traditionally does this, though.
unsigned long, prot, unsigned long, flags, unsigned long, fd,
unsigned long, off)
{
- struct file * file = NULL;
- unsigned long retval = -EBADF;
-
- if (!(flags & MAP_ANONYMOUS)) {
- file = fget(fd);
- if (!file)
- goto out;
- }
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
- len = PAGE_ALIGN(len);
+ unsigned long retval = -EINVAL;
- down_write(¤t->mm->mmap_sem);
- retval = do_mmap(file, addr, len, prot, flags, off);
- up_write(¤t->mm->mmap_sem);
-
- if (file)
- fput(file);
+ if ((off + PAGE_ALIGN(len)) < off)
+ goto out;
+ if (off & ~PAGE_MASK)
+ goto out;
+ retval = sys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
out:
return retval;
}
if (test_thread_flag(TIF_32BIT))
goto out;
- if (unlikely(new_len >= VA_EXCLUDE_START))
- goto out;
- if (unlikely(sparc_mmap_check(addr, old_len)))
- goto out;
- if (unlikely(sparc_mmap_check(new_addr, new_len)))
- goto out;
down_write(¤t->mm->mmap_sem);
ret = do_mremap(addr, old_len, new_len, flags, new_addr);
struct new_utsname;
extern asmlinkage unsigned long sys_getpagesize(void);
-extern asmlinkage unsigned long sparc_brk(unsigned long brk);
extern asmlinkage long sparc_pipe(struct pt_regs *regs);
extern asmlinkage long sys_ipc(unsigned int call, int first,
unsigned long second,
/*0*/ .long sys_restart_syscall, sys_exit, sys_fork, sys_read, sys_write
/*5*/ .long sys_open, sys_close, sys_wait4, sys_creat, sys_link
/*10*/ .long sys_unlink, sunos_execv, sys_chdir, sys_chown16, sys_mknod
-/*15*/ .long sys_chmod, sys_lchown16, sparc_brk, sys_nis_syscall, sys_lseek
+/*15*/ .long sys_chmod, sys_lchown16, sys_brk, sys_nis_syscall, sys_lseek
/*20*/ .long sys_getpid, sys_capget, sys_capset, sys_setuid16, sys_getuid16
/*25*/ .long sys_vmsplice, sys_ptrace, sys_alarm, sys_sigaltstack, sys_pause
/*30*/ .long sys_utime, sys_lchown, sys_fchown, sys_access, sys_nice
/*235*/ .long sys_fstatfs64, sys_llseek, sys_mlock, sys_munlock, sys_mlockall
/*240*/ .long sys_munlockall, sys_sched_setparam, sys_sched_getparam, sys_sched_setscheduler, sys_sched_getscheduler
/*245*/ .long sys_sched_yield, sys_sched_get_priority_max, sys_sched_get_priority_min, sys_sched_rr_get_interval, sys_nanosleep
-/*250*/ .long sparc_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys_nfsservctl
+/*250*/ .long sys_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys_nfsservctl
/*255*/ .long sys_sync_file_range, sys_clock_settime, sys_clock_gettime, sys_clock_getres, sys_clock_nanosleep
/*260*/ .long sys_sched_getaffinity, sys_sched_setaffinity, sys_timer_settime, sys_timer_gettime, sys_timer_getoverrun
/*265*/ .long sys_timer_delete, sys_timer_create, sys_nis_syscall, sys_io_setup, sys_io_destroy
/*0*/ .word sys_restart_syscall, sys32_exit, sys_fork, sys_read, sys_write
/*5*/ .word sys32_open, sys_close, sys32_wait4, sys32_creat, sys_link
/*10*/ .word sys_unlink, sunos_execv, sys_chdir, sys_chown16, sys32_mknod
-/*15*/ .word sys_chmod, sys_lchown16, sys_sparc_brk, sys32_perfctr, sys32_lseek
+/*15*/ .word sys_chmod, sys_lchown16, sys_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, sys_pause
/*30*/ .word compat_sys_utime, sys_lchown, sys_fchown, sys32_access, sys32_nice
.word compat_sys_fstatfs64, sys_llseek, sys_mlock, sys_munlock, sys32_mlockall
/*240*/ .word sys_munlockall, sys32_sched_setparam, sys32_sched_getparam, sys32_sched_setscheduler, sys32_sched_getscheduler
.word sys_sched_yield, sys32_sched_get_priority_max, sys32_sched_get_priority_min, sys32_sched_rr_get_interval, compat_sys_nanosleep
-/*250*/ .word sys32_mremap, compat_sys_sysctl, sys32_getsid, sys_fdatasync, sys32_nfsservctl
+/*250*/ .word sys_mremap, compat_sys_sysctl, sys32_getsid, sys_fdatasync, sys32_nfsservctl
.word sys32_sync_file_range, compat_sys_clock_settime, compat_sys_clock_gettime, compat_sys_clock_getres, sys32_clock_nanosleep
/*260*/ .word compat_sys_sched_getaffinity, compat_sys_sched_setaffinity, sys32_timer_settime, compat_sys_timer_gettime, sys_timer_getoverrun
.word sys_timer_delete, compat_sys_timer_create, sys_ni_syscall, compat_sys_io_setup, sys_io_destroy
/*0*/ .word sys_restart_syscall, sparc_exit, sys_fork, sys_read, sys_write
/*5*/ .word sys_open, sys_close, sys_wait4, sys_creat, sys_link
/*10*/ .word sys_unlink, sys_nis_syscall, sys_chdir, sys_chown, sys_mknod
-/*15*/ .word sys_chmod, sys_lchown, sys_sparc_brk, sys_perfctr, sys_lseek
+/*15*/ .word sys_chmod, sys_lchown, sys_brk, sys_perfctr, sys_lseek
/*20*/ .word sys_getpid, sys_capget, sys_capset, sys_setuid, sys_getuid
/*25*/ .word sys_vmsplice, sys_ptrace, sys_alarm, sys_sigaltstack, sys_nis_syscall
/*30*/ .word sys_utime, sys_nis_syscall, sys_nis_syscall, sys_access, sys_nice
#include "linux/mm.h"
#include "linux/sched.h"
#include "linux/utsname.h"
+#include "linux/syscalls.h"
#include "asm/current.h"
#include "asm/mman.h"
#include "asm/uaccess.h"
return ret;
}
-/* common code for old and new mmaps */
-long sys_mmap2(unsigned long addr, unsigned long len,
- unsigned long prot, unsigned long flags,
- unsigned long fd, unsigned long pgoff)
-{
- long error = -EBADF;
- struct file * file = NULL;
-
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
- if (!(flags & MAP_ANONYMOUS)) {
- file = fget(fd);
- if (!file)
- goto out;
- }
-
- down_write(¤t->mm->mmap_sem);
- error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
- up_write(¤t->mm->mmap_sem);
-
- if (file)
- fput(file);
- out:
- return error;
-}
-
long old_mmap(unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags,
unsigned long fd, unsigned long offset)
if (offset & ~PAGE_MASK)
goto out;
- err = sys_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT);
+ err = sys_mmap_pgoff(addr, len, prot, flags, fd, offset >> PAGE_SHIFT);
out:
return err;
}
#define EXECUTE_SYSCALL(syscall, regs) \
((long (*)(struct syscall_args)) \
(*sys_call_table[syscall]))(SYSCALL_ARGS(®s->regs))
-
-extern long sys_mmap2(unsigned long addr, unsigned long len,
- unsigned long prot, unsigned long flags,
- unsigned long fd, unsigned long pgoff);
.quad quiet_ni_syscall /* streams2 */
.quad stub32_vfork /* 190 */
.quad compat_sys_getrlimit
- .quad sys32_mmap2
+ .quad sys_mmap_pgoff
.quad sys32_truncate64
.quad sys32_ftruncate64
.quad sys32_stat64 /* 195 */
asmlinkage long sys32_mmap(struct mmap_arg_struct __user *arg)
{
struct mmap_arg_struct a;
- struct file *file = NULL;
- unsigned long retval;
- struct mm_struct *mm ;
if (copy_from_user(&a, arg, sizeof(a)))
return -EFAULT;
if (a.offset & ~PAGE_MASK)
return -EINVAL;
- if (!(a.flags & MAP_ANONYMOUS)) {
- file = fget(a.fd);
- if (!file)
- return -EBADF;
- }
-
- mm = current->mm;
- down_write(&mm->mmap_sem);
- retval = do_mmap_pgoff(file, a.addr, a.len, a.prot, a.flags,
+ return sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd,
a.offset>>PAGE_SHIFT);
- if (file)
- fput(file);
-
- up_write(&mm->mmap_sem);
-
- return retval;
}
asmlinkage long sys32_mprotect(unsigned long start, size_t len,
return ret;
}
-asmlinkage long sys32_mmap2(unsigned long addr, unsigned long len,
- unsigned long prot, unsigned long flags,
- unsigned long fd, unsigned long pgoff)
-{
- struct mm_struct *mm = current->mm;
- unsigned long error;
- struct file *file = NULL;
-
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
- if (!(flags & MAP_ANONYMOUS)) {
- file = fget(fd);
- if (!file)
- return -EBADF;
- }
-
- down_write(&mm->mmap_sem);
- error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
- up_write(&mm->mmap_sem);
-
- if (file)
- fput(file);
- return error;
-}
-
asmlinkage long sys32_olduname(struct oldold_utsname __user *name)
{
char *arch = "x86_64";
/* pci-mmconfig.c */
+/* "PCI MMCONFIG %04x [bus %02x-%02x]" */
+#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2)
+
+struct pci_mmcfg_region {
+ struct list_head list;
+ struct resource res;
+ u64 address;
+ char __iomem *virt;
+ u16 segment;
+ u8 start_bus;
+ u8 end_bus;
+ char name[PCI_MMCFG_RESOURCE_NAME_LEN];
+};
+
extern int __init pci_mmcfg_arch_init(void);
extern void __init pci_mmcfg_arch_free(void);
+extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus);
+
+extern struct list_head pci_mmcfg_list;
-extern struct acpi_mcfg_allocation *pci_mmcfg_config;
-extern int pci_mmcfg_config_num;
+#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20)
/*
* AMD Fam10h CPUs are buggy, and cannot access MMIO config space
asmlinkage long sys32_personality(unsigned long);
asmlinkage long sys32_sendfile(int, int, compat_off_t __user *, s32);
-asmlinkage long sys32_mmap2(unsigned long, unsigned long, unsigned long,
- unsigned long, unsigned long, unsigned long);
-
struct oldold_utsname;
struct old_utsname;
asmlinkage long sys32_olduname(struct oldold_utsname __user *);
struct oldold_utsname;
struct old_utsname;
-asmlinkage long sys_mmap2(unsigned long, unsigned long, unsigned long,
- unsigned long, unsigned long, unsigned long);
asmlinkage int old_mmap(struct mmap_arg_struct __user *);
asmlinkage int old_select(struct sel_arg_struct __user *);
asmlinkage int sys_ipc(uint, int, int, int, void __user *, long);
extern struct shared_info *HYPERVISOR_shared_info;
extern struct start_info *xen_start_info;
-enum xen_domain_type {
- XEN_NATIVE, /* running on bare hardware */
- XEN_PV_DOMAIN, /* running in a PV domain */
- XEN_HVM_DOMAIN, /* running in a Xen hvm domain */
-};
-
-#ifdef CONFIG_XEN
-extern enum xen_domain_type xen_domain_type;
-#else
-#define xen_domain_type XEN_NATIVE
-#endif
-
-#define xen_domain() (xen_domain_type != XEN_NATIVE)
-#define xen_pv_domain() (xen_domain() && \
- xen_domain_type == XEN_PV_DOMAIN)
-#define xen_hvm_domain() (xen_domain() && \
- xen_domain_type == XEN_HVM_DOMAIN)
-
-#ifdef CONFIG_XEN_DOM0
-#include <xen/interface/xen.h>
-
-#define xen_initial_domain() (xen_pv_domain() && \
- xen_start_info->flags & SIF_INITDOMAIN)
-#else /* !CONFIG_XEN_DOM0 */
-#define xen_initial_domain() (0)
-#endif /* CONFIG_XEN_DOM0 */
-
#endif /* _ASM_X86_XEN_HYPERVISOR_H */
iommu_detected = 1;
amd_iommu_detected = 1;
x86_init.iommu.iommu_init = amd_iommu_init;
+
+ /* Make sure ACS will be enabled */
+ pci_request_acs();
}
}
#include <asm/syscalls.h>
-asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
- unsigned long prot, unsigned long flags,
- unsigned long fd, unsigned long pgoff)
-{
- int error = -EBADF;
- struct file *file = NULL;
- struct mm_struct *mm = current->mm;
-
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
- if (!(flags & MAP_ANONYMOUS)) {
- file = fget(fd);
- if (!file)
- goto out;
- }
-
- down_write(&mm->mmap_sem);
- error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
- up_write(&mm->mmap_sem);
-
- if (file)
- fput(file);
-out:
- return error;
-}
-
/*
* Perform the select(nd, in, out, ex, tv) and mmap() system
* calls. Linux/i386 didn't use to be able to handle more than
if (a.offset & ~PAGE_MASK)
goto out;
- err = sys_mmap2(a.addr, a.len, a.prot, a.flags,
+ err = sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags,
a.fd, a.offset >> PAGE_SHIFT);
out:
return err;
unsigned long, fd, unsigned long, off)
{
long error;
- struct file *file;
-
error = -EINVAL;
if (off & ~PAGE_MASK)
goto out;
- error = -EBADF;
- file = NULL;
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
- if (!(flags & MAP_ANONYMOUS)) {
- file = fget(fd);
- if (!file)
- goto out;
- }
- down_write(¤t->mm->mmap_sem);
- error = do_mmap_pgoff(file, addr, len, prot, flags, off >> PAGE_SHIFT);
- up_write(¤t->mm->mmap_sem);
-
- if (file)
- fput(file);
+ error = sys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
out:
return error;
}
.long sys_ni_syscall /* reserved for streams2 */
.long ptregs_vfork /* 190 */
.long sys_getrlimit
- .long sys_mmap2
+ .long sys_mmap_pgoff
.long sys_truncate64
.long sys_ftruncate64
.long sys_stat64 /* 195 */
obj-y += common.o early.o
obj-y += amd_bus.o
+obj-$(CONFIG_X86_64) += bus_numa.o intel_bus.o
+
+ifeq ($(CONFIG_PCI_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
#include <asm/pci_x86.h>
struct pci_root_info {
+ struct acpi_device *bridge;
char *name;
unsigned int res_num;
struct resource *res;
return false;
}
+static void
+align_resource(struct acpi_device *bridge, struct resource *res)
+{
+ int align = (res->flags & IORESOURCE_MEM) ? 16 : 4;
+
+ /*
+ * Host bridge windows are not BARs, but the decoders on the PCI side
+ * that claim this address space have starting alignment and length
+ * constraints, so fix any obvious BIOS goofs.
+ */
+ if (!IS_ALIGNED(res->start, align)) {
+ dev_printk(KERN_DEBUG, &bridge->dev,
+ "host bridge window %pR invalid; "
+ "aligning start to %d-byte boundary\n", res, align);
+ res->start &= ~(align - 1);
+ }
+ if (!IS_ALIGNED(res->end + 1, align)) {
+ dev_printk(KERN_DEBUG, &bridge->dev,
+ "host bridge window %pR invalid; "
+ "aligning end to %d-byte boundary\n", res, align);
+ res->end = ALIGN(res->end, align) - 1;
+ }
+}
+
static acpi_status
setup_resource(struct acpi_resource *acpi_res, void *data)
{
start = addr.minimum + addr.translation_offset;
end = start + addr.address_length - 1;
if (info->res_num >= max_root_bus_resources) {
- printk(KERN_WARNING "PCI: Failed to allocate 0x%lx-0x%lx "
- "from %s for %s due to _CRS returning more than "
- "%d resource descriptors\n", (unsigned long) start,
- (unsigned long) end, root->name, info->name,
- max_root_bus_resources);
+ if (pci_probe & PCI_USE__CRS)
+ printk(KERN_WARNING "PCI: Failed to allocate "
+ "0x%lx-0x%lx from %s for %s due to _CRS "
+ "returning more than %d resource descriptors\n",
+ (unsigned long) start, (unsigned long) end,
+ root->name, info->name, max_root_bus_resources);
return AE_OK;
}
res->start = start;
res->end = end;
res->child = NULL;
+ align_resource(info->bridge, res);
+
+ if (!(pci_probe & PCI_USE__CRS)) {
+ dev_printk(KERN_DEBUG, &info->bridge->dev,
+ "host bridge window %pR (ignored)\n", res);
+ return AE_OK;
+ }
if (insert_resource(root, res)) {
- printk(KERN_ERR "PCI: Failed to allocate 0x%lx-0x%lx "
- "from %s for %s\n", (unsigned long) res->start,
- (unsigned long) res->end, root->name, info->name);
+ dev_err(&info->bridge->dev,
+ "can't allocate host bridge window %pR\n", res);
} else {
info->bus->resource[info->res_num] = res;
info->res_num++;
+ if (addr.translation_offset)
+ dev_info(&info->bridge->dev, "host bridge window %pR "
+ "(PCI address [%#llx-%#llx])\n",
+ res, res->start - addr.translation_offset,
+ res->end - addr.translation_offset);
+ else
+ dev_info(&info->bridge->dev,
+ "host bridge window %pR\n", res);
}
return AE_OK;
}
struct pci_root_info info;
size_t size;
+ if (!(pci_probe & PCI_USE__CRS))
+ dev_info(&device->dev,
+ "ignoring host bridge windows from ACPI; "
+ "boot with \"pci=use_crs\" to use them\n");
+
+ info.bridge = device;
info.bus = bus;
info.res_num = 0;
acpi_walk_resources(device->handle, METHOD_NAME__CRS, count_resource,
#endif
if (domain && !pci_domains_supported) {
- printk(KERN_WARNING "PCI: Multiple domains not supported "
- "(dom %d, bus %d)\n", domain, busnum);
+ printk(KERN_WARNING "pci_bus %04x:%02x: "
+ "ignored (multiple domains not supported)\n",
+ domain, busnum);
return NULL;
}
*/
sd = kzalloc(sizeof(*sd), GFP_KERNEL);
if (!sd) {
- printk(KERN_ERR "PCI: OOM, not probing PCI bus %02x\n", busnum);
+ printk(KERN_WARNING "pci_bus %04x:%02x: "
+ "ignored (out of memory)\n", domain, busnum);
return NULL;
}
} else {
bus = pci_create_bus(NULL, busnum, &pci_root_ops, sd);
if (bus) {
- if (pci_probe & PCI_USE__CRS)
- get_current_resources(device, busnum, domain,
- bus);
+ get_current_resources(device, busnum, domain, bus);
bus->subordinate = pci_scan_child_bus(bus);
}
}
#ifdef CONFIG_X86_64
#include <asm/pci-direct.h>
-#include <asm/mpspec.h>
-#include <linux/cpumask.h>
#endif
+#include "bus_numa.h"
+
/*
* This discovers the pcibus <-> node mapping on AMD K8.
* also get peer root bus resource for io,mmio
#ifdef CONFIG_X86_64
-/*
- * sub bus (transparent) will use entres from 3 to store extra from root,
- * so need to make sure have enought slot there, increase PCI_BUS_NUM_RESOURCES?
- */
-#define RES_NUM 16
-struct pci_root_info {
- char name[12];
- unsigned int res_num;
- struct resource res[RES_NUM];
- int bus_min;
- int bus_max;
- int node;
- int link;
-};
-
-/* 4 at this time, it may become to 32 */
-#define PCI_ROOT_NR 4
-static int pci_root_num;
-static struct pci_root_info pci_root_info[PCI_ROOT_NR];
-
-void x86_pci_root_bus_res_quirks(struct pci_bus *b)
-{
- int i;
- int j;
- struct pci_root_info *info;
-
- /* don't go for it if _CRS is used already */
- if (b->resource[0] != &ioport_resource ||
- b->resource[1] != &iomem_resource)
- return;
-
- /* if only one root bus, don't need to anything */
- if (pci_root_num < 2)
- return;
-
- for (i = 0; i < pci_root_num; i++) {
- if (pci_root_info[i].bus_min == b->number)
- break;
- }
-
- if (i == pci_root_num)
- return;
-
- printk(KERN_DEBUG "PCI: peer root bus %02x res updated from pci conf\n",
- b->number);
-
- info = &pci_root_info[i];
- for (j = 0; j < info->res_num; j++) {
- struct resource *res;
- struct resource *root;
-
- res = &info->res[j];
- b->resource[j] = res;
- if (res->flags & IORESOURCE_IO)
- root = &ioport_resource;
- else
- root = &iomem_resource;
- insert_resource(root, res);
- }
-}
-
#define RANGE_NUM 16
struct res_range {
}
}
-static void __init update_res(struct pci_root_info *info, size_t start,
- size_t end, unsigned long flags, int merge)
-{
- int i;
- struct resource *res;
-
- if (!merge)
- goto addit;
-
- /* try to merge it with old one */
- for (i = 0; i < info->res_num; i++) {
- size_t final_start, final_end;
- size_t common_start, common_end;
-
- res = &info->res[i];
- if (res->flags != flags)
- continue;
-
- common_start = max((size_t)res->start, start);
- common_end = min((size_t)res->end, end);
- if (common_start > common_end + 1)
- continue;
-
- final_start = min((size_t)res->start, start);
- final_end = max((size_t)res->end, end);
-
- res->start = final_start;
- res->end = final_end;
- return;
- }
-
-addit:
-
- /* need to add that */
- if (info->res_num >= RES_NUM)
- return;
-
- res = &info->res[info->res_num];
- res->name = info->name;
- res->flags = flags;
- res->start = start;
- res->end = end;
- res->child = NULL;
- info->res_num++;
-}
-
struct pci_hostbridge_probe {
u32 bus;
u32 slot;
int j;
unsigned bus;
unsigned slot;
- int found;
int node;
int link;
int def_node;
if (!early_pci_allowed())
return -1;
- found = 0;
+ found_all_numa_early = 0;
for (i = 0; i < ARRAY_SIZE(pci_probes); i++) {
u32 id;
u16 device;
device = (id>>16) & 0xffff;
if (pci_probes[i].vendor == vendor &&
pci_probes[i].device == device) {
- found = 1;
+ found_all_numa_early = 1;
break;
}
}
- if (!found)
+ if (!found_all_numa_early)
return 0;
pci_root_num = 0;
info = &pci_root_info[i];
res_num = info->res_num;
busnum = info->bus_min;
- printk(KERN_DEBUG "bus: [%02x,%02x] on node %x link %x\n",
+ printk(KERN_DEBUG "bus: [%02x, %02x] on node %x link %x\n",
info->bus_min, info->bus_max, info->node, info->link);
for (j = 0; j < res_num; j++) {
res = &info->res[j];
--- /dev/null
+#include <linux/init.h>
+#include <linux/pci.h>
+
+#include "bus_numa.h"
+
+int pci_root_num;
+struct pci_root_info pci_root_info[PCI_ROOT_NR];
+int found_all_numa_early;
+
+void x86_pci_root_bus_res_quirks(struct pci_bus *b)
+{
+ int i;
+ int j;
+ struct pci_root_info *info;
+
+ /* don't go for it if _CRS is used already */
+ if (b->resource[0] != &ioport_resource ||
+ b->resource[1] != &iomem_resource)
+ return;
+
+ if (!pci_root_num)
+ return;
+
+ /* for amd, if only one root bus, don't need to do anything */
+ if (pci_root_num < 2 && found_all_numa_early)
+ return;
+
+ for (i = 0; i < pci_root_num; i++) {
+ if (pci_root_info[i].bus_min == b->number)
+ break;
+ }
+
+ if (i == pci_root_num)
+ return;
+
+ printk(KERN_DEBUG "PCI: peer root bus %02x res updated from pci conf\n",
+ b->number);
+
+ info = &pci_root_info[i];
+ for (j = 0; j < info->res_num; j++) {
+ struct resource *res;
+ struct resource *root;
+
+ res = &info->res[j];
+ b->resource[j] = res;
+ if (res->flags & IORESOURCE_IO)
+ root = &ioport_resource;
+ else
+ root = &iomem_resource;
+ insert_resource(root, res);
+ }
+}
+
+void __init update_res(struct pci_root_info *info, size_t start,
+ size_t end, unsigned long flags, int merge)
+{
+ int i;
+ struct resource *res;
+
+ if (start > end)
+ return;
+
+ if (!merge)
+ goto addit;
+
+ /* try to merge it with old one */
+ for (i = 0; i < info->res_num; i++) {
+ size_t final_start, final_end;
+ size_t common_start, common_end;
+
+ res = &info->res[i];
+ if (res->flags != flags)
+ continue;
+
+ common_start = max((size_t)res->start, start);
+ common_end = min((size_t)res->end, end);
+ if (common_start > common_end + 1)
+ continue;
+
+ final_start = min((size_t)res->start, start);
+ final_end = max((size_t)res->end, end);
+
+ res->start = final_start;
+ res->end = final_end;
+ return;
+ }
+
+addit:
+
+ /* need to add that */
+ if (info->res_num >= RES_NUM)
+ return;
+
+ res = &info->res[info->res_num];
+ res->name = info->name;
+ res->flags = flags;
+ res->start = start;
+ res->end = end;
+ res->child = NULL;
+ info->res_num++;
+}
--- /dev/null
+#ifdef CONFIG_X86_64
+
+/*
+ * sub bus (transparent) will use entres from 3 to store extra from
+ * root, so need to make sure we have enough slot there, Should we
+ * increase PCI_BUS_NUM_RESOURCES?
+ */
+#define RES_NUM 16
+struct pci_root_info {
+ char name[12];
+ unsigned int res_num;
+ struct resource res[RES_NUM];
+ int bus_min;
+ int bus_max;
+ int node;
+ int link;
+};
+
+/* 4 at this time, it may become to 32 */
+#define PCI_ROOT_NR 4
+extern int pci_root_num;
+extern struct pci_root_info pci_root_info[PCI_ROOT_NR];
+extern int found_all_numa_early;
+
+extern void update_res(struct pci_root_info *info, size_t start,
+ size_t end, unsigned long flags, int merge);
+#endif
return bus;
}
-extern u8 pci_cache_line_size;
-
int __init pcibios_init(void)
{
struct cpuinfo_x86 *c = &boot_cpu_data;
}
/*
- * Assume PCI cacheline size of 32 bytes for all x86s except K7/K8
- * and P4. It's also good for 386/486s (which actually have 16)
+ * Set PCI cacheline size to that of the CPU if the CPU has reported it.
+ * (For older CPUs that don't support cpuid, we se it to 32 bytes
+ * It's also good for 386/486s (which actually have 16)
* as quite a few PCI devices do not support smaller values.
*/
- pci_cache_line_size = 32 >> 2;
- if (c->x86 >= 6 && c->x86_vendor == X86_VENDOR_AMD)
- pci_cache_line_size = 64 >> 2; /* K7 & K8 */
- else if (c->x86 > 6 && c->x86_vendor == X86_VENDOR_INTEL)
- pci_cache_line_size = 128 >> 2; /* P4 */
+ if (c->x86_clflush_size > 0) {
+ pci_dfl_cache_line_size = c->x86_clflush_size >> 2;
+ printk(KERN_DEBUG "PCI: pci_cache_line_size set to %d bytes\n",
+ pci_dfl_cache_line_size << 2);
+ } else {
+ pci_dfl_cache_line_size = 32 >> 2;
+ printk(KERN_DEBUG "PCI: Unknown cacheline size. Setting to 32 bytes\n");
+ }
pcibios_resource_survey();
u32 v;
outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
v = inl(0xcfc);
- if (v != 0xffffffff)
- pr_debug("%x reading 4 from %x: %x\n", slot, offset, v);
return v;
}
u8 v;
outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
v = inb(0xcfc + (offset&3));
- pr_debug("%x reading 1 from %x: %x\n", slot, offset, v);
return v;
}
u16 v;
outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
v = inw(0xcfc + (offset&2));
- pr_debug("%x reading 2 from %x: %x\n", slot, offset, v);
return v;
}
void write_pci_config(u8 bus, u8 slot, u8 func, u8 offset,
u32 val)
{
- pr_debug("%x writing to %x: %x\n", slot, offset, val);
outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
outl(val, 0xcfc);
}
void write_pci_config_byte(u8 bus, u8 slot, u8 func, u8 offset, u8 val)
{
- pr_debug("%x writing to %x: %x\n", slot, offset, val);
outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
outb(val, 0xcfc + (offset&3));
}
void write_pci_config_16(u8 bus, u8 slot, u8 func, u8 offset, u16 val)
{
- pr_debug("%x writing to %x: %x\n", slot, offset, val);
outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
outw(val, 0xcfc + (offset&2));
}
continue;
if (!r->start ||
pci_claim_resource(dev, idx) < 0) {
- dev_info(&dev->dev, "BAR %d: can't allocate resource\n", idx);
+ dev_info(&dev->dev,
+ "can't reserve window %pR\n",
+ r);
/*
* Something is wrong with the region.
* Invalidate the resource to prevent
}
}
+struct pci_check_idx_range {
+ int start;
+ int end;
+};
+
static void __init pcibios_allocate_resources(int pass)
{
struct pci_dev *dev = NULL;
- int idx, disabled;
+ int idx, disabled, i;
u16 command;
struct resource *r;
+ struct pci_check_idx_range idx_range[] = {
+ { PCI_STD_RESOURCES, PCI_STD_RESOURCE_END },
+#ifdef CONFIG_PCI_IOV
+ { PCI_IOV_RESOURCES, PCI_IOV_RESOURCE_END },
+#endif
+ };
+
for_each_pci_dev(dev) {
pci_read_config_word(dev, PCI_COMMAND, &command);
- for (idx = 0; idx < PCI_ROM_RESOURCE; idx++) {
+ for (i = 0; i < ARRAY_SIZE(idx_range); i++)
+ for (idx = idx_range[i].start; idx <= idx_range[i].end; idx++) {
r = &dev->resource[idx];
if (r->parent) /* Already allocated */
continue;
else
disabled = !(command & PCI_COMMAND_MEMORY);
if (pass == disabled) {
- dev_dbg(&dev->dev, "resource %#08llx-%#08llx (f=%lx, d=%d, p=%d)\n",
- (unsigned long long) r->start,
- (unsigned long long) r->end,
- r->flags, disabled, pass);
+ dev_dbg(&dev->dev,
+ "BAR %d: reserving %pr (d=%d, p=%d)\n",
+ idx, r, disabled, pass);
if (pci_claim_resource(dev, idx) < 0) {
- dev_info(&dev->dev, "BAR %d: can't allocate resource\n", idx);
+ dev_info(&dev->dev,
+ "can't reserve %pR\n", r);
/* We'll assign a new address later */
r->end -= r->start;
r->start = 0;
/* Turn the ROM off, leave the resource region,
* but keep it unregistered. */
u32 reg;
- dev_dbg(&dev->dev, "disabling ROM\n");
+ dev_dbg(&dev->dev, "disabling ROM %pR\n", r);
r->flags &= ~IORESOURCE_ROM_ENABLE;
pci_read_config_dword(dev,
dev->rom_base_reg, ®);
return -EINVAL;
prot = pgprot_val(vma->vm_page_prot);
+
+ /*
+ * Return error if pat is not enabled and write_combine is requested.
+ * Caller can followup with UC MINUS request and add a WC mtrr if there
+ * is a free mtrr slot.
+ */
+ if (!pat_enabled && write_combine)
+ return -EINVAL;
+
if (pat_enabled && write_combine)
prot |= _PAGE_CACHE_WC;
else if (pat_enabled || boot_cpu_data.x86 > 3)
--- /dev/null
+/*
+ * to read io range from IOH pci conf, need to do it after mmconfig is there
+ */
+
+#include <linux/delay.h>
+#include <linux/dmi.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <asm/pci_x86.h>
+
+#include "bus_numa.h"
+
+static inline void print_ioh_resources(struct pci_root_info *info)
+{
+ int res_num;
+ int busnum;
+ int i;
+
+ printk(KERN_DEBUG "IOH bus: [%02x, %02x]\n",
+ info->bus_min, info->bus_max);
+ res_num = info->res_num;
+ busnum = info->bus_min;
+ for (i = 0; i < res_num; i++) {
+ struct resource *res;
+
+ res = &info->res[i];
+ printk(KERN_DEBUG "IOH bus: %02x index %x %s: [%llx, %llx]\n",
+ busnum, i,
+ (res->flags & IORESOURCE_IO) ? "io port" :
+ "mmio",
+ res->start, res->end);
+ }
+}
+
+#define IOH_LIO 0x108
+#define IOH_LMMIOL 0x10c
+#define IOH_LMMIOH 0x110
+#define IOH_LMMIOH_BASEU 0x114
+#define IOH_LMMIOH_LIMITU 0x118
+#define IOH_LCFGBUS 0x11c
+
+static void __devinit pci_root_bus_res(struct pci_dev *dev)
+{
+ u16 word;
+ u32 dword;
+ struct pci_root_info *info;
+ u16 io_base, io_end;
+ u32 mmiol_base, mmiol_end;
+ u64 mmioh_base, mmioh_end;
+ int bus_base, bus_end;
+
+ if (pci_root_num >= PCI_ROOT_NR) {
+ printk(KERN_DEBUG "intel_bus.c: PCI_ROOT_NR is too small\n");
+ return;
+ }
+
+ info = &pci_root_info[pci_root_num];
+ pci_root_num++;
+
+ pci_read_config_word(dev, IOH_LCFGBUS, &word);
+ bus_base = (word & 0xff);
+ bus_end = (word & 0xff00) >> 8;
+ sprintf(info->name, "PCI Bus #%02x", bus_base);
+ info->bus_min = bus_base;
+ info->bus_max = bus_end;
+
+ pci_read_config_word(dev, IOH_LIO, &word);
+ io_base = (word & 0xf0) << (12 - 4);
+ io_end = (word & 0xf000) | 0xfff;
+ update_res(info, io_base, io_end, IORESOURCE_IO, 0);
+
+ pci_read_config_dword(dev, IOH_LMMIOL, &dword);
+ mmiol_base = (dword & 0xff00) << (24 - 8);
+ mmiol_end = (dword & 0xff000000) | 0xffffff;
+ update_res(info, mmiol_base, mmiol_end, IORESOURCE_MEM, 0);
+
+ pci_read_config_dword(dev, IOH_LMMIOH, &dword);
+ mmioh_base = ((u64)(dword & 0xfc00)) << (26 - 10);
+ mmioh_end = ((u64)(dword & 0xfc000000) | 0x3ffffff);
+ pci_read_config_dword(dev, IOH_LMMIOH_BASEU, &dword);
+ mmioh_base |= ((u64)(dword & 0x7ffff)) << 32;
+ pci_read_config_dword(dev, IOH_LMMIOH_LIMITU, &dword);
+ mmioh_end |= ((u64)(dword & 0x7ffff)) << 32;
+ update_res(info, mmioh_base, mmioh_end, IORESOURCE_MEM, 0);
+
+ print_ioh_resources(info);
+}
+
+/* intel IOH */
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x342e, pci_root_bus_res);
#include <linux/acpi.h>
#include <linux/sfi_acpi.h>
#include <linux/bitmap.h>
-#include <linux/sort.h>
+#include <linux/dmi.h>
#include <asm/e820.h>
#include <asm/pci_x86.h>
#include <asm/acpi.h>
#define PREFIX "PCI: "
-/* aperture is up to 256MB but BIOS may reserve less */
-#define MMCONFIG_APER_MIN (2 * 1024*1024)
-#define MMCONFIG_APER_MAX (256 * 1024*1024)
-
/* Indicate if the mmcfg resources have been placed into the resource table. */
static int __initdata pci_mmcfg_resources_inserted;
-static __init int extend_mmcfg(int num)
+LIST_HEAD(pci_mmcfg_list);
+
+static __init void pci_mmconfig_remove(struct pci_mmcfg_region *cfg)
{
- struct acpi_mcfg_allocation *new;
- int new_num = pci_mmcfg_config_num + num;
+ if (cfg->res.parent)
+ release_resource(&cfg->res);
+ list_del(&cfg->list);
+ kfree(cfg);
+}
- new = kzalloc(sizeof(pci_mmcfg_config[0]) * new_num, GFP_KERNEL);
- if (!new)
- return -1;
+static __init void free_all_mmcfg(void)
+{
+ struct pci_mmcfg_region *cfg, *tmp;
- if (pci_mmcfg_config) {
- memcpy(new, pci_mmcfg_config,
- sizeof(pci_mmcfg_config[0]) * new_num);
- kfree(pci_mmcfg_config);
+ pci_mmcfg_arch_free();
+ list_for_each_entry_safe(cfg, tmp, &pci_mmcfg_list, list)
+ pci_mmconfig_remove(cfg);
+}
+
+static __init void list_add_sorted(struct pci_mmcfg_region *new)
+{
+ struct pci_mmcfg_region *cfg;
+
+ /* keep list sorted by segment and starting bus number */
+ list_for_each_entry(cfg, &pci_mmcfg_list, list) {
+ if (cfg->segment > new->segment ||
+ (cfg->segment == new->segment &&
+ cfg->start_bus >= new->start_bus)) {
+ list_add_tail(&new->list, &cfg->list);
+ return;
+ }
}
- pci_mmcfg_config = new;
+ list_add_tail(&new->list, &pci_mmcfg_list);
+}
- return 0;
+static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
+ int end, u64 addr)
+{
+ struct pci_mmcfg_region *new;
+ int num_buses;
+ struct resource *res;
+
+ if (addr == 0)
+ return NULL;
+
+ new = kzalloc(sizeof(*new), GFP_KERNEL);
+ if (!new)
+ return NULL;
+
+ new->address = addr;
+ new->segment = segment;
+ new->start_bus = start;
+ new->end_bus = end;
+
+ list_add_sorted(new);
+
+ num_buses = end - start + 1;
+ res = &new->res;
+ res->start = addr + PCI_MMCFG_BUS_OFFSET(start);
+ res->end = addr + PCI_MMCFG_BUS_OFFSET(num_buses) - 1;
+ res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+ snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN,
+ "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end);
+ res->name = new->name;
+
+ printk(KERN_INFO PREFIX "MMCONFIG for domain %04x [bus %02x-%02x] at "
+ "%pR (base %#lx)\n", segment, start, end, &new->res,
+ (unsigned long) addr);
+
+ return new;
}
-static __init void fill_one_mmcfg(u64 addr, int segment, int start, int end)
+struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
{
- int i = pci_mmcfg_config_num;
+ struct pci_mmcfg_region *cfg;
- pci_mmcfg_config_num++;
- pci_mmcfg_config[i].address = addr;
- pci_mmcfg_config[i].pci_segment = segment;
- pci_mmcfg_config[i].start_bus_number = start;
- pci_mmcfg_config[i].end_bus_number = end;
+ list_for_each_entry(cfg, &pci_mmcfg_list, list)
+ if (cfg->segment == segment &&
+ cfg->start_bus <= bus && bus <= cfg->end_bus)
+ return cfg;
+
+ return NULL;
}
static const char __init *pci_mmcfg_e7520(void)
if (win == 0x0000 || win == 0xf000)
return NULL;
- if (extend_mmcfg(1) == -1)
+ if (pci_mmconfig_add(0, 0, 255, win << 16) == NULL)
return NULL;
- fill_one_mmcfg(win << 16, 0, 0, 255);
-
return "Intel Corporation E7520 Memory Controller Hub";
}
if ((pciexbar & mask) >= 0xf0000000U)
return NULL;
- if (extend_mmcfg(1) == -1)
+ if (pci_mmconfig_add(0, 0, (len >> 20) - 1, pciexbar & mask) == NULL)
return NULL;
- fill_one_mmcfg(pciexbar & mask, 0, 0, (len >> 20) - 1);
-
return "Intel Corporation 945G/GZ/P/PL Express Memory Controller Hub";
}
u32 low, high, address;
u64 base, msr;
int i;
- unsigned segnbits = 0, busnbits;
+ unsigned segnbits = 0, busnbits, end_bus;
if (!(pci_probe & PCI_CHECK_ENABLE_AMD_MMCONF))
return NULL;
busnbits = 8;
}
- if (extend_mmcfg(1 << segnbits) == -1)
- return NULL;
-
+ end_bus = (1 << busnbits) - 1;
for (i = 0; i < (1 << segnbits); i++)
- fill_one_mmcfg(base + (1<<28) * i, i, 0, (1 << busnbits) - 1);
+ if (pci_mmconfig_add(i, 0, end_bus,
+ base + (1<<28) * i) == NULL) {
+ free_all_mmcfg();
+ return NULL;
+ }
return "AMD Family 10h NB";
}
/*
* do check if amd fam10h already took over
*/
- if (!acpi_disabled || pci_mmcfg_config_num || mcp55_checked)
+ if (!acpi_disabled || !list_empty(&pci_mmcfg_list) || mcp55_checked)
return NULL;
mcp55_checked = true;
if (!(extcfg & extcfg_enable_mask))
continue;
- if (extend_mmcfg(1) == -1)
- continue;
-
size_index = (extcfg & extcfg_size_mask) >> extcfg_size_shift;
base = extcfg & extcfg_base_mask[size_index];
/* base could > 4G */
base <<= extcfg_base_lshift;
start = (extcfg & extcfg_start_mask) >> extcfg_start_shift;
end = start + extcfg_sizebus[size_index] - 1;
- fill_one_mmcfg(base, 0, start, end);
+ if (pci_mmconfig_add(0, start, end, base) == NULL)
+ continue;
mcp55_mmconf_found++;
}
0x0369, pci_mmcfg_nvidia_mcp55 },
};
-static int __init cmp_mmcfg(const void *x1, const void *x2)
-{
- const typeof(pci_mmcfg_config[0]) *m1 = x1;
- const typeof(pci_mmcfg_config[0]) *m2 = x2;
- int start1, start2;
-
- start1 = m1->start_bus_number;
- start2 = m2->start_bus_number;
-
- return start1 - start2;
-}
-
static void __init pci_mmcfg_check_end_bus_number(void)
{
- int i;
- typeof(pci_mmcfg_config[0]) *cfg, *cfgx;
-
- /* sort them at first */
- sort(pci_mmcfg_config, pci_mmcfg_config_num,
- sizeof(pci_mmcfg_config[0]), cmp_mmcfg, NULL);
+ struct pci_mmcfg_region *cfg, *cfgx;
/* last one*/
- if (pci_mmcfg_config_num > 0) {
- i = pci_mmcfg_config_num - 1;
- cfg = &pci_mmcfg_config[i];
- if (cfg->end_bus_number < cfg->start_bus_number)
- cfg->end_bus_number = 255;
- }
+ cfg = list_entry(pci_mmcfg_list.prev, typeof(*cfg), list);
+ if (cfg)
+ if (cfg->end_bus < cfg->start_bus)
+ cfg->end_bus = 255;
- /* don't overlap please */
- for (i = 0; i < pci_mmcfg_config_num - 1; i++) {
- cfg = &pci_mmcfg_config[i];
- cfgx = &pci_mmcfg_config[i+1];
+ if (list_is_singular(&pci_mmcfg_list))
+ return;
- if (cfg->end_bus_number < cfg->start_bus_number)
- cfg->end_bus_number = 255;
+ /* don't overlap please */
+ list_for_each_entry(cfg, &pci_mmcfg_list, list) {
+ if (cfg->end_bus < cfg->start_bus)
+ cfg->end_bus = 255;
- if (cfg->end_bus_number >= cfgx->start_bus_number)
- cfg->end_bus_number = cfgx->start_bus_number - 1;
+ cfgx = list_entry(cfg->list.next, typeof(*cfg), list);
+ if (cfg != cfgx && cfg->end_bus >= cfgx->start_bus)
+ cfg->end_bus = cfgx->start_bus - 1;
}
}
if (!raw_pci_ops)
return 0;
- pci_mmcfg_config_num = 0;
- pci_mmcfg_config = NULL;
+ free_all_mmcfg();
for (i = 0; i < ARRAY_SIZE(pci_mmcfg_probes); i++) {
bus = pci_mmcfg_probes[i].bus;
name = pci_mmcfg_probes[i].probe();
if (name)
- printk(KERN_INFO "PCI: Found %s with MMCONFIG support.\n",
+ printk(KERN_INFO PREFIX "%s with MMCONFIG support\n",
name);
}
/* some end_bus_number is crazy, fix it */
pci_mmcfg_check_end_bus_number();
- return pci_mmcfg_config_num != 0;
+ return !list_empty(&pci_mmcfg_list);
}
static void __init pci_mmcfg_insert_resources(void)
{
-#define PCI_MMCFG_RESOURCE_NAME_LEN 24
- int i;
- struct resource *res;
- char *names;
- unsigned num_buses;
-
- res = kcalloc(PCI_MMCFG_RESOURCE_NAME_LEN + sizeof(*res),
- pci_mmcfg_config_num, GFP_KERNEL);
- if (!res) {
- printk(KERN_ERR "PCI: Unable to allocate MMCONFIG resources\n");
- return;
- }
+ struct pci_mmcfg_region *cfg;
- names = (void *)&res[pci_mmcfg_config_num];
- for (i = 0; i < pci_mmcfg_config_num; i++, res++) {
- struct acpi_mcfg_allocation *cfg = &pci_mmcfg_config[i];
- num_buses = cfg->end_bus_number - cfg->start_bus_number + 1;
- res->name = names;
- snprintf(names, PCI_MMCFG_RESOURCE_NAME_LEN,
- "PCI MMCONFIG %u [%02x-%02x]", cfg->pci_segment,
- cfg->start_bus_number, cfg->end_bus_number);
- res->start = cfg->address + (cfg->start_bus_number << 20);
- res->end = res->start + (num_buses << 20) - 1;
- res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
- insert_resource(&iomem_resource, res);
- names += PCI_MMCFG_RESOURCE_NAME_LEN;
- }
+ list_for_each_entry(cfg, &pci_mmcfg_list, list)
+ insert_resource(&iomem_resource, &cfg->res);
/* Mark that the resources have been inserted. */
pci_mmcfg_resources_inserted = 1;
typedef int (*check_reserved_t)(u64 start, u64 end, unsigned type);
static int __init is_mmconf_reserved(check_reserved_t is_reserved,
- u64 addr, u64 size, int i,
- typeof(pci_mmcfg_config[0]) *cfg, int with_e820)
+ struct pci_mmcfg_region *cfg, int with_e820)
{
+ u64 addr = cfg->res.start;
+ u64 size = resource_size(&cfg->res);
u64 old_size = size;
- int valid = 0;
+ int valid = 0, num_buses;
while (!is_reserved(addr, addr + size, E820_RESERVED)) {
size >>= 1;
}
if (size >= (16UL<<20) || size == old_size) {
- printk(KERN_NOTICE
- "PCI: MCFG area at %Lx reserved in %s\n",
- addr, with_e820?"E820":"ACPI motherboard resources");
+ printk(KERN_INFO PREFIX "MMCONFIG at %pR reserved in %s\n",
+ &cfg->res,
+ with_e820 ? "E820" : "ACPI motherboard resources");
valid = 1;
if (old_size != size) {
- /* update end_bus_number */
- cfg->end_bus_number = cfg->start_bus_number + ((size>>20) - 1);
- printk(KERN_NOTICE "PCI: updated MCFG configuration %d: base %lx "
- "segment %hu buses %u - %u\n",
- i, (unsigned long)cfg->address, cfg->pci_segment,
- (unsigned int)cfg->start_bus_number,
- (unsigned int)cfg->end_bus_number);
+ /* update end_bus */
+ cfg->end_bus = cfg->start_bus + ((size>>20) - 1);
+ num_buses = cfg->end_bus - cfg->start_bus + 1;
+ cfg->res.end = cfg->res.start +
+ PCI_MMCFG_BUS_OFFSET(num_buses) - 1;
+ snprintf(cfg->name, PCI_MMCFG_RESOURCE_NAME_LEN,
+ "PCI MMCONFIG %04x [bus %02x-%02x]",
+ cfg->segment, cfg->start_bus, cfg->end_bus);
+ printk(KERN_INFO PREFIX
+ "MMCONFIG for %04x [bus%02x-%02x] "
+ "at %pR (base %#lx) (size reduced!)\n",
+ cfg->segment, cfg->start_bus, cfg->end_bus,
+ &cfg->res, (unsigned long) cfg->address);
}
}
static void __init pci_mmcfg_reject_broken(int early)
{
- typeof(pci_mmcfg_config[0]) *cfg;
- int i;
+ struct pci_mmcfg_region *cfg;
- if ((pci_mmcfg_config_num == 0) ||
- (pci_mmcfg_config == NULL) ||
- (pci_mmcfg_config[0].address == 0))
- return;
-
- for (i = 0; i < pci_mmcfg_config_num; i++) {
+ list_for_each_entry(cfg, &pci_mmcfg_list, list) {
int valid = 0;
- u64 addr, size;
-
- cfg = &pci_mmcfg_config[i];
- addr = cfg->start_bus_number;
- addr <<= 20;
- addr += cfg->address;
- size = cfg->end_bus_number + 1 - cfg->start_bus_number;
- size <<= 20;
- printk(KERN_NOTICE "PCI: MCFG configuration %d: base %lx "
- "segment %hu buses %u - %u\n",
- i, (unsigned long)cfg->address, cfg->pci_segment,
- (unsigned int)cfg->start_bus_number,
- (unsigned int)cfg->end_bus_number);
if (!early && !acpi_disabled)
- valid = is_mmconf_reserved(is_acpi_reserved, addr, size, i, cfg, 0);
+ valid = is_mmconf_reserved(is_acpi_reserved, cfg, 0);
if (valid)
continue;
if (!early)
- printk(KERN_ERR "PCI: BIOS Bug: MCFG area at %Lx is not"
- " reserved in ACPI motherboard resources\n",
- cfg->address);
+ printk(KERN_ERR FW_BUG PREFIX
+ "MMCONFIG at %pR not reserved in "
+ "ACPI motherboard resources\n", &cfg->res);
/* Don't try to do this check unless configuration
type 1 is available. how about type 2 ?*/
if (raw_pci_ops)
- valid = is_mmconf_reserved(e820_all_mapped, addr, size, i, cfg, 1);
+ valid = is_mmconf_reserved(e820_all_mapped, cfg, 1);
if (!valid)
goto reject;
return;
reject:
- printk(KERN_INFO "PCI: Not using MMCONFIG.\n");
- pci_mmcfg_arch_free();
- kfree(pci_mmcfg_config);
- pci_mmcfg_config = NULL;
- pci_mmcfg_config_num = 0;
+ printk(KERN_INFO PREFIX "not using MMCONFIG\n");
+ free_all_mmcfg();
}
static int __initdata known_bridge;
-static int acpi_mcfg_64bit_base_addr __initdata = FALSE;
+static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
+ struct acpi_mcfg_allocation *cfg)
+{
+ int year;
-/* The physical address of the MMCONFIG aperture. Set from ACPI tables. */
-struct acpi_mcfg_allocation *pci_mmcfg_config;
-int pci_mmcfg_config_num;
+ if (cfg->address < 0xFFFFFFFF)
+ return 0;
-static int __init acpi_mcfg_oem_check(struct acpi_table_mcfg *mcfg)
-{
if (!strcmp(mcfg->header.oem_id, "SGI"))
- acpi_mcfg_64bit_base_addr = TRUE;
+ return 0;
- return 0;
+ if (mcfg->header.revision >= 1) {
+ if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) &&
+ year >= 2010)
+ return 0;
+ }
+
+ printk(KERN_ERR PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx "
+ "is above 4GB, ignored\n", cfg->pci_segment,
+ cfg->start_bus_number, cfg->end_bus_number, cfg->address);
+ return -EINVAL;
}
static int __init pci_parse_mcfg(struct acpi_table_header *header)
{
struct acpi_table_mcfg *mcfg;
+ struct acpi_mcfg_allocation *cfg_table, *cfg;
unsigned long i;
- int config_size;
+ int entries;
if (!header)
return -EINVAL;
mcfg = (struct acpi_table_mcfg *)header;
/* how many config structures do we have */
- pci_mmcfg_config_num = 0;
+ free_all_mmcfg();
+ entries = 0;
i = header->length - sizeof(struct acpi_table_mcfg);
while (i >= sizeof(struct acpi_mcfg_allocation)) {
- ++pci_mmcfg_config_num;
+ entries++;
i -= sizeof(struct acpi_mcfg_allocation);
};
- if (pci_mmcfg_config_num == 0) {
+ if (entries == 0) {
printk(KERN_ERR PREFIX "MMCONFIG has no entries\n");
return -ENODEV;
}
- config_size = pci_mmcfg_config_num * sizeof(*pci_mmcfg_config);
- pci_mmcfg_config = kmalloc(config_size, GFP_KERNEL);
- if (!pci_mmcfg_config) {
- printk(KERN_WARNING PREFIX
- "No memory for MCFG config tables\n");
- return -ENOMEM;
- }
-
- memcpy(pci_mmcfg_config, &mcfg[1], config_size);
-
- acpi_mcfg_oem_check(mcfg);
-
- for (i = 0; i < pci_mmcfg_config_num; ++i) {
- if ((pci_mmcfg_config[i].address > 0xFFFFFFFF) &&
- !acpi_mcfg_64bit_base_addr) {
- printk(KERN_ERR PREFIX
- "MMCONFIG not in low 4GB of memory\n");
- kfree(pci_mmcfg_config);
- pci_mmcfg_config_num = 0;
+ cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1];
+ for (i = 0; i < entries; i++) {
+ cfg = &cfg_table[i];
+ if (acpi_mcfg_check_entry(mcfg, cfg)) {
+ free_all_mmcfg();
return -ENODEV;
}
+
+ if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number,
+ cfg->end_bus_number, cfg->address) == NULL) {
+ printk(KERN_WARNING PREFIX
+ "no memory for MCFG entries\n");
+ free_all_mmcfg();
+ return -ENOMEM;
+ }
}
return 0;
pci_mmcfg_reject_broken(early);
- if ((pci_mmcfg_config_num == 0) ||
- (pci_mmcfg_config == NULL) ||
- (pci_mmcfg_config[0].address == 0))
+ if (list_empty(&pci_mmcfg_list))
return;
if (pci_mmcfg_arch_init())
*/
if ((pci_mmcfg_resources_inserted == 1) ||
(pci_probe & PCI_PROBE_MMCONF) == 0 ||
- (pci_mmcfg_config_num == 0) ||
- (pci_mmcfg_config == NULL) ||
- (pci_mmcfg_config[0].address == 0))
+ list_empty(&pci_mmcfg_list))
return 1;
/*
*/
static u32 get_base_addr(unsigned int seg, int bus, unsigned devfn)
{
- struct acpi_mcfg_allocation *cfg;
- int cfg_num;
-
- for (cfg_num = 0; cfg_num < pci_mmcfg_config_num; cfg_num++) {
- cfg = &pci_mmcfg_config[cfg_num];
- if (cfg->pci_segment == seg &&
- (cfg->start_bus_number <= bus) &&
- (cfg->end_bus_number >= bus))
- return cfg->address;
- }
+ struct pci_mmcfg_region *cfg = pci_mmconfig_lookup(seg, bus);
- /* Fall back to type 0 */
+ if (cfg)
+ return cfg->address;
return 0;
}
*/
static void pci_exp_set_dev_base(unsigned int base, int bus, int devfn)
{
- u32 dev_base = base | (bus << 20) | (devfn << 12);
+ u32 dev_base = base | PCI_MMCFG_BUS_OFFSET(bus) | (devfn << 12);
int cpu = smp_processor_id();
if (dev_base != mmcfg_last_accessed_device ||
cpu != mmcfg_last_accessed_cpu) {
#include <asm/e820.h>
#include <asm/pci_x86.h>
-/* Static virtual mapping of the MMCONFIG aperture */
-struct mmcfg_virt {
- struct acpi_mcfg_allocation *cfg;
- char __iomem *virt;
-};
-static struct mmcfg_virt *pci_mmcfg_virt;
-
-static char __iomem *get_virt(unsigned int seg, unsigned bus)
-{
- struct acpi_mcfg_allocation *cfg;
- int cfg_num;
-
- for (cfg_num = 0; cfg_num < pci_mmcfg_config_num; cfg_num++) {
- cfg = pci_mmcfg_virt[cfg_num].cfg;
- if (cfg->pci_segment == seg &&
- (cfg->start_bus_number <= bus) &&
- (cfg->end_bus_number >= bus))
- return pci_mmcfg_virt[cfg_num].virt;
- }
-
- /* Fall back to type 0 */
- return NULL;
-}
+#define PREFIX "PCI: "
static char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn)
{
- char __iomem *addr;
+ struct pci_mmcfg_region *cfg = pci_mmconfig_lookup(seg, bus);
- addr = get_virt(seg, bus);
- if (!addr)
- return NULL;
- return addr + ((bus << 20) | (devfn << 12));
+ if (cfg && cfg->virt)
+ return cfg->virt + (PCI_MMCFG_BUS_OFFSET(bus) | (devfn << 12));
+ return NULL;
}
static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
.write = pci_mmcfg_write,
};
-static void __iomem * __init mcfg_ioremap(struct acpi_mcfg_allocation *cfg)
+static void __iomem * __init mcfg_ioremap(struct pci_mmcfg_region *cfg)
{
void __iomem *addr;
u64 start, size;
+ int num_buses;
- start = cfg->start_bus_number;
- start <<= 20;
- start += cfg->address;
- size = cfg->end_bus_number + 1 - cfg->start_bus_number;
- size <<= 20;
+ start = cfg->address + PCI_MMCFG_BUS_OFFSET(cfg->start_bus);
+ num_buses = cfg->end_bus - cfg->start_bus + 1;
+ size = PCI_MMCFG_BUS_OFFSET(num_buses);
addr = ioremap_nocache(start, size);
- if (addr) {
- printk(KERN_INFO "PCI: Using MMCONFIG at %Lx - %Lx\n",
- start, start + size - 1);
- addr -= cfg->start_bus_number << 20;
- }
+ if (addr)
+ addr -= PCI_MMCFG_BUS_OFFSET(cfg->start_bus);
return addr;
}
int __init pci_mmcfg_arch_init(void)
{
- int i;
- pci_mmcfg_virt = kzalloc(sizeof(*pci_mmcfg_virt) *
- pci_mmcfg_config_num, GFP_KERNEL);
- if (pci_mmcfg_virt == NULL) {
- printk(KERN_ERR "PCI: Can not allocate memory for mmconfig structures\n");
- return 0;
- }
+ struct pci_mmcfg_region *cfg;
- for (i = 0; i < pci_mmcfg_config_num; ++i) {
- pci_mmcfg_virt[i].cfg = &pci_mmcfg_config[i];
- pci_mmcfg_virt[i].virt = mcfg_ioremap(&pci_mmcfg_config[i]);
- if (!pci_mmcfg_virt[i].virt) {
- printk(KERN_ERR "PCI: Cannot map mmconfig aperture for "
- "segment %d\n",
- pci_mmcfg_config[i].pci_segment);
+ list_for_each_entry(cfg, &pci_mmcfg_list, list) {
+ cfg->virt = mcfg_ioremap(cfg);
+ if (!cfg->virt) {
+ printk(KERN_ERR PREFIX "can't map MMCONFIG at %pR\n",
+ &cfg->res);
pci_mmcfg_arch_free();
return 0;
}
void __init pci_mmcfg_arch_free(void)
{
- int i;
-
- if (pci_mmcfg_virt == NULL)
- return;
+ struct pci_mmcfg_region *cfg;
- for (i = 0; i < pci_mmcfg_config_num; ++i) {
- if (pci_mmcfg_virt[i].virt) {
- iounmap(pci_mmcfg_virt[i].virt + (pci_mmcfg_virt[i].cfg->start_bus_number << 20));
- pci_mmcfg_virt[i].virt = NULL;
- pci_mmcfg_virt[i].cfg = NULL;
+ list_for_each_entry(cfg, &pci_mmcfg_list, list) {
+ if (cfg->virt) {
+ iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus));
+ cfg->virt = NULL;
}
}
-
- kfree(pci_mmcfg_virt);
- pci_mmcfg_virt = NULL;
}
#include <linux/page-flags.h>
#include <linux/highmem.h>
#include <linux/console.h>
+#include <linux/pci.h>
+#include <xen/xen.h>
#include <xen/interface/xen.h>
#include <xen/interface/version.h>
#include <xen/interface/physdev.h>
add_preferred_console("xenboot", 0, NULL);
add_preferred_console("tty", 0, NULL);
add_preferred_console("hvc", 0, NULL);
+ } else {
+ /* Make sure ACS will be enabled */
+ pci_request_acs();
}
+
xen_raw_console_write("about to get started...\n");
asmlinkage long xtensa_execve(char*, char**, char**, struct pt_regs*);
asmlinkage long xtensa_clone(unsigned long, unsigned long, struct pt_regs*);
asmlinkage long xtensa_pipe(int __user *);
-asmlinkage long xtensa_mmap2(unsigned long, unsigned long, unsigned long,
- unsigned long, unsigned long, unsigned long);
asmlinkage long xtensa_ptrace(long, long, long, long);
asmlinkage long xtensa_sigreturn(struct pt_regs*);
asmlinkage long xtensa_rt_sigreturn(struct pt_regs*);
/* File Map / Shared Memory Operations */
#define __NR_mmap2 80
-__SYSCALL( 80, xtensa_mmap2, 6)
+__SYSCALL( 80, sys_mmap_pgoff, 6)
#define __NR_munmap 81
__SYSCALL( 81, sys_munmap, 2)
#define __NR_mprotect 82
return error;
}
-
-asmlinkage long xtensa_mmap2(unsigned long addr, unsigned long len,
- unsigned long prot, unsigned long flags,
- unsigned long fd, unsigned long pgoff)
-{
- int error = -EBADF;
- struct file * file = NULL;
-
- flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
- if (!(flags & MAP_ANONYMOUS)) {
- file = fget(fd);
- if (!file)
- goto out;
- }
-
- down_write(¤t->mm->mmap_sem);
- error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
- up_write(¤t->mm->mmap_sem);
-
- if (file)
- fput(file);
-out:
- return error;
-}
-
asmlinkage long xtensa_shmat(int shmid, char __user *shmaddr, int shmflg)
{
unsigned long ret;
# All the builtin files are in the "acpi." module_param namespace.
acpi-y += osl.o utils.o reboot.o
+acpi-y += hest.o
# sleep related files
acpi-y += wakeup.o
--- /dev/null
+#include <linux/acpi.h>
+#include <linux/pci.h>
+
+#define PREFIX "ACPI: "
+
+static inline unsigned long parse_acpi_hest_ia_machine_check(struct acpi_hest_ia_machine_check *p)
+{
+ return sizeof(*p) +
+ (sizeof(struct acpi_hest_ia_error_bank) * p->num_hardware_banks);
+}
+
+static inline unsigned long parse_acpi_hest_ia_corrected(struct acpi_hest_ia_corrected *p)
+{
+ return sizeof(*p) +
+ (sizeof(struct acpi_hest_ia_error_bank) * p->num_hardware_banks);
+}
+
+static inline unsigned long parse_acpi_hest_ia_nmi(struct acpi_hest_ia_nmi *p)
+{
+ return sizeof(*p);
+}
+
+static inline unsigned long parse_acpi_hest_generic(struct acpi_hest_generic *p)
+{
+ return sizeof(*p);
+}
+
+static inline unsigned int hest_match_pci(struct acpi_hest_aer_common *p, struct pci_dev *pci)
+{
+ return (0 == pci_domain_nr(pci->bus) &&
+ p->bus == pci->bus->number &&
+ p->device == PCI_SLOT(pci->devfn) &&
+ p->function == PCI_FUNC(pci->devfn));
+}
+
+static unsigned long parse_acpi_hest_aer(void *hdr, int type, struct pci_dev *pci, int *firmware_first)
+{
+ struct acpi_hest_aer_common *p = hdr + sizeof(struct acpi_hest_header);
+ unsigned long rc=0;
+ u8 pcie_type = 0;
+ u8 bridge = 0;
+ switch (type) {
+ case ACPI_HEST_TYPE_AER_ROOT_PORT:
+ rc = sizeof(struct acpi_hest_aer_root);
+ pcie_type = PCI_EXP_TYPE_ROOT_PORT;
+ break;
+ case ACPI_HEST_TYPE_AER_ENDPOINT:
+ rc = sizeof(struct acpi_hest_aer);
+ pcie_type = PCI_EXP_TYPE_ENDPOINT;
+ break;
+ case ACPI_HEST_TYPE_AER_BRIDGE:
+ rc = sizeof(struct acpi_hest_aer_bridge);
+ if ((pci->class >> 16) == PCI_BASE_CLASS_BRIDGE)
+ bridge = 1;
+ break;
+ }
+
+ if (p->flags & ACPI_HEST_GLOBAL) {
+ if ((pci->is_pcie && (pci->pcie_type == pcie_type)) || bridge)
+ *firmware_first = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST);
+ }
+ else
+ if (hest_match_pci(p, pci))
+ *firmware_first = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST);
+ return rc;
+}
+
+static int acpi_hest_firmware_first(struct acpi_table_header *stdheader, struct pci_dev *pci)
+{
+ struct acpi_table_hest *hest = (struct acpi_table_hest *)stdheader;
+ void *p = (void *)hest + sizeof(*hest); /* defined by the ACPI 4.0 spec */
+ struct acpi_hest_header *hdr = p;
+
+ int i;
+ int firmware_first = 0;
+ static unsigned char printed_unused = 0;
+ static unsigned char printed_reserved = 0;
+
+ for (i=0, hdr=p; p < (((void *)hest) + hest->header.length) && i < hest->error_source_count; i++) {
+ switch (hdr->type) {
+ case ACPI_HEST_TYPE_IA32_CHECK:
+ p += parse_acpi_hest_ia_machine_check(p);
+ break;
+ case ACPI_HEST_TYPE_IA32_CORRECTED_CHECK:
+ p += parse_acpi_hest_ia_corrected(p);
+ break;
+ case ACPI_HEST_TYPE_IA32_NMI:
+ p += parse_acpi_hest_ia_nmi(p);
+ break;
+ /* These three should never appear */
+ case ACPI_HEST_TYPE_NOT_USED3:
+ case ACPI_HEST_TYPE_NOT_USED4:
+ case ACPI_HEST_TYPE_NOT_USED5:
+ if (!printed_unused) {
+ printk(KERN_DEBUG PREFIX
+ "HEST Error Source list contains an obsolete type (%d).\n", hdr->type);
+ printed_unused = 1;
+ }
+ break;
+ case ACPI_HEST_TYPE_AER_ROOT_PORT:
+ case ACPI_HEST_TYPE_AER_ENDPOINT:
+ case ACPI_HEST_TYPE_AER_BRIDGE:
+ p += parse_acpi_hest_aer(p, hdr->type, pci, &firmware_first);
+ break;
+ case ACPI_HEST_TYPE_GENERIC_ERROR:
+ p += parse_acpi_hest_generic(p);
+ break;
+ /* These should never appear either */
+ case ACPI_HEST_TYPE_RESERVED:
+ default:
+ if (!printed_reserved) {
+ printk(KERN_DEBUG PREFIX
+ "HEST Error Source list contains a reserved type (%d).\n", hdr->type);
+ printed_reserved = 1;
+ }
+ break;
+ }
+ }
+ return firmware_first;
+}
+
+int acpi_hest_firmware_first_pci(struct pci_dev *pci)
+{
+ acpi_status status = AE_NOT_FOUND;
+ struct acpi_table_header *hest = NULL;
+ status = acpi_get_table(ACPI_SIG_HEST, 1, &hest);
+
+ if (ACPI_SUCCESS(status)) {
+ if (acpi_hest_firmware_first(hest, pci)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_hest_firmware_first_pci);
#include <linux/module.h>
#include <linux/scatterlist.h>
+#include <xen/xen.h>
#include <xen/xenbus.h>
#include <xen/grant_table.h>
#include <xen/events.h>
#include <linux/types.h>
#include <asm/xen/hypervisor.h>
+
+#include <xen/xen.h>
#include <xen/page.h>
#include <xen/events.h>
#include <xen/interface/io/console.h>
obj-$(CONFIG_DRM_SIS) += sis/
obj-$(CONFIG_DRM_SAVAGE)+= savage/
obj-$(CONFIG_DRM_VIA) +=via/
+obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
+obj-y += i2c/
--- /dev/null
+ccflags-y := -Iinclude/drm
+
+ch7006-y := ch7006_drv.o ch7006_mode.o
+obj-$(CONFIG_DRM_I2C_CH7006) += ch7006.o
--- /dev/null
+/*
+ * Copyright (C) 2009 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (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 (including the
+ * next paragraph) 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 COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "ch7006_priv.h"
+
+/* DRM encoder functions */
+
+static void ch7006_encoder_set_config(struct drm_encoder *encoder,
+ void *params)
+{
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+
+ priv->params = params;
+}
+
+static void ch7006_encoder_destroy(struct drm_encoder *encoder)
+{
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+
+ drm_property_destroy(encoder->dev, priv->scale_property);
+
+ kfree(priv);
+ to_encoder_slave(encoder)->slave_priv = NULL;
+
+ drm_i2c_encoder_destroy(encoder);
+}
+
+static void ch7006_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+ struct ch7006_state *state = &priv->state;
+
+ ch7006_dbg(client, "\n");
+
+ if (mode == priv->last_dpms)
+ return;
+ priv->last_dpms = mode;
+
+ ch7006_setup_power_state(encoder);
+
+ ch7006_load_reg(client, state, CH7006_POWER);
+}
+
+static void ch7006_encoder_save(struct drm_encoder *encoder)
+{
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+
+ ch7006_dbg(client, "\n");
+
+ ch7006_state_save(client, &priv->saved_state);
+}
+
+static void ch7006_encoder_restore(struct drm_encoder *encoder)
+{
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+
+ ch7006_dbg(client, "\n");
+
+ ch7006_state_load(client, &priv->saved_state);
+}
+
+static bool ch7006_encoder_mode_fixup(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+
+ /* The ch7006 is painfully picky with the input timings so no
+ * custom modes for now... */
+
+ priv->mode = ch7006_lookup_mode(encoder, mode);
+
+ return !!priv->mode;
+}
+
+static int ch7006_encoder_mode_valid(struct drm_encoder *encoder,
+ struct drm_display_mode *mode)
+{
+ if (ch7006_lookup_mode(encoder, mode))
+ return MODE_OK;
+ else
+ return MODE_BAD;
+}
+
+static void ch7006_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *drm_mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+ struct ch7006_encoder_params *params = priv->params;
+ struct ch7006_state *state = &priv->state;
+ uint8_t *regs = state->regs;
+ struct ch7006_mode *mode = priv->mode;
+ struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
+ int start_active;
+
+ ch7006_dbg(client, "\n");
+
+ regs[CH7006_DISPMODE] = norm->dispmode | mode->dispmode;
+ regs[CH7006_BWIDTH] = 0;
+ regs[CH7006_INPUT_FORMAT] = bitf(CH7006_INPUT_FORMAT_FORMAT,
+ params->input_format);
+
+ regs[CH7006_CLKMODE] = CH7006_CLKMODE_SUBC_LOCK
+ | bitf(CH7006_CLKMODE_XCM, params->xcm)
+ | bitf(CH7006_CLKMODE_PCM, params->pcm);
+ if (params->clock_mode)
+ regs[CH7006_CLKMODE] |= CH7006_CLKMODE_MASTER;
+ if (params->clock_edge)
+ regs[CH7006_CLKMODE] |= CH7006_CLKMODE_POS_EDGE;
+
+ start_active = (drm_mode->htotal & ~0x7) - (drm_mode->hsync_start & ~0x7);
+ regs[CH7006_POV] = bitf(CH7006_POV_START_ACTIVE_8, start_active);
+ regs[CH7006_START_ACTIVE] = bitf(CH7006_START_ACTIVE_0, start_active);
+
+ regs[CH7006_INPUT_SYNC] = 0;
+ if (params->sync_direction)
+ regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_OUTPUT;
+ if (params->sync_encoding)
+ regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_EMBEDDED;
+ if (drm_mode->flags & DRM_MODE_FLAG_PVSYNC)
+ regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_PVSYNC;
+ if (drm_mode->flags & DRM_MODE_FLAG_PHSYNC)
+ regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_PHSYNC;
+
+ regs[CH7006_DETECT] = 0;
+ regs[CH7006_BCLKOUT] = 0;
+
+ regs[CH7006_SUBC_INC3] = 0;
+ if (params->pout_level)
+ regs[CH7006_SUBC_INC3] |= CH7006_SUBC_INC3_POUT_3_3V;
+
+ regs[CH7006_SUBC_INC4] = 0;
+ if (params->active_detect)
+ regs[CH7006_SUBC_INC4] |= CH7006_SUBC_INC4_DS_INPUT;
+
+ regs[CH7006_PLL_CONTROL] = priv->saved_state.regs[CH7006_PLL_CONTROL];
+
+ ch7006_setup_levels(encoder);
+ ch7006_setup_subcarrier(encoder);
+ ch7006_setup_pll(encoder);
+ ch7006_setup_power_state(encoder);
+ ch7006_setup_properties(encoder);
+
+ ch7006_state_load(client, state);
+}
+
+static enum drm_connector_status ch7006_encoder_detect(struct drm_encoder *encoder,
+ struct drm_connector *connector)
+{
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+ struct ch7006_state *state = &priv->state;
+ int det;
+
+ ch7006_dbg(client, "\n");
+
+ ch7006_save_reg(client, state, CH7006_DETECT);
+ ch7006_save_reg(client, state, CH7006_POWER);
+ ch7006_save_reg(client, state, CH7006_CLKMODE);
+
+ ch7006_write(client, CH7006_POWER, CH7006_POWER_RESET |
+ bitfs(CH7006_POWER_LEVEL, NORMAL));
+ ch7006_write(client, CH7006_CLKMODE, CH7006_CLKMODE_MASTER);
+
+ ch7006_write(client, CH7006_DETECT, CH7006_DETECT_SENSE);
+
+ ch7006_write(client, CH7006_DETECT, 0);
+
+ det = ch7006_read(client, CH7006_DETECT);
+
+ ch7006_load_reg(client, state, CH7006_CLKMODE);
+ ch7006_load_reg(client, state, CH7006_POWER);
+ ch7006_load_reg(client, state, CH7006_DETECT);
+
+ if ((det & (CH7006_DETECT_SVIDEO_Y_TEST|
+ CH7006_DETECT_SVIDEO_C_TEST|
+ CH7006_DETECT_CVBS_TEST)) == 0)
+ priv->subconnector = DRM_MODE_SUBCONNECTOR_SCART;
+ else if ((det & (CH7006_DETECT_SVIDEO_Y_TEST|
+ CH7006_DETECT_SVIDEO_C_TEST)) == 0)
+ priv->subconnector = DRM_MODE_SUBCONNECTOR_SVIDEO;
+ else if ((det & CH7006_DETECT_CVBS_TEST) == 0)
+ priv->subconnector = DRM_MODE_SUBCONNECTOR_Composite;
+ else
+ priv->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
+
+ drm_connector_property_set_value(connector,
+ encoder->dev->mode_config.tv_subconnector_property,
+ priv->subconnector);
+
+ return priv->subconnector ? connector_status_connected :
+ connector_status_disconnected;
+}
+
+static int ch7006_encoder_get_modes(struct drm_encoder *encoder,
+ struct drm_connector *connector)
+{
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+ struct ch7006_mode *mode;
+ int n = 0;
+
+ for (mode = ch7006_modes; mode->mode.clock; mode++) {
+ if (~mode->valid_scales & 1<<priv->scale ||
+ ~mode->valid_norms & 1<<priv->norm)
+ continue;
+
+ drm_mode_probed_add(connector,
+ drm_mode_duplicate(encoder->dev, &mode->mode));
+
+ n++;
+ }
+
+ return n;
+}
+
+static int ch7006_encoder_create_resources(struct drm_encoder *encoder,
+ struct drm_connector *connector)
+{
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+ struct drm_device *dev = encoder->dev;
+ struct drm_mode_config *conf = &dev->mode_config;
+
+ drm_mode_create_tv_properties(dev, NUM_TV_NORMS, ch7006_tv_norm_names);
+
+ priv->scale_property = drm_property_create(dev, DRM_MODE_PROP_RANGE,
+ "scale", 2);
+ priv->scale_property->values[0] = 0;
+ priv->scale_property->values[1] = 2;
+
+ drm_connector_attach_property(connector, conf->tv_select_subconnector_property,
+ priv->select_subconnector);
+ drm_connector_attach_property(connector, conf->tv_subconnector_property,
+ priv->subconnector);
+ drm_connector_attach_property(connector, conf->tv_left_margin_property,
+ priv->hmargin);
+ drm_connector_attach_property(connector, conf->tv_bottom_margin_property,
+ priv->vmargin);
+ drm_connector_attach_property(connector, conf->tv_mode_property,
+ priv->norm);
+ drm_connector_attach_property(connector, conf->tv_brightness_property,
+ priv->brightness);
+ drm_connector_attach_property(connector, conf->tv_contrast_property,
+ priv->contrast);
+ drm_connector_attach_property(connector, conf->tv_flicker_reduction_property,
+ priv->flicker);
+ drm_connector_attach_property(connector, priv->scale_property,
+ priv->scale);
+
+ return 0;
+}
+
+static int ch7006_encoder_set_property(struct drm_encoder *encoder,
+ struct drm_connector *connector,
+ struct drm_property *property,
+ uint64_t val)
+{
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+ struct ch7006_state *state = &priv->state;
+ struct drm_mode_config *conf = &encoder->dev->mode_config;
+ struct drm_crtc *crtc = encoder->crtc;
+ bool modes_changed = false;
+
+ ch7006_dbg(client, "\n");
+
+ if (property == conf->tv_select_subconnector_property) {
+ priv->select_subconnector = val;
+
+ ch7006_setup_power_state(encoder);
+
+ ch7006_load_reg(client, state, CH7006_POWER);
+
+ } else if (property == conf->tv_left_margin_property) {
+ priv->hmargin = val;
+
+ ch7006_setup_properties(encoder);
+
+ ch7006_load_reg(client, state, CH7006_POV);
+ ch7006_load_reg(client, state, CH7006_HPOS);
+
+ } else if (property == conf->tv_bottom_margin_property) {
+ priv->vmargin = val;
+
+ ch7006_setup_properties(encoder);
+
+ ch7006_load_reg(client, state, CH7006_POV);
+ ch7006_load_reg(client, state, CH7006_VPOS);
+
+ } else if (property == conf->tv_mode_property) {
+ if (connector->dpms != DRM_MODE_DPMS_OFF)
+ return -EINVAL;
+
+ priv->norm = val;
+
+ modes_changed = true;
+
+ } else if (property == conf->tv_brightness_property) {
+ priv->brightness = val;
+
+ ch7006_setup_levels(encoder);
+
+ ch7006_load_reg(client, state, CH7006_BLACK_LEVEL);
+
+ } else if (property == conf->tv_contrast_property) {
+ priv->contrast = val;
+
+ ch7006_setup_properties(encoder);
+
+ ch7006_load_reg(client, state, CH7006_CONTRAST);
+
+ } else if (property == conf->tv_flicker_reduction_property) {
+ priv->flicker = val;
+
+ ch7006_setup_properties(encoder);
+
+ ch7006_load_reg(client, state, CH7006_FFILTER);
+
+ } else if (property == priv->scale_property) {
+ if (connector->dpms != DRM_MODE_DPMS_OFF)
+ return -EINVAL;
+
+ priv->scale = val;
+
+ modes_changed = true;
+
+ } else {
+ return -EINVAL;
+ }
+
+ if (modes_changed) {
+ drm_helper_probe_single_connector_modes(connector, 0, 0);
+
+ /* Disable the crtc to ensure a full modeset is
+ * performed whenever it's turned on again. */
+ if (crtc) {
+ struct drm_mode_set modeset = {
+ .crtc = crtc,
+ };
+
+ crtc->funcs->set_config(&modeset);
+ }
+ }
+
+ return 0;
+}
+
+static struct drm_encoder_slave_funcs ch7006_encoder_funcs = {
+ .set_config = ch7006_encoder_set_config,
+ .destroy = ch7006_encoder_destroy,
+ .dpms = ch7006_encoder_dpms,
+ .save = ch7006_encoder_save,
+ .restore = ch7006_encoder_restore,
+ .mode_fixup = ch7006_encoder_mode_fixup,
+ .mode_valid = ch7006_encoder_mode_valid,
+ .mode_set = ch7006_encoder_mode_set,
+ .detect = ch7006_encoder_detect,
+ .get_modes = ch7006_encoder_get_modes,
+ .create_resources = ch7006_encoder_create_resources,
+ .set_property = ch7006_encoder_set_property,
+};
+
+
+/* I2C driver functions */
+
+static int ch7006_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ uint8_t addr = CH7006_VERSION_ID;
+ uint8_t val;
+ int ret;
+
+ ch7006_dbg(client, "\n");
+
+ ret = i2c_master_send(client, &addr, sizeof(addr));
+ if (ret < 0)
+ goto fail;
+
+ ret = i2c_master_recv(client, &val, sizeof(val));
+ if (ret < 0)
+ goto fail;
+
+ ch7006_info(client, "Detected version ID: %x\n", val);
+
+ return 0;
+
+fail:
+ ch7006_err(client, "Error %d reading version ID\n", ret);
+
+ return -ENODEV;
+}
+
+static int ch7006_remove(struct i2c_client *client)
+{
+ ch7006_dbg(client, "\n");
+
+ return 0;
+}
+
+static int ch7006_encoder_init(struct i2c_client *client,
+ struct drm_device *dev,
+ struct drm_encoder_slave *encoder)
+{
+ struct ch7006_priv *priv;
+ int i;
+
+ ch7006_dbg(client, "\n");
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ encoder->slave_priv = priv;
+ encoder->slave_funcs = &ch7006_encoder_funcs;
+
+ priv->norm = TV_NORM_PAL;
+ priv->select_subconnector = DRM_MODE_SUBCONNECTOR_Automatic;
+ priv->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
+ priv->scale = 1;
+ priv->contrast = 50;
+ priv->brightness = 50;
+ priv->flicker = 50;
+ priv->hmargin = 50;
+ priv->vmargin = 50;
+ priv->last_dpms = -1;
+
+ if (ch7006_tv_norm) {
+ for (i = 0; i < NUM_TV_NORMS; i++) {
+ if (!strcmp(ch7006_tv_norm_names[i], ch7006_tv_norm)) {
+ priv->norm = i;
+ break;
+ }
+ }
+
+ if (i == NUM_TV_NORMS)
+ ch7006_err(client, "Invalid TV norm setting \"%s\".\n",
+ ch7006_tv_norm);
+ }
+
+ if (ch7006_scale >= 0 && ch7006_scale <= 2)
+ priv->scale = ch7006_scale;
+ else
+ ch7006_err(client, "Invalid scale setting \"%d\".\n",
+ ch7006_scale);
+
+ return 0;
+}
+
+static struct i2c_device_id ch7006_ids[] = {
+ { "ch7006", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ch7006_ids);
+
+static struct drm_i2c_encoder_driver ch7006_driver = {
+ .i2c_driver = {
+ .probe = ch7006_probe,
+ .remove = ch7006_remove,
+
+ .driver = {
+ .name = "ch7006",
+ },
+
+ .id_table = ch7006_ids,
+ },
+
+ .encoder_init = ch7006_encoder_init,
+};
+
+
+/* Module initialization */
+
+static int __init ch7006_init(void)
+{
+ return drm_i2c_encoder_register(THIS_MODULE, &ch7006_driver);
+}
+
+static void __exit ch7006_exit(void)
+{
+ drm_i2c_encoder_unregister(&ch7006_driver);
+}
+
+int ch7006_debug;
+module_param_named(debug, ch7006_debug, int, 0600);
+MODULE_PARM_DESC(debug, "Enable debug output.");
+
+char *ch7006_tv_norm;
+module_param_named(tv_norm, ch7006_tv_norm, charp, 0600);
+MODULE_PARM_DESC(tv_norm, "Default TV norm.\n"
+ "\t\tSupported: PAL, PAL-M, PAL-N, PAL-Nc, PAL-60, NTSC-M, NTSC-J.\n"
+ "\t\tDefault: PAL");
+
+int ch7006_scale = 1;
+module_param_named(scale, ch7006_scale, int, 0600);
+MODULE_PARM_DESC(scale, "Default scale.\n"
+ "\t\tSupported: 0 -> Select video modes with a higher blanking ratio.\n"
+ "\t\t\t1 -> Select default video modes.\n"
+ "\t\t\t2 -> Select video modes with a lower blanking ratio.");
+
+MODULE_AUTHOR("Francisco Jerez <currojerez@riseup.net>");
+MODULE_DESCRIPTION("Chrontel ch7006 TV encoder driver");
+MODULE_LICENSE("GPL and additional rights");
+
+module_init(ch7006_init);
+module_exit(ch7006_exit);
--- /dev/null
+/*
+ * Copyright (C) 2009 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (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 (including the
+ * next paragraph) 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 COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "ch7006_priv.h"
+
+char *ch7006_tv_norm_names[] = {
+ [TV_NORM_PAL] = "PAL",
+ [TV_NORM_PAL_M] = "PAL-M",
+ [TV_NORM_PAL_N] = "PAL-N",
+ [TV_NORM_PAL_NC] = "PAL-Nc",
+ [TV_NORM_PAL_60] = "PAL-60",
+ [TV_NORM_NTSC_M] = "NTSC-M",
+ [TV_NORM_NTSC_J] = "NTSC-J",
+};
+
+#define NTSC_LIKE_TIMINGS .vrefresh = 60 * fixed1/1.001, \
+ .vdisplay = 480, \
+ .vtotal = 525, \
+ .hvirtual = 660
+
+#define PAL_LIKE_TIMINGS .vrefresh = 50 * fixed1, \
+ .vdisplay = 576, \
+ .vtotal = 625, \
+ .hvirtual = 810
+
+struct ch7006_tv_norm_info ch7006_tv_norms[] = {
+ [TV_NORM_NTSC_M] = {
+ NTSC_LIKE_TIMINGS,
+ .black_level = 0.339 * fixed1,
+ .subc_freq = 3579545 * fixed1,
+ .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, NTSC),
+ .voffset = 0,
+ },
+ [TV_NORM_NTSC_J] = {
+ NTSC_LIKE_TIMINGS,
+ .black_level = 0.286 * fixed1,
+ .subc_freq = 3579545 * fixed1,
+ .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, NTSC_J),
+ .voffset = 0,
+ },
+ [TV_NORM_PAL] = {
+ PAL_LIKE_TIMINGS,
+ .black_level = 0.3 * fixed1,
+ .subc_freq = 4433618.75 * fixed1,
+ .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL),
+ .voffset = 0,
+ },
+ [TV_NORM_PAL_M] = {
+ NTSC_LIKE_TIMINGS,
+ .black_level = 0.339 * fixed1,
+ .subc_freq = 3575611.433 * fixed1,
+ .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL_M),
+ .voffset = 16,
+ },
+
+ /* The following modes seem to work right but they're
+ * undocumented */
+
+ [TV_NORM_PAL_N] = {
+ PAL_LIKE_TIMINGS,
+ .black_level = 0.339 * fixed1,
+ .subc_freq = 4433618.75 * fixed1,
+ .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL),
+ .voffset = 0,
+ },
+ [TV_NORM_PAL_NC] = {
+ PAL_LIKE_TIMINGS,
+ .black_level = 0.3 * fixed1,
+ .subc_freq = 3582056.25 * fixed1,
+ .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL),
+ .voffset = 0,
+ },
+ [TV_NORM_PAL_60] = {
+ NTSC_LIKE_TIMINGS,
+ .black_level = 0.3 * fixed1,
+ .subc_freq = 4433618.75 * fixed1,
+ .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL_M),
+ .voffset = 16,
+ },
+};
+
+#define __MODE(f, hd, vd, ht, vt, hsynp, vsynp, \
+ subc, scale, scale_mask, norm_mask, e_hd, e_vd) { \
+ .mode = { \
+ .name = #hd "x" #vd, \
+ .status = 0, \
+ .type = DRM_MODE_TYPE_DRIVER, \
+ .clock = f, \
+ .hdisplay = hd, \
+ .hsync_start = e_hd + 16, \
+ .hsync_end = e_hd + 80, \
+ .htotal = ht, \
+ .hskew = 0, \
+ .vdisplay = vd, \
+ .vsync_start = vd + 10, \
+ .vsync_end = vd + 26, \
+ .vtotal = vt, \
+ .vscan = 0, \
+ .flags = DRM_MODE_FLAG_##hsynp##HSYNC | \
+ DRM_MODE_FLAG_##vsynp##VSYNC, \
+ .vrefresh = 0, \
+ }, \
+ .enc_hdisp = e_hd, \
+ .enc_vdisp = e_vd, \
+ .subc_coeff = subc * fixed1, \
+ .dispmode = bitfs(CH7006_DISPMODE_SCALING_RATIO, scale) | \
+ bitfs(CH7006_DISPMODE_INPUT_RES, e_hd##x##e_vd), \
+ .valid_scales = scale_mask, \
+ .valid_norms = norm_mask \
+ }
+
+#define MODE(f, hd, vd, ht, vt, hsynp, vsynp, \
+ subc, scale, scale_mask, norm_mask) \
+ __MODE(f, hd, vd, ht, vt, hsynp, vsynp, subc, scale, \
+ scale_mask, norm_mask, hd, vd)
+
+#define NTSC_LIKE (1 << TV_NORM_NTSC_M | 1 << TV_NORM_NTSC_J | \
+ 1 << TV_NORM_PAL_M | 1 << TV_NORM_PAL_60)
+
+#define PAL_LIKE (1 << TV_NORM_PAL | 1 << TV_NORM_PAL_N | 1 << TV_NORM_PAL_NC)
+
+struct ch7006_mode ch7006_modes[] = {
+ MODE(21000, 512, 384, 840, 500, N, N, 181.797557582, 5_4, 0x6, PAL_LIKE),
+ MODE(26250, 512, 384, 840, 625, N, N, 145.438046066, 1_1, 0x1, PAL_LIKE),
+ MODE(20140, 512, 384, 800, 420, N, N, 213.257083791, 5_4, 0x4, NTSC_LIKE),
+ MODE(24671, 512, 384, 784, 525, N, N, 174.0874153, 1_1, 0x3, NTSC_LIKE),
+ MODE(28125, 720, 400, 1125, 500, N, N, 135.742176298, 5_4, 0x6, PAL_LIKE),
+ MODE(34875, 720, 400, 1116, 625, N, N, 109.469496898, 1_1, 0x1, PAL_LIKE),
+ MODE(23790, 720, 400, 945, 420, N, N, 160.475642016, 5_4, 0x4, NTSC_LIKE),
+ MODE(29455, 720, 400, 936, 525, N, N, 129.614941843, 1_1, 0x3, NTSC_LIKE),
+ MODE(25000, 640, 400, 1000, 500, N, N, 152.709948279, 5_4, 0x6, PAL_LIKE),
+ MODE(31500, 640, 400, 1008, 625, N, N, 121.198371646, 1_1, 0x1, PAL_LIKE),
+ MODE(21147, 640, 400, 840, 420, N, N, 180.535097338, 5_4, 0x4, NTSC_LIKE),
+ MODE(26434, 640, 400, 840, 525, N, N, 144.42807787, 1_1, 0x2, NTSC_LIKE),
+ MODE(30210, 640, 400, 840, 600, N, N, 126.374568276, 7_8, 0x1, NTSC_LIKE),
+ MODE(21000, 640, 480, 840, 500, N, N, 181.797557582, 5_4, 0x4, PAL_LIKE),
+ MODE(26250, 640, 480, 840, 625, N, N, 145.438046066, 1_1, 0x2, PAL_LIKE),
+ MODE(31500, 640, 480, 840, 750, N, N, 121.198371646, 5_6, 0x1, PAL_LIKE),
+ MODE(24671, 640, 480, 784, 525, N, N, 174.0874153, 1_1, 0x4, NTSC_LIKE),
+ MODE(28196, 640, 480, 784, 600, N, N, 152.326488422, 7_8, 0x2, NTSC_LIKE),
+ MODE(30210, 640, 480, 800, 630, N, N, 142.171389101, 5_6, 0x1, NTSC_LIKE),
+ __MODE(29500, 720, 576, 944, 625, P, P, 145.592111636, 1_1, 0x7, PAL_LIKE, 800, 600),
+ MODE(36000, 800, 600, 960, 750, P, P, 119.304647022, 5_6, 0x6, PAL_LIKE),
+ MODE(39000, 800, 600, 936, 836, P, P, 110.127366499, 3_4, 0x1, PAL_LIKE),
+ MODE(39273, 800, 600, 1040, 630, P, P, 145.816809399, 5_6, 0x4, NTSC_LIKE),
+ MODE(43636, 800, 600, 1040, 700, P, P, 131.235128487, 3_4, 0x2, NTSC_LIKE),
+ MODE(47832, 800, 600, 1064, 750, P, P, 119.723275165, 7_10, 0x1, NTSC_LIKE),
+ {}
+};
+
+struct ch7006_mode *ch7006_lookup_mode(struct drm_encoder *encoder,
+ struct drm_display_mode *drm_mode)
+{
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+ struct ch7006_mode *mode;
+
+ for (mode = ch7006_modes; mode->mode.clock; mode++) {
+
+ if (~mode->valid_norms & 1<<priv->norm)
+ continue;
+
+ if (mode->mode.hdisplay != drm_mode->hdisplay ||
+ mode->mode.vdisplay != drm_mode->vdisplay ||
+ mode->mode.vtotal != drm_mode->vtotal ||
+ mode->mode.htotal != drm_mode->htotal ||
+ mode->mode.clock != drm_mode->clock)
+ continue;
+
+ return mode;
+ }
+
+ return NULL;
+}
+
+/* Some common HW state calculation code */
+
+void ch7006_setup_levels(struct drm_encoder *encoder)
+{
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+ uint8_t *regs = priv->state.regs;
+ struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
+ int gain;
+ int black_level;
+
+ /* Set DAC_GAIN if the voltage drop between white and black is
+ * high enough. */
+ if (norm->black_level < 339*fixed1/1000) {
+ gain = 76;
+
+ regs[CH7006_INPUT_FORMAT] |= CH7006_INPUT_FORMAT_DAC_GAIN;
+ } else {
+ gain = 71;
+
+ regs[CH7006_INPUT_FORMAT] &= ~CH7006_INPUT_FORMAT_DAC_GAIN;
+ }
+
+ black_level = round_fixed(norm->black_level*26625)/gain;
+
+ /* Correct it with the specified brightness. */
+ black_level = interpolate(90, black_level, 208, priv->brightness);
+
+ regs[CH7006_BLACK_LEVEL] = bitf(CH7006_BLACK_LEVEL_0, black_level);
+
+ ch7006_dbg(client, "black level: %d\n", black_level);
+}
+
+void ch7006_setup_subcarrier(struct drm_encoder *encoder)
+{
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+ struct ch7006_state *state = &priv->state;
+ struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
+ struct ch7006_mode *mode = priv->mode;
+ uint32_t subc_inc;
+
+ subc_inc = round_fixed((mode->subc_coeff >> 8)
+ * (norm->subc_freq >> 24));
+
+ setbitf(state, CH7006_SUBC_INC0, 28, subc_inc);
+ setbitf(state, CH7006_SUBC_INC1, 24, subc_inc);
+ setbitf(state, CH7006_SUBC_INC2, 20, subc_inc);
+ setbitf(state, CH7006_SUBC_INC3, 16, subc_inc);
+ setbitf(state, CH7006_SUBC_INC4, 12, subc_inc);
+ setbitf(state, CH7006_SUBC_INC5, 8, subc_inc);
+ setbitf(state, CH7006_SUBC_INC6, 4, subc_inc);
+ setbitf(state, CH7006_SUBC_INC7, 0, subc_inc);
+
+ ch7006_dbg(client, "subcarrier inc: %u\n", subc_inc);
+}
+
+void ch7006_setup_pll(struct drm_encoder *encoder)
+{
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+ uint8_t *regs = priv->state.regs;
+ struct ch7006_mode *mode = priv->mode;
+ int n, best_n = 0;
+ int m, best_m = 0;
+ int freq, best_freq = 0;
+
+ for (n = 0; n < CH7006_MAXN; n++) {
+ for (m = 0; m < CH7006_MAXM; m++) {
+ freq = CH7006_FREQ0*(n+2)/(m+2);
+
+ if (abs(freq - mode->mode.clock) <
+ abs(best_freq - mode->mode.clock)) {
+ best_freq = freq;
+ best_n = n;
+ best_m = m;
+ }
+ }
+ }
+
+ regs[CH7006_PLLOV] = bitf(CH7006_PLLOV_N_8, best_n) |
+ bitf(CH7006_PLLOV_M_8, best_m);
+
+ regs[CH7006_PLLM] = bitf(CH7006_PLLM_0, best_m);
+ regs[CH7006_PLLN] = bitf(CH7006_PLLN_0, best_n);
+
+ if (best_n < 108)
+ regs[CH7006_PLL_CONTROL] |= CH7006_PLL_CONTROL_CAPACITOR;
+ else
+ regs[CH7006_PLL_CONTROL] &= ~CH7006_PLL_CONTROL_CAPACITOR;
+
+ ch7006_dbg(client, "n=%d m=%d f=%d c=%d\n",
+ best_n, best_m, best_freq, best_n < 108);
+}
+
+void ch7006_setup_power_state(struct drm_encoder *encoder)
+{
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+ uint8_t *power = &priv->state.regs[CH7006_POWER];
+ int subconnector;
+
+ subconnector = priv->select_subconnector ? priv->select_subconnector :
+ priv->subconnector;
+
+ *power = CH7006_POWER_RESET;
+
+ if (priv->last_dpms == DRM_MODE_DPMS_ON) {
+ switch (subconnector) {
+ case DRM_MODE_SUBCONNECTOR_SVIDEO:
+ *power |= bitfs(CH7006_POWER_LEVEL, CVBS_OFF);
+ break;
+ case DRM_MODE_SUBCONNECTOR_Composite:
+ *power |= bitfs(CH7006_POWER_LEVEL, SVIDEO_OFF);
+ break;
+ case DRM_MODE_SUBCONNECTOR_SCART:
+ *power |= bitfs(CH7006_POWER_LEVEL, NORMAL) |
+ CH7006_POWER_SCART;
+ break;
+ }
+
+ } else {
+ *power |= bitfs(CH7006_POWER_LEVEL, FULL_POWER_OFF);
+ }
+}
+
+void ch7006_setup_properties(struct drm_encoder *encoder)
+{
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+ struct ch7006_state *state = &priv->state;
+ struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
+ struct ch7006_mode *ch_mode = priv->mode;
+ struct drm_display_mode *mode = &ch_mode->mode;
+ uint8_t *regs = state->regs;
+ int flicker, contrast, hpos, vpos;
+ uint64_t scale, aspect;
+
+ flicker = interpolate(0, 2, 3, priv->flicker);
+ regs[CH7006_FFILTER] = bitf(CH7006_FFILTER_TEXT, flicker) |
+ bitf(CH7006_FFILTER_LUMA, flicker) |
+ bitf(CH7006_FFILTER_CHROMA, 1);
+
+ contrast = interpolate(0, 5, 7, priv->contrast);
+ regs[CH7006_CONTRAST] = bitf(CH7006_CONTRAST_0, contrast);
+
+ scale = norm->vtotal*fixed1;
+ do_div(scale, mode->vtotal);
+
+ aspect = ch_mode->enc_hdisp*fixed1;
+ do_div(aspect, ch_mode->enc_vdisp);
+
+ hpos = round_fixed((norm->hvirtual * aspect - mode->hdisplay * scale)
+ * priv->hmargin * mode->vtotal) / norm->vtotal / 100 / 4;
+
+ setbitf(state, CH7006_POV, HPOS_8, hpos);
+ setbitf(state, CH7006_HPOS, 0, hpos);
+
+ vpos = max(0, norm->vdisplay - round_fixed(mode->vdisplay*scale)
+ + norm->voffset) * priv->vmargin / 100 / 2;
+
+ setbitf(state, CH7006_POV, VPOS_8, vpos);
+ setbitf(state, CH7006_VPOS, 0, vpos);
+
+ ch7006_dbg(client, "hpos: %d, vpos: %d\n", hpos, vpos);
+}
+
+/* HW access functions */
+
+void ch7006_write(struct i2c_client *client, uint8_t addr, uint8_t val)
+{
+ uint8_t buf[] = {addr, val};
+ int ret;
+
+ ret = i2c_master_send(client, buf, ARRAY_SIZE(buf));
+ if (ret < 0)
+ ch7006_err(client, "Error %d writing to subaddress 0x%x\n",
+ ret, addr);
+}
+
+uint8_t ch7006_read(struct i2c_client *client, uint8_t addr)
+{
+ uint8_t val;
+ int ret;
+
+ ret = i2c_master_send(client, &addr, sizeof(addr));
+ if (ret < 0)
+ goto fail;
+
+ ret = i2c_master_recv(client, &val, sizeof(val));
+ if (ret < 0)
+ goto fail;
+
+ return val;
+
+fail:
+ ch7006_err(client, "Error %d reading from subaddress 0x%x\n",
+ ret, addr);
+ return 0;
+}
+
+void ch7006_state_load(struct i2c_client *client,
+ struct ch7006_state *state)
+{
+ ch7006_load_reg(client, state, CH7006_POWER);
+
+ ch7006_load_reg(client, state, CH7006_DISPMODE);
+ ch7006_load_reg(client, state, CH7006_FFILTER);
+ ch7006_load_reg(client, state, CH7006_BWIDTH);
+ ch7006_load_reg(client, state, CH7006_INPUT_FORMAT);
+ ch7006_load_reg(client, state, CH7006_CLKMODE);
+ ch7006_load_reg(client, state, CH7006_START_ACTIVE);
+ ch7006_load_reg(client, state, CH7006_POV);
+ ch7006_load_reg(client, state, CH7006_BLACK_LEVEL);
+ ch7006_load_reg(client, state, CH7006_HPOS);
+ ch7006_load_reg(client, state, CH7006_VPOS);
+ ch7006_load_reg(client, state, CH7006_INPUT_SYNC);
+ ch7006_load_reg(client, state, CH7006_DETECT);
+ ch7006_load_reg(client, state, CH7006_CONTRAST);
+ ch7006_load_reg(client, state, CH7006_PLLOV);
+ ch7006_load_reg(client, state, CH7006_PLLM);
+ ch7006_load_reg(client, state, CH7006_PLLN);
+ ch7006_load_reg(client, state, CH7006_BCLKOUT);
+ ch7006_load_reg(client, state, CH7006_SUBC_INC0);
+ ch7006_load_reg(client, state, CH7006_SUBC_INC1);
+ ch7006_load_reg(client, state, CH7006_SUBC_INC2);
+ ch7006_load_reg(client, state, CH7006_SUBC_INC3);
+ ch7006_load_reg(client, state, CH7006_SUBC_INC4);
+ ch7006_load_reg(client, state, CH7006_SUBC_INC5);
+ ch7006_load_reg(client, state, CH7006_SUBC_INC6);
+ ch7006_load_reg(client, state, CH7006_SUBC_INC7);
+ ch7006_load_reg(client, state, CH7006_PLL_CONTROL);
+ ch7006_load_reg(client, state, CH7006_CALC_SUBC_INC0);
+
+ /* I don't know what this is for, but otherwise I get no
+ * signal.
+ */
+ ch7006_write(client, 0x3d, 0x0);
+}
+
+void ch7006_state_save(struct i2c_client *client,
+ struct ch7006_state *state)
+{
+ ch7006_save_reg(client, state, CH7006_POWER);
+
+ ch7006_save_reg(client, state, CH7006_DISPMODE);
+ ch7006_save_reg(client, state, CH7006_FFILTER);
+ ch7006_save_reg(client, state, CH7006_BWIDTH);
+ ch7006_save_reg(client, state, CH7006_INPUT_FORMAT);
+ ch7006_save_reg(client, state, CH7006_CLKMODE);
+ ch7006_save_reg(client, state, CH7006_START_ACTIVE);
+ ch7006_save_reg(client, state, CH7006_POV);
+ ch7006_save_reg(client, state, CH7006_BLACK_LEVEL);
+ ch7006_save_reg(client, state, CH7006_HPOS);
+ ch7006_save_reg(client, state, CH7006_VPOS);
+ ch7006_save_reg(client, state, CH7006_INPUT_SYNC);
+ ch7006_save_reg(client, state, CH7006_DETECT);
+ ch7006_save_reg(client, state, CH7006_CONTRAST);
+ ch7006_save_reg(client, state, CH7006_PLLOV);
+ ch7006_save_reg(client, state, CH7006_PLLM);
+ ch7006_save_reg(client, state, CH7006_PLLN);
+ ch7006_save_reg(client, state, CH7006_BCLKOUT);
+ ch7006_save_reg(client, state, CH7006_SUBC_INC0);
+ ch7006_save_reg(client, state, CH7006_SUBC_INC1);
+ ch7006_save_reg(client, state, CH7006_SUBC_INC2);
+ ch7006_save_reg(client, state, CH7006_SUBC_INC3);
+ ch7006_save_reg(client, state, CH7006_SUBC_INC4);
+ ch7006_save_reg(client, state, CH7006_SUBC_INC5);
+ ch7006_save_reg(client, state, CH7006_SUBC_INC6);
+ ch7006_save_reg(client, state, CH7006_SUBC_INC7);
+ ch7006_save_reg(client, state, CH7006_PLL_CONTROL);
+ ch7006_save_reg(client, state, CH7006_CALC_SUBC_INC0);
+
+ state->regs[CH7006_FFILTER] = (state->regs[CH7006_FFILTER] & 0xf0) |
+ (state->regs[CH7006_FFILTER] & 0x0c) >> 2 |
+ (state->regs[CH7006_FFILTER] & 0x03) << 2;
+}
--- /dev/null
+/*
+ * Copyright (C) 2009 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (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 (including the
+ * next paragraph) 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 COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS 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 __DRM_I2C_CH7006_PRIV_H__
+#define __DRM_I2C_CH7006_PRIV_H__
+
+#include "drmP.h"
+#include "drm_crtc_helper.h"
+#include "drm_encoder_slave.h"
+#include "i2c/ch7006.h"
+
+typedef int64_t fixed;
+#define fixed1 (1LL << 32)
+
+enum ch7006_tv_norm {
+ TV_NORM_PAL,
+ TV_NORM_PAL_M,
+ TV_NORM_PAL_N,
+ TV_NORM_PAL_NC,
+ TV_NORM_PAL_60,
+ TV_NORM_NTSC_M,
+ TV_NORM_NTSC_J,
+ NUM_TV_NORMS
+};
+
+struct ch7006_tv_norm_info {
+ fixed vrefresh;
+ int vdisplay;
+ int vtotal;
+ int hvirtual;
+
+ fixed subc_freq;
+ fixed black_level;
+
+ uint32_t dispmode;
+ int voffset;
+};
+
+struct ch7006_mode {
+ struct drm_display_mode mode;
+
+ int enc_hdisp;
+ int enc_vdisp;
+
+ fixed subc_coeff;
+ uint32_t dispmode;
+
+ uint32_t valid_scales;
+ uint32_t valid_norms;
+};
+
+struct ch7006_state {
+ uint8_t regs[0x26];
+};
+
+struct ch7006_priv {
+ struct ch7006_encoder_params *params;
+ struct ch7006_mode *mode;
+
+ struct ch7006_state state;
+ struct ch7006_state saved_state;
+
+ struct drm_property *scale_property;
+
+ int select_subconnector;
+ int subconnector;
+ int hmargin;
+ int vmargin;
+ enum ch7006_tv_norm norm;
+ int brightness;
+ int contrast;
+ int flicker;
+ int scale;
+
+ int last_dpms;
+};
+
+#define to_ch7006_priv(x) \
+ ((struct ch7006_priv *)to_encoder_slave(x)->slave_priv)
+
+extern int ch7006_debug;
+extern char *ch7006_tv_norm;
+extern int ch7006_scale;
+
+extern char *ch7006_tv_norm_names[];
+extern struct ch7006_tv_norm_info ch7006_tv_norms[];
+extern struct ch7006_mode ch7006_modes[];
+
+struct ch7006_mode *ch7006_lookup_mode(struct drm_encoder *encoder,
+ struct drm_display_mode *drm_mode);
+
+void ch7006_setup_levels(struct drm_encoder *encoder);
+void ch7006_setup_subcarrier(struct drm_encoder *encoder);
+void ch7006_setup_pll(struct drm_encoder *encoder);
+void ch7006_setup_power_state(struct drm_encoder *encoder);
+void ch7006_setup_properties(struct drm_encoder *encoder);
+
+void ch7006_write(struct i2c_client *client, uint8_t addr, uint8_t val);
+uint8_t ch7006_read(struct i2c_client *client, uint8_t addr);
+
+void ch7006_state_load(struct i2c_client *client,
+ struct ch7006_state *state);
+void ch7006_state_save(struct i2c_client *client,
+ struct ch7006_state *state);
+
+/* Some helper macros */
+
+#define ch7006_dbg(client, format, ...) do { \
+ if (ch7006_debug) \
+ dev_printk(KERN_DEBUG, &client->dev, \
+ "%s: " format, __func__, ## __VA_ARGS__); \
+ } while (0)
+#define ch7006_info(client, format, ...) \
+ dev_info(&client->dev, format, __VA_ARGS__)
+#define ch7006_err(client, format, ...) \
+ dev_err(&client->dev, format, __VA_ARGS__)
+
+#define __mask(src, bitfield) \
+ (((2 << (1 ? bitfield)) - 1) & ~((1 << (0 ? bitfield)) - 1))
+#define mask(bitfield) __mask(bitfield)
+
+#define __bitf(src, bitfield, x) \
+ (((x) >> (src) << (0 ? bitfield)) & __mask(src, bitfield))
+#define bitf(bitfield, x) __bitf(bitfield, x)
+#define bitfs(bitfield, s) __bitf(bitfield, bitfield##_##s)
+#define setbitf(state, reg, bitfield, x) \
+ state->regs[reg] = (state->regs[reg] & ~mask(reg##_##bitfield)) \
+ | bitf(reg##_##bitfield, x)
+
+#define __unbitf(src, bitfield, x) \
+ ((x & __mask(src, bitfield)) >> (0 ? bitfield) << (src))
+#define unbitf(bitfield, x) __unbitf(bitfield, x)
+
+static inline int interpolate(int y0, int y1, int y2, int x)
+{
+ return y1 + (x < 50 ? y1 - y0 : y2 - y1) * (x - 50) / 50;
+}
+
+static inline int32_t round_fixed(fixed x)
+{
+ return (x + fixed1/2) >> 32;
+}
+
+#define ch7006_load_reg(client, state, reg) ch7006_write(client, reg, state->regs[reg])
+#define ch7006_save_reg(client, state, reg) state->regs[reg] = ch7006_read(client, reg)
+
+/* Fixed hardware specs */
+
+#define CH7006_FREQ0 14318
+#define CH7006_MAXN 650
+#define CH7006_MAXM 315
+
+/* Register definitions */
+
+#define CH7006_DISPMODE 0x00
+#define CH7006_DISPMODE_INPUT_RES 0, 7:5
+#define CH7006_DISPMODE_INPUT_RES_512x384 0x0
+#define CH7006_DISPMODE_INPUT_RES_720x400 0x1
+#define CH7006_DISPMODE_INPUT_RES_640x400 0x2
+#define CH7006_DISPMODE_INPUT_RES_640x480 0x3
+#define CH7006_DISPMODE_INPUT_RES_800x600 0x4
+#define CH7006_DISPMODE_INPUT_RES_NATIVE 0x5
+#define CH7006_DISPMODE_OUTPUT_STD 0, 4:3
+#define CH7006_DISPMODE_OUTPUT_STD_PAL 0x0
+#define CH7006_DISPMODE_OUTPUT_STD_NTSC 0x1
+#define CH7006_DISPMODE_OUTPUT_STD_PAL_M 0x2
+#define CH7006_DISPMODE_OUTPUT_STD_NTSC_J 0x3
+#define CH7006_DISPMODE_SCALING_RATIO 0, 2:0
+#define CH7006_DISPMODE_SCALING_RATIO_5_4 0x0
+#define CH7006_DISPMODE_SCALING_RATIO_1_1 0x1
+#define CH7006_DISPMODE_SCALING_RATIO_7_8 0x2
+#define CH7006_DISPMODE_SCALING_RATIO_5_6 0x3
+#define CH7006_DISPMODE_SCALING_RATIO_3_4 0x4
+#define CH7006_DISPMODE_SCALING_RATIO_7_10 0x5
+
+#define CH7006_FFILTER 0x01
+#define CH7006_FFILTER_TEXT 0, 5:4
+#define CH7006_FFILTER_LUMA 0, 3:2
+#define CH7006_FFILTER_CHROMA 0, 1:0
+#define CH7006_FFILTER_CHROMA_NO_DCRAWL 0x3
+
+#define CH7006_BWIDTH 0x03
+#define CH7006_BWIDTH_5L_FFILER (1 << 7)
+#define CH7006_BWIDTH_CVBS_NO_CHROMA (1 << 6)
+#define CH7006_BWIDTH_CHROMA 0, 5:4
+#define CH7006_BWIDTH_SVIDEO_YPEAK (1 << 3)
+#define CH7006_BWIDTH_SVIDEO_LUMA 0, 2:1
+#define CH7006_BWIDTH_CVBS_LUMA 0, 0:0
+
+#define CH7006_INPUT_FORMAT 0x04
+#define CH7006_INPUT_FORMAT_DAC_GAIN (1 << 6)
+#define CH7006_INPUT_FORMAT_RGB_PASS_THROUGH (1 << 5)
+#define CH7006_INPUT_FORMAT_FORMAT 0, 3:0
+#define CH7006_INPUT_FORMAT_FORMAT_RGB16 0x0
+#define CH7006_INPUT_FORMAT_FORMAT_YCrCb24m16 0x1
+#define CH7006_INPUT_FORMAT_FORMAT_RGB24m16 0x2
+#define CH7006_INPUT_FORMAT_FORMAT_RGB15 0x3
+#define CH7006_INPUT_FORMAT_FORMAT_RGB24m12C 0x4
+#define CH7006_INPUT_FORMAT_FORMAT_RGB24m12I 0x5
+#define CH7006_INPUT_FORMAT_FORMAT_RGB24m8 0x6
+#define CH7006_INPUT_FORMAT_FORMAT_RGB16m8 0x7
+#define CH7006_INPUT_FORMAT_FORMAT_RGB15m8 0x8
+#define CH7006_INPUT_FORMAT_FORMAT_YCrCb24m8 0x9
+
+#define CH7006_CLKMODE 0x06
+#define CH7006_CLKMODE_SUBC_LOCK (1 << 7)
+#define CH7006_CLKMODE_MASTER (1 << 6)
+#define CH7006_CLKMODE_POS_EDGE (1 << 4)
+#define CH7006_CLKMODE_XCM 0, 3:2
+#define CH7006_CLKMODE_PCM 0, 1:0
+
+#define CH7006_START_ACTIVE 0x07
+#define CH7006_START_ACTIVE_0 0, 7:0
+
+#define CH7006_POV 0x08
+#define CH7006_POV_START_ACTIVE_8 8, 2:2
+#define CH7006_POV_HPOS_8 8, 1:1
+#define CH7006_POV_VPOS_8 8, 0:0
+
+#define CH7006_BLACK_LEVEL 0x09
+#define CH7006_BLACK_LEVEL_0 0, 7:0
+
+#define CH7006_HPOS 0x0a
+#define CH7006_HPOS_0 0, 7:0
+
+#define CH7006_VPOS 0x0b
+#define CH7006_VPOS_0 0, 7:0
+
+#define CH7006_INPUT_SYNC 0x0d
+#define CH7006_INPUT_SYNC_EMBEDDED (1 << 3)
+#define CH7006_INPUT_SYNC_OUTPUT (1 << 2)
+#define CH7006_INPUT_SYNC_PVSYNC (1 << 1)
+#define CH7006_INPUT_SYNC_PHSYNC (1 << 0)
+
+#define CH7006_POWER 0x0e
+#define CH7006_POWER_SCART (1 << 4)
+#define CH7006_POWER_RESET (1 << 3)
+#define CH7006_POWER_LEVEL 0, 2:0
+#define CH7006_POWER_LEVEL_CVBS_OFF 0x0
+#define CH7006_POWER_LEVEL_POWER_OFF 0x1
+#define CH7006_POWER_LEVEL_SVIDEO_OFF 0x2
+#define CH7006_POWER_LEVEL_NORMAL 0x3
+#define CH7006_POWER_LEVEL_FULL_POWER_OFF 0x4
+
+#define CH7006_DETECT 0x10
+#define CH7006_DETECT_SVIDEO_Y_TEST (1 << 3)
+#define CH7006_DETECT_SVIDEO_C_TEST (1 << 2)
+#define CH7006_DETECT_CVBS_TEST (1 << 1)
+#define CH7006_DETECT_SENSE (1 << 0)
+
+#define CH7006_CONTRAST 0x11
+#define CH7006_CONTRAST_0 0, 2:0
+
+#define CH7006_PLLOV 0x13
+#define CH7006_PLLOV_N_8 8, 2:1
+#define CH7006_PLLOV_M_8 8, 0:0
+
+#define CH7006_PLLM 0x14
+#define CH7006_PLLM_0 0, 7:0
+
+#define CH7006_PLLN 0x15
+#define CH7006_PLLN_0 0, 7:0
+
+#define CH7006_BCLKOUT 0x17
+
+#define CH7006_SUBC_INC0 0x18
+#define CH7006_SUBC_INC0_28 28, 3:0
+
+#define CH7006_SUBC_INC1 0x19
+#define CH7006_SUBC_INC1_24 24, 3:0
+
+#define CH7006_SUBC_INC2 0x1a
+#define CH7006_SUBC_INC2_20 20, 3:0
+
+#define CH7006_SUBC_INC3 0x1b
+#define CH7006_SUBC_INC3_GPIO1_VAL (1 << 7)
+#define CH7006_SUBC_INC3_GPIO0_VAL (1 << 6)
+#define CH7006_SUBC_INC3_POUT_3_3V (1 << 5)
+#define CH7006_SUBC_INC3_POUT_INV (1 << 4)
+#define CH7006_SUBC_INC3_16 16, 3:0
+
+#define CH7006_SUBC_INC4 0x1c
+#define CH7006_SUBC_INC4_GPIO1_IN (1 << 7)
+#define CH7006_SUBC_INC4_GPIO0_IN (1 << 6)
+#define CH7006_SUBC_INC4_DS_INPUT (1 << 4)
+#define CH7006_SUBC_INC4_12 12, 3:0
+
+#define CH7006_SUBC_INC5 0x1d
+#define CH7006_SUBC_INC5_8 8, 3:0
+
+#define CH7006_SUBC_INC6 0x1e
+#define CH7006_SUBC_INC6_4 4, 3:0
+
+#define CH7006_SUBC_INC7 0x1f
+#define CH7006_SUBC_INC7_0 0, 3:0
+
+#define CH7006_PLL_CONTROL 0x20
+#define CH7006_PLL_CONTROL_CPI (1 << 5)
+#define CH7006_PLL_CONTROL_CAPACITOR (1 << 4)
+#define CH7006_PLL_CONTROL_7STAGES (1 << 3)
+#define CH7006_PLL_CONTROL_DIGITAL_5V (1 << 2)
+#define CH7006_PLL_CONTROL_ANALOG_5V (1 << 1)
+#define CH7006_PLL_CONTROL_MEMORY_5V (1 << 0)
+
+#define CH7006_CALC_SUBC_INC0 0x21
+#define CH7006_CALC_SUBC_INC0_24 24, 4:3
+#define CH7006_CALC_SUBC_INC0_HYST 0, 2:1
+#define CH7006_CALC_SUBC_INC0_AUTO (1 << 0)
+
+#define CH7006_CALC_SUBC_INC1 0x22
+#define CH7006_CALC_SUBC_INC1_16 16, 7:0
+
+#define CH7006_CALC_SUBC_INC2 0x23
+#define CH7006_CALC_SUBC_INC2_8 8, 7:0
+
+#define CH7006_CALC_SUBC_INC3 0x24
+#define CH7006_CALC_SUBC_INC3_0 0, 7:0
+
+#define CH7006_VERSION_ID 0x25
+
+#endif
--- /dev/null
+config DRM_NOUVEAU
+ tristate "Nouveau (nVidia) cards"
+ depends on DRM
+ select FW_LOADER
+ select DRM_KMS_HELPER
+ select DRM_TTM
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ select FB
+ select FRAMEBUFFER_CONSOLE if !EMBEDDED
+ select FB_BACKLIGHT if DRM_NOUVEAU_BACKLIGHT
+ help
+ Choose this option for open-source nVidia support.
+
+config DRM_NOUVEAU_BACKLIGHT
+ bool "Support for backlight control"
+ depends on DRM_NOUVEAU
+ default y
+ help
+ Say Y here if you want to control the backlight of your display
+ (e.g. a laptop panel).
+
+config DRM_NOUVEAU_DEBUG
+ bool "Build in Nouveau's debugfs support"
+ depends on DRM_NOUVEAU && DEBUG_FS
+ default y
+ help
+ Say Y here if you want Nouveau to output debugging information
+ via debugfs.
+
+menu "I2C encoder or helper chips"
+ depends on DRM
+
+config DRM_I2C_CH7006
+ tristate "Chrontel ch7006 TV encoder"
+ default m if DRM_NOUVEAU
+ help
+ Support for Chrontel ch7006 and similar TV encoders, found
+ on some nVidia video cards.
+
+ This driver is currently only useful if you're also using
+ the nouveau driver.
+endmenu
--- /dev/null
+#
+# Makefile for the drm device driver. This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+ccflags-y := -Iinclude/drm
+nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \
+ nouveau_object.o nouveau_irq.o nouveau_notifier.o \
+ nouveau_sgdma.o nouveau_dma.o \
+ nouveau_bo.o nouveau_fence.o nouveau_gem.o nouveau_ttm.o \
+ nouveau_hw.o nouveau_calc.o nouveau_bios.o nouveau_i2c.o \
+ nouveau_display.o nouveau_connector.o nouveau_fbcon.o \
+ nouveau_dp.o \
+ nv04_timer.o \
+ nv04_mc.o nv40_mc.o nv50_mc.o \
+ nv04_fb.o nv10_fb.o nv40_fb.o \
+ nv04_fifo.o nv10_fifo.o nv40_fifo.o nv50_fifo.o \
+ nv04_graph.o nv10_graph.o nv20_graph.o \
+ nv40_graph.o nv50_graph.o \
+ nv04_instmem.o nv50_instmem.o \
+ nv50_crtc.o nv50_dac.o nv50_sor.o \
+ nv50_cursor.o nv50_display.o nv50_fbcon.o \
+ nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o \
+ nv04_crtc.o nv04_display.o nv04_cursor.o nv04_fbcon.o \
+ nv17_gpio.o
+
+nouveau-$(CONFIG_DRM_NOUVEAU_DEBUG) += nouveau_debugfs.o
+nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o
+nouveau-$(CONFIG_DRM_NOUVEAU_BACKLIGHT) += nouveau_backlight.o
+nouveau-$(CONFIG_ACPI) += nouveau_acpi.o
+
+obj-$(CONFIG_DRM_NOUVEAU)+= nouveau.o
--- /dev/null
+#include <linux/pci.h>
+#include <linux/acpi.h>
+#include <acpi/acpi_drivers.h>
+#include <acpi/acpi_bus.h>
+
+#include "drmP.h"
+#include "drm.h"
+#include "drm_sarea.h"
+#include "drm_crtc_helper.h"
+#include "nouveau_drv.h"
+#include "nouveau_drm.h"
+#include "nv50_display.h"
+
+#define NOUVEAU_DSM_SUPPORTED 0x00
+#define NOUVEAU_DSM_SUPPORTED_FUNCTIONS 0x00
+
+#define NOUVEAU_DSM_ACTIVE 0x01
+#define NOUVEAU_DSM_ACTIVE_QUERY 0x00
+
+#define NOUVEAU_DSM_LED 0x02
+#define NOUVEAU_DSM_LED_STATE 0x00
+#define NOUVEAU_DSM_LED_OFF 0x10
+#define NOUVEAU_DSM_LED_STAMINA 0x11
+#define NOUVEAU_DSM_LED_SPEED 0x12
+
+#define NOUVEAU_DSM_POWER 0x03
+#define NOUVEAU_DSM_POWER_STATE 0x00
+#define NOUVEAU_DSM_POWER_SPEED 0x01
+#define NOUVEAU_DSM_POWER_STAMINA 0x02
+
+static int nouveau_dsm(struct drm_device *dev, int func, int arg, int *result)
+{
+ static char muid[] = {
+ 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D,
+ 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4,
+ };
+
+ struct pci_dev *pdev = dev->pdev;
+ struct acpi_handle *handle;
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_object_list input;
+ union acpi_object params[4];
+ union acpi_object *obj;
+ int err;
+
+ handle = DEVICE_ACPI_HANDLE(&pdev->dev);
+
+ if (!handle)
+ return -ENODEV;
+
+ input.count = 4;
+ input.pointer = params;
+ params[0].type = ACPI_TYPE_BUFFER;
+ params[0].buffer.length = sizeof(muid);
+ params[0].buffer.pointer = (char *)muid;
+ params[1].type = ACPI_TYPE_INTEGER;
+ params[1].integer.value = 0x00000102;
+ params[2].type = ACPI_TYPE_INTEGER;
+ params[2].integer.value = func;
+ params[3].type = ACPI_TYPE_INTEGER;
+ params[3].integer.value = arg;
+
+ err = acpi_evaluate_object(handle, "_DSM", &input, &output);
+ if (err) {
+ NV_INFO(dev, "failed to evaluate _DSM: %d\n", err);
+ return err;
+ }
+
+ obj = (union acpi_object *)output.pointer;
+
+ if (obj->type == ACPI_TYPE_INTEGER)
+ if (obj->integer.value == 0x80000002)
+ return -ENODEV;
+
+ if (obj->type == ACPI_TYPE_BUFFER) {
+ if (obj->buffer.length == 4 && result) {
+ *result = 0;
+ *result |= obj->buffer.pointer[0];
+ *result |= (obj->buffer.pointer[1] << 8);
+ *result |= (obj->buffer.pointer[2] << 16);
+ *result |= (obj->buffer.pointer[3] << 24);
+ }
+ }
+
+ kfree(output.pointer);
+ return 0;
+}
+
+int nouveau_hybrid_setup(struct drm_device *dev)
+{
+ int result;
+
+ if (nouveau_dsm(dev, NOUVEAU_DSM_ACTIVE, NOUVEAU_DSM_ACTIVE_QUERY,
+ &result))
+ return -ENODEV;
+
+ NV_INFO(dev, "_DSM hardware status gave 0x%x\n", result);
+
+ if (result & 0x1) { /* Stamina mode - disable the external GPU */
+ nouveau_dsm(dev, NOUVEAU_DSM_LED, NOUVEAU_DSM_LED_STAMINA,
+ NULL);
+ nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_STAMINA,
+ NULL);
+ } else { /* Ensure that the external GPU is enabled */
+ nouveau_dsm(dev, NOUVEAU_DSM_LED, NOUVEAU_DSM_LED_SPEED, NULL);
+ nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_SPEED,
+ NULL);
+ }
+
+ return 0;
+}
+
+bool nouveau_dsm_probe(struct drm_device *dev)
+{
+ int support = 0;
+
+ if (nouveau_dsm(dev, NOUVEAU_DSM_SUPPORTED,
+ NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &support))
+ return false;
+
+ if (!support)
+ return false;
+
+ return true;
+}
--- /dev/null
+/*
+ * Copyright (C) 2009 Red Hat <mjg@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (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 (including the
+ * next paragraph) 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 COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS 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.
+ *
+ */
+
+/*
+ * Authors:
+ * Matthew Garrett <mjg@redhat.com>
+ *
+ * Register locations derived from NVClock by Roderick Colenbrander
+ */
+
+#include <linux/backlight.h>
+
+#include "drmP.h"
+#include "nouveau_drv.h"
+#include "nouveau_drm.h"
+#include "nouveau_reg.h"
+
+static int nv40_get_intensity(struct backlight_device *bd)
+{
+ struct drm_device *dev = bl_get_data(bd);
+ int val = (nv_rd32(dev, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK)
+ >> 16;
+
+ return val;
+}
+
+static int nv40_set_intensity(struct backlight_device *bd)
+{
+ struct drm_device *dev = bl_get_data(bd);
+ int val = bd->props.brightness;
+ int reg = nv_rd32(dev, NV40_PMC_BACKLIGHT);
+
+ nv_wr32(dev, NV40_PMC_BACKLIGHT,
+ (val << 16) | (reg & ~NV40_PMC_BACKLIGHT_MASK));
+
+ return 0;
+}
+
+static struct backlight_ops nv40_bl_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .get_brightness = nv40_get_intensity,
+ .update_status = nv40_set_intensity,
+};
+
+static int nv50_get_intensity(struct backlight_device *bd)
+{
+ struct drm_device *dev = bl_get_data(bd);
+
+ return nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT);
+}
+
+static int nv50_set_intensity(struct backlight_device *bd)
+{
+ struct drm_device *dev = bl_get_data(bd);
+ int val = bd->props.brightness;
+
+ nv_wr32(dev, NV50_PDISPLAY_SOR_BACKLIGHT,
+ val | NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE);
+ return 0;
+}
+
+static struct backlight_ops nv50_bl_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .get_brightness = nv50_get_intensity,
+ .update_status = nv50_set_intensity,
+};
+
+static int nouveau_nv40_backlight_init(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct backlight_device *bd;
+
+ if (!(nv_rd32(dev, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK))
+ return 0;
+
+ bd = backlight_device_register("nv_backlight", &dev->pdev->dev, dev,
+ &nv40_bl_ops);
+ if (IS_ERR(bd))
+ return PTR_ERR(bd);
+
+ dev_priv->backlight = bd;
+ bd->props.max_brightness = 31;
+ bd->props.brightness = nv40_get_intensity(bd);
+ backlight_update_status(bd);
+
+ return 0;
+}
+
+static int nouveau_nv50_backlight_init(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct backlight_device *bd;
+
+ if (!nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT))
+ return 0;
+
+ bd = backlight_device_register("nv_backlight", &dev->pdev->dev, dev,
+ &nv50_bl_ops);
+ if (IS_ERR(bd))
+ return PTR_ERR(bd);
+
+ dev_priv->backlight = bd;
+ bd->props.max_brightness = 1025;
+ bd->props.brightness = nv50_get_intensity(bd);
+ backlight_update_status(bd);
+ return 0;
+}
+
+int nouveau_backlight_init(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+ switch (dev_priv->card_type) {
+ case NV_40:
+ return nouveau_nv40_backlight_init(dev);
+ case NV_50:
+ return nouveau_nv50_backlight_init(dev);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+void nouveau_backlight_exit(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+ if (dev_priv->backlight) {
+ backlight_device_unregister(dev_priv->backlight);
+ dev_priv->backlight = NULL;
+ }
+}
--- /dev/null
+/*
+ * Copyright 2005-2006 Erik Waling
+ * Copyright 2006 Stephane Marchesin
+ * Copyright 2007-2009 Stuart Bennett
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (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 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "drmP.h"
+#define NV_DEBUG_NOTRACE
+#include "nouveau_drv.h"
+#include "nouveau_hw.h"
+
+/* these defines are made up */
+#define NV_CIO_CRE_44_HEADA 0x0
+#define NV_CIO_CRE_44_HEADB 0x3
+#define FEATURE_MOBILE 0x10 /* also FEATURE_QUADRO for BMP */
+#define LEGACY_I2C_CRT 0x80
+#define LEGACY_I2C_PANEL 0x81
+#define LEGACY_I2C_TV 0x82
+
+#define EDID1_LEN 128
+
+#define BIOSLOG(sip, fmt, arg...) NV_DEBUG(sip->dev, fmt, ##arg)
+#define LOG_OLD_VALUE(x)
+
+#define ROM16(x) le16_to_cpu(*(uint16_t *)&(x))
+#define ROM32(x) le32_to_cpu(*(uint32_t *)&(x))
+
+struct init_exec {
+ bool execute;
+ bool repeat;
+};
+
+static bool nv_cksum(const uint8_t *data, unsigned int length)
+{
+ /*
+ * There's a few checksums in the BIOS, so here's a generic checking
+ * function.
+ */
+ int i;
+ uint8_t sum = 0;
+
+ for (i = 0; i < length; i++)
+ sum += data[i];
+
+ if (sum)
+ return true;
+
+ return false;
+}
+
+static int
+score_vbios(struct drm_device *dev, const uint8_t *data, const bool writeable)
+{
+ if (!(data[0] == 0x55 && data[1] == 0xAA)) {
+ NV_TRACEWARN(dev, "... BIOS signature not found\n");
+ return 0;
+ }
+
+ if (nv_cksum(data, data[2] * 512)) {
+ NV_TRACEWARN(dev, "... BIOS checksum invalid\n");
+ /* if a ro image is somewhat bad, it's probably all rubbish */
+ return writeable ? 2 : 1;
+ } else
+ NV_TRACE(dev, "... appears to be valid\n");
+
+ return 3;
+}
+
+static void load_vbios_prom(struct drm_device *dev, uint8_t *data)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ uint32_t pci_nv_20, save_pci_nv_20;
+ int pcir_ptr;
+ int i;
+
+ if (dev_priv->card_type >= NV_50)
+ pci_nv_20 = 0x88050;
+ else
+ pci_nv_20 = NV_PBUS_PCI_NV_20;
+
+ /* enable ROM access */
+ save_pci_nv_20 = nvReadMC(dev, pci_nv_20);
+ nvWriteMC(dev, pci_nv_20,
+ save_pci_nv_20 & ~NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED);
+
+ /* bail if no rom signature */
+ if (nv_rd08(dev, NV_PROM_OFFSET) != 0x55 ||
+ nv_rd08(dev, NV_PROM_OFFSET + 1) != 0xaa)
+ goto out;
+
+ /* additional check (see note below) - read PCI record header */
+ pcir_ptr = nv_rd08(dev, NV_PROM_OFFSET + 0x18) |
+ nv_rd08(dev, NV_PROM_OFFSET + 0x19) << 8;
+ if (nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr) != 'P' ||
+ nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 1) != 'C' ||
+ nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 2) != 'I' ||
+ nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 3) != 'R')
+ goto out;
+
+ /* on some 6600GT/6800LE prom reads are messed up. nvclock alleges a
+ * a good read may be obtained by waiting or re-reading (cargocult: 5x)
+ * each byte. we'll hope pramin has something usable instead
+ */
+ for (i = 0; i < NV_PROM_SIZE; i++)
+ data[i] = nv_rd08(dev, NV_PROM_OFFSET + i);
+
+out:
+ /* disable ROM access */
+ nvWriteMC(dev, pci_nv_20,
+ save_pci_nv_20 | NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED);
+}
+
+static void load_vbios_pramin(struct drm_device *dev, uint8_t *data)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ uint32_t old_bar0_pramin = 0;
+ int i;
+
+ if (dev_priv->card_type >= NV_50) {
+ uint32_t vbios_vram = (nv_rd32(dev, 0x619f04) & ~0xff) << 8;
+
+ if (!vbios_vram)
+ vbios_vram = (nv_rd32(dev, 0x1700) << 16) + 0xf0000;
+
+ old_bar0_pramin = nv_rd32(dev, 0x1700);
+ nv_wr32(dev, 0x1700, vbios_vram >> 16);
+ }
+
+ /* bail if no rom signature */
+ if (nv_rd08(dev, NV_PRAMIN_OFFSET) != 0x55 ||
+ nv_rd08(dev, NV_PRAMIN_OFFSET + 1) != 0xaa)
+ goto out;
+
+ for (i = 0; i < NV_PROM_SIZE; i++)
+ data[i] = nv_rd08(dev, NV_PRAMIN_OFFSET + i);
+
+out:
+ if (dev_priv->card_type >= NV_50)
+ nv_wr32(dev, 0x1700, old_bar0_pramin);
+}
+
+static void load_vbios_pci(struct drm_device *dev, uint8_t *data)
+{
+ void __iomem *rom = NULL;
+ size_t rom_len;
+ int ret;
+
+ ret = pci_enable_rom(dev->pdev);
+ if (ret)
+ return;
+
+ rom = pci_map_rom(dev->pdev, &rom_len);
+ if (!rom)
+ goto out;
+ memcpy_fromio(data, rom, rom_len);
+ pci_unmap_rom(dev->pdev, rom);
+
+out:
+ pci_disable_rom(dev->pdev);
+}
+
+struct methods {
+ const char desc[8];
+ void (*loadbios)(struct drm_device *, uint8_t *);
+ const bool rw;
+ int score;
+};
+
+static struct methods nv04_methods[] = {
+ { "PROM", load_vbios_prom, false },
+ { "PRAMIN", load_vbios_pramin, true },
+ { "PCIROM", load_vbios_pci, true },
+ { }
+};
+
+static struct methods nv50_methods[] = {
+ { "PRAMIN", load_vbios_pramin, true },
+ { "PROM", load_vbios_prom, false },
+ { "PCIROM", load_vbios_pci, true },
+ { }
+};
+
+static bool NVShadowVBIOS(struct drm_device *dev, uint8_t *data)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct methods *methods, *method;
+ int testscore = 3;
+
+ if (nouveau_vbios) {
+ method = nv04_methods;
+ while (method->loadbios) {
+ if (!strcasecmp(nouveau_vbios, method->desc))
+ break;
+ method++;
+ }
+
+ if (method->loadbios) {
+ NV_INFO(dev, "Attempting to use BIOS image from %s\n",
+ method->desc);
+
+ method->loadbios(dev, data);
+ if (score_vbios(dev, data, method->rw))
+ return true;
+ }
+
+ NV_ERROR(dev, "VBIOS source \'%s\' invalid\n", nouveau_vbios);
+ }
+
+ if (dev_priv->card_type < NV_50)
+ methods = nv04_methods;
+ else
+ methods = nv50_methods;
+
+ method = methods;
+ while (method->loadbios) {
+ NV_TRACE(dev, "Attempting to load BIOS image from %s\n",
+ method->desc);
+ data[0] = data[1] = 0; /* avoid reuse of previous image */
+ method->loadbios(dev, data);
+ method->score = score_vbios(dev, data, method->rw);
+ if (method->score == testscore)
+ return true;
+ method++;
+ }
+
+ while (--testscore > 0) {
+ method = methods;
+ while (method->loadbios) {
+ if (method->score == testscore) {
+ NV_TRACE(dev, "Using BIOS image from %s\n",
+ method->desc);
+ method->loadbios(dev, data);
+ return true;
+ }
+ method++;
+ }
+ }
+
+ NV_ERROR(dev, "No valid BIOS image found\n");
+ return false;
+}
+
+struct init_tbl_entry {
+ char *name;
+ uint8_t id;
+ int length;
+ int length_offset;
+ int length_multiplier;
+ bool (*handler)(struct nvbios *, uint16_t, struct init_exec *);
+};
+
+struct bit_entry {
+ uint8_t id[2];
+ uint16_t length;
+ uint16_t offset;
+};
+
+static int parse_init_table(struct nvbios *, unsigned int, struct init_exec *);
+
+#define MACRO_INDEX_SIZE 2
+#define MACRO_SIZE 8
+#define CONDITION_SIZE 12
+#define IO_FLAG_CONDITION_SIZE 9
+#define IO_CONDITION_SIZE 5
+#define MEM_INIT_SIZE 66
+
+static void still_alive(void)
+{
+#if 0
+ sync();
+ msleep(2);
+#endif
+}
+
+static uint32_t
+munge_reg(struct nvbios *bios, uint32_t reg)
+{
+ struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
+ struct dcb_entry *dcbent = bios->display.output;
+
+ if (dev_priv->card_type < NV_50)
+ return reg;
+
+ if (reg & 0x40000000) {
+ BUG_ON(!dcbent);
+
+ reg += (ffs(dcbent->or) - 1) * 0x800;
+ if ((reg & 0x20000000) && !(dcbent->sorconf.link & 1))
+ reg += 0x00000080;
+ }
+
+ reg &= ~0x60000000;
+ return reg;
+}
+
+static int
+valid_reg(struct nvbios *bios, uint32_t reg)
+{
+ struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
+ struct drm_device *dev = bios->dev;
+
+ /* C51 has misaligned regs on purpose. Marvellous */
+ if (reg & 0x2 || (reg & 0x1 && dev_priv->VBIOS.pub.chip_version != 0x51)) {
+ NV_ERROR(dev, "========== misaligned reg 0x%08X ==========\n",
+ reg);
+ return 0;
+ }
+ /*
+ * Warn on C51 regs that have not been verified accessible in
+ * mmiotracing
+ */
+ if (reg & 0x1 && dev_priv->VBIOS.pub.chip_version == 0x51 &&
+ reg != 0x130d && reg != 0x1311 && reg != 0x60081d)
+ NV_WARN(dev, "=== C51 misaligned reg 0x%08X not verified ===\n",
+ reg);
+
+ /* Trust the init scripts on G80 */
+ if (dev_priv->card_type >= NV_50)
+ return 1;
+
+ #define WITHIN(x, y, z) ((x >= y) && (x < y + z))
+ if (WITHIN(reg, NV_PMC_OFFSET, NV_PMC_SIZE))
+ return 1;
+ if (WITHIN(reg, NV_PBUS_OFFSET, NV_PBUS_SIZE))
+ return 1;
+ if (WITHIN(reg, NV_PFIFO_OFFSET, NV_PFIFO_SIZE))
+ return 1;
+ if (dev_priv->VBIOS.pub.chip_version >= 0x30 &&
+ (WITHIN(reg, 0x4000, 0x600) || reg == 0x00004600))
+ return 1;
+ if (dev_priv->VBI