Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
Conflicts: drivers/net/vxlan.c drivers/vhost/net.c include/linux/if_vlan.h net/core/dev.c The net/core/dev.c conflict was the overlap of one commit marking an existing function static whilst another was adding a new function. In the include/linux/if_vlan.h case, the type used for a local variable was changed in 'net', whereas the function got rewritten to fix a stacked vlan bug in 'net-next'. In drivers/vhost/net.c, Al Viro's iov_iter conversions in 'net-next' overlapped with an endainness fix for VHOST 1.0 in 'net'. In drivers/net/vxlan.c, vxlan_find_vni() added a 'flags' parameter in 'net-next' whereas in 'net' there was a bug fix to pass in the correct network namespace pointer in calls to this function. Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
6e03f896b5
@ -31,7 +31,7 @@ i2c0: i2c@fed40000 {
|
||||
compatible = "st,comms-ssc4-i2c";
|
||||
reg = <0xfed40000 0x110>;
|
||||
interrupts = <GIC_SPI 187 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&CLK_S_ICN_REG_0>;
|
||||
clocks = <&clk_s_a0_ls CLK_ICN_REG>;
|
||||
clock-names = "ssc";
|
||||
clock-frequency = <400000>;
|
||||
pinctrl-names = "default";
|
||||
|
@ -47,6 +47,7 @@ dallas,ds3232 Extremely Accurate I²C RTC with Integrated Crystal and SRAM
|
||||
dallas,ds4510 CPU Supervisor with Nonvolatile Memory and Programmable I/O
|
||||
dallas,ds75 Digital Thermometer and Thermostat
|
||||
dlg,da9053 DA9053: flexible system level PMIC with multicore support
|
||||
dlg,da9063 DA9063: system PMIC for quad-core application processors
|
||||
epson,rx8025 High-Stability. I2C-Bus INTERFACE REAL TIME CLOCK MODULE
|
||||
epson,rx8581 I2C-BUS INTERFACE REAL TIME CLOCK MODULE
|
||||
fsl,mag3110 MAG3110: Xtrinsic High Accuracy, 3D Magnetometer
|
||||
|
@ -199,16 +199,9 @@ frame header.
|
||||
TX limitations
|
||||
--------------
|
||||
|
||||
Kernel processing usually involves validation of the message received by
|
||||
user-space, then processing its contents. The kernel must assure that
|
||||
userspace is not able to modify the message contents after they have been
|
||||
validated. In order to do so, the message is copied from the ring frame
|
||||
to an allocated buffer if either of these conditions is false:
|
||||
|
||||
- only a single mapping of the ring exists
|
||||
- the file descriptor is not shared between processes
|
||||
|
||||
This means that for threaded programs, the kernel will fall back to copying.
|
||||
As of Jan 2015 the message is always copied from the ring frame to an
|
||||
allocated buffer due to unresolved security concerns.
|
||||
See commit 4682a0358639b29cf ("netlink: Always copy on mmap TX.").
|
||||
|
||||
Example
|
||||
-------
|
||||
|
11
MAINTAINERS
11
MAINTAINERS
@ -708,6 +708,16 @@ X: drivers/iio/*/adjd*
|
||||
F: drivers/staging/iio/*/ad*
|
||||
F: staging/iio/trigger/iio-trig-bfin-timer.c
|
||||
|
||||
ANDROID DRIVERS
|
||||
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
M: Arve Hjønnevåg <arve@android.com>
|
||||
M: Riley Andrews <riandrews@android.com>
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/gregkh/staging.git
|
||||
L: devel@driverdev.osuosl.org
|
||||
S: Supported
|
||||
F: drivers/android/
|
||||
F: drivers/staging/android/
|
||||
|
||||
AOA (Apple Onboard Audio) ALSA DRIVER
|
||||
M: Johannes Berg <johannes@sipsolutions.net>
|
||||
L: linuxppc-dev@lists.ozlabs.org
|
||||
@ -10181,6 +10191,7 @@ USERSPACE I/O (UIO)
|
||||
M: "Hans J. Koch" <hjk@hansjkoch.de>
|
||||
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git
|
||||
F: Documentation/DocBook/uio-howto.tmpl
|
||||
F: drivers/uio/
|
||||
F: include/linux/uio*.h
|
||||
|
2
Makefile
2
Makefile
@ -1,7 +1,7 @@
|
||||
VERSION = 3
|
||||
PATCHLEVEL = 19
|
||||
SUBLEVEL = 0
|
||||
EXTRAVERSION = -rc6
|
||||
EXTRAVERSION = -rc7
|
||||
NAME = Diseased Newt
|
||||
|
||||
# *DOCUMENTATION*
|
||||
|
@ -156,6 +156,8 @@ retry:
|
||||
if (unlikely(fault & VM_FAULT_ERROR)) {
|
||||
if (fault & VM_FAULT_OOM)
|
||||
goto out_of_memory;
|
||||
else if (fault & VM_FAULT_SIGSEGV)
|
||||
goto bad_area;
|
||||
else if (fault & VM_FAULT_SIGBUS)
|
||||
goto do_sigbus;
|
||||
BUG();
|
||||
|
@ -161,6 +161,8 @@ good_area:
|
||||
|
||||
if (fault & VM_FAULT_OOM)
|
||||
goto out_of_memory;
|
||||
else if (fault & VM_FAULT_SIGSEGV)
|
||||
goto bad_area;
|
||||
else if (fault & VM_FAULT_SIGBUS)
|
||||
goto do_sigbus;
|
||||
|
||||
|
@ -263,16 +263,37 @@ restart: adr r0, LC0
|
||||
* OK... Let's do some funky business here.
|
||||
* If we do have a DTB appended to zImage, and we do have
|
||||
* an ATAG list around, we want the later to be translated
|
||||
* and folded into the former here. To be on the safe side,
|
||||
* let's temporarily move the stack away into the malloc
|
||||
* area. No GOT fixup has occurred yet, but none of the
|
||||
* code we're about to call uses any global variable.
|
||||
* and folded into the former here. No GOT fixup has occurred
|
||||
* yet, but none of the code we're about to call uses any
|
||||
* global variable.
|
||||
*/
|
||||
add sp, sp, #0x10000
|
||||
|
||||
/* Get the initial DTB size */
|
||||
ldr r5, [r6, #4]
|
||||
#ifndef __ARMEB__
|
||||
/* convert to little endian */
|
||||
eor r1, r5, r5, ror #16
|
||||
bic r1, r1, #0x00ff0000
|
||||
mov r5, r5, ror #8
|
||||
eor r5, r5, r1, lsr #8
|
||||
#endif
|
||||
/* 50% DTB growth should be good enough */
|
||||
add r5, r5, r5, lsr #1
|
||||
/* preserve 64-bit alignment */
|
||||
add r5, r5, #7
|
||||
bic r5, r5, #7
|
||||
/* clamp to 32KB min and 1MB max */
|
||||
cmp r5, #(1 << 15)
|
||||
movlo r5, #(1 << 15)
|
||||
cmp r5, #(1 << 20)
|
||||
movhi r5, #(1 << 20)
|
||||
/* temporarily relocate the stack past the DTB work space */
|
||||
add sp, sp, r5
|
||||
|
||||
stmfd sp!, {r0-r3, ip, lr}
|
||||
mov r0, r8
|
||||
mov r1, r6
|
||||
sub r2, sp, r6
|
||||
mov r2, r5
|
||||
bl atags_to_fdt
|
||||
|
||||
/*
|
||||
@ -285,11 +306,11 @@ restart: adr r0, LC0
|
||||
bic r0, r0, #1
|
||||
add r0, r0, #0x100
|
||||
mov r1, r6
|
||||
sub r2, sp, r6
|
||||
mov r2, r5
|
||||
bleq atags_to_fdt
|
||||
|
||||
ldmfd sp!, {r0-r3, ip, lr}
|
||||
sub sp, sp, #0x10000
|
||||
sub sp, sp, r5
|
||||
#endif
|
||||
|
||||
mov r8, r6 @ use the appended device tree
|
||||
@ -306,7 +327,7 @@ restart: adr r0, LC0
|
||||
subs r1, r5, r1
|
||||
addhi r9, r9, r1
|
||||
|
||||
/* Get the dtb's size */
|
||||
/* Get the current DTB size */
|
||||
ldr r5, [r6, #4]
|
||||
#ifndef __ARMEB__
|
||||
/* convert r5 (dtb size) to little endian */
|
||||
|
@ -17,14 +17,6 @@
|
||||
|
||||
aliases {
|
||||
ethernet0 = &emac;
|
||||
serial0 = &uart0;
|
||||
serial1 = &uart1;
|
||||
serial2 = &uart2;
|
||||
serial3 = &uart3;
|
||||
serial4 = &uart4;
|
||||
serial5 = &uart5;
|
||||
serial6 = &uart6;
|
||||
serial7 = &uart7;
|
||||
};
|
||||
|
||||
chosen {
|
||||
@ -39,6 +31,14 @@
|
||||
<&ahb_gates 44>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
framebuffer@1 {
|
||||
compatible = "allwinner,simple-framebuffer", "simple-framebuffer";
|
||||
allwinner,pipeline = "de_fe0-de_be0-lcd0-hdmi";
|
||||
clocks = <&pll5 1>, <&ahb_gates 36>, <&ahb_gates 43>,
|
||||
<&ahb_gates 44>, <&ahb_gates 46>;
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
|
||||
cpus {
|
||||
@ -438,8 +438,8 @@
|
||||
reg-names = "phy_ctrl", "pmu1", "pmu2";
|
||||
clocks = <&usb_clk 8>;
|
||||
clock-names = "usb_phy";
|
||||
resets = <&usb_clk 1>, <&usb_clk 2>;
|
||||
reset-names = "usb1_reset", "usb2_reset";
|
||||
resets = <&usb_clk 0>, <&usb_clk 1>, <&usb_clk 2>;
|
||||
reset-names = "usb0_reset", "usb1_reset", "usb2_reset";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -55,6 +55,12 @@
|
||||
model = "Olimex A10s-Olinuxino Micro";
|
||||
compatible = "olimex,a10s-olinuxino-micro", "allwinner,sun5i-a10s";
|
||||
|
||||
aliases {
|
||||
serial0 = &uart0;
|
||||
serial1 = &uart2;
|
||||
serial2 = &uart3;
|
||||
};
|
||||
|
||||
soc@01c00000 {
|
||||
emac: ethernet@01c0b000 {
|
||||
pinctrl-names = "default";
|
||||
|
@ -18,10 +18,6 @@
|
||||
|
||||
aliases {
|
||||
ethernet0 = &emac;
|
||||
serial0 = &uart0;
|
||||
serial1 = &uart1;
|
||||
serial2 = &uart2;
|
||||
serial3 = &uart3;
|
||||
};
|
||||
|
||||
chosen {
|
||||
@ -390,8 +386,8 @@
|
||||
reg-names = "phy_ctrl", "pmu1";
|
||||
clocks = <&usb_clk 8>;
|
||||
clock-names = "usb_phy";
|
||||
resets = <&usb_clk 1>;
|
||||
reset-names = "usb1_reset";
|
||||
resets = <&usb_clk 0>, <&usb_clk 1>;
|
||||
reset-names = "usb0_reset", "usb1_reset";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -53,6 +53,10 @@
|
||||
model = "HSG H702";
|
||||
compatible = "hsg,h702", "allwinner,sun5i-a13";
|
||||
|
||||
aliases {
|
||||
serial0 = &uart1;
|
||||
};
|
||||
|
||||
soc@01c00000 {
|
||||
mmc0: mmc@01c0f000 {
|
||||
pinctrl-names = "default";
|
||||
|
@ -54,6 +54,10 @@
|
||||
model = "Olimex A13-Olinuxino Micro";
|
||||
compatible = "olimex,a13-olinuxino-micro", "allwinner,sun5i-a13";
|
||||
|
||||
aliases {
|
||||
serial0 = &uart1;
|
||||
};
|
||||
|
||||
soc@01c00000 {
|
||||
mmc0: mmc@01c0f000 {
|
||||
pinctrl-names = "default";
|
||||
|
@ -55,6 +55,10 @@
|
||||
model = "Olimex A13-Olinuxino";
|
||||
compatible = "olimex,a13-olinuxino", "allwinner,sun5i-a13";
|
||||
|
||||
aliases {
|
||||
serial0 = &uart1;
|
||||
};
|
||||
|
||||
soc@01c00000 {
|
||||
mmc0: mmc@01c0f000 {
|
||||
pinctrl-names = "default";
|
||||
|
@ -16,11 +16,6 @@
|
||||
/ {
|
||||
interrupt-parent = <&intc>;
|
||||
|
||||
aliases {
|
||||
serial0 = &uart1;
|
||||
serial1 = &uart3;
|
||||
};
|
||||
|
||||
cpus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
@ -349,8 +344,8 @@
|
||||
reg-names = "phy_ctrl", "pmu1";
|
||||
clocks = <&usb_clk 8>;
|
||||
clock-names = "usb_phy";
|
||||
resets = <&usb_clk 1>;
|
||||
reset-names = "usb1_reset";
|
||||
resets = <&usb_clk 0>, <&usb_clk 1>;
|
||||
reset-names = "usb0_reset", "usb1_reset";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -53,12 +53,6 @@
|
||||
interrupt-parent = <&gic>;
|
||||
|
||||
aliases {
|
||||
serial0 = &uart0;
|
||||
serial1 = &uart1;
|
||||
serial2 = &uart2;
|
||||
serial3 = &uart3;
|
||||
serial4 = &uart4;
|
||||
serial5 = &uart5;
|
||||
ethernet0 = &gmac;
|
||||
};
|
||||
|
||||
|
@ -55,6 +55,12 @@
|
||||
model = "LeMaker Banana Pi";
|
||||
compatible = "lemaker,bananapi", "allwinner,sun7i-a20";
|
||||
|
||||
aliases {
|
||||
serial0 = &uart0;
|
||||
serial1 = &uart3;
|
||||
serial2 = &uart7;
|
||||
};
|
||||
|
||||
soc@01c00000 {
|
||||
spi0: spi@01c05000 {
|
||||
pinctrl-names = "default";
|
||||
|
@ -19,6 +19,14 @@
|
||||
model = "Merrii A20 Hummingbird";
|
||||
compatible = "merrii,a20-hummingbird", "allwinner,sun7i-a20";
|
||||
|
||||
aliases {
|
||||
serial0 = &uart0;
|
||||
serial1 = &uart2;
|
||||
serial2 = &uart3;
|
||||
serial3 = &uart4;
|
||||
serial4 = &uart5;
|
||||
};
|
||||
|
||||
soc@01c00000 {
|
||||
mmc0: mmc@01c0f000 {
|
||||
pinctrl-names = "default";
|
||||
|
@ -20,6 +20,9 @@
|
||||
compatible = "olimex,a20-olinuxino-micro", "allwinner,sun7i-a20";
|
||||
|
||||
aliases {
|
||||
serial0 = &uart0;
|
||||
serial1 = &uart6;
|
||||
serial2 = &uart7;
|
||||
spi0 = &spi1;
|
||||
spi1 = &spi2;
|
||||
};
|
||||
|
@ -54,14 +54,6 @@
|
||||
|
||||
aliases {
|
||||
ethernet0 = &gmac;
|
||||
serial0 = &uart0;
|
||||
serial1 = &uart1;
|
||||
serial2 = &uart2;
|
||||
serial3 = &uart3;
|
||||
serial4 = &uart4;
|
||||
serial5 = &uart5;
|
||||
serial6 = &uart6;
|
||||
serial7 = &uart7;
|
||||
};
|
||||
|
||||
chosen {
|
||||
|
@ -55,6 +55,10 @@
|
||||
model = "Ippo Q8H Dual Core Tablet (v5)";
|
||||
compatible = "ippo,q8h-v5", "allwinner,sun8i-a23";
|
||||
|
||||
aliases {
|
||||
serial0 = &r_uart;
|
||||
};
|
||||
|
||||
chosen {
|
||||
bootargs = "earlyprintk console=ttyS0,115200";
|
||||
};
|
||||
|
@ -52,15 +52,6 @@
|
||||
/ {
|
||||
interrupt-parent = <&gic>;
|
||||
|
||||
aliases {
|
||||
serial0 = &uart0;
|
||||
serial1 = &uart1;
|
||||
serial2 = &uart2;
|
||||
serial3 = &uart3;
|
||||
serial4 = &uart4;
|
||||
serial5 = &r_uart;
|
||||
};
|
||||
|
||||
cpus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
@ -54,6 +54,11 @@
|
||||
model = "Merrii A80 Optimus Board";
|
||||
compatible = "merrii,a80-optimus", "allwinner,sun9i-a80";
|
||||
|
||||
aliases {
|
||||
serial0 = &uart0;
|
||||
serial1 = &uart4;
|
||||
};
|
||||
|
||||
chosen {
|
||||
bootargs = "earlyprintk console=ttyS0,115200";
|
||||
};
|
||||
|
@ -52,16 +52,6 @@
|
||||
/ {
|
||||
interrupt-parent = <&gic>;
|
||||
|
||||
aliases {
|
||||
serial0 = &uart0;
|
||||
serial1 = &uart1;
|
||||
serial2 = &uart2;
|
||||
serial3 = &uart3;
|
||||
serial4 = &uart4;
|
||||
serial5 = &uart5;
|
||||
serial6 = &r_uart;
|
||||
};
|
||||
|
||||
cpus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
@ -38,6 +38,16 @@ static inline void vcpu_reset_hcr(struct kvm_vcpu *vcpu)
|
||||
vcpu->arch.hcr = HCR_GUEST_MASK;
|
||||
}
|
||||
|
||||
static inline unsigned long vcpu_get_hcr(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return vcpu->arch.hcr;
|
||||
}
|
||||
|
||||
static inline void vcpu_set_hcr(struct kvm_vcpu *vcpu, unsigned long hcr)
|
||||
{
|
||||
vcpu->arch.hcr = hcr;
|
||||
}
|
||||
|
||||
static inline bool vcpu_mode_is_32bit(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return 1;
|
||||
|
@ -125,9 +125,6 @@ struct kvm_vcpu_arch {
|
||||
* Anything that is not used directly from assembly code goes
|
||||
* here.
|
||||
*/
|
||||
/* dcache set/way operation pending */
|
||||
int last_pcpu;
|
||||
cpumask_t require_dcache_flush;
|
||||
|
||||
/* Don't run the guest on this vcpu */
|
||||
bool pause;
|
||||
|
@ -44,6 +44,7 @@
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#include <linux/highmem.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/pgalloc.h>
|
||||
|
||||
@ -161,13 +162,10 @@ static inline bool vcpu_has_cache_enabled(struct kvm_vcpu *vcpu)
|
||||
return (vcpu->arch.cp15[c1_SCTLR] & 0b101) == 0b101;
|
||||
}
|
||||
|
||||
static inline void coherent_cache_guest_page(struct kvm_vcpu *vcpu, hva_t hva,
|
||||
unsigned long size,
|
||||
bool ipa_uncached)
|
||||
static inline void __coherent_cache_guest_page(struct kvm_vcpu *vcpu, pfn_t pfn,
|
||||
unsigned long size,
|
||||
bool ipa_uncached)
|
||||
{
|
||||
if (!vcpu_has_cache_enabled(vcpu) || ipa_uncached)
|
||||
kvm_flush_dcache_to_poc((void *)hva, size);
|
||||
|
||||
/*
|
||||
* If we are going to insert an instruction page and the icache is
|
||||
* either VIPT or PIPT, there is a potential problem where the host
|
||||
@ -179,18 +177,77 @@ static inline void coherent_cache_guest_page(struct kvm_vcpu *vcpu, hva_t hva,
|
||||
*
|
||||
* VIVT caches are tagged using both the ASID and the VMID and doesn't
|
||||
* need any kind of flushing (DDI 0406C.b - Page B3-1392).
|
||||
*
|
||||
* We need to do this through a kernel mapping (using the
|
||||
* user-space mapping has proved to be the wrong
|
||||
* solution). For that, we need to kmap one page at a time,
|
||||
* and iterate over the range.
|
||||
*/
|
||||
if (icache_is_pipt()) {
|
||||
__cpuc_coherent_user_range(hva, hva + size);
|
||||
} else if (!icache_is_vivt_asid_tagged()) {
|
||||
|
||||
bool need_flush = !vcpu_has_cache_enabled(vcpu) || ipa_uncached;
|
||||
|
||||
VM_BUG_ON(size & PAGE_MASK);
|
||||
|
||||
if (!need_flush && !icache_is_pipt())
|
||||
goto vipt_cache;
|
||||
|
||||
while (size) {
|
||||
void *va = kmap_atomic_pfn(pfn);
|
||||
|
||||
if (need_flush)
|
||||
kvm_flush_dcache_to_poc(va, PAGE_SIZE);
|
||||
|
||||
if (icache_is_pipt())
|
||||
__cpuc_coherent_user_range((unsigned long)va,
|
||||
(unsigned long)va + PAGE_SIZE);
|
||||
|
||||
size -= PAGE_SIZE;
|
||||
pfn++;
|
||||
|
||||
kunmap_atomic(va);
|
||||
}
|
||||
|
||||
vipt_cache:
|
||||
if (!icache_is_pipt() && !icache_is_vivt_asid_tagged()) {
|
||||
/* any kind of VIPT cache */
|
||||
__flush_icache_all();
|
||||
}
|
||||
}
|
||||
|
||||
static inline void __kvm_flush_dcache_pte(pte_t pte)
|
||||
{
|
||||
void *va = kmap_atomic(pte_page(pte));
|
||||
|
||||
kvm_flush_dcache_to_poc(va, PAGE_SIZE);
|
||||
|
||||
kunmap_atomic(va);
|
||||
}
|
||||
|
||||
static inline void __kvm_flush_dcache_pmd(pmd_t pmd)
|
||||
{
|
||||
unsigned long size = PMD_SIZE;
|
||||
pfn_t pfn = pmd_pfn(pmd);
|
||||
|
||||
while (size) {
|
||||
void *va = kmap_atomic_pfn(pfn);
|
||||
|
||||
kvm_flush_dcache_to_poc(va, PAGE_SIZE);
|
||||
|
||||
pfn++;
|
||||
size -= PAGE_SIZE;
|
||||
|
||||
kunmap_atomic(va);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void __kvm_flush_dcache_pud(pud_t pud)
|
||||
{
|
||||
}
|
||||
|
||||
#define kvm_virt_to_phys(x) virt_to_idmap((unsigned long)(x))
|
||||
|
||||
void stage2_flush_vm(struct kvm *kvm);
|
||||
void kvm_set_way_flush(struct kvm_vcpu *vcpu);
|
||||
void kvm_toggle_cache(struct kvm_vcpu *vcpu, bool was_enabled);
|
||||
|
||||
#endif /* !__ASSEMBLY__ */
|
||||
|
||||
|
@ -22,10 +22,12 @@
|
||||
|
||||
__invalid_entry:
|
||||
v7m_exception_entry
|
||||
#ifdef CONFIG_PRINTK
|
||||
adr r0, strerr
|
||||
mrs r1, ipsr
|
||||
mov r2, lr
|
||||
bl printk
|
||||
#endif
|
||||
mov r0, sp
|
||||
bl show_regs
|
||||
1: b 1b
|
||||
|
@ -281,15 +281,6 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
|
||||
vcpu->cpu = cpu;
|
||||
vcpu->arch.host_cpu_context = this_cpu_ptr(kvm_host_cpu_state);
|
||||
|
||||
/*
|
||||
* Check whether this vcpu requires the cache to be flushed on
|
||||
* this physical CPU. This is a consequence of doing dcache
|
||||
* operations by set/way on this vcpu. We do it here to be in
|
||||
* a non-preemptible section.
|
||||
*/
|
||||
if (cpumask_test_and_clear_cpu(cpu, &vcpu->arch.require_dcache_flush))
|
||||
flush_cache_all(); /* We'd really want v7_flush_dcache_all() */
|
||||
|
||||
kvm_arm_set_running_vcpu(vcpu);
|
||||
}
|
||||
|
||||
@ -541,7 +532,6 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
|
||||
ret = kvm_call_hyp(__kvm_vcpu_run, vcpu);
|
||||
|
||||
vcpu->mode = OUTSIDE_GUEST_MODE;
|
||||
vcpu->arch.last_pcpu = smp_processor_id();
|
||||
kvm_guest_exit();
|
||||
trace_kvm_exit(*vcpu_pc(vcpu));
|
||||
/*
|
||||
|
@ -189,82 +189,40 @@ static bool access_l2ectlr(struct kvm_vcpu *vcpu,
|
||||
return true;
|
||||
}
|
||||
|
||||
/* See note at ARM ARM B1.14.4 */
|
||||
/*
|
||||
* See note at ARMv7 ARM B1.14.4 (TL;DR: S/W ops are not easily virtualized).
|
||||
*/
|
||||
static bool access_dcsw(struct kvm_vcpu *vcpu,
|
||||
const struct coproc_params *p,
|
||||
const struct coproc_reg *r)
|
||||
{
|
||||
unsigned long val;
|
||||
int cpu;
|
||||
|
||||
if (!p->is_write)
|
||||
return read_from_write_only(vcpu, p);
|
||||
|
||||
cpu = get_cpu();
|
||||
|
||||
cpumask_setall(&vcpu->arch.require_dcache_flush);
|
||||
cpumask_clear_cpu(cpu, &vcpu->arch.require_dcache_flush);
|
||||
|
||||
/* If we were already preempted, take the long way around */
|
||||
if (cpu != vcpu->arch.last_pcpu) {
|
||||
flush_cache_all();
|
||||
goto done;
|
||||
}
|
||||
|
||||
val = *vcpu_reg(vcpu, p->Rt1);
|
||||
|
||||
switch (p->CRm) {
|
||||
case 6: /* Upgrade DCISW to DCCISW, as per HCR.SWIO */
|
||||
case 14: /* DCCISW */
|
||||
asm volatile("mcr p15, 0, %0, c7, c14, 2" : : "r" (val));
|
||||
break;
|
||||
|
||||
case 10: /* DCCSW */
|
||||
asm volatile("mcr p15, 0, %0, c7, c10, 2" : : "r" (val));
|
||||
break;
|
||||
}
|
||||
|
||||
done:
|
||||
put_cpu();
|
||||
|
||||
kvm_set_way_flush(vcpu);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic accessor for VM registers. Only called as long as HCR_TVM
|
||||
* is set.
|
||||
* is set. If the guest enables the MMU, we stop trapping the VM
|
||||
* sys_regs and leave it in complete control of the caches.
|
||||
*
|
||||
* Used by the cpu-specific code.
|
||||
*/
|
||||
static bool access_vm_reg(struct kvm_vcpu *vcpu,
|
||||
const struct coproc_params *p,
|
||||
const struct coproc_reg *r)
|
||||
bool access_vm_reg(struct kvm_vcpu *vcpu,
|
||||
const struct coproc_params *p,
|
||||
const struct coproc_reg *r)
|
||||
{
|
||||
bool was_enabled = vcpu_has_cache_enabled(vcpu);
|
||||
|
||||
BUG_ON(!p->is_write);
|
||||
|
||||
vcpu->arch.cp15[r->reg] = *vcpu_reg(vcpu, p->Rt1);
|
||||
if (p->is_64bit)
|
||||
vcpu->arch.cp15[r->reg + 1] = *vcpu_reg(vcpu, p->Rt2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* SCTLR accessor. Only called as long as HCR_TVM is set. If the
|
||||
* guest enables the MMU, we stop trapping the VM sys_regs and leave
|
||||
* it in complete control of the caches.
|
||||
*
|
||||
* Used by the cpu-specific code.
|
||||
*/
|
||||
bool access_sctlr(struct kvm_vcpu *vcpu,
|
||||
const struct coproc_params *p,
|
||||
const struct coproc_reg *r)
|
||||
{
|
||||
access_vm_reg(vcpu, p, r);
|
||||
|
||||
if (vcpu_has_cache_enabled(vcpu)) { /* MMU+Caches enabled? */
|
||||
vcpu->arch.hcr &= ~HCR_TVM;
|
||||
stage2_flush_vm(vcpu->kvm);
|
||||
}
|
||||
|
||||
kvm_toggle_cache(vcpu, was_enabled);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -153,8 +153,8 @@ static inline int cmp_reg(const struct coproc_reg *i1,
|
||||
#define is64 .is_64 = true
|
||||
#define is32 .is_64 = false
|
||||
|
||||
bool access_sctlr(struct kvm_vcpu *vcpu,
|
||||
const struct coproc_params *p,
|
||||
const struct coproc_reg *r);
|
||||
bool access_vm_reg(struct kvm_vcpu *vcpu,
|
||||
const struct coproc_params *p,
|
||||
const struct coproc_reg *r);
|
||||
|
||||
#endif /* __ARM_KVM_COPROC_LOCAL_H__ */
|
||||
|
@ -34,7 +34,7 @@
|
||||
static const struct coproc_reg a15_regs[] = {
|
||||
/* SCTLR: swapped by interrupt.S. */
|
||||
{ CRn( 1), CRm( 0), Op1( 0), Op2( 0), is32,
|
||||
access_sctlr, reset_val, c1_SCTLR, 0x00C50078 },
|
||||
access_vm_reg, reset_val, c1_SCTLR, 0x00C50078 },
|
||||
};
|
||||
|
||||
static struct kvm_coproc_target_table a15_target_table = {
|
||||
|
@ -37,7 +37,7 @@
|
||||
static const struct coproc_reg a7_regs[] = {
|
||||
/* SCTLR: swapped by interrupt.S. */
|
||||
{ CRn( 1), CRm( 0), Op1( 0), Op2( 0), is32,
|
||||
access_sctlr, reset_val, c1_SCTLR, 0x00C50878 },
|
||||
access_vm_reg, reset_val, c1_SCTLR, 0x00C50878 },
|
||||
};
|
||||
|
||||
static struct kvm_coproc_target_table a7_target_table = {
|
||||
|
@ -58,6 +58,26 @@ static void kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa)
|
||||
kvm_call_hyp(__kvm_tlb_flush_vmid_ipa, kvm, ipa);
|
||||
}
|
||||
|
||||
/*
|
||||
* D-Cache management functions. They take the page table entries by
|
||||
* value, as they are flushing the cache using the kernel mapping (or
|
||||
* kmap on 32bit).
|
||||
*/
|
||||
static void kvm_flush_dcache_pte(pte_t pte)
|
||||
{
|
||||
__kvm_flush_dcache_pte(pte);
|
||||
}
|
||||
|
||||
static void kvm_flush_dcache_pmd(pmd_t pmd)
|
||||
{
|
||||
__kvm_flush_dcache_pmd(pmd);
|
||||
}
|
||||
|
||||
static void kvm_flush_dcache_pud(pud_t pud)
|
||||
{
|
||||
__kvm_flush_dcache_pud(pud);
|
||||
}
|
||||
|
||||
static int mmu_topup_memory_cache(struct kvm_mmu_memory_cache *cache,
|
||||
int min, int max)
|
||||
{
|
||||
@ -119,6 +139,26 @@ static void clear_pmd_entry(struct kvm *kvm, pmd_t *pmd, phys_addr_t addr)
|
||||
put_page(virt_to_page(pmd));
|
||||
}
|
||||
|
||||
/*
|
||||
* Unmapping vs dcache management:
|
||||
*
|
||||
* If a guest maps certain memory pages as uncached, all writes will
|
||||
* bypass the data cache and go directly to RAM. However, the CPUs
|
||||
* can still speculate reads (not writes) and fill cache lines with
|
||||
* data.
|
||||
*
|
||||
* Those cache lines will be *clean* cache lines though, so a
|
||||
* clean+invalidate operation is equivalent to an invalidate
|
||||
* operation, because no cache lines are marked dirty.
|
||||
*
|
||||
* Those clean cache lines could be filled prior to an uncached write
|
||||
* by the guest, and the cache coherent IO subsystem would therefore
|
||||
* end up writing old data to disk.
|
||||
*
|
||||
* This is why right after unmapping a page/section and invalidating
|
||||
* the corresponding TLBs, we call kvm_flush_dcache_p*() to make sure
|
||||
* the IO subsystem will never hit in the cache.
|
||||
*/
|
||||
static void unmap_ptes(struct kvm *kvm, pmd_t *pmd,
|
||||
phys_addr_t addr, phys_addr_t end)
|
||||
{
|
||||
@ -128,9 +168,16 @@ static void unmap_ptes(struct kvm *kvm, pmd_t *pmd,
|
||||
start_pte = pte = pte_offset_kernel(pmd, addr);
|
||||
do {
|
||||
if (!pte_none(*pte)) {
|
||||
pte_t old_pte = *pte;
|
||||
|
||||
kvm_set_pte(pte, __pte(0));
|
||||
put_page(virt_to_page(pte));
|
||||
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
||||
|
||||
/* No need to invalidate the cache for device mappings */
|
||||
if ((pte_val(old_pte) & PAGE_S2_DEVICE) != PAGE_S2_DEVICE)
|
||||
kvm_flush_dcache_pte(old_pte);
|
||||
|
||||
put_page(virt_to_page(pte));
|
||||
}
|
||||
} while (pte++, addr += PAGE_SIZE, addr != end);
|
||||
|
||||
@ -149,8 +196,13 @@ static void unmap_pmds(struct kvm *kvm, pud_t *pud,
|
||||
next = kvm_pmd_addr_end(addr, end);
|
||||
if (!pmd_none(*pmd)) {
|
||||
if (kvm_pmd_huge(*pmd)) {
|
||||
pmd_t old_pmd = *pmd;
|
||||
|
||||
pmd_clear(pmd);
|
||||
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
||||
|
||||
kvm_flush_dcache_pmd(old_pmd);
|
||||
|
||||
put_page(virt_to_page(pmd));
|
||||
} else {
|
||||
unmap_ptes(kvm, pmd, addr, next);
|
||||
@ -173,8 +225,13 @@ static void unmap_puds(struct kvm *kvm, pgd_t *pgd,
|
||||
next = kvm_pud_addr_end(addr, end);
|
||||
if (!pud_none(*pud)) {
|
||||
if (pud_huge(*pud)) {
|
||||
pud_t old_pud = *pud;
|
||||
|
||||
pud_clear(pud);
|
||||
kvm_tlb_flush_vmid_ipa(kvm, addr);
|
||||
|
||||
kvm_flush_dcache_pud(old_pud);
|
||||
|
||||
put_page(virt_to_page(pud));
|
||||
} else {
|
||||
unmap_pmds(kvm, pud, addr, next);
|
||||
@ -209,10 +266,9 @@ static void stage2_flush_ptes(struct kvm *kvm, pmd_t *pmd,
|
||||
|
||||
pte = pte_offset_kernel(pmd, addr);
|
||||
do {
|
||||
if (!pte_none(*pte)) {
|
||||
hva_t hva = gfn_to_hva(kvm, addr >> PAGE_SHIFT);
|
||||
kvm_flush_dcache_to_poc((void*)hva, PAGE_SIZE);
|
||||
}
|
||||
if (!pte_none(*pte) &&
|
||||
(pte_val(*pte) & PAGE_S2_DEVICE) != PAGE_S2_DEVICE)
|
||||
kvm_flush_dcache_pte(*pte);
|
||||
} while (pte++, addr += PAGE_SIZE, addr != end);
|
||||
}
|
||||
|
||||
@ -226,12 +282,10 @@ static void stage2_flush_pmds(struct kvm *kvm, pud_t *pud,
|
||||
do {
|
||||
next = kvm_pmd_addr_end(addr, end);
|
||||
if (!pmd_none(*pmd)) {
|
||||
if (kvm_pmd_huge(*pmd)) {
|
||||
hva_t hva = gfn_to_hva(kvm, addr >> PAGE_SHIFT);
|
||||
kvm_flush_dcache_to_poc((void*)hva, PMD_SIZE);
|
||||
} else {
|
||||
if (kvm_pmd_huge(*pmd))
|
||||
kvm_flush_dcache_pmd(*pmd);
|
||||
else
|
||||
stage2_flush_ptes(kvm, pmd, addr, next);
|
||||
}
|
||||
}
|
||||
} while (pmd++, addr = next, addr != end);
|
||||
}
|
||||
@ -246,12 +300,10 @@ static void stage2_flush_puds(struct kvm *kvm, pgd_t *pgd,
|
||||
do {
|
||||
next = kvm_pud_addr_end(addr, end);
|
||||
if (!pud_none(*pud)) {
|
||||
if (pud_huge(*pud)) {
|
||||
hva_t hva = gfn_to_hva(kvm, addr >> PAGE_SHIFT);
|
||||
kvm_flush_dcache_to_poc((void*)hva, PUD_SIZE);
|
||||
} else {
|
||||
if (pud_huge(*pud))
|
||||
kvm_flush_dcache_pud(*pud);
|
||||
else
|
||||
stage2_flush_pmds(kvm, pud, addr, next);
|
||||
}
|
||||
}
|
||||
} while (pud++, addr = next, addr != end);
|
||||
}
|
||||
@ -278,7 +330,7 @@ static void stage2_flush_memslot(struct kvm *kvm,
|
||||
* Go through the stage 2 page tables and invalidate any cache lines
|
||||
* backing memory already mapped to the VM.
|
||||
*/
|
||||
void stage2_flush_vm(struct kvm *kvm)
|
||||
static void stage2_flush_vm(struct kvm *kvm)
|
||||
{
|
||||
struct kvm_memslots *slots;
|
||||
struct kvm_memory_slot *memslot;
|
||||
@ -905,6 +957,12 @@ static bool kvm_is_device_pfn(unsigned long pfn)
|
||||
return !pfn_valid(pfn);
|
||||
}
|
||||
|
||||
static void coherent_cache_guest_page(struct kvm_vcpu *vcpu, pfn_t pfn,
|
||||
unsigned long size, bool uncached)
|
||||
{
|
||||
__coherent_cache_guest_page(vcpu, pfn, size, uncached);
|
||||
}
|
||||
|
||||
static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
|
||||
struct kvm_memory_slot *memslot, unsigned long hva,
|
||||
unsigned long fault_status)
|
||||
@ -994,8 +1052,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
|
||||
kvm_set_s2pmd_writable(&new_pmd);
|
||||
kvm_set_pfn_dirty(pfn);
|
||||
}
|
||||
coherent_cache_guest_page(vcpu, hva & PMD_MASK, PMD_SIZE,
|
||||
fault_ipa_uncached);
|
||||
coherent_cache_guest_page(vcpu, pfn, PMD_SIZE, fault_ipa_uncached);
|
||||
ret = stage2_set_pmd_huge(kvm, memcache, fault_ipa, &new_pmd);
|
||||
} else {
|
||||
pte_t new_pte = pfn_pte(pfn, mem_type);
|
||||
@ -1003,8 +1060,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
|
||||
kvm_set_s2pte_writable(&new_pte);
|
||||
kvm_set_pfn_dirty(pfn);
|
||||
}
|
||||
coherent_cache_guest_page(vcpu, hva, PAGE_SIZE,
|
||||
fault_ipa_uncached);
|
||||
coherent_cache_guest_page(vcpu, pfn, PAGE_SIZE, fault_ipa_uncached);
|
||||
ret = stage2_set_pte(kvm, memcache, fault_ipa, &new_pte,
|
||||
pgprot_val(mem_type) == pgprot_val(PAGE_S2_DEVICE));
|
||||
}
|
||||
@ -1411,3 +1467,71 @@ void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
|
||||
unmap_stage2_range(kvm, gpa, size);
|
||||
spin_unlock(&kvm->mmu_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* See note at ARMv7 ARM B1.14.4 (TL;DR: S/W ops are not easily virtualized).
|
||||
*
|
||||
* Main problems:
|
||||
* - S/W ops are local to a CPU (not broadcast)
|
||||
* - We have line migration behind our back (speculation)
|
||||
* - System caches don't support S/W at all (damn!)
|
||||
*
|
||||
* In the face of the above, the best we can do is to try and convert
|
||||
* S/W ops to VA ops. Because the guest is not allowed to infer the
|
||||
* S/W to PA mapping, it can only use S/W to nuke the whole cache,
|
||||
* which is a rather good thing for us.
|
||||
*
|
||||
* Also, it is only used when turning caches on/off ("The expected
|
||||
* usage of the cache maintenance instructions that operate by set/way
|
||||
* is associated with the cache maintenance instructions associated
|
||||
* with the powerdown and powerup of caches, if this is required by
|
||||
* the implementation.").
|
||||
*
|
||||
* We use the following policy:
|
||||
*
|
||||
* - If we trap a S/W operation, we enable VM trapping to detect
|
||||
* caches being turned on/off, and do a full clean.
|
||||
*
|
||||
* - We flush the caches on both caches being turned on and off.
|
||||
*
|
||||
* - Once the caches are enabled, we stop trapping VM ops.
|
||||
*/
|
||||
void kvm_set_way_flush(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
unsigned long hcr = vcpu_get_hcr(vcpu);
|
||||
|
||||
/*
|
||||
* If this is the first time we do a S/W operation
|
||||
* (i.e. HCR_TVM not set) flush the whole memory, and set the
|
||||
* VM trapping.
|
||||
*
|
||||
* Otherwise, rely on the VM trapping to wait for the MMU +
|
||||
* Caches to be turned off. At that point, we'll be able to
|
||||
* clean the caches again.
|
||||
*/
|
||||
if (!(hcr & HCR_TVM)) {
|
||||
trace_kvm_set_way_flush(*vcpu_pc(vcpu),
|
||||
vcpu_has_cache_enabled(vcpu));
|
||||
stage2_flush_vm(vcpu->kvm);
|
||||
vcpu_set_hcr(vcpu, hcr | HCR_TVM);
|
||||
}
|
||||
}
|
||||
|
||||
void kvm_toggle_cache(struct kvm_vcpu *vcpu, bool was_enabled)
|
||||
{
|
||||
bool now_enabled = vcpu_has_cache_enabled(vcpu);
|
||||
|
||||
/*
|
||||
* If switching the MMU+caches on, need to invalidate the caches.
|
||||
* If switching it off, need to clean the caches.
|
||||
* Clean + invalidate does the trick always.
|
||||
*/
|
||||
if (now_enabled != was_enabled)
|
||||
stage2_flush_vm(vcpu->kvm);
|
||||
|
||||
/* Caches are now on, stop trapping VM ops (until a S/W op) */
|
||||
if (now_enabled)
|
||||
vcpu_set_hcr(vcpu, vcpu_get_hcr(vcpu) & ~HCR_TVM);
|
||||
|
||||
trace_kvm_toggle_cache(*vcpu_pc(vcpu), was_enabled, now_enabled);
|
||||
}
|
||||
|
@ -223,6 +223,45 @@ TRACE_EVENT(kvm_hvc,
|
||||
__entry->vcpu_pc, __entry->r0, __entry->imm)
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_set_way_flush,
|
||||
TP_PROTO(unsigned long vcpu_pc, bool cache),
|
||||
TP_ARGS(vcpu_pc, cache),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( unsigned long, vcpu_pc )
|
||||
__field( bool, cache )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->vcpu_pc = vcpu_pc;
|
||||
__entry->cache = cache;
|
||||
),
|
||||
|
||||
TP_printk("S/W flush at 0x%016lx (cache %s)",
|
||||
__entry->vcpu_pc, __entry->cache ? "on" : "off")
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_toggle_cache,
|
||||
TP_PROTO(unsigned long vcpu_pc, bool was, bool now),
|
||||
TP_ARGS(vcpu_pc, was, now),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( unsigned long, vcpu_pc )
|
||||
__field( bool, was )
|
||||
__field( bool, now )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->vcpu_pc = vcpu_pc;
|
||||
__entry->was = was;
|
||||
__entry->now = now;
|
||||
),
|
||||
|
||||
TP_printk("VM op at 0x%016lx (cache was %s, now %s)",
|
||||
__entry->vcpu_pc, __entry->was ? "on" : "off",
|
||||
__entry->now ? "on" : "off")
|
||||
);
|
||||
|
||||
#endif /* _TRACE_KVM_H */
|
||||
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
|
@ -189,6 +189,13 @@ static void __init armada_375_380_coherency_init(struct device_node *np)
|
||||
coherency_cpu_base = of_iomap(np, 0);
|
||||
arch_ioremap_caller = armada_pcie_wa_ioremap_caller;
|
||||
|
||||
/*
|
||||
* We should switch the PL310 to I/O coherency mode only if
|
||||
* I/O coherency is actually enabled.
|
||||
*/
|
||||
if (!coherency_available())
|
||||
return;
|
||||
|
||||
/*
|
||||
* Add the PL310 property "arm,io-coherent". This makes sure the
|
||||
* outer sync operation is not used, which allows to
|
||||
|
@ -18,6 +18,8 @@
|
||||
#include <linux/gpio_keys.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/irqchip/arm-gic.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/tmio.h>
|
||||
#include <linux/mmc/host.h>
|
||||
@ -273,6 +275,22 @@ static void __init ape6evm_add_standard_devices(void)
|
||||
sizeof(ape6evm_leds_pdata));
|
||||
}
|
||||
|
||||
static void __init ape6evm_legacy_init_time(void)
|
||||
{
|
||||
/* Do not invoke DT-based timers via clocksource_of_init() */
|
||||
}
|
||||
|
||||
static void __init ape6evm_legacy_init_irq(void)
|
||||
{
|
||||
void __iomem *gic_dist_base = ioremap_nocache(0xf1001000, 0x1000);
|
||||
void __iomem *gic_cpu_base = ioremap_nocache(0xf1002000, 0x1000);
|
||||
|
||||
gic_init(0, 29, gic_dist_base, gic_cpu_base);
|
||||
|
||||
/* Do not invoke DT-based interrupt code via irqchip_init() */
|
||||
}
|
||||
|
||||
|
||||
static const char *ape6evm_boards_compat_dt[] __initdata = {
|
||||
"renesas,ape6evm",
|
||||
NULL,
|
||||
@ -280,7 +298,9 @@ static const char *ape6evm_boards_compat_dt[] __initdata = {
|
||||
|
||||
DT_MACHINE_START(APE6EVM_DT, "ape6evm")
|
||||
.init_early = shmobile_init_delay,
|
||||
.init_irq = ape6evm_legacy_init_irq,
|
||||
.init_machine = ape6evm_add_standard_devices,
|
||||
.init_late = shmobile_init_late,
|
||||
.dt_compat = ape6evm_boards_compat_dt,
|
||||
.init_time = ape6evm_legacy_init_time,
|
||||
MACHINE_END
|
||||
|
@ -21,6 +21,8 @@
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/irqchip/arm-gic.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/mfd/tmio.h>
|
||||
@ -811,6 +813,16 @@ static void __init lager_init(void)
|
||||
lager_ksz8041_fixup);
|
||||
}
|
||||
|
||||
static void __init lager_legacy_init_irq(void)
|
||||
{
|
||||
void __iomem *gic_dist_base = ioremap_nocache(0xf1001000, 0x1000);
|
||||
void __iomem *gic_cpu_base = ioremap_nocache(0xf1002000, 0x1000);
|
||||
|
||||
gic_init(0, 29, gic_dist_base, gic_cpu_base);
|
||||
|
||||
/* Do not invoke DT-based interrupt code via irqchip_init() */
|
||||
}
|
||||
|
||||
static const char * const lager_boards_compat_dt[] __initconst = {
|
||||
"renesas,lager",
|
||||
NULL,
|
||||
@ -819,6 +831,7 @@ static const char * const lager_boards_compat_dt[] __initconst = {
|
||||
DT_MACHINE_START(LAGER_DT, "lager")
|
||||
.smp = smp_ops(r8a7790_smp_ops),
|
||||
.init_early = shmobile_init_delay,
|
||||
.init_irq = lager_legacy_init_irq,
|
||||
.init_time = rcar_gen2_timer_init,
|
||||
.init_machine = lager_init,
|
||||
.init_late = shmobile_init_late,
|
||||
|
@ -133,7 +133,9 @@ void __init rcar_gen2_timer_init(void)
|
||||
#ifdef CONFIG_COMMON_CLK
|
||||
rcar_gen2_clocks_init(mode);
|
||||
#endif
|
||||
#ifdef CONFIG_ARCH_SHMOBILE_MULTI
|
||||
clocksource_of_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
struct memory_reserve_config {
|
||||
|
@ -70,6 +70,18 @@ void __init shmobile_init_delay(void)
|
||||
if (!max_freq)
|
||||
return;
|
||||
|
||||
#ifdef CONFIG_ARCH_SHMOBILE_LEGACY
|
||||
/* Non-multiplatform r8a73a4 SoC cannot use arch timer due
|
||||
* to GIC being initialized from C and arch timer via DT */
|
||||
if (of_machine_is_compatible("renesas,r8a73a4"))
|
||||
has_arch_timer = false;
|
||||
|
||||
/* Non-multiplatform r8a7790 SoC cannot use arch timer due
|
||||
* to GIC being initialized from C and arch timer via DT */
|
||||
if (of_machine_is_compatible("renesas,r8a7790"))
|
||||
has_arch_timer = false;
|
||||
#endif
|
||||
|
||||
if (!has_arch_timer || !IS_ENABLED(CONFIG_ARM_ARCH_TIMER)) {
|
||||
if (is_a7_a8_a9)
|
||||
shmobile_setup_delay_hz(max_freq, 1, 3);
|
||||
|
@ -1012,6 +1012,7 @@ config ARCH_SUPPORTS_BIG_ENDIAN
|
||||
|
||||
config ARM_KERNMEM_PERMS
|
||||
bool "Restrict kernel memory permissions"
|
||||
depends on MMU
|
||||
help
|
||||
If this is set, kernel memory other than kernel text (and rodata)
|
||||
will be made non-executable. The tradeoff is that each region is
|
||||
|
@ -144,21 +144,17 @@ static void flush_context(unsigned int cpu)
|
||||
/* Update the list of reserved ASIDs and the ASID bitmap. */
|
||||
bitmap_clear(asid_map, 0, NUM_USER_ASIDS);
|
||||
for_each_possible_cpu(i) {
|
||||
if (i == cpu) {
|
||||
asid = 0;
|
||||
} else {
|
||||
asid = atomic64_xchg(&per_cpu(active_asids, i), 0);
|
||||
/*
|
||||
* If this CPU has already been through a
|
||||
* rollover, but hasn't run another task in
|
||||
* the meantime, we must preserve its reserved
|
||||
* ASID, as this is the only trace we have of
|
||||
* the process it is still running.
|
||||
*/
|
||||
if (asid == 0)
|
||||
asid = per_cpu(reserved_asids, i);
|
||||
__set_bit(asid & ~ASID_MASK, asid_map);
|
||||
}
|
||||
asid = atomic64_xchg(&per_cpu(active_asids, i), 0);
|
||||
/*
|
||||
* If this CPU has already been through a
|
||||
* rollover, but hasn't run another task in
|
||||
* the meantime, we must preserve its reserved
|
||||
* ASID, as this is the only trace we have of
|
||||
* the process it is still running.
|
||||
*/
|
||||
if (asid == 0)
|
||||
asid = per_cpu(reserved_asids, i);
|
||||
__set_bit(asid & ~ASID_MASK, asid_map);
|
||||
per_cpu(reserved_asids, i) = asid;
|
||||
}
|
||||
|
||||
|
@ -1940,18 +1940,8 @@ void arm_iommu_release_mapping(struct dma_iommu_mapping *mapping)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(arm_iommu_release_mapping);
|
||||
|
||||
/**
|
||||
* arm_iommu_attach_device
|
||||
* @dev: valid struct device pointer
|
||||
* @mapping: io address space mapping structure (returned from
|
||||
* arm_iommu_create_mapping)
|
||||
*
|
||||
* Attaches specified io address space mapping to the provided device,
|
||||
* More than one client might be attached to the same io address space
|
||||
* mapping.
|
||||
*/
|
||||
int arm_iommu_attach_device(struct device *dev,
|
||||
struct dma_iommu_mapping *mapping)
|
||||
static int __arm_iommu_attach_device(struct device *dev,
|
||||
struct dma_iommu_mapping *mapping)
|
||||
{
|
||||
int err;
|
||||
|
||||
@ -1965,15 +1955,35 @@ int arm_iommu_attach_device(struct device *dev,
|
||||
pr_debug("Attached IOMMU controller to %s device.\n", dev_name(dev));
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(arm_iommu_attach_device);
|
||||
|
||||
/**
|
||||
* arm_iommu_detach_device
|
||||
* arm_iommu_attach_device
|
||||
* @dev: valid struct device pointer
|
||||
* @mapping: io address space mapping structure (returned from
|
||||
* arm_iommu_create_mapping)
|
||||
*
|
||||
* Detaches the provided device from a previously attached map.
|
||||
* Attaches specified io address space mapping to the provided device.
|
||||
* This replaces the dma operations (dma_map_ops pointer) with the
|
||||
* IOMMU aware version.
|
||||
*
|
||||
* More than one client might be attached to the same io address space
|
||||
* mapping.
|
||||
*/
|
||||
void arm_iommu_detach_device(struct device *dev)
|
||||
int arm_iommu_attach_device(struct device *dev,
|
||||
struct dma_iommu_mapping *mapping)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = __arm_iommu_attach_device(dev, mapping);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
set_dma_ops(dev, &iommu_ops);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(arm_iommu_attach_device);
|
||||
|
||||
static void __arm_iommu_detach_device(struct device *dev)
|
||||
{
|
||||
struct dma_iommu_mapping *mapping;
|
||||
|
||||
@ -1989,6 +1999,19 @@ void arm_iommu_detach_device(struct device *dev)
|
||||
|
||||
pr_debug("Detached IOMMU controller from %s device.\n", dev_name(dev));
|
||||
}
|
||||
|
||||
/**
|
||||
* arm_iommu_detach_device
|
||||
* @dev: valid struct device pointer
|
||||
*
|
||||
* Detaches the provided device from a previously attached map.
|
||||
* This voids the dma operations (dma_map_ops pointer)
|
||||
*/
|
||||
void arm_iommu_detach_device(struct device *dev)
|
||||
{
|
||||
__arm_iommu_detach_device(dev);
|
||||
set_dma_ops(dev, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(arm_iommu_detach_device);
|
||||
|
||||
static struct dma_map_ops *arm_get_iommu_dma_map_ops(bool coherent)
|
||||
@ -2011,7 +2034,7 @@ static bool arm_setup_iommu_dma_ops(struct device *dev, u64 dma_base, u64 size,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (arm_iommu_attach_device(dev, mapping)) {
|
||||
if (__arm_iommu_attach_device(dev, mapping)) {
|
||||
pr_warn("Failed to attached device %s to IOMMU_mapping\n",
|
||||
dev_name(dev));
|
||||
arm_iommu_release_mapping(mapping);
|
||||
@ -2025,7 +2048,10 @@ static void arm_teardown_iommu_dma_ops(struct device *dev)
|
||||
{
|
||||
struct dma_iommu_mapping *mapping = dev->archdata.mapping;
|
||||
|
||||
arm_iommu_detach_device(dev);
|
||||
if (!mapping)
|
||||
return;
|
||||
|
||||
__arm_iommu_detach_device(dev);
|
||||
arm_iommu_release_mapping(mapping);
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,16 @@ static inline void vcpu_reset_hcr(struct kvm_vcpu *vcpu)
|
||||
vcpu->arch.hcr_el2 &= ~HCR_RW;
|
||||
}
|
||||
|
||||
static inline unsigned long vcpu_get_hcr(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return vcpu->arch.hcr_el2;
|
||||
}
|
||||
|
||||
static inline void vcpu_set_hcr(struct kvm_vcpu *vcpu, unsigned long hcr)
|
||||
{
|
||||
vcpu->arch.hcr_el2 = hcr;
|
||||
}
|
||||
|
||||
static inline unsigned long *vcpu_pc(const struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return (unsigned long *)&vcpu_gp_regs(vcpu)->regs.pc;
|
||||
|
@ -116,9 +116,6 @@ struct kvm_vcpu_arch {
|
||||
* Anything that is not used directly from assembly code goes
|
||||
* here.
|
||||
*/
|
||||
/* dcache set/way operation pending */
|
||||
int last_pcpu;
|
||||
cpumask_t require_dcache_flush;
|
||||
|
||||
/* Don't run the guest */
|
||||
bool pause;
|
||||
|
@ -243,24 +243,46 @@ static inline bool vcpu_has_cache_enabled(struct kvm_vcpu *vcpu)
|
||||
return (vcpu_sys_reg(vcpu, SCTLR_EL1) & 0b101) == 0b101;
|
||||
}
|
||||
|
||||
static inline void coherent_cache_guest_page(struct kvm_vcpu *vcpu, hva_t hva,
|
||||
unsigned long size,
|
||||
bool ipa_uncached)
|
||||
static inline void __coherent_cache_guest_page(struct kvm_vcpu *vcpu, pfn_t pfn,
|
||||
unsigned long size,
|
||||
bool ipa_uncached)
|
||||
{
|
||||
void *va = page_address(pfn_to_page(pfn));
|
||||
|
||||
if (!vcpu_has_cache_enabled(vcpu) || ipa_uncached)
|
||||
kvm_flush_dcache_to_poc((void *)hva, size);
|
||||
kvm_flush_dcache_to_poc(va, size);
|
||||
|
||||
if (!icache_is_aliasing()) { /* PIPT */
|
||||
flush_icache_range(hva, hva + size);
|
||||
flush_icache_range((unsigned long)va,
|
||||
(unsigned long)va + size);
|
||||
} else if (!icache_is_aivivt()) { /* non ASID-tagged VIVT */
|
||||
/* any kind of VIPT cache */
|
||||
__flush_icache_all();
|
||||
}
|
||||
}
|
||||
|
||||
static inline void __kvm_flush_dcache_pte(pte_t pte)
|
||||
{
|
||||
struct page *page = pte_page(pte);
|
||||
kvm_flush_dcache_to_poc(page_address(page), PAGE_SIZE);
|
||||
}
|
||||
|
||||
static inline void __kvm_flush_dcache_pmd(pmd_t pmd)
|
||||
{
|
||||
struct page *page = pmd_page(pmd);
|
||||
kvm_flush_dcache_to_poc(page_address(page), PMD_SIZE);
|
||||
}
|
||||
|
||||
static inline void __kvm_flush_dcache_pud(pud_t pud)
|
||||
{
|
||||
struct page *page = pud_page(pud);
|
||||
kvm_flush_dcache_to_poc(page_address(page), PUD_SIZE);
|
||||
}
|
||||
|
||||
#define kvm_virt_to_phys(x) __virt_to_phys((unsigned long)(x))
|
||||
|
||||
void stage2_flush_vm(struct kvm *kvm);
|
||||
void kvm_set_way_flush(struct kvm_vcpu *vcpu);
|
||||
void kvm_toggle_cache(struct kvm_vcpu *vcpu, bool was_enabled);
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
#endif /* __ARM64_KVM_MMU_H__ */
|
||||
|
@ -69,68 +69,31 @@ static u32 get_ccsidr(u32 csselr)
|
||||
return ccsidr;
|
||||
}
|
||||
|
||||
static void do_dc_cisw(u32 val)
|
||||
{
|
||||
asm volatile("dc cisw, %x0" : : "r" (val));
|
||||
dsb(ish);
|
||||
}
|
||||
|
||||
static void do_dc_csw(u32 val)
|
||||
{
|
||||
asm volatile("dc csw, %x0" : : "r" (val));
|
||||
dsb(ish);
|
||||
}
|
||||
|
||||
/* See note at ARM ARM B1.14.4 */
|
||||
/*
|
||||
* See note at ARMv7 ARM B1.14.4 (TL;DR: S/W ops are not easily virtualized).
|
||||
*/
|
||||
static bool access_dcsw(struct kvm_vcpu *vcpu,
|
||||
const struct sys_reg_params *p,
|
||||
const struct sys_reg_desc *r)
|
||||
{
|
||||
unsigned long val;
|
||||
int cpu;
|
||||
|
||||
if (!p->is_write)
|
||||
return read_from_write_only(vcpu, p);
|
||||
|
||||
cpu = get_cpu();
|
||||
|
||||
cpumask_setall(&vcpu->arch.require_dcache_flush);
|
||||
cpumask_clear_cpu(cpu, &vcpu->arch.require_dcache_flush);
|
||||
|
||||
/* If we were already preempted, take the long way around */
|
||||
if (cpu != vcpu->arch.last_pcpu) {
|
||||
flush_cache_all();
|
||||
goto done;
|
||||
}
|
||||
|
||||
val = *vcpu_reg(vcpu, p->Rt);
|
||||
|
||||
switch (p->CRm) {
|
||||
case 6: /* Upgrade DCISW to DCCISW, as per HCR.SWIO */
|
||||
case 14: /* DCCISW */
|
||||
do_dc_cisw(val);
|
||||
break;
|
||||
|
||||
case 10: /* DCCSW */
|
||||
do_dc_csw(val);
|
||||
break;
|
||||
}
|
||||
|
||||
done:
|
||||
put_cpu();
|
||||
|
||||
kvm_set_way_flush(vcpu);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic accessor for VM registers. Only called as long as HCR_TVM
|
||||
* is set.
|
||||
* is set. If the guest enables the MMU, we stop trapping the VM
|
||||
* sys_regs and leave it in complete control of the caches.
|
||||
*/
|
||||
static bool access_vm_reg(struct kvm_vcpu *vcpu,
|
||||
const struct sys_reg_params *p,
|
||||
const struct sys_reg_desc *r)
|
||||
{
|
||||
unsigned long val;
|
||||
bool was_enabled = vcpu_has_cache_enabled(vcpu);
|
||||
|
||||
BUG_ON(!p->is_write);
|
||||
|
||||
@ -143,25 +106,7 @@ static bool access_vm_reg(struct kvm_vcpu *vcpu,
|
||||
vcpu_cp15_64_low(vcpu, r->reg) = val & 0xffffffffUL;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* SCTLR_EL1 accessor. Only called as long as HCR_TVM is set. If the
|
||||
* guest enables the MMU, we stop trapping the VM sys_regs and leave
|
||||
* it in complete control of the caches.
|
||||
*/
|
||||
static bool access_sctlr(struct kvm_vcpu *vcpu,
|
||||
const struct sys_reg_params *p,
|
||||
const struct sys_reg_desc *r)
|
||||
{
|
||||
access_vm_reg(vcpu, p, r);
|
||||
|
||||
if (vcpu_has_cache_enabled(vcpu)) { /* MMU+Caches enabled? */
|
||||
vcpu->arch.hcr_el2 &= ~HCR_TVM;
|
||||
stage2_flush_vm(vcpu->kvm);
|
||||
}
|
||||
|
||||
kvm_toggle_cache(vcpu, was_enabled);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -377,7 +322,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {
|
||||
NULL, reset_mpidr, MPIDR_EL1 },
|
||||
/* SCTLR_EL1 */
|
||||
{ Op0(0b11), Op1(0b000), CRn(0b0001), CRm(0b0000), Op2(0b000),
|
||||
access_sctlr, reset_val, SCTLR_EL1, 0x00C50078 },
|
||||
access_vm_reg, reset_val, SCTLR_EL1, 0x00C50078 },
|
||||
/* CPACR_EL1 */
|
||||
{ Op0(0b11), Op1(0b000), CRn(0b0001), CRm(0b0000), Op2(0b010),
|
||||
NULL, reset_val, CPACR_EL1, 0 },
|
||||
@ -657,7 +602,7 @@ static const struct sys_reg_desc cp14_64_regs[] = {
|
||||
* register).
|
||||
*/
|
||||
static const struct sys_reg_desc cp15_regs[] = {
|
||||
{ Op1( 0), CRn( 1), CRm( 0), Op2( 0), access_sctlr, NULL, c1_SCTLR },
|
||||
{ Op1( 0), CRn( 1), CRm( 0), Op2( 0), access_vm_reg, NULL, c1_SCTLR },
|
||||
{ Op1( 0), CRn( 2), CRm( 0), Op2( 0), access_vm_reg, NULL, c2_TTBR0 },
|
||||
{ Op1( 0), CRn( 2), CRm( 0), Op2( 1), access_vm_reg, NULL, c2_TTBR1 },
|
||||
{ Op1( 0), CRn( 2), CRm( 0), Op2( 2), access_vm_reg, NULL, c2_TTBCR },
|
||||
|
@ -142,6 +142,8 @@ good_area:
|
||||
if (unlikely(fault & VM_FAULT_ERROR)) {
|
||||
if (fault & VM_FAULT_OOM)
|
||||
goto out_of_memory;
|
||||
else if (fault & VM_FAULT_SIGSEGV)
|
||||
goto bad_area;
|
||||
else if (fault & VM_FAULT_SIGBUS)
|
||||
goto do_sigbus;
|
||||
BUG();
|
||||
|
@ -176,6 +176,8 @@ retry:
|
||||
if (unlikely(fault & VM_FAULT_ERROR)) {
|
||||
if (fault & VM_FAULT_OOM)
|
||||
goto out_of_memory;
|
||||
else if (fault & VM_FAULT_SIGSEGV)
|
||||
goto bad_area;
|
||||
else if (fault & VM_FAULT_SIGBUS)
|
||||
goto do_sigbus;
|
||||
BUG();
|
||||
|
@ -168,6 +168,8 @@ asmlinkage void do_page_fault(int datammu, unsigned long esr0, unsigned long ear
|
||||
if (unlikely(fault & VM_FAULT_ERROR)) {
|
||||
if (fault & VM_FAULT_OOM)
|
||||
goto out_of_memory;
|
||||
else if (fault & VM_FAULT_SIGSEGV)
|
||||
goto bad_area;
|
||||
else if (fault & VM_FAULT_SIGBUS)
|
||||
goto do_sigbus;
|
||||
BUG();
|
||||
|
@ -172,6 +172,8 @@ retry:
|
||||
*/
|
||||
if (fault & VM_FAULT_OOM) {
|
||||
goto out_of_memory;
|
||||
} else if (fault & VM_FAULT_SIGSEGV) {
|
||||
goto bad_area;
|
||||
} else if (fault & VM_FAULT_SIGBUS) {
|
||||
signal = SIGBUS;
|
||||
goto bad_area;
|
||||
|
@ -200,6 +200,8 @@ good_area:
|
||||
if (unlikely(fault & VM_FAULT_ERROR)) {
|
||||
if (fault & VM_FAULT_OOM)
|
||||
goto out_of_memory;
|
||||
else if (fault & VM_FAULT_SIGSEGV)
|
||||
goto bad_area;
|
||||
else if (fault & VM_FAULT_SIGBUS)
|
||||
goto do_sigbus;
|
||||
BUG();
|
||||
|
@ -145,6 +145,8 @@ good_area:
|
||||
if (unlikely(fault & VM_FAULT_ERROR)) {
|
||||
if (fault & VM_FAULT_OOM)
|
||||
goto out_of_memory;
|
||||
else if (fault & VM_FAULT_SIGSEGV)
|
||||
goto map_err;
|
||||
else if (fault & VM_FAULT_SIGBUS)
|
||||
goto bus_err;
|
||||
BUG();
|
||||
|
@ -141,6 +141,8 @@ good_area:
|
||||
if (unlikely(fault & VM_FAULT_ERROR)) {
|
||||
if (fault & VM_FAULT_OOM)
|
||||
goto out_of_memory;
|
||||
else if (fault & VM_FAULT_SIGSEGV)
|
||||
goto bad_area;
|
||||
else if (fault & VM_FAULT_SIGBUS)
|
||||
goto do_sigbus;
|
||||
BUG();
|
||||
|
@ -224,6 +224,8 @@ good_area:
|
||||
if (unlikely(fault & VM_FAULT_ERROR)) {
|
||||
if (fault & VM_FAULT_OOM)
|
||||
goto out_of_memory;
|
||||
else if (fault & VM_FAULT_SIGSEGV)
|
||||
goto bad_area;
|
||||
else if (fault & VM_FAULT_SIGBUS)
|
||||
goto do_sigbus;
|
||||
BUG();
|
||||
|
@ -158,6 +158,8 @@ good_area:
|
||||
if (unlikely(fault & VM_FAULT_ERROR)) {
|
||||
if (fault & VM_FAULT_OOM)
|
||||
goto out_of_memory;
|
||||
else if (fault & VM_FAULT_SIGSEGV)
|
||||
goto bad_area;
|
||||
else if (fault & VM_FAULT_SIGBUS)
|
||||
goto do_sigbus;
|
||||
BUG();
|
||||
|
@ -262,6 +262,8 @@ good_area:
|
||||
if (unlikely(fault & VM_FAULT_ERROR)) {
|
||||
if (fault & VM_FAULT_OOM)
|
||||
goto out_of_memory;
|
||||
else if (fault & VM_FAULT_SIGSEGV)
|
||||
goto bad_area;
|
||||
else if (fault & VM_FAULT_SIGBUS)
|
||||
goto do_sigbus;
|
||||
BUG();
|
||||
|
@ -135,6 +135,8 @@ survive:
|
||||
if (unlikely(fault & VM_FAULT_ERROR)) {
|
||||
if (fault & VM_FAULT_OOM)
|
||||
goto out_of_memory;
|
||||
else if (fault & VM_FAULT_SIGSEGV)
|
||||
goto bad_area;
|
||||
else if (fault & VM_FAULT_SIGBUS)
|
||||
goto do_sigbus;
|
||||
BUG();
|
||||
|
@ -171,6 +171,8 @@ good_area:
|
||||
if (unlikely(fault & VM_FAULT_ERROR)) {
|
||||
if (fault & VM_FAULT_OOM)
|
||||
goto out_of_memory;
|
||||
else if (fault & VM_FAULT_SIGSEGV)
|
||||
goto bad_area;
|
||||
else if (fault & VM_FAULT_SIGBUS)
|
||||
goto do_sigbus;
|
||||
BUG();
|
||||
|
@ -256,6 +256,8 @@ good_area:
|
||||
*/
|
||||
if (fault & VM_FAULT_OOM)
|
||||
goto out_of_memory;
|
||||
else if (fault & VM_FAULT_SIGSEGV)
|
||||
goto bad_area;
|
||||
else if (fault & VM_FAULT_SIGBUS)
|
||||
goto bad_area;
|
||||
BUG();
|
||||
|
@ -76,7 +76,7 @@ int copro_handle_mm_fault(struct mm_struct *mm, unsigned long ea,
|
||||
if (*flt & VM_FAULT_OOM) {
|
||||
ret = -ENOMEM;
|
||||
goto out_unlock;
|
||||
} else if (*flt & VM_FAULT_SIGBUS) {
|
||||
} else if (*flt & (VM_FAULT_SIGBUS | VM_FAULT_SIGSEGV)) {
|
||||
ret = -EFAULT;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
@ -437,6 +437,8 @@ good_area:
|
||||
*/
|
||||
fault = handle_mm_fault(mm, vma, address, flags);
|
||||
if (unlikely(fault & (VM_FAULT_RETRY|VM_FAULT_ERROR))) {
|
||||
if (fault & VM_FAULT_SIGSEGV)
|
||||
goto bad_area;
|
||||
rc = mm_fault_error(regs, address, fault);
|
||||
if (rc >= MM_FAULT_RETURN)
|
||||
goto bail;
|
||||
|
@ -374,6 +374,12 @@ static noinline void do_fault_error(struct pt_regs *regs, int fault)
|
||||
do_no_context(regs);
|
||||
else
|
||||
pagefault_out_of_memory();
|
||||
} else if (fault & VM_FAULT_SIGSEGV) {
|
||||
/* Kernel mode? Handle exceptions or die */
|
||||
if (!user_mode(regs))
|
||||
do_no_context(regs);
|
||||
else
|
||||
do_sigsegv(regs, SEGV_MAPERR);
|
||||
} else if (fault & VM_FAULT_SIGBUS) {
|
||||
/* Kernel mode? Handle exceptions or die */
|
||||
if (!user_mode(regs))
|
||||
|
@ -114,6 +114,8 @@ good_area:
|
||||
if (unlikely(fault & VM_FAULT_ERROR)) {
|
||||
if (fault & VM_FAULT_OOM)
|
||||
goto out_of_memory;
|
||||
else if (fault & VM_FAULT_SIGSEGV)
|
||||
goto bad_area;
|
||||
else if (fault & VM_FAULT_SIGBUS)
|
||||
goto do_sigbus;
|
||||
BUG();
|
||||
|
@ -353,6 +353,8 @@ mm_fault_error(struct pt_regs *regs, unsigned long error_code,
|
||||
} else {
|
||||
if (fault & VM_FAULT_SIGBUS)
|
||||
do_sigbus(regs, error_code, address);
|
||||
else if (fault & VM_FAULT_SIGSEGV)
|
||||
bad_area(regs, error_code, address);
|
||||
else
|
||||
BUG();
|
||||
}
|
||||
|
@ -249,6 +249,8 @@ good_area:
|
||||
if (unlikely(fault & VM_FAULT_ERROR)) {
|
||||
if (fault & VM_FAULT_OOM)
|
||||
goto out_of_memory;
|
||||
else if (fault & VM_FAULT_SIGSEGV)
|
||||
goto bad_area;
|
||||
else if (fault & VM_FAULT_SIGBUS)
|
||||
goto do_sigbus;
|
||||
BUG();
|
||||
|
@ -446,6 +446,8 @@ good_area:
|
||||
if (unlikely(fault & VM_FAULT_ERROR)) {
|
||||
if (fault & VM_FAULT_OOM)
|
||||
goto out_of_memory;
|
||||
else if (fault & VM_FAULT_SIGSEGV)
|
||||
goto bad_area;
|
||||
else if (fault & VM_FAULT_SIGBUS)
|
||||
goto do_sigbus;
|
||||
BUG();
|
||||
|
@ -442,6 +442,8 @@ good_area:
|
||||
if (unlikely(fault & VM_FAULT_ERROR)) {
|
||||
if (fault & VM_FAULT_OOM)
|
||||
goto out_of_memory;
|
||||
else if (fault & VM_FAULT_SIGSEGV)
|
||||
goto bad_area;
|
||||
else if (fault & VM_FAULT_SIGBUS)
|
||||
goto do_sigbus;
|
||||
BUG();
|
||||
|
@ -80,6 +80,8 @@ good_area:
|
||||
if (unlikely(fault & VM_FAULT_ERROR)) {
|
||||
if (fault & VM_FAULT_OOM) {
|
||||
goto out_of_memory;
|
||||
} else if (fault & VM_FAULT_SIGSEGV) {
|
||||
goto out;
|
||||
} else if (fault & VM_FAULT_SIGBUS) {
|
||||
err = -EACCES;
|
||||
goto out;
|
||||
|
@ -2431,6 +2431,7 @@ __init int intel_pmu_init(void)
|
||||
break;
|
||||
|
||||
case 55: /* 22nm Atom "Silvermont" */
|
||||
case 76: /* 14nm Atom "Airmont" */
|
||||
case 77: /* 22nm Atom "Silvermont Avoton/Rangely" */
|
||||
memcpy(hw_cache_event_ids, slm_hw_cache_event_ids,
|
||||
sizeof(hw_cache_event_ids));
|
||||
|
@ -142,7 +142,7 @@ static inline u64 rapl_scale(u64 v)
|
||||
* or use ldexp(count, -32).
|
||||
* Watts = Joules/Time delta
|
||||
*/
|
||||
return v << (32 - __this_cpu_read(rapl_pmu->hw_unit));
|
||||
return v << (32 - __this_cpu_read(rapl_pmu)->hw_unit);
|
||||
}
|
||||
|
||||
static u64 rapl_event_update(struct perf_event *event)
|
||||
|
@ -840,7 +840,6 @@ static int uncore_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id
|
||||
box->phys_id = phys_id;
|
||||
box->pci_dev = pdev;
|
||||
box->pmu = pmu;
|
||||
uncore_box_init(box);
|
||||
pci_set_drvdata(pdev, box);
|
||||
|
||||
raw_spin_lock(&uncore_box_lock);
|
||||
@ -1004,10 +1003,8 @@ static int uncore_cpu_starting(int cpu)
|
||||
pmu = &type->pmus[j];
|
||||
box = *per_cpu_ptr(pmu->box, cpu);
|
||||
/* called by uncore_cpu_init? */
|
||||
if (box && box->phys_id >= 0) {
|
||||
uncore_box_init(box);
|
||||
if (box && box->phys_id >= 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
for_each_online_cpu(k) {
|
||||
exist = *per_cpu_ptr(pmu->box, k);
|
||||
@ -1023,10 +1020,8 @@ static int uncore_cpu_starting(int cpu)
|
||||
}
|
||||
}
|
||||
|
||||
if (box) {
|
||||
if (box)
|
||||
box->phys_id = phys_id;
|
||||
uncore_box_init(box);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
@ -257,6 +257,14 @@ static inline int uncore_num_counters(struct intel_uncore_box *box)
|
||||
return box->pmu->type->num_counters;
|
||||
}
|
||||
|
||||
static inline void uncore_box_init(struct intel_uncore_box *box)
|
||||
{
|
||||
if (!test_and_set_bit(UNCORE_BOX_FLAG_INITIATED, &box->flags)) {
|
||||
if (box->pmu->type->ops->init_box)
|
||||
box->pmu->type->ops->init_box(box);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void uncore_disable_box(struct intel_uncore_box *box)
|
||||
{
|
||||
if (box->pmu->type->ops->disable_box)
|
||||
@ -265,6 +273,8 @@ static inline void uncore_disable_box(struct intel_uncore_box *box)
|
||||
|
||||
static inline void uncore_enable_box(struct intel_uncore_box *box)
|
||||
{
|
||||
uncore_box_init(box);
|
||||
|
||||
if (box->pmu->type->ops->enable_box)
|
||||
box->pmu->type->ops->enable_box(box);
|
||||
}
|
||||
@ -287,14 +297,6 @@ static inline u64 uncore_read_counter(struct intel_uncore_box *box,
|
||||
return box->pmu->type->ops->read_counter(box, event);
|
||||
}
|
||||
|
||||
static inline void uncore_box_init(struct intel_uncore_box *box)
|
||||
{
|
||||
if (!test_and_set_bit(UNCORE_BOX_FLAG_INITIATED, &box->flags)) {
|
||||
if (box->pmu->type->ops->init_box)
|
||||
box->pmu->type->ops->init_box(box);
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool uncore_box_is_fake(struct intel_uncore_box *box)
|
||||
{
|
||||
return (box->phys_id < 0);
|
||||
|
@ -192,6 +192,9 @@ static void recalculate_apic_map(struct kvm *kvm)
|
||||
u16 cid, lid;
|
||||
u32 ldr, aid;
|
||||
|
||||
if (!kvm_apic_present(vcpu))
|
||||
continue;
|
||||
|
||||
aid = kvm_apic_id(apic);
|
||||
ldr = kvm_apic_get_reg(apic, APIC_LDR);
|
||||
cid = apic_cluster_id(new, ldr);
|
||||
|
@ -898,6 +898,8 @@ mm_fault_error(struct pt_regs *regs, unsigned long error_code,
|
||||
if (fault & (VM_FAULT_SIGBUS|VM_FAULT_HWPOISON|
|
||||
VM_FAULT_HWPOISON_LARGE))
|
||||
do_sigbus(regs, error_code, address, fault);
|
||||
else if (fault & VM_FAULT_SIGSEGV)
|
||||
bad_area_nosemaphore(regs, error_code, address);
|
||||
else
|
||||
BUG();
|
||||
}
|
||||
|
@ -448,6 +448,22 @@ static const struct dmi_system_id pciprobe_dmi_table[] __initconst = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "ftServer"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = set_scan_all,
|
||||
.ident = "Stratus/NEC ftServer",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "NEC"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Express5800/R32"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = set_scan_all,
|
||||
.ident = "Stratus/NEC ftServer",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "NEC"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Express5800/R31"),
|
||||
},
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -117,6 +117,8 @@ good_area:
|
||||
if (unlikely(fault & VM_FAULT_ERROR)) {
|
||||
if (fault & VM_FAULT_OOM)
|
||||
goto out_of_memory;
|
||||
else if (fault & VM_FAULT_SIGSEGV)
|
||||
goto bad_area;
|
||||
else if (fault & VM_FAULT_SIGBUS)
|
||||
goto do_sigbus;
|
||||
BUG();
|
||||
|
@ -15,26 +15,6 @@
|
||||
|
||||
static void blk_mq_sysfs_release(struct kobject *kobj)
|
||||
{
|
||||
struct request_queue *q;
|
||||
|
||||
q = container_of(kobj, struct request_queue, mq_kobj);
|
||||
free_percpu(q->queue_ctx);
|
||||
}
|
||||
|
||||
static void blk_mq_ctx_release(struct kobject *kobj)
|
||||
{
|
||||
struct blk_mq_ctx *ctx;
|
||||
|
||||
ctx = container_of(kobj, struct blk_mq_ctx, kobj);
|
||||
kobject_put(&ctx->queue->mq_kobj);
|
||||
}
|
||||
|
||||
static void blk_mq_hctx_release(struct kobject *kobj)
|
||||
{
|
||||
struct blk_mq_hw_ctx *hctx;
|
||||
|
||||
hctx = container_of(kobj, struct blk_mq_hw_ctx, kobj);
|
||||
kfree(hctx);
|
||||
}
|
||||
|
||||
struct blk_mq_ctx_sysfs_entry {
|
||||
@ -338,13 +318,13 @@ static struct kobj_type blk_mq_ktype = {
|
||||
static struct kobj_type blk_mq_ctx_ktype = {
|
||||
.sysfs_ops = &blk_mq_sysfs_ops,
|
||||
.default_attrs = default_ctx_attrs,
|
||||
.release = blk_mq_ctx_release,
|
||||
.release = blk_mq_sysfs_release,
|
||||
};
|
||||
|
||||
static struct kobj_type blk_mq_hw_ktype = {
|
||||
.sysfs_ops = &blk_mq_hw_sysfs_ops,
|
||||
.default_attrs = default_hw_ctx_attrs,
|
||||
.release = blk_mq_hctx_release,
|
||||
.release = blk_mq_sysfs_release,
|
||||
};
|
||||
|
||||
static void blk_mq_unregister_hctx(struct blk_mq_hw_ctx *hctx)
|
||||
@ -375,7 +355,6 @@ static int blk_mq_register_hctx(struct blk_mq_hw_ctx *hctx)
|
||||
return ret;
|
||||
|
||||
hctx_for_each_ctx(hctx, ctx, i) {
|
||||
kobject_get(&q->mq_kobj);
|
||||
ret = kobject_add(&ctx->kobj, &hctx->kobj, "cpu%u", ctx->cpu);
|
||||
if (ret)
|
||||
break;
|
||||
|
@ -1867,6 +1867,27 @@ static void blk_mq_add_queue_tag_set(struct blk_mq_tag_set *set,
|
||||
mutex_unlock(&set->tag_list_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* It is the actual release handler for mq, but we do it from
|
||||
* request queue's release handler for avoiding use-after-free
|
||||
* and headache because q->mq_kobj shouldn't have been introduced,
|
||||
* but we can't group ctx/kctx kobj without it.
|
||||
*/
|
||||
void blk_mq_release(struct request_queue *q)
|
||||
{
|
||||
struct blk_mq_hw_ctx *hctx;
|
||||
unsigned int i;
|
||||
|
||||
/* hctx kobj stays in hctx */
|
||||
queue_for_each_hw_ctx(q, hctx, i)
|
||||
kfree(hctx);
|
||||
|
||||
kfree(q->queue_hw_ctx);
|
||||
|
||||
/* ctx kobj stays in queue_ctx */
|
||||
free_percpu(q->queue_ctx);
|
||||
}
|
||||
|
||||
struct request_queue *blk_mq_init_queue(struct blk_mq_tag_set *set)
|
||||
{
|
||||
struct blk_mq_hw_ctx **hctxs;
|
||||
@ -2000,10 +2021,8 @@ void blk_mq_free_queue(struct request_queue *q)
|
||||
|
||||
percpu_ref_exit(&q->mq_usage_counter);
|
||||
|
||||
kfree(q->queue_hw_ctx);
|
||||
kfree(q->mq_map);
|
||||
|
||||
q->queue_hw_ctx = NULL;
|
||||
q->mq_map = NULL;
|
||||
|
||||
mutex_lock(&all_q_mutex);
|
||||
|
@ -62,6 +62,8 @@ extern void blk_mq_sysfs_unregister(struct request_queue *q);
|
||||
|
||||
extern void blk_mq_rq_timed_out(struct request *req, bool reserved);
|
||||
|
||||
void blk_mq_release(struct request_queue *q);
|
||||
|
||||
/*
|
||||
* Basic implementation of sparser bitmap, allowing the user to spread
|
||||
* the bits over more cachelines.
|
||||
|
@ -517,6 +517,8 @@ static void blk_release_queue(struct kobject *kobj)
|
||||
|
||||
if (!q->mq_ops)
|
||||
blk_free_flush_queue(q->fq);
|
||||
else
|
||||
blk_mq_release(q);
|
||||
|
||||
blk_trace_shutdown(q);
|
||||
|
||||
|
@ -134,8 +134,6 @@ source "drivers/staging/Kconfig"
|
||||
|
||||
source "drivers/platform/Kconfig"
|
||||
|
||||
source "drivers/soc/Kconfig"
|
||||
|
||||
source "drivers/clk/Kconfig"
|
||||
|
||||
source "drivers/hwspinlock/Kconfig"
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* ACPI support for Intel Lynxpoint LPSS.
|
||||
*
|
||||
* Copyright (C) 2013, 2014, Intel Corporation
|
||||
* Copyright (C) 2013, Intel Corporation
|
||||
* Authors: Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||
* Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
||||
*
|
||||
@ -60,8 +60,6 @@ ACPI_MODULE_NAME("acpi_lpss");
|
||||
#define LPSS_CLK_DIVIDER BIT(2)
|
||||
#define LPSS_LTR BIT(3)
|
||||
#define LPSS_SAVE_CTX BIT(4)
|
||||
#define LPSS_DEV_PROXY BIT(5)
|
||||
#define LPSS_PROXY_REQ BIT(6)
|
||||
|
||||
struct lpss_private_data;
|
||||
|
||||
@ -72,10 +70,8 @@ struct lpss_device_desc {
|
||||
void (*setup)(struct lpss_private_data *pdata);
|
||||
};
|
||||
|
||||
static struct device *proxy_device;
|
||||
|
||||
static struct lpss_device_desc lpss_dma_desc = {
|
||||
.flags = LPSS_CLK | LPSS_PROXY_REQ,
|
||||
.flags = LPSS_CLK,
|
||||
};
|
||||
|
||||
struct lpss_private_data {
|
||||
@ -150,24 +146,22 @@ static struct lpss_device_desc byt_pwm_dev_desc = {
|
||||
};
|
||||
|
||||
static struct lpss_device_desc byt_uart_dev_desc = {
|
||||
.flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX |
|
||||
LPSS_DEV_PROXY,
|
||||
.flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX,
|
||||
.prv_offset = 0x800,
|
||||
.setup = lpss_uart_setup,
|
||||
};
|
||||
|
||||
static struct lpss_device_desc byt_spi_dev_desc = {
|
||||
.flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX |
|
||||
LPSS_DEV_PROXY,
|
||||
.flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX,
|
||||
.prv_offset = 0x400,
|
||||
};
|
||||
|
||||
static struct lpss_device_desc byt_sdio_dev_desc = {
|
||||
.flags = LPSS_CLK | LPSS_DEV_PROXY,
|
||||
.flags = LPSS_CLK,
|
||||
};
|
||||
|
||||
static struct lpss_device_desc byt_i2c_dev_desc = {
|
||||
.flags = LPSS_CLK | LPSS_SAVE_CTX | LPSS_DEV_PROXY,
|
||||
.flags = LPSS_CLK | LPSS_SAVE_CTX,
|
||||
.prv_offset = 0x800,
|
||||
.setup = byt_i2c_setup,
|
||||
};
|
||||
@ -374,8 +368,6 @@ static int acpi_lpss_create_device(struct acpi_device *adev,
|
||||
adev->driver_data = pdata;
|
||||
pdev = acpi_create_platform_device(adev);
|
||||
if (!IS_ERR_OR_NULL(pdev)) {
|
||||
if (!proxy_device && dev_desc->flags & LPSS_DEV_PROXY)
|
||||
proxy_device = &pdev->dev;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -600,14 +592,7 @@ static int acpi_lpss_runtime_suspend(struct device *dev)
|
||||
if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
|
||||
acpi_lpss_save_ctx(dev, pdata);
|
||||
|
||||
ret = acpi_dev_runtime_suspend(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (pdata->dev_desc->flags & LPSS_PROXY_REQ && proxy_device)
|
||||
return pm_runtime_put_sync_suspend(proxy_device);
|
||||
|
||||
return 0;
|
||||
return acpi_dev_runtime_suspend(dev);
|
||||
}
|
||||
|
||||
static int acpi_lpss_runtime_resume(struct device *dev)
|
||||
@ -615,12 +600,6 @@ static int acpi_lpss_runtime_resume(struct device *dev)
|
||||
struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
|
||||
int ret;
|
||||
|
||||
if (pdata->dev_desc->flags & LPSS_PROXY_REQ && proxy_device) {
|
||||
ret = pm_runtime_get_sync(proxy_device);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = acpi_dev_runtime_resume(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -2098,32 +2098,26 @@ static void rbd_dev_parent_put(struct rbd_device *rbd_dev)
|
||||
* If an image has a non-zero parent overlap, get a reference to its
|
||||
* parent.
|
||||
*
|
||||
* We must get the reference before checking for the overlap to
|
||||
* coordinate properly with zeroing the parent overlap in
|
||||
* rbd_dev_v2_parent_info() when an image gets flattened. We
|
||||
* drop it again if there is no overlap.
|
||||
*
|
||||
* Returns true if the rbd device has a parent with a non-zero
|
||||
* overlap and a reference for it was successfully taken, or
|
||||
* false otherwise.
|
||||
*/
|
||||
static bool rbd_dev_parent_get(struct rbd_device *rbd_dev)
|
||||
{
|
||||
int counter;
|
||||
int counter = 0;
|
||||
|
||||
if (!rbd_dev->parent_spec)
|
||||
return false;
|
||||
|
||||
counter = atomic_inc_return_safe(&rbd_dev->parent_ref);
|
||||
if (counter > 0 && rbd_dev->parent_overlap)
|
||||
return true;
|
||||
|
||||
/* Image was flattened, but parent is not yet torn down */
|
||||
down_read(&rbd_dev->header_rwsem);
|
||||
if (rbd_dev->parent_overlap)
|
||||
counter = atomic_inc_return_safe(&rbd_dev->parent_ref);
|
||||
up_read(&rbd_dev->header_rwsem);
|
||||
|
||||
if (counter < 0)
|
||||
rbd_warn(rbd_dev, "parent reference overflow");
|
||||
|
||||
return false;
|
||||
return counter > 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -4239,7 +4233,6 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev)
|
||||
*/
|
||||
if (rbd_dev->parent_overlap) {
|
||||
rbd_dev->parent_overlap = 0;
|
||||
smp_mb();
|
||||
rbd_dev_parent_put(rbd_dev);
|
||||
pr_info("%s: clone image has been flattened\n",
|
||||
rbd_dev->disk->disk_name);
|
||||
@ -4285,7 +4278,6 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev)
|
||||
* treat it specially.
|
||||
*/
|
||||
rbd_dev->parent_overlap = overlap;
|
||||
smp_mb();
|
||||
if (!overlap) {
|
||||
|
||||
/* A null parent_spec indicates it's the initial probe */
|
||||
@ -5114,10 +5106,7 @@ static void rbd_dev_unprobe(struct rbd_device *rbd_dev)
|
||||
{
|
||||
struct rbd_image_header *header;
|
||||
|
||||
/* Drop parent reference unless it's already been done (or none) */
|
||||
|
||||
if (rbd_dev->parent_overlap)
|
||||
rbd_dev_parent_put(rbd_dev);
|
||||
rbd_dev_parent_put(rbd_dev);
|
||||
|
||||
/* Free dynamic fields from the header, then zero it out */
|
||||
|
||||
|
@ -801,9 +801,11 @@ static int mcp230xx_probe(struct i2c_client *client,
|
||||
client->irq = irq_of_parse_and_map(client->dev.of_node, 0);
|
||||
} else {
|
||||
pdata = dev_get_platdata(&client->dev);
|
||||
if (!pdata || !gpio_is_valid(pdata->base)) {
|
||||
dev_dbg(&client->dev, "invalid platform data\n");
|
||||
return -EINVAL;
|
||||
if (!pdata) {
|
||||
pdata = devm_kzalloc(&client->dev,
|
||||
sizeof(struct mcp23s08_platform_data),
|
||||
GFP_KERNEL);
|
||||
pdata->base = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -924,10 +926,11 @@ static int mcp23s08_probe(struct spi_device *spi)
|
||||
} else {
|
||||
type = spi_get_device_id(spi)->driver_data;
|
||||
pdata = dev_get_platdata(&spi->dev);
|
||||
if (!pdata || !gpio_is_valid(pdata->base)) {
|
||||
dev_dbg(&spi->dev,
|
||||
"invalid or missing platform data\n");
|
||||
return -EINVAL;
|
||||
if (!pdata) {
|
||||
pdata = devm_kzalloc(&spi->dev,
|
||||
sizeof(struct mcp23s08_platform_data),
|
||||
GFP_KERNEL);
|
||||
pdata->base = -1;
|
||||
}
|
||||
|
||||
for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) {
|
||||
|
@ -88,6 +88,8 @@ struct gpio_bank {
|
||||
#define BANK_USED(bank) (bank->mod_usage || bank->irq_usage)
|
||||
#define LINE_USED(line, offset) (line & (BIT(offset)))
|
||||
|
||||
static void omap_gpio_unmask_irq(struct irq_data *d);
|
||||
|
||||
static int omap_irq_to_gpio(struct gpio_bank *bank, unsigned int gpio_irq)
|
||||
{
|
||||
return bank->chip.base + gpio_irq;
|
||||
@ -477,6 +479,16 @@ static int omap_gpio_is_input(struct gpio_bank *bank, int mask)
|
||||
return readl_relaxed(reg) & mask;
|
||||
}
|
||||
|
||||
static void omap_gpio_init_irq(struct gpio_bank *bank, unsigned gpio,
|
||||
unsigned offset)
|
||||
{
|
||||
if (!LINE_USED(bank->mod_usage, offset)) {
|
||||
omap_enable_gpio_module(bank, offset);
|
||||
omap_set_gpio_direction(bank, offset, 1);
|
||||
}
|
||||
bank->irq_usage |= BIT(GPIO_INDEX(bank, gpio));
|
||||
}
|
||||
|
||||
static int omap_gpio_irq_type(struct irq_data *d, unsigned type)
|
||||
{
|
||||
struct gpio_bank *bank = omap_irq_data_get_bank(d);
|
||||
@ -506,15 +518,11 @@ static int omap_gpio_irq_type(struct irq_data *d, unsigned type)
|
||||
spin_lock_irqsave(&bank->lock, flags);
|
||||
offset = GPIO_INDEX(bank, gpio);
|
||||
retval = omap_set_gpio_triggering(bank, offset, type);
|
||||
if (!LINE_USED(bank->mod_usage, offset)) {
|
||||
omap_enable_gpio_module(bank, offset);
|
||||
omap_set_gpio_direction(bank, offset, 1);
|
||||
} else if (!omap_gpio_is_input(bank, BIT(offset))) {
|
||||
omap_gpio_init_irq(bank, gpio, offset);
|
||||
if (!omap_gpio_is_input(bank, BIT(offset))) {
|
||||
spin_unlock_irqrestore(&bank->lock, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bank->irq_usage |= BIT(GPIO_INDEX(bank, gpio));
|
||||
spin_unlock_irqrestore(&bank->lock, flags);
|
||||
|
||||
if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
|
||||
@ -792,6 +800,24 @@ exit:
|
||||
pm_runtime_put(bank->dev);
|
||||
}
|
||||
|
||||
static unsigned int omap_gpio_irq_startup(struct irq_data *d)
|
||||
{
|
||||
struct gpio_bank *bank = omap_irq_data_get_bank(d);
|
||||
unsigned int gpio = omap_irq_to_gpio(bank, d->hwirq);
|
||||
unsigned long flags;
|
||||
unsigned offset = GPIO_INDEX(bank, gpio);
|
||||
|
||||
if (!BANK_USED(bank))
|
||||
pm_runtime_get_sync(bank->dev);
|
||||
|
||||
spin_lock_irqsave(&bank->lock, flags);
|
||||
omap_gpio_init_irq(bank, gpio, offset);
|
||||
spin_unlock_irqrestore(&bank->lock, flags);
|
||||
omap_gpio_unmask_irq(d);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void omap_gpio_irq_shutdown(struct irq_data *d)
|
||||
{
|
||||
struct gpio_bank *bank = omap_irq_data_get_bank(d);
|
||||
@ -1181,6 +1207,7 @@ static int omap_gpio_probe(struct platform_device *pdev)
|
||||
if (!irqc)
|
||||
return -ENOMEM;
|
||||
|
||||
irqc->irq_startup = omap_gpio_irq_startup,
|
||||
irqc->irq_shutdown = omap_gpio_irq_shutdown,
|
||||
irqc->irq_ack = omap_gpio_ack_irq,
|
||||
irqc->irq_mask = omap_gpio_mask_irq,
|
||||
|
@ -648,6 +648,7 @@ int gpiod_export_link(struct device *dev, const char *name,
|
||||
if (tdev != NULL) {
|
||||
status = sysfs_create_link(&dev->kobj, &tdev->kobj,
|
||||
name);
|
||||
put_device(tdev);
|
||||
} else {
|
||||
status = -ENODEV;
|
||||
}
|
||||
@ -695,7 +696,7 @@ int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value)
|
||||
}
|
||||
|
||||
status = sysfs_set_active_low(desc, dev, value);
|
||||
|
||||
put_device(dev);
|
||||
unlock:
|
||||
mutex_unlock(&sysfs_lock);
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include "kfd_priv.h"
|
||||
#include "kfd_device_queue_manager.h"
|
||||
#include "kfd_pm4_headers.h"
|
||||
|
||||
#define MQD_SIZE_ALIGNED 768
|
||||
|
||||
@ -169,9 +170,8 @@ bool kgd2kfd_device_init(struct kfd_dev *kfd,
|
||||
kfd->shared_resources = *gpu_resources;
|
||||
|
||||
/* calculate max size of mqds needed for queues */
|
||||
size = max_num_of_processes *
|
||||
max_num_of_queues_per_process *
|
||||
kfd->device_info->mqd_size_aligned;
|
||||
size = max_num_of_queues_per_device *
|
||||
kfd->device_info->mqd_size_aligned;
|
||||
|
||||
/* add another 512KB for all other allocations on gart */
|
||||
size += 512 * 1024;
|
||||
|
@ -183,6 +183,13 @@ static int create_queue_nocpsch(struct device_queue_manager *dqm,
|
||||
|
||||
mutex_lock(&dqm->lock);
|
||||
|
||||
if (dqm->total_queue_count >= max_num_of_queues_per_device) {
|
||||
pr_warn("amdkfd: Can't create new usermode queue because %d queues were already created\n",
|
||||
dqm->total_queue_count);
|
||||
mutex_unlock(&dqm->lock);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if (list_empty(&qpd->queues_list)) {
|
||||
retval = allocate_vmid(dqm, qpd, q);
|
||||
if (retval != 0) {
|
||||
@ -207,6 +214,14 @@ static int create_queue_nocpsch(struct device_queue_manager *dqm,
|
||||
list_add(&q->list, &qpd->queues_list);
|
||||
dqm->queue_count++;
|
||||
|
||||
/*
|
||||
* Unconditionally increment this counter, regardless of the queue's
|
||||
* type or whether the queue is active.
|
||||
*/
|
||||
dqm->total_queue_count++;
|
||||
pr_debug("Total of %d queues are accountable so far\n",
|
||||
dqm->total_queue_count);
|
||||
|
||||
mutex_unlock(&dqm->lock);
|
||||
return 0;
|
||||
}
|
||||
@ -326,6 +341,15 @@ static int destroy_queue_nocpsch(struct device_queue_manager *dqm,
|
||||
if (list_empty(&qpd->queues_list))
|
||||
deallocate_vmid(dqm, qpd, q);
|
||||
dqm->queue_count--;
|
||||
|
||||
/*
|
||||
* Unconditionally decrement this counter, regardless of the queue's
|
||||
* type
|
||||
*/
|
||||
dqm->total_queue_count--;
|
||||
pr_debug("Total of %d queues are accountable so far\n",
|
||||
dqm->total_queue_count);
|
||||
|
||||
out:
|
||||
mutex_unlock(&dqm->lock);
|
||||
return retval;
|
||||
@ -541,10 +565,14 @@ static int init_pipelines(struct device_queue_manager *dqm,
|
||||
|
||||
for (i = 0; i < pipes_num; i++) {
|
||||
inx = i + first_pipe;
|
||||
/*
|
||||
* HPD buffer on GTT is allocated by amdkfd, no need to waste
|
||||
* space in GTT for pipelines we don't initialize
|
||||
*/
|
||||
pipe_hpd_addr = dqm->pipelines_addr + i * CIK_HPD_EOP_BYTES;
|
||||
pr_debug("kfd: pipeline address %llX\n", pipe_hpd_addr);
|
||||
/* = log2(bytes/4)-1 */
|
||||
kfd2kgd->init_pipeline(dqm->dev->kgd, i,
|
||||
kfd2kgd->init_pipeline(dqm->dev->kgd, inx,
|
||||
CIK_HPD_EOP_BYTES_LOG2 - 3, pipe_hpd_addr);
|
||||
}
|
||||
|
||||
@ -560,7 +588,7 @@ static int init_scheduler(struct device_queue_manager *dqm)
|
||||
|
||||
pr_debug("kfd: In %s\n", __func__);
|
||||
|
||||
retval = init_pipelines(dqm, get_pipes_num(dqm), KFD_DQM_FIRST_PIPE);
|
||||
retval = init_pipelines(dqm, get_pipes_num(dqm), get_first_pipe(dqm));
|
||||
if (retval != 0)
|
||||
return retval;
|
||||
|
||||
@ -752,6 +780,21 @@ static int create_kernel_queue_cpsch(struct device_queue_manager *dqm,
|
||||
pr_debug("kfd: In func %s\n", __func__);
|
||||
|
||||
mutex_lock(&dqm->lock);
|
||||
if (dqm->total_queue_count >= max_num_of_queues_per_device) {
|
||||
pr_warn("amdkfd: Can't create new kernel queue because %d queues were already created\n",
|
||||
dqm->total_queue_count);
|
||||
mutex_unlock(&dqm->lock);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unconditionally increment this counter, regardless of the queue's
|
||||
* type or whether the queue is active.
|
||||
*/
|
||||
dqm->total_queue_count++;
|
||||
pr_debug("Total of %d queues are accountable so far\n",
|
||||
dqm->total_queue_count);
|
||||
|
||||
list_add(&kq->list, &qpd->priv_queue_list);
|
||||
dqm->queue_count++;
|
||||
qpd->is_debug = true;
|
||||
@ -775,6 +818,13 @@ static void destroy_kernel_queue_cpsch(struct device_queue_manager *dqm,
|
||||
dqm->queue_count--;
|
||||
qpd->is_debug = false;
|
||||
execute_queues_cpsch(dqm, false);
|
||||
/*
|
||||
* Unconditionally decrement this counter, regardless of the queue's
|
||||
* type.
|
||||
*/
|
||||
dqm->total_queue_count--;
|
||||
pr_debug("Total of %d queues are accountable so far\n",
|
||||
dqm->total_queue_count);
|
||||
mutex_unlock(&dqm->lock);
|
||||
}
|
||||
|
||||
@ -793,6 +843,13 @@ static int create_queue_cpsch(struct device_queue_manager *dqm, struct queue *q,
|
||||
|
||||
mutex_lock(&dqm->lock);
|
||||
|
||||
if (dqm->total_queue_count >= max_num_of_queues_per_device) {
|
||||
pr_warn("amdkfd: Can't create new usermode queue because %d queues were already created\n",
|
||||
dqm->total_queue_count);
|
||||
retval = -EPERM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mqd = dqm->get_mqd_manager(dqm, KFD_MQD_TYPE_CIK_CP);
|
||||
if (mqd == NULL) {
|
||||
mutex_unlock(&dqm->lock);
|
||||
@ -810,6 +867,15 @@ static int create_queue_cpsch(struct device_queue_manager *dqm, struct queue *q,
|
||||
retval = execute_queues_cpsch(dqm, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unconditionally increment this counter, regardless of the queue's
|
||||
* type or whether the queue is active.
|
||||
*/
|
||||
dqm->total_queue_count++;
|
||||
|
||||
pr_debug("Total of %d queues are accountable so far\n",
|
||||
dqm->total_queue_count);
|
||||
|
||||
out:
|
||||
mutex_unlock(&dqm->lock);
|
||||
return retval;
|
||||
@ -930,6 +996,14 @@ static int destroy_queue_cpsch(struct device_queue_manager *dqm,
|
||||
|
||||
mqd->uninit_mqd(mqd, q->mqd, q->mqd_mem_obj);
|
||||
|
||||
/*
|
||||
* Unconditionally decrement this counter, regardless of the queue's
|
||||
* type
|
||||
*/
|
||||
dqm->total_queue_count--;
|
||||
pr_debug("Total of %d queues are accountable so far\n",
|
||||
dqm->total_queue_count);
|
||||
|
||||
mutex_unlock(&dqm->lock);
|
||||
|
||||
return 0;
|
||||
|
@ -130,6 +130,7 @@ struct device_queue_manager {
|
||||
struct list_head queues;
|
||||
unsigned int processes_count;
|
||||
unsigned int queue_count;
|
||||
unsigned int total_queue_count;
|
||||
unsigned int next_pipe_to_allocate;
|
||||
unsigned int *allocated_queues;
|
||||
unsigned int vmid_bitmap;
|
||||
|
@ -50,15 +50,10 @@ module_param(sched_policy, int, 0444);
|
||||
MODULE_PARM_DESC(sched_policy,
|
||||
"Kernel cmdline parameter that defines the amdkfd scheduling policy");
|
||||
|
||||
int max_num_of_processes = KFD_MAX_NUM_OF_PROCESSES_DEFAULT;
|
||||
module_param(max_num_of_processes, int, 0444);
|
||||
MODULE_PARM_DESC(max_num_of_processes,
|
||||
"Kernel cmdline parameter that defines the amdkfd maximum number of supported processes");
|
||||
|
||||
int max_num_of_queues_per_process = KFD_MAX_NUM_OF_QUEUES_PER_PROCESS_DEFAULT;
|
||||
module_param(max_num_of_queues_per_process, int, 0444);
|
||||
MODULE_PARM_DESC(max_num_of_queues_per_process,
|
||||
"Kernel cmdline parameter that defines the amdkfd maximum number of supported queues per process");
|
||||
int max_num_of_queues_per_device = KFD_MAX_NUM_OF_QUEUES_PER_DEVICE_DEFAULT;
|
||||
module_param(max_num_of_queues_per_device, int, 0444);
|
||||
MODULE_PARM_DESC(max_num_of_queues_per_device,
|
||||
"Maximum number of supported queues per device (1 = Minimum, 4096 = default)");
|
||||
|
||||
bool kgd2kfd_init(unsigned interface_version,
|
||||
const struct kfd2kgd_calls *f2g,
|
||||
@ -100,16 +95,10 @@ static int __init kfd_module_init(void)
|
||||
}
|
||||
|
||||
/* Verify module parameters */
|
||||
if ((max_num_of_processes < 0) ||
|
||||
(max_num_of_processes > KFD_MAX_NUM_OF_PROCESSES)) {
|
||||
pr_err("kfd: max_num_of_processes must be between 0 to KFD_MAX_NUM_OF_PROCESSES\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((max_num_of_queues_per_process < 0) ||
|
||||
(max_num_of_queues_per_process >
|
||||
KFD_MAX_NUM_OF_QUEUES_PER_PROCESS)) {
|
||||
pr_err("kfd: max_num_of_queues_per_process must be between 0 to KFD_MAX_NUM_OF_QUEUES_PER_PROCESS\n");
|
||||
if ((max_num_of_queues_per_device < 1) ||
|
||||
(max_num_of_queues_per_device >
|
||||
KFD_MAX_NUM_OF_QUEUES_PER_DEVICE)) {
|
||||
pr_err("kfd: max_num_of_queues_per_device must be between 1 to KFD_MAX_NUM_OF_QUEUES_PER_DEVICE\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ static DEFINE_MUTEX(pasid_mutex);
|
||||
|
||||
int kfd_pasid_init(void)
|
||||
{
|
||||
pasid_limit = max_num_of_processes;
|
||||
pasid_limit = KFD_MAX_NUM_OF_PROCESSES;
|
||||
|
||||
pasid_bitmap = kcalloc(BITS_TO_LONGS(pasid_limit), sizeof(long), GFP_KERNEL);
|
||||
if (!pasid_bitmap)
|
||||
|
@ -52,20 +52,19 @@
|
||||
#define kfd_alloc_struct(ptr_to_struct) \
|
||||
((typeof(ptr_to_struct)) kzalloc(sizeof(*ptr_to_struct), GFP_KERNEL))
|
||||
|
||||
/* Kernel module parameter to specify maximum number of supported processes */
|
||||
extern int max_num_of_processes;
|
||||
|
||||
#define KFD_MAX_NUM_OF_PROCESSES_DEFAULT 32
|
||||
#define KFD_MAX_NUM_OF_PROCESSES 512
|
||||
#define KFD_MAX_NUM_OF_QUEUES_PER_PROCESS 1024
|
||||
|
||||
/*
|
||||
* Kernel module parameter to specify maximum number of supported queues
|
||||
* per process
|
||||
* Kernel module parameter to specify maximum number of supported queues per
|
||||
* device
|
||||
*/
|
||||
extern int max_num_of_queues_per_process;
|
||||
extern int max_num_of_queues_per_device;
|
||||
|
||||
#define KFD_MAX_NUM_OF_QUEUES_PER_PROCESS_DEFAULT 128
|
||||
#define KFD_MAX_NUM_OF_QUEUES_PER_PROCESS 1024
|
||||
#define KFD_MAX_NUM_OF_QUEUES_PER_DEVICE_DEFAULT 4096
|
||||
#define KFD_MAX_NUM_OF_QUEUES_PER_DEVICE \
|
||||
(KFD_MAX_NUM_OF_PROCESSES * \
|
||||
KFD_MAX_NUM_OF_QUEUES_PER_PROCESS)
|
||||
|
||||
#define KFD_KERNEL_QUEUE_SIZE 2048
|
||||
|
||||
|
@ -54,11 +54,11 @@ static int find_available_queue_slot(struct process_queue_manager *pqm,
|
||||
pr_debug("kfd: in %s\n", __func__);
|
||||
|
||||
found = find_first_zero_bit(pqm->queue_slot_bitmap,
|
||||
max_num_of_queues_per_process);
|
||||
KFD_MAX_NUM_OF_QUEUES_PER_PROCESS);
|
||||
|
||||
pr_debug("kfd: the new slot id %lu\n", found);
|
||||
|
||||
if (found >= max_num_of_queues_per_process) {
|
||||
if (found >= KFD_MAX_NUM_OF_QUEUES_PER_PROCESS) {
|
||||
pr_info("amdkfd: Can not open more queues for process with pasid %d\n",
|
||||
pqm->process->pasid);
|
||||
return -ENOMEM;
|
||||
@ -76,7 +76,7 @@ int pqm_init(struct process_queue_manager *pqm, struct kfd_process *p)
|
||||
|
||||
INIT_LIST_HEAD(&pqm->queues);
|
||||
pqm->queue_slot_bitmap =
|
||||
kzalloc(DIV_ROUND_UP(max_num_of_queues_per_process,
|
||||
kzalloc(DIV_ROUND_UP(KFD_MAX_NUM_OF_QUEUES_PER_PROCESS,
|
||||
BITS_PER_BYTE), GFP_KERNEL);
|
||||
if (pqm->queue_slot_bitmap == NULL)
|
||||
return -ENOMEM;
|
||||
@ -203,6 +203,7 @@ int pqm_create_queue(struct process_queue_manager *pqm,
|
||||
pqn->kq = NULL;
|
||||
retval = dev->dqm->create_queue(dev->dqm, q, &pdd->qpd,
|
||||
&q->properties.vmid);
|
||||
pr_debug("DQM returned %d for create_queue\n", retval);
|
||||
print_queue(q);
|
||||
break;
|
||||
case KFD_QUEUE_TYPE_DIQ:
|
||||
@ -222,7 +223,7 @@ int pqm_create_queue(struct process_queue_manager *pqm,
|
||||
}
|
||||
|
||||
if (retval != 0) {
|
||||
pr_err("kfd: error dqm create queue\n");
|
||||
pr_debug("Error dqm create queue\n");
|
||||
goto err_create_queue;
|
||||
}
|
||||
|
||||
@ -241,7 +242,10 @@ int pqm_create_queue(struct process_queue_manager *pqm,
|
||||
err_create_queue:
|
||||
kfree(pqn);
|
||||
err_allocate_pqn:
|
||||
/* check if queues list is empty unregister process from device */
|
||||
clear_bit(*qid, pqm->queue_slot_bitmap);
|
||||
if (list_empty(&pqm->queues))
|
||||
dev->dqm->unregister_process(dev->dqm, &pdd->qpd);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@ -311,7 +315,11 @@ int pqm_update_queue(struct process_queue_manager *pqm, unsigned int qid,
|
||||
BUG_ON(!pqm);
|
||||
|
||||
pqn = get_queue_by_qid(pqm, qid);
|
||||
BUG_ON(!pqn);
|
||||
if (!pqn) {
|
||||
pr_debug("amdkfd: No queue %d exists for update operation\n",
|
||||
qid);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
pqn->q->properties.queue_address = p->queue_address;
|
||||
pqn->q->properties.queue_size = p->queue_size;
|
||||
|
@ -16,9 +16,12 @@
|
||||
#include "cirrus_drv.h"
|
||||
|
||||
int cirrus_modeset = -1;
|
||||
int cirrus_bpp = 24;
|
||||
|
||||
MODULE_PARM_DESC(modeset, "Disable/Enable modesetting");
|
||||
module_param_named(modeset, cirrus_modeset, int, 0400);
|
||||
MODULE_PARM_DESC(bpp, "Max bits-per-pixel (default:24)");
|
||||
module_param_named(bpp, cirrus_bpp, int, 0400);
|
||||
|
||||
/*
|
||||
* This is the generic driver code. This binds the driver to the drm core,
|
||||
|
@ -262,4 +262,7 @@ static inline void cirrus_bo_unreserve(struct cirrus_bo *bo)
|
||||
|
||||
int cirrus_bo_push_sysram(struct cirrus_bo *bo);
|
||||
int cirrus_bo_pin(struct cirrus_bo *bo, u32 pl_flag, u64 *gpu_addr);
|
||||
|
||||
extern int cirrus_bpp;
|
||||
|
||||
#endif /* __CIRRUS_DRV_H__ */
|
||||
|
@ -320,6 +320,8 @@ bool cirrus_check_framebuffer(struct cirrus_device *cdev, int width, int height,
|
||||
const int max_pitch = 0x1FF << 3; /* (4096 - 1) & ~111b bytes */
|
||||
const int max_size = cdev->mc.vram_size;
|
||||
|
||||
if (bpp > cirrus_bpp)
|
||||
return false;
|
||||
if (bpp > 32)
|
||||
return false;
|
||||
|
||||
|
@ -501,8 +501,13 @@ static int cirrus_vga_get_modes(struct drm_connector *connector)
|
||||
int count;
|
||||
|
||||
/* Just add a static list of modes */
|
||||
count = drm_add_modes_noedid(connector, 1280, 1024);
|
||||
drm_set_preferred_mode(connector, 1024, 768);
|
||||
if (cirrus_bpp <= 24) {
|
||||
count = drm_add_modes_noedid(connector, 1280, 1024);
|
||||
drm_set_preferred_mode(connector, 1024, 768);
|
||||
} else {
|
||||
count = drm_add_modes_noedid(connector, 800, 600);
|
||||
drm_set_preferred_mode(connector, 800, 600);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -145,6 +145,31 @@ int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_
|
||||
}
|
||||
EXPORT_SYMBOL(drm_fb_helper_add_one_connector);
|
||||
|
||||
static void remove_from_modeset(struct drm_mode_set *set,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < set->num_connectors; i++) {
|
||||
if (set->connectors[i] == connector)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == set->num_connectors)
|
||||
return;
|
||||
|
||||
for (j = i + 1; j < set->num_connectors; j++) {
|
||||
set->connectors[j - 1] = set->connectors[j];
|
||||
}
|
||||
set->num_connectors--;
|
||||
|
||||
/* because i915 is pissy about this..
|
||||
* TODO maybe need to makes sure we set it back to !=NULL somewhere?
|
||||
*/
|
||||
if (set->num_connectors == 0)
|
||||
set->fb = NULL;
|
||||
}
|
||||
|
||||
int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
@ -167,6 +192,11 @@ int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
|
||||
}
|
||||
fb_helper->connector_count--;
|
||||
kfree(fb_helper_connector);
|
||||
|
||||
/* also cleanup dangling references to the connector: */
|
||||
for (i = 0; i < fb_helper->crtc_count; i++)
|
||||
remove_from_modeset(&fb_helper->crtc_info[i].mode_set, connector);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_fb_helper_remove_one_connector);
|
||||
|
@ -32,6 +32,8 @@
|
||||
struct tda998x_priv {
|
||||
struct i2c_client *cec;
|
||||
struct i2c_client *hdmi;
|
||||
struct mutex mutex;
|
||||
struct delayed_work dwork;
|
||||
uint16_t rev;
|
||||
uint8_t current_page;
|
||||
int dpms;
|
||||
@ -402,9 +404,10 @@ reg_read_range(struct tda998x_priv *priv, uint16_t reg, char *buf, int cnt)
|
||||
uint8_t addr = REG2ADDR(reg);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&priv->mutex);
|
||||
ret = set_page(priv, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto out;
|
||||
|
||||
ret = i2c_master_send(client, &addr, sizeof(addr));
|
||||
if (ret < 0)
|
||||
@ -414,10 +417,12 @@ reg_read_range(struct tda998x_priv *priv, uint16_t reg, char *buf, int cnt)
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
return ret;
|
||||
goto out;
|
||||
|
||||
fail:
|
||||
dev_err(&client->dev, "Error %d reading from 0x%x\n", ret, reg);
|
||||
out:
|
||||
mutex_unlock(&priv->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -431,13 +436,16 @@ reg_write_range(struct tda998x_priv *priv, uint16_t reg, uint8_t *p, int cnt)
|
||||
buf[0] = REG2ADDR(reg);
|
||||
memcpy(&buf[1], p, cnt);
|
||||
|
||||
mutex_lock(&priv->mutex);
|
||||
ret = set_page(priv, reg);
|
||||
if (ret < 0)
|
||||
return;
|
||||
goto out;
|
||||
|
||||
ret = i2c_master_send(client, buf, cnt + 1);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "Error %d writing to 0x%x\n", ret, reg);
|
||||
out:
|
||||
mutex_unlock(&priv->mutex);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -459,13 +467,16 @@ reg_write(struct tda998x_priv *priv, uint16_t reg, uint8_t val)
|
||||
uint8_t buf[] = {REG2ADDR(reg), val};
|
||||
int ret;
|
||||
|
||||
mutex_lock(&priv->mutex);
|
||||
ret = set_page(priv, reg);
|
||||
if (ret < 0)
|
||||
return;
|
||||
goto out;
|
||||
|
||||
ret = i2c_master_send(client, buf, sizeof(buf));
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "Error %d writing to 0x%x\n", ret, reg);
|
||||
out:
|
||||
mutex_unlock(&priv->mutex);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -475,13 +486,16 @@ reg_write16(struct tda998x_priv *priv, uint16_t reg, uint16_t val)
|
||||
uint8_t buf[] = {REG2ADDR(reg), val >> 8, val};
|
||||
int ret;
|
||||
|
||||
mutex_lock(&priv->mutex);
|
||||
ret = set_page(priv, reg);
|
||||
if (ret < 0)
|
||||
return;
|
||||
goto out;
|
||||
|
||||
ret = i2c_master_send(client, buf, sizeof(buf));
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "Error %d writing to 0x%x\n", ret, reg);
|
||||
out:
|
||||
mutex_unlock(&priv->mutex);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -536,6 +550,17 @@ tda998x_reset(struct tda998x_priv *priv)
|
||||
reg_write(priv, REG_MUX_VP_VIP_OUT, 0x24);
|
||||
}
|
||||
|
||||
/* handle HDMI connect/disconnect */
|
||||
static void tda998x_hpd(struct work_struct *work)
|
||||
{
|
||||
struct delayed_work *dwork = to_delayed_work(work);
|
||||
struct tda998x_priv *priv =
|
||||
container_of(dwork, struct tda998x_priv, dwork);
|
||||
|
||||
if (priv->encoder && priv->encoder->dev)
|
||||
drm_kms_helper_hotplug_event(priv->encoder->dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* only 2 interrupts may occur: screen plug/unplug and EDID read
|
||||
*/
|
||||
@ -559,8 +584,7 @@ static irqreturn_t tda998x_irq_thread(int irq, void *data)
|
||||
priv->wq_edid_wait = 0;
|
||||
wake_up(&priv->wq_edid);
|
||||
} else if (cec != 0) { /* HPD change */
|
||||
if (priv->encoder && priv->encoder->dev)
|
||||
drm_helper_hpd_irq_event(priv->encoder->dev);
|
||||
schedule_delayed_work(&priv->dwork, HZ/10);
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
@ -1170,8 +1194,10 @@ static void tda998x_destroy(struct tda998x_priv *priv)
|
||||
/* disable all IRQs and free the IRQ handler */
|
||||
cec_write(priv, REG_CEC_RXSHPDINTENA, 0);
|
||||
reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
|
||||
if (priv->hdmi->irq)
|
||||
if (priv->hdmi->irq) {
|
||||
free_irq(priv->hdmi->irq, priv);
|
||||
cancel_delayed_work_sync(&priv->dwork);
|
||||
}
|
||||
|
||||
i2c_unregister_device(priv->cec);
|
||||
}
|
||||
@ -1255,6 +1281,7 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
|
||||
struct device_node *np = client->dev.of_node;
|
||||
u32 video;
|
||||
int rev_lo, rev_hi, ret;
|
||||
unsigned short cec_addr;
|
||||
|
||||
priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(2) | VIP_CNTRL_0_SWAP_B(3);
|
||||
priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(0) | VIP_CNTRL_1_SWAP_D(1);
|
||||
@ -1262,12 +1289,16 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
|
||||
|
||||
priv->current_page = 0xff;
|
||||
priv->hdmi = client;
|
||||
priv->cec = i2c_new_dummy(client->adapter, 0x34);
|
||||
/* CEC I2C address bound to TDA998x I2C addr by configuration pins */
|
||||
cec_addr = 0x34 + (client->addr & 0x03);
|
||||
priv->cec = i2c_new_dummy(client->adapter, cec_addr);
|
||||
if (!priv->cec)
|
||||
return -ENODEV;
|
||||
|
||||
priv->dpms = DRM_MODE_DPMS_OFF;
|
||||
|
||||
mutex_init(&priv->mutex); /* protect the page access */
|
||||
|
||||
/* wake up the device: */
|
||||
cec_write(priv, REG_CEC_ENAMODS,
|
||||
CEC_ENAMODS_EN_RXSENS | CEC_ENAMODS_EN_HDMI);
|
||||
@ -1323,8 +1354,9 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
|
||||
if (client->irq) {
|
||||
int irqf_trigger;
|
||||
|
||||
/* init read EDID waitqueue */
|
||||
/* init read EDID waitqueue and HDP work */
|
||||
init_waitqueue_head(&priv->wq_edid);
|
||||
INIT_DELAYED_WORK(&priv->dwork, tda998x_hpd);
|
||||
|
||||
/* clear pending interrupts */
|
||||
reg_read(priv, REG_INT_FLAGS_0);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user