Pull char/misc driver updates from Greg KH: "Here is the big char/misc driver update for 4.14-rc1. Lots of different stuff in here, it's been an active development cycle for some reason. Highlights are: - updated binder driver, this brings binder up to date with what shipped in the Android O release, plus some more changes that happened since then that are in the Android development trees. - coresight updates and fixes - mux driver file renames to be a bit "nicer" - intel_th driver updates - normal set of hyper-v updates and changes - small fpga subsystem and driver updates - lots of const code changes all over the driver trees - extcon driver updates - fmc driver subsystem upadates - w1 subsystem minor reworks and new features and drivers added - spmi driver updates Plus a smattering of other minor driver updates and fixes. All of these have been in linux-next with no reported issues for a while" * tag 'char-misc-4.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (244 commits) ANDROID: binder: don't queue async transactions to thread. ANDROID: binder: don't enqueue death notifications to thread todo. ANDROID: binder: Don't BUG_ON(!spin_is_locked()). ANDROID: binder: Add BINDER_GET_NODE_DEBUG_INFO ioctl ANDROID: binder: push new transactions to waiting threads. ANDROID: binder: remove proc waitqueue android: binder: Add page usage in binder stats android: binder: fixup crash introduced by moving buffer hdr drivers: w1: add hwmon temp support for w1_therm drivers: w1: refactor w1_slave_show to make the temp reading functionality separate drivers: w1: add hwmon support structures eeprom: idt_89hpesx: Support both ACPI and OF probing mcb: Fix an error handling path in 'chameleon_parse_cells()' MCB: add support for SC31 to mcb-lpc mux: make device_type const char: virtio: constify attribute_group structures. Documentation/ABI: document the nvmem sysfs files lkdtm: fix spelling mistake: "incremeted" -> "incremented" perf: cs-etm: Fix ETMv4 CONFIGR entry in perf.data file nvmem: include linux/err.h from header ...
Unified · Split
| @@ -0,0 +1,19 @@ | ||
| 1 | +What: /sys/bus/nvmem/devices/.../nvmem | |
| 2 | +Date: July 2015 | |
| 3 | +KernelVersion: 4.2 | |
| 4 | +Contact: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> | |
| 5 | +Description: | |
| 6 | + This file allows user to read/write the raw NVMEM contents. | |
| 7 | + Permissions for write to this file depends on the nvmem | |
| 8 | + provider configuration. | |
| 9 | + | |
| 10 | + ex: | |
| 11 | + hexdump /sys/bus/nvmem/devices/qfprom0/nvmem | |
| 12 | + | |
| 13 | + 0000000 0000 0000 0000 0000 0000 0000 0000 0000 | |
| 14 | + * | |
| 15 | + 00000a0 db10 2240 0000 e000 0c00 0c00 0000 0c00 | |
| 16 | + 0000000 0000 0000 0000 0000 0000 0000 0000 0000 | |
| 17 | + ... | |
| 18 | + * | |
| 19 | + 0001000 | |
| @@ -45,6 +45,8 @@ Contact: thunderbolt-software@lists.01.org · 44 unmodified lines | ||
| 45 | 45 | Description: When a devices supports Thunderbolt secure connect it will |
| 46 | 46 | have this attribute. Writing 32 byte hex string changes |
| 47 | 47 | authorization to use the secure connection method instead. |
| 48 | + Writing an empty string clears the key and regular connection | |
| 49 | + method can be used again. | |
| 48 | 50 | |
| 49 | 51 | What: /sys/bus/thunderbolt/devices/.../device |
| 50 | 52 | Date: Sep 2017 |
| @@ -0,0 +1,8 @@ | ||
| 1 | +What: /sys/bus/pci/drivers/altera-cvp/chkcfg | |
| 2 | +Date: May 2017 | |
| 3 | +Kernel Version: 4.13 | |
| 4 | +Contact: Anatolij Gustschin <agust@denx.de> | |
| 5 | +Description: | |
| 6 | + Contains either 1 or 0 and controls if configuration | |
| 7 | + error checking in altera-cvp driver is turned on or | |
| 8 | + off. | |
| @@ -3081,3 +3081,8 @@ · 3080 unmodified lines | ||
| 3081 | 3081 | 1 = /dev/osd1 Second OSD Device |
| 3082 | 3082 | ... |
| 3083 | 3083 | 255 = /dev/osd255 256th OSD Device |
| 3084 | + | |
| 3085 | + 384-511 char RESERVED FOR DYNAMIC ASSIGNMENT | |
| 3086 | + Character devices that request a dynamic allocation of major | |
| 3087 | + number will take numbers starting from 511 and downward, | |
| 3088 | + once the 234-254 range is full. | |
| @@ -34,8 +34,8 @@ its hardware characteristcs. · 33 unmodified lines | ||
| 34 | 34 | - Embedded Trace Macrocell (version 4.x): |
| 35 | 35 | "arm,coresight-etm4x", "arm,primecell"; |
| 36 | 36 | |
| 37 | − - Qualcomm Configurable Replicator (version 1.x): | |
| 38 | − "qcom,coresight-replicator1x", "arm,primecell"; | |
| 37 | + - Coresight programmable Replicator : | |
| 38 | + "arm,coresight-dynamic-replicator", "arm,primecell"; | |
| 39 | 39 | |
| 40 | 40 | - System Trace Macrocell: |
| 41 | 41 | "arm,coresight-stm", "arm,primecell"; [1] |
| @@ -0,0 +1,24 @@ | ||
| 1 | +ChromeOS EC USB Type-C cable and accessories detection | |
| 2 | + | |
| 3 | +On ChromeOS systems with USB Type C ports, the ChromeOS Embedded Controller is | |
| 4 | +able to detect the state of external accessories such as display adapters | |
| 5 | +or USB devices when said accessories are attached or detached. | |
| 6 | + | |
| 7 | +The node for this device must be under a cros-ec node like google,cros-ec-spi | |
| 8 | +or google,cros-ec-i2c. | |
| 9 | + | |
| 10 | +Required properties: | |
| 11 | +- compatible: Should be "google,extcon-usbc-cros-ec". | |
| 12 | +- google,usb-port-id: Specifies the USB port ID to use. | |
| 13 | + | |
| 14 | +Example: | |
| 15 | + cros-ec@0 { | |
| 16 | + compatible = "google,cros-ec-i2c"; | |
| 17 | + | |
| 18 | + ... | |
| 19 | + | |
| 20 | + extcon { | |
| 21 | + compatible = "google,extcon-usbc-cros-ec"; | |
| 22 | + google,usb-port-id = <0>; | |
| 23 | + }; | |
| 24 | + } | |
| @@ -0,0 +1,29 @@ | ||
| 1 | +Altera Passive Serial SPI FPGA Manager | |
| 2 | + | |
| 3 | +Altera FPGAs support a method of loading the bitstream over what is | |
| 4 | +referred to as "passive serial". | |
| 5 | +The passive serial link is not technically SPI, and might require extra | |
| 6 | +circuits in order to play nicely with other SPI slaves on the same bus. | |
| 7 | + | |
| 8 | +See https://www.altera.com/literature/hb/cyc/cyc_c51013.pdf | |
| 9 | + | |
| 10 | +Required properties: | |
| 11 | +- compatible: Must be one of the following: | |
| 12 | + "altr,fpga-passive-serial", | |
| 13 | + "altr,fpga-arria10-passive-serial" | |
| 14 | +- reg: SPI chip select of the FPGA | |
| 15 | +- nconfig-gpios: config pin (referred to as nCONFIG in the manual) | |
| 16 | +- nstat-gpios: status pin (referred to as nSTATUS in the manual) | |
| 17 | + | |
| 18 | +Optional properties: | |
| 19 | +- confd-gpios: confd pin (referred to as CONF_DONE in the manual) | |
| 20 | + | |
| 21 | +Example: | |
| 22 | + fpga: fpga@0 { | |
| 23 | + compatible = "altr,fpga-passive-serial"; | |
| 24 | + spi-max-frequency = <20000000>; | |
| 25 | + reg = <0>; | |
| 26 | + nconfig-gpios = <&gpio4 9 GPIO_ACTIVE_LOW>; | |
| 27 | + nstat-gpios = <&gpio4 11 GPIO_ACTIVE_LOW>; | |
| 28 | + confd-gpios = <&gpio4 12 GPIO_ACTIVE_LOW>; | |
| 29 | + }; | |
| @@ -0,0 +1,36 @@ | ||
| 1 | +Xilinx LogiCORE Partial Reconfig Decoupler Softcore | |
| 2 | + | |
| 3 | +The Xilinx LogiCORE Partial Reconfig Decoupler manages one or more | |
| 4 | +decouplers / fpga bridges. | |
| 5 | +The controller can decouple/disable the bridges which prevents signal | |
| 6 | +changes from passing through the bridge. The controller can also | |
| 7 | +couple / enable the bridges which allows traffic to pass through the | |
| 8 | +bridge normally. | |
| 9 | + | |
| 10 | +The Driver supports only MMIO handling. A PR region can have multiple | |
| 11 | +PR Decouplers which can be handled independently or chained via decouple/ | |
| 12 | +decouple_status signals. | |
| 13 | + | |
| 14 | +Required properties: | |
| 15 | +- compatible : Should contain "xlnx,pr-decoupler-1.00" followed by | |
| 16 | + "xlnx,pr-decoupler" | |
| 17 | +- regs : base address and size for decoupler module | |
| 18 | +- clocks : input clock to IP | |
| 19 | +- clock-names : should contain "aclk" | |
| 20 | + | |
| 21 | +Optional properties: | |
| 22 | +- bridge-enable : 0 if driver should disable bridge at startup | |
| 23 | + 1 if driver should enable bridge at startup | |
| 24 | + Default is to leave bridge in current state. | |
| 25 | + | |
| 26 | +See Documentation/devicetree/bindings/fpga/fpga-region.txt for generic bindings. | |
| 27 | + | |
| 28 | +Example: | |
| 29 | + fpga-bridge@100000450 { | |
| 30 | + compatible = "xlnx,pr-decoupler-1.00", | |
| 31 | + "xlnx-pr-decoupler"; | |
| 32 | + regs = <0x10000045 0x10>; | |
| 33 | + clocks = <&clkc 15>; | |
| 34 | + clock-names = "aclk"; | |
| 35 | + bridge-enable = <0>; | |
| 36 | + }; | |
| @@ -175,6 +175,7 @@ kosagi Sutajio Ko-Usagi PTE Ltd. · 174 unmodified lines | ||
| 175 | 175 | kyo Kyocera Corporation |
| 176 | 176 | lacie LaCie |
| 177 | 177 | lantiq Lantiq Semiconductor |
| 178 | +lattice Lattice Semiconductor | |
| 178 | 179 | lego LEGO Systems A/S |
| 179 | 180 | lenovo Lenovo Group Ltd. |
| 180 | 181 | lg LG Corporation |
| @@ -281,6 +281,8 @@ · 280 unmodified lines | ||
| 281 | 281 | capabilities of the underlying ICAP hardware |
| 282 | 282 | differ between different families. May be |
| 283 | 283 | 'virtex2p', 'virtex4', or 'virtex5'. |
| 284 | + - compatible : should contain "xlnx,xps-hwicap-1.00.a" or | |
| 285 | + "xlnx,opb-hwicap-1.00.b". | |
| 284 | 286 | |
| 285 | 287 | vi) Xilinx Uart 16550 |
| 286 | 288 | |
| @@ -83,7 +83,7 @@ by writing the name of the desired stm device there, for example: · 82 unmodified lines | ||
| 83 | 83 | $ echo dummy_stm.0 > /sys/class/stm_source/console/stm_source_link |
| 84 | 84 | |
| 85 | 85 | For examples on how to use stm_source interface in the kernel, refer |
| 86 | −to stm_console or stm_heartbeat drivers. | |
| 86 | +to stm_console, stm_heartbeat or stm_ftrace drivers. | |
| 87 | 87 | |
| 88 | 88 | Each stm_source device will need to assume a master and a range of |
| 89 | 89 | channels, depending on how many channels it requires. These are |
| @@ -107,5 +107,16 @@ console in the STP stream, create a "console" policy entry (see the · 17 unmodified lines | ||
| 107 | 107 | beginning of this text on how to do that). When initialized, it will |
| 108 | 108 | consume one channel. |
| 109 | 109 | |
| 110 | +stm_ftrace | |
| 111 | +========== | |
| 112 | + | |
| 113 | +This is another "stm_source" device, once the stm_ftrace has been | |
| 114 | +linked with an stm device, and if "function" tracer is enabled, | |
| 115 | +function address and parent function address which Ftrace subsystem | |
| 116 | +would store into ring buffer will be exported via the stm device at | |
| 117 | +the same time. | |
| 118 | + | |
| 119 | +Currently only Ftrace "function" tracer is supported. | |
| 120 | + | |
| 110 | 121 | [1] https://software.intel.com/sites/default/files/managed/d3/3c/intel-th-developer-manual.pdf |
| 111 | 122 | [2] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0444b/index.html |
| @@ -5362,10 +5362,11 @@ K: fmc_d.*register · 5361 unmodified lines | ||
| 5362 | 5362 | |
| 5363 | 5363 | FPGA MANAGER FRAMEWORK |
| 5364 | 5364 | M: Alan Tull <atull@kernel.org> |
| 5365 | −R: Moritz Fischer <moritz.fischer@ettus.com> | |
| 5365 | +R: Moritz Fischer <mdf@kernel.org> | |
| 5366 | 5366 | L: linux-fpga@vger.kernel.org |
| 5367 | 5367 | S: Maintained |
| 5368 | 5368 | T: git git://git.kernel.org/pub/scm/linux/kernel/git/atull/linux-fpga.git |
| 5369 | +Q: http://patchwork.kernel.org/project/linux-fpga/list/ | |
| 5369 | 5370 | F: Documentation/fpga/ |
| 5370 | 5371 | F: Documentation/devicetree/bindings/fpga/ |
| 5371 | 5372 | F: drivers/fpga/ |
| @@ -9484,6 +9485,7 @@ M: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> · 4112 unmodified lines | ||
| 9484 | 9485 | S: Maintained |
| 9485 | 9486 | F: drivers/nvmem/ |
| 9486 | 9487 | F: Documentation/devicetree/bindings/nvmem/ |
| 9488 | +F: Documentation/ABI/stable/sysfs-bus-nvmem | |
| 9487 | 9489 | F: include/linux/nvmem-consumer.h |
| 9488 | 9490 | F: include/linux/nvmem-provider.h |
| 9489 | 9491 | |
| @@ -94,6 +94,15 @@ · 93 unmodified lines | ||
| 94 | 94 | pinctrl-names = "default"; |
| 95 | 95 | pinctrl-0 = <&pinctrl_ecspi1 &pinctrl_ecspi1cs>; |
| 96 | 96 | status = "okay"; |
| 97 | + | |
| 98 | + fpga: fpga@0 { | |
| 99 | + compatible = "altr,fpga-passive-serial"; | |
| 100 | + spi-max-frequency = <20000000>; | |
| 101 | + reg = <0>; | |
| 102 | + pinctrl-0 = <&pinctrl_fpgaspi>; | |
| 103 | + nconfig-gpios = <&gpio4 9 GPIO_ACTIVE_LOW>; | |
| 104 | + nstat-gpios = <&gpio4 11 GPIO_ACTIVE_LOW>; | |
| 105 | + }; | |
| 97 | 106 | }; |
| 98 | 107 | |
| 99 | 108 | &ecspi3 { |
| @@ -319,6 +328,13 @@ · 219 unmodified lines | ||
| 319 | 328 | >; |
| 320 | 329 | }; |
| 321 | 330 | |
| 331 | + pinctrl_fpgaspi: fpgaspigrp { | |
| 332 | + fsl,pins = < | |
| 333 | + MX6QDL_PAD_KEY_ROW1__GPIO4_IO09 0x1b0b0 | |
| 334 | + MX6QDL_PAD_KEY_ROW2__GPIO4_IO11 0x1b0b0 | |
| 335 | + >; | |
| 336 | + }; | |
| 337 | + | |
| 322 | 338 | pinctrl_gpminand: gpminandgrp { |
| 323 | 339 | fsl,pins = < |
| 324 | 340 | MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1 |
| @@ -28,6 +28,8 @@ struct ms_hyperv_info { · 27 unmodified lines | ||
| 28 | 28 | u32 features; |
| 29 | 29 | u32 misc_features; |
| 30 | 30 | u32 hints; |
| 31 | + u32 max_vp_index; | |
| 32 | + u32 max_lp_index; | |
| 31 | 33 | }; |
| 32 | 34 | |
| 33 | 35 | extern struct ms_hyperv_info ms_hyperv; |
| @@ -179,9 +179,15 @@ static void __init ms_hyperv_init_platform(void) · 178 unmodified lines | ||
| 179 | 179 | ms_hyperv.misc_features = cpuid_edx(HYPERV_CPUID_FEATURES); |
| 180 | 180 | ms_hyperv.hints = cpuid_eax(HYPERV_CPUID_ENLIGHTMENT_INFO); |
| 181 | 181 | |
| 182 | − pr_info("HyperV: features 0x%x, hints 0x%x\n", | |
| 182 | + pr_info("Hyper-V: features 0x%x, hints 0x%x\n", | |
| 183 | 183 | ms_hyperv.features, ms_hyperv.hints); |
| 184 | 184 | |
| 185 | + ms_hyperv.max_vp_index = cpuid_eax(HVCPUID_IMPLEMENTATION_LIMITS); | |
| 186 | + ms_hyperv.max_lp_index = cpuid_ebx(HVCPUID_IMPLEMENTATION_LIMITS); | |
| 187 | + | |
| 188 | + pr_debug("Hyper-V: max %u virtual processors, %u logical processors\n", | |
| 189 | + ms_hyperv.max_vp_index, ms_hyperv.max_lp_index); | |
| 190 | + | |
| 185 | 191 | /* |
| 186 | 192 | * Extract host information. |
| 187 | 193 | */ |
| @@ -214,7 +220,7 @@ static void __init ms_hyperv_init_platform(void) · 26 unmodified lines | ||
| 214 | 220 | rdmsrl(HV_X64_MSR_APIC_FREQUENCY, hv_lapic_frequency); |
| 215 | 221 | hv_lapic_frequency = div_u64(hv_lapic_frequency, HZ); |
| 216 | 222 | lapic_timer_frequency = hv_lapic_frequency; |
| 217 | − pr_info("HyperV: LAPIC Timer Frequency: %#x\n", | |
| 223 | + pr_info("Hyper-V: LAPIC Timer Frequency: %#x\n", | |
| 218 | 224 | lapic_timer_frequency); |
| 219 | 225 | } |
| 220 | 226 | |
| @@ -248,7 +254,7 @@ static void __init ms_hyperv_init_platform(void) · 27 unmodified lines | ||
| 248 | 254 | } |
| 249 | 255 | |
| 250 | 256 | const __refconst struct hypervisor_x86 x86_hyper_ms_hyperv = { |
| 251 | − .name = "Microsoft HyperV", | |
| 257 | + .name = "Microsoft Hyper-V", | |
| 252 | 258 | .detect = ms_hyperv_platform, |
| 253 | 259 | .init_platform = ms_hyperv_init_platform, |
| 254 | 260 | }; |
| @@ -242,6 +242,7 @@ EXPORT_SYMBOL_GPL(disk_map_sector_rcu); · 241 unmodified lines | ||
| 242 | 242 | * Can be deleted altogether. Later. |
| 243 | 243 | * |
| 244 | 244 | */ |
| 245 | +#define BLKDEV_MAJOR_HASH_SIZE 255 | |
| 245 | 246 | static struct blk_major_name { |
| 246 | 247 | struct blk_major_name *next; |
| 247 | 248 | int major; |
| @@ -259,12 +260,11 @@ void blkdev_show(struct seq_file *seqf, off_t offset) · 11 unmodified lines | ||
| 259 | 260 | { |
| 260 | 261 | struct blk_major_name *dp; |
| 261 | 262 | |
| 262 | − if (offset < BLKDEV_MAJOR_HASH_SIZE) { | |
| 263 | − mutex_lock(&block_class_lock); | |
| 264 | − for (dp = major_names[offset]; dp; dp = dp->next) | |
| 263 | + mutex_lock(&block_class_lock); | |
| 264 | + for (dp = major_names[major_to_index(offset)]; dp; dp = dp->next) | |
| 265 | + if (dp->major == offset) | |
| 265 | 266 | seq_printf(seqf, "%3d %s\n", dp->major, dp->name); |
| 266 | − mutex_unlock(&block_class_lock); | |
| 267 | − } | |
| 267 | + mutex_unlock(&block_class_lock); | |
| 268 | 268 | } |
| 269 | 269 | #endif /* CONFIG_PROC_FS */ |
| 270 | 270 | |
| @@ -309,6 +309,14 @@ int register_blkdev(unsigned int major, const char *name) · 38 unmodified lines | ||
| 309 | 309 | ret = major; |
| 310 | 310 | } |
| 311 | 311 | |
| 312 | + if (major >= BLKDEV_MAJOR_MAX) { | |
| 313 | + pr_err("register_blkdev: major requested (%d) is greater than the maximum (%d) for %s\n", | |
| 314 | + major, BLKDEV_MAJOR_MAX, name); | |
| 315 | + | |
| 316 | + ret = -EINVAL; | |
| 317 | + goto out; | |
| 318 | + } | |
| 319 | + | |
| 312 | 320 | p = kmalloc(sizeof(struct blk_major_name), GFP_KERNEL); |
| 313 | 321 | if (p == NULL) { |
| 314 | 322 | ret = -ENOMEM; |
| @@ -22,7 +22,7 @@ config ANDROID_BINDER_IPC · 21 unmodified lines | ||
| 22 | 22 | config ANDROID_BINDER_DEVICES |
| 23 | 23 | string "Android Binder devices" |
| 24 | 24 | depends on ANDROID_BINDER_IPC |
| 25 | − default "binder,hwbinder" | |
| 25 | + default "binder,hwbinder,vndbinder" | |
| 26 | 26 | ---help--- |
| 27 | 27 | Default value for the binder.devices parameter. |
| 28 | 28 | |
| @@ -32,7 +32,7 @@ config ANDROID_BINDER_DEVICES · 3 unmodified lines | ||
| 32 | 32 | therefore logically separated from the other devices. |
| 33 | 33 | |
| 34 | 34 | config ANDROID_BINDER_IPC_32BIT |
| 35 | − bool | |
| 35 | + bool "Use old (Android 4.4 and earlier) 32-bit binder API" | |
| 36 | 36 | depends on !64BIT && ANDROID_BINDER_IPC |
| 37 | 37 | default y |
| 38 | 38 | ---help--- |
| @@ -44,6 +44,16 @@ config ANDROID_BINDER_IPC_32BIT · 5 unmodified lines | ||
| 44 | 44 | |
| 45 | 45 | Note that enabling this will break newer Android user-space. |
| 46 | 46 | |
| 47 | +config ANDROID_BINDER_IPC_SELFTEST | |
| 48 | + bool "Android Binder IPC Driver Selftest" | |
| 49 | + depends on ANDROID_BINDER_IPC | |
| 50 | + ---help--- | |
| 51 | + This feature allows binder selftest to run. | |
| 52 | + | |
| 53 | + Binder selftest checks the allocation and free of binder buffers | |
| 54 | + exhaustively with combinations of various buffer sizes and | |
| 55 | + alignments. | |
| 56 | + | |
| 47 | 57 | endif # if ANDROID |
| 48 | 58 | |
| 49 | 59 | endmenu |
| @@ -1,3 +1,4 @@ | ||
| 1 | 1 | ccflags-y += -I$(src) # needed for trace events |
| 2 | 2 | |
| 3 | −obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o | |
| 3 | +obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o binder_alloc.o | |
| 4 | +obj-$(CONFIG_ANDROID_BINDER_IPC_SELFTEST) += binder_alloc_selftest.o | |
| @@ -15,6 +15,40 @@ · 14 unmodified lines | ||
| 15 | 15 | * |
| 16 | 16 | */ |
| 17 | 17 | |
| 18 | +/* | |
| 19 | + * Locking overview | |
| 20 | + * | |
| 21 | + * There are 3 main spinlocks which must be acquired in the | |
| 22 | + * order shown: | |
| 23 | + * | |
| 24 | + * 1) proc->outer_lock : protects binder_ref | |
| 25 | + * binder_proc_lock() and binder_proc_unlock() are | |
| 26 | + * used to acq/rel. | |
| 27 | + * 2) node->lock : protects most fields of binder_node. | |
| 28 | + * binder_node_lock() and binder_node_unlock() are | |
| 29 | + * used to acq/rel | |
| 30 | + * 3) proc->inner_lock : protects the thread and node lists | |
| 31 | + * (proc->threads, proc->waiting_threads, proc->nodes) | |
| 32 | + * and all todo lists associated with the binder_proc | |
| 33 | + * (proc->todo, thread->todo, proc->delivered_death and | |
| 34 | + * node->async_todo), as well as thread->transaction_stack | |
| 35 | + * binder_inner_proc_lock() and binder_inner_proc_unlock() | |
| 36 | + * are used to acq/rel | |
| 37 | + * | |
| 38 | + * Any lock under procA must never be nested under any lock at the same | |
| 39 | + * level or below on procB. | |
| 40 | + * | |
| 41 | + * Functions that require a lock held on entry indicate which lock | |
| 42 | + * in the suffix of the function name: | |
| 43 | + * | |
| 44 | + * foo_olocked() : requires node->outer_lock | |
| 45 | + * foo_nlocked() : requires node->lock | |
| 46 | + * foo_ilocked() : requires proc->inner_lock | |
| 47 | + * foo_oilocked(): requires proc->outer_lock and proc->inner_lock | |
| 48 | + * foo_nilocked(): requires node->lock and proc->inner_lock | |
| 49 | + * ... | |
| 50 | + */ | |
| 51 | + | |
| 18 | 52 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| 19 | 53 | |
| 20 | 54 | #include <asm/cacheflush.h> |
| @@ -24,7 +58,6 @@ · 3 unmodified lines | ||
| 24 | 58 | #include <linux/fs.h> |
| 25 | 59 | #include <linux/list.h> |
| 26 | 60 | #include <linux/miscdevice.h> |
| 27 | −#include <linux/mm.h> | |
| 28 | 61 | #include <linux/module.h> |
| 29 | 62 | #include <linux/mutex.h> |
| 30 | 63 | #include <linux/nsproxy.h> |
| @@ -35,30 +68,31 @@ · 4 unmodified lines | ||
| 35 | 68 | #include <linux/sched/mm.h> |
| 36 | 69 | #include <linux/seq_file.h> |
| 37 | 70 | #include <linux/uaccess.h> |
| 38 | −#include <linux/vmalloc.h> | |
| 39 | −#include <linux/slab.h> | |
| 40 | 71 | #include <linux/pid_namespace.h> |
| 41 | 72 | #include <linux/security.h> |
| 73 | +#include <linux/spinlock.h> | |
| 42 | 74 | |
| 43 | 75 | #ifdef CONFIG_ANDROID_BINDER_IPC_32BIT |
| 44 | 76 | #define BINDER_IPC_32BIT 1 |
| 45 | 77 | #endif |
| 46 | 78 | |
| 47 | 79 | #include <uapi/linux/android/binder.h> |
| 80 | +#include "binder_alloc.h" | |
| 48 | 81 | #include "binder_trace.h" |
| 49 | 82 | |
| 50 | −static DEFINE_MUTEX(binder_main_lock); | |
| 83 | +static HLIST_HEAD(binder_deferred_list); | |
| 51 | 84 | static DEFINE_MUTEX(binder_deferred_lock); |
| 52 | −static DEFINE_MUTEX(binder_mmap_lock); | |
| 53 | 85 | |
| 54 | 86 | static HLIST_HEAD(binder_devices); |
| 55 | 87 | static HLIST_HEAD(binder_procs); |
| 56 | −static HLIST_HEAD(binder_deferred_list); | |
| 88 | +static DEFINE_MUTEX(binder_procs_lock); | |
| 89 | + | |
| 57 | 90 | static HLIST_HEAD(binder_dead_nodes); |
| 91 | +static DEFINE_SPINLOCK(binder_dead_nodes_lock); | |
| 58 | 92 | |
| 59 | 93 | static struct dentry *binder_debugfs_dir_entry_root; |
| 60 | 94 | static struct dentry *binder_debugfs_dir_entry_proc; |
| 61 | −static int binder_last_id; | |
| 95 | +static atomic_t binder_last_id; | |
| 62 | 96 | |
| 63 | 97 | #define BINDER_DEBUG_ENTRY(name) \ |
| 64 | 98 | static int binder_##name##_open(struct inode *inode, struct file *file) \ |
| @@ -88,8 +122,6 @@ BINDER_DEBUG_ENTRY(proc); · 23 unmodified lines | ||
| 88 | 122 | |
| 89 | 123 | #define FORBIDDEN_MMAP_FLAGS (VM_WRITE) |
| 90 | 124 | |
| 91 | −#define BINDER_SMALL_BUF_SIZE (PAGE_SIZE * 64) | |
| 92 | − | |
| 93 | 125 | enum { |
| 94 | 126 | BINDER_DEBUG_USER_ERROR = 1U << 0, |
| 95 | 127 | BINDER_DEBUG_FAILED_TRANSACTION = 1U << 1, |
| @@ -104,17 +136,13 @@ enum { · 8 unmodified lines | ||
| 104 | 136 | BINDER_DEBUG_TRANSACTION_COMPLETE = 1U << 10, |
| 105 | 137 | BINDER_DEBUG_FREE_BUFFER = 1U << 11, |
| 106 | 138 | BINDER_DEBUG_INTERNAL_REFS = 1U << 12, |
| 107 | − BINDER_DEBUG_BUFFER_ALLOC = 1U << 13, | |
| 108 | − BINDER_DEBUG_PRIORITY_CAP = 1U << 14, | |
| 109 | − BINDER_DEBUG_BUFFER_ALLOC_ASYNC = 1U << 15, | |
| 139 | + BINDER_DEBUG_PRIORITY_CAP = 1U << 13, | |
| 140 | + BINDER_DEBUG_SPINLOCKS = 1U << 14, | |
| 110 | 141 | }; |
| 111 | 142 | static uint32_t binder_debug_mask = BINDER_DEBUG_USER_ERROR | |
| 112 | 143 | BINDER_DEBUG_FAILED_TRANSACTION | BINDER_DEBUG_DEAD_TRANSACTION; |
| 113 | 144 | module_param_named(debug_mask, binder_debug_mask, uint, S_IWUSR | S_IRUGO); |
| 114 | 145 | |
| 115 | −static bool binder_debug_no_lock; | |
| 116 | −module_param_named(proc_no_lock, binder_debug_no_lock, bool, S_IWUSR | S_IRUGO); | |
| 117 | − | |
| 118 | 146 | static char *binder_devices_param = CONFIG_ANDROID_BINDER_DEVICES; |
| 119 | 147 | module_param_named(devices, binder_devices_param, charp, 0444); |
| 120 | 148 | |
| @@ -171,26 +199,27 @@ enum binder_stat_types { · 50 unmodified lines | ||
| 171 | 199 | }; |
| 172 | 200 | |
| 173 | 201 | struct binder_stats { |
| 174 | − int br[_IOC_NR(BR_FAILED_REPLY) + 1]; | |
| 175 | − int bc[_IOC_NR(BC_REPLY_SG) + 1]; | |
| 176 | − int obj_created[BINDER_STAT_COUNT]; | |
| 177 | − int obj_deleted[BINDER_STAT_COUNT]; | |
| 202 | + atomic_t br[_IOC_NR(BR_FAILED_REPLY) + 1]; | |
| 203 | + atomic_t bc[_IOC_NR(BC_REPLY_SG) + 1]; | |
| 204 | + atomic_t obj_created[BINDER_STAT_COUNT]; | |
| 205 | + atomic_t obj_deleted[BINDER_STAT_COUNT]; | |
| 178 | 206 | }; |
| 179 | 207 | |
| 180 | 208 | static struct binder_stats binder_stats; |
| 181 | 209 | |
| 182 | 210 | static inline void binder_stats_deleted(enum binder_stat_types type) |
| 183 | 211 | { |
| 184 | − binder_stats.obj_deleted[type]++; | |
| 212 | + atomic_inc(&binder_stats.obj_deleted[type]); | |
| 185 | 213 | } |
| 186 | 214 | |
| 187 | 215 | static inline void binder_stats_created(enum binder_stat_types type) |
| 188 | 216 | { |
| 189 | − binder_stats.obj_created[type]++; | |
| 217 | + atomic_inc(&binder_stats.obj_created[type]); | |
| 190 | 218 | } |
| 191 | 219 | |
| 192 | 220 | struct binder_transaction_log_entry { |
| 193 | 221 | int debug_id; |
| 222 | + int debug_id_done; | |
| 194 | 223 | int call_type; |
| 195 | 224 | int from_proc; |
| 196 | 225 | int from_thread; |
| @@ -200,11 +229,14 @@ struct binder_transaction_log_entry { · 3 unmodified lines | ||
| 200 | 229 | int to_node; |
| 201 | 230 | int data_size; |
| 202 | 231 | int offsets_size; |
| 232 | + int return_error_line; | |
| 233 | + uint32_t return_error; | |
| 234 | + uint32_t return_error_param; | |
| 203 | 235 | const char *context_name; |
| 204 | 236 | }; |
| 205 | 237 | struct binder_transaction_log { |
| 206 | − int next; | |
| 207 | − int full; | |
| 238 | + atomic_t cur; | |
| 239 | + bool full; | |
| 208 | 240 | struct binder_transaction_log_entry entry[32]; |
| 209 | 241 | }; |
| 210 | 242 | static struct binder_transaction_log binder_transaction_log; |
| @@ -214,19 +246,26 @@ static struct binder_transaction_log_entry *binder_transaction_log_add( · 3 unmodified lines | ||
| 214 | 246 | struct binder_transaction_log *log) |
| 215 | 247 | { |
| 216 | 248 | struct binder_transaction_log_entry *e; |
| 249 | + unsigned int cur = atomic_inc_return(&log->cur); | |
| 217 | 250 | |
| 218 | − e = &log->entry[log->next]; | |
| 219 | − memset(e, 0, sizeof(*e)); | |
| 220 | − log->next++; | |
| 221 | − if (log->next == ARRAY_SIZE(log->entry)) { | |
| 222 | − log->next = 0; | |
| 251 | + if (cur >= ARRAY_SIZE(log->entry)) | |
| 223 | 252 | log->full = 1; |
| 224 | − } | |
| 253 | + e = &log->entry[cur % ARRAY_SIZE(log->entry)]; | |
| 254 | + WRITE_ONCE(e->debug_id_done, 0); | |
| 255 | + /* | |
| 256 | + * write-barrier to synchronize access to e->debug_id_done. | |
| 257 | + * We make sure the initialized 0 value is seen before | |
| 258 | + * memset() other fields are zeroed by memset. | |
| 259 | + */ | |
| 260 | + smp_wmb(); | |
| 261 | + memset(e, 0, sizeof(*e)); | |
| 225 | 262 | return e; |
| 226 | 263 | } |
| 227 | 264 | |
| 228 | 265 | struct binder_context { |
| 229 | 266 | struct binder_node *binder_context_mgr_node; |
| 267 | + struct mutex context_mgr_node_lock; | |
| 268 | + | |
| 230 | 269 | kuid_t binder_context_mgr_uid; |
| 231 | 270 | const char *name; |
| 232 | 271 | }; |
| @@ -237,11 +276,20 @@ struct binder_device { · 4 unmodified lines | ||
| 237 | 276 | struct binder_context context; |
| 238 | 277 | }; |
| 239 | 278 | |
| 279 | +/** | |
| 280 | + * struct binder_work - work enqueued on a worklist | |
| 281 | + * @entry: node enqueued on list | |
| 282 | + * @type: type of work to be performed | |
| 283 | + * | |
| 284 | + * There are separate work lists for proc, thread, and node (async). | |
| 285 | + */ | |
| 240 | 286 | struct binder_work { |
| 241 | 287 | struct list_head entry; |
| 288 | + | |
| 242 | 289 | enum { |
| 243 | 290 | BINDER_WORK_TRANSACTION = 1, |
| 244 | 291 | BINDER_WORK_TRANSACTION_COMPLETE, |
| 292 | + BINDER_WORK_RETURN_ERROR, | |
| 245 | 293 | BINDER_WORK_NODE, |
| 246 | 294 | BINDER_WORK_DEAD_BINDER, |
| 247 | 295 | BINDER_WORK_DEAD_BINDER_AND_CLEAR, |
| @@ -249,8 +297,72 @@ struct binder_work { · 1 unmodified line | ||
| 249 | 297 | } type; |
| 250 | 298 | }; |
| 251 | 299 | |
| 300 | +struct binder_error { | |
| 301 | + struct binder_work work; | |
| 302 | + uint32_t cmd; | |
| 303 | +}; | |
| 304 | + | |
| 305 | +/** | |
| 306 | + * struct binder_node - binder node bookkeeping | |
| 307 | + * @debug_id: unique ID for debugging | |
| 308 | + * (invariant after initialized) | |
| 309 | + * @lock: lock for node fields | |
| 310 | + * @work: worklist element for node work | |
| 311 | + * (protected by @proc->inner_lock) | |
| 312 | + * @rb_node: element for proc->nodes tree | |
| 313 | + * (protected by @proc->inner_lock) | |
| 314 | + * @dead_node: element for binder_dead_nodes list | |
| 315 | + * (protected by binder_dead_nodes_lock) | |
| 316 | + * @proc: binder_proc that owns this node | |
| 317 | + * (invariant after initialized) | |
| 318 | + * @refs: list of references on this node | |
| 319 | + * (protected by @lock) | |
| 320 | + * @internal_strong_refs: used to take strong references when | |
| 321 | + * initiating a transaction | |
| 322 | + * (protected by @proc->inner_lock if @proc | |
| 323 | + * and by @lock) | |
| 324 | + * @local_weak_refs: weak user refs from local process | |
| 325 | + * (protected by @proc->inner_lock if @proc | |
| 326 | + * and by @lock) | |
| 327 | + * @local_strong_refs: strong user refs from local process | |
| 328 | + * (protected by @proc->inner_lock if @proc | |
| 329 | + * and by @lock) | |
| 330 | + * @tmp_refs: temporary kernel refs | |
| 331 | + * (protected by @proc->inner_lock while @proc | |
| 332 | + * is valid, and by binder_dead_nodes_lock | |
| 333 | + * if @proc is NULL. During inc/dec and node release | |
| 334 | + * it is also protected by @lock to provide safety | |
| 335 | + * as the node dies and @proc becomes NULL) | |
| 336 | + * @ptr: userspace pointer for node | |
| 337 | + * (invariant, no lock needed) | |
| 338 | + * @cookie: userspace cookie for node | |
| 339 | + * (invariant, no lock needed) | |
| 340 | + * @has_strong_ref: userspace notified of strong ref | |
| 341 | + * (protected by @proc->inner_lock if @proc | |
| 342 | + * and by @lock) | |
| 343 | + * @pending_strong_ref: userspace has acked notification of strong ref | |
| 344 | + * (protected by @proc->inner_lock if @proc | |
| 345 | + * and by @lock) | |
| 346 | + * @has_weak_ref: userspace notified of weak ref | |
| 347 | + * (protected by @proc->inner_lock if @proc | |
| 348 | + * and by @lock) | |
| 349 | + * @pending_weak_ref: userspace has acked notification of weak ref | |
| 350 | + * (protected by @proc->inner_lock if @proc | |
| 351 | + * and by @lock) | |
| 352 | + * @has_async_transaction: async transaction to node in progress | |
| 353 | + * (protected by @lock) | |
| 354 | + * @accept_fds: file descriptor operations supported for node | |
| 355 | + * (invariant after initialized) | |
| 356 | + * @min_priority: minimum scheduling priority | |
| 357 | + * (invariant after initialized) | |
| 358 | + * @async_todo: list of async work items | |
| 359 | + * (protected by @proc->inner_lock) | |
| 360 | + * | |
| 361 | + * Bookkeeping structure for binder nodes. | |
| 362 | + */ | |
| 252 | 363 | struct binder_node { |
| 253 | 364 | int debug_id; |
| 365 | + spinlock_t lock; | |
| 254 | 366 | struct binder_work work; |
| 255 | 367 | union { |
| 256 | 368 | struct rb_node rb_node; |
| @@ -261,88 +373,167 @@ struct binder_node { · 4 unmodified lines | ||
| 261 | 373 | int internal_strong_refs; |
| 262 | 374 | int local_weak_refs; |
| 263 | 375 | int local_strong_refs; |
| 376 | + int tmp_refs; | |
| 264 | 377 | binder_uintptr_t ptr; |
| 265 | 378 | binder_uintptr_t cookie; |
| 266 | − unsigned has_strong_ref:1; | |
| 267 | − unsigned pending_strong_ref:1; | |
| 268 | − unsigned has_weak_ref:1; | |
| 269 | − unsigned pending_weak_ref:1; | |
| 270 | − unsigned has_async_transaction:1; | |
| 271 | − unsigned accept_fds:1; | |
| 272 | − unsigned min_priority:8; | |
| 379 | + struct { | |
| 380 | + /* | |
| 381 | + * bitfield elements protected by | |
| 382 | + * proc inner_lock | |
| 383 | + */ | |
| 384 | + u8 has_strong_ref:1; | |
| 385 | + u8 pending_strong_ref:1; | |
| 386 | + u8 has_weak_ref:1; | |
| 387 | + u8 pending_weak_ref:1; | |
| 388 | + }; | |
| 389 | + struct { | |
| 390 | + /* | |
| 391 | + * invariant after initialization | |
| 392 | + */ | |
| 393 | + u8 accept_fds:1; | |
| 394 | + u8 min_priority; | |
| 395 | + }; | |
| 396 | + bool has_async_transaction; | |
| 273 | 397 | struct list_head async_todo; |
| 274 | 398 | }; |
| 275 | 399 | |
| 276 | 400 | struct binder_ref_death { |
| 401 | + /** | |
| 402 | + * @work: worklist element for death notifications | |
| 403 | + * (protected by inner_lock of the proc that | |
| 404 | + * this ref belongs to) | |
| 405 | + */ | |
| 277 | 406 | struct binder_work work; |
| 278 | 407 | binder_uintptr_t cookie; |
| 279 | 408 | }; |
| 280 | 409 | |
| 410 | +/** | |
| 411 | + * struct binder_ref_data - binder_ref counts and id | |
| 412 | + * @debug_id: unique ID for the ref | |
| 413 | + * @desc: unique userspace handle for ref | |
| 414 | + * @strong: strong ref count (debugging only if not locked) | |
| 415 | + * @weak: weak ref count (debugging only if not locked) | |
| 416 | + * | |
| 417 | + * Structure to hold ref count and ref id information. Since | |
| 418 | + * the actual ref can only be accessed with a lock, this structure | |
| 419 | + * is used to return information about the ref to callers of | |
| 420 | + * ref inc/dec functions. | |
| 421 | + */ | |
| 422 | +struct binder_ref_data { | |
| 423 | + int debug_id; | |
| 424 | + uint32_t desc; | |
| 425 | + int strong; | |
| 426 | + int weak; | |
| 427 | +}; | |
| 428 | + | |
| 429 | +/** | |
| 430 | + * struct binder_ref - struct to track references on nodes | |
| 431 | + * @data: binder_ref_data containing id, handle, and current refcounts | |
| 432 | + * @rb_node_desc: node for lookup by @data.desc in proc's rb_tree | |
| 433 | + * @rb_node_node: node for lookup by @node in proc's rb_tree | |
| 434 | + * @node_entry: list entry for node->refs list in target node | |
| 435 | + * (protected by @node->lock) | |
| 436 | + * @proc: binder_proc containing ref | |
| 437 | + * @node: binder_node of target node. When cleaning up a | |
| 438 | + * ref for deletion in binder_cleanup_ref, a non-NULL | |
| 439 | + * @node indicates the node must be freed | |
| 440 | + * @death: pointer to death notification (ref_death) if requested | |
| 441 | + * (protected by @node->lock) | |
| 442 | + * | |
| 443 | + * Structure to track references from procA to target node (on procB). This | |
| 444 | + * structure is unsafe to access without holding @proc->outer_lock. | |
| 445 | + */ | |
| 281 | 446 | struct binder_ref { |
| 282 | 447 | /* Lookups needed: */ |
| 283 | 448 | /* node + proc => ref (transaction) */ |
| 284 | 449 | /* desc + proc => ref (transaction, inc/dec ref) */ |
| 285 | 450 | /* node => refs + procs (proc exit) */ |
| 286 | − int debug_id; | |
| 451 | + struct binder_ref_data data; | |
| 287 | 452 | struct rb_node rb_node_desc; |
| 288 | 453 | struct rb_node rb_node_node; |
| 289 | 454 | struct hlist_node node_entry; |
| 290 | 455 | struct binder_proc *proc; |
| 291 | 456 | struct binder_node *node; |
| 292 | − uint32_t desc; | |
| 293 | − int strong; | |
| 294 | − int weak; | |
| 295 | 457 | struct binder_ref_death *death; |
| 296 | 458 | }; |
| 297 | 459 | |
| 298 | −struct binder_buffer { | |
| 299 | − struct list_head entry; /* free and allocated entries by address */ | |
| 300 | − struct rb_node rb_node; /* free entry by size or allocated entry */ | |
| 301 | − /* by address */ | |
| 302 | − unsigned free:1; | |
| 303 | − unsigned allow_user_free:1; | |
| 304 | − unsigned async_transaction:1; | |
| 305 | − unsigned debug_id:29; | |
| 306 | − | |
| 307 | − struct binder_transaction *transaction; | |
| 308 | − | |
| 309 | − struct binder_node *target_node; | |
| 310 | − size_t data_size; | |
| 311 | − size_t offsets_size; | |
| 312 | − size_t extra_buffers_size; | |
| 313 | − uint8_t data[0]; | |
| 314 | −}; | |
| 315 | − | |
| 316 | 460 | enum binder_deferred_state { |
| 317 | 461 | BINDER_DEFERRED_PUT_FILES = 0x01, |
| 318 | 462 | BINDER_DEFERRED_FLUSH = 0x02, |
| 319 | 463 | BINDER_DEFERRED_RELEASE = 0x04, |
| 320 | 464 | }; |
| 321 | 465 | |
| 466 | +/** | |
| 467 | + * struct binder_proc - binder process bookkeeping | |
| 468 | + * @proc_node: element for binder_procs list | |
| 469 | + * @threads: rbtree of binder_threads in this proc | |
| 470 | + * (protected by @inner_lock) | |
| 471 | + * @nodes: rbtree of binder nodes associated with | |
| 472 | + * this proc ordered by node->ptr | |
| 473 | + * (protected by @inner_lock) | |
| 474 | + * @refs_by_desc: rbtree of refs ordered by ref->desc | |
| 475 | + * (protected by @outer_lock) | |
| 476 | + * @refs_by_node: rbtree of refs ordered by ref->node | |
| 477 | + * (protected by @outer_lock) | |
| 478 | + * @waiting_threads: threads currently waiting for proc work | |
| 479 | + * (protected by @inner_lock) | |
| 480 | + * @pid PID of group_leader of process | |
| 481 | + * (invariant after initialized) | |
| 482 | + * @tsk task_struct for group_leader of process | |
| 483 | + * (invariant after initialized) | |
| 484 | + * @files files_struct for process | |
| 485 | + * (invariant after initialized) | |
| 486 | + * @deferred_work_node: element for binder_deferred_list | |
| 487 | + * (protected by binder_deferred_lock) | |
| 488 | + * @deferred_work: bitmap of deferred work to perform | |
| 489 | + * (protected by binder_deferred_lock) | |
| 490 | + * @is_dead: process is dead and awaiting free | |
| 491 | + * when outstanding transactions are cleaned up | |
| 492 | + * (protected by @inner_lock) | |
| 493 | + * @todo: list of work for this process | |
| 494 | + * (protected by @inner_lock) | |
| 495 | + * @wait: wait queue head to wait for proc work | |
| 496 | + * (invariant after initialized) | |
| 497 | + * @stats: per-process binder statistics | |
| 498 | + * (atomics, no lock needed) | |
| 499 | + * @delivered_death: list of delivered death notification | |
| 500 | + * (protected by @inner_lock) | |
| 501 | + * @max_threads: cap on number of binder threads | |
| 502 | + * (protected by @inner_lock) | |
| 503 | + * @requested_threads: number of binder threads requested but not | |
| 504 | + * yet started. In current implementation, can | |
| 505 | + * only be 0 or 1. | |
| 506 | + * (protected by @inner_lock) | |
| 507 | + * @requested_threads_started: number binder threads started | |
| 508 | + * (protected by @inner_lock) | |
| 509 | + * @tmp_ref: temporary reference to indicate proc is in use | |
| 510 | + * (protected by @inner_lock) | |
| 511 | + * @default_priority: default scheduler priority | |
| 512 | + * (invariant after initialized) | |
| 513 | + * @debugfs_entry: debugfs node | |
| 514 | + * @alloc: binder allocator bookkeeping | |
| 515 | + * @context: binder_context for this proc | |
| 516 | + * (invariant after initialized) | |
| 517 | + * @inner_lock: can nest under outer_lock and/or node lock | |
| 518 | + * @outer_lock: no nesting under innor or node lock | |
| 519 | + * Lock order: 1) outer, 2) node, 3) inner | |
| 520 | + * | |
| 521 | + * Bookkeeping structure for binder processes | |
| 522 | + */ | |
| 322 | 523 | struct binder_proc { |
| 323 | 524 | struct hlist_node proc_node; |
| 324 | 525 | struct rb_root threads; |
| 325 | 526 | struct rb_root nodes; |
| 326 | 527 | struct rb_root refs_by_desc; |
| 327 | 528 | struct rb_root refs_by_node; |
| 529 | + struct list_head waiting_threads; | |
| 328 | 530 | int pid; |
| 329 | − struct vm_area_struct *vma; | |
| 330 | − struct mm_struct *vma_vm_mm; | |
| 331 | 531 | struct task_struct *tsk; |
| 332 | 532 | struct files_struct *files; |
| 333 | 533 | struct hlist_node deferred_work_node; |
| 334 | 534 | int deferred_work; |
| 335 | − void *buffer; | |
| 336 | − ptrdiff_t user_buffer_offset; | |
| 337 | − | |
| 338 | − struct list_head buffers; | |
| 339 | − struct rb_root free_buffers; | |
| 340 | − struct rb_root allocated_buffers; | |
| 341 | − size_t free_async_space; | |
| 535 | + bool is_dead; | |
| 342 | 536 | |
| 343 | − struct page **pages; | |
| 344 | − size_t buffer_size; | |
| 345 | − uint32_t buffer_free; | |
| 346 | 537 | struct list_head todo; |
| 347 | 538 | wait_queue_head_t wait; |
| 348 | 539 | struct binder_stats stats; |
| @@ -350,10 +541,13 @@ struct binder_proc { · 1 unmodified line | ||
| 350 | 541 | int max_threads; |
| 351 | 542 | int requested_threads; |
| 352 | 543 | int requested_threads_started; |
| 353 | − int ready_threads; | |
| 544 | + int tmp_ref; | |
| 354 | 545 | long default_priority; |
| 355 | 546 | struct dentry *debugfs_entry; |
| 547 | + struct binder_alloc alloc; | |
| 356 | 548 | struct binder_context *context; |
| 549 | + spinlock_t inner_lock; | |
| 550 | + spinlock_t outer_lock; | |
| 357 | 551 | }; |
| 358 | 552 | |
| 359 | 553 | enum { |
| @@ -362,22 +556,58 @@ enum { · 2 unmodified lines | ||
| 362 | 556 | BINDER_LOOPER_STATE_EXITED = 0x04, |
| 363 | 557 | BINDER_LOOPER_STATE_INVALID = 0x08, |
| 364 | 558 | BINDER_LOOPER_STATE_WAITING = 0x10, |
| 365 | − BINDER_LOOPER_STATE_NEED_RETURN = 0x20 | |
| 559 | + BINDER_LOOPER_STATE_POLL = 0x20, | |
| 366 | 560 | }; |
| 367 | 561 | |
| 562 | +/** | |
| 563 | + * struct binder_thread - binder thread bookkeeping | |
| 564 | + * @proc: binder process for this thread | |
| 565 | + * (invariant after initialization) | |
| 566 | + * @rb_node: element for proc->threads rbtree | |
| 567 | + * (protected by @proc->inner_lock) | |
| 568 | + * @waiting_thread_node: element for @proc->waiting_threads list | |
| 569 | + * (protected by @proc->inner_lock) | |
| 570 | + * @pid: PID for this thread | |
| 571 | + * (invariant after initialization) | |
| 572 | + * @looper: bitmap of looping state | |
| 573 | + * (only accessed by this thread) | |
| 574 | + * @looper_needs_return: looping thread needs to exit driver | |
| 575 | + * (no lock needed) | |
| 576 | + * @transaction_stack: stack of in-progress transactions for this thread | |
| 577 | + * (protected by @proc->inner_lock) | |
| 578 | + * @todo: list of work to do for this thread | |
| 579 | + * (protected by @proc->inner_lock) | |
| 580 | + * @return_error: transaction errors reported by this thread | |
| 581 | + * (only accessed by this thread) | |
| 582 | + * @reply_error: transaction errors reported by target thread | |
| 583 | + * (protected by @proc->inner_lock) | |
| 584 | + * @wait: wait queue for thread work | |
| 585 | + * @stats: per-thread statistics | |
| 586 | + * (atomics, no lock needed) | |
| 587 | + * @tmp_ref: temporary reference to indicate thread is in use | |
| 588 | + * (atomic since @proc->inner_lock cannot | |
| 589 | + * always be acquired) | |
| 590 | + * @is_dead: thread is dead and awaiting free | |
| 591 | + * when outstanding transactions are cleaned up | |
| 592 | + * (protected by @proc->inner_lock) | |
| 593 | + * | |
| 594 | + * Bookkeeping structure for binder threads. | |
| 595 | + */ | |
| 368 | 596 | struct binder_thread { |
| 369 | 597 | struct binder_proc *proc; |
| 370 | 598 | struct rb_node rb_node; |
| 599 | + struct list_head waiting_thread_node; | |
| 371 | 600 | int pid; |
| 372 | − int looper; | |
| 601 | + int looper; /* only modified by this thread */ | |
| 602 | + bool looper_need_return; /* can be written by other thread */ | |
| 373 | 603 | struct binder_transaction *transaction_stack; |
| 374 | 604 | struct list_head todo; |
| 375 | − uint32_t return_error; /* Write failed, return error code in read buf */ | |
| 376 | − uint32_t return_error2; /* Write failed, return error code in read */ | |
| 377 | − /* buffer. Used when sending a reply to a dead process that */ | |
| 378 | − /* we are also waiting on */ | |
| 605 | + struct binder_error return_error; | |
| 606 | + struct binder_error reply_error; | |
| 379 | 607 | wait_queue_head_t wait; |
| 380 | 608 | struct binder_stats stats; |
| 609 | + atomic_t tmp_ref; | |
| 610 | + bool is_dead; | |
| 381 | 611 | }; |
| 382 | 612 | |
| 383 | 613 | struct binder_transaction { |
| @@ -397,10 +627,253 @@ struct binder_transaction { · 13 unmodified lines | ||
| 397 | 627 | long priority; |
| 398 | 628 | long saved_priority; |
| 399 | 629 | kuid_t sender_euid; |
| 630 | + /** | |
| 631 | + * @lock: protects @from, @to_proc, and @to_thread | |
| 632 | + * | |
| 633 | + * @from, @to_proc, and @to_thread can be set to NULL | |
| 634 | + * during thread teardown | |
| 635 | + */ | |
| 636 | + spinlock_t lock; | |
| 400 | 637 | }; |
| 401 | 638 | |
| 639 | +/** | |
| 640 | + * binder_proc_lock() - Acquire outer lock for given binder_proc | |
| 641 | + * @proc: struct binder_proc to acquire | |
| 642 | + * | |
| 643 | + * Acquires proc->outer_lock. Used to protect binder_ref | |
| 644 | + * structures associated with the given proc. | |
| 645 | + */ | |
| 646 | +#define binder_proc_lock(proc) _binder_proc_lock(proc, __LINE__) | |
| 647 | +static void | |
| 648 | +_binder_proc_lock(struct binder_proc *proc, int line) | |
| 649 | +{ | |
| 650 | + binder_debug(BINDER_DEBUG_SPINLOCKS, | |
| 651 | + "%s: line=%d\n", __func__, line); | |
| 652 | + spin_lock(&proc->outer_lock); | |
| 653 | +} | |
| 654 | + | |
| 655 | +/** | |
| 656 | + * binder_proc_unlock() - Release spinlock for given binder_proc | |
| 657 | + * @proc: struct binder_proc to acquire | |
| 658 | + * | |
| 659 | + * Release lock acquired via binder_proc_lock() | |
| 660 | + */ | |
| 661 | +#define binder_proc_unlock(_proc) _binder_proc_unlock(_proc, __LINE__) | |
| 662 | +static void | |
| 663 | +_binder_proc_unlock(struct binder_proc *proc, int line) | |
| 664 | +{ | |
| 665 | + binder_debug(BINDER_DEBUG_SPINLOCKS, | |
| 666 | + "%s: line=%d\n", __func__, line); | |
| 667 | + spin_unlock(&proc->outer_lock); | |
| 668 | +} | |
| 669 | + | |
| 670 | +/** | |
| 671 | + * binder_inner_proc_lock() - Acquire inner lock for given binder_proc | |
| 672 | + * @proc: struct binder_proc to acquire | |
| 673 | + * | |
| 674 | + * Acquires proc->inner_lock. Used to protect todo lists | |
| 675 | + */ | |
| 676 | +#define binder_inner_proc_lock(proc) _binder_inner_proc_lock(proc, __LINE__) | |
| 677 | +static void | |
| 678 | +_binder_inner_proc_lock(struct binder_proc *proc, int line) | |
| 679 | +{ | |
| 680 | + binder_debug(BINDER_DEBUG_SPINLOCKS, | |
| 681 | + "%s: line=%d\n", __func__, line); | |
| 682 | + spin_lock(&proc->inner_lock); | |
| 683 | +} | |
| 684 | + | |
| 685 | +/** | |
| 686 | + * binder_inner_proc_unlock() - Release inner lock for given binder_proc | |
| 687 | + * @proc: struct binder_proc to acquire | |
| 688 | + * | |
| 689 | + * Release lock acquired via binder_inner_proc_lock() | |
| 690 | + */ | |
| 691 | +#define binder_inner_proc_unlock(proc) _binder_inner_proc_unlock(proc, __LINE__) | |
| 692 | +static void | |
| 693 | +_binder_inner_proc_unlock(struct binder_proc *proc, int line) | |
| 694 | +{ | |
| 695 | + binder_debug(BINDER_DEBUG_SPINLOCKS, | |
| 696 | + "%s: line=%d\n", __func__, line); | |
| 697 | + spin_unlock(&proc->inner_lock); | |
| 698 | +} | |
| 699 | + | |
| 700 | +/** | |
| 701 | + * binder_node_lock() - Acquire spinlock for given binder_node | |
| 702 | + * @node: struct binder_node to acquire | |
| 703 | + * | |
| 704 | + * Acquires node->lock. Used to protect binder_node fields | |
| 705 | + */ | |
| 706 | +#define binder_node_lock(node) _binder_node_lock(node, __LINE__) | |
| 707 | +static void | |
| 708 | +_binder_node_lock(struct binder_node *node, int line) | |
| 709 | +{ | |
| 710 | + binder_debug(BINDER_DEBUG_SPINLOCKS, | |
| 711 | + "%s: line=%d\n", __func__, line); | |
| 712 | + spin_lock(&node->lock); | |
| 713 | +} | |
| 714 | + | |
| 715 | +/** | |
| 716 | + * binder_node_unlock() - Release spinlock for given binder_proc | |
| 717 | + * @node: struct binder_node to acquire | |
| 718 | + * | |
| 719 | + * Release lock acquired via binder_node_lock() | |
| 720 | + */ | |
| 721 | +#define binder_node_unlock(node) _binder_node_unlock(node, __LINE__) | |
| 722 | +static void | |
| 723 | +_binder_node_unlock(struct binder_node *node, int line) | |
| 724 | +{ | |
| 725 | + binder_debug(BINDER_DEBUG_SPINLOCKS, | |
| 726 | + "%s: line=%d\n", __func__, line); | |
| 727 | + spin_unlock(&node->lock); | |
| 728 | +} | |
| 729 | + | |
| 730 | +/** | |
| 731 | + * binder_node_inner_lock() - Acquire node and inner locks | |
| 732 | + * @node: struct binder_node to acquire | |
| 733 | + * | |
| 734 | + * Acquires node->lock. If node->proc also acquires | |
| 735 | + * proc->inner_lock. Used to protect binder_node fields | |
| 736 | + */ | |
| 737 | +#define binder_node_inner_lock(node) _binder_node_inner_lock(node, __LINE__) | |
| 738 | +static void | |
| 739 | +_binder_node_inner_lock(struct binder_node *node, int line) | |
| 740 | +{ | |
| 741 | + binder_debug(BINDER_DEBUG_SPINLOCKS, | |
| 742 | + "%s: line=%d\n", __func__, line); | |
| 743 | + spin_lock(&node->lock); | |
| 744 | + if (node->proc) | |
| 745 | + binder_inner_proc_lock(node->proc); | |
| 746 | +} | |
| 747 | + | |
| 748 | +/** | |
| 749 | + * binder_node_unlock() - Release node and inner locks | |
| 750 | + * @node: struct binder_node to acquire | |
| 751 | + * | |
| 752 | + * Release lock acquired via binder_node_lock() | |
| 753 | + */ | |
| 754 | +#define binder_node_inner_unlock(node) _binder_node_inner_unlock(node, __LINE__) | |
| 755 | +static void | |
| 756 | +_binder_node_inner_unlock(struct binder_node *node, int line) | |
| 757 | +{ | |
| 758 | + struct binder_proc *proc = node->proc; | |
| 759 | + | |
| 760 | + binder_debug(BINDER_DEBUG_SPINLOCKS, | |
| 761 | + "%s: line=%d\n", __func__, line); | |
| 762 | + if (proc) | |
| 763 | + binder_inner_proc_unlock(proc); | |
| 764 | + spin_unlock(&node->lock); | |
| 765 | +} | |
| 766 | + | |
| 767 | +static bool binder_worklist_empty_ilocked(struct list_head *list) | |
| 768 | +{ | |
| 769 | + return list_empty(list); | |
| 770 | +} | |
| 771 | + | |
| 772 | +/** | |
| 773 | + * binder_worklist_empty() - Check if no items on the work list | |
| 774 | + * @proc: binder_proc associated with list | |
| 775 | + * @list: list to check | |
| 776 | + * | |
| 777 | + * Return: true if there are no items on list, else false | |
| 778 | + */ | |
| 779 | +static bool binder_worklist_empty(struct binder_proc *proc, | |
| 780 | + struct list_head *list) | |
| 781 | +{ | |
| 782 | + bool ret; | |
| 783 | + | |
| 784 | + binder_inner_proc_lock(proc); | |
| 785 | + ret = binder_worklist_empty_ilocked(list); | |
| 786 | + binder_inner_proc_unlock(proc); | |
| 787 | + return ret; | |
| 788 | +} | |
| 789 | + | |
| 790 | +static void | |
| 791 | +binder_enqueue_work_ilocked(struct binder_work *work, | |
| 792 | + struct list_head *target_list) | |
| 793 | +{ | |
| 794 | + BUG_ON(target_list == NULL); | |
| 795 | + BUG_ON(work->entry.next && !list_empty(&work->entry)); | |
| 796 | + list_add_tail(&work->entry, target_list); | |
| 797 | +} | |
| 798 | + | |
| 799 | +/** | |
| 800 | + * binder_enqueue_work() - Add an item to the work list | |
| 801 | + * @proc: binder_proc associated with list | |
| 802 | + * @work: struct binder_work to add to list | |
| 803 | + * @target_list: list to add work to | |
| 804 | + * | |
| 805 | + * Adds the work to the specified list. Asserts that work | |
| 806 | + * is not already on a list. | |
| 807 | + */ | |
| 808 | +static void | |
| 809 | +binder_enqueue_work(struct binder_proc *proc, | |
| 810 | + struct binder_work *work, | |
| 811 | + struct list_head *target_list) | |
| 812 | +{ | |
| 813 | + binder_inner_proc_lock(proc); | |
| 814 | + binder_enqueue_work_ilocked(work, target_list); | |
| 815 | + binder_inner_proc_unlock(proc); | |
| 816 | +} | |
| 817 | + | |
| 818 | +static void | |
| 819 | +binder_dequeue_work_ilocked(struct binder_work *work) | |
| 820 | +{ | |
| 821 | + list_del_init(&work->entry); | |
| 822 | +} | |
| 823 | + | |
| 824 | +/** | |
| 825 | + * binder_dequeue_work() - Removes an item from the work list | |
| 826 | + * @proc: binder_proc associated with list | |
| 827 | + * @work: struct binder_work to remove from list | |
| 828 | + * | |
| 829 | + * Removes the specified work item from whatever list it is on. | |
| 830 | + * Can safely be called if work is not on any list. | |
| 831 | + */ | |
| 832 | +static void | |
| 833 | +binder_dequeue_work(struct binder_proc *proc, struct binder_work *work) | |
| 834 | +{ | |
| 835 | + binder_inner_proc_lock(proc); | |
| 836 | + binder_dequeue_work_ilocked(work); | |
| 837 | + binder_inner_proc_unlock(proc); | |
| 838 | +} | |
| 839 | + | |
| 840 | +static struct binder_work *binder_dequeue_work_head_ilocked( | |
| 841 | + struct list_head *list) | |
| 842 | +{ | |
| 843 | + struct binder_work *w; | |
| 844 | + | |
| 845 | + w = list_first_entry_or_null(list, struct binder_work, entry); | |
| 846 | + if (w) | |
| 847 | + list_del_init(&w->entry); | |
| 848 | + return w; | |
| 849 | +} | |
| 850 | + | |
| 851 | +/** | |
| 852 | + * binder_dequeue_work_head() - Dequeues the item at head of list | |
| 853 | + * @proc: binder_proc associated with list | |
| 854 | + * @list: list to dequeue head | |
| 855 | + * | |
| 856 | + * Removes the head of the list if there are items on the list | |
| 857 | + * | |
| 858 | + * Return: pointer dequeued binder_work, NULL if list was empty | |
| 859 | + */ | |
| 860 | +static struct binder_work *binder_dequeue_work_head( | |
| 861 | + struct binder_proc *proc, | |
| 862 | + struct list_head *list) | |
| 863 | +{ | |
| 864 | + struct binder_work *w; | |
| 865 | + | |
| 866 | + binder_inner_proc_lock(proc); | |
| 867 | + w = binder_dequeue_work_head_ilocked(list); | |
| 868 | + binder_inner_proc_unlock(proc); | |
| 869 | + return w; | |
| 870 | +} | |
| 871 | + | |
| 402 | 872 | static void |
| 403 | 873 | binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer); |
| 874 | +static void binder_free_thread(struct binder_thread *thread); | |
| 875 | +static void binder_free_proc(struct binder_proc *proc); | |
| 876 | +static void binder_inc_node_tmpref_ilocked(struct binder_node *node); | |
| 404 | 877 | |
| 405 | 878 | static int task_get_unused_fd_flags(struct binder_proc *proc, int flags) |
| 406 | 879 | { |
| @@ -451,484 +924,206 @@ static long task_close_fd(struct binder_proc *proc, unsigned int fd) · 44 unmodified lines | ||
| 451 | 924 | return retval; |
| 452 | 925 | } |
| 453 | 926 | |
| 454 | −static inline void binder_lock(const char *tag) | |
| 927 | +static bool binder_has_work_ilocked(struct binder_thread *thread, | |
| 928 | + bool do_proc_work) | |
| 455 | 929 | { |
| 456 | − trace_binder_lock(tag); | |
| 457 | − mutex_lock(&binder_main_lock); | |
| 458 | − trace_binder_locked(tag); | |
| 930 | + return !binder_worklist_empty_ilocked(&thread->todo) || | |
| 931 | + thread->looper_need_return || | |
| 932 | + (do_proc_work && | |
| 933 | + !binder_worklist_empty_ilocked(&thread->proc->todo)); | |
| 459 | 934 | } |
| 460 | 935 | |
| 461 | −static inline void binder_unlock(const char *tag) | |
| 936 | +static bool binder_has_work(struct binder_thread *thread, bool do_proc_work) | |
| 462 | 937 | { |
| 463 | − trace_binder_unlock(tag); | |
| 464 | − mutex_unlock(&binder_main_lock); | |
| 465 | −} | |
| 938 | + bool has_work; | |
| 466 | 939 | |
| 467 | −static void binder_set_nice(long nice) | |
| 468 | −{ | |
| 469 | − long min_nice; | |
| 940 | + binder_inner_proc_lock(thread->proc); | |
| 941 | + has_work = binder_has_work_ilocked(thread, do_proc_work); | |
| 942 | + binder_inner_proc_unlock(thread->proc); | |
| 470 | 943 | |
| 471 | − if (can_nice(current, nice)) { | |
| 472 | − set_user_nice(current, nice); | |
| 473 | − return; | |
| 474 | − } | |
| 475 | − min_nice = rlimit_to_nice(current->signal->rlim[RLIMIT_NICE].rlim_cur); | |
| 476 | − binder_debug(BINDER_DEBUG_PRIORITY_CAP, | |
| 477 | − "%d: nice value %ld not allowed use %ld instead\n", | |
| 478 | − current->pid, nice, min_nice); | |
| 479 | − set_user_nice(current, min_nice); | |
| 480 | − if (min_nice <= MAX_NICE) | |
| 481 | − return; | |
| 482 | − binder_user_error("%d RLIMIT_NICE not set\n", current->pid); | |
| 944 | + return has_work; | |
| 483 | 945 | } |
| 484 | 946 | |
| 485 | −static size_t binder_buffer_size(struct binder_proc *proc, | |
| 486 | − struct binder_buffer *buffer) | |
| 947 | +static bool binder_available_for_proc_work_ilocked(struct binder_thread *thread) | |
| 487 | 948 | { |
| 488 | − if (list_is_last(&buffer->entry, &proc->buffers)) | |
| 489 | − return proc->buffer + proc->buffer_size - (void *)buffer->data; | |
| 490 | − return (size_t)list_entry(buffer->entry.next, | |
| 491 | − struct binder_buffer, entry) - (size_t)buffer->data; | |
| 949 | + return !thread->transaction_stack && | |
| 950 | + binder_worklist_empty_ilocked(&thread->todo) && | |
| 951 | + (thread->looper & (BINDER_LOOPER_STATE_ENTERED | | |
| 952 | + BINDER_LOOPER_STATE_REGISTERED)); | |
| 492 | 953 | } |
| 493 | 954 | |
| 494 | −static void binder_insert_free_buffer(struct binder_proc *proc, | |
| 495 | − struct binder_buffer *new_buffer) | |
| 955 | +static void binder_wakeup_poll_threads_ilocked(struct binder_proc *proc, | |
| 956 | + bool sync) | |
| 496 | 957 | { |
| 497 | − struct rb_node **p = &proc->free_buffers.rb_node; | |
| 498 | − struct rb_node *parent = NULL; | |
| 499 | − struct binder_buffer *buffer; | |
| 500 | − size_t buffer_size; | |
| 501 | − size_t new_buffer_size; | |
| 958 | + struct rb_node *n; | |
| 959 | + struct binder_thread *thread; | |
| 960 | + | |
| 961 | + for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n)) { | |
| 962 | + thread = rb_entry(n, struct binder_thread, rb_node); | |
| 963 | + if (thread->looper & BINDER_LOOPER_STATE_POLL && | |
| 964 | + binder_available_for_proc_work_ilocked(thread)) { | |
| 965 | + if (sync) | |
| 966 | + wake_up_interruptible_sync(&thread->wait); | |
| 967 | + else | |
| 968 | + wake_up_interruptible(&thread->wait); | |
| 969 | + } | |
| 970 | + } | |
| 971 | +} | |
| 502 | 972 | |
| 503 | − BUG_ON(!new_buffer->free); | |
| 973 | +/** | |
| 974 | + * binder_select_thread_ilocked() - selects a thread for doing proc work. | |
| 975 | + * @proc: process to select a thread from | |
| 976 | + * | |
| 977 | + * Note that calling this function moves the thread off the waiting_threads | |
| 978 | + * list, so it can only be woken up by the caller of this function, or a | |
| 979 | + * signal. Therefore, callers *should* always wake up the thread this function | |
| 980 | + * returns. | |
| 981 | + * | |
| 982 | + * Return: If there's a thread currently waiting for process work, | |
| 983 | + * returns that thread. Otherwise returns NULL. | |
| 984 | + */ | |
| 985 | +static struct binder_thread * | |
| 986 | +binder_select_thread_ilocked(struct binder_proc *proc) | |
| 987 | +{ | |
| 988 | + struct binder_thread *thread; | |
| 504 | 989 | |
| 505 | − new_buffer_size = binder_buffer_size(proc, new_buffer); | |
| 990 | + assert_spin_locked(&proc->inner_lock); | |
| 991 | + thread = list_first_entry_or_null(&proc->waiting_threads, | |
| 992 | + struct binder_thread, | |
| 993 | + waiting_thread_node); | |
| 506 | 994 | |
| 507 | − binder_debug(BINDER_DEBUG_BUFFER_ALLOC, | |
| 508 | − "%d: add free buffer, size %zd, at %p\n", | |
| 509 | − proc->pid, new_buffer_size, new_buffer); | |
| 995 | + if (thread) | |
| 996 | + list_del_init(&thread->waiting_thread_node); | |
| 510 | 997 | |
| 511 | − while (*p) { | |
| 512 | − parent = *p; | |
| 513 | − buffer = rb_entry(parent, struct binder_buffer, rb_node); | |
| 514 | − BUG_ON(!buffer->free); | |
| 998 | + return thread; | |
| 999 | +} | |
| 515 | 1000 | |
| 516 | − buffer_size = binder_buffer_size(proc, buffer); | |
| 1001 | +/** | |
| 1002 | + * binder_wakeup_thread_ilocked() - wakes up a thread for doing proc work. | |
| 1003 | + * @proc: process to wake up a thread in | |
| 1004 | + * @thread: specific thread to wake-up (may be NULL) | |
| 1005 | + * @sync: whether to do a synchronous wake-up | |
| 1006 | + * | |
| 1007 | + * This function wakes up a thread in the @proc process. | |
| 1008 | + * The caller may provide a specific thread to wake-up in | |
| 1009 | + * the @thread parameter. If @thread is NULL, this function | |
| 1010 | + * will wake up threads that have called poll(). | |
| 1011 | + * | |
| 1012 | + * Note that for this function to work as expected, callers | |
| 1013 | + * should first call binder_select_thread() to find a thread | |
| 1014 | + * to handle the work (if they don't have a thread already), | |
| 1015 | + * and pass the result into the @thread parameter. | |
| 1016 | + */ | |
| 1017 | +static void binder_wakeup_thread_ilocked(struct binder_proc *proc, | |
| 1018 | + struct binder_thread *thread, | |
| 1019 | + bool sync) | |
| 1020 | +{ | |
| 1021 | + assert_spin_locked(&proc->inner_lock); | |
| 517 | 1022 | |
| 518 | − if (new_buffer_size < buffer_size) | |
| 519 | − p = &parent->rb_left; | |
| 1023 | + if (thread) { | |
| 1024 | + if (sync) | |
| 1025 | + wake_up_interruptible_sync(&thread->wait); | |
| 520 | 1026 | else |
| 521 | − p = &parent->rb_right; | |
| 1027 | + wake_up_interruptible(&thread->wait); | |
| 1028 | + return; | |
| 522 | 1029 | } |
| 523 | − rb_link_node(&new_buffer->rb_node, parent, p); | |
| 524 | − rb_insert_color(&new_buffer->rb_node, &proc->free_buffers); | |
| 1030 | + | |
| 1031 | + /* Didn't find a thread waiting for proc work; this can happen | |
| 1032 | + * in two scenarios: | |
| 1033 | + * 1. All threads are busy handling transactions | |
| 1034 | + * In that case, one of those threads should call back into | |
| 1035 | + * the kernel driver soon and pick up this work. | |
| 1036 | + * 2. Threads are using the (e)poll interface, in which case | |
| 1037 | + * they may be blocked on the waitqueue without having been | |
| 1038 | + * added to waiting_threads. For this case, we just iterate | |
| 1039 | + * over all threads not handling transaction work, and | |
| 1040 | + * wake them all up. We wake all because we don't know whether | |
| 1041 | + * a thread that called into (e)poll is handling non-binder | |
| 1042 | + * work currently. | |
| 1043 | + */ | |
| 1044 | + binder_wakeup_poll_threads_ilocked(proc, sync); | |
| 525 | 1045 | } |
| 526 | 1046 | |
| 527 | −static void binder_insert_allocated_buffer(struct binder_proc *proc, | |
| 528 | − struct binder_buffer *new_buffer) | |
| 1047 | +static void binder_wakeup_proc_ilocked(struct binder_proc *proc) | |
| 529 | 1048 | { |
| 530 | − struct rb_node **p = &proc->allocated_buffers.rb_node; | |
| 531 | − struct rb_node *parent = NULL; | |
| 532 | − struct binder_buffer *buffer; | |
| 1049 | + struct binder_thread *thread = binder_select_thread_ilocked(proc); | |
| 533 | 1050 | |
| 534 | − BUG_ON(new_buffer->free); | |
| 1051 | + binder_wakeup_thread_ilocked(proc, thread, /* sync = */false); | |
| 1052 | +} | |
| 535 | 1053 | |
| 536 | − while (*p) { | |
| 537 | − parent = *p; | |
| 538 | − buffer = rb_entry(parent, struct binder_buffer, rb_node); | |
| 539 | − BUG_ON(buffer->free); | |
| 1054 | +static void binder_set_nice(long nice) | |
| 1055 | +{ | |
| 1056 | + long min_nice; | |
| 540 | 1057 | |
| 541 | − if (new_buffer < buffer) | |
| 542 | − p = &parent->rb_left; | |
| 543 | − else if (new_buffer > buffer) | |
| 544 | − p = &parent->rb_right; | |
| 545 | − else | |
| 546 | − BUG(); | |
| 1058 | + if (can_nice(current, nice)) { | |
| 1059 | + set_user_nice(current, nice); | |
| 1060 | + return; | |
| 547 | 1061 | } |
| 548 | − rb_link_node(&new_buffer->rb_node, parent, p); | |
| 549 | − rb_insert_color(&new_buffer->rb_node, &proc->allocated_buffers); | |
| 1062 | + min_nice = rlimit_to_nice(rlimit(RLIMIT_NICE)); | |
| 1063 | + binder_debug(BINDER_DEBUG_PRIORITY_CAP, | |
| 1064 | + "%d: nice value %ld not allowed use %ld instead\n", | |
| 1065 | + current->pid, nice, min_nice); | |
| 1066 | + set_user_nice(current, min_nice); | |
| 1067 | + if (min_nice <= MAX_NICE) | |
| 1068 | + return; | |
| 1069 | + binder_user_error("%d RLIMIT_NICE not set\n", current->pid); | |
| 550 | 1070 | } |
| 551 | 1071 | |
| 552 | −static struct binder_buffer *binder_buffer_lookup(struct binder_proc *proc, | |
| 553 | − uintptr_t user_ptr) | |
| 1072 | +static struct binder_node *binder_get_node_ilocked(struct binder_proc *proc, | |
| 1073 | + binder_uintptr_t ptr) | |
| 554 | 1074 | { |
| 555 | − struct rb_node *n = proc->allocated_buffers.rb_node; | |
| 556 | − struct binder_buffer *buffer; | |
| 557 | − struct binder_buffer *kern_ptr; | |
| 1075 | + struct rb_node *n = proc->nodes.rb_node; | |
| 1076 | + struct binder_node *node; | |
| 558 | 1077 | |
| 559 | − kern_ptr = (struct binder_buffer *)(user_ptr - proc->user_buffer_offset | |
| 560 | − - offsetof(struct binder_buffer, data)); | |
| 1078 | + assert_spin_locked(&proc->inner_lock); | |
| 561 | 1079 | |
| 562 | 1080 | while (n) { |
| 563 | − buffer = rb_entry(n, struct binder_buffer, rb_node); | |
| 564 | − BUG_ON(buffer->free); | |
| 1081 | + node = rb_entry(n, struct binder_node, rb_node); | |
| 565 | 1082 | |
| 566 | − if (kern_ptr < buffer) | |
| 1083 | + if (ptr < node->ptr) | |
| 567 | 1084 | n = n->rb_left; |
| 568 | − else if (kern_ptr > buffer) | |
| 1085 | + else if (ptr > node->ptr) | |
| 569 | 1086 | n = n->rb_right; |
| 570 | − else | |
| 571 | − return buffer; | |
| 1087 | + else { | |
| 1088 | + /* | |
| 1089 | + * take an implicit weak reference | |
| 1090 | + * to ensure node stays alive until | |
| 1091 | + * call to binder_put_node() | |
| 1092 | + */ | |
| 1093 | + binder_inc_node_tmpref_ilocked(node); | |
| 1094 | + return node; | |
| 1095 | + } | |
| 572 | 1096 | } |
| 573 | 1097 | return NULL; |
| 574 | 1098 | } |
| 575 | 1099 | |
| 576 | −static int binder_update_page_range(struct binder_proc *proc, int allocate, | |
| 577 | − void *start, void *end, | |
| 578 | − struct vm_area_struct *vma) | |
| 1100 | +static struct binder_node *binder_get_node(struct binder_proc *proc, | |
| 1101 | + binder_uintptr_t ptr) | |
| 579 | 1102 | { |
| 580 | − void *page_addr; | |
| 581 | − unsigned long user_page_addr; | |
| 582 | − struct page **page; | |
| 583 | − struct mm_struct *mm; | |
| 584 | − | |
| 585 | − binder_debug(BINDER_DEBUG_BUFFER_ALLOC, | |
| 586 | − "%d: %s pages %p-%p\n", proc->pid, | |
| 587 | − allocate ? "allocate" : "free", start, end); | |
| 588 | − | |
| 589 | − if (end <= start) | |
| 590 | − return 0; | |
| 1103 | + struct binder_node *node; | |
| 591 | 1104 | |
| 592 | − trace_binder_update_page_range(proc, allocate, start, end); | |
| 1105 | + binder_inner_proc_lock(proc); | |
| 1106 | + node = binder_get_node_ilocked(proc, ptr); | |
| 1107 | + binder_inner_proc_unlock(proc); | |
| 1108 | + return node; | |
| 1109 | +} | |
| 593 | 1110 | |
| 594 | − if (vma) | |
| 595 | − mm = NULL; | |
| 596 | − else | |
| 597 | − mm = get_task_mm(proc->tsk); | |
| 598 | − | |
| 599 | − if (mm) { | |
| 600 | − down_write(&mm->mmap_sem); | |
| 601 | − vma = proc->vma; | |
| 602 | − if (vma && mm != proc->vma_vm_mm) { | |
| 603 | − pr_err("%d: vma mm and task mm mismatch\n", | |
| 604 | − proc->pid); | |
| 605 | − vma = NULL; | |
| 606 | − } | |
| 607 | − } | |
| 608 | − | |
| 609 | − if (allocate == 0) | |
| 610 | − goto free_range; | |
| 611 | − | |
| 612 | − if (vma == NULL) { | |
| 613 | − pr_err("%d: binder_alloc_buf failed to map pages in userspace, no vma\n", | |
| 614 | − proc->pid); | |
| 615 | − goto err_no_vma; | |
| 616 | − } | |
| 617 | − | |
| 618 | − for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) { | |
| 619 | − int ret; | |
| 620 | − | |
| 621 | − page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE]; | |
| 622 | − | |
| 623 | − BUG_ON(*page); | |
| 624 | − *page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO); | |
| 625 | − if (*page == NULL) { | |
| 626 | − pr_err("%d: binder_alloc_buf failed for page at %p\n", | |
| 627 | − proc->pid, page_addr); | |
| 628 | − goto err_alloc_page_failed; | |
| 629 | − } | |
| 630 | − ret = map_kernel_range_noflush((unsigned long)page_addr, | |
| 631 | − PAGE_SIZE, PAGE_KERNEL, page); | |
| 632 | − flush_cache_vmap((unsigned long)page_addr, | |
| 633 | − (unsigned long)page_addr + PAGE_SIZE); | |
| 634 | − if (ret != 1) { | |
| 635 | − pr_err("%d: binder_alloc_buf failed to map page at %p in kernel\n", | |
| 636 | − proc->pid, page_addr); | |
| 637 | − goto err_map_kernel_failed; | |
| 638 | − } | |
| 639 | − user_page_addr = | |
| 640 | − (uintptr_t)page_addr + proc->user_buffer_offset; | |
| 641 | − ret = vm_insert_page(vma, user_page_addr, page[0]); | |
| 642 | − if (ret) { | |
| 643 | − pr_err("%d: binder_alloc_buf failed to map page at %lx in userspace\n", | |
| 644 | − proc->pid, user_page_addr); | |
| 645 | − goto err_vm_insert_page_failed; | |
| 646 | − } | |
| 647 | − /* vm_insert_page does not seem to increment the refcount */ | |
| 648 | − } | |
| 649 | − if (mm) { | |
| 650 | − up_write(&mm->mmap_sem); | |
| 651 | − mmput(mm); | |
| 652 | − } | |
| 653 | − return 0; | |
| 654 | − | |
| 655 | −free_range: | |
| 656 | − for (page_addr = end - PAGE_SIZE; page_addr >= start; | |
| 657 | − page_addr -= PAGE_SIZE) { | |
| 658 | − page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE]; | |
| 659 | − if (vma) | |
| 660 | − zap_page_range(vma, (uintptr_t)page_addr + | |
| 661 | − proc->user_buffer_offset, PAGE_SIZE); | |
| 662 | −err_vm_insert_page_failed: | |
| 663 | − unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE); | |
| 664 | −err_map_kernel_failed: | |
| 665 | − __free_page(*page); | |
| 666 | − *page = NULL; | |
| 667 | −err_alloc_page_failed: | |
| 668 | − ; | |
| 669 | − } | |
| 670 | −err_no_vma: | |
| 671 | − if (mm) { | |
| 672 | − up_write(&mm->mmap_sem); | |
| 673 | − mmput(mm); | |
| 674 | − } | |
| 675 | − return -ENOMEM; | |
| 676 | −} | |
| 677 | − | |
| 678 | −static struct binder_buffer *binder_alloc_buf(struct binder_proc *proc, | |
| 679 | − size_t data_size, | |
| 680 | − size_t offsets_size, | |
| 681 | − size_t extra_buffers_size, | |
| 682 | − int is_async) | |
| 683 | −{ | |
| 684 | − struct rb_node *n = proc->free_buffers.rb_node; | |
| 685 | − struct binder_buffer *buffer; | |
| 686 | − size_t buffer_size; | |
| 687 | − struct rb_node *best_fit = NULL; | |
| 688 | − void *has_page_addr; | |
| 689 | − void *end_page_addr; | |
| 690 | − size_t size, data_offsets_size; | |
| 691 | − | |
| 692 | − if (proc->vma == NULL) { | |
| 693 | − pr_err("%d: binder_alloc_buf, no vma\n", | |
| 694 | − proc->pid); | |
| 695 | − return NULL; | |
| 696 | − } | |
| 697 | − | |
| 698 | − data_offsets_size = ALIGN(data_size, sizeof(void *)) + | |
| 699 | − ALIGN(offsets_size, sizeof(void *)); | |
| 700 | − | |
| 701 | − if (data_offsets_size < data_size || data_offsets_size < offsets_size) { | |
| 702 | − binder_user_error("%d: got transaction with invalid size %zd-%zd\n", | |
| 703 | − proc->pid, data_size, offsets_size); | |
| 704 | − return NULL; | |
| 705 | − } | |
| 706 | − size = data_offsets_size + ALIGN(extra_buffers_size, sizeof(void *)); | |
| 707 | − if (size < data_offsets_size || size < extra_buffers_size) { | |
| 708 | − binder_user_error("%d: got transaction with invalid extra_buffers_size %zd\n", | |
| 709 | − proc->pid, extra_buffers_size); | |
| 710 | − return NULL; | |
| 711 | − } | |
| 712 | − if (is_async && | |
| 713 | − proc->free_async_space < size + sizeof(struct binder_buffer)) { | |
| 714 | − binder_debug(BINDER_DEBUG_BUFFER_ALLOC, | |
| 715 | − "%d: binder_alloc_buf size %zd failed, no async space left\n", | |
| 716 | − proc->pid, size); | |
| 717 | − return NULL; | |
| 718 | − } | |
| 719 | − | |
| 720 | − while (n) { | |
| 721 | − buffer = rb_entry(n, struct binder_buffer, rb_node); | |
| 722 | − BUG_ON(!buffer->free); | |
| 723 | − buffer_size = binder_buffer_size(proc, buffer); | |
| 724 | − | |
| 725 | − if (size < buffer_size) { | |
| 726 | − best_fit = n; | |
| 727 | − n = n->rb_left; | |
| 728 | − } else if (size > buffer_size) | |
| 729 | − n = n->rb_right; | |
| 730 | − else { | |
| 731 | − best_fit = n; | |
| 732 | − break; | |
| 733 | − } | |
| 734 | − } | |
| 735 | − if (best_fit == NULL) { | |
| 736 | − pr_err("%d: binder_alloc_buf size %zd failed, no address space\n", | |
| 737 | − proc->pid, size); | |
| 738 | − return NULL; | |
| 739 | − } | |
| 740 | − if (n == NULL) { | |
| 741 | − buffer = rb_entry(best_fit, struct binder_buffer, rb_node); | |
| 742 | − buffer_size = binder_buffer_size(proc, buffer); | |
| 743 | − } | |
| 744 | − | |
| 745 | − binder_debug(BINDER_DEBUG_BUFFER_ALLOC, | |
| 746 | − "%d: binder_alloc_buf size %zd got buffer %p size %zd\n", | |
| 747 | − proc->pid, size, buffer, buffer_size); | |
| 748 | − | |
| 749 | − has_page_addr = | |
| 750 | − (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK); | |
| 751 | − if (n == NULL) { | |
| 752 | − if (size + sizeof(struct binder_buffer) + 4 >= buffer_size) | |
| 753 | − buffer_size = size; /* no room for other buffers */ | |
| 754 | − else | |
| 755 | − buffer_size = size + sizeof(struct binder_buffer); | |
| 756 | − } | |
| 757 | − end_page_addr = | |
| 758 | − (void *)PAGE_ALIGN((uintptr_t)buffer->data + buffer_size); | |
| 759 | − if (end_page_addr > has_page_addr) | |
| 760 | − end_page_addr = has_page_addr; | |
| 761 | − if (binder_update_page_range(proc, 1, | |
| 762 | − (void *)PAGE_ALIGN((uintptr_t)buffer->data), end_page_addr, NULL)) | |
| 763 | − return NULL; | |
| 764 | − | |
| 765 | − rb_erase(best_fit, &proc->free_buffers); | |
| 766 | − buffer->free = 0; | |
| 767 | − binder_insert_allocated_buffer(proc, buffer); | |
| 768 | − if (buffer_size != size) { | |
| 769 | − struct binder_buffer *new_buffer = (void *)buffer->data + size; | |
| 770 | − | |
| 771 | − list_add(&new_buffer->entry, &buffer->entry); | |
| 772 | − new_buffer->free = 1; | |
| 773 | − binder_insert_free_buffer(proc, new_buffer); | |
| 774 | − } | |
| 775 | − binder_debug(BINDER_DEBUG_BUFFER_ALLOC, | |
| 776 | − "%d: binder_alloc_buf size %zd got %p\n", | |
| 777 | − proc->pid, size, buffer); | |
| 778 | − buffer->data_size = data_size; | |
| 779 | − buffer->offsets_size = offsets_size; | |
| 780 | − buffer->extra_buffers_size = extra_buffers_size; | |
| 781 | − buffer->async_transaction = is_async; | |
| 782 | − if (is_async) { | |
| 783 | − proc->free_async_space -= size + sizeof(struct binder_buffer); | |
| 784 | − binder_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC, | |
| 785 | − "%d: binder_alloc_buf size %zd async free %zd\n", | |
| 786 | − proc->pid, size, proc->free_async_space); | |
| 787 | − } | |
| 788 | − | |
| 789 | − return buffer; | |
| 790 | −} | |
| 791 | − | |
| 792 | −static void *buffer_start_page(struct binder_buffer *buffer) | |
| 793 | −{ | |
| 794 | − return (void *)((uintptr_t)buffer & PAGE_MASK); | |
| 795 | −} | |
| 796 | − | |
| 797 | −static void *buffer_end_page(struct binder_buffer *buffer) | |
| 798 | −{ | |
| 799 | − return (void *)(((uintptr_t)(buffer + 1) - 1) & PAGE_MASK); | |
| 800 | −} | |
| 801 | − | |
| 802 | −static void binder_delete_free_buffer(struct binder_proc *proc, | |
| 803 | − struct binder_buffer *buffer) | |
| 804 | −{ | |
| 805 | − struct binder_buffer *prev, *next = NULL; | |
| 806 | − int free_page_end = 1; | |
| 807 | − int free_page_start = 1; | |
| 808 | − | |
| 809 | − BUG_ON(proc->buffers.next == &buffer->entry); | |
| 810 | − prev = list_entry(buffer->entry.prev, struct binder_buffer, entry); | |
| 811 | − BUG_ON(!prev->free); | |
| 812 | − if (buffer_end_page(prev) == buffer_start_page(buffer)) { | |
| 813 | − free_page_start = 0; | |
| 814 | − if (buffer_end_page(prev) == buffer_end_page(buffer)) | |
| 815 | − free_page_end = 0; | |
| 816 | − binder_debug(BINDER_DEBUG_BUFFER_ALLOC, | |
| 817 | − "%d: merge free, buffer %p share page with %p\n", | |
| 818 | − proc->pid, buffer, prev); | |
| 819 | − } | |
| 820 | − | |
| 821 | − if (!list_is_last(&buffer->entry, &proc->buffers)) { | |
| 822 | − next = list_entry(buffer->entry.next, | |
| 823 | − struct binder_buffer, entry); | |
| 824 | − if (buffer_start_page(next) == buffer_end_page(buffer)) { | |
| 825 | − free_page_end = 0; | |
| 826 | − if (buffer_start_page(next) == | |
| 827 | − buffer_start_page(buffer)) | |
| 828 | − free_page_start = 0; | |
| 829 | − binder_debug(BINDER_DEBUG_BUFFER_ALLOC, | |
| 830 | − "%d: merge free, buffer %p share page with %p\n", | |
| 831 | − proc->pid, buffer, prev); | |
| 832 | − } | |
| 833 | − } | |
| 834 | − list_del(&buffer->entry); | |
| 835 | − if (free_page_start || free_page_end) { | |
| 836 | − binder_debug(BINDER_DEBUG_BUFFER_ALLOC, | |
| 837 | − "%d: merge free, buffer %p do not share page%s%s with %p or %p\n", | |
| 838 | − proc->pid, buffer, free_page_start ? "" : " end", | |
| 839 | − free_page_end ? "" : " start", prev, next); | |
| 840 | − binder_update_page_range(proc, 0, free_page_start ? | |
| 841 | − buffer_start_page(buffer) : buffer_end_page(buffer), | |
| 842 | − (free_page_end ? buffer_end_page(buffer) : | |
| 843 | − buffer_start_page(buffer)) + PAGE_SIZE, NULL); | |
| 844 | − } | |
| 845 | −} | |
| 846 | − | |
| 847 | −static void binder_free_buf(struct binder_proc *proc, | |
| 848 | − struct binder_buffer *buffer) | |
| 849 | −{ | |
| 850 | − size_t size, buffer_size; | |
| 851 | − | |
| 852 | − buffer_size = binder_buffer_size(proc, buffer); | |
| 853 | − | |
| 854 | − size = ALIGN(buffer->data_size, sizeof(void *)) + | |
| 855 | − ALIGN(buffer->offsets_size, sizeof(void *)) + | |
| 856 | − ALIGN(buffer->extra_buffers_size, sizeof(void *)); | |
| 857 | − | |
| 858 | − binder_debug(BINDER_DEBUG_BUFFER_ALLOC, | |
| 859 | − "%d: binder_free_buf %p size %zd buffer_size %zd\n", | |
| 860 | − proc->pid, buffer, size, buffer_size); | |
| 861 | − | |
| 862 | − BUG_ON(buffer->free); | |
| 863 | − BUG_ON(size > buffer_size); | |
| 864 | − BUG_ON(buffer->transaction != NULL); | |
| 865 | − BUG_ON((void *)buffer < proc->buffer); | |
| 866 | − BUG_ON((void *)buffer > proc->buffer + proc->buffer_size); | |
| 867 | − | |
| 868 | − if (buffer->async_transaction) { | |
| 869 | − proc->free_async_space += size + sizeof(struct binder_buffer); | |
| 870 | − | |
| 871 | − binder_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC, | |
| 872 | − "%d: binder_free_buf size %zd async free %zd\n", | |
| 873 | − proc->pid, size, proc->free_async_space); | |
| 874 | − } | |
| 875 | − | |
| 876 | − binder_update_page_range(proc, 0, | |
| 877 | − (void *)PAGE_ALIGN((uintptr_t)buffer->data), | |
| 878 | − (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK), | |
| 879 | − NULL); | |
| 880 | − rb_erase(&buffer->rb_node, &proc->allocated_buffers); | |
| 881 | − buffer->free = 1; | |
| 882 | − if (!list_is_last(&buffer->entry, &proc->buffers)) { | |
| 883 | − struct binder_buffer *next = list_entry(buffer->entry.next, | |
| 884 | − struct binder_buffer, entry); | |
| 885 | − | |
| 886 | − if (next->free) { | |
| 887 | − rb_erase(&next->rb_node, &proc->free_buffers); | |
| 888 | − binder_delete_free_buffer(proc, next); | |
| 889 | − } | |
| 890 | − } | |
| 891 | − if (proc->buffers.next != &buffer->entry) { | |
| 892 | − struct binder_buffer *prev = list_entry(buffer->entry.prev, | |
| 893 | − struct binder_buffer, entry); | |
| 894 | − | |
| 895 | − if (prev->free) { | |
| 896 | − binder_delete_free_buffer(proc, buffer); | |
| 897 | − rb_erase(&prev->rb_node, &proc->free_buffers); | |
| 898 | − buffer = prev; | |
| 899 | − } | |
| 900 | − } | |
| 901 | − binder_insert_free_buffer(proc, buffer); | |
| 902 | −} | |
| 903 | − | |
| 904 | −static struct binder_node *binder_get_node(struct binder_proc *proc, | |
| 905 | − binder_uintptr_t ptr) | |
| 906 | −{ | |
| 907 | − struct rb_node *n = proc->nodes.rb_node; | |
| 908 | − struct binder_node *node; | |
| 909 | − | |
| 910 | − while (n) { | |
| 911 | − node = rb_entry(n, struct binder_node, rb_node); | |
| 912 | − | |
| 913 | − if (ptr < node->ptr) | |
| 914 | − n = n->rb_left; | |
| 915 | − else if (ptr > node->ptr) | |
| 916 | − n = n->rb_right; | |
| 917 | − else | |
| 918 | − return node; | |
| 919 | − } | |
| 920 | − return NULL; | |
| 921 | −} | |
| 922 | − | |
| 923 | −static struct binder_node *binder_new_node(struct binder_proc *proc, | |
| 924 | − binder_uintptr_t ptr, | |
| 925 | − binder_uintptr_t cookie) | |
| 1111 | +static struct binder_node *binder_init_node_ilocked( | |
| 1112 | + struct binder_proc *proc, | |
| 1113 | + struct binder_node *new_node, | |
| 1114 | + struct flat_binder_object *fp) | |
| 926 | 1115 | { |
| 927 | 1116 | struct rb_node **p = &proc->nodes.rb_node; |
| 928 | 1117 | struct rb_node *parent = NULL; |
| 929 | 1118 | struct binder_node *node; |
| 1119 | + binder_uintptr_t ptr = fp ? fp->binder : 0; | |
| 1120 | + binder_uintptr_t cookie = fp ? fp->cookie : 0; | |
| 1121 | + __u32 flags = fp ? fp->flags : 0; | |
| 1122 | + | |
| 1123 | + assert_spin_locked(&proc->inner_lock); | |
| 930 | 1124 | |
| 931 | 1125 | while (*p) { |
| 1126 | + | |
| 932 | 1127 | parent = *p; |
| 933 | 1128 | node = rb_entry(parent, struct binder_node, rb_node); |
| 934 | 1129 | |
| @@ -936,33 +1131,74 @@ static struct binder_node *binder_new_node(struct binder_proc *proc, · 1 unmodified line | ||
| 936 | 1131 | p = &(*p)->rb_left; |
| 937 | 1132 | else if (ptr > node->ptr) |
| 938 | 1133 | p = &(*p)->rb_right; |
| 939 | − else | |
| 940 | − return NULL; | |
| 1134 | + else { | |
| 1135 | + /* | |
| 1136 | + * A matching node is already in | |
| 1137 | + * the rb tree. Abandon the init | |
| 1138 | + * and return it. | |
| 1139 | + */ | |
| 1140 | + binder_inc_node_tmpref_ilocked(node); | |
| 1141 | + return node; | |
| 1142 | + } | |
| 941 | 1143 | } |
| 942 | − | |
| 943 | − node = kzalloc(sizeof(*node), GFP_KERNEL); | |
| 944 | − if (node == NULL) | |
| 945 | − return NULL; | |
| 1144 | + node = new_node; | |
| 946 | 1145 | binder_stats_created(BINDER_STAT_NODE); |
| 1146 | + node->tmp_refs++; | |
| 947 | 1147 | rb_link_node(&node->rb_node, parent, p); |
| 948 | 1148 | rb_insert_color(&node->rb_node, &proc->nodes); |
| 949 | − node->debug_id = ++binder_last_id; | |
| 1149 | + node->debug_id = atomic_inc_return(&binder_last_id); | |
| 950 | 1150 | node->proc = proc; |
| 951 | 1151 | node->ptr = ptr; |
| 952 | 1152 | node->cookie = cookie; |
| 953 | 1153 | node->work.type = BINDER_WORK_NODE; |
| 1154 | + node->min_priority = flags & FLAT_BINDER_FLAG_PRIORITY_MASK; | |
| 1155 | + node->accept_fds = !!(flags & FLAT_BINDER_FLAG_ACCEPTS_FDS); | |
| 1156 | + spin_lock_init(&node->lock); | |
| 954 | 1157 | INIT_LIST_HEAD(&node->work.entry); |
| 955 | 1158 | INIT_LIST_HEAD(&node->async_todo); |
| 956 | 1159 | binder_debug(BINDER_DEBUG_INTERNAL_REFS, |
| 957 | 1160 | "%d:%d node %d u%016llx c%016llx created\n", |
| 958 | 1161 | proc->pid, current->pid, node->debug_id, |
| 959 | 1162 | (u64)node->ptr, (u64)node->cookie); |
| 1163 | + | |
| 960 | 1164 | return node; |
| 961 | 1165 | } |
| 962 | 1166 | |
| 963 | −static int binder_inc_node(struct binder_node *node, int strong, int internal, | |
| 964 | − struct list_head *target_list) | |
| 1167 | +static struct binder_node *binder_new_node(struct binder_proc *proc, | |
| 1168 | + struct flat_binder_object *fp) | |
| 1169 | +{ | |
| 1170 | + struct binder_node *node; | |
| 1171 | + struct binder_node *new_node = kzalloc(sizeof(*node), GFP_KERNEL); | |
| 1172 | + | |
| 1173 | + if (!new_node) | |
| 1174 | + return NULL; | |
| 1175 | + binder_inner_proc_lock(proc); | |
| 1176 | + node = binder_init_node_ilocked(proc, new_node, fp); | |
| 1177 | + binder_inner_proc_unlock(proc); | |
| 1178 | + if (node != new_node) | |
| 1179 | + /* | |
| 1180 | + * The node was already added by another thread | |
| 1181 | + */ | |
| 1182 | + kfree(new_node); | |
| 1183 | + | |
| 1184 | + return node; | |
| 1185 | +} | |
| 1186 | + | |
| 1187 | +static void binder_free_node(struct binder_node *node) | |
| 965 | 1188 | { |
| 1189 | + kfree(node); | |
| 1190 | + binder_stats_deleted(BINDER_STAT_NODE); | |
| 1191 | +} | |
| 1192 | + | |
| 1193 | +static int binder_inc_node_nilocked(struct binder_node *node, int strong, | |
| 1194 | + int internal, | |
| 1195 | + struct list_head *target_list) | |
| 1196 | +{ | |
| 1197 | + struct binder_proc *proc = node->proc; | |
| 1198 | + | |
| 1199 | + assert_spin_locked(&node->lock); | |
| 1200 | + if (proc) | |
| 1201 | + assert_spin_locked(&proc->inner_lock); | |
| 966 | 1202 | if (strong) { |
| 967 | 1203 | if (internal) { |
| 968 | 1204 | if (target_list == NULL && |
| @@ -978,8 +1214,8 @@ static int binder_inc_node(struct binder_node *node, int strong, int internal, · 9 unmodified lines | ||
| 978 | 1214 | } else |
| 979 | 1215 | node->local_strong_refs++; |
| 980 | 1216 | if (!node->has_strong_ref && target_list) { |
| 981 | − list_del_init(&node->work.entry); | |
| 982 | − list_add_tail(&node->work.entry, target_list); | |
| 1217 | + binder_dequeue_work_ilocked(&node->work); | |
| 1218 | + binder_enqueue_work_ilocked(&node->work, target_list); | |
| 983 | 1219 | } |
| 984 | 1220 | } else { |
| 985 | 1221 | if (!internal) |
| @@ -990,58 +1226,169 @@ static int binder_inc_node(struct binder_node *node, int strong, int internal, · 4 unmodified lines | ||
| 990 | 1226 | node->debug_id); |
| 991 | 1227 | return -EINVAL; |
| 992 | 1228 | } |
| 993 | − list_add_tail(&node->work.entry, target_list); | |
| 1229 | + binder_enqueue_work_ilocked(&node->work, target_list); | |
| 994 | 1230 | } |
| 995 | 1231 | } |
| 996 | 1232 | return 0; |
| 997 | 1233 | } |
| 998 | 1234 | |
| 999 | −static int binder_dec_node(struct binder_node *node, int strong, int internal) | |
| 1235 | +static int binder_inc_node(struct binder_node *node, int strong, int internal, | |
| 1236 | + struct list_head *target_list) | |
| 1237 | +{ | |
| 1238 | + int ret; | |
| 1239 | + | |
| 1240 | + binder_node_inner_lock(node); | |
| 1241 | + ret = binder_inc_node_nilocked(node, strong, internal, target_list); | |
| 1242 | + binder_node_inner_unlock(node); | |
| 1243 | + | |
| 1244 | + return ret; | |
| 1245 | +} | |
| 1246 | + | |
| 1247 | +static bool binder_dec_node_nilocked(struct binder_node *node, | |
| 1248 | + int strong, int internal) | |
| 1000 | 1249 | { |
| 1250 | + struct binder_proc *proc = node->proc; | |
| 1251 | + | |
| 1252 | + assert_spin_locked(&node->lock); | |
| 1253 | + if (proc) | |
| 1254 | + assert_spin_locked(&proc->inner_lock); | |
| 1001 | 1255 | if (strong) { |
| 1002 | 1256 | if (internal) |
| 1003 | 1257 | node->internal_strong_refs--; |
| 1004 | 1258 | else |
| 1005 | 1259 | node->local_strong_refs--; |
| 1006 | 1260 | if (node->local_strong_refs || node->internal_strong_refs) |
| 1007 | − return 0; | |
| 1261 | + return false; | |
| 1008 | 1262 | } else { |
| 1009 | 1263 | if (!internal) |
| 1010 | 1264 | node->local_weak_refs--; |
| 1011 | − if (node->local_weak_refs || !hlist_empty(&node->refs)) | |
| 1012 | − return 0; | |
| 1265 | + if (node->local_weak_refs || node->tmp_refs || | |
| 1266 | + !hlist_empty(&node->refs)) | |
| 1267 | + return false; | |
| 1013 | 1268 | } |
| 1014 | − if (node->proc && (node->has_strong_ref || node->has_weak_ref)) { | |
| 1269 | + | |
| 1270 | + if (proc && (node->has_strong_ref || node->has_weak_ref)) { | |
| 1015 | 1271 | if (list_empty(&node->work.entry)) { |
| 1016 | − list_add_tail(&node->work.entry, &node->proc->todo); | |
| 1017 | − wake_up_interruptible(&node->proc->wait); | |
| 1272 | + binder_enqueue_work_ilocked(&node->work, &proc->todo); | |
| 1273 | + binder_wakeup_proc_ilocked(proc); | |
| 1018 | 1274 | } |
| 1019 | 1275 | } else { |
| 1020 | 1276 | if (hlist_empty(&node->refs) && !node->local_strong_refs && |
| 1021 | − !node->local_weak_refs) { | |
| 1022 | − list_del_init(&node->work.entry); | |
| 1023 | − if (node->proc) { | |
| 1024 | − rb_erase(&node->rb_node, &node->proc->nodes); | |
| 1277 | + !node->local_weak_refs && !node->tmp_refs) { | |
| 1278 | + if (proc) { | |
| 1279 | + binder_dequeue_work_ilocked(&node->work); | |
| 1280 | + rb_erase(&node->rb_node, &proc->nodes); | |
| 1025 | 1281 | binder_debug(BINDER_DEBUG_INTERNAL_REFS, |
| 1026 | 1282 | "refless node %d deleted\n", |
| 1027 | 1283 | node->debug_id); |
| 1028 | 1284 | } else { |
| 1285 | + BUG_ON(!list_empty(&node->work.entry)); | |
| 1286 | + spin_lock(&binder_dead_nodes_lock); | |
| 1287 | + /* | |
| 1288 | + * tmp_refs could have changed so | |
| 1289 | + * check it again | |
| 1290 | + */ | |
| 1291 | + if (node->tmp_refs) { | |
| 1292 | + spin_unlock(&binder_dead_nodes_lock); | |
| 1293 | + return false; | |
| 1294 | + } | |
| 1029 | 1295 | hlist_del(&node->dead_node); |
| 1296 | + spin_unlock(&binder_dead_nodes_lock); | |
| 1030 | 1297 | binder_debug(BINDER_DEBUG_INTERNAL_REFS, |
| 1031 | 1298 | "dead node %d deleted\n", |
| 1032 | 1299 | node->debug_id); |
| 1033 | 1300 | } |
| 1034 | − kfree(node); | |
| 1035 | − binder_stats_deleted(BINDER_STAT_NODE); | |
| 1301 | + return true; | |
| 1036 | 1302 | } |
| 1037 | 1303 | } |
| 1304 | + return false; | |
| 1305 | +} | |
| 1038 | 1306 | |
| 1039 | − return 0; | |
| 1307 | +static void binder_dec_node(struct binder_node *node, int strong, int internal) | |
| 1308 | +{ | |
| 1309 | + bool free_node; | |
| 1310 | + | |
| 1311 | + binder_node_inner_lock(node); | |
| 1312 | + free_node = binder_dec_node_nilocked(node, strong, internal); | |
| 1313 | + binder_node_inner_unlock(node); | |
| 1314 | + if (free_node) | |
| 1315 | + binder_free_node(node); | |
| 1040 | 1316 | } |
| 1041 | 1317 | |
| 1318 | +static void binder_inc_node_tmpref_ilocked(struct binder_node *node) | |
| 1319 | +{ | |
| 1320 | + /* | |
| 1321 | + * No call to binder_inc_node() is needed since we | |
| 1322 | + * don't need to inform userspace of any changes to | |
| 1323 | + * tmp_refs | |
| 1324 | + */ | |
| 1325 | + node->tmp_refs++; | |
| 1326 | +} | |
| 1042 | 1327 | |
| 1043 | −static struct binder_ref *binder_get_ref(struct binder_proc *proc, | |
| 1044 | − u32 desc, bool need_strong_ref) | |
| 1328 | +/** | |
| 1329 | + * binder_inc_node_tmpref() - take a temporary reference on node | |
| 1330 | + * @node: node to reference | |
| 1331 | + * | |
| 1332 | + * Take reference on node to prevent the node from being freed | |
| 1333 | + * while referenced only by a local variable. The inner lock is | |
| 1334 | + * needed to serialize with the node work on the queue (which | |
| 1335 | + * isn't needed after the node is dead). If the node is dead | |
| 1336 | + * (node->proc is NULL), use binder_dead_nodes_lock to protect | |
| 1337 | + * node->tmp_refs against dead-node-only cases where the node | |
| 1338 | + * lock cannot be acquired (eg traversing the dead node list to | |
| 1339 | + * print nodes) | |
| 1340 | + */ | |
| 1341 | +static void binder_inc_node_tmpref(struct binder_node *node) | |
| 1342 | +{ | |
| 1343 | + binder_node_lock(node); | |
| 1344 | + if (node->proc) | |
| 1345 | + binder_inner_proc_lock(node->proc); | |
| 1346 | + else | |
| 1347 | + spin_lock(&binder_dead_nodes_lock); | |
| 1348 | + binder_inc_node_tmpref_ilocked(node); | |
| 1349 | + if (node->proc) | |
| 1350 | + binder_inner_proc_unlock(node->proc); | |
| 1351 | + else | |
| 1352 | + spin_unlock(&binder_dead_nodes_lock); | |
| 1353 | + binder_node_unlock(node); | |
| 1354 | +} | |
| 1355 | + | |
| 1356 | +/** | |
| 1357 | + * binder_dec_node_tmpref() - remove a temporary reference on node | |
| 1358 | + * @node: node to reference | |
| 1359 | + * | |
| 1360 | + * Release temporary reference on node taken via binder_inc_node_tmpref() | |
| 1361 | + */ | |
| 1362 | +static void binder_dec_node_tmpref(struct binder_node *node) | |
| 1363 | +{ | |
| 1364 | + bool free_node; | |
| 1365 | + | |
| 1366 | + binder_node_inner_lock(node); | |
| 1367 | + if (!node->proc) | |
| 1368 | + spin_lock(&binder_dead_nodes_lock); | |
| 1369 | + node->tmp_refs--; | |
| 1370 | + BUG_ON(node->tmp_refs < 0); | |
| 1371 | + if (!node->proc) | |
| 1372 | + spin_unlock(&binder_dead_nodes_lock); | |
| 1373 | + /* | |
| 1374 | + * Call binder_dec_node() to check if all refcounts are 0 | |
| 1375 | + * and cleanup is needed. Calling with strong=0 and internal=1 | |
| 1376 | + * causes no actual reference to be released in binder_dec_node(). | |
| 1377 | + * If that changes, a change is needed here too. | |
| 1378 | + */ | |
| 1379 | + free_node = binder_dec_node_nilocked(node, 0, 1); | |
| 1380 | + binder_node_inner_unlock(node); | |
| 1381 | + if (free_node) | |
| 1382 | + binder_free_node(node); | |
| 1383 | +} | |
| 1384 | + | |
| 1385 | +static void binder_put_node(struct binder_node *node) | |
| 1386 | +{ | |
| 1387 | + binder_dec_node_tmpref(node); | |
| 1388 | +} | |
| 1389 | + | |
| 1390 | +static struct binder_ref *binder_get_ref_olocked(struct binder_proc *proc, | |
| 1391 | + u32 desc, bool need_strong_ref) | |
| 1045 | 1392 | { |
| 1046 | 1393 | struct rb_node *n = proc->refs_by_desc.rb_node; |
| 1047 | 1394 | struct binder_ref *ref; |
| @@ -1049,11 +1396,11 @@ static struct binder_ref *binder_get_ref(struct binder_proc *proc, · 1 unmodified line | ||
| 1049 | 1396 | while (n) { |
| 1050 | 1397 | ref = rb_entry(n, struct binder_ref, rb_node_desc); |
| 1051 | 1398 | |
| 1052 | − if (desc < ref->desc) { | |
| 1399 | + if (desc < ref->data.desc) { | |
| 1053 | 1400 | n = n->rb_left; |
| 1054 | − } else if (desc > ref->desc) { | |
| 1401 | + } else if (desc > ref->data.desc) { | |
| 1055 | 1402 | n = n->rb_right; |
| 1056 | − } else if (need_strong_ref && !ref->strong) { | |
| 1403 | + } else if (need_strong_ref && !ref->data.strong) { | |
| 1057 | 1404 | binder_user_error("tried to use weak ref as strong ref\n"); |
| 1058 | 1405 | return NULL; |
| 1059 | 1406 | } else { |
| @@ -1063,14 +1410,34 @@ static struct binder_ref *binder_get_ref(struct binder_proc *proc, · 3 unmodified lines | ||
| 1063 | 1410 | return NULL; |
| 1064 | 1411 | } |
| 1065 | 1412 | |
| 1066 | −static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc, | |
| 1067 | − struct binder_node *node) | |
| 1413 | +/** | |
| 1414 | + * binder_get_ref_for_node_olocked() - get the ref associated with given node | |
| 1415 | + * @proc: binder_proc that owns the ref | |
| 1416 | + * @node: binder_node of target | |
| 1417 | + * @new_ref: newly allocated binder_ref to be initialized or %NULL | |
| 1418 | + * | |
| 1419 | + * Look up the ref for the given node and return it if it exists | |
| 1420 | + * | |
| 1421 | + * If it doesn't exist and the caller provides a newly allocated | |
| 1422 | + * ref, initialize the fields of the newly allocated ref and insert | |
| 1423 | + * into the given proc rb_trees and node refs list. | |
| 1424 | + * | |
| 1425 | + * Return: the ref for node. It is possible that another thread | |
| 1426 | + * allocated/initialized the ref first in which case the | |
| 1427 | + * returned ref would be different than the passed-in | |
| 1428 | + * new_ref. new_ref must be kfree'd by the caller in | |
| 1429 | + * this case. | |
| 1430 | + */ | |
| 1431 | +static struct binder_ref *binder_get_ref_for_node_olocked( | |
| 1432 | + struct binder_proc *proc, | |
| 1433 | + struct binder_node *node, | |
| 1434 | + struct binder_ref *new_ref) | |
| 1068 | 1435 | { |
| 1069 | − struct rb_node *n; | |
| 1436 | + struct binder_context *context = proc->context; | |
| 1070 | 1437 | struct rb_node **p = &proc->refs_by_node.rb_node; |
| 1071 | 1438 | struct rb_node *parent = NULL; |
| 1072 | − struct binder_ref *ref, *new_ref; | |
| 1073 | − struct binder_context *context = proc->context; | |
| 1439 | + struct binder_ref *ref; | |
| 1440 | + struct rb_node *n; | |
| 1074 | 1441 | |
| 1075 | 1442 | while (*p) { |
| 1076 | 1443 | parent = *p; |
| @@ -1083,144 +1450,446 @@ static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc, · 6 unmodified lines | ||
| 1083 | 1450 | else |
| 1084 | 1451 | return ref; |
| 1085 | 1452 | } |
| 1086 | − new_ref = kzalloc(sizeof(*ref), GFP_KERNEL); | |
| 1087 | − if (new_ref == NULL) | |
| 1453 | + if (!new_ref) | |
| 1088 | 1454 | return NULL; |
| 1455 | + | |
| 1089 | 1456 | binder_stats_created(BINDER_STAT_REF); |
| 1090 | − new_ref->debug_id = ++binder_last_id; | |
| 1457 | + new_ref->data.debug_id = atomic_inc_return(&binder_last_id); | |
| 1091 | 1458 | new_ref->proc = proc; |
| 1092 | 1459 | new_ref->node = node; |
| 1093 | 1460 | rb_link_node(&new_ref->rb_node_node, parent, p); |
| 1094 | 1461 | rb_insert_color(&new_ref->rb_node_node, &proc->refs_by_node); |
| 1095 | 1462 | |
| 1096 | − new_ref->desc = (node == context->binder_context_mgr_node) ? 0 : 1; | |
| 1097 | − for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) { | |
| 1098 | − ref = rb_entry(n, struct binder_ref, rb_node_desc); | |
| 1099 | − if (ref->desc > new_ref->desc) | |
| 1100 | − break; | |
| 1101 | − new_ref->desc = ref->desc + 1; | |
| 1463 | + new_ref->data.desc = (node == context->binder_context_mgr_node) ? 0 : 1; | |
| 1464 | + for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) { | |
| 1465 | + ref = rb_entry(n, struct binder_ref, rb_node_desc); | |
| 1466 | + if (ref->data.desc > new_ref->data.desc) | |
| 1467 | + break; | |
| 1468 | + new_ref->data.desc = ref->data.desc + 1; | |
| 1469 | + } | |
| 1470 | + | |
| 1471 | + p = &proc->refs_by_desc.rb_node; | |
| 1472 | + while (*p) { | |
| 1473 | + parent = *p; | |
| 1474 | + ref = rb_entry(parent, struct binder_ref, rb_node_desc); | |
| 1475 | + | |
| 1476 | + if (new_ref->data.desc < ref->data.desc) | |
| 1477 | + p = &(*p)->rb_left; | |
| 1478 | + else if (new_ref->data.desc > ref->data.desc) | |
| 1479 | + p = &(*p)->rb_right; | |
| 1480 | + else | |
| 1481 | + BUG(); | |
| 1482 | + } | |
| 1483 | + rb_link_node(&new_ref->rb_node_desc, parent, p); | |
| 1484 | + rb_insert_color(&new_ref->rb_node_desc, &proc->refs_by_desc); | |
| 1485 | + | |
| 1486 | + binder_node_lock(node); | |
| 1487 | + hlist_add_head(&new_ref->node_entry, &node->refs); | |
| 1488 | + | |
| 1489 | + binder_debug(BINDER_DEBUG_INTERNAL_REFS, | |
| 1490 | + "%d new ref %d desc %d for node %d\n", | |
| 1491 | + proc->pid, new_ref->data.debug_id, new_ref->data.desc, | |
| 1492 | + node->debug_id); | |
| 1493 | + binder_node_unlock(node); | |
| 1494 | + return new_ref; | |
| 1495 | +} | |
| 1496 | + | |
| 1497 | +static void binder_cleanup_ref_olocked(struct binder_ref *ref) | |
| 1498 | +{ | |
| 1499 | + bool delete_node = false; | |
| 1500 | + | |
| 1501 | + binder_debug(BINDER_DEBUG_INTERNAL_REFS, | |
| 1502 | + "%d delete ref %d desc %d for node %d\n", | |
| 1503 | + ref->proc->pid, ref->data.debug_id, ref->data.desc, | |
| 1504 | + ref->node->debug_id); | |
| 1505 | + | |
| 1506 | + rb_erase(&ref->rb_node_desc, &ref->proc->refs_by_desc); | |
| 1507 | + rb_erase(&ref->rb_node_node, &ref->proc->refs_by_node); | |
| 1508 | + | |
| 1509 | + binder_node_inner_lock(ref->node); | |
| 1510 | + if (ref->data.strong) | |
| 1511 | + binder_dec_node_nilocked(ref->node, 1, 1); | |
| 1512 | + | |
| 1513 | + hlist_del(&ref->node_entry); | |
| 1514 | + delete_node = binder_dec_node_nilocked(ref->node, 0, 1); | |
| 1515 | + binder_node_inner_unlock(ref->node); | |
| 1516 | + /* | |
| 1517 | + * Clear ref->node unless we want the caller to free the node | |
| 1518 | + */ | |
| 1519 | + if (!delete_node) { | |
| 1520 | + /* | |
| 1521 | + * The caller uses ref->node to determine | |
| 1522 | + * whether the node needs to be freed. Clear | |
| 1523 | + * it since the node is still alive. | |
| 1524 | + */ | |
| 1525 | + ref->node = NULL; | |
| 1526 | + } | |
| 1527 | + | |
| 1528 | + if (ref->death) { | |
| 1529 | + binder_debug(BINDER_DEBUG_DEAD_BINDER, | |
| 1530 | + "%d delete ref %d desc %d has death notification\n", | |
| 1531 | + ref->proc->pid, ref->data.debug_id, | |
| 1532 | + ref->data.desc); | |
| 1533 | + binder_dequeue_work(ref->proc, &ref->death->work); | |
| 1534 | + binder_stats_deleted(BINDER_STAT_DEATH); | |
| 1535 | + } | |
| 1536 | + binder_stats_deleted(BINDER_STAT_REF); | |
| 1537 | +} | |
| 1538 | + | |
| 1539 | +/** | |
| 1540 | + * binder_inc_ref_olocked() - increment the ref for given handle | |
| 1541 | + * @ref: ref to be incremented | |
| 1542 | + * @strong: if true, strong increment, else weak | |
| 1543 | + * @target_list: list to queue node work on | |
| 1544 | + * | |
| 1545 | + * Increment the ref. @ref->proc->outer_lock must be held on entry | |
| 1546 | + * | |
| 1547 | + * Return: 0, if successful, else errno | |
| 1548 | + */ | |
| 1549 | +static int binder_inc_ref_olocked(struct binder_ref *ref, int strong, | |
| 1550 | + struct list_head *target_list) | |
| 1551 | +{ | |
| 1552 | + int ret; | |
| 1553 | + | |
| 1554 | + if (strong) { | |
| 1555 | + if (ref->data.strong == 0) { | |
| 1556 | + ret = binder_inc_node(ref->node, 1, 1, target_list); | |
| 1557 | + if (ret) | |
| 1558 | + return ret; | |
| 1559 | + } | |
| 1560 | + ref->data.strong++; | |
| 1561 | + } else { | |
| 1562 | + if (ref->data.weak == 0) { | |
| 1563 | + ret = binder_inc_node(ref->node, 0, 1, target_list); | |
| 1564 | + if (ret) | |
| 1565 | + return ret; | |
| 1566 | + } | |
| 1567 | + ref->data.weak++; | |
| 1568 | + } | |
| 1569 | + return 0; | |
| 1570 | +} | |
| 1571 | + | |
| 1572 | +/** | |
| 1573 | + * binder_dec_ref() - dec the ref for given handle | |
| 1574 | + * @ref: ref to be decremented | |
| 1575 | + * @strong: if true, strong decrement, else weak | |
| 1576 | + * | |
| 1577 | + * Decrement the ref. | |
| 1578 | + * | |
| 1579 | + * Return: true if ref is cleaned up and ready to be freed | |
| 1580 | + */ | |
| 1581 | +static bool binder_dec_ref_olocked(struct binder_ref *ref, int strong) | |
| 1582 | +{ | |
| 1583 | + if (strong) { | |
| 1584 | + if (ref->data.strong == 0) { | |
| 1585 | + binder_user_error("%d invalid dec strong, ref %d desc %d s %d w %d\n", | |
| 1586 | + ref->proc->pid, ref->data.debug_id, | |
| 1587 | + ref->data.desc, ref->data.strong, | |
| 1588 | + ref->data.weak); | |
| 1589 | + return false; | |
| 1590 | + } | |
| 1591 | + ref->data.strong--; | |
| 1592 | + if (ref->data.strong == 0) | |
| 1593 | + binder_dec_node(ref->node, strong, 1); | |
| 1594 | + } else { | |
| 1595 | + if (ref->data.weak == 0) { | |
| 1596 | + binder_user_error("%d invalid dec weak, ref %d desc %d s %d w %d\n", | |
| 1597 | + ref->proc->pid, ref->data.debug_id, | |
| 1598 | + ref->data.desc, ref->data.strong, | |
| 1599 | + ref->data.weak); | |
| 1600 | + return false; | |
| 1601 | + } | |
| 1602 | + ref->data.weak--; | |
| 1603 | + } | |
| 1604 | + if (ref->data.strong == 0 && ref->data.weak == 0) { | |
| 1605 | + binder_cleanup_ref_olocked(ref); | |
| 1606 | + return true; | |
| 1607 | + } | |
| 1608 | + return false; | |
| 1609 | +} | |
| 1610 | + | |
| 1611 | +/** | |
| 1612 | + * binder_get_node_from_ref() - get the node from the given proc/desc | |
| 1613 | + * @proc: proc containing the ref | |
| 1614 | + * @desc: the handle associated with the ref | |
| 1615 | + * @need_strong_ref: if true, only return node if ref is strong | |
| 1616 | + * @rdata: the id/refcount data for the ref | |
| 1617 | + * | |
| 1618 | + * Given a proc and ref handle, return the associated binder_node | |
| 1619 | + * | |
| 1620 | + * Return: a binder_node or NULL if not found or not strong when strong required | |
| 1621 | + */ | |
| 1622 | +static struct binder_node *binder_get_node_from_ref( | |
| 1623 | + struct binder_proc *proc, | |
| 1624 | + u32 desc, bool need_strong_ref, | |
| 1625 | + struct binder_ref_data *rdata) | |
| 1626 | +{ | |
| 1627 | + struct binder_node *node; | |
| 1628 | + struct binder_ref *ref; | |
| 1629 | + | |
| 1630 | + binder_proc_lock(proc); | |
| 1631 | + ref = binder_get_ref_olocked(proc, desc, need_strong_ref); | |
| 1632 | + if (!ref) | |
| 1633 | + goto err_no_ref; | |
| 1634 | + node = ref->node; | |
| 1635 | + /* | |
| 1636 | + * Take an implicit reference on the node to ensure | |
| 1637 | + * it stays alive until the call to binder_put_node() | |
| 1638 | + */ | |
| 1639 | + binder_inc_node_tmpref(node); | |
| 1640 | + if (rdata) | |
| 1641 | + *rdata = ref->data; | |
| 1642 | + binder_proc_unlock(proc); | |
| 1643 | + | |
| 1644 | + return node; | |
| 1645 | + | |
| 1646 | +err_no_ref: | |
| 1647 | + binder_proc_unlock(proc); | |
| 1648 | + return NULL; | |
| 1649 | +} | |
| 1650 | + | |
| 1651 | +/** | |
| 1652 | + * binder_free_ref() - free the binder_ref | |
| 1653 | + * @ref: ref to free | |
| 1654 | + * | |
| 1655 | + * Free the binder_ref. Free the binder_node indicated by ref->node | |
| 1656 | + * (if non-NULL) and the binder_ref_death indicated by ref->death. | |
| 1657 | + */ | |
| 1658 | +static void binder_free_ref(struct binder_ref *ref) | |
| 1659 | +{ | |
| 1660 | + if (ref->node) | |
| 1661 | + binder_free_node(ref->node); | |
| 1662 | + kfree(ref->death); | |
| 1663 | + kfree(ref); | |
| 1664 | +} | |
| 1665 | + | |
| 1666 | +/** | |
| 1667 | + * binder_update_ref_for_handle() - inc/dec the ref for given handle | |
| 1668 | + * @proc: proc containing the ref | |
| 1669 | + * @desc: the handle associated with the ref | |
| 1670 | + * @increment: true=inc reference, false=dec reference | |
| 1671 | + * @strong: true=strong reference, false=weak reference | |
| 1672 | + * @rdata: the id/refcount data for the ref | |
| 1673 | + * | |
| 1674 | + * Given a proc and ref handle, increment or decrement the ref | |
| 1675 | + * according to "increment" arg. | |
| 1676 | + * | |
| 1677 | + * Return: 0 if successful, else errno | |
| 1678 | + */ | |
| 1679 | +static int binder_update_ref_for_handle(struct binder_proc *proc, | |
| 1680 | + uint32_t desc, bool increment, bool strong, | |
| 1681 | + struct binder_ref_data *rdata) | |
| 1682 | +{ | |
| 1683 | + int ret = 0; | |
| 1684 | + struct binder_ref *ref; | |
| 1685 | + bool delete_ref = false; | |
| 1686 | + | |
| 1687 | + binder_proc_lock(proc); | |
| 1688 | + ref = binder_get_ref_olocked(proc, desc, strong); | |
| 1689 | + if (!ref) { | |
| 1690 | + ret = -EINVAL; | |
| 1691 | + goto err_no_ref; | |
| 1102 | 1692 | } |
| 1693 | + if (increment) | |
| 1694 | + ret = binder_inc_ref_olocked(ref, strong, NULL); | |
| 1695 | + else | |
| 1696 | + delete_ref = binder_dec_ref_olocked(ref, strong); | |
| 1103 | 1697 | |
| 1104 | − p = &proc->refs_by_desc.rb_node; | |
| 1105 | − while (*p) { | |
| 1106 | − parent = *p; | |
| 1107 | − ref = rb_entry(parent, struct binder_ref, rb_node_desc); | |
| 1698 | + if (rdata) | |
| 1699 | + *rdata = ref->data; | |
| 1700 | + binder_proc_unlock(proc); | |
| 1108 | 1701 | |
| 1109 | − if (new_ref->desc < ref->desc) | |
| 1110 | − p = &(*p)->rb_left; | |
| 1111 | − else if (new_ref->desc > ref->desc) | |
| 1112 | − p = &(*p)->rb_right; | |
| 1113 | − else | |
| 1114 | − BUG(); | |
| 1115 | − } | |
| 1116 | − rb_link_node(&new_ref->rb_node_desc, parent, p); | |
| 1117 | − rb_insert_color(&new_ref->rb_node_desc, &proc->refs_by_desc); | |
| 1118 | − if (node) { | |
| 1119 | − hlist_add_head(&new_ref->node_entry, &node->refs); | |
| 1702 | + if (delete_ref) | |
| 1703 | + binder_free_ref(ref); | |
| 1704 | + return ret; | |
| 1120 | 1705 | |
| 1121 | − binder_debug(BINDER_DEBUG_INTERNAL_REFS, | |
| 1122 | − "%d new ref %d desc %d for node %d\n", | |
| 1123 | − proc->pid, new_ref->debug_id, new_ref->desc, | |
| 1124 | − node->debug_id); | |
| 1125 | − } else { | |
| 1126 | − binder_debug(BINDER_DEBUG_INTERNAL_REFS, | |
| 1127 | − "%d new ref %d desc %d for dead node\n", | |
| 1128 | − proc->pid, new_ref->debug_id, new_ref->desc); | |
| 1129 | − } | |
| 1130 | − return new_ref; | |
| 1706 | +err_no_ref: | |
| 1707 | + binder_proc_unlock(proc); | |
| 1708 | + return ret; | |
| 1709 | +} | |
| 1710 | + | |
| 1711 | +/** | |
| 1712 | + * binder_dec_ref_for_handle() - dec the ref for given handle | |
| 1713 | + * @proc: proc containing the ref | |
| 1714 | + * @desc: the handle associated with the ref | |
| 1715 | + * @strong: true=strong reference, false=weak reference | |
| 1716 | + * @rdata: the id/refcount data for the ref | |
| 1717 | + * | |
| 1718 | + * Just calls binder_update_ref_for_handle() to decrement the ref. | |
| 1719 | + * | |
| 1720 | + * Return: 0 if successful, else errno | |
| 1721 | + */ | |
| 1722 | +static int binder_dec_ref_for_handle(struct binder_proc *proc, | |
| 1723 | + uint32_t desc, bool strong, struct binder_ref_data *rdata) | |
| 1724 | +{ | |
| 1725 | + return binder_update_ref_for_handle(proc, desc, false, strong, rdata); | |
| 1131 | 1726 | } |
| 1132 | 1727 | |
| 1133 | −static void binder_delete_ref(struct binder_ref *ref) | |
| 1728 | + | |
| 1729 | +/** | |
| 1730 | + * binder_inc_ref_for_node() - increment the ref for given proc/node | |
| 1731 | + * @proc: proc containing the ref | |
| 1732 | + * @node: target node | |
| 1733 | + * @strong: true=strong reference, false=weak reference | |
| 1734 | + * @target_list: worklist to use if node is incremented | |
| 1735 | + * @rdata: the id/refcount data for the ref | |
| 1736 | + * | |
| 1737 | + * Given a proc and node, increment the ref. Create the ref if it | |
| 1738 | + * doesn't already exist | |
| 1739 | + * | |
| 1740 | + * Return: 0 if successful, else errno | |
| 1741 | + */ | |
| 1742 | +static int binder_inc_ref_for_node(struct binder_proc *proc, | |
| 1743 | + struct binder_node *node, | |
| 1744 | + bool strong, | |
| 1745 | + struct list_head *target_list, | |
| 1746 | + struct binder_ref_data *rdata) | |
| 1134 | 1747 | { |
| 1135 | − binder_debug(BINDER_DEBUG_INTERNAL_REFS, | |
| 1136 | − "%d delete ref %d desc %d for node %d\n", | |
| 1137 | − ref->proc->pid, ref->debug_id, ref->desc, | |
| 1138 | − ref->node->debug_id); | |
| 1748 | + struct binder_ref *ref; | |
| 1749 | + struct binder_ref *new_ref = NULL; | |
| 1750 | + int ret = 0; | |
| 1139 | 1751 | |
| 1140 | − rb_erase(&ref->rb_node_desc, &ref->proc->refs_by_desc); | |
| 1141 | − rb_erase(&ref->rb_node_node, &ref->proc->refs_by_node); | |
| 1142 | − if (ref->strong) | |
| 1143 | − binder_dec_node(ref->node, 1, 1); | |
| 1144 | − hlist_del(&ref->node_entry); | |
| 1145 | − binder_dec_node(ref->node, 0, 1); | |
| 1146 | − if (ref->death) { | |
| 1147 | − binder_debug(BINDER_DEBUG_DEAD_BINDER, | |
| 1148 | − "%d delete ref %d desc %d has death notification\n", | |
| 1149 | − ref->proc->pid, ref->debug_id, ref->desc); | |
| 1150 | − list_del(&ref->death->work.entry); | |
| 1151 | − kfree(ref->death); | |
| 1152 | − binder_stats_deleted(BINDER_STAT_DEATH); | |
| 1752 | + binder_proc_lock(proc); | |
| 1753 | + ref = binder_get_ref_for_node_olocked(proc, node, NULL); | |
| 1754 | + if (!ref) { | |
| 1755 | + binder_proc_unlock(proc); | |
| 1756 | + new_ref = kzalloc(sizeof(*ref), GFP_KERNEL); | |
| 1757 | + if (!new_ref) | |
| 1758 | + return -ENOMEM; | |
| 1759 | + binder_proc_lock(proc); | |
| 1760 | + ref = binder_get_ref_for_node_olocked(proc, node, new_ref); | |
| 1153 | 1761 | } |
| 1154 | − kfree(ref); | |
| 1155 | − binder_stats_deleted(BINDER_STAT_REF); | |
| 1762 | + ret = binder_inc_ref_olocked(ref, strong, target_list); | |
| 1763 | + *rdata = ref->data; | |
| 1764 | + binder_proc_unlock(proc); | |
| 1765 | + if (new_ref && ref != new_ref) | |
| 1766 | + /* | |
| 1767 | + * Another thread created the ref first so | |
| 1768 | + * free the one we allocated | |
| 1769 | + */ | |
| 1770 | + kfree(new_ref); | |
| 1771 | + return ret; | |
| 1156 | 1772 | } |
| 1157 | 1773 | |
| 1158 | −static int binder_inc_ref(struct binder_ref *ref, int strong, | |
| 1159 | − struct list_head *target_list) | |
| 1774 | +static void binder_pop_transaction_ilocked(struct binder_thread *target_thread, | |
| 1775 | + struct binder_transaction *t) | |
| 1160 | 1776 | { |
| 1161 | − int ret; | |
| 1777 | + BUG_ON(!target_thread); | |
| 1778 | + assert_spin_locked(&target_thread->proc->inner_lock); | |
| 1779 | + BUG_ON(target_thread->transaction_stack != t); | |
| 1780 | + BUG_ON(target_thread->transaction_stack->from != target_thread); | |
| 1781 | + target_thread->transaction_stack = | |
| 1782 | + target_thread->transaction_stack->from_parent; | |
| 1783 | + t->from = NULL; | |
| 1784 | +} | |
| 1162 | 1785 | |
| 1163 | − if (strong) { | |
| 1164 | − if (ref->strong == 0) { | |
| 1165 | − ret = binder_inc_node(ref->node, 1, 1, target_list); | |
| 1166 | − if (ret) | |
| 1167 | − return ret; | |
| 1168 | − } | |
| 1169 | − ref->strong++; | |
| 1170 | − } else { | |
| 1171 | − if (ref->weak == 0) { | |
| 1172 | − ret = binder_inc_node(ref->node, 0, 1, target_list); | |
| 1173 | − if (ret) | |
| 1174 | − return ret; | |
| 1175 | − } | |
| 1176 | − ref->weak++; | |
| 1786 | +/** | |
| 1787 | + * binder_thread_dec_tmpref() - decrement thread->tmp_ref | |
| 1788 | + * @thread: thread to decrement | |
| 1789 | + * | |
| 1790 | + * A thread needs to be kept alive while being used to create or | |
| 1791 | + * handle a transaction. binder_get_txn_from() is used to safely | |
| 1792 | + * extract t->from from a binder_transaction and keep the thread | |
| 1793 | + * indicated by t->from from being freed. When done with that | |
| 1794 | + * binder_thread, this function is called to decrement the | |
| 1795 | + * tmp_ref and free if appropriate (thread has been released | |
| 1796 | + * and no transaction being processed by the driver) | |
| 1797 | + */ | |
| 1798 | +static void binder_thread_dec_tmpref(struct binder_thread *thread) | |
| 1799 | +{ | |
| 1800 | + /* | |
| 1801 | + * atomic is used to protect the counter value while | |
| 1802 | + * it cannot reach zero or thread->is_dead is false | |
| 1803 | + */ | |
| 1804 | + binder_inner_proc_lock(thread->proc); | |
| 1805 | + atomic_dec(&thread->tmp_ref); | |
| 1806 | + if (thread->is_dead && !atomic_read(&thread->tmp_ref)) { | |
| 1807 | + binder_inner_proc_unlock(thread->proc); | |
| 1808 | + binder_free_thread(thread); | |
| 1809 | + return; | |
| 1177 | 1810 | } |
| 1178 | − return 0; | |
| 1811 | + binder_inner_proc_unlock(thread->proc); | |
| 1179 | 1812 | } |
| 1180 | 1813 | |
| 1814 | +/** | |
| 1815 | + * binder_proc_dec_tmpref() - decrement proc->tmp_ref | |
| 1816 | + * @proc: proc to decrement | |
| 1817 | + * | |
| 1818 | + * A binder_proc needs to be kept alive while being used to create or | |
| 1819 | + * handle a transaction. proc->tmp_ref is incremented when | |
| 1820 | + * creating a new transaction or the binder_proc is currently in-use | |
| 1821 | + * by threads that are being released. When done with the binder_proc, | |
| 1822 | + * this function is called to decrement the counter and free the | |
| 1823 | + * proc if appropriate (proc has been released, all threads have | |
| 1824 | + * been released and not currenly in-use to process a transaction). | |
| 1825 | + */ | |
| 1826 | +static void binder_proc_dec_tmpref(struct binder_proc *proc) | |
| 1827 | +{ | |
| 1828 | + binder_inner_proc_lock(proc); | |
| 1829 | + proc->tmp_ref--; | |
| 1830 | + if (proc->is_dead && RB_EMPTY_ROOT(&proc->threads) && | |
| 1831 | + !proc->tmp_ref) { | |
| 1832 | + binder_inner_proc_unlock(proc); | |
| 1833 | + binder_free_proc(proc); | |
| 1834 | + return; | |
| 1835 | + } | |
| 1836 | + binder_inner_proc_unlock(proc); | |
| 1837 | +} | |
| 1181 | 1838 | |
| 1182 | −static int binder_dec_ref(struct binder_ref *ref, int strong) | |
| 1839 | +/** | |
| 1840 | + * binder_get_txn_from() - safely extract the "from" thread in transaction | |
| 1841 | + * @t: binder transaction for t->from | |
| 1842 | + * | |
| 1843 | + * Atomically return the "from" thread and increment the tmp_ref | |
| 1844 | + * count for the thread to ensure it stays alive until | |
| 1845 | + * binder_thread_dec_tmpref() is called. | |
| 1846 | + * | |
| 1847 | + * Return: the value of t->from | |
| 1848 | + */ | |
| 1849 | +static struct binder_thread *binder_get_txn_from( | |
| 1850 | + struct binder_transaction *t) | |
| 1183 | 1851 | { |
| 1184 | − if (strong) { | |
| 1185 | − if (ref->strong == 0) { | |
| 1186 | − binder_user_error("%d invalid dec strong, ref %d desc %d s %d w %d\n", | |
| 1187 | − ref->proc->pid, ref->debug_id, | |
| 1188 | − ref->desc, ref->strong, ref->weak); | |
| 1189 | − return -EINVAL; | |
| 1190 | − } | |
| 1191 | − ref->strong--; | |
| 1192 | − if (ref->strong == 0) { | |
| 1193 | − int ret; | |
| 1852 | + struct binder_thread *from; | |
| 1194 | 1853 | |
| 1195 | − ret = binder_dec_node(ref->node, strong, 1); | |
| 1196 | − if (ret) | |
| 1197 | − return ret; | |
| 1198 | − } | |
| 1199 | − } else { | |
| 1200 | − if (ref->weak == 0) { | |
| 1201 | − binder_user_error("%d invalid dec weak, ref %d desc %d s %d w %d\n", | |
| 1202 | − ref->proc->pid, ref->debug_id, | |
| 1203 | − ref->desc, ref->strong, ref->weak); | |
| 1204 | − return -EINVAL; | |
| 1205 | − } | |
| 1206 | − ref->weak--; | |
| 1207 | − } | |
| 1208 | − if (ref->strong == 0 && ref->weak == 0) | |
| 1209 | − binder_delete_ref(ref); | |
| 1210 | − return 0; | |
| 1854 | + spin_lock(&t->lock); | |
| 1855 | + from = t->from; | |
| 1856 | + if (from) | |
| 1857 | + atomic_inc(&from->tmp_ref); | |
| 1858 | + spin_unlock(&t->lock); | |
| 1859 | + return from; | |
| 1211 | 1860 | } |
| 1212 | 1861 | |
| 1213 | −static void binder_pop_transaction(struct binder_thread *target_thread, | |
| 1214 | − struct binder_transaction *t) | |
| 1862 | +/** | |
| 1863 | + * binder_get_txn_from_and_acq_inner() - get t->from and acquire inner lock | |
| 1864 | + * @t: binder transaction for t->from | |
| 1865 | + * | |
| 1866 | + * Same as binder_get_txn_from() except it also acquires the proc->inner_lock | |
| 1867 | + * to guarantee that the thread cannot be released while operating on it. | |
| 1868 | + * The caller must call binder_inner_proc_unlock() to release the inner lock | |
| 1869 | + * as well as call binder_dec_thread_txn() to release the reference. | |
| 1870 | + * | |
| 1871 | + * Return: the value of t->from | |
| 1872 | + */ | |
| 1873 | +static struct binder_thread *binder_get_txn_from_and_acq_inner( | |
| 1874 | + struct binder_transaction *t) | |
| 1215 | 1875 | { |
| 1216 | − if (target_thread) { | |
| 1217 | − BUG_ON(target_thread->transaction_stack != t); | |
| 1218 | − BUG_ON(target_thread->transaction_stack->from != target_thread); | |
| 1219 | − target_thread->transaction_stack = | |
| 1220 | − target_thread->transaction_stack->from_parent; | |
| 1221 | − t->from = NULL; | |
| 1876 | + struct binder_thread *from; | |
| 1877 | + | |
| 1878 | + from = binder_get_txn_from(t); | |
| 1879 | + if (!from) | |
| 1880 | + return NULL; | |
| 1881 | + binder_inner_proc_lock(from->proc); | |
| 1882 | + if (t->from) { | |
| 1883 | + BUG_ON(from != t->from); | |
| 1884 | + return from; | |
| 1222 | 1885 | } |
| 1223 | − t->need_reply = 0; | |
| 1886 | + binder_inner_proc_unlock(from->proc); | |
| 1887 | + binder_thread_dec_tmpref(from); | |
| 1888 | + return NULL; | |
| 1889 | +} | |
| 1890 | + | |
| 1891 | +static void binder_free_transaction(struct binder_transaction *t) | |
| 1892 | +{ | |
| 1224 | 1893 | if (t->buffer) |
| 1225 | 1894 | t->buffer->transaction = NULL; |
| 1226 | 1895 | kfree(t); |
| @@ -1235,30 +1904,28 @@ static void binder_send_failed_reply(struct binder_transaction *t, · 8 unmodified lines | ||
| 1235 | 1904 | |
| 1236 | 1905 | BUG_ON(t->flags & TF_ONE_WAY); |
| 1237 | 1906 | while (1) { |
| 1238 | − target_thread = t->from; | |
| 1907 | + target_thread = binder_get_txn_from_and_acq_inner(t); | |
| 1239 | 1908 | if (target_thread) { |
| 1240 | − if (target_thread->return_error != BR_OK && | |
| 1241 | − target_thread->return_error2 == BR_OK) { | |
| 1242 | − target_thread->return_error2 = | |
| 1243 | − target_thread->return_error; | |
| 1244 | − target_thread->return_error = BR_OK; | |
| 1245 | − } | |
| 1246 | − if (target_thread->return_error == BR_OK) { | |
| 1247 | − binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, | |
| 1248 | − "send failed reply for transaction %d to %d:%d\n", | |
| 1249 | − t->debug_id, | |
| 1250 | − target_thread->proc->pid, | |
| 1251 | − target_thread->pid); | |
| 1252 | − | |
| 1253 | − binder_pop_transaction(target_thread, t); | |
| 1254 | − target_thread->return_error = error_code; | |
| 1909 | + binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, | |
| 1910 | + "send failed reply for transaction %d to %d:%d\n", | |
| 1911 | + t->debug_id, | |
| 1912 | + target_thread->proc->pid, | |
| 1913 | + target_thread->pid); | |
| 1914 | + | |
| 1915 | + binder_pop_transaction_ilocked(target_thread, t); | |
| 1916 | + if (target_thread->reply_error.cmd == BR_OK) { | |
| 1917 | + target_thread->reply_error.cmd = error_code; | |
| 1918 | + binder_enqueue_work_ilocked( | |
| 1919 | + &target_thread->reply_error.work, | |
| 1920 | + &target_thread->todo); | |
| 1255 | 1921 | wake_up_interruptible(&target_thread->wait); |
| 1256 | 1922 | } else { |
| 1257 | − pr_err("reply failed, target thread, %d:%d, has error code %d already\n", | |
| 1258 | − target_thread->proc->pid, | |
| 1259 | − target_thread->pid, | |
| 1260 | − target_thread->return_error); | |
| 1923 | + WARN(1, "Unexpected reply error: %u\n", | |
| 1924 | + target_thread->reply_error.cmd); | |
| 1261 | 1925 | } |
| 1926 | + binder_inner_proc_unlock(target_thread->proc); | |
| 1927 | + binder_thread_dec_tmpref(target_thread); | |
| 1928 | + binder_free_transaction(t); | |
| 1262 | 1929 | return; |
| 1263 | 1930 | } |
| 1264 | 1931 | next = t->from_parent; |
| @@ -1267,7 +1934,7 @@ static void binder_send_failed_reply(struct binder_transaction *t, · 2 unmodified lines | ||
| 1267 | 1934 | "send failed reply for transaction %d, target dead\n", |
| 1268 | 1935 | t->debug_id); |
| 1269 | 1936 | |
| 1270 | − binder_pop_transaction(target_thread, t); | |
| 1937 | + binder_free_transaction(t); | |
| 1271 | 1938 | if (next == NULL) { |
| 1272 | 1939 | binder_debug(BINDER_DEBUG_DEAD_BINDER, |
| 1273 | 1940 | "reply failed, no target thread at root\n"); |
| @@ -1476,24 +2143,26 @@ static void binder_transaction_buffer_release(struct binder_proc *proc, · 202 unmodified lines | ||
| 1476 | 2143 | node->debug_id, (u64)node->ptr); |
| 1477 | 2144 | binder_dec_node(node, hdr->type == BINDER_TYPE_BINDER, |
| 1478 | 2145 | 0); |
| 2146 | + binder_put_node(node); | |
| 1479 | 2147 | } break; |
| 1480 | 2148 | case BINDER_TYPE_HANDLE: |
| 1481 | 2149 | case BINDER_TYPE_WEAK_HANDLE: { |
| 1482 | 2150 | struct flat_binder_object *fp; |
| 1483 | − struct binder_ref *ref; | |
| 2151 | + struct binder_ref_data rdata; | |
| 2152 | + int ret; | |
| 1484 | 2153 | |
| 1485 | 2154 | fp = to_flat_binder_object(hdr); |
| 1486 | − ref = binder_get_ref(proc, fp->handle, | |
| 1487 | − hdr->type == BINDER_TYPE_HANDLE); | |
| 1488 | − if (ref == NULL) { | |
| 1489 | − pr_err("transaction release %d bad handle %d\n", | |
| 1490 | − debug_id, fp->handle); | |
| 2155 | + ret = binder_dec_ref_for_handle(proc, fp->handle, | |
| 2156 | + hdr->type == BINDER_TYPE_HANDLE, &rdata); | |
| 2157 | + | |
| 2158 | + if (ret) { | |
| 2159 | + pr_err("transaction release %d bad handle %d, ret = %d\n", | |
| 2160 | + debug_id, fp->handle, ret); | |
| 1491 | 2161 | break; |
| 1492 | 2162 | } |
| 1493 | 2163 | binder_debug(BINDER_DEBUG_TRANSACTION, |
| 1494 | − " ref %d desc %d (node %d)\n", | |
| 1495 | − ref->debug_id, ref->desc, ref->node->debug_id); | |
| 1496 | − binder_dec_ref(ref, hdr->type == BINDER_TYPE_HANDLE); | |
| 2164 | + " ref %d desc %d\n", | |
| 2165 | + rdata.debug_id, rdata.desc); | |
| 1497 | 2166 | } break; |
| 1498 | 2167 | |
| 1499 | 2168 | case BINDER_TYPE_FD: { |
| @@ -1532,7 +2201,8 @@ static void binder_transaction_buffer_release(struct binder_proc *proc, · 32 unmodified lines | ||
| 1532 | 2201 | * back to kernel address space to access it |
| 1533 | 2202 | */ |
| 1534 | 2203 | parent_buffer = parent->buffer - |
| 1535 | − proc->user_buffer_offset; | |
| 2204 | + binder_alloc_get_user_buffer_offset( | |
| 2205 | + &proc->alloc); | |
| 1536 | 2206 | |
| 1537 | 2207 | fd_buf_size = sizeof(u32) * fda->num_fds; |
| 1538 | 2208 | if (fda->num_fds >= SIZE_MAX / sizeof(u32)) { |
| @@ -1564,102 +2234,122 @@ static int binder_translate_binder(struct flat_binder_object *fp, · 25 unmodified lines | ||
| 1564 | 2234 | struct binder_thread *thread) |
| 1565 | 2235 | { |
| 1566 | 2236 | struct binder_node *node; |
| 1567 | − struct binder_ref *ref; | |
| 1568 | 2237 | struct binder_proc *proc = thread->proc; |
| 1569 | 2238 | struct binder_proc *target_proc = t->to_proc; |
| 2239 | + struct binder_ref_data rdata; | |
| 2240 | + int ret = 0; | |
| 1570 | 2241 | |
| 1571 | 2242 | node = binder_get_node(proc, fp->binder); |
| 1572 | 2243 | if (!node) { |
| 1573 | − node = binder_new_node(proc, fp->binder, fp->cookie); | |
| 2244 | + node = binder_new_node(proc, fp); | |
| 1574 | 2245 | if (!node) |
| 1575 | 2246 | return -ENOMEM; |
| 1576 | − | |
| 1577 | − node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK; | |
| 1578 | − node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS); | |
| 1579 | 2247 | } |
| 1580 | 2248 | if (fp->cookie != node->cookie) { |
| 1581 | 2249 | binder_user_error("%d:%d sending u%016llx node %d, cookie mismatch %016llx != %016llx\n", |
| 1582 | 2250 | proc->pid, thread->pid, (u64)fp->binder, |
| 1583 | 2251 | node->debug_id, (u64)fp->cookie, |
| 1584 | 2252 | (u64)node->cookie); |
| 1585 | − return -EINVAL; | |
| 2253 | + ret = -EINVAL; | |
| 2254 | + goto done; | |
| 2255 | + } | |
| 2256 | + if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) { | |
| 2257 | + ret = -EPERM; | |
| 2258 | + goto done; | |
| 1586 | 2259 | } |
| 1587 | − if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) | |
| 1588 | − return -EPERM; | |
| 1589 | 2260 | |
| 1590 | − ref = binder_get_ref_for_node(target_proc, node); | |
| 1591 | − if (!ref) | |
| 1592 | − return -EINVAL; | |
| 2261 | + ret = binder_inc_ref_for_node(target_proc, node, | |
| 2262 | + fp->hdr.type == BINDER_TYPE_BINDER, | |
| 2263 | + &thread->todo, &rdata); | |
| 2264 | + if (ret) | |
| 2265 | + goto done; | |
| 1593 | 2266 | |
| 1594 | 2267 | if (fp->hdr.type == BINDER_TYPE_BINDER) |
| 1595 | 2268 | fp->hdr.type = BINDER_TYPE_HANDLE; |
| 1596 | 2269 | else |
| 1597 | 2270 | fp->hdr.type = BINDER_TYPE_WEAK_HANDLE; |
| 1598 | 2271 | fp->binder = 0; |
| 1599 | − fp->handle = ref->desc; | |
| 2272 | + fp->handle = rdata.desc; | |
| 1600 | 2273 | fp->cookie = 0; |
| 1601 | − binder_inc_ref(ref, fp->hdr.type == BINDER_TYPE_HANDLE, &thread->todo); | |
| 1602 | 2274 | |
| 1603 | − trace_binder_transaction_node_to_ref(t, node, ref); | |
| 2275 | + trace_binder_transaction_node_to_ref(t, node, &rdata); | |
| 1604 | 2276 | binder_debug(BINDER_DEBUG_TRANSACTION, |
| 1605 | 2277 | " node %d u%016llx -> ref %d desc %d\n", |
| 1606 | 2278 | node->debug_id, (u64)node->ptr, |
| 1607 | − ref->debug_id, ref->desc); | |
| 1608 | − | |
| 1609 | − return 0; | |
| 2279 | + rdata.debug_id, rdata.desc); | |
| 2280 | +done: | |
| 2281 | + binder_put_node(node); | |
| 2282 | + return ret; | |
| 1610 | 2283 | } |
| 1611 | 2284 | |
| 1612 | 2285 | static int binder_translate_handle(struct flat_binder_object *fp, |
| 1613 | 2286 | struct binder_transaction *t, |
| 1614 | 2287 | struct binder_thread *thread) |
| 1615 | 2288 | { |
| 1616 | − struct binder_ref *ref; | |
| 1617 | 2289 | struct binder_proc *proc = thread->proc; |
| 1618 | 2290 | struct binder_proc *target_proc = t->to_proc; |
| 2291 | + struct binder_node *node; | |
| 2292 | + struct binder_ref_data src_rdata; | |
| 2293 | + int ret = 0; | |
| 1619 | 2294 | |
| 1620 | − ref = binder_get_ref(proc, fp->handle, | |
| 1621 | − fp->hdr.type == BINDER_TYPE_HANDLE); | |
| 1622 | − if (!ref) { | |
| 2295 | + node = binder_get_node_from_ref(proc, fp->handle, | |
| 2296 | + fp->hdr.type == BINDER_TYPE_HANDLE, &src_rdata); | |
| 2297 | + if (!node) { | |
| 1623 | 2298 | binder_user_error("%d:%d got transaction with invalid handle, %d\n", |
| 1624 | 2299 | proc->pid, thread->pid, fp->handle); |
| 1625 | 2300 | return -EINVAL; |
| 1626 | 2301 | } |
| 1627 | − if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) | |
| 1628 | − return -EPERM; | |
| 2302 | + if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) { | |
| 2303 | + ret = -EPERM; | |
| 2304 | + goto done; | |
| 2305 | + } | |
| 1629 | 2306 | |
| 1630 | − if (ref->node->proc == target_proc) { | |
| 2307 | + binder_node_lock(node); | |
| 2308 | + if (node->proc == target_proc) { | |
| 1631 | 2309 | if (fp->hdr.type == BINDER_TYPE_HANDLE) |
| 1632 | 2310 | fp->hdr.type = BINDER_TYPE_BINDER; |
| 1633 | 2311 | else |
| 1634 | 2312 | fp->hdr.type = BINDER_TYPE_WEAK_BINDER; |
| 1635 | − fp->binder = ref->node->ptr; | |
| 1636 | − fp->cookie = ref->node->cookie; | |
| 1637 | − binder_inc_node(ref->node, fp->hdr.type == BINDER_TYPE_BINDER, | |
| 1638 | − 0, NULL); | |
| 1639 | − trace_binder_transaction_ref_to_node(t, ref); | |
| 2313 | + fp->binder = node->ptr; | |
| 2314 | + fp->cookie = node->cookie; | |
| 2315 | + if (node->proc) | |
| 2316 | + binder_inner_proc_lock(node->proc); | |
| 2317 | + binder_inc_node_nilocked(node, | |
| 2318 | + fp->hdr.type == BINDER_TYPE_BINDER, | |
| 2319 | + 0, NULL); | |
| 2320 | + if (node->proc) | |
| 2321 | + binder_inner_proc_unlock(node->proc); | |
| 2322 | + trace_binder_transaction_ref_to_node(t, node, &src_rdata); | |
| 1640 | 2323 | binder_debug(BINDER_DEBUG_TRANSACTION, |
| 1641 | 2324 | " ref %d desc %d -> node %d u%016llx\n", |
| 1642 | − ref->debug_id, ref->desc, ref->node->debug_id, | |
| 1643 | − (u64)ref->node->ptr); | |
| 2325 | + src_rdata.debug_id, src_rdata.desc, node->debug_id, | |
| 2326 | + (u64)node->ptr); | |
| 2327 | + binder_node_unlock(node); | |
| 1644 | 2328 | } else { |
| 1645 | − struct binder_ref *new_ref; | |
| 2329 | + int ret; | |
| 2330 | + struct binder_ref_data dest_rdata; | |
| 1646 | 2331 | |
| 1647 | − new_ref = binder_get_ref_for_node(target_proc, ref->node); | |
| 1648 | − if (!new_ref) | |
| 1649 | − return -EINVAL; | |
| 2332 | + binder_node_unlock(node); | |
| 2333 | + ret = binder_inc_ref_for_node(target_proc, node, | |
| 2334 | + fp->hdr.type == BINDER_TYPE_HANDLE, | |
| 2335 | + NULL, &dest_rdata); | |
| 2336 | + if (ret) | |
| 2337 | + goto done; | |
| 1650 | 2338 | |
| 1651 | 2339 | fp->binder = 0; |
| 1652 | − fp->handle = new_ref->desc; | |
| 2340 | + fp->handle = dest_rdata.desc; | |
| 1653 | 2341 | fp->cookie = 0; |
| 1654 | − binder_inc_ref(new_ref, fp->hdr.type == BINDER_TYPE_HANDLE, | |
| 1655 | − NULL); | |
| 1656 | − trace_binder_transaction_ref_to_ref(t, ref, new_ref); | |
| 2342 | + trace_binder_transaction_ref_to_ref(t, node, &src_rdata, | |
| 2343 | + &dest_rdata); | |
| 1657 | 2344 | binder_debug(BINDER_DEBUG_TRANSACTION, |
| 1658 | 2345 | " ref %d desc %d -> ref %d desc %d (node %d)\n", |
| 1659 | − ref->debug_id, ref->desc, new_ref->debug_id, | |
| 1660 | − new_ref->desc, ref->node->debug_id); | |
| 2346 | + src_rdata.debug_id, src_rdata.desc, | |
| 2347 | + dest_rdata.debug_id, dest_rdata.desc, | |
| 2348 | + node->debug_id); | |
| 1661 | 2349 | } |
| 1662 | − return 0; | |
| 2350 | +done: | |
| 2351 | + binder_put_node(node); | |
| 2352 | + return ret; | |
| 1663 | 2353 | } |
| 1664 | 2354 | |
| 1665 | 2355 | static int binder_translate_fd(int fd, |
| @@ -1750,7 +2440,8 @@ static int binder_translate_fd_array(struct binder_fd_array_object *fda, · 84 unmodified lines | ||
| 1750 | 2440 | * Since the parent was already fixed up, convert it |
| 1751 | 2441 | * back to the kernel address space to access it |
| 1752 | 2442 | */ |
| 1753 | − parent_buffer = parent->buffer - target_proc->user_buffer_offset; | |
| 2443 | + parent_buffer = parent->buffer - | |
| 2444 | + binder_alloc_get_user_buffer_offset(&target_proc->alloc); | |
| 1754 | 2445 | fd_array = (u32 *)(parent_buffer + fda->parent_offset); |
| 1755 | 2446 | if (!IS_ALIGNED((unsigned long)fd_array, sizeof(u32))) { |
| 1756 | 2447 | binder_user_error("%d:%d parent offset not aligned correctly.\n", |
| @@ -1818,12 +2509,80 @@ static int binder_fixup_parent(struct binder_transaction *t, · 61 unmodified lines | ||
| 1818 | 2509 | return -EINVAL; |
| 1819 | 2510 | } |
| 1820 | 2511 | parent_buffer = (u8 *)(parent->buffer - |
| 1821 | − target_proc->user_buffer_offset); | |
| 2512 | + binder_alloc_get_user_buffer_offset( | |
| 2513 | + &target_proc->alloc)); | |
| 1822 | 2514 | *(binder_uintptr_t *)(parent_buffer + bp->parent_offset) = bp->buffer; |
| 1823 | 2515 | |
| 1824 | 2516 | return 0; |
| 1825 | 2517 | } |
| 1826 | 2518 | |
| 2519 | +/** | |
| 2520 | + * binder_proc_transaction() - sends a transaction to a process and wakes it up | |
| 2521 | + * @t: transaction to send | |
| 2522 | + * @proc: process to send the transaction to | |
| 2523 | + * @thread: thread in @proc to send the transaction to (may be NULL) | |
| 2524 | + * | |
| 2525 | + * This function queues a transaction to the specified process. It will try | |
| 2526 | + * to find a thread in the target process to handle the transaction and | |
| 2527 | + * wake it up. If no thread is found, the work is queued to the proc | |
| 2528 | + * waitqueue. | |
| 2529 | + * | |
| 2530 | + * If the @thread parameter is not NULL, the transaction is always queued | |
| 2531 | + * to the waitlist of that specific thread. | |
| 2532 | + * | |
| 2533 | + * Return: true if the transactions was successfully queued | |
| 2534 | + * false if the target process or thread is dead | |
| 2535 | + */ | |
| 2536 | +static bool binder_proc_transaction(struct binder_transaction *t, | |
| 2537 | + struct binder_proc *proc, | |
| 2538 | + struct binder_thread *thread) | |
| 2539 | +{ | |
| 2540 | + struct list_head *target_list = NULL; | |
| 2541 | + struct binder_node *node = t->buffer->target_node; | |
| 2542 | + bool oneway = !!(t->flags & TF_ONE_WAY); | |
| 2543 | + bool wakeup = true; | |
| 2544 | + | |
| 2545 | + BUG_ON(!node); | |
| 2546 | + binder_node_lock(node); | |
| 2547 | + if (oneway) { | |
| 2548 | + BUG_ON(thread); | |
| 2549 | + if (node->has_async_transaction) { | |
| 2550 | + target_list = &node->async_todo; | |
| 2551 | + wakeup = false; | |
| 2552 | + } else { | |
| 2553 | + node->has_async_transaction = 1; | |
| 2554 | + } | |
| 2555 | + } | |
| 2556 | + | |
| 2557 | + binder_inner_proc_lock(proc); | |
| 2558 | + | |
| 2559 | + if (proc->is_dead || (thread && thread->is_dead)) { | |
| 2560 | + binder_inner_proc_unlock(proc); | |
| 2561 | + binder_node_unlock(node); | |
| 2562 | + return false; | |
| 2563 | + } | |
| 2564 | + | |
| 2565 | + if (!thread && !target_list) | |
| 2566 | + thread = binder_select_thread_ilocked(proc); | |
| 2567 | + | |
| 2568 | + if (thread) | |
| 2569 | + target_list = &thread->todo; | |
| 2570 | + else if (!target_list) | |
| 2571 | + target_list = &proc->todo; | |
| 2572 | + else | |
| 2573 | + BUG_ON(target_list != &node->async_todo); | |
| 2574 | + | |
| 2575 | + binder_enqueue_work_ilocked(&t->work, target_list); | |
| 2576 | + | |
| 2577 | + if (wakeup) | |
| 2578 | + binder_wakeup_thread_ilocked(proc, thread, !oneway /* sync */); | |
| 2579 | + | |
| 2580 | + binder_inner_proc_unlock(proc); | |
| 2581 | + binder_node_unlock(node); | |
| 2582 | + | |
| 2583 | + return true; | |
| 2584 | +} | |
| 2585 | + | |
| 1827 | 2586 | static void binder_transaction(struct binder_proc *proc, |
| 1828 | 2587 | struct binder_thread *thread, |
| 1829 | 2588 | struct binder_transaction_data *tr, int reply, |
| @@ -1835,19 +2594,21 @@ static void binder_transaction(struct binder_proc *proc, · 5 unmodified lines | ||
| 1835 | 2594 | binder_size_t *offp, *off_end, *off_start; |
| 1836 | 2595 | binder_size_t off_min; |
| 1837 | 2596 | u8 *sg_bufp, *sg_buf_end; |
| 1838 | − struct binder_proc *target_proc; | |
| 2597 | + struct binder_proc *target_proc = NULL; | |
| 1839 | 2598 | struct binder_thread *target_thread = NULL; |
| 1840 | 2599 | struct binder_node *target_node = NULL; |
| 1841 | − struct list_head *target_list; | |
| 1842 | − wait_queue_head_t *target_wait; | |
| 1843 | 2600 | struct binder_transaction *in_reply_to = NULL; |
| 1844 | 2601 | struct binder_transaction_log_entry *e; |
| 1845 | − uint32_t return_error; | |
| 2602 | + uint32_t return_error = 0; | |
| 2603 | + uint32_t return_error_param = 0; | |
| 2604 | + uint32_t return_error_line = 0; | |
| 1846 | 2605 | struct binder_buffer_object *last_fixup_obj = NULL; |
| 1847 | 2606 | binder_size_t last_fixup_min_off = 0; |
| 1848 | 2607 | struct binder_context *context = proc->context; |
| 2608 | + int t_debug_id = atomic_inc_return(&binder_last_id); | |
| 1849 | 2609 | |
| 1850 | 2610 | e = binder_transaction_log_add(&binder_transaction_log); |
| 2611 | + e->debug_id = t_debug_id; | |
| 1851 | 2612 | e->call_type = reply ? 2 : !!(tr->flags & TF_ONE_WAY); |
| 1852 | 2613 | e->from_proc = proc->pid; |
| 1853 | 2614 | e->from_thread = thread->pid; |
| @@ -1857,29 +2618,40 @@ static void binder_transaction(struct binder_proc *proc, · 3 unmodified lines | ||
| 1857 | 2618 | e->context_name = proc->context->name; |
| 1858 | 2619 | |
| 1859 | 2620 | if (reply) { |
| 2621 | + binder_inner_proc_lock(proc); | |
| 1860 | 2622 | in_reply_to = thread->transaction_stack; |
| 1861 | 2623 | if (in_reply_to == NULL) { |
| 2624 | + binder_inner_proc_unlock(proc); | |
| 1862 | 2625 | binder_user_error("%d:%d got reply transaction with no transaction stack\n", |
| 1863 | 2626 | proc->pid, thread->pid); |
| 1864 | 2627 | return_error = BR_FAILED_REPLY; |
| 2628 | + return_error_param = -EPROTO; | |
| 2629 | + return_error_line = __LINE__; | |
| 1865 | 2630 | goto err_empty_call_stack; |
| 1866 | 2631 | } |
| 1867 | − binder_set_nice(in_reply_to->saved_priority); | |
| 1868 | 2632 | if (in_reply_to->to_thread != thread) { |
| 2633 | + spin_lock(&in_reply_to->lock); | |
| 1869 | 2634 | binder_user_error("%d:%d got reply transaction with bad transaction stack, transaction %d has target %d:%d\n", |
| 1870 | 2635 | proc->pid, thread->pid, in_reply_to->debug_id, |
| 1871 | 2636 | in_reply_to->to_proc ? |
| 1872 | 2637 | in_reply_to->to_proc->pid : 0, |
| 1873 | 2638 | in_reply_to->to_thread ? |
| 1874 | 2639 | in_reply_to->to_thread->pid : 0); |
| 2640 | + spin_unlock(&in_reply_to->lock); | |
| 2641 | + binder_inner_proc_unlock(proc); | |
| 1875 | 2642 | return_error = BR_FAILED_REPLY; |
| 2643 | + return_error_param = -EPROTO; | |
| 2644 | + return_error_line = __LINE__; | |
| 1876 | 2645 | in_reply_to = NULL; |
| 1877 | 2646 | goto err_bad_call_stack; |
| 1878 | 2647 | } |
| 1879 | 2648 | thread->transaction_stack = in_reply_to->to_parent; |
| 1880 | − target_thread = in_reply_to->from; | |
| 2649 | + binder_inner_proc_unlock(proc); | |
| 2650 | + binder_set_nice(in_reply_to->saved_priority); | |
| 2651 | + target_thread = binder_get_txn_from_and_acq_inner(in_reply_to); | |
| 1881 | 2652 | if (target_thread == NULL) { |
| 1882 | 2653 | return_error = BR_DEAD_REPLY; |
| 2654 | + return_error_line = __LINE__; | |
| 1883 | 2655 | goto err_dead_binder; |
| 1884 | 2656 | } |
| 1885 | 2657 | if (target_thread->transaction_stack != in_reply_to) { |
| @@ -1888,89 +2660,137 @@ static void binder_transaction(struct binder_proc *proc, · 2 unmodified lines | ||
| 1888 | 2660 | target_thread->transaction_stack ? |
| 1889 | 2661 | target_thread->transaction_stack->debug_id : 0, |
| 1890 | 2662 | in_reply_to->debug_id); |
| 2663 | + binder_inner_proc_unlock(target_thread->proc); | |
| 1891 | 2664 | return_error = BR_FAILED_REPLY; |
| 2665 | + return_error_param = -EPROTO; | |
| 2666 | + return_error_line = __LINE__; | |
| 1892 | 2667 | in_reply_to = NULL; |
| 1893 | 2668 | target_thread = NULL; |
| 1894 | 2669 | goto err_dead_binder; |
| 1895 | 2670 | } |
| 1896 | 2671 | target_proc = target_thread->proc; |
| 2672 | + target_proc->tmp_ref++; | |
| 2673 | + binder_inner_proc_unlock(target_thread->proc); | |
| 1897 | 2674 | } else { |
| 1898 | 2675 | if (tr->target.handle) { |
| 1899 | 2676 | struct binder_ref *ref; |
| 1900 | 2677 | |
| 1901 | − ref = binder_get_ref(proc, tr->target.handle, true); | |
| 1902 | − if (ref == NULL) { | |
| 2678 | + /* | |
| 2679 | + * There must already be a strong ref | |
| 2680 | + * on this node. If so, do a strong | |
| 2681 | + * increment on the node to ensure it | |
| 2682 | + * stays alive until the transaction is | |
| 2683 | + * done. | |
| 2684 | + */ | |
| 2685 | + binder_proc_lock(proc); | |
| 2686 | + ref = binder_get_ref_olocked(proc, tr->target.handle, | |
| 2687 | + true); | |
| 2688 | + if (ref) { | |
| 2689 | + binder_inc_node(ref->node, 1, 0, NULL); | |
| 2690 | + target_node = ref->node; | |
| 2691 | + } | |
| 2692 | + binder_proc_unlock(proc); | |
| 2693 | + if (target_node == NULL) { | |
| 1903 | 2694 | binder_user_error("%d:%d got transaction to invalid handle\n", |
| 1904 | 2695 | proc->pid, thread->pid); |
| 1905 | 2696 | return_error = BR_FAILED_REPLY; |
| 2697 | + return_error_param = -EINVAL; | |
| 2698 | + return_error_line = __LINE__; | |
| 1906 | 2699 | goto err_invalid_target_handle; |
| 1907 | 2700 | } |
| 1908 | − target_node = ref->node; | |
| 1909 | 2701 | } else { |
| 2702 | + mutex_lock(&context->context_mgr_node_lock); | |
| 1910 | 2703 | target_node = context->binder_context_mgr_node; |
| 1911 | 2704 | if (target_node == NULL) { |
| 1912 | 2705 | return_error = BR_DEAD_REPLY; |
| 2706 | + mutex_unlock(&context->context_mgr_node_lock); | |
| 2707 | + return_error_line = __LINE__; | |
| 1913 | 2708 | goto err_no_context_mgr_node; |
| 1914 | 2709 | } |
| 2710 | + binder_inc_node(target_node, 1, 0, NULL); | |
| 2711 | + mutex_unlock(&context->context_mgr_node_lock); | |
| 1915 | 2712 | } |
| 1916 | 2713 | e->to_node = target_node->debug_id; |
| 2714 | + binder_node_lock(target_node); | |
| 1917 | 2715 | target_proc = target_node->proc; |
| 1918 | 2716 | if (target_proc == NULL) { |
| 2717 | + binder_node_unlock(target_node); | |
| 1919 | 2718 | return_error = BR_DEAD_REPLY; |
| 2719 | + return_error_line = __LINE__; | |
| 1920 | 2720 | goto err_dead_binder; |
| 1921 | 2721 | } |
| 2722 | + binder_inner_proc_lock(target_proc); | |
| 2723 | + target_proc->tmp_ref++; | |
| 2724 | + binder_inner_proc_unlock(target_proc); | |
| 2725 | + binder_node_unlock(target_node); | |
| 1922 | 2726 | if (security_binder_transaction(proc->tsk, |
| 1923 | 2727 | target_proc->tsk) < 0) { |
| 1924 | 2728 | return_error = BR_FAILED_REPLY; |
| 2729 | + return_error_param = -EPERM; | |
| 2730 | + return_error_line = __LINE__; | |
| 1925 | 2731 | goto err_invalid_target_handle; |
| 1926 | 2732 | } |
| 2733 | + binder_inner_proc_lock(proc); | |
| 1927 | 2734 | if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) { |
| 1928 | 2735 | struct binder_transaction *tmp; |
| 1929 | 2736 | |
| 1930 | 2737 | tmp = thread->transaction_stack; |
| 1931 | 2738 | if (tmp->to_thread != thread) { |
| 2739 | + spin_lock(&tmp->lock); | |
| 1932 | 2740 | binder_user_error("%d:%d got new transaction with bad transaction stack, transaction %d has target %d:%d\n", |
| 1933 | 2741 | proc->pid, thread->pid, tmp->debug_id, |
| 1934 | 2742 | tmp->to_proc ? tmp->to_proc->pid : 0, |
| 1935 | 2743 | tmp->to_thread ? |
| 1936 | 2744 | tmp->to_thread->pid : 0); |
| 2745 | + spin_unlock(&tmp->lock); | |
| 2746 | + binder_inner_proc_unlock(proc); | |
| 1937 | 2747 | return_error = BR_FAILED_REPLY; |
| 2748 | + return_error_param = -EPROTO; | |
| 2749 | + return_error_line = __LINE__; | |
| 1938 | 2750 | goto err_bad_call_stack; |
| 1939 | 2751 | } |
| 1940 | 2752 | while (tmp) { |
| 1941 | − if (tmp->from && tmp->from->proc == target_proc) | |
| 1942 | − target_thread = tmp->from; | |
| 2753 | + struct binder_thread *from; | |
| 2754 | + | |
| 2755 | + spin_lock(&tmp->lock); | |
| 2756 | + from = tmp->from; | |
| 2757 | + if (from && from->proc == target_proc) { | |
| 2758 | + atomic_inc(&from->tmp_ref); | |
| 2759 | + target_thread = from; | |
| 2760 | + spin_unlock(&tmp->lock); | |
| 2761 | + break; | |
| 2762 | + } | |
| 2763 | + spin_unlock(&tmp->lock); | |
| 1943 | 2764 | tmp = tmp->from_parent; |
| 1944 | 2765 | } |
| 1945 | 2766 | } |
| 2767 | + binder_inner_proc_unlock(proc); | |
| 1946 | 2768 | } |
| 1947 | − if (target_thread) { | |
| 2769 | + if (target_thread) | |
| 1948 | 2770 | e->to_thread = target_thread->pid; |
| 1949 | − target_list = &target_thread->todo; | |
| 1950 | − target_wait = &target_thread->wait; | |
| 1951 | − } else { | |
| 1952 | − target_list = &target_proc->todo; | |
| 1953 | − target_wait = &target_proc->wait; | |
| 1954 | − } | |
| 1955 | 2771 | e->to_proc = target_proc->pid; |
| 1956 | 2772 | |
| 1957 | 2773 | /* TODO: reuse incoming transaction for reply */ |
| 1958 | 2774 | t = kzalloc(sizeof(*t), GFP_KERNEL); |
| 1959 | 2775 | if (t == NULL) { |
| 1960 | 2776 | return_error = BR_FAILED_REPLY; |
| 2777 | + return_error_param = -ENOMEM; | |
| 2778 | + return_error_line = __LINE__; | |
| 1961 | 2779 | goto err_alloc_t_failed; |
| 1962 | 2780 | } |
| 1963 | 2781 | binder_stats_created(BINDER_STAT_TRANSACTION); |
| 2782 | + spin_lock_init(&t->lock); | |
| 1964 | 2783 | |
| 1965 | 2784 | tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL); |
| 1966 | 2785 | if (tcomplete == NULL) { |
| 1967 | 2786 | return_error = BR_FAILED_REPLY; |
| 2787 | + return_error_param = -ENOMEM; | |
| 2788 | + return_error_line = __LINE__; | |
| 1968 | 2789 | goto err_alloc_tcomplete_failed; |
| 1969 | 2790 | } |
| 1970 | 2791 | binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE); |
| 1971 | 2792 | |
| 1972 | − t->debug_id = ++binder_last_id; | |
| 1973 | − e->debug_id = t->debug_id; | |
| 2793 | + t->debug_id = t_debug_id; | |
| 1974 | 2794 | |
| 1975 | 2795 | if (reply) |
| 1976 | 2796 | binder_debug(BINDER_DEBUG_TRANSACTION, |
| @@ -2004,11 +2824,18 @@ static void binder_transaction(struct binder_proc *proc, · 27 unmodified lines | ||
| 2004 | 2824 | |
| 2005 | 2825 | trace_binder_transaction(reply, t, target_node); |
| 2006 | 2826 | |
| 2007 | − t->buffer = binder_alloc_buf(target_proc, tr->data_size, | |
| 2827 | + t->buffer = binder_alloc_new_buf(&target_proc->alloc, tr->data_size, | |
| 2008 | 2828 | tr->offsets_size, extra_buffers_size, |
| 2009 | 2829 | !reply && (t->flags & TF_ONE_WAY)); |
| 2010 | − if (t->buffer == NULL) { | |
| 2011 | − return_error = BR_FAILED_REPLY; | |
| 2830 | + if (IS_ERR(t->buffer)) { | |
| 2831 | + /* | |
| 2832 | + * -ESRCH indicates VMA cleared. The target is dying. | |
| 2833 | + */ | |
| 2834 | + return_error_param = PTR_ERR(t->buffer); | |
| 2835 | + return_error = return_error_param == -ESRCH ? | |
| 2836 | + BR_DEAD_REPLY : BR_FAILED_REPLY; | |
| 2837 | + return_error_line = __LINE__; | |
| 2838 | + t->buffer = NULL; | |
| 2012 | 2839 | goto err_binder_alloc_buf_failed; |
| 2013 | 2840 | } |
| 2014 | 2841 | t->buffer->allow_user_free = 0; |
| @@ -2016,9 +2843,6 @@ static void binder_transaction(struct binder_proc *proc, · 1 unmodified line | ||
| 2016 | 2843 | t->buffer->transaction = t; |
| 2017 | 2844 | t->buffer->target_node = target_node; |
| 2018 | 2845 | trace_binder_transaction_alloc_buf(t->buffer); |
| 2019 | − if (target_node) | |
| 2020 | − binder_inc_node(target_node, 1, 0, NULL); | |
| 2021 | − | |
| 2022 | 2846 | off_start = (binder_size_t *)(t->buffer->data + |
| 2023 | 2847 | ALIGN(tr->data_size, sizeof(void *))); |
| 2024 | 2848 | offp = off_start; |
| @@ -2028,6 +2852,8 @@ static void binder_transaction(struct binder_proc *proc, · 3 unmodified lines | ||
| 2028 | 2852 | binder_user_error("%d:%d got transaction with invalid data ptr\n", |
| 2029 | 2853 | proc->pid, thread->pid); |
| 2030 | 2854 | return_error = BR_FAILED_REPLY; |
| 2855 | + return_error_param = -EFAULT; | |
| 2856 | + return_error_line = __LINE__; | |
| 2031 | 2857 | goto err_copy_data_failed; |
| 2032 | 2858 | } |
| 2033 | 2859 | if (copy_from_user(offp, (const void __user *)(uintptr_t) |
| @@ -2035,12 +2861,16 @@ static void binder_transaction(struct binder_proc *proc, · 1 unmodified line | ||
| 2035 | 2861 | binder_user_error("%d:%d got transaction with invalid offsets ptr\n", |
| 2036 | 2862 | proc->pid, thread->pid); |
| 2037 | 2863 | return_error = BR_FAILED_REPLY; |
| 2864 | + return_error_param = -EFAULT; | |
| 2865 | + return_error_line = __LINE__; | |
| 2038 | 2866 | goto err_copy_data_failed; |
| 2039 | 2867 | } |
| 2040 | 2868 | if (!IS_ALIGNED(tr->offsets_size, sizeof(binder_size_t))) { |
| 2041 | 2869 | binder_user_error("%d:%d got transaction with invalid offsets size, %lld\n", |
| 2042 | 2870 | proc->pid, thread->pid, (u64)tr->offsets_size); |
| 2043 | 2871 | return_error = BR_FAILED_REPLY; |
| 2872 | + return_error_param = -EINVAL; | |
| 2873 | + return_error_line = __LINE__; | |
| 2044 | 2874 | goto err_bad_offset; |
| 2045 | 2875 | } |
| 2046 | 2876 | if (!IS_ALIGNED(extra_buffers_size, sizeof(u64))) { |
| @@ -2048,6 +2878,8 @@ static void binder_transaction(struct binder_proc *proc, · 1 unmodified line | ||
| 2048 | 2878 | proc->pid, thread->pid, |
| 2049 | 2879 | (u64)extra_buffers_size); |
| 2050 | 2880 | return_error = BR_FAILED_REPLY; |
| 2881 | + return_error_param = -EINVAL; | |
| 2882 | + return_error_line = __LINE__; | |
| 2051 | 2883 | goto err_bad_offset; |
| 2052 | 2884 | } |
| 2053 | 2885 | off_end = (void *)off_start + tr->offsets_size; |
| @@ -2064,6 +2896,8 @@ static void binder_transaction(struct binder_proc *proc, · 10 unmodified lines | ||
| 2064 | 2896 | (u64)off_min, |
| 2065 | 2897 | (u64)t->buffer->data_size); |
| 2066 | 2898 | return_error = BR_FAILED_REPLY; |
| 2899 | + return_error_param = -EINVAL; | |
| 2900 | + return_error_line = __LINE__; | |
| 2067 | 2901 | goto err_bad_offset; |
| 2068 | 2902 | } |
| 2069 | 2903 | |
| @@ -2078,6 +2912,8 @@ static void binder_transaction(struct binder_proc *proc, · 8 unmodified lines | ||
| 2078 | 2912 | ret = binder_translate_binder(fp, t, thread); |
| 2079 | 2913 | if (ret < 0) { |
| 2080 | 2914 | return_error = BR_FAILED_REPLY; |
| 2915 | + return_error_param = ret; | |
| 2916 | + return_error_line = __LINE__; | |
| 2081 | 2917 | goto err_translate_failed; |
| 2082 | 2918 | } |
| 2083 | 2919 | } break; |
| @@ -2089,6 +2925,8 @@ static void binder_transaction(struct binder_proc *proc, · 5 unmodified lines | ||
| 2089 | 2925 | ret = binder_translate_handle(fp, t, thread); |
| 2090 | 2926 | if (ret < 0) { |
| 2091 | 2927 | return_error = BR_FAILED_REPLY; |
| 2928 | + return_error_param = ret; | |
| 2929 | + return_error_line = __LINE__; | |
| 2092 | 2930 | goto err_translate_failed; |
| 2093 | 2931 | } |
| 2094 | 2932 | } break; |
| @@ -2100,6 +2938,8 @@ static void binder_transaction(struct binder_proc *proc, · 5 unmodified lines | ||
| 2100 | 2938 | |
| 2101 | 2939 | if (target_fd < 0) { |
| 2102 | 2940 | return_error = BR_FAILED_REPLY; |
| 2941 | + return_error_param = target_fd; | |
| 2942 | + return_error_line = __LINE__; | |
| 2103 | 2943 | goto err_translate_failed; |
| 2104 | 2944 | } |
| 2105 | 2945 | fp->pad_binder = 0; |
| @@ -2116,6 +2956,8 @@ static void binder_transaction(struct binder_proc *proc, · 10 unmodified lines | ||
| 2116 | 2956 | binder_user_error("%d:%d got transaction with invalid parent offset or type\n", |
| 2117 | 2957 | proc->pid, thread->pid); |
| 2118 | 2958 | return_error = BR_FAILED_REPLY; |
| 2959 | + return_error_param = -EINVAL; | |
| 2960 | + return_error_line = __LINE__; | |
| 2119 | 2961 | goto err_bad_parent; |
| 2120 | 2962 | } |
| 2121 | 2963 | if (!binder_validate_fixup(t->buffer, off_start, |
| @@ -2125,12 +2967,16 @@ static void binder_transaction(struct binder_proc *proc, · 3 unmodified lines | ||
| 2125 | 2967 | binder_user_error("%d:%d got transaction with out-of-order buffer fixup\n", |
| 2126 | 2968 | proc->pid, thread->pid); |
| 2127 | 2969 | return_error = BR_FAILED_REPLY; |
| 2970 | + return_error_param = -EINVAL; | |
| 2971 | + return_error_line = __LINE__; | |
| 2128 | 2972 | goto err_bad_parent; |
| 2129 | 2973 | } |
| 2130 | 2974 | ret = binder_translate_fd_array(fda, parent, t, thread, |
| 2131 | 2975 | in_reply_to); |
| 2132 | 2976 | if (ret < 0) { |
| 2133 | 2977 | return_error = BR_FAILED_REPLY; |
| 2978 | + return_error_param = ret; | |
| 2979 | + return_error_line = __LINE__; | |
| 2134 | 2980 | goto err_translate_failed; |
| 2135 | 2981 | } |
| 2136 | 2982 | last_fixup_obj = parent; |
| @@ -2146,6 +2992,8 @@ static void binder_transaction(struct binder_proc *proc, · 9 unmodified lines | ||
| 2146 | 2992 | binder_user_error("%d:%d got transaction with too large buffer\n", |
| 2147 | 2993 | proc->pid, thread->pid); |
| 2148 | 2994 | return_error = BR_FAILED_REPLY; |
| 2995 | + return_error_param = -EINVAL; | |
| 2996 | + return_error_line = __LINE__; | |
| 2149 | 2997 | goto err_bad_offset; |
| 2150 | 2998 | } |
| 2151 | 2999 | if (copy_from_user(sg_bufp, |
| @@ -2153,12 +3001,15 @@ static void binder_transaction(struct binder_proc *proc, · 1 unmodified line | ||
| 2153 | 3001 | bp->buffer, bp->length)) { |
| 2154 | 3002 | binder_user_error("%d:%d got transaction with invalid offsets ptr\n", |
| 2155 | 3003 | proc->pid, thread->pid); |
| 3004 | + return_error_param = -EFAULT; | |
| 2156 | 3005 | return_error = BR_FAILED_REPLY; |
| 3006 | + return_error_line = __LINE__; | |
| 2157 | 3007 | goto err_copy_data_failed; |
| 2158 | 3008 | } |
| 2159 | 3009 | /* Fixup buffer pointer to target proc address space */ |
| 2160 | 3010 | bp->buffer = (uintptr_t)sg_bufp + |
| 2161 | − target_proc->user_buffer_offset; | |
| 3011 | + binder_alloc_get_user_buffer_offset( | |
| 3012 | + &target_proc->alloc); | |
| 2162 | 3013 | sg_bufp += ALIGN(bp->length, sizeof(u64)); |
| 2163 | 3014 | |
| 2164 | 3015 | ret = binder_fixup_parent(t, thread, bp, off_start, |
| @@ -2167,6 +3018,8 @@ static void binder_transaction(struct binder_proc *proc, · 2 unmodified lines | ||
| 2167 | 3018 | last_fixup_min_off); |
| 2168 | 3019 | if (ret < 0) { |
| 2169 | 3020 | return_error = BR_FAILED_REPLY; |
| 3021 | + return_error_param = ret; | |
| 3022 | + return_error_line = __LINE__; | |
| 2170 | 3023 | goto err_translate_failed; |
| 2171 | 3024 | } |
| 2172 | 3025 | last_fixup_obj = bp; |
| @@ -2176,38 +3029,60 @@ static void binder_transaction(struct binder_proc *proc, · 3 unmodified lines | ||
| 2176 | 3029 | binder_user_error("%d:%d got transaction with invalid object type, %x\n", |
| 2177 | 3030 | proc->pid, thread->pid, hdr->type); |
| 2178 | 3031 | return_error = BR_FAILED_REPLY; |
| 3032 | + return_error_param = -EINVAL; | |
| 3033 | + return_error_line = __LINE__; | |
| 2179 | 3034 | goto err_bad_object_type; |
| 2180 | 3035 | } |
| 2181 | 3036 | } |
| 3037 | + tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; | |
| 3038 | + binder_enqueue_work(proc, tcomplete, &thread->todo); | |
| 3039 | + t->work.type = BINDER_WORK_TRANSACTION; | |
| 3040 | + | |
| 2182 | 3041 | if (reply) { |
| 3042 | + binder_inner_proc_lock(target_proc); | |
| 3043 | + if (target_thread->is_dead) { | |
| 3044 | + binder_inner_proc_unlock(target_proc); | |
| 3045 | + goto err_dead_proc_or_thread; | |
| 3046 | + } | |
| 2183 | 3047 | BUG_ON(t->buffer->async_transaction != 0); |
| 2184 | − binder_pop_transaction(target_thread, in_reply_to); | |
| 3048 | + binder_pop_transaction_ilocked(target_thread, in_reply_to); | |
| 3049 | + binder_enqueue_work_ilocked(&t->work, &target_thread->todo); | |
| 3050 | + binder_inner_proc_unlock(target_proc); | |
| 3051 | + wake_up_interruptible_sync(&target_thread->wait); | |
| 3052 | + binder_free_transaction(in_reply_to); | |
| 2185 | 3053 | } else if (!(t->flags & TF_ONE_WAY)) { |
| 2186 | 3054 | BUG_ON(t->buffer->async_transaction != 0); |
| 3055 | + binder_inner_proc_lock(proc); | |
| 2187 | 3056 | t->need_reply = 1; |
| 2188 | 3057 | t->from_parent = thread->transaction_stack; |
| 2189 | 3058 | thread->transaction_stack = t; |
| 3059 | + binder_inner_proc_unlock(proc); | |
| 3060 | + if (!binder_proc_transaction(t, target_proc, target_thread)) { | |
| 3061 | + binder_inner_proc_lock(proc); | |
| 3062 | + binder_pop_transaction_ilocked(thread, t); | |
| 3063 | + binder_inner_proc_unlock(proc); | |
| 3064 | + goto err_dead_proc_or_thread; | |
| 3065 | + } | |
| 2190 | 3066 | } else { |
| 2191 | 3067 | BUG_ON(target_node == NULL); |
| 2192 | 3068 | BUG_ON(t->buffer->async_transaction != 1); |
| 2193 | − if (target_node->has_async_transaction) { | |
| 2194 | − target_list = &target_node->async_todo; | |
| 2195 | − target_wait = NULL; | |
| 2196 | − } else | |
| 2197 | − target_node->has_async_transaction = 1; | |
| 2198 | − } | |
| 2199 | − t->work.type = BINDER_WORK_TRANSACTION; | |
| 2200 | − list_add_tail(&t->work.entry, target_list); | |
| 2201 | − tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; | |
| 2202 | − list_add_tail(&tcomplete->entry, &thread->todo); | |
| 2203 | − if (target_wait) { | |
| 2204 | − if (reply || !(t->flags & TF_ONE_WAY)) | |
| 2205 | − wake_up_interruptible_sync(target_wait); | |
| 2206 | − else | |
| 2207 | − wake_up_interruptible(target_wait); | |
| 3069 | + if (!binder_proc_transaction(t, target_proc, NULL)) | |
| 3070 | + goto err_dead_proc_or_thread; | |
| 2208 | 3071 | } |
| 3072 | + if (target_thread) | |
| 3073 | + binder_thread_dec_tmpref(target_thread); | |
| 3074 | + binder_proc_dec_tmpref(target_proc); | |
| 3075 | + /* | |
| 3076 | + * write barrier to synchronize with initialization | |
| 3077 | + * of log entry | |
| 3078 | + */ | |
| 3079 | + smp_wmb(); | |
| 3080 | + WRITE_ONCE(e->debug_id_done, t_debug_id); | |
| 2209 | 3081 | return; |
| 2210 | 3082 | |
| 3083 | +err_dead_proc_or_thread: | |
| 3084 | + return_error = BR_DEAD_REPLY; | |
| 3085 | + return_error_line = __LINE__; | |
| 2211 | 3086 | err_translate_failed: |
| 2212 | 3087 | err_bad_object_type: |
| 2213 | 3088 | err_bad_offset: |
| @@ -2215,8 +3090,9 @@ err_bad_parent: · 1 unmodified line | ||
| 2215 | 3090 | err_copy_data_failed: |
| 2216 | 3091 | trace_binder_transaction_failed_buffer_release(t->buffer); |
| 2217 | 3092 | binder_transaction_buffer_release(target_proc, t->buffer, offp); |
| 3093 | + target_node = NULL; | |
| 2218 | 3094 | t->buffer->transaction = NULL; |
| 2219 | − binder_free_buf(target_proc, t->buffer); | |
| 3095 | + binder_alloc_free_buf(&target_proc->alloc, t->buffer); | |
| 2220 | 3096 | err_binder_alloc_buf_failed: |
| 2221 | 3097 | kfree(tcomplete); |
| 2222 | 3098 | binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE); |
| @@ -2229,24 +3105,49 @@ err_empty_call_stack: · 6 unmodified lines | ||
| 2229 | 3105 | err_dead_binder: |
| 2230 | 3106 | err_invalid_target_handle: |
| 2231 | 3107 | err_no_context_mgr_node: |
| 3108 | + if (target_thread) | |
| 3109 | + binder_thread_dec_tmpref(target_thread); | |
| 3110 | + if (target_proc) | |
| 3111 | + binder_proc_dec_tmpref(target_proc); | |
| 3112 | + if (target_node) | |
| 3113 | + binder_dec_node(target_node, 1, 0); | |
| 3114 | + | |
| 2232 | 3115 | binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, |
| 2233 | − "%d:%d transaction failed %d, size %lld-%lld\n", | |
| 2234 | − proc->pid, thread->pid, return_error, | |
| 2235 | − (u64)tr->data_size, (u64)tr->offsets_size); | |
| 3116 | + "%d:%d transaction failed %d/%d, size %lld-%lld line %d\n", | |
| 3117 | + proc->pid, thread->pid, return_error, return_error_param, | |
| 3118 | + (u64)tr->data_size, (u64)tr->offsets_size, | |
| 3119 | + return_error_line); | |
| 2236 | 3120 | |
| 2237 | 3121 | { |
| 2238 | 3122 | struct binder_transaction_log_entry *fe; |
| 2239 | 3123 | |
| 3124 | + e->return_error = return_error; | |
| 3125 | + e->return_error_param = return_error_param; | |
| 3126 | + e->return_error_line = return_error_line; | |
| 2240 | 3127 | fe = binder_transaction_log_add(&binder_transaction_log_failed); |
| 2241 | 3128 | *fe = *e; |
| 3129 | + /* | |
| 3130 | + * write barrier to synchronize with initialization | |
| 3131 | + * of log entry | |
| 3132 | + */ | |
| 3133 | + smp_wmb(); | |
| 3134 | + WRITE_ONCE(e->debug_id_done, t_debug_id); | |
| 3135 | + WRITE_ONCE(fe->debug_id_done, t_debug_id); | |
| 2242 | 3136 | } |
| 2243 | 3137 | |
| 2244 | − BUG_ON(thread->return_error != BR_OK); | |
| 3138 | + BUG_ON(thread->return_error.cmd != BR_OK); | |
| 2245 | 3139 | if (in_reply_to) { |
| 2246 | − thread->return_error = BR_TRANSACTION_COMPLETE; | |
| 3140 | + thread->return_error.cmd = BR_TRANSACTION_COMPLETE; | |
| 3141 | + binder_enqueue_work(thread->proc, | |
| 3142 | + &thread->return_error.work, | |
| 3143 | + &thread->todo); | |
| 2247 | 3144 | binder_send_failed_reply(in_reply_to, return_error); |
| 2248 | − } else | |
| 2249 | − thread->return_error = return_error; | |
| 3145 | + } else { | |
| 3146 | + thread->return_error.cmd = return_error; | |
| 3147 | + binder_enqueue_work(thread->proc, | |
| 3148 | + &thread->return_error.work, | |
| 3149 | + &thread->todo); | |
| 3150 | + } | |
| 2250 | 3151 | } |
| 2251 | 3152 | |
| 2252 | 3153 | static int binder_thread_write(struct binder_proc *proc, |
| @@ -2260,15 +3161,17 @@ static int binder_thread_write(struct binder_proc *proc, · 7 unmodified lines | ||
| 2260 | 3161 | void __user *ptr = buffer + *consumed; |
| 2261 | 3162 | void __user *end = buffer + size; |
| 2262 | 3163 | |
| 2263 | − while (ptr < end && thread->return_error == BR_OK) { | |
| 3164 | + while (ptr < end && thread->return_error.cmd == BR_OK) { | |
| 3165 | + int ret; | |
| 3166 | + | |
| 2264 | 3167 | if (get_user(cmd, (uint32_t __user *)ptr)) |
| 2265 | 3168 | return -EFAULT; |
| 2266 | 3169 | ptr += sizeof(uint32_t); |
| 2267 | 3170 | trace_binder_command(cmd); |
| 2268 | 3171 | if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) { |
| 2269 | − binder_stats.bc[_IOC_NR(cmd)]++; | |
| 2270 | − proc->stats.bc[_IOC_NR(cmd)]++; | |
| 2271 | − thread->stats.bc[_IOC_NR(cmd)]++; | |
| 3172 | + atomic_inc(&binder_stats.bc[_IOC_NR(cmd)]); | |
| 3173 | + atomic_inc(&proc->stats.bc[_IOC_NR(cmd)]); | |
| 3174 | + atomic_inc(&thread->stats.bc[_IOC_NR(cmd)]); | |
| 2272 | 3175 | } |
| 2273 | 3176 | switch (cmd) { |
| 2274 | 3177 | case BC_INCREFS: |
| @@ -2276,53 +3179,61 @@ static int binder_thread_write(struct binder_proc *proc, · 1 unmodified line | ||
| 2276 | 3179 | case BC_RELEASE: |
| 2277 | 3180 | case BC_DECREFS: { |
| 2278 | 3181 | uint32_t target; |
| 2279 | − struct binder_ref *ref; | |
| 2280 | 3182 | const char *debug_string; |
| 3183 | + bool strong = cmd == BC_ACQUIRE || cmd == BC_RELEASE; | |
| 3184 | + bool increment = cmd == BC_INCREFS || cmd == BC_ACQUIRE; | |
| 3185 | + struct binder_ref_data rdata; | |
| 2281 | 3186 | |
| 2282 | 3187 | if (get_user(target, (uint32_t __user *)ptr)) |
| 2283 | 3188 | return -EFAULT; |
| 3189 | + | |
| 2284 | 3190 | ptr += sizeof(uint32_t); |
| 2285 | − if (target == 0 && context->binder_context_mgr_node && | |
| 2286 | − (cmd == BC_INCREFS || cmd == BC_ACQUIRE)) { | |
| 2287 | − ref = binder_get_ref_for_node(proc, | |
| 2288 | − context->binder_context_mgr_node); | |
| 2289 | − if (ref->desc != target) { | |
| 2290 | − binder_user_error("%d:%d tried to acquire reference to desc 0, got %d instead\n", | |
| 2291 | − proc->pid, thread->pid, | |
| 2292 | − ref->desc); | |
| 2293 | − } | |
| 2294 | − } else | |
| 2295 | − ref = binder_get_ref(proc, target, | |
| 2296 | − cmd == BC_ACQUIRE || | |
| 2297 | − cmd == BC_RELEASE); | |
| 2298 | − if (ref == NULL) { | |
| 2299 | − binder_user_error("%d:%d refcount change on invalid ref %d\n", | |
| 2300 | − proc->pid, thread->pid, target); | |
| 2301 | − break; | |
| 3191 | + ret = -1; | |
| 3192 | + if (increment && !target) { | |
| 3193 | + struct binder_node *ctx_mgr_node; | |
| 3194 | + mutex_lock(&context->context_mgr_node_lock); | |
| 3195 | + ctx_mgr_node = context->binder_context_mgr_node; | |
| 3196 | + if (ctx_mgr_node) | |
| 3197 | + ret = binder_inc_ref_for_node( | |
| 3198 | + proc, ctx_mgr_node, | |
| 3199 | + strong, NULL, &rdata); | |
| 3200 | + mutex_unlock(&context->context_mgr_node_lock); | |
| 3201 | + } | |
| 3202 | + if (ret) | |
| 3203 | + ret = binder_update_ref_for_handle( | |
| 3204 | + proc, target, increment, strong, | |
| 3205 | + &rdata); | |
| 3206 | + if (!ret && rdata.desc != target) { | |
| 3207 | + binder_user_error("%d:%d tried to acquire reference to desc %d, got %d instead\n", | |
| 3208 | + proc->pid, thread->pid, | |
| 3209 | + target, rdata.desc); | |
| 2302 | 3210 | } |
| 2303 | 3211 | switch (cmd) { |
| 2304 | 3212 | case BC_INCREFS: |
| 2305 | 3213 | debug_string = "IncRefs"; |
| 2306 | − binder_inc_ref(ref, 0, NULL); | |
| 2307 | 3214 | break; |
| 2308 | 3215 | case BC_ACQUIRE: |
| 2309 | 3216 | debug_string = "Acquire"; |
| 2310 | − binder_inc_ref(ref, 1, NULL); | |
| 2311 | 3217 | break; |
| 2312 | 3218 | case BC_RELEASE: |
| 2313 | 3219 | debug_string = "Release"; |
| 2314 | − binder_dec_ref(ref, 1); | |
| 2315 | 3220 | break; |
| 2316 | 3221 | case BC_DECREFS: |
| 2317 | 3222 | default: |
| 2318 | 3223 | debug_string = "DecRefs"; |
| 2319 | − binder_dec_ref(ref, 0); | |
| 3224 | + break; | |
| 3225 | + } | |
| 3226 | + if (ret) { | |
| 3227 | + binder_user_error("%d:%d %s %d refcount change on invalid ref %d ret %d\n", | |
| 3228 | + proc->pid, thread->pid, debug_string, | |
| 3229 | + strong, target, ret); | |
| 2320 | 3230 | break; |
| 2321 | 3231 | } |
| 2322 | 3232 | binder_debug(BINDER_DEBUG_USER_REFS, |
| 2323 | − "%d:%d %s ref %d desc %d s %d w %d for node %d\n", | |
| 2324 | − proc->pid, thread->pid, debug_string, ref->debug_id, | |
| 2325 | − ref->desc, ref->strong, ref->weak, ref->node->debug_id); | |
| 3233 | + "%d:%d %s ref %d desc %d s %d w %d\n", | |
| 3234 | + proc->pid, thread->pid, debug_string, | |
| 3235 | + rdata.debug_id, rdata.desc, rdata.strong, | |
| 3236 | + rdata.weak); | |
| 2326 | 3237 | break; |
| 2327 | 3238 | } |
| 2328 | 3239 | case BC_INCREFS_DONE: |
| @@ -2330,6 +3241,7 @@ static int binder_thread_write(struct binder_proc *proc, · 1 unmodified line | ||
| 2330 | 3241 | binder_uintptr_t node_ptr; |
| 2331 | 3242 | binder_uintptr_t cookie; |
| 2332 | 3243 | struct binder_node *node; |
| 3244 | + bool free_node; | |
| 2333 | 3245 | |
| 2334 | 3246 | if (get_user(node_ptr, (binder_uintptr_t __user *)ptr)) |
| 2335 | 3247 | return -EFAULT; |
| @@ -2354,13 +3266,17 @@ static int binder_thread_write(struct binder_proc *proc, · 18 unmodified lines | ||
| 2354 | 3266 | "BC_INCREFS_DONE" : "BC_ACQUIRE_DONE", |
| 2355 | 3267 | (u64)node_ptr, node->debug_id, |
| 2356 | 3268 | (u64)cookie, (u64)node->cookie); |
| 3269 | + binder_put_node(node); | |
| 2357 | 3270 | break; |
| 2358 | 3271 | } |
| 3272 | + binder_node_inner_lock(node); | |
| 2359 | 3273 | if (cmd == BC_ACQUIRE_DONE) { |
| 2360 | 3274 | if (node->pending_strong_ref == 0) { |
| 2361 | 3275 | binder_user_error("%d:%d BC_ACQUIRE_DONE node %d has no pending acquire request\n", |
| 2362 | 3276 | proc->pid, thread->pid, |
| 2363 | 3277 | node->debug_id); |
| 3278 | + binder_node_inner_unlock(node); | |
| 3279 | + binder_put_node(node); | |
| 2364 | 3280 | break; |
| 2365 | 3281 | } |
| 2366 | 3282 | node->pending_strong_ref = 0; |
| @@ -2369,16 +3285,23 @@ static int binder_thread_write(struct binder_proc *proc, · 2 unmodified lines | ||
| 2369 | 3285 | binder_user_error("%d:%d BC_INCREFS_DONE node %d has no pending increfs request\n", |
| 2370 | 3286 | proc->pid, thread->pid, |
| 2371 | 3287 | node->debug_id); |
| 3288 | + binder_node_inner_unlock(node); | |
| 3289 | + binder_put_node(node); | |
| 2372 | 3290 | break; |
| 2373 | 3291 | } |
| 2374 | 3292 | node->pending_weak_ref = 0; |
| 2375 | 3293 | } |
| 2376 | − binder_dec_node(node, cmd == BC_ACQUIRE_DONE, 0); | |
| 3294 | + free_node = binder_dec_node_nilocked(node, | |
| 3295 | + cmd == BC_ACQUIRE_DONE, 0); | |
| 3296 | + WARN_ON(free_node); | |
| 2377 | 3297 | binder_debug(BINDER_DEBUG_USER_REFS, |
| 2378 | − "%d:%d %s node %d ls %d lw %d\n", | |
| 3298 | + "%d:%d %s node %d ls %d lw %d tr %d\n", | |
| 2379 | 3299 | proc->pid, thread->pid, |
| 2380 | 3300 | cmd == BC_INCREFS_DONE ? "BC_INCREFS_DONE" : "BC_ACQUIRE_DONE", |
| 2381 | − node->debug_id, node->local_strong_refs, node->local_weak_refs); | |
| 3301 | + node->debug_id, node->local_strong_refs, | |
| 3302 | + node->local_weak_refs, node->tmp_refs); | |
| 3303 | + binder_node_inner_unlock(node); | |
| 3304 | + binder_put_node(node); | |
| 2382 | 3305 | break; |
| 2383 | 3306 | } |
| 2384 | 3307 | case BC_ATTEMPT_ACQUIRE: |
| @@ -2396,7 +3319,8 @@ static int binder_thread_write(struct binder_proc *proc, · 11 unmodified lines | ||
| 2396 | 3319 | return -EFAULT; |
| 2397 | 3320 | ptr += sizeof(binder_uintptr_t); |
| 2398 | 3321 | |
| 2399 | − buffer = binder_buffer_lookup(proc, data_ptr); | |
| 3322 | + buffer = binder_alloc_prepare_to_free(&proc->alloc, | |
| 3323 | + data_ptr); | |
| 2400 | 3324 | if (buffer == NULL) { |
| 2401 | 3325 | binder_user_error("%d:%d BC_FREE_BUFFER u%016llx no match\n", |
| 2402 | 3326 | proc->pid, thread->pid, (u64)data_ptr); |
| @@ -2418,15 +3342,27 @@ static int binder_thread_write(struct binder_proc *proc, · 15 unmodified lines | ||
| 2418 | 3342 | buffer->transaction = NULL; |
| 2419 | 3343 | } |
| 2420 | 3344 | if (buffer->async_transaction && buffer->target_node) { |
| 2421 | − BUG_ON(!buffer->target_node->has_async_transaction); | |
| 2422 | − if (list_empty(&buffer->target_node->async_todo)) | |
| 2423 | − buffer->target_node->has_async_transaction = 0; | |
| 2424 | − else | |
| 2425 | − list_move_tail(buffer->target_node->async_todo.next, &thread->todo); | |
| 3345 | + struct binder_node *buf_node; | |
| 3346 | + struct binder_work *w; | |
| 3347 | + | |
| 3348 | + buf_node = buffer->target_node; | |
| 3349 | + binder_node_inner_lock(buf_node); | |
| 3350 | + BUG_ON(!buf_node->has_async_transaction); | |
| 3351 | + BUG_ON(buf_node->proc != proc); | |
| 3352 | + w = binder_dequeue_work_head_ilocked( | |
| 3353 | + &buf_node->async_todo); | |
| 3354 | + if (!w) { | |
| 3355 | + buf_node->has_async_transaction = 0; | |
| 3356 | + } else { | |
| 3357 | + binder_enqueue_work_ilocked( | |
| 3358 | + w, &proc->todo); | |
| 3359 | + binder_wakeup_proc_ilocked(proc); | |
| 3360 | + } | |
| 3361 | + binder_node_inner_unlock(buf_node); | |
| 2426 | 3362 | } |
| 2427 | 3363 | trace_binder_transaction_buffer_release(buffer); |
| 2428 | 3364 | binder_transaction_buffer_release(proc, buffer, NULL); |
| 2429 | − binder_free_buf(proc, buffer); | |
| 3365 | + binder_alloc_free_buf(&proc->alloc, buffer); | |
| 2430 | 3366 | break; |
| 2431 | 3367 | } |
| 2432 | 3368 | |
| @@ -2457,6 +3393,7 @@ static int binder_thread_write(struct binder_proc *proc, · 24 unmodified lines | ||
| 2457 | 3393 | binder_debug(BINDER_DEBUG_THREADS, |
| 2458 | 3394 | "%d:%d BC_REGISTER_LOOPER\n", |
| 2459 | 3395 | proc->pid, thread->pid); |
| 3396 | + binder_inner_proc_lock(proc); | |
| 2460 | 3397 | if (thread->looper & BINDER_LOOPER_STATE_ENTERED) { |
| 2461 | 3398 | thread->looper |= BINDER_LOOPER_STATE_INVALID; |
| 2462 | 3399 | binder_user_error("%d:%d ERROR: BC_REGISTER_LOOPER called after BC_ENTER_LOOPER\n", |
| @@ -2470,6 +3407,7 @@ static int binder_thread_write(struct binder_proc *proc, · 7 unmodified lines | ||
| 2470 | 3407 | proc->requested_threads_started++; |
| 2471 | 3408 | } |
| 2472 | 3409 | thread->looper |= BINDER_LOOPER_STATE_REGISTERED; |
| 3410 | + binder_inner_proc_unlock(proc); | |
| 2473 | 3411 | break; |
| 2474 | 3412 | case BC_ENTER_LOOPER: |
| 2475 | 3413 | binder_debug(BINDER_DEBUG_THREADS, |
| @@ -2494,7 +3432,7 @@ static int binder_thread_write(struct binder_proc *proc, · 18 unmodified lines | ||
| 2494 | 3432 | uint32_t target; |
| 2495 | 3433 | binder_uintptr_t cookie; |
| 2496 | 3434 | struct binder_ref *ref; |
| 2497 | − struct binder_ref_death *death; | |
| 3435 | + struct binder_ref_death *death = NULL; | |
| 2498 | 3436 | |
| 2499 | 3437 | if (get_user(target, (uint32_t __user *)ptr)) |
| 2500 | 3438 | return -EFAULT; |
| @@ -2502,7 +3440,29 @@ static int binder_thread_write(struct binder_proc *proc, · 1 unmodified line | ||
| 2502 | 3440 | if (get_user(cookie, (binder_uintptr_t __user *)ptr)) |
| 2503 | 3441 | return -EFAULT; |
| 2504 | 3442 | ptr += sizeof(binder_uintptr_t); |
| 2505 | − ref = binder_get_ref(proc, target, false); | |
| 3443 | + if (cmd == BC_REQUEST_DEATH_NOTIFICATION) { | |
| 3444 | + /* | |
| 3445 | + * Allocate memory for death notification | |
| 3446 | + * before taking lock | |
| 3447 | + */ | |
| 3448 | + death = kzalloc(sizeof(*death), GFP_KERNEL); | |
| 3449 | + if (death == NULL) { | |
| 3450 | + WARN_ON(thread->return_error.cmd != | |
| 3451 | + BR_OK); | |
| 3452 | + thread->return_error.cmd = BR_ERROR; | |
| 3453 | + binder_enqueue_work( | |
| 3454 | + thread->proc, | |
| 3455 | + &thread->return_error.work, | |
| 3456 | + &thread->todo); | |
| 3457 | + binder_debug( | |
| 3458 | + BINDER_DEBUG_FAILED_TRANSACTION, | |
| 3459 | + "%d:%d BC_REQUEST_DEATH_NOTIFICATION failed\n", | |
| 3460 | + proc->pid, thread->pid); | |
| 3461 | + break; | |
| 3462 | + } | |
| 3463 | + } | |
| 3464 | + binder_proc_lock(proc); | |
| 3465 | + ref = binder_get_ref_olocked(proc, target, false); | |
| 2506 | 3466 | if (ref == NULL) { |
| 2507 | 3467 | binder_user_error("%d:%d %s invalid ref %d\n", |
| 2508 | 3468 | proc->pid, thread->pid, |
| @@ -2510,6 +3470,8 @@ static int binder_thread_write(struct binder_proc *proc, · 1 unmodified line | ||
| 2510 | 3470 | "BC_REQUEST_DEATH_NOTIFICATION" : |
| 2511 | 3471 | "BC_CLEAR_DEATH_NOTIFICATION", |
| 2512 | 3472 | target); |
| 3473 | + binder_proc_unlock(proc); | |
| 3474 | + kfree(death); | |
| 2513 | 3475 | break; |
| 2514 | 3476 | } |
| 2515 | 3477 | |
| @@ -2519,21 +3481,18 @@ static int binder_thread_write(struct binder_proc *proc, · 3 unmodified lines | ||
| 2519 | 3481 | cmd == BC_REQUEST_DEATH_NOTIFICATION ? |
| 2520 | 3482 | "BC_REQUEST_DEATH_NOTIFICATION" : |
| 2521 | 3483 | "BC_CLEAR_DEATH_NOTIFICATION", |
| 2522 | − (u64)cookie, ref->debug_id, ref->desc, | |
| 2523 | − ref->strong, ref->weak, ref->node->debug_id); | |
| 3484 | + (u64)cookie, ref->data.debug_id, | |
| 3485 | + ref->data.desc, ref->data.strong, | |
| 3486 | + ref->data.weak, ref->node->debug_id); | |
| 2524 | 3487 | |
| 3488 | + binder_node_lock(ref->node); | |
| 2525 | 3489 | if (cmd == BC_REQUEST_DEATH_NOTIFICATION) { |
| 2526 | 3490 | if (ref->death) { |
| 2527 | 3491 | binder_user_error("%d:%d BC_REQUEST_DEATH_NOTIFICATION death notification already set\n", |
| 2528 | 3492 | proc->pid, thread->pid); |
| 2529 | − break; | |
| 2530 | − } | |
| 2531 | − death = kzalloc(sizeof(*death), GFP_KERNEL); | |
| 2532 | − if (death == NULL) { | |
| 2533 | − thread->return_error = BR_ERROR; | |
| 2534 | − binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, | |
| 2535 | − "%d:%d BC_REQUEST_DEATH_NOTIFICATION failed\n", | |
| 2536 | − proc->pid, thread->pid); | |
| 3493 | + binder_node_unlock(ref->node); | |
| 3494 | + binder_proc_unlock(proc); | |
| 3495 | + kfree(death); | |
| 2537 | 3496 | break; |
| 2538 | 3497 | } |
| 2539 | 3498 | binder_stats_created(BINDER_STAT_DEATH); |
| @@ -2542,17 +3501,19 @@ static int binder_thread_write(struct binder_proc *proc, · 2 unmodified lines | ||
| 2542 | 3501 | ref->death = death; |
| 2543 | 3502 | if (ref->node->proc == NULL) { |
| 2544 | 3503 | ref->death->work.type = BINDER_WORK_DEAD_BINDER; |
| 2545 | − if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) { | |
| 2546 | − list_add_tail(&ref->death->work.entry, &thread->todo); | |
| 2547 | − } else { | |
| 2548 | − list_add_tail(&ref->death->work.entry, &proc->todo); | |
| 2549 | − wake_up_interruptible(&proc->wait); | |
| 2550 | − } | |
| 3504 | + | |
| 3505 | + binder_inner_proc_lock(proc); | |
| 3506 | + binder_enqueue_work_ilocked( | |
| 3507 | + &ref->death->work, &proc->todo); | |
| 3508 | + binder_wakeup_proc_ilocked(proc); | |
| 3509 | + binder_inner_proc_unlock(proc); | |
| 2551 | 3510 | } |
| 2552 | 3511 | } else { |
| 2553 | 3512 | if (ref->death == NULL) { |
| 2554 | 3513 | binder_user_error("%d:%d BC_CLEAR_DEATH_NOTIFICATION death notification not active\n", |
| 2555 | 3514 | proc->pid, thread->pid); |
| 3515 | + binder_node_unlock(ref->node); | |
| 3516 | + binder_proc_unlock(proc); | |
| 2556 | 3517 | break; |
| 2557 | 3518 | } |
| 2558 | 3519 | death = ref->death; |
| @@ -2561,22 +3522,35 @@ static int binder_thread_write(struct binder_proc *proc, · 2 unmodified lines | ||
| 2561 | 3522 | proc->pid, thread->pid, |
| 2562 | 3523 | (u64)death->cookie, |
| 2563 | 3524 | (u64)cookie); |
| 3525 | + binder_node_unlock(ref->node); | |
| 3526 | + binder_proc_unlock(proc); | |
| 2564 | 3527 | break; |
| 2565 | 3528 | } |
| 2566 | 3529 | ref->death = NULL; |
| 3530 | + binder_inner_proc_lock(proc); | |
| 2567 | 3531 | if (list_empty(&death->work.entry)) { |
| 2568 | 3532 | death->work.type = BINDER_WORK_CLEAR_DEATH_NOTIFICATION; |
| 2569 | − if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) { | |
| 2570 | − list_add_tail(&death->work.entry, &thread->todo); | |
| 2571 | − } else { | |
| 2572 | − list_add_tail(&death->work.entry, &proc->todo); | |
| 2573 | − wake_up_interruptible(&proc->wait); | |
| 3533 | + if (thread->looper & | |
| 3534 | + (BINDER_LOOPER_STATE_REGISTERED | | |
| 3535 | + BINDER_LOOPER_STATE_ENTERED)) | |
| 3536 | + binder_enqueue_work_ilocked( | |
| 3537 | + &death->work, | |
| 3538 | + &thread->todo); | |
| 3539 | + else { | |
| 3540 | + binder_enqueue_work_ilocked( | |
| 3541 | + &death->work, | |
| 3542 | + &proc->todo); | |
| 3543 | + binder_wakeup_proc_ilocked( | |
| 3544 | + proc); | |
| 2574 | 3545 | } |
| 2575 | 3546 | } else { |
| 2576 | 3547 | BUG_ON(death->work.type != BINDER_WORK_DEAD_BINDER); |
| 2577 | 3548 | death->work.type = BINDER_WORK_DEAD_BINDER_AND_CLEAR; |
| 2578 | 3549 | } |
| 3550 | + binder_inner_proc_unlock(proc); | |
| 2579 | 3551 | } |
| 3552 | + binder_node_unlock(ref->node); | |
| 3553 | + binder_proc_unlock(proc); | |
| 2580 | 3554 | } break; |
| 2581 | 3555 | case BC_DEAD_BINDER_DONE: { |
| 2582 | 3556 | struct binder_work *w; |
| @@ -2587,8 +3561,13 @@ static int binder_thread_write(struct binder_proc *proc, · 4 unmodified lines | ||
| 2587 | 3561 | return -EFAULT; |
| 2588 | 3562 | |
| 2589 | 3563 | ptr += sizeof(cookie); |
| 2590 | − list_for_each_entry(w, &proc->delivered_death, entry) { | |
| 2591 | − struct binder_ref_death *tmp_death = container_of(w, struct binder_ref_death, work); | |
| 3564 | + binder_inner_proc_lock(proc); | |
| 3565 | + list_for_each_entry(w, &proc->delivered_death, | |
| 3566 | + entry) { | |
| 3567 | + struct binder_ref_death *tmp_death = | |
| 3568 | + container_of(w, | |
| 3569 | + struct binder_ref_death, | |
| 3570 | + work); | |
| 2592 | 3571 | |
| 2593 | 3572 | if (tmp_death->cookie == cookie) { |
| 2594 | 3573 | death = tmp_death; |
| @@ -2602,19 +3581,25 @@ static int binder_thread_write(struct binder_proc *proc, · 7 unmodified lines | ||
| 2602 | 3581 | if (death == NULL) { |
| 2603 | 3582 | binder_user_error("%d:%d BC_DEAD_BINDER_DONE %016llx not found\n", |
| 2604 | 3583 | proc->pid, thread->pid, (u64)cookie); |
| 3584 | + binder_inner_proc_unlock(proc); | |
| 2605 | 3585 | break; |
| 2606 | 3586 | } |
| 2607 | − | |
| 2608 | − list_del_init(&death->work.entry); | |
| 3587 | + binder_dequeue_work_ilocked(&death->work); | |
| 2609 | 3588 | if (death->work.type == BINDER_WORK_DEAD_BINDER_AND_CLEAR) { |
| 2610 | 3589 | death->work.type = BINDER_WORK_CLEAR_DEATH_NOTIFICATION; |
| 2611 | − if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) { | |
| 2612 | − list_add_tail(&death->work.entry, &thread->todo); | |
| 2613 | − } else { | |
| 2614 | − list_add_tail(&death->work.entry, &proc->todo); | |
| 2615 | − wake_up_interruptible(&proc->wait); | |
| 3590 | + if (thread->looper & | |
| 3591 | + (BINDER_LOOPER_STATE_REGISTERED | | |
| 3592 | + BINDER_LOOPER_STATE_ENTERED)) | |
| 3593 | + binder_enqueue_work_ilocked( | |
| 3594 | + &death->work, &thread->todo); | |
| 3595 | + else { | |
| 3596 | + binder_enqueue_work_ilocked( | |
| 3597 | + &death->work, | |
| 3598 | + &proc->todo); | |
| 3599 | + binder_wakeup_proc_ilocked(proc); | |
| 2616 | 3600 | } |
| 2617 | 3601 | } |
| 3602 | + binder_inner_proc_unlock(proc); | |
| 2618 | 3603 | } break; |
| 2619 | 3604 | |
| 2620 | 3605 | default: |
| @@ -2632,23 +3617,79 @@ static void binder_stat_br(struct binder_proc *proc, · 11 unmodified lines | ||
| 2632 | 3617 | { |
| 2633 | 3618 | trace_binder_return(cmd); |
| 2634 | 3619 | if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.br)) { |
| 2635 | − binder_stats.br[_IOC_NR(cmd)]++; | |
| 2636 | − proc->stats.br[_IOC_NR(cmd)]++; | |
| 2637 | − thread->stats.br[_IOC_NR(cmd)]++; | |
| 3620 | + atomic_inc(&binder_stats.br[_IOC_NR(cmd)]); | |
| 3621 | + atomic_inc(&proc->stats.br[_IOC_NR(cmd)]); | |
| 3622 | + atomic_inc(&thread->stats.br[_IOC_NR(cmd)]); | |
| 2638 | 3623 | } |
| 2639 | 3624 | } |
| 2640 | 3625 | |
| 2641 | −static int binder_has_proc_work(struct binder_proc *proc, | |
| 2642 | − struct binder_thread *thread) | |
| 3626 | +static int binder_has_thread_work(struct binder_thread *thread) | |
| 3627 | +{ | |
| 3628 | + return !binder_worklist_empty(thread->proc, &thread->todo) || | |
| 3629 | + thread->looper_need_return; | |
| 3630 | +} | |
| 3631 | + | |
| 3632 | +static int binder_put_node_cmd(struct binder_proc *proc, | |
| 3633 | + struct binder_thread *thread, | |
| 3634 | + void __user **ptrp, | |
| 3635 | + binder_uintptr_t node_ptr, | |
| 3636 | + binder_uintptr_t node_cookie, | |
| 3637 | + int node_debug_id, | |
| 3638 | + uint32_t cmd, const char *cmd_name) | |
| 2643 | 3639 | { |
| 2644 | − return !list_empty(&proc->todo) || | |
| 2645 | − (thread->looper & BINDER_LOOPER_STATE_NEED_RETURN); | |
| 3640 | + void __user *ptr = *ptrp; | |
| 3641 | + | |
| 3642 | + if (put_user(cmd, (uint32_t __user *)ptr)) | |
| 3643 | + return -EFAULT; | |
| 3644 | + ptr += sizeof(uint32_t); | |
| 3645 | + | |
| 3646 | + if (put_user(node_ptr, (binder_uintptr_t __user *)ptr)) | |
| 3647 | + return -EFAULT; | |
| 3648 | + ptr += sizeof(binder_uintptr_t); | |
| 3649 | + | |
| 3650 | + if (put_user(node_cookie, (binder_uintptr_t __user *)ptr)) | |
| 3651 | + return -EFAULT; | |
| 3652 | + ptr += sizeof(binder_uintptr_t); | |
| 3653 | + | |
| 3654 | + binder_stat_br(proc, thread, cmd); | |
| 3655 | + binder_debug(BINDER_DEBUG_USER_REFS, "%d:%d %s %d u%016llx c%016llx\n", | |
| 3656 | + proc->pid, thread->pid, cmd_name, node_debug_id, | |
| 3657 | + (u64)node_ptr, (u64)node_cookie); | |
| 3658 | + | |
| 3659 | + *ptrp = ptr; | |
| 3660 | + return 0; | |
| 2646 | 3661 | } |
| 2647 | 3662 | |
| 2648 | −static int binder_has_thread_work(struct binder_thread *thread) | |
| 3663 | +static int binder_wait_for_work(struct binder_thread *thread, | |
| 3664 | + bool do_proc_work) | |
| 2649 | 3665 | { |
| 2650 | − return !list_empty(&thread->todo) || thread->return_error != BR_OK || | |
| 2651 | − (thread->looper & BINDER_LOOPER_STATE_NEED_RETURN); | |
| 3666 | + DEFINE_WAIT(wait); | |
| 3667 | + struct binder_proc *proc = thread->proc; | |
| 3668 | + int ret = 0; | |
| 3669 | + | |
| 3670 | + freezer_do_not_count(); | |
| 3671 | + binder_inner_proc_lock(proc); | |
| 3672 | + for (;;) { | |
| 3673 | + prepare_to_wait(&thread->wait, &wait, TASK_INTERRUPTIBLE); | |
| 3674 | + if (binder_has_work_ilocked(thread, do_proc_work)) | |
| 3675 | + break; | |
| 3676 | + if (do_proc_work) | |
| 3677 | + list_add(&thread->waiting_thread_node, | |
| 3678 | + &proc->waiting_threads); | |
| 3679 | + binder_inner_proc_unlock(proc); | |
| 3680 | + schedule(); | |
| 3681 | + binder_inner_proc_lock(proc); | |
| 3682 | + list_del_init(&thread->waiting_thread_node); | |
| 3683 | + if (signal_pending(current)) { | |
| 3684 | + ret = -ERESTARTSYS; | |
| 3685 | + break; | |
| 3686 | + } | |
| 3687 | + } | |
| 3688 | + finish_wait(&thread->wait, &wait); | |
| 3689 | + binder_inner_proc_unlock(proc); | |
| 3690 | + freezer_count(); | |
| 3691 | + | |
| 3692 | + return ret; | |
| 2652 | 3693 | } |
| 2653 | 3694 | |
| 2654 | 3695 | static int binder_thread_read(struct binder_proc *proc, |
| @@ -2670,37 +3711,15 @@ static int binder_thread_read(struct binder_proc *proc, · 15 unmodified lines | ||
| 2670 | 3711 | } |
| 2671 | 3712 | |
| 2672 | 3713 | retry: |
| 2673 | − wait_for_proc_work = thread->transaction_stack == NULL && | |
| 2674 | − list_empty(&thread->todo); | |
| 2675 | − | |
| 2676 | − if (thread->return_error != BR_OK && ptr < end) { | |
| 2677 | − if (thread->return_error2 != BR_OK) { | |
| 2678 | − if (put_user(thread->return_error2, (uint32_t __user *)ptr)) | |
| 2679 | − return -EFAULT; | |
| 2680 | − ptr += sizeof(uint32_t); | |
| 2681 | − binder_stat_br(proc, thread, thread->return_error2); | |
| 2682 | − if (ptr == end) | |
| 2683 | − goto done; | |
| 2684 | − thread->return_error2 = BR_OK; | |
| 2685 | − } | |
| 2686 | − if (put_user(thread->return_error, (uint32_t __user *)ptr)) | |
| 2687 | − return -EFAULT; | |
| 2688 | − ptr += sizeof(uint32_t); | |
| 2689 | − binder_stat_br(proc, thread, thread->return_error); | |
| 2690 | − thread->return_error = BR_OK; | |
| 2691 | − goto done; | |
| 2692 | − } | |
| 2693 | − | |
| 3714 | + binder_inner_proc_lock(proc); | |
| 3715 | + wait_for_proc_work = binder_available_for_proc_work_ilocked(thread); | |
| 3716 | + binder_inner_proc_unlock(proc); | |
| 2694 | 3717 | |
| 2695 | 3718 | thread->looper |= BINDER_LOOPER_STATE_WAITING; |
| 2696 | − if (wait_for_proc_work) | |
| 2697 | − proc->ready_threads++; | |
| 2698 | − | |
| 2699 | − binder_unlock(__func__); | |
| 2700 | 3719 | |
| 2701 | 3720 | trace_binder_wait_for_work(wait_for_proc_work, |
| 2702 | 3721 | !!thread->transaction_stack, |
| 2703 | − !list_empty(&thread->todo)); | |
| 3722 | + !binder_worklist_empty(proc, &thread->todo)); | |
| 2704 | 3723 | if (wait_for_proc_work) { |
| 2705 | 3724 | if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED | |
| 2706 | 3725 | BINDER_LOOPER_STATE_ENTERED))) { |
| @@ -2710,23 +3729,15 @@ retry: · 3 unmodified lines | ||
| 2710 | 3729 | binder_stop_on_user_error < 2); |
| 2711 | 3730 | } |
| 2712 | 3731 | binder_set_nice(proc->default_priority); |
| 2713 | − if (non_block) { | |
| 2714 | − if (!binder_has_proc_work(proc, thread)) | |
| 2715 | − ret = -EAGAIN; | |
| 2716 | − } else | |
| 2717 | − ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread)); | |
| 2718 | − } else { | |
| 2719 | − if (non_block) { | |
| 2720 | − if (!binder_has_thread_work(thread)) | |
| 2721 | − ret = -EAGAIN; | |
| 2722 | − } else | |
| 2723 | − ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread)); | |
| 2724 | 3732 | } |
| 2725 | 3733 | |
| 2726 | − binder_lock(__func__); | |
| 3734 | + if (non_block) { | |
| 3735 | + if (!binder_has_work(thread, wait_for_proc_work)) | |
| 3736 | + ret = -EAGAIN; | |
| 3737 | + } else { | |
| 3738 | + ret = binder_wait_for_work(thread, wait_for_proc_work); | |
| 3739 | + } | |
| 2727 | 3740 | |
| 2728 | − if (wait_for_proc_work) | |
| 2729 | − proc->ready_threads--; | |
| 2730 | 3741 | thread->looper &= ~BINDER_LOOPER_STATE_WAITING; |
| 2731 | 3742 | |
| 2732 | 3743 | if (ret) |
| @@ -2735,31 +3746,52 @@ retry: · 2 unmodified lines | ||
| 2735 | 3746 | while (1) { |
| 2736 | 3747 | uint32_t cmd; |
| 2737 | 3748 | struct binder_transaction_data tr; |
| 2738 | − struct binder_work *w; | |
| 3749 | + struct binder_work *w = NULL; | |
| 3750 | + struct list_head *list = NULL; | |
| 2739 | 3751 | struct binder_transaction *t = NULL; |
| 3752 | + struct binder_thread *t_from; | |
| 3753 | + | |
| 3754 | + binder_inner_proc_lock(proc); | |
| 3755 | + if (!binder_worklist_empty_ilocked(&thread->todo)) | |
| 3756 | + list = &thread->todo; | |
| 3757 | + else if (!binder_worklist_empty_ilocked(&proc->todo) && | |
| 3758 | + wait_for_proc_work) | |
| 3759 | + list = &proc->todo; | |
| 3760 | + else { | |
| 3761 | + binder_inner_proc_unlock(proc); | |
| 2740 | 3762 | |
| 2741 | − if (!list_empty(&thread->todo)) { | |
| 2742 | − w = list_first_entry(&thread->todo, struct binder_work, | |
| 2743 | − entry); | |
| 2744 | − } else if (!list_empty(&proc->todo) && wait_for_proc_work) { | |
| 2745 | − w = list_first_entry(&proc->todo, struct binder_work, | |
| 2746 | − entry); | |
| 2747 | − } else { | |
| 2748 | 3763 | /* no data added */ |
| 2749 | − if (ptr - buffer == 4 && | |
| 2750 | − !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN)) | |
| 3764 | + if (ptr - buffer == 4 && !thread->looper_need_return) | |
| 2751 | 3765 | goto retry; |
| 2752 | 3766 | break; |
| 2753 | 3767 | } |
| 2754 | 3768 | |
| 2755 | − if (end - ptr < sizeof(tr) + 4) | |
| 3769 | + if (end - ptr < sizeof(tr) + 4) { | |
| 3770 | + binder_inner_proc_unlock(proc); | |
| 2756 | 3771 | break; |
| 3772 | + } | |
| 3773 | + w = binder_dequeue_work_head_ilocked(list); | |
| 2757 | 3774 | |
| 2758 | 3775 | switch (w->type) { |
| 2759 | 3776 | case BINDER_WORK_TRANSACTION: { |
| 3777 | + binder_inner_proc_unlock(proc); | |
| 2760 | 3778 | t = container_of(w, struct binder_transaction, work); |
| 2761 | 3779 | } break; |
| 3780 | + case BINDER_WORK_RETURN_ERROR: { | |
| 3781 | + struct binder_error *e = container_of( | |
| 3782 | + w, struct binder_error, work); | |
| 3783 | + | |
| 3784 | + WARN_ON(e->cmd == BR_OK); | |
| 3785 | + binder_inner_proc_unlock(proc); | |
| 3786 | + if (put_user(e->cmd, (uint32_t __user *)ptr)) | |
| 3787 | + return -EFAULT; | |
| 3788 | + e->cmd = BR_OK; | |
| 3789 | + ptr += sizeof(uint32_t); | |
| 3790 | + | |
| 3791 | + binder_stat_br(proc, thread, e->cmd); | |
| 3792 | + } break; | |
| 2762 | 3793 | case BINDER_WORK_TRANSACTION_COMPLETE: { |
| 3794 | + binder_inner_proc_unlock(proc); | |
| 2763 | 3795 | cmd = BR_TRANSACTION_COMPLETE; |
| 2764 | 3796 | if (put_user(cmd, (uint32_t __user *)ptr)) |
| 2765 | 3797 | return -EFAULT; |
| @@ -2769,113 +3801,134 @@ retry: · 3 unmodified lines | ||
| 2769 | 3801 | binder_debug(BINDER_DEBUG_TRANSACTION_COMPLETE, |
| 2770 | 3802 | "%d:%d BR_TRANSACTION_COMPLETE\n", |
| 2771 | 3803 | proc->pid, thread->pid); |
| 2772 | − | |
| 2773 | − list_del(&w->entry); | |
| 2774 | 3804 | kfree(w); |
| 2775 | 3805 | binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE); |
| 2776 | 3806 | } break; |
| 2777 | 3807 | case BINDER_WORK_NODE: { |
| 2778 | 3808 | struct binder_node *node = container_of(w, struct binder_node, work); |
| 2779 | − uint32_t cmd = BR_NOOP; | |
| 2780 | − const char *cmd_name; | |
| 2781 | − int strong = node->internal_strong_refs || node->local_strong_refs; | |
| 2782 | − int weak = !hlist_empty(&node->refs) || node->local_weak_refs || strong; | |
| 2783 | − | |
| 2784 | − if (weak && !node->has_weak_ref) { | |
| 2785 | − cmd = BR_INCREFS; | |
| 2786 | − cmd_name = "BR_INCREFS"; | |
| 3809 | + int strong, weak; | |
| 3810 | + binder_uintptr_t node_ptr = node->ptr; | |
| 3811 | + binder_uintptr_t node_cookie = node->cookie; | |
| 3812 | + int node_debug_id = node->debug_id; | |
| 3813 | + int has_weak_ref; | |
| 3814 | + int has_strong_ref; | |
| 3815 | + void __user *orig_ptr = ptr; | |
| 3816 | + | |
| 3817 | + BUG_ON(proc != node->proc); | |
| 3818 | + strong = node->internal_strong_refs || | |
| 3819 | + node->local_strong_refs; | |
| 3820 | + weak = !hlist_empty(&node->refs) || | |
| 3821 | + node->local_weak_refs || | |
| 3822 | + node->tmp_refs || strong; | |
| 3823 | + has_strong_ref = node->has_strong_ref; | |
| 3824 | + has_weak_ref = node->has_weak_ref; | |
| 3825 | + | |
| 3826 | + if (weak && !has_weak_ref) { | |
| 2787 | 3827 | node->has_weak_ref = 1; |
| 2788 | 3828 | node->pending_weak_ref = 1; |
| 2789 | 3829 | node->local_weak_refs++; |
| 2790 | − } else if (strong && !node->has_strong_ref) { | |
| 2791 | − cmd = BR_ACQUIRE; | |
| 2792 | − cmd_name = "BR_ACQUIRE"; | |
| 3830 | + } | |
| 3831 | + if (strong && !has_strong_ref) { | |
| 2793 | 3832 | node->has_strong_ref = 1; |
| 2794 | 3833 | node->pending_strong_ref = 1; |
| 2795 | 3834 | node->local_strong_refs++; |
| 2796 | − } else if (!strong && node->has_strong_ref) { | |
| 2797 | − cmd = BR_RELEASE; | |
| 2798 | − cmd_name = "BR_RELEASE"; | |
| 3835 | + } | |
| 3836 | + if (!strong && has_strong_ref) | |
| 2799 | 3837 | node->has_strong_ref = 0; |
| 2800 | − } else if (!weak && node->has_weak_ref) { | |
| 2801 | − cmd = BR_DECREFS; | |
| 2802 | − cmd_name = "BR_DECREFS"; | |
| 3838 | + if (!weak && has_weak_ref) | |
| 2803 | 3839 | node->has_weak_ref = 0; |
| 2804 | − } | |
| 2805 | − if (cmd != BR_NOOP) { | |
| 2806 | − if (put_user(cmd, (uint32_t __user *)ptr)) | |
| 2807 | − return -EFAULT; | |
| 2808 | − ptr += sizeof(uint32_t); | |
| 2809 | − if (put_user(node->ptr, | |
| 2810 | − (binder_uintptr_t __user *)ptr)) | |
| 2811 | − return -EFAULT; | |
| 2812 | − ptr += sizeof(binder_uintptr_t); | |
| 2813 | − if (put_user(node->cookie, | |
| 2814 | − (binder_uintptr_t __user *)ptr)) | |
| 2815 | − return -EFAULT; | |
| 2816 | − ptr += sizeof(binder_uintptr_t); | |
| 2817 | − | |
| 2818 | − binder_stat_br(proc, thread, cmd); | |
| 2819 | − binder_debug(BINDER_DEBUG_USER_REFS, | |
| 2820 | − "%d:%d %s %d u%016llx c%016llx\n", | |
| 2821 | − proc->pid, thread->pid, cmd_name, | |
| 2822 | − node->debug_id, | |
| 2823 | − (u64)node->ptr, (u64)node->cookie); | |
| 2824 | − } else { | |
| 2825 | − list_del_init(&w->entry); | |
| 2826 | − if (!weak && !strong) { | |
| 2827 | − binder_debug(BINDER_DEBUG_INTERNAL_REFS, | |
| 2828 | − "%d:%d node %d u%016llx c%016llx deleted\n", | |
| 2829 | − proc->pid, thread->pid, | |
| 2830 | − node->debug_id, | |
| 2831 | − (u64)node->ptr, | |
| 2832 | − (u64)node->cookie); | |
| 2833 | − rb_erase(&node->rb_node, &proc->nodes); | |
| 2834 | − kfree(node); | |
| 2835 | − binder_stats_deleted(BINDER_STAT_NODE); | |
| 2836 | − } else { | |
| 2837 | − binder_debug(BINDER_DEBUG_INTERNAL_REFS, | |
| 2838 | − "%d:%d node %d u%016llx c%016llx state unchanged\n", | |
| 2839 | − proc->pid, thread->pid, | |
| 2840 | − node->debug_id, | |
| 2841 | − (u64)node->ptr, | |
| 2842 | − (u64)node->cookie); | |
| 2843 | − } | |
| 2844 | − } | |
| 3840 | + if (!weak && !strong) { | |
| 3841 | + binder_debug(BINDER_DEBUG_INTERNAL_REFS, | |
| 3842 | + "%d:%d node %d u%016llx c%016llx deleted\n", | |
| 3843 | + proc->pid, thread->pid, | |
| 3844 | + node_debug_id, | |
| 3845 | + (u64)node_ptr, | |
| 3846 | + (u64)node_cookie); | |
| 3847 | + rb_erase(&node->rb_node, &proc->nodes); | |
| 3848 | + binder_inner_proc_unlock(proc); | |
| 3849 | + binder_node_lock(node); | |
| 3850 | + /* | |
| 3851 | + * Acquire the node lock before freeing the | |
| 3852 | + * node to serialize with other threads that | |
| 3853 | + * may have been holding the node lock while | |
| 3854 | + * decrementing this node (avoids race where | |
| 3855 | + * this thread frees while the other thread | |
| 3856 | + * is unlocking the node after the final | |
| 3857 | + * decrement) | |
| 3858 | + */ | |
| 3859 | + binder_node_unlock(node); | |
| 3860 | + binder_free_node(node); | |
| 3861 | + } else | |
| 3862 | + binder_inner_proc_unlock(proc); | |
| 3863 | + | |
| 3864 | + if (weak && !has_weak_ref) | |
| 3865 | + ret = binder_put_node_cmd( | |
| 3866 | + proc, thread, &ptr, node_ptr, | |
| 3867 | + node_cookie, node_debug_id, | |
| 3868 | + BR_INCREFS, "BR_INCREFS"); | |
| 3869 | + if (!ret && strong && !has_strong_ref) | |
| 3870 | + ret = binder_put_node_cmd( | |
| 3871 | + proc, thread, &ptr, node_ptr, | |
| 3872 | + node_cookie, node_debug_id, | |
| 3873 | + BR_ACQUIRE, "BR_ACQUIRE"); | |
| 3874 | + if (!ret && !strong && has_strong_ref) | |
| 3875 | + ret = binder_put_node_cmd( | |
| 3876 | + proc, thread, &ptr, node_ptr, | |
| 3877 | + node_cookie, node_debug_id, | |
| 3878 | + BR_RELEASE, "BR_RELEASE"); | |
| 3879 | + if (!ret && !weak && has_weak_ref) | |
| 3880 | + ret = binder_put_node_cmd( | |
| 3881 | + proc, thread, &ptr, node_ptr, | |
| 3882 | + node_cookie, node_debug_id, | |
| 3883 | + BR_DECREFS, "BR_DECREFS"); | |
| 3884 | + if (orig_ptr == ptr) | |
| 3885 | + binder_debug(BINDER_DEBUG_INTERNAL_REFS, | |
| 3886 | + "%d:%d node %d u%016llx c%016llx state unchanged\n", | |
| 3887 | + proc->pid, thread->pid, | |
| 3888 | + node_debug_id, | |
| 3889 | + (u64)node_ptr, | |
| 3890 | + (u64)node_cookie); | |
| 3891 | + if (ret) | |
| 3892 | + return ret; | |
| 2845 | 3893 | } break; |
| 2846 | 3894 | case BINDER_WORK_DEAD_BINDER: |
| 2847 | 3895 | case BINDER_WORK_DEAD_BINDER_AND_CLEAR: |
| 2848 | 3896 | case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: { |
| 2849 | 3897 | struct binder_ref_death *death; |
| 2850 | 3898 | uint32_t cmd; |
| 3899 | + binder_uintptr_t cookie; | |
| 2851 | 3900 | |
| 2852 | 3901 | death = container_of(w, struct binder_ref_death, work); |
| 2853 | 3902 | if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION) |
| 2854 | 3903 | cmd = BR_CLEAR_DEATH_NOTIFICATION_DONE; |
| 2855 | 3904 | else |
| 2856 | 3905 | cmd = BR_DEAD_BINDER; |
| 2857 | − if (put_user(cmd, (uint32_t __user *)ptr)) | |
| 2858 | − return -EFAULT; | |
| 2859 | − ptr += sizeof(uint32_t); | |
| 2860 | − if (put_user(death->cookie, | |
| 2861 | − (binder_uintptr_t __user *)ptr)) | |
| 2862 | − return -EFAULT; | |
| 2863 | − ptr += sizeof(binder_uintptr_t); | |
| 2864 | − binder_stat_br(proc, thread, cmd); | |
| 3906 | + cookie = death->cookie; | |
| 3907 | + | |
| 2865 | 3908 | binder_debug(BINDER_DEBUG_DEATH_NOTIFICATION, |
| 2866 | 3909 | "%d:%d %s %016llx\n", |
| 2867 | 3910 | proc->pid, thread->pid, |
| 2868 | 3911 | cmd == BR_DEAD_BINDER ? |
| 2869 | 3912 | "BR_DEAD_BINDER" : |
| 2870 | 3913 | "BR_CLEAR_DEATH_NOTIFICATION_DONE", |
| 2871 | − (u64)death->cookie); | |
| 2872 | − | |
| 3914 | + (u64)cookie); | |
| 2873 | 3915 | if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION) { |
| 2874 | − list_del(&w->entry); | |
| 3916 | + binder_inner_proc_unlock(proc); | |
| 2875 | 3917 | kfree(death); |
| 2876 | 3918 | binder_stats_deleted(BINDER_STAT_DEATH); |
| 2877 | − } else | |
| 2878 | − list_move(&w->entry, &proc->delivered_death); | |
| 3919 | + } else { | |
| 3920 | + binder_enqueue_work_ilocked( | |
| 3921 | + w, &proc->delivered_death); | |
| 3922 | + binder_inner_proc_unlock(proc); | |
| 3923 | + } | |
| 3924 | + if (put_user(cmd, (uint32_t __user *)ptr)) | |
| 3925 | + return -EFAULT; | |
| 3926 | + ptr += sizeof(uint32_t); | |
| 3927 | + if (put_user(cookie, | |
| 3928 | + (binder_uintptr_t __user *)ptr)) | |
| 3929 | + return -EFAULT; | |
| 3930 | + ptr += sizeof(binder_uintptr_t); | |
| 3931 | + binder_stat_br(proc, thread, cmd); | |
| 2879 | 3932 | if (cmd == BR_DEAD_BINDER) |
| 2880 | 3933 | goto done; /* DEAD_BINDER notifications can cause transactions */ |
| 2881 | 3934 | } break; |
| @@ -2907,8 +3960,9 @@ retry: · 25 unmodified lines | ||
| 2907 | 3960 | tr.flags = t->flags; |
| 2908 | 3961 | tr.sender_euid = from_kuid(current_user_ns(), t->sender_euid); |
| 2909 | 3962 | |
| 2910 | − if (t->from) { | |
| 2911 | − struct task_struct *sender = t->from->proc->tsk; | |
| 3963 | + t_from = binder_get_txn_from(t); | |
| 3964 | + if (t_from) { | |
| 3965 | + struct task_struct *sender = t_from->proc->tsk; | |
| 2912 | 3966 | |
| 2913 | 3967 | tr.sender_pid = task_tgid_nr_ns(sender, |
| 2914 | 3968 | task_active_pid_ns(current)); |
| @@ -2918,18 +3972,24 @@ retry: · 3 unmodified lines | ||
| 2918 | 3972 | |
| 2919 | 3973 | tr.data_size = t->buffer->data_size; |
| 2920 | 3974 | tr.offsets_size = t->buffer->offsets_size; |
| 2921 | − tr.data.ptr.buffer = (binder_uintptr_t)( | |
| 2922 | − (uintptr_t)t->buffer->data + | |
| 2923 | − proc->user_buffer_offset); | |
| 3975 | + tr.data.ptr.buffer = (binder_uintptr_t) | |
| 3976 | + ((uintptr_t)t->buffer->data + | |
| 3977 | + binder_alloc_get_user_buffer_offset(&proc->alloc)); | |
| 2924 | 3978 | tr.data.ptr.offsets = tr.data.ptr.buffer + |
| 2925 | 3979 | ALIGN(t->buffer->data_size, |
| 2926 | 3980 | sizeof(void *)); |
| 2927 | 3981 | |
| 2928 | − if (put_user(cmd, (uint32_t __user *)ptr)) | |
| 3982 | + if (put_user(cmd, (uint32_t __user *)ptr)) { | |
| 3983 | + if (t_from) | |
| 3984 | + binder_thread_dec_tmpref(t_from); | |
| 2929 | 3985 | return -EFAULT; |
| 3986 | + } | |
| 2930 | 3987 | ptr += sizeof(uint32_t); |
| 2931 | − if (copy_to_user(ptr, &tr, sizeof(tr))) | |
| 3988 | + if (copy_to_user(ptr, &tr, sizeof(tr))) { | |
| 3989 | + if (t_from) | |
| 3990 | + binder_thread_dec_tmpref(t_from); | |
| 2932 | 3991 | return -EFAULT; |
| 3992 | + } | |
| 2933 | 3993 | ptr += sizeof(tr); |
| 2934 | 3994 | |
| 2935 | 3995 | trace_binder_transaction_received(t); |
| @@ -2939,21 +3999,22 @@ retry: · 3 unmodified lines | ||
| 2939 | 3999 | proc->pid, thread->pid, |
| 2940 | 4000 | (cmd == BR_TRANSACTION) ? "BR_TRANSACTION" : |
| 2941 | 4001 | "BR_REPLY", |
| 2942 | − t->debug_id, t->from ? t->from->proc->pid : 0, | |
| 2943 | − t->from ? t->from->pid : 0, cmd, | |
| 4002 | + t->debug_id, t_from ? t_from->proc->pid : 0, | |
| 4003 | + t_from ? t_from->pid : 0, cmd, | |
| 2944 | 4004 | t->buffer->data_size, t->buffer->offsets_size, |
| 2945 | 4005 | (u64)tr.data.ptr.buffer, (u64)tr.data.ptr.offsets); |
| 2946 | 4006 | |
| 2947 | − list_del(&t->work.entry); | |
| 4007 | + if (t_from) | |
| 4008 | + binder_thread_dec_tmpref(t_from); | |
| 2948 | 4009 | t->buffer->allow_user_free = 1; |
| 2949 | 4010 | if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) { |
| 4011 | + binder_inner_proc_lock(thread->proc); | |
| 2950 | 4012 | t->to_parent = thread->transaction_stack; |
| 2951 | 4013 | t->to_thread = thread; |
| 2952 | 4014 | thread->transaction_stack = t; |
| 4015 | + binder_inner_proc_unlock(thread->proc); | |
| 2953 | 4016 | } else { |
| 2954 | − t->buffer->transaction = NULL; | |
| 2955 | − kfree(t); | |
| 2956 | − binder_stats_deleted(BINDER_STAT_TRANSACTION); | |
| 4017 | + binder_free_transaction(t); | |
| 2957 | 4018 | } |
| 2958 | 4019 | break; |
| 2959 | 4020 | } |
| @@ -2961,29 +4022,36 @@ retry: · 1 unmodified line | ||
| 2961 | 4022 | done: |
| 2962 | 4023 | |
| 2963 | 4024 | *consumed = ptr - buffer; |
| 2964 | − if (proc->requested_threads + proc->ready_threads == 0 && | |
| 4025 | + binder_inner_proc_lock(proc); | |
| 4026 | + if (proc->requested_threads == 0 && | |
| 4027 | + list_empty(&thread->proc->waiting_threads) && | |
| 2965 | 4028 | proc->requested_threads_started < proc->max_threads && |
| 2966 | 4029 | (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | |
| 2967 | 4030 | BINDER_LOOPER_STATE_ENTERED)) /* the user-space code fails to */ |
| 2968 | 4031 | /*spawn a new thread if we leave this out */) { |
| 2969 | 4032 | proc->requested_threads++; |
| 4033 | + binder_inner_proc_unlock(proc); | |
| 2970 | 4034 | binder_debug(BINDER_DEBUG_THREADS, |
| 2971 | 4035 | "%d:%d BR_SPAWN_LOOPER\n", |
| 2972 | 4036 | proc->pid, thread->pid); |
| 2973 | 4037 | if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer)) |
| 2974 | 4038 | return -EFAULT; |
| 2975 | 4039 | binder_stat_br(proc, thread, BR_SPAWN_LOOPER); |
| 2976 | − } | |
| 4040 | + } else | |
| 4041 | + binder_inner_proc_unlock(proc); | |
| 2977 | 4042 | return 0; |
| 2978 | 4043 | } |
| 2979 | 4044 | |
| 2980 | −static void binder_release_work(struct list_head *list) | |
| 4045 | +static void binder_release_work(struct binder_proc *proc, | |
| 4046 | + struct list_head *list) | |
| 2981 | 4047 | { |
| 2982 | 4048 | struct binder_work *w; |
| 2983 | 4049 | |
| 2984 | − while (!list_empty(list)) { | |
| 2985 | − w = list_first_entry(list, struct binder_work, entry); | |
| 2986 | − list_del_init(&w->entry); | |
| 4050 | + while (1) { | |
| 4051 | + w = binder_dequeue_work_head(proc, list); | |
| 4052 | + if (!w) | |
| 4053 | + return; | |
| 4054 | + | |
| 2987 | 4055 | switch (w->type) { |
| 2988 | 4056 | case BINDER_WORK_TRANSACTION: { |
| 2989 | 4057 | struct binder_transaction *t; |
| @@ -2996,11 +4064,17 @@ static void binder_release_work(struct list_head *list) · 6 unmodified lines | ||
| 2996 | 4064 | binder_debug(BINDER_DEBUG_DEAD_TRANSACTION, |
| 2997 | 4065 | "undelivered transaction %d\n", |
| 2998 | 4066 | t->debug_id); |
| 2999 | − t->buffer->transaction = NULL; | |
| 3000 | − kfree(t); | |
| 3001 | − binder_stats_deleted(BINDER_STAT_TRANSACTION); | |
| 4067 | + binder_free_transaction(t); | |
| 3002 | 4068 | } |
| 3003 | 4069 | } break; |
| 4070 | + case BINDER_WORK_RETURN_ERROR: { | |
| 4071 | + struct binder_error *e = container_of( | |
| 4072 | + w, struct binder_error, work); | |
| 4073 | + | |
| 4074 | + binder_debug(BINDER_DEBUG_DEAD_TRANSACTION, | |
| 4075 | + "undelivered TRANSACTION_ERROR: %u\n", | |
| 4076 | + e->cmd); | |
| 4077 | + } break; | |
| 3004 | 4078 | case BINDER_WORK_TRANSACTION_COMPLETE: { |
| 3005 | 4079 | binder_debug(BINDER_DEBUG_DEAD_TRANSACTION, |
| 3006 | 4080 | "undelivered TRANSACTION_COMPLETE\n"); |
| @@ -3027,7 +4101,8 @@ static void binder_release_work(struct list_head *list) · 20 unmodified lines | ||
| 3027 | 4101 | |
| 3028 | 4102 | } |
| 3029 | 4103 | |
| 3030 | −static struct binder_thread *binder_get_thread(struct binder_proc *proc) | |
| 4104 | +static struct binder_thread *binder_get_thread_ilocked( | |
| 4105 | + struct binder_proc *proc, struct binder_thread *new_thread) | |
| 3031 | 4106 | { |
| 3032 | 4107 | struct binder_thread *thread = NULL; |
| 3033 | 4108 | struct rb_node *parent = NULL; |
| @@ -3042,38 +4117,99 @@ static struct binder_thread *binder_get_thread(struct binder_proc *proc) · 8 unmodified lines | ||
| 3042 | 4117 | else if (current->pid > thread->pid) |
| 3043 | 4118 | p = &(*p)->rb_right; |
| 3044 | 4119 | else |
| 3045 | − break; | |
| 4120 | + return thread; | |
| 3046 | 4121 | } |
| 3047 | − if (*p == NULL) { | |
| 3048 | − thread = kzalloc(sizeof(*thread), GFP_KERNEL); | |
| 3049 | − if (thread == NULL) | |
| 4122 | + if (!new_thread) | |
| 4123 | + return NULL; | |
| 4124 | + thread = new_thread; | |
| 4125 | + binder_stats_created(BINDER_STAT_THREAD); | |
| 4126 | + thread->proc = proc; | |
| 4127 | + thread->pid = current->pid; | |
| 4128 | + atomic_set(&thread->tmp_ref, 0); | |
| 4129 | + init_waitqueue_head(&thread->wait); | |
| 4130 | + INIT_LIST_HEAD(&thread->todo); | |
| 4131 | + rb_link_node(&thread->rb_node, parent, p); | |
| 4132 | + rb_insert_color(&thread->rb_node, &proc->threads); | |
| 4133 | + thread->looper_need_return = true; | |
| 4134 | + thread->return_error.work.type = BINDER_WORK_RETURN_ERROR; | |
| 4135 | + thread->return_error.cmd = BR_OK; | |
| 4136 | + thread->reply_error.work.type = BINDER_WORK_RETURN_ERROR; | |
| 4137 | + thread->reply_error.cmd = BR_OK; | |
| 4138 | + INIT_LIST_HEAD(&new_thread->waiting_thread_node); | |
| 4139 | + return thread; | |
| 4140 | +} | |
| 4141 | + | |
| 4142 | +static struct binder_thread *binder_get_thread(struct binder_proc *proc) | |
| 4143 | +{ | |
| 4144 | + struct binder_thread *thread; | |
| 4145 | + struct binder_thread *new_thread; | |
| 4146 | + | |
| 4147 | + binder_inner_proc_lock(proc); | |
| 4148 | + thread = binder_get_thread_ilocked(proc, NULL); | |
| 4149 | + binder_inner_proc_unlock(proc); | |
| 4150 | + if (!thread) { | |
| 4151 | + new_thread = kzalloc(sizeof(*thread), GFP_KERNEL); | |
| 4152 | + if (new_thread == NULL) | |
| 3050 | 4153 | return NULL; |
| 3051 | − binder_stats_created(BINDER_STAT_THREAD); | |
| 3052 | − thread->proc = proc; | |
| 3053 | − thread->pid = current->pid; | |
| 3054 | − init_waitqueue_head(&thread->wait); | |
| 3055 | − INIT_LIST_HEAD(&thread->todo); | |
| 3056 | − rb_link_node(&thread->rb_node, parent, p); | |
| 3057 | − rb_insert_color(&thread->rb_node, &proc->threads); | |
| 3058 | − thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN; | |
| 3059 | − thread->return_error = BR_OK; | |
| 3060 | − thread->return_error2 = BR_OK; | |
| 4154 | + binder_inner_proc_lock(proc); | |
| 4155 | + thread = binder_get_thread_ilocked(proc, new_thread); | |
| 4156 | + binder_inner_proc_unlock(proc); | |
| 4157 | + if (thread != new_thread) | |
| 4158 | + kfree(new_thread); | |
| 3061 | 4159 | } |
| 3062 | 4160 | return thread; |
| 3063 | 4161 | } |
| 3064 | 4162 | |
| 3065 | −static int binder_free_thread(struct binder_proc *proc, | |
| 3066 | − struct binder_thread *thread) | |
| 4163 | +static void binder_free_proc(struct binder_proc *proc) | |
| 4164 | +{ | |
| 4165 | + BUG_ON(!list_empty(&proc->todo)); | |
| 4166 | + BUG_ON(!list_empty(&proc->delivered_death)); | |
| 4167 | + binder_alloc_deferred_release(&proc->alloc); | |
| 4168 | + put_task_struct(proc->tsk); | |
| 4169 | + binder_stats_deleted(BINDER_STAT_PROC); | |
| 4170 | + kfree(proc); | |
| 4171 | +} | |
| 4172 | + | |
| 4173 | +static void binder_free_thread(struct binder_thread *thread) | |
| 4174 | +{ | |
| 4175 | + BUG_ON(!list_empty(&thread->todo)); | |
| 4176 | + binder_stats_deleted(BINDER_STAT_THREAD); | |
| 4177 | + binder_proc_dec_tmpref(thread->proc); | |
| 4178 | + kfree(thread); | |
| 4179 | +} | |
| 4180 | + | |
| 4181 | +static int binder_thread_release(struct binder_proc *proc, | |
| 4182 | + struct binder_thread *thread) | |
| 3067 | 4183 | { |
| 3068 | 4184 | struct binder_transaction *t; |
| 3069 | 4185 | struct binder_transaction *send_reply = NULL; |
| 3070 | 4186 | int active_transactions = 0; |
| 4187 | + struct binder_transaction *last_t = NULL; | |
| 3071 | 4188 | |
| 4189 | + binder_inner_proc_lock(thread->proc); | |
| 4190 | + /* | |
| 4191 | + * take a ref on the proc so it survives | |
| 4192 | + * after we remove this thread from proc->threads. | |
| 4193 | + * The corresponding dec is when we actually | |
| 4194 | + * free the thread in binder_free_thread() | |
| 4195 | + */ | |
| 4196 | + proc->tmp_ref++; | |
| 4197 | + /* | |
| 4198 | + * take a ref on this thread to ensure it | |
| 4199 | + * survives while we are releasing it | |
| 4200 | + */ | |
| 4201 | + atomic_inc(&thread->tmp_ref); | |
| 3072 | 4202 | rb_erase(&thread->rb_node, &proc->threads); |
| 3073 | 4203 | t = thread->transaction_stack; |
| 3074 | − if (t && t->to_thread == thread) | |
| 3075 | − send_reply = t; | |
| 4204 | + if (t) { | |
| 4205 | + spin_lock(&t->lock); | |
| 4206 | + if (t->to_thread == thread) | |
| 4207 | + send_reply = t; | |
| 4208 | + } | |
| 4209 | + thread->is_dead = true; | |
| 4210 | + | |
| 3076 | 4211 | while (t) { |
| 4212 | + last_t = t; | |
| 3077 | 4213 | active_transactions++; |
| 3078 | 4214 | binder_debug(BINDER_DEBUG_DEAD_TRANSACTION, |
| 3079 | 4215 | "release %d:%d transaction %d %s, still active\n", |
| @@ -3094,12 +4230,16 @@ static int binder_free_thread(struct binder_proc *proc, · 14 unmodified lines | ||
| 3094 | 4230 | t = t->from_parent; |
| 3095 | 4231 | } else |
| 3096 | 4232 | BUG(); |
| 4233 | + spin_unlock(&last_t->lock); | |
| 4234 | + if (t) | |
| 4235 | + spin_lock(&t->lock); | |
| 3097 | 4236 | } |
| 4237 | + binder_inner_proc_unlock(thread->proc); | |
| 4238 | + | |
| 3098 | 4239 | if (send_reply) |
| 3099 | 4240 | binder_send_failed_reply(send_reply, BR_DEAD_REPLY); |
| 3100 | − binder_release_work(&thread->todo); | |
| 3101 | − kfree(thread); | |
| 3102 | − binder_stats_deleted(BINDER_STAT_THREAD); | |
| 4241 | + binder_release_work(proc, &thread->todo); | |
| 4242 | + binder_thread_dec_tmpref(thread); | |
| 3103 | 4243 | return active_transactions; |
| 3104 | 4244 | } |
| 3105 | 4245 | |
| @@ -3108,30 +4248,24 @@ static unsigned int binder_poll(struct file *filp, · 2 unmodified lines | ||
| 3108 | 4248 | { |
| 3109 | 4249 | struct binder_proc *proc = filp->private_data; |
| 3110 | 4250 | struct binder_thread *thread = NULL; |
| 3111 | − int wait_for_proc_work; | |
| 3112 | − | |
| 3113 | − binder_lock(__func__); | |
| 4251 | + bool wait_for_proc_work; | |
| 3114 | 4252 | |
| 3115 | 4253 | thread = binder_get_thread(proc); |
| 3116 | 4254 | |
| 3117 | − wait_for_proc_work = thread->transaction_stack == NULL && | |
| 3118 | − list_empty(&thread->todo) && thread->return_error == BR_OK; | |
| 4255 | + binder_inner_proc_lock(thread->proc); | |
| 4256 | + thread->looper |= BINDER_LOOPER_STATE_POLL; | |
| 4257 | + wait_for_proc_work = binder_available_for_proc_work_ilocked(thread); | |
| 3119 | 4258 | |
| 3120 | − binder_unlock(__func__); | |
| 4259 | + binder_inner_proc_unlock(thread->proc); | |
| 4260 | + | |
| 4261 | + if (binder_has_work(thread, wait_for_proc_work)) | |
| 4262 | + return POLLIN; | |
| 4263 | + | |
| 4264 | + poll_wait(filp, &thread->wait, wait); | |
| 4265 | + | |
| 4266 | + if (binder_has_thread_work(thread)) | |
| 4267 | + return POLLIN; | |
| 3121 | 4268 | |
| 3122 | − if (wait_for_proc_work) { | |
| 3123 | − if (binder_has_proc_work(proc, thread)) | |
| 3124 | − return POLLIN; | |
| 3125 | − poll_wait(filp, &proc->wait, wait); | |
| 3126 | − if (binder_has_proc_work(proc, thread)) | |
| 3127 | − return POLLIN; | |
| 3128 | − } else { | |
| 3129 | − if (binder_has_thread_work(thread)) | |
| 3130 | − return POLLIN; | |
| 3131 | − poll_wait(filp, &thread->wait, wait); | |
| 3132 | − if (binder_has_thread_work(thread)) | |
| 3133 | − return POLLIN; | |
| 3134 | − } | |
| 3135 | 4269 | return 0; |
| 3136 | 4270 | } |
| 3137 | 4271 | |
| @@ -3178,8 +4312,10 @@ static int binder_ioctl_write_read(struct file *filp, · 40 unmodified lines | ||
| 3178 | 4312 | &bwr.read_consumed, |
| 3179 | 4313 | filp->f_flags & O_NONBLOCK); |
| 3180 | 4314 | trace_binder_read_done(ret); |
| 3181 | − if (!list_empty(&proc->todo)) | |
| 3182 | − wake_up_interruptible(&proc->wait); | |
| 4315 | + binder_inner_proc_lock(proc); | |
| 4316 | + if (!binder_worklist_empty_ilocked(&proc->todo)) | |
| 4317 | + binder_wakeup_proc_ilocked(proc); | |
| 4318 | + binder_inner_proc_unlock(proc); | |
| 3183 | 4319 | if (ret < 0) { |
| 3184 | 4320 | if (copy_to_user(ubuf, &bwr, sizeof(bwr))) |
| 3185 | 4321 | ret = -EFAULT; |
| @@ -3204,9 +4340,10 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp) · 18 unmodified lines | ||
| 3204 | 4340 | int ret = 0; |
| 3205 | 4341 | struct binder_proc *proc = filp->private_data; |
| 3206 | 4342 | struct binder_context *context = proc->context; |
| 3207 | − | |
| 4343 | + struct binder_node *new_node; | |
| 3208 | 4344 | kuid_t curr_euid = current_euid(); |
| 3209 | 4345 | |
| 4346 | + mutex_lock(&context->context_mgr_node_lock); | |
| 3210 | 4347 | if (context->binder_context_mgr_node) { |
| 3211 | 4348 | pr_err("BINDER_SET_CONTEXT_MGR already set\n"); |
| 3212 | 4349 | ret = -EBUSY; |
| @@ -3227,19 +4364,49 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp) · 14 unmodified lines | ||
| 3227 | 4364 | } else { |
| 3228 | 4365 | context->binder_context_mgr_uid = curr_euid; |
| 3229 | 4366 | } |
| 3230 | − context->binder_context_mgr_node = binder_new_node(proc, 0, 0); | |
| 3231 | − if (!context->binder_context_mgr_node) { | |
| 4367 | + new_node = binder_new_node(proc, NULL); | |
| 4368 | + if (!new_node) { | |
| 3232 | 4369 | ret = -ENOMEM; |
| 3233 | 4370 | goto out; |
| 3234 | 4371 | } |
| 3235 | − context->binder_context_mgr_node->local_weak_refs++; | |
| 3236 | − context->binder_context_mgr_node->local_strong_refs++; | |
| 3237 | − context->binder_context_mgr_node->has_strong_ref = 1; | |
| 3238 | − context->binder_context_mgr_node->has_weak_ref = 1; | |
| 4372 | + binder_node_lock(new_node); | |
| 4373 | + new_node->local_weak_refs++; | |
| 4374 | + new_node->local_strong_refs++; | |
| 4375 | + new_node->has_strong_ref = 1; | |
| 4376 | + new_node->has_weak_ref = 1; | |
| 4377 | + context->binder_context_mgr_node = new_node; | |
| 4378 | + binder_node_unlock(new_node); | |
| 4379 | + binder_put_node(new_node); | |
| 3239 | 4380 | out: |
| 4381 | + mutex_unlock(&context->context_mgr_node_lock); | |
| 3240 | 4382 | return ret; |
| 3241 | 4383 | } |
| 3242 | 4384 | |
| 4385 | +static int binder_ioctl_get_node_debug_info(struct binder_proc *proc, | |
| 4386 | + struct binder_node_debug_info *info) | |
| 4387 | +{ | |
| 4388 | + struct rb_node *n; | |
| 4389 | + binder_uintptr_t ptr = info->ptr; | |
| 4390 | + | |
| 4391 | + memset(info, 0, sizeof(*info)); | |
| 4392 | + | |
| 4393 | + binder_inner_proc_lock(proc); | |
| 4394 | + for (n = rb_first(&proc->nodes); n != NULL; n = rb_next(n)) { | |
| 4395 | + struct binder_node *node = rb_entry(n, struct binder_node, | |
| 4396 | + rb_node); | |
| 4397 | + if (node->ptr > ptr) { | |
| 4398 | + info->ptr = node->ptr; | |
| 4399 | + info->cookie = node->cookie; | |
| 4400 | + info->has_strong_ref = node->has_strong_ref; | |
| 4401 | + info->has_weak_ref = node->has_weak_ref; | |
| 4402 | + break; | |
| 4403 | + } | |
| 4404 | + } | |
| 4405 | + binder_inner_proc_unlock(proc); | |
| 4406 | + | |
| 4407 | + return 0; | |
| 4408 | +} | |
| 4409 | + | |
| 3243 | 4410 | static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) |
| 3244 | 4411 | { |
| 3245 | 4412 | int ret; |
| @@ -3251,13 +4418,14 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) · 5 unmodified lines | ||
| 3251 | 4418 | /*pr_info("binder_ioctl: %d:%d %x %lx\n", |
| 3252 | 4419 | proc->pid, current->pid, cmd, arg);*/ |
| 3253 | 4420 | |
| 4421 | + binder_selftest_alloc(&proc->alloc); | |
| 4422 | + | |
| 3254 | 4423 | trace_binder_ioctl(cmd, arg); |
| 3255 | 4424 | |
| 3256 | 4425 | ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); |
| 3257 | 4426 | if (ret) |
| 3258 | 4427 | goto err_unlocked; |
| 3259 | 4428 | |
| 3260 | − binder_lock(__func__); | |
| 3261 | 4429 | thread = binder_get_thread(proc); |
| 3262 | 4430 | if (thread == NULL) { |
| 3263 | 4431 | ret = -ENOMEM; |
| @@ -3270,12 +4438,19 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) · 6 unmodified lines | ||
| 3270 | 4438 | if (ret) |
| 3271 | 4439 | goto err; |
| 3272 | 4440 | break; |
| 3273 | − case BINDER_SET_MAX_THREADS: | |
| 3274 | − if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) { | |
| 4441 | + case BINDER_SET_MAX_THREADS: { | |
| 4442 | + int max_threads; | |
| 4443 | + | |
| 4444 | + if (copy_from_user(&max_threads, ubuf, | |
| 4445 | + sizeof(max_threads))) { | |
| 3275 | 4446 | ret = -EINVAL; |
| 3276 | 4447 | goto err; |
| 3277 | 4448 | } |
| 4449 | + binder_inner_proc_lock(proc); | |
| 4450 | + proc->max_threads = max_threads; | |
| 4451 | + binder_inner_proc_unlock(proc); | |
| 3278 | 4452 | break; |
| 4453 | + } | |
| 3279 | 4454 | case BINDER_SET_CONTEXT_MGR: |
| 3280 | 4455 | ret = binder_ioctl_set_ctx_mgr(filp); |
| 3281 | 4456 | if (ret) |
| @@ -3284,7 +4459,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) · 2 unmodified lines | ||
| 3284 | 4459 | case BINDER_THREAD_EXIT: |
| 3285 | 4460 | binder_debug(BINDER_DEBUG_THREADS, "%d:%d exit\n", |
| 3286 | 4461 | proc->pid, thread->pid); |
| 3287 | − binder_free_thread(proc, thread); | |
| 4462 | + binder_thread_release(proc, thread); | |
| 3288 | 4463 | thread = NULL; |
| 3289 | 4464 | break; |
| 3290 | 4465 | case BINDER_VERSION: { |
| @@ -3301,6 +4476,24 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) · 10 unmodified lines | ||
| 3301 | 4476 | } |
| 3302 | 4477 | break; |
| 3303 | 4478 | } |
| 4479 | + case BINDER_GET_NODE_DEBUG_INFO: { | |
| 4480 | + struct binder_node_debug_info info; | |
| 4481 | + | |
| 4482 | + if (copy_from_user(&info, ubuf, sizeof(info))) { | |
| 4483 | + ret = -EFAULT; | |
| 4484 | + goto err; | |
| 4485 | + } | |
| 4486 | + | |
| 4487 | + ret = binder_ioctl_get_node_debug_info(proc, &info); | |
| 4488 | + if (ret < 0) | |
| 4489 | + goto err; | |
| 4490 | + | |
| 4491 | + if (copy_to_user(ubuf, &info, sizeof(info))) { | |
| 4492 | + ret = -EFAULT; | |
| 4493 | + goto err; | |
| 4494 | + } | |
| 4495 | + break; | |
| 4496 | + } | |
| 3304 | 4497 | default: |
| 3305 | 4498 | ret = -EINVAL; |
| 3306 | 4499 | goto err; |
| @@ -3308,8 +4501,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) · 1 unmodified line | ||
| 3308 | 4501 | ret = 0; |
| 3309 | 4502 | err: |
| 3310 | 4503 | if (thread) |
| 3311 | − thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN; | |
| 3312 | − binder_unlock(__func__); | |
| 4504 | + thread->looper_need_return = false; | |
| 3313 | 4505 | wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); |
| 3314 | 4506 | if (ret && ret != -ERESTARTSYS) |
| 3315 | 4507 | pr_info("%d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret); |
| @@ -3338,8 +4530,7 @@ static void binder_vma_close(struct vm_area_struct *vma) · 22 unmodified lines | ||
| 3338 | 4530 | proc->pid, vma->vm_start, vma->vm_end, |
| 3339 | 4531 | (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags, |
| 3340 | 4532 | (unsigned long)pgprot_val(vma->vm_page_prot)); |
| 3341 | − proc->vma = NULL; | |
| 3342 | − proc->vma_vm_mm = NULL; | |
| 4533 | + binder_alloc_vma_close(&proc->alloc); | |
| 3343 | 4534 | binder_defer_work(proc, BINDER_DEFERRED_PUT_FILES); |
| 3344 | 4535 | } |
| 3345 | 4536 | |
| @@ -3357,10 +4548,8 @@ static const struct vm_operations_struct binder_vm_ops = { · 11 unmodified lines | ||
| 3357 | 4548 | static int binder_mmap(struct file *filp, struct vm_area_struct *vma) |
| 3358 | 4549 | { |
| 3359 | 4550 | int ret; |
| 3360 | − struct vm_struct *area; | |
| 3361 | 4551 | struct binder_proc *proc = filp->private_data; |
| 3362 | 4552 | const char *failure_string; |
| 3363 | − struct binder_buffer *buffer; | |
| 3364 | 4553 | |
| 3365 | 4554 | if (proc->tsk != current->group_leader) |
| 3366 | 4555 | return -EINVAL; |
| @@ -3369,8 +4558,8 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) · 2 unmodified lines | ||
| 3369 | 4558 | vma->vm_end = vma->vm_start + SZ_4M; |
| 3370 | 4559 | |
| 3371 | 4560 | binder_debug(BINDER_DEBUG_OPEN_CLOSE, |
| 3372 | − "binder_mmap: %d %lx-%lx (%ld K) vma %lx pagep %lx\n", | |
| 3373 | − proc->pid, vma->vm_start, vma->vm_end, | |
| 4561 | + "%s: %d %lx-%lx (%ld K) vma %lx pagep %lx\n", | |
| 4562 | + __func__, proc->pid, vma->vm_start, vma->vm_end, | |
| 3374 | 4563 | (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags, |
| 3375 | 4564 | (unsigned long)pgprot_val(vma->vm_page_prot)); |
| 3376 | 4565 | |
| @@ -3380,73 +4569,15 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) · 3 unmodified lines | ||
| 3380 | 4569 | goto err_bad_arg; |
| 3381 | 4570 | } |
| 3382 | 4571 | vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE; |
| 3383 | − | |
| 3384 | − mutex_lock(&binder_mmap_lock); | |
| 3385 | − if (proc->buffer) { | |
| 3386 | − ret = -EBUSY; | |
| 3387 | − failure_string = "already mapped"; | |
| 3388 | − goto err_already_mapped; | |
| 3389 | − } | |
| 3390 | − | |
| 3391 | − area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP); | |
| 3392 | − if (area == NULL) { | |
| 3393 | − ret = -ENOMEM; | |
| 3394 | − failure_string = "get_vm_area"; | |
| 3395 | − goto err_get_vm_area_failed; | |
| 3396 | − } | |
| 3397 | − proc->buffer = area->addr; | |
| 3398 | − proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer; | |
| 3399 | − mutex_unlock(&binder_mmap_lock); | |
| 3400 | − | |
| 3401 | −#ifdef CONFIG_CPU_CACHE_VIPT | |
| 3402 | − if (cache_is_vipt_aliasing()) { | |
| 3403 | − while (CACHE_COLOUR((vma->vm_start ^ (uint32_t)proc->buffer))) { | |
| 3404 | − pr_info("binder_mmap: %d %lx-%lx maps %p bad alignment\n", proc->pid, vma->vm_start, vma->vm_end, proc->buffer); | |
| 3405 | − vma->vm_start += PAGE_SIZE; | |
| 3406 | − } | |
| 3407 | − } | |
| 3408 | −#endif | |
| 3409 | − proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL); | |
| 3410 | − if (proc->pages == NULL) { | |
| 3411 | − ret = -ENOMEM; | |
| 3412 | − failure_string = "alloc page array"; | |
| 3413 | − goto err_alloc_pages_failed; | |
| 3414 | − } | |
| 3415 | − proc->buffer_size = vma->vm_end - vma->vm_start; | |
| 3416 | − | |
| 3417 | 4572 | vma->vm_ops = &binder_vm_ops; |
| 3418 | 4573 | vma->vm_private_data = proc; |
| 3419 | 4574 | |
| 3420 | − if (binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)) { | |
| 3421 | − ret = -ENOMEM; | |
| 3422 | − failure_string = "alloc small buf"; | |
| 3423 | − goto err_alloc_small_buf_failed; | |
| 3424 | − } | |
| 3425 | − buffer = proc->buffer; | |
| 3426 | − INIT_LIST_HEAD(&proc->buffers); | |
| 3427 | − list_add(&buffer->entry, &proc->buffers); | |
| 3428 | − buffer->free = 1; | |
| 3429 | − binder_insert_free_buffer(proc, buffer); | |
| 3430 | − proc->free_async_space = proc->buffer_size / 2; | |
| 3431 | − barrier(); | |
| 4575 | + ret = binder_alloc_mmap_handler(&proc->alloc, vma); | |
| 4576 | + if (ret) | |
| 4577 | + return ret; | |
| 3432 | 4578 | proc->files = get_files_struct(current); |
| 3433 | − proc->vma = vma; | |
| 3434 | − proc->vma_vm_mm = vma->vm_mm; | |
| 3435 | − | |
| 3436 | − /*pr_info("binder_mmap: %d %lx-%lx maps %p\n", | |
| 3437 | − proc->pid, vma->vm_start, vma->vm_end, proc->buffer);*/ | |
| 3438 | 4579 | return 0; |
| 3439 | 4580 | |
| 3440 | −err_alloc_small_buf_failed: | |
| 3441 | − kfree(proc->pages); | |
| 3442 | − proc->pages = NULL; | |
| 3443 | −err_alloc_pages_failed: | |
| 3444 | − mutex_lock(&binder_mmap_lock); | |
| 3445 | − vfree(proc->buffer); | |
| 3446 | − proc->buffer = NULL; | |
| 3447 | −err_get_vm_area_failed: | |
| 3448 | −err_already_mapped: | |
| 3449 | − mutex_unlock(&binder_mmap_lock); | |
| 3450 | 4581 | err_bad_arg: |
| 3451 | 4582 | pr_err("binder_mmap: %d %lx-%lx %s failed %d\n", |
| 3452 | 4583 | proc->pid, vma->vm_start, vma->vm_end, failure_string, ret); |
| @@ -3464,24 +4595,26 @@ static int binder_open(struct inode *nodp, struct file *filp) · 11 unmodified lines | ||
| 3464 | 4595 | proc = kzalloc(sizeof(*proc), GFP_KERNEL); |
| 3465 | 4596 | if (proc == NULL) |
| 3466 | 4597 | return -ENOMEM; |
| 4598 | + spin_lock_init(&proc->inner_lock); | |
| 4599 | + spin_lock_init(&proc->outer_lock); | |
| 3467 | 4600 | get_task_struct(current->group_leader); |
| 3468 | 4601 | proc->tsk = current->group_leader; |
| 3469 | 4602 | INIT_LIST_HEAD(&proc->todo); |
| 3470 | − init_waitqueue_head(&proc->wait); | |
| 3471 | 4603 | proc->default_priority = task_nice(current); |
| 3472 | 4604 | binder_dev = container_of(filp->private_data, struct binder_device, |
| 3473 | 4605 | miscdev); |
| 3474 | 4606 | proc->context = &binder_dev->context; |
| 3475 | − | |
| 3476 | − binder_lock(__func__); | |
| 4607 | + binder_alloc_init(&proc->alloc); | |
| 3477 | 4608 | |
| 3478 | 4609 | binder_stats_created(BINDER_STAT_PROC); |
| 3479 | − hlist_add_head(&proc->proc_node, &binder_procs); | |
| 3480 | 4610 | proc->pid = current->group_leader->pid; |
| 3481 | 4611 | INIT_LIST_HEAD(&proc->delivered_death); |
| 4612 | + INIT_LIST_HEAD(&proc->waiting_threads); | |
| 3482 | 4613 | filp->private_data = proc; |
| 3483 | 4614 | |
| 3484 | − binder_unlock(__func__); | |
| 4615 | + mutex_lock(&binder_procs_lock); | |
| 4616 | + hlist_add_head(&proc->proc_node, &binder_procs); | |
| 4617 | + mutex_unlock(&binder_procs_lock); | |
| 3485 | 4618 | |
| 3486 | 4619 | if (binder_debugfs_dir_entry_proc) { |
| 3487 | 4620 | char strbuf[11]; |
| @@ -3517,16 +4650,17 @@ static void binder_deferred_flush(struct binder_proc *proc) · 29 unmodified lines | ||
| 3517 | 4650 | struct rb_node *n; |
| 3518 | 4651 | int wake_count = 0; |
| 3519 | 4652 | |
| 4653 | + binder_inner_proc_lock(proc); | |
| 3520 | 4654 | for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n)) { |
| 3521 | 4655 | struct binder_thread *thread = rb_entry(n, struct binder_thread, rb_node); |
| 3522 | 4656 | |
| 3523 | − thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN; | |
| 4657 | + thread->looper_need_return = true; | |
| 3524 | 4658 | if (thread->looper & BINDER_LOOPER_STATE_WAITING) { |
| 3525 | 4659 | wake_up_interruptible(&thread->wait); |
| 3526 | 4660 | wake_count++; |
| 3527 | 4661 | } |
| 3528 | 4662 | } |
| 3529 | − wake_up_interruptible_all(&proc->wait); | |
| 4663 | + binder_inner_proc_unlock(proc); | |
| 3530 | 4664 | |
| 3531 | 4665 | binder_debug(BINDER_DEBUG_OPEN_CLOSE, |
| 3532 | 4666 | "binder_flush: %d woke %d threads\n", proc->pid, |
| @@ -3547,13 +4681,21 @@ static int binder_node_release(struct binder_node *node, int refs) · 14 unmodified lines | ||
| 3547 | 4681 | { |
| 3548 | 4682 | struct binder_ref *ref; |
| 3549 | 4683 | int death = 0; |
| 4684 | + struct binder_proc *proc = node->proc; | |
| 3550 | 4685 | |
| 3551 | − list_del_init(&node->work.entry); | |
| 3552 | − binder_release_work(&node->async_todo); | |
| 4686 | + binder_release_work(proc, &node->async_todo); | |
| 3553 | 4687 | |
| 3554 | − if (hlist_empty(&node->refs)) { | |
| 3555 | − kfree(node); | |
| 3556 | − binder_stats_deleted(BINDER_STAT_NODE); | |
| 4688 | + binder_node_lock(node); | |
| 4689 | + binder_inner_proc_lock(proc); | |
| 4690 | + binder_dequeue_work_ilocked(&node->work); | |
| 4691 | + /* | |
| 4692 | + * The caller must have taken a temporary ref on the node, | |
| 4693 | + */ | |
| 4694 | + BUG_ON(!node->tmp_refs); | |
| 4695 | + if (hlist_empty(&node->refs) && node->tmp_refs == 1) { | |
| 4696 | + binder_inner_proc_unlock(proc); | |
| 4697 | + binder_node_unlock(node); | |
| 4698 | + binder_free_node(node); | |
| 3557 | 4699 | |
| 3558 | 4700 | return refs; |
| 3559 | 4701 | } |
| @@ -3561,45 +4703,58 @@ static int binder_node_release(struct binder_node *node, int refs) · 1 unmodified line | ||
| 3561 | 4703 | node->proc = NULL; |
| 3562 | 4704 | node->local_strong_refs = 0; |
| 3563 | 4705 | node->local_weak_refs = 0; |
| 4706 | + binder_inner_proc_unlock(proc); | |
| 4707 | + | |
| 4708 | + spin_lock(&binder_dead_nodes_lock); | |
| 3564 | 4709 | hlist_add_head(&node->dead_node, &binder_dead_nodes); |
| 4710 | + spin_unlock(&binder_dead_nodes_lock); | |
| 3565 | 4711 | |
| 3566 | 4712 | hlist_for_each_entry(ref, &node->refs, node_entry) { |
| 3567 | 4713 | refs++; |
| 3568 | − | |
| 3569 | − if (!ref->death) | |
| 4714 | + /* | |
| 4715 | + * Need the node lock to synchronize | |
| 4716 | + * with new notification requests and the | |
| 4717 | + * inner lock to synchronize with queued | |
| 4718 | + * death notifications. | |
| 4719 | + */ | |
| 4720 | + binder_inner_proc_lock(ref->proc); | |
| 4721 | + if (!ref->death) { | |
| 4722 | + binder_inner_proc_unlock(ref->proc); | |
| 3570 | 4723 | continue; |
| 4724 | + } | |
| 3571 | 4725 | |
| 3572 | 4726 | death++; |
| 3573 | 4727 | |
| 3574 | − if (list_empty(&ref->death->work.entry)) { | |
| 3575 | − ref->death->work.type = BINDER_WORK_DEAD_BINDER; | |
| 3576 | − list_add_tail(&ref->death->work.entry, | |
| 3577 | − &ref->proc->todo); | |
| 3578 | − wake_up_interruptible(&ref->proc->wait); | |
| 3579 | − } else | |
| 3580 | − BUG(); | |
| 4728 | + BUG_ON(!list_empty(&ref->death->work.entry)); | |
| 4729 | + ref->death->work.type = BINDER_WORK_DEAD_BINDER; | |
| 4730 | + binder_enqueue_work_ilocked(&ref->death->work, | |
| 4731 | + &ref->proc->todo); | |
| 4732 | + binder_wakeup_proc_ilocked(ref->proc); | |
| 4733 | + binder_inner_proc_unlock(ref->proc); | |
| 3581 | 4734 | } |
| 3582 | 4735 | |
| 3583 | 4736 | binder_debug(BINDER_DEBUG_DEAD_BINDER, |
| 3584 | 4737 | "node %d now dead, refs %d, death %d\n", |
| 3585 | 4738 | node->debug_id, refs, death); |
| 4739 | + binder_node_unlock(node); | |
| 4740 | + binder_put_node(node); | |
| 3586 | 4741 | |
| 3587 | 4742 | return refs; |
| 3588 | 4743 | } |
| 3589 | 4744 | |
| 3590 | 4745 | static void binder_deferred_release(struct binder_proc *proc) |
| 3591 | 4746 | { |
| 3592 | − struct binder_transaction *t; | |
| 3593 | 4747 | struct binder_context *context = proc->context; |
| 3594 | 4748 | struct rb_node *n; |
| 3595 | − int threads, nodes, incoming_refs, outgoing_refs, buffers, | |
| 3596 | − active_transactions, page_count; | |
| 4749 | + int threads, nodes, incoming_refs, outgoing_refs, active_transactions; | |
| 3597 | 4750 | |
| 3598 | − BUG_ON(proc->vma); | |
| 3599 | 4751 | BUG_ON(proc->files); |
| 3600 | 4752 | |
| 4753 | + mutex_lock(&binder_procs_lock); | |
| 3601 | 4754 | hlist_del(&proc->proc_node); |
| 4755 | + mutex_unlock(&binder_procs_lock); | |
| 3602 | 4756 | |
| 4757 | + mutex_lock(&context->context_mgr_node_lock); | |
| 3603 | 4758 | if (context->binder_context_mgr_node && |
| 3604 | 4759 | context->binder_context_mgr_node->proc == proc) { |
| 3605 | 4760 | binder_debug(BINDER_DEBUG_DEAD_BINDER, |
| @@ -3607,15 +4762,25 @@ static void binder_deferred_release(struct binder_proc *proc) · 1 unmodified line | ||
| 3607 | 4762 | __func__, proc->pid); |
| 3608 | 4763 | context->binder_context_mgr_node = NULL; |
| 3609 | 4764 | } |
| 4765 | + mutex_unlock(&context->context_mgr_node_lock); | |
| 4766 | + binder_inner_proc_lock(proc); | |
| 4767 | + /* | |
| 4768 | + * Make sure proc stays alive after we | |
| 4769 | + * remove all the threads | |
| 4770 | + */ | |
| 4771 | + proc->tmp_ref++; | |
| 3610 | 4772 | |
| 4773 | + proc->is_dead = true; | |
| 3611 | 4774 | threads = 0; |
| 3612 | 4775 | active_transactions = 0; |
| 3613 | 4776 | while ((n = rb_first(&proc->threads))) { |
| 3614 | 4777 | struct binder_thread *thread; |
| 3615 | 4778 | |
| 3616 | 4779 | thread = rb_entry(n, struct binder_thread, rb_node); |
| 4780 | + binder_inner_proc_unlock(proc); | |
| 3617 | 4781 | threads++; |
| 3618 | − active_transactions += binder_free_thread(proc, thread); | |
| 4782 | + active_transactions += binder_thread_release(proc, thread); | |
| 4783 | + binder_inner_proc_lock(proc); | |
| 3619 | 4784 | } |
| 3620 | 4785 | |
| 3621 | 4786 | nodes = 0; |
| @@ -3625,73 +4790,42 @@ static void binder_deferred_release(struct binder_proc *proc) · 3 unmodified lines | ||
| 3625 | 4790 | |
| 3626 | 4791 | node = rb_entry(n, struct binder_node, rb_node); |
| 3627 | 4792 | nodes++; |
| 4793 | + /* | |
| 4794 | + * take a temporary ref on the node before | |
| 4795 | + * calling binder_node_release() which will either | |
| 4796 | + * kfree() the node or call binder_put_node() | |
| 4797 | + */ | |
| 4798 | + binder_inc_node_tmpref_ilocked(node); | |
| 3628 | 4799 | rb_erase(&node->rb_node, &proc->nodes); |
| 4800 | + binder_inner_proc_unlock(proc); | |
| 3629 | 4801 | incoming_refs = binder_node_release(node, incoming_refs); |
| 4802 | + binder_inner_proc_lock(proc); | |
| 3630 | 4803 | } |
| 4804 | + binder_inner_proc_unlock(proc); | |
| 3631 | 4805 | |
| 3632 | 4806 | outgoing_refs = 0; |
| 4807 | + binder_proc_lock(proc); | |
| 3633 | 4808 | while ((n = rb_first(&proc->refs_by_desc))) { |
| 3634 | 4809 | struct binder_ref *ref; |
| 3635 | 4810 | |
| 3636 | 4811 | ref = rb_entry(n, struct binder_ref, rb_node_desc); |
| 3637 | 4812 | outgoing_refs++; |
| 3638 | − binder_delete_ref(ref); | |
| 3639 | − } | |
| 3640 | − | |
| 3641 | − binder_release_work(&proc->todo); | |
| 3642 | − binder_release_work(&proc->delivered_death); | |
| 3643 | − | |
| 3644 | − buffers = 0; | |
| 3645 | − while ((n = rb_first(&proc->allocated_buffers))) { | |
| 3646 | − struct binder_buffer *buffer; | |
| 3647 | − | |
| 3648 | − buffer = rb_entry(n, struct binder_buffer, rb_node); | |
| 3649 | − | |
| 3650 | − t = buffer->transaction; | |
| 3651 | − if (t) { | |
| 3652 | − t->buffer = NULL; | |
| 3653 | − buffer->transaction = NULL; | |
| 3654 | − pr_err("release proc %d, transaction %d, not freed\n", | |
| 3655 | − proc->pid, t->debug_id); | |
| 3656 | − /*BUG();*/ | |
| 3657 | − } | |
| 3658 | − | |
| 3659 | − binder_free_buf(proc, buffer); | |
| 3660 | − buffers++; | |
| 4813 | + binder_cleanup_ref_olocked(ref); | |
| 4814 | + binder_proc_unlock(proc); | |
| 4815 | + binder_free_ref(ref); | |
| 4816 | + binder_proc_lock(proc); | |
| 3661 | 4817 | } |
| 4818 | + binder_proc_unlock(proc); | |
| 3662 | 4819 | |
| 3663 | − binder_stats_deleted(BINDER_STAT_PROC); | |
| 3664 | − | |
| 3665 | − page_count = 0; | |
| 3666 | − if (proc->pages) { | |
| 3667 | − int i; | |
| 3668 | − | |
| 3669 | − for (i = 0; i < proc->buffer_size / PAGE_SIZE; i++) { | |
| 3670 | − void *page_addr; | |
| 3671 | − | |
| 3672 | − if (!proc->pages[i]) | |
| 3673 | − continue; | |
| 3674 | − | |
| 3675 | − page_addr = proc->buffer + i * PAGE_SIZE; | |
| 3676 | − binder_debug(BINDER_DEBUG_BUFFER_ALLOC, | |
| 3677 | − "%s: %d: page %d at %p not freed\n", | |
| 3678 | − __func__, proc->pid, i, page_addr); | |
| 3679 | − unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE); | |
| 3680 | − __free_page(proc->pages[i]); | |
| 3681 | − page_count++; | |
| 3682 | − } | |
| 3683 | − kfree(proc->pages); | |
| 3684 | − vfree(proc->buffer); | |
| 3685 | − } | |
| 3686 | − | |
| 3687 | − put_task_struct(proc->tsk); | |
| 4820 | + binder_release_work(proc, &proc->todo); | |
| 4821 | + binder_release_work(proc, &proc->delivered_death); | |
| 3688 | 4822 | |
| 3689 | 4823 | binder_debug(BINDER_DEBUG_OPEN_CLOSE, |
| 3690 | − "%s: %d threads %d, nodes %d (ref %d), refs %d, active transactions %d, buffers %d, pages %d\n", | |
| 4824 | + "%s: %d threads %d, nodes %d (ref %d), refs %d, active transactions %d\n", | |
| 3691 | 4825 | __func__, proc->pid, threads, nodes, incoming_refs, |
| 3692 | − outgoing_refs, active_transactions, buffers, page_count); | |
| 4826 | + outgoing_refs, active_transactions); | |
| 3693 | 4827 | |
| 3694 | − kfree(proc); | |
| 4828 | + binder_proc_dec_tmpref(proc); | |
| 3695 | 4829 | } |
| 3696 | 4830 | |
| 3697 | 4831 | static void binder_deferred_func(struct work_struct *work) |
| @@ -3702,7 +4836,6 @@ static void binder_deferred_func(struct work_struct *work) · 4 unmodified lines | ||
| 3702 | 4836 | int defer; |
| 3703 | 4837 | |
| 3704 | 4838 | do { |
| 3705 | − binder_lock(__func__); | |
| 3706 | 4839 | mutex_lock(&binder_deferred_lock); |
| 3707 | 4840 | if (!hlist_empty(&binder_deferred_list)) { |
| 3708 | 4841 | proc = hlist_entry(binder_deferred_list.first, |
| @@ -3729,7 +4862,6 @@ static void binder_deferred_func(struct work_struct *work) · 20 unmodified lines | ||
| 3729 | 4862 | if (defer & BINDER_DEFERRED_RELEASE) |
| 3730 | 4863 | binder_deferred_release(proc); /* frees proc */ |
| 3731 | 4864 | |
| 3732 | − binder_unlock(__func__); | |
| 3733 | 4865 | if (files) |
| 3734 | 4866 | put_files_struct(files); |
| 3735 | 4867 | } while (proc); |
| @@ -3749,41 +4881,51 @@ binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer) · 13 unmodified lines | ||
| 3749 | 4881 | mutex_unlock(&binder_deferred_lock); |
| 3750 | 4882 | } |
| 3751 | 4883 | |
| 3752 | −static void print_binder_transaction(struct seq_file *m, const char *prefix, | |
| 3753 | − struct binder_transaction *t) | |
| 4884 | +static void print_binder_transaction_ilocked(struct seq_file *m, | |
587 more lines aren’t shown here.
| @@ -0,0 +1,1009 @@ | ||
| 1 | +/* binder_alloc.c | |
| 2 | + * | |
| 3 | + * Android IPC Subsystem | |
| 4 | + * | |
| 5 | + * Copyright (C) 2007-2017 Google, Inc. | |
| 6 | + * | |
| 7 | + * This software is licensed under the terms of the GNU General Public | |
| 8 | + * License version 2, as published by the Free Software Foundation, and | |
| 9 | + * may be copied, distributed, and modified under those terms. | |
| 10 | + * | |
| 11 | + * This program is distributed in the hope that it will be useful, | |
| 12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 14 | + * GNU General Public License for more details. | |
| 15 | + * | |
| 16 | + */ | |
| 17 | + | |
| 18 | +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
| 19 | + | |
| 20 | +#include <asm/cacheflush.h> | |
| 21 | +#include <linux/list.h> | |
| 22 | +#include <linux/sched/mm.h> | |
| 23 | +#include <linux/module.h> | |
| 24 | +#include <linux/rtmutex.h> | |
| 25 | +#include <linux/rbtree.h> | |
| 26 | +#include <linux/seq_file.h> | |
| 27 | +#include <linux/vmalloc.h> | |
| 28 | +#include <linux/slab.h> | |
| 29 | +#include <linux/sched.h> | |
| 30 | +#include <linux/list_lru.h> | |
| 31 | +#include "binder_alloc.h" | |
| 32 | +#include "binder_trace.h" | |
| 33 | + | |
| 34 | +struct list_lru binder_alloc_lru; | |
| 35 | + | |
| 36 | +static DEFINE_MUTEX(binder_alloc_mmap_lock); | |
| 37 | + | |
| 38 | +enum { | |
| 39 | + BINDER_DEBUG_OPEN_CLOSE = 1U << 1, | |
| 40 | + BINDER_DEBUG_BUFFER_ALLOC = 1U << 2, | |
| 41 | + BINDER_DEBUG_BUFFER_ALLOC_ASYNC = 1U << 3, | |
| 42 | +}; | |
| 43 | +static uint32_t binder_alloc_debug_mask; | |
| 44 | + | |
| 45 | +module_param_named(debug_mask, binder_alloc_debug_mask, | |
| 46 | + uint, 0644); | |
| 47 | + | |
| 48 | +#define binder_alloc_debug(mask, x...) \ | |
| 49 | + do { \ | |
| 50 | + if (binder_alloc_debug_mask & mask) \ | |
| 51 | + pr_info(x); \ | |
| 52 | + } while (0) | |
| 53 | + | |
| 54 | +static struct binder_buffer *binder_buffer_next(struct binder_buffer *buffer) | |
| 55 | +{ | |
| 56 | + return list_entry(buffer->entry.next, struct binder_buffer, entry); | |
| 57 | +} | |
| 58 | + | |
| 59 | +static struct binder_buffer *binder_buffer_prev(struct binder_buffer *buffer) | |
| 60 | +{ | |
| 61 | + return list_entry(buffer->entry.prev, struct binder_buffer, entry); | |
| 62 | +} | |
| 63 | + | |
| 64 | +static size_t binder_alloc_buffer_size(struct binder_alloc *alloc, | |
| 65 | + struct binder_buffer *buffer) | |
| 66 | +{ | |
| 67 | + if (list_is_last(&buffer->entry, &alloc->buffers)) | |
| 68 | + return (u8 *)alloc->buffer + | |
| 69 | + alloc->buffer_size - (u8 *)buffer->data; | |
| 70 | + return (u8 *)binder_buffer_next(buffer)->data - (u8 *)buffer->data; | |
| 71 | +} | |
| 72 | + | |
| 73 | +static void binder_insert_free_buffer(struct binder_alloc *alloc, | |
| 74 | + struct binder_buffer *new_buffer) | |
| 75 | +{ | |
| 76 | + struct rb_node **p = &alloc->free_buffers.rb_node; | |
| 77 | + struct rb_node *parent = NULL; | |
| 78 | + struct binder_buffer *buffer; | |
| 79 | + size_t buffer_size; | |
| 80 | + size_t new_buffer_size; | |
| 81 | + | |
| 82 | + BUG_ON(!new_buffer->free); | |
| 83 | + | |
| 84 | + new_buffer_size = binder_alloc_buffer_size(alloc, new_buffer); | |
| 85 | + | |
| 86 | + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, | |
| 87 | + "%d: add free buffer, size %zd, at %pK\n", | |
| 88 | + alloc->pid, new_buffer_size, new_buffer); | |
| 89 | + | |
| 90 | + while (*p) { | |
| 91 | + parent = *p; | |
| 92 | + buffer = rb_entry(parent, struct binder_buffer, rb_node); | |
| 93 | + BUG_ON(!buffer->free); | |
| 94 | + | |
| 95 | + buffer_size = binder_alloc_buffer_size(alloc, buffer); | |
| 96 | + | |
| 97 | + if (new_buffer_size < buffer_size) | |
| 98 | + p = &parent->rb_left; | |
| 99 | + else | |
| 100 | + p = &parent->rb_right; | |
| 101 | + } | |
| 102 | + rb_link_node(&new_buffer->rb_node, parent, p); | |
| 103 | + rb_insert_color(&new_buffer->rb_node, &alloc->free_buffers); | |
| 104 | +} | |
| 105 | + | |
| 106 | +static void binder_insert_allocated_buffer_locked( | |
| 107 | + struct binder_alloc *alloc, struct binder_buffer *new_buffer) | |
| 108 | +{ | |
| 109 | + struct rb_node **p = &alloc->allocated_buffers.rb_node; | |
| 110 | + struct rb_node *parent = NULL; | |
| 111 | + struct binder_buffer *buffer; | |
| 112 | + | |
| 113 | + BUG_ON(new_buffer->free); | |
| 114 | + | |
| 115 | + while (*p) { | |
| 116 | + parent = *p; | |
| 117 | + buffer = rb_entry(parent, struct binder_buffer, rb_node); | |
| 118 | + BUG_ON(buffer->free); | |
| 119 | + | |
| 120 | + if (new_buffer->data < buffer->data) | |
| 121 | + p = &parent->rb_left; | |
| 122 | + else if (new_buffer->data > buffer->data) | |
| 123 | + p = &parent->rb_right; | |
| 124 | + else | |
| 125 | + BUG(); | |
| 126 | + } | |
| 127 | + rb_link_node(&new_buffer->rb_node, parent, p); | |
| 128 | + rb_insert_color(&new_buffer->rb_node, &alloc->allocated_buffers); | |
| 129 | +} | |
| 130 | + | |
| 131 | +static struct binder_buffer *binder_alloc_prepare_to_free_locked( | |
| 132 | + struct binder_alloc *alloc, | |
| 133 | + uintptr_t user_ptr) | |
| 134 | +{ | |
| 135 | + struct rb_node *n = alloc->allocated_buffers.rb_node; | |
| 136 | + struct binder_buffer *buffer; | |
| 137 | + void *kern_ptr; | |
| 138 | + | |
| 139 | + kern_ptr = (void *)(user_ptr - alloc->user_buffer_offset); | |
| 140 | + | |
| 141 | + while (n) { | |
| 142 | + buffer = rb_entry(n, struct binder_buffer, rb_node); | |
| 143 | + BUG_ON(buffer->free); | |
| 144 | + | |
| 145 | + if (kern_ptr < buffer->data) | |
| 146 | + n = n->rb_left; | |
| 147 | + else if (kern_ptr > buffer->data) | |
| 148 | + n = n->rb_right; | |
| 149 | + else { | |
| 150 | + /* | |
| 151 | + * Guard against user threads attempting to | |
| 152 | + * free the buffer twice | |
| 153 | + */ | |
| 154 | + if (buffer->free_in_progress) { | |
| 155 | + pr_err("%d:%d FREE_BUFFER u%016llx user freed buffer twice\n", | |
| 156 | + alloc->pid, current->pid, (u64)user_ptr); | |
| 157 | + return NULL; | |
| 158 | + } | |
| 159 | + buffer->free_in_progress = 1; | |
| 160 | + return buffer; | |
| 161 | + } | |
| 162 | + } | |
| 163 | + return NULL; | |
| 164 | +} | |
| 165 | + | |
| 166 | +/** | |
| 167 | + * binder_alloc_buffer_lookup() - get buffer given user ptr | |
| 168 | + * @alloc: binder_alloc for this proc | |
| 169 | + * @user_ptr: User pointer to buffer data | |
| 170 | + * | |
| 171 | + * Validate userspace pointer to buffer data and return buffer corresponding to | |
| 172 | + * that user pointer. Search the rb tree for buffer that matches user data | |
| 173 | + * pointer. | |
| 174 | + * | |
| 175 | + * Return: Pointer to buffer or NULL | |
| 176 | + */ | |
| 177 | +struct binder_buffer *binder_alloc_prepare_to_free(struct binder_alloc *alloc, | |
| 178 | + uintptr_t user_ptr) | |
| 179 | +{ | |
| 180 | + struct binder_buffer *buffer; | |
| 181 | + | |
| 182 | + mutex_lock(&alloc->mutex); | |
| 183 | + buffer = binder_alloc_prepare_to_free_locked(alloc, user_ptr); | |
| 184 | + mutex_unlock(&alloc->mutex); | |
| 185 | + return buffer; | |
| 186 | +} | |
| 187 | + | |
| 188 | +static int binder_update_page_range(struct binder_alloc *alloc, int allocate, | |
| 189 | + void *start, void *end, | |
| 190 | + struct vm_area_struct *vma) | |
| 191 | +{ | |
| 192 | + void *page_addr; | |
| 193 | + unsigned long user_page_addr; | |
| 194 | + struct binder_lru_page *page; | |
| 195 | + struct mm_struct *mm = NULL; | |
| 196 | + bool need_mm = false; | |
| 197 | + | |
| 198 | + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, | |
| 199 | + "%d: %s pages %pK-%pK\n", alloc->pid, | |
| 200 | + allocate ? "allocate" : "free", start, end); | |
| 201 | + | |
| 202 | + if (end <= start) | |
| 203 | + return 0; | |
| 204 | + | |
| 205 | + trace_binder_update_page_range(alloc, allocate, start, end); | |
| 206 | + | |
| 207 | + if (allocate == 0) | |
| 208 | + goto free_range; | |
| 209 | + | |
| 210 | + for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) { | |
| 211 | + page = &alloc->pages[(page_addr - alloc->buffer) / PAGE_SIZE]; | |
| 212 | + if (!page->page_ptr) { | |
| 213 | + need_mm = true; | |
| 214 | + break; | |
| 215 | + } | |
| 216 | + } | |
| 217 | + | |
| 218 | + if (!vma && need_mm) | |
| 219 | + mm = get_task_mm(alloc->tsk); | |
| 220 | + | |
| 221 | + if (mm) { | |
| 222 | + down_write(&mm->mmap_sem); | |
| 223 | + vma = alloc->vma; | |
| 224 | + if (vma && mm != alloc->vma_vm_mm) { | |
| 225 | + pr_err("%d: vma mm and task mm mismatch\n", | |
| 226 | + alloc->pid); | |
| 227 | + vma = NULL; | |
| 228 | + } | |
| 229 | + } | |
| 230 | + | |
| 231 | + if (!vma && need_mm) { | |
| 232 | + pr_err("%d: binder_alloc_buf failed to map pages in userspace, no vma\n", | |
| 233 | + alloc->pid); | |
| 234 | + goto err_no_vma; | |
| 235 | + } | |
| 236 | + | |
| 237 | + for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) { | |
| 238 | + int ret; | |
| 239 | + bool on_lru; | |
| 240 | + size_t index; | |
| 241 | + | |
| 242 | + index = (page_addr - alloc->buffer) / PAGE_SIZE; | |
| 243 | + page = &alloc->pages[index]; | |
| 244 | + | |
| 245 | + if (page->page_ptr) { | |
| 246 | + trace_binder_alloc_lru_start(alloc, index); | |
| 247 | + | |
| 248 | + on_lru = list_lru_del(&binder_alloc_lru, &page->lru); | |
| 249 | + WARN_ON(!on_lru); | |
| 250 | + | |
| 251 | + trace_binder_alloc_lru_end(alloc, index); | |
| 252 | + continue; | |
| 253 | + } | |
| 254 | + | |
| 255 | + if (WARN_ON(!vma)) | |
| 256 | + goto err_page_ptr_cleared; | |
| 257 | + | |
| 258 | + trace_binder_alloc_page_start(alloc, index); | |
| 259 | + page->page_ptr = alloc_page(GFP_KERNEL | | |
| 260 | + __GFP_HIGHMEM | | |
| 261 | + __GFP_ZERO); | |
| 262 | + if (!page->page_ptr) { | |
| 263 | + pr_err("%d: binder_alloc_buf failed for page at %pK\n", | |
| 264 | + alloc->pid, page_addr); | |
| 265 | + goto err_alloc_page_failed; | |
| 266 | + } | |
| 267 | + page->alloc = alloc; | |
| 268 | + INIT_LIST_HEAD(&page->lru); | |
| 269 | + | |
| 270 | + ret = map_kernel_range_noflush((unsigned long)page_addr, | |
| 271 | + PAGE_SIZE, PAGE_KERNEL, | |
| 272 | + &page->page_ptr); | |
| 273 | + flush_cache_vmap((unsigned long)page_addr, | |
| 274 | + (unsigned long)page_addr + PAGE_SIZE); | |
| 275 | + if (ret != 1) { | |
| 276 | + pr_err("%d: binder_alloc_buf failed to map page at %pK in kernel\n", | |
| 277 | + alloc->pid, page_addr); | |
| 278 | + goto err_map_kernel_failed; | |
| 279 | + } | |
| 280 | + user_page_addr = | |
| 281 | + (uintptr_t)page_addr + alloc->user_buffer_offset; | |
| 282 | + ret = vm_insert_page(vma, user_page_addr, page[0].page_ptr); | |
| 283 | + if (ret) { | |
| 284 | + pr_err("%d: binder_alloc_buf failed to map page at %lx in userspace\n", | |
| 285 | + alloc->pid, user_page_addr); | |
| 286 | + goto err_vm_insert_page_failed; | |
| 287 | + } | |
| 288 | + | |
| 289 | + trace_binder_alloc_page_end(alloc, index); | |
| 290 | + /* vm_insert_page does not seem to increment the refcount */ | |
| 291 | + } | |
| 292 | + if (mm) { | |
| 293 | + up_write(&mm->mmap_sem); | |
| 294 | + mmput(mm); | |
| 295 | + } | |
| 296 | + return 0; | |
| 297 | + | |
| 298 | +free_range: | |
| 299 | + for (page_addr = end - PAGE_SIZE; page_addr >= start; | |
| 300 | + page_addr -= PAGE_SIZE) { | |
| 301 | + bool ret; | |
| 302 | + size_t index; | |
| 303 | + | |
| 304 | + index = (page_addr - alloc->buffer) / PAGE_SIZE; | |
| 305 | + page = &alloc->pages[index]; | |
| 306 | + | |
| 307 | + trace_binder_free_lru_start(alloc, index); | |
| 308 | + | |
| 309 | + ret = list_lru_add(&binder_alloc_lru, &page->lru); | |
| 310 | + WARN_ON(!ret); | |
| 311 | + | |
| 312 | + trace_binder_free_lru_end(alloc, index); | |
| 313 | + continue; | |
| 314 | + | |
| 315 | +err_vm_insert_page_failed: | |
| 316 | + unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE); | |
| 317 | +err_map_kernel_failed: | |
| 318 | + __free_page(page->page_ptr); | |
| 319 | + page->page_ptr = NULL; | |
| 320 | +err_alloc_page_failed: | |
| 321 | +err_page_ptr_cleared: | |
| 322 | + ; | |
| 323 | + } | |
| 324 | +err_no_vma: | |
| 325 | + if (mm) { | |
| 326 | + up_write(&mm->mmap_sem); | |
| 327 | + mmput(mm); | |
| 328 | + } | |
| 329 | + return vma ? -ENOMEM : -ESRCH; | |
| 330 | +} | |
| 331 | + | |
| 332 | +struct binder_buffer *binder_alloc_new_buf_locked(struct binder_alloc *alloc, | |
| 333 | + size_t data_size, | |
| 334 | + size_t offsets_size, | |
| 335 | + size_t extra_buffers_size, | |
| 336 | + int is_async) | |
| 337 | +{ | |
| 338 | + struct rb_node *n = alloc->free_buffers.rb_node; | |
| 339 | + struct binder_buffer *buffer; | |
| 340 | + size_t buffer_size; | |
| 341 | + struct rb_node *best_fit = NULL; | |
| 342 | + void *has_page_addr; | |
| 343 | + void *end_page_addr; | |
| 344 | + size_t size, data_offsets_size; | |
| 345 | + int ret; | |
| 346 | + | |
| 347 | + if (alloc->vma == NULL) { | |
| 348 | + pr_err("%d: binder_alloc_buf, no vma\n", | |
| 349 | + alloc->pid); | |
| 350 | + return ERR_PTR(-ESRCH); | |
| 351 | + } | |
| 352 | + | |
| 353 | + data_offsets_size = ALIGN(data_size, sizeof(void *)) + | |
| 354 | + ALIGN(offsets_size, sizeof(void *)); | |
| 355 | + | |
| 356 | + if (data_offsets_size < data_size || data_offsets_size < offsets_size) { | |
| 357 | + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, | |
| 358 | + "%d: got transaction with invalid size %zd-%zd\n", | |
| 359 | + alloc->pid, data_size, offsets_size); | |
| 360 | + return ERR_PTR(-EINVAL); | |
| 361 | + } | |
| 362 | + size = data_offsets_size + ALIGN(extra_buffers_size, sizeof(void *)); | |
| 363 | + if (size < data_offsets_size || size < extra_buffers_size) { | |
| 364 | + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, | |
| 365 | + "%d: got transaction with invalid extra_buffers_size %zd\n", | |
| 366 | + alloc->pid, extra_buffers_size); | |
| 367 | + return ERR_PTR(-EINVAL); | |
| 368 | + } | |
| 369 | + if (is_async && | |
| 370 | + alloc->free_async_space < size + sizeof(struct binder_buffer)) { | |
| 371 | + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, | |
| 372 | + "%d: binder_alloc_buf size %zd failed, no async space left\n", | |
| 373 | + alloc->pid, size); | |
| 374 | + return ERR_PTR(-ENOSPC); | |
| 375 | + } | |
| 376 | + | |
| 377 | + /* Pad 0-size buffers so they get assigned unique addresses */ | |
| 378 | + size = max(size, sizeof(void *)); | |
| 379 | + | |
| 380 | + while (n) { | |
| 381 | + buffer = rb_entry(n, struct binder_buffer, rb_node); | |
| 382 | + BUG_ON(!buffer->free); | |
| 383 | + buffer_size = binder_alloc_buffer_size(alloc, buffer); | |
| 384 | + | |
| 385 | + if (size < buffer_size) { | |
| 386 | + best_fit = n; | |
| 387 | + n = n->rb_left; | |
| 388 | + } else if (size > buffer_size) | |
| 389 | + n = n->rb_right; | |
| 390 | + else { | |
| 391 | + best_fit = n; | |
| 392 | + break; | |
| 393 | + } | |
| 394 | + } | |
| 395 | + if (best_fit == NULL) { | |
| 396 | + size_t allocated_buffers = 0; | |
| 397 | + size_t largest_alloc_size = 0; | |
| 398 | + size_t total_alloc_size = 0; | |
| 399 | + size_t free_buffers = 0; | |
| 400 | + size_t largest_free_size = 0; | |
| 401 | + size_t total_free_size = 0; | |
| 402 | + | |
| 403 | + for (n = rb_first(&alloc->allocated_buffers); n != NULL; | |
| 404 | + n = rb_next(n)) { | |
| 405 | + buffer = rb_entry(n, struct binder_buffer, rb_node); | |
| 406 | + buffer_size = binder_alloc_buffer_size(alloc, buffer); | |
| 407 | + allocated_buffers++; | |
| 408 | + total_alloc_size += buffer_size; | |
| 409 | + if (buffer_size > largest_alloc_size) | |
| 410 | + largest_alloc_size = buffer_size; | |
| 411 | + } | |
| 412 | + for (n = rb_first(&alloc->free_buffers); n != NULL; | |
| 413 | + n = rb_next(n)) { | |
| 414 | + buffer = rb_entry(n, struct binder_buffer, rb_node); | |
| 415 | + buffer_size = binder_alloc_buffer_size(alloc, buffer); | |
| 416 | + free_buffers++; | |
| 417 | + total_free_size += buffer_size; | |
| 418 | + if (buffer_size > largest_free_size) | |
| 419 | + largest_free_size = buffer_size; | |
| 420 | + } | |
| 421 | + pr_err("%d: binder_alloc_buf size %zd failed, no address space\n", | |
| 422 | + alloc->pid, size); | |
| 423 | + pr_err("allocated: %zd (num: %zd largest: %zd), free: %zd (num: %zd largest: %zd)\n", | |
| 424 | + total_alloc_size, allocated_buffers, largest_alloc_size, | |
| 425 | + total_free_size, free_buffers, largest_free_size); | |
| 426 | + return ERR_PTR(-ENOSPC); | |
| 427 | + } | |
| 428 | + if (n == NULL) { | |
| 429 | + buffer = rb_entry(best_fit, struct binder_buffer, rb_node); | |
| 430 | + buffer_size = binder_alloc_buffer_size(alloc, buffer); | |
| 431 | + } | |
| 432 | + | |
| 433 | + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, | |
| 434 | + "%d: binder_alloc_buf size %zd got buffer %pK size %zd\n", | |
| 435 | + alloc->pid, size, buffer, buffer_size); | |
| 436 | + | |
| 437 | + has_page_addr = | |
| 438 | + (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK); | |
| 439 | + WARN_ON(n && buffer_size != size); | |
| 440 | + end_page_addr = | |
| 441 | + (void *)PAGE_ALIGN((uintptr_t)buffer->data + size); | |
| 442 | + if (end_page_addr > has_page_addr) | |
| 443 | + end_page_addr = has_page_addr; | |
| 444 | + ret = binder_update_page_range(alloc, 1, | |
| 445 | + (void *)PAGE_ALIGN((uintptr_t)buffer->data), end_page_addr, NULL); | |
| 446 | + if (ret) | |
| 447 | + return ERR_PTR(ret); | |
| 448 | + | |
| 449 | + if (buffer_size != size) { | |
| 450 | + struct binder_buffer *new_buffer; | |
| 451 | + | |
| 452 | + new_buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); | |
| 453 | + if (!new_buffer) { | |
| 454 | + pr_err("%s: %d failed to alloc new buffer struct\n", | |
| 455 | + __func__, alloc->pid); | |
| 456 | + goto err_alloc_buf_struct_failed; | |
| 457 | + } | |
| 458 | + new_buffer->data = (u8 *)buffer->data + size; | |
| 459 | + list_add(&new_buffer->entry, &buffer->entry); | |
| 460 | + new_buffer->free = 1; | |
| 461 | + binder_insert_free_buffer(alloc, new_buffer); | |
| 462 | + } | |
| 463 | + | |
| 464 | + rb_erase(best_fit, &alloc->free_buffers); | |
| 465 | + buffer->free = 0; | |
| 466 | + buffer->free_in_progress = 0; | |
| 467 | + binder_insert_allocated_buffer_locked(alloc, buffer); | |
| 468 | + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, | |
| 469 | + "%d: binder_alloc_buf size %zd got %pK\n", | |
| 470 | + alloc->pid, size, buffer); | |
| 471 | + buffer->data_size = data_size; | |
| 472 | + buffer->offsets_size = offsets_size; | |
| 473 | + buffer->async_transaction = is_async; | |
| 474 | + buffer->extra_buffers_size = extra_buffers_size; | |
| 475 | + if (is_async) { | |
| 476 | + alloc->free_async_space -= size + sizeof(struct binder_buffer); | |
| 477 | + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC, | |
| 478 | + "%d: binder_alloc_buf size %zd async free %zd\n", | |
| 479 | + alloc->pid, size, alloc->free_async_space); | |
| 480 | + } | |
| 481 | + return buffer; | |
| 482 | + | |
| 483 | +err_alloc_buf_struct_failed: | |
| 484 | + binder_update_page_range(alloc, 0, | |
| 485 | + (void *)PAGE_ALIGN((uintptr_t)buffer->data), | |
| 486 | + end_page_addr, NULL); | |
| 487 | + return ERR_PTR(-ENOMEM); | |
| 488 | +} | |
| 489 | + | |
| 490 | +/** | |
| 491 | + * binder_alloc_new_buf() - Allocate a new binder buffer | |
| 492 | + * @alloc: binder_alloc for this proc | |
| 493 | + * @data_size: size of user data buffer | |
| 494 | + * @offsets_size: user specified buffer offset | |
| 495 | + * @extra_buffers_size: size of extra space for meta-data (eg, security context) | |
| 496 | + * @is_async: buffer for async transaction | |
| 497 | + * | |
| 498 | + * Allocate a new buffer given the requested sizes. Returns | |
| 499 | + * the kernel version of the buffer pointer. The size allocated | |
| 500 | + * is the sum of the three given sizes (each rounded up to | |
| 501 | + * pointer-sized boundary) | |
| 502 | + * | |
| 503 | + * Return: The allocated buffer or %NULL if error | |
| 504 | + */ | |
| 505 | +struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc, | |
| 506 | + size_t data_size, | |
| 507 | + size_t offsets_size, | |
| 508 | + size_t extra_buffers_size, | |
| 509 | + int is_async) | |
| 510 | +{ | |
| 511 | + struct binder_buffer *buffer; | |
| 512 | + | |
| 513 | + mutex_lock(&alloc->mutex); | |
| 514 | + buffer = binder_alloc_new_buf_locked(alloc, data_size, offsets_size, | |
| 515 | + extra_buffers_size, is_async); | |
| 516 | + mutex_unlock(&alloc->mutex); | |
| 517 | + return buffer; | |
| 518 | +} | |
| 519 | + | |
| 520 | +static void *buffer_start_page(struct binder_buffer *buffer) | |
| 521 | +{ | |
| 522 | + return (void *)((uintptr_t)buffer->data & PAGE_MASK); | |
| 523 | +} | |
| 524 | + | |
| 525 | +static void *prev_buffer_end_page(struct binder_buffer *buffer) | |
| 526 | +{ | |
| 527 | + return (void *)(((uintptr_t)(buffer->data) - 1) & PAGE_MASK); | |
| 528 | +} | |
| 529 | + | |
| 530 | +static void binder_delete_free_buffer(struct binder_alloc *alloc, | |
| 531 | + struct binder_buffer *buffer) | |
| 532 | +{ | |
| 533 | + struct binder_buffer *prev, *next = NULL; | |
| 534 | + bool to_free = true; | |
| 535 | + BUG_ON(alloc->buffers.next == &buffer->entry); | |
| 536 | + prev = binder_buffer_prev(buffer); | |
| 537 | + BUG_ON(!prev->free); | |
| 538 | + if (prev_buffer_end_page(prev) == buffer_start_page(buffer)) { | |
| 539 | + to_free = false; | |
| 540 | + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, | |
| 541 | + "%d: merge free, buffer %pK share page with %pK\n", | |
| 542 | + alloc->pid, buffer->data, prev->data); | |
| 543 | + } | |
| 544 | + | |
| 545 | + if (!list_is_last(&buffer->entry, &alloc->buffers)) { | |
| 546 | + next = binder_buffer_next(buffer); | |
| 547 | + if (buffer_start_page(next) == buffer_start_page(buffer)) { | |
| 548 | + to_free = false; | |
| 549 | + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, | |
| 550 | + "%d: merge free, buffer %pK share page with %pK\n", | |
| 551 | + alloc->pid, | |
| 552 | + buffer->data, | |
| 553 | + next->data); | |
| 554 | + } | |
| 555 | + } | |
| 556 | + | |
| 557 | + if (PAGE_ALIGNED(buffer->data)) { | |
| 558 | + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, | |
| 559 | + "%d: merge free, buffer start %pK is page aligned\n", | |
| 560 | + alloc->pid, buffer->data); | |
| 561 | + to_free = false; | |
| 562 | + } | |
| 563 | + | |
| 564 | + if (to_free) { | |
| 565 | + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, | |
| 566 | + "%d: merge free, buffer %pK do not share page with %pK or %pK\n", | |
| 567 | + alloc->pid, buffer->data, | |
| 568 | + prev->data, next->data); | |
| 569 | + binder_update_page_range(alloc, 0, buffer_start_page(buffer), | |
| 570 | + buffer_start_page(buffer) + PAGE_SIZE, | |
| 571 | + NULL); | |
| 572 | + } | |
| 573 | + list_del(&buffer->entry); | |
| 574 | + kfree(buffer); | |
| 575 | +} | |
| 576 | + | |
| 577 | +static void binder_free_buf_locked(struct binder_alloc *alloc, | |
| 578 | + struct binder_buffer *buffer) | |
| 579 | +{ | |
| 580 | + size_t size, buffer_size; | |
| 581 | + | |
| 582 | + buffer_size = binder_alloc_buffer_size(alloc, buffer); | |
| 583 | + | |
| 584 | + size = ALIGN(buffer->data_size, sizeof(void *)) + | |
| 585 | + ALIGN(buffer->offsets_size, sizeof(void *)) + | |
| 586 | + ALIGN(buffer->extra_buffers_size, sizeof(void *)); | |
| 587 | + | |
| 588 | + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, | |
| 589 | + "%d: binder_free_buf %pK size %zd buffer_size %zd\n", | |
| 590 | + alloc->pid, buffer, size, buffer_size); | |
| 591 | + | |
| 592 | + BUG_ON(buffer->free); | |
| 593 | + BUG_ON(size > buffer_size); | |
| 594 | + BUG_ON(buffer->transaction != NULL); | |
| 595 | + BUG_ON(buffer->data < alloc->buffer); | |
| 596 | + BUG_ON(buffer->data > alloc->buffer + alloc->buffer_size); | |
| 597 | + | |
| 598 | + if (buffer->async_transaction) { | |
| 599 | + alloc->free_async_space += size + sizeof(struct binder_buffer); | |
| 600 | + | |
| 601 | + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC, | |
| 602 | + "%d: binder_free_buf size %zd async free %zd\n", | |
| 603 | + alloc->pid, size, alloc->free_async_space); | |
| 604 | + } | |
| 605 | + | |
| 606 | + binder_update_page_range(alloc, 0, | |
| 607 | + (void *)PAGE_ALIGN((uintptr_t)buffer->data), | |
| 608 | + (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK), | |
| 609 | + NULL); | |
| 610 | + | |
| 611 | + rb_erase(&buffer->rb_node, &alloc->allocated_buffers); | |
| 612 | + buffer->free = 1; | |
| 613 | + if (!list_is_last(&buffer->entry, &alloc->buffers)) { | |
| 614 | + struct binder_buffer *next = binder_buffer_next(buffer); | |
| 615 | + | |
| 616 | + if (next->free) { | |
| 617 | + rb_erase(&next->rb_node, &alloc->free_buffers); | |
| 618 | + binder_delete_free_buffer(alloc, next); | |
| 619 | + } | |
| 620 | + } | |
| 621 | + if (alloc->buffers.next != &buffer->entry) { | |
| 622 | + struct binder_buffer *prev = binder_buffer_prev(buffer); | |
| 623 | + | |
| 624 | + if (prev->free) { | |
| 625 | + binder_delete_free_buffer(alloc, buffer); | |
| 626 | + rb_erase(&prev->rb_node, &alloc->free_buffers); | |
| 627 | + buffer = prev; | |
| 628 | + } | |
| 629 | + } | |
| 630 | + binder_insert_free_buffer(alloc, buffer); | |
| 631 | +} | |
| 632 | + | |
| 633 | +/** | |
| 634 | + * binder_alloc_free_buf() - free a binder buffer | |
| 635 | + * @alloc: binder_alloc for this proc | |
| 636 | + * @buffer: kernel pointer to buffer | |
| 637 | + * | |
| 638 | + * Free the buffer allocated via binder_alloc_new_buffer() | |
| 639 | + */ | |
| 640 | +void binder_alloc_free_buf(struct binder_alloc *alloc, | |
| 641 | + struct binder_buffer *buffer) | |
| 642 | +{ | |
| 643 | + mutex_lock(&alloc->mutex); | |
| 644 | + binder_free_buf_locked(alloc, buffer); | |
| 645 | + mutex_unlock(&alloc->mutex); | |
| 646 | +} | |
| 647 | + | |
| 648 | +/** | |
| 649 | + * binder_alloc_mmap_handler() - map virtual address space for proc | |
| 650 | + * @alloc: alloc structure for this proc | |
| 651 | + * @vma: vma passed to mmap() | |
| 652 | + * | |
| 653 | + * Called by binder_mmap() to initialize the space specified in | |
| 654 | + * vma for allocating binder buffers | |
| 655 | + * | |
| 656 | + * Return: | |
| 657 | + * 0 = success | |
| 658 | + * -EBUSY = address space already mapped | |
| 659 | + * -ENOMEM = failed to map memory to given address space | |
| 660 | + */ | |
| 661 | +int binder_alloc_mmap_handler(struct binder_alloc *alloc, | |
| 662 | + struct vm_area_struct *vma) | |
| 663 | +{ | |
| 664 | + int ret; | |
| 665 | + struct vm_struct *area; | |
| 666 | + const char *failure_string; | |
| 667 | + struct binder_buffer *buffer; | |
| 668 | + | |
| 669 | + mutex_lock(&binder_alloc_mmap_lock); | |
| 670 | + if (alloc->buffer) { | |
| 671 | + ret = -EBUSY; | |
| 672 | + failure_string = "already mapped"; | |
| 673 | + goto err_already_mapped; | |
| 674 | + } | |
| 675 | + | |
| 676 | + area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP); | |
| 677 | + if (area == NULL) { | |
| 678 | + ret = -ENOMEM; | |
| 679 | + failure_string = "get_vm_area"; | |
| 680 | + goto err_get_vm_area_failed; | |
| 681 | + } | |
| 682 | + alloc->buffer = area->addr; | |
| 683 | + alloc->user_buffer_offset = | |
| 684 | + vma->vm_start - (uintptr_t)alloc->buffer; | |
| 685 | + mutex_unlock(&binder_alloc_mmap_lock); | |
| 686 | + | |
| 687 | +#ifdef CONFIG_CPU_CACHE_VIPT | |
| 688 | + if (cache_is_vipt_aliasing()) { | |
| 689 | + while (CACHE_COLOUR( | |
| 690 | + (vma->vm_start ^ (uint32_t)alloc->buffer))) { | |
| 691 | + pr_info("%s: %d %lx-%lx maps %pK bad alignment\n", | |
| 692 | + __func__, alloc->pid, vma->vm_start, | |
| 693 | + vma->vm_end, alloc->buffer); | |
| 694 | + vma->vm_start += PAGE_SIZE; | |
| 695 | + } | |
| 696 | + } | |
| 697 | +#endif | |
| 698 | + alloc->pages = kzalloc(sizeof(alloc->pages[0]) * | |
| 699 | + ((vma->vm_end - vma->vm_start) / PAGE_SIZE), | |
| 700 | + GFP_KERNEL); | |
| 701 | + if (alloc->pages == NULL) { | |
| 702 | + ret = -ENOMEM; | |
| 703 | + failure_string = "alloc page array"; | |
| 704 | + goto err_alloc_pages_failed; | |
| 705 | + } | |
| 706 | + alloc->buffer_size = vma->vm_end - vma->vm_start; | |
| 707 | + | |
| 708 | + buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); | |
| 709 | + if (!buffer) { | |
| 710 | + ret = -ENOMEM; | |
| 711 | + failure_string = "alloc buffer struct"; | |
| 712 | + goto err_alloc_buf_struct_failed; | |
| 713 | + } | |
| 714 | + | |
| 715 | + buffer->data = alloc->buffer; | |
| 716 | + list_add(&buffer->entry, &alloc->buffers); | |
| 717 | + buffer->free = 1; | |
| 718 | + binder_insert_free_buffer(alloc, buffer); | |
| 719 | + alloc->free_async_space = alloc->buffer_size / 2; | |
| 720 | + barrier(); | |
| 721 | + alloc->vma = vma; | |
| 722 | + alloc->vma_vm_mm = vma->vm_mm; | |
| 723 | + | |
| 724 | + return 0; | |
| 725 | + | |
| 726 | +err_alloc_buf_struct_failed: | |
| 727 | + kfree(alloc->pages); | |
| 728 | + alloc->pages = NULL; | |
| 729 | +err_alloc_pages_failed: | |
| 730 | + mutex_lock(&binder_alloc_mmap_lock); | |
| 731 | + vfree(alloc->buffer); | |
| 732 | + alloc->buffer = NULL; | |
| 733 | +err_get_vm_area_failed: | |
| 734 | +err_already_mapped: | |
| 735 | + mutex_unlock(&binder_alloc_mmap_lock); | |
| 736 | + pr_err("%s: %d %lx-%lx %s failed %d\n", __func__, | |
| 737 | + alloc->pid, vma->vm_start, vma->vm_end, failure_string, ret); | |
| 738 | + return ret; | |
| 739 | +} | |
| 740 | + | |
| 741 | + | |
| 742 | +void binder_alloc_deferred_release(struct binder_alloc *alloc) | |
| 743 | +{ | |
| 744 | + struct rb_node *n; | |
| 745 | + int buffers, page_count; | |
| 746 | + struct binder_buffer *buffer; | |
| 747 | + | |
| 748 | + BUG_ON(alloc->vma); | |
| 749 | + | |
| 750 | + buffers = 0; | |
| 751 | + mutex_lock(&alloc->mutex); | |
| 752 | + while ((n = rb_first(&alloc->allocated_buffers))) { | |
| 753 | + buffer = rb_entry(n, struct binder_buffer, rb_node); | |
| 754 | + | |
| 755 | + /* Transaction should already have been freed */ | |
| 756 | + BUG_ON(buffer->transaction); | |
| 757 | + | |
| 758 | + binder_free_buf_locked(alloc, buffer); | |
| 759 | + buffers++; | |
| 760 | + } | |
| 761 | + | |
| 762 | + while (!list_empty(&alloc->buffers)) { | |
| 763 | + buffer = list_first_entry(&alloc->buffers, | |
| 764 | + struct binder_buffer, entry); | |
| 765 | + WARN_ON(!buffer->free); | |
| 766 | + | |
| 767 | + list_del(&buffer->entry); | |
| 768 | + WARN_ON_ONCE(!list_empty(&alloc->buffers)); | |
| 769 | + kfree(buffer); | |
| 770 | + } | |
| 771 | + | |
| 772 | + page_count = 0; | |
| 773 | + if (alloc->pages) { | |
| 774 | + int i; | |
| 775 | + | |
| 776 | + for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) { | |
| 777 | + void *page_addr; | |
| 778 | + bool on_lru; | |
| 779 | + | |
| 780 | + if (!alloc->pages[i].page_ptr) | |
| 781 | + continue; | |
| 782 | + | |
| 783 | + on_lru = list_lru_del(&binder_alloc_lru, | |
| 784 | + &alloc->pages[i].lru); | |
| 785 | + page_addr = alloc->buffer + i * PAGE_SIZE; | |
| 786 | + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, | |
| 787 | + "%s: %d: page %d at %pK %s\n", | |
| 788 | + __func__, alloc->pid, i, page_addr, | |
| 789 | + on_lru ? "on lru" : "active"); | |
| 790 | + unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE); | |
| 791 | + __free_page(alloc->pages[i].page_ptr); | |
| 792 | + page_count++; | |
| 793 | + } | |
| 794 | + kfree(alloc->pages); | |
| 795 | + vfree(alloc->buffer); | |
| 796 | + } | |
| 797 | + mutex_unlock(&alloc->mutex); | |
| 798 | + | |
| 799 | + binder_alloc_debug(BINDER_DEBUG_OPEN_CLOSE, | |
| 800 | + "%s: %d buffers %d, pages %d\n", | |
| 801 | + __func__, alloc->pid, buffers, page_count); | |
| 802 | +} | |
| 803 | + | |
| 804 | +static void print_binder_buffer(struct seq_file *m, const char *prefix, | |
| 805 | + struct binder_buffer *buffer) | |
| 806 | +{ | |
| 807 | + seq_printf(m, "%s %d: %pK size %zd:%zd:%zd %s\n", | |
| 808 | + prefix, buffer->debug_id, buffer->data, | |
| 809 | + buffer->data_size, buffer->offsets_size, | |
| 810 | + buffer->extra_buffers_size, | |
| 811 | + buffer->transaction ? "active" : "delivered"); | |
| 812 | +} | |
| 813 | + | |
| 814 | +/** | |
| 815 | + * binder_alloc_print_allocated() - print buffer info | |
| 816 | + * @m: seq_file for output via seq_printf() | |
| 817 | + * @alloc: binder_alloc for this proc | |
| 818 | + * | |
| 819 | + * Prints information about every buffer associated with | |
| 820 | + * the binder_alloc state to the given seq_file | |
| 821 | + */ | |
| 822 | +void binder_alloc_print_allocated(struct seq_file *m, | |
| 823 | + struct binder_alloc *alloc) | |
| 824 | +{ | |
| 825 | + struct rb_node *n; | |
| 826 | + | |
| 827 | + mutex_lock(&alloc->mutex); | |
| 828 | + for (n = rb_first(&alloc->allocated_buffers); n != NULL; n = rb_next(n)) | |
| 829 | + print_binder_buffer(m, " buffer", | |
| 830 | + rb_entry(n, struct binder_buffer, rb_node)); | |
| 831 | + mutex_unlock(&alloc->mutex); | |
| 832 | +} | |
| 833 | + | |
| 834 | +/** | |
| 835 | + * binder_alloc_print_pages() - print page usage | |
| 836 | + * @m: seq_file for output via seq_printf() | |
| 837 | + * @alloc: binder_alloc for this proc | |
| 838 | + */ | |
| 839 | +void binder_alloc_print_pages(struct seq_file *m, | |
| 840 | + struct binder_alloc *alloc) | |
| 841 | +{ | |
| 842 | + struct binder_lru_page *page; | |
| 843 | + int i; | |
| 844 | + int active = 0; | |
| 845 | + int lru = 0; | |
| 846 | + int free = 0; | |
| 847 | + | |
| 848 | + mutex_lock(&alloc->mutex); | |
| 849 | + for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) { | |
| 850 | + page = &alloc->pages[i]; | |
| 851 | + if (!page->page_ptr) | |
| 852 | + free++; | |
| 853 | + else if (list_empty(&page->lru)) | |
| 854 | + active++; | |
| 855 | + else | |
| 856 | + lru++; | |
| 857 | + } | |
| 858 | + mutex_unlock(&alloc->mutex); | |
| 859 | + seq_printf(m, " pages: %d:%d:%d\n", active, lru, free); | |
| 860 | +} | |
| 861 | + | |
| 862 | +/** | |
| 863 | + * binder_alloc_get_allocated_count() - return count of buffers | |
| 864 | + * @alloc: binder_alloc for this proc | |
| 865 | + * | |
| 866 | + * Return: count of allocated buffers | |
| 867 | + */ | |
| 868 | +int binder_alloc_get_allocated_count(struct binder_alloc *alloc) | |
| 869 | +{ | |
| 870 | + struct rb_node *n; | |
| 871 | + int count = 0; | |
| 872 | + | |
| 873 | + mutex_lock(&alloc->mutex); | |
| 874 | + for (n = rb_first(&alloc->allocated_buffers); n != NULL; n = rb_next(n)) | |
| 875 | + count++; | |
| 876 | + mutex_unlock(&alloc->mutex); | |
| 877 | + return count; | |
| 878 | +} | |
| 879 | + | |
| 880 | + | |
| 881 | +/** | |
| 882 | + * binder_alloc_vma_close() - invalidate address space | |
| 883 | + * @alloc: binder_alloc for this proc | |
| 884 | + * | |
| 885 | + * Called from binder_vma_close() when releasing address space. | |
| 886 | + * Clears alloc->vma to prevent new incoming transactions from | |
| 887 | + * allocating more buffers. | |
| 888 | + */ | |
| 889 | +void binder_alloc_vma_close(struct binder_alloc *alloc) | |
| 890 | +{ | |
| 891 | + WRITE_ONCE(alloc->vma, NULL); | |
| 892 | + WRITE_ONCE(alloc->vma_vm_mm, NULL); | |
| 893 | +} | |
| 894 | + | |
| 895 | +/** | |
| 896 | + * binder_alloc_free_page() - shrinker callback to free pages | |
| 897 | + * @item: item to free | |
| 898 | + * @lock: lock protecting the item | |
| 899 | + * @cb_arg: callback argument | |
| 900 | + * | |
| 901 | + * Called from list_lru_walk() in binder_shrink_scan() to free | |
| 902 | + * up pages when the system is under memory pressure. | |
| 903 | + */ | |
| 904 | +enum lru_status binder_alloc_free_page(struct list_head *item, | |
| 905 | + struct list_lru_one *lru, | |
| 906 | + spinlock_t *lock, | |
| 907 | + void *cb_arg) | |
| 908 | +{ | |
| 909 | + struct mm_struct *mm = NULL; | |
| 910 | + struct binder_lru_page *page = container_of(item, | |
| 911 | + struct binder_lru_page, | |
| 912 | + lru); | |
| 913 | + struct binder_alloc *alloc; | |
| 914 | + uintptr_t page_addr; | |
| 915 | + size_t index; | |
| 916 | + | |
| 917 | + alloc = page->alloc; | |
| 918 | + if (!mutex_trylock(&alloc->mutex)) | |
| 919 | + goto err_get_alloc_mutex_failed; | |
| 920 | + | |
| 921 | + if (!page->page_ptr) | |
| 922 | + goto err_page_already_freed; | |
| 923 | + | |
| 924 | + index = page - alloc->pages; | |
| 925 | + page_addr = (uintptr_t)alloc->buffer + index * PAGE_SIZE; | |
| 926 | + if (alloc->vma) { | |
| 927 | + mm = get_task_mm(alloc->tsk); | |
| 928 | + if (!mm) | |
| 929 | + goto err_get_task_mm_failed; | |
| 930 | + if (!down_write_trylock(&mm->mmap_sem)) | |
| 931 | + goto err_down_write_mmap_sem_failed; | |
| 932 | + | |
| 933 | + trace_binder_unmap_user_start(alloc, index); | |
| 934 | + | |
| 935 | + zap_page_range(alloc->vma, | |
| 936 | + page_addr + alloc->user_buffer_offset, | |
| 937 | + PAGE_SIZE); | |
| 938 | + | |
| 939 | + trace_binder_unmap_user_end(alloc, index); | |
| 940 | + | |
| 941 | + up_write(&mm->mmap_sem); | |
| 942 | + mmput(mm); | |
| 943 | + } | |
| 944 | + | |
| 945 | + trace_binder_unmap_kernel_start(alloc, index); | |
| 946 | + | |
| 947 | + unmap_kernel_range(page_addr, PAGE_SIZE); | |
| 948 | + __free_page(page->page_ptr); | |
| 949 | + page->page_ptr = NULL; | |
| 950 | + | |
| 951 | + trace_binder_unmap_kernel_end(alloc, index); | |
| 952 | + | |
| 953 | + list_lru_isolate(lru, item); | |
| 954 | + | |
| 955 | + mutex_unlock(&alloc->mutex); | |
| 956 | + return LRU_REMOVED; | |
| 957 | + | |
| 958 | +err_down_write_mmap_sem_failed: | |
| 959 | + mmput(mm); | |
| 960 | +err_get_task_mm_failed: | |
| 961 | +err_page_already_freed: | |
| 962 | + mutex_unlock(&alloc->mutex); | |
| 963 | +err_get_alloc_mutex_failed: | |
| 964 | + return LRU_SKIP; | |
| 965 | +} | |
| 966 | + | |
| 967 | +static unsigned long | |
| 968 | +binder_shrink_count(struct shrinker *shrink, struct shrink_control *sc) | |
| 969 | +{ | |
| 970 | + unsigned long ret = list_lru_count(&binder_alloc_lru); | |
| 971 | + return ret; | |
| 972 | +} | |
| 973 | + | |
| 974 | +static unsigned long | |
| 975 | +binder_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) | |
| 976 | +{ | |
| 977 | + unsigned long ret; | |
| 978 | + | |
| 979 | + ret = list_lru_walk(&binder_alloc_lru, binder_alloc_free_page, | |
| 980 | + NULL, sc->nr_to_scan); | |
| 981 | + return ret; | |
| 982 | +} | |
| 983 | + | |
| 984 | +struct shrinker binder_shrinker = { | |
| 985 | + .count_objects = binder_shrink_count, | |
| 986 | + .scan_objects = binder_shrink_scan, | |
| 987 | + .seeks = DEFAULT_SEEKS, | |
| 988 | +}; | |
| 989 | + | |
| 990 | +/** | |
| 991 | + * binder_alloc_init() - called by binder_open() for per-proc initialization | |
| 992 | + * @alloc: binder_alloc for this proc | |
| 993 | + * | |
| 994 | + * Called from binder_open() to initialize binder_alloc fields for | |
| 995 | + * new binder proc | |
| 996 | + */ | |
| 997 | +void binder_alloc_init(struct binder_alloc *alloc) | |
| 998 | +{ | |
| 999 | + alloc->tsk = current->group_leader; | |
| 1000 | + alloc->pid = current->group_leader->pid; | |
| 1001 | + mutex_init(&alloc->mutex); | |
| 1002 | + INIT_LIST_HEAD(&alloc->buffers); | |
| 1003 | +} | |
| 1004 | + | |
| 1005 | +void binder_alloc_shrinker_init(void) | |
| 1006 | +{ | |
| 1007 | + list_lru_init(&binder_alloc_lru); | |
| 1008 | + register_shrinker(&binder_shrinker); | |
| 1009 | +} | |
| @@ -0,0 +1,187 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (C) 2017 Google, Inc. | |
| 3 | + * | |
| 4 | + * This software is licensed under the terms of the GNU General Public | |
| 5 | + * License version 2, as published by the Free Software Foundation, and | |
| 6 | + * may be copied, distributed, and modified under those terms. | |
| 7 | + * | |
| 8 | + * This program is distributed in the hope that it will be useful, | |
| 9 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 10 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 11 | + * GNU General Public License for more details. | |
| 12 | + * | |
| 13 | + */ | |
| 14 | + | |
| 15 | +#ifndef _LINUX_BINDER_ALLOC_H | |
| 16 | +#define _LINUX_BINDER_ALLOC_H | |
| 17 | + | |
| 18 | +#include <linux/rbtree.h> | |
| 19 | +#include <linux/list.h> | |
| 20 | +#include <linux/mm.h> | |
| 21 | +#include <linux/rtmutex.h> | |
| 22 | +#include <linux/vmalloc.h> | |
| 23 | +#include <linux/slab.h> | |
| 24 | +#include <linux/list_lru.h> | |
| 25 | + | |
| 26 | +extern struct list_lru binder_alloc_lru; | |
| 27 | +struct binder_transaction; | |
| 28 | + | |
| 29 | +/** | |
| 30 | + * struct binder_buffer - buffer used for binder transactions | |
| 31 | + * @entry: entry alloc->buffers | |
| 32 | + * @rb_node: node for allocated_buffers/free_buffers rb trees | |
| 33 | + * @free: true if buffer is free | |
| 34 | + * @allow_user_free: describe the second member of struct blah, | |
| 35 | + * @async_transaction: describe the second member of struct blah, | |
| 36 | + * @debug_id: describe the second member of struct blah, | |
| 37 | + * @transaction: describe the second member of struct blah, | |
| 38 | + * @target_node: describe the second member of struct blah, | |
| 39 | + * @data_size: describe the second member of struct blah, | |
| 40 | + * @offsets_size: describe the second member of struct blah, | |
| 41 | + * @extra_buffers_size: describe the second member of struct blah, | |
| 42 | + * @data:i describe the second member of struct blah, | |
| 43 | + * | |
| 44 | + * Bookkeeping structure for binder transaction buffers | |
| 45 | + */ | |
| 46 | +struct binder_buffer { | |
| 47 | + struct list_head entry; /* free and allocated entries by address */ | |
| 48 | + struct rb_node rb_node; /* free entry by size or allocated entry */ | |
| 49 | + /* by address */ | |
| 50 | + unsigned free:1; | |
| 51 | + unsigned allow_user_free:1; | |
| 52 | + unsigned async_transaction:1; | |
| 53 | + unsigned free_in_progress:1; | |
| 54 | + unsigned debug_id:28; | |
| 55 | + | |
| 56 | + struct binder_transaction *transaction; | |
| 57 | + | |
| 58 | + struct binder_node *target_node; | |
| 59 | + size_t data_size; | |
| 60 | + size_t offsets_size; | |
| 61 | + size_t extra_buffers_size; | |
| 62 | + void *data; | |
| 63 | +}; | |
| 64 | + | |
| 65 | +/** | |
| 66 | + * struct binder_lru_page - page object used for binder shrinker | |
| 67 | + * @page_ptr: pointer to physical page in mmap'd space | |
| 68 | + * @lru: entry in binder_alloc_lru | |
| 69 | + * @alloc: binder_alloc for a proc | |
| 70 | + */ | |
| 71 | +struct binder_lru_page { | |
| 72 | + struct list_head lru; | |
| 73 | + struct page *page_ptr; | |
| 74 | + struct binder_alloc *alloc; | |
| 75 | +}; | |
| 76 | + | |
| 77 | +/** | |
| 78 | + * struct binder_alloc - per-binder proc state for binder allocator | |
| 79 | + * @vma: vm_area_struct passed to mmap_handler | |
| 80 | + * (invarient after mmap) | |
| 81 | + * @tsk: tid for task that called init for this proc | |
| 82 | + * (invariant after init) | |
| 83 | + * @vma_vm_mm: copy of vma->vm_mm (invarient after mmap) | |
| 84 | + * @buffer: base of per-proc address space mapped via mmap | |
| 85 | + * @user_buffer_offset: offset between user and kernel VAs for buffer | |
| 86 | + * @buffers: list of all buffers for this proc | |
| 87 | + * @free_buffers: rb tree of buffers available for allocation | |
| 88 | + * sorted by size | |
| 89 | + * @allocated_buffers: rb tree of allocated buffers sorted by address | |
| 90 | + * @free_async_space: VA space available for async buffers. This is | |
| 91 | + * initialized at mmap time to 1/2 the full VA space | |
| 92 | + * @pages: array of binder_lru_page | |
| 93 | + * @buffer_size: size of address space specified via mmap | |
| 94 | + * @pid: pid for associated binder_proc (invariant after init) | |
| 95 | + * | |
| 96 | + * Bookkeeping structure for per-proc address space management for binder | |
| 97 | + * buffers. It is normally initialized during binder_init() and binder_mmap() | |
| 98 | + * calls. The address space is used for both user-visible buffers and for | |
| 99 | + * struct binder_buffer objects used to track the user buffers | |
| 100 | + */ | |
| 101 | +struct binder_alloc { | |
| 102 | + struct mutex mutex; | |
| 103 | + struct task_struct *tsk; | |
| 104 | + struct vm_area_struct *vma; | |
| 105 | + struct mm_struct *vma_vm_mm; | |
| 106 | + void *buffer; | |
| 107 | + ptrdiff_t user_buffer_offset; | |
| 108 | + struct list_head buffers; | |
| 109 | + struct rb_root free_buffers; | |
| 110 | + struct rb_root allocated_buffers; | |
| 111 | + size_t free_async_space; | |
| 112 | + struct binder_lru_page *pages; | |
| 113 | + size_t buffer_size; | |
| 114 | + uint32_t buffer_free; | |
| 115 | + int pid; | |
| 116 | +}; | |
| 117 | + | |
| 118 | +#ifdef CONFIG_ANDROID_BINDER_IPC_SELFTEST | |
| 119 | +void binder_selftest_alloc(struct binder_alloc *alloc); | |
| 120 | +#else | |
| 121 | +static inline void binder_selftest_alloc(struct binder_alloc *alloc) {} | |
| 122 | +#endif | |
| 123 | +enum lru_status binder_alloc_free_page(struct list_head *item, | |
| 124 | + struct list_lru_one *lru, | |
| 125 | + spinlock_t *lock, void *cb_arg); | |
| 126 | +extern struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc, | |
| 127 | + size_t data_size, | |
| 128 | + size_t offsets_size, | |
| 129 | + size_t extra_buffers_size, | |
| 130 | + int is_async); | |
| 131 | +extern void binder_alloc_init(struct binder_alloc *alloc); | |
| 132 | +void binder_alloc_shrinker_init(void); | |
| 133 | +extern void binder_alloc_vma_close(struct binder_alloc *alloc); | |
| 134 | +extern struct binder_buffer * | |
| 135 | +binder_alloc_prepare_to_free(struct binder_alloc *alloc, | |
| 136 | + uintptr_t user_ptr); | |
| 137 | +extern void binder_alloc_free_buf(struct binder_alloc *alloc, | |
| 138 | + struct binder_buffer *buffer); | |
| 139 | +extern int binder_alloc_mmap_handler(struct binder_alloc *alloc, | |
| 140 | + struct vm_area_struct *vma); | |
| 141 | +extern void binder_alloc_deferred_release(struct binder_alloc *alloc); | |
| 142 | +extern int binder_alloc_get_allocated_count(struct binder_alloc *alloc); | |
| 143 | +extern void binder_alloc_print_allocated(struct seq_file *m, | |
| 144 | + struct binder_alloc *alloc); | |
| 145 | +void binder_alloc_print_pages(struct seq_file *m, | |
| 146 | + struct binder_alloc *alloc); | |
| 147 | + | |
| 148 | +/** | |
| 149 | + * binder_alloc_get_free_async_space() - get free space available for async | |
| 150 | + * @alloc: binder_alloc for this proc | |
| 151 | + * | |
| 152 | + * Return: the bytes remaining in the address-space for async transactions | |
| 153 | + */ | |
| 154 | +static inline size_t | |
| 155 | +binder_alloc_get_free_async_space(struct binder_alloc *alloc) | |
| 156 | +{ | |
| 157 | + size_t free_async_space; | |
| 158 | + | |
| 159 | + mutex_lock(&alloc->mutex); | |
| 160 | + free_async_space = alloc->free_async_space; | |
| 161 | + mutex_unlock(&alloc->mutex); | |
| 162 | + return free_async_space; | |
| 163 | +} | |
| 164 | + | |
| 165 | +/** | |
| 166 | + * binder_alloc_get_user_buffer_offset() - get offset between kernel/user addrs | |
| 167 | + * @alloc: binder_alloc for this proc | |
| 168 | + * | |
| 169 | + * Return: the offset between kernel and user-space addresses to use for | |
| 170 | + * virtual address conversion | |
| 171 | + */ | |
| 172 | +static inline ptrdiff_t | |
| 173 | +binder_alloc_get_user_buffer_offset(struct binder_alloc *alloc) | |
| 174 | +{ | |
| 175 | + /* | |
| 176 | + * user_buffer_offset is constant if vma is set and | |
| 177 | + * undefined if vma is not set. It is possible to | |
| 178 | + * get here with !alloc->vma if the target process | |
| 179 | + * is dying while a transaction is being initiated. | |
| 180 | + * Returning the old value is ok in this case and | |
| 181 | + * the transaction will fail. | |
| 182 | + */ | |
| 183 | + return alloc->user_buffer_offset; | |
| 184 | +} | |
| 185 | + | |
| 186 | +#endif /* _LINUX_BINDER_ALLOC_H */ | |
| 187 | + | |
| @@ -0,0 +1,310 @@ | ||
| 1 | +/* binder_alloc_selftest.c | |
| 2 | + * | |
| 3 | + * Android IPC Subsystem | |
| 4 | + * | |
| 5 | + * Copyright (C) 2017 Google, Inc. | |
| 6 | + * | |
| 7 | + * This software is licensed under the terms of the GNU General Public | |
| 8 | + * License version 2, as published by the Free Software Foundation, and | |
| 9 | + * may be copied, distributed, and modified under those terms. | |
| 10 | + * | |
| 11 | + * This program is distributed in the hope that it will be useful, | |
| 12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 14 | + * GNU General Public License for more details. | |
| 15 | + * | |
| 16 | + */ | |
| 17 | + | |
| 18 | +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
| 19 | + | |
| 20 | +#include <linux/mm_types.h> | |
| 21 | +#include <linux/err.h> | |
| 22 | +#include "binder_alloc.h" | |
| 23 | + | |
| 24 | +#define BUFFER_NUM 5 | |
| 25 | +#define BUFFER_MIN_SIZE (PAGE_SIZE / 8) | |
| 26 | + | |
| 27 | +static bool binder_selftest_run = true; | |
| 28 | +static int binder_selftest_failures; | |
| 29 | +static DEFINE_MUTEX(binder_selftest_lock); | |
| 30 | + | |
| 31 | +/** | |
| 32 | + * enum buf_end_align_type - Page alignment of a buffer | |
| 33 | + * end with regard to the end of the previous buffer. | |
| 34 | + * | |
| 35 | + * In the pictures below, buf2 refers to the buffer we | |
| 36 | + * are aligning. buf1 refers to previous buffer by addr. | |
| 37 | + * Symbol [ means the start of a buffer, ] means the end | |
| 38 | + * of a buffer, and | means page boundaries. | |
| 39 | + */ | |
| 40 | +enum buf_end_align_type { | |
| 41 | + /** | |
| 42 | + * @SAME_PAGE_UNALIGNED: The end of this buffer is on | |
| 43 | + * the same page as the end of the previous buffer and | |
| 44 | + * is not page aligned. Examples: | |
| 45 | + * buf1 ][ buf2 ][ ... | |
| 46 | + * buf1 ]|[ buf2 ][ ... | |
| 47 | + */ | |
| 48 | + SAME_PAGE_UNALIGNED = 0, | |
| 49 | + /** | |
| 50 | + * @SAME_PAGE_ALIGNED: When the end of the previous buffer | |
| 51 | + * is not page aligned, the end of this buffer is on the | |
| 52 | + * same page as the end of the previous buffer and is page | |
| 53 | + * aligned. When the previous buffer is page aligned, the | |
| 54 | + * end of this buffer is aligned to the next page boundary. | |
| 55 | + * Examples: | |
| 56 | + * buf1 ][ buf2 ]| ... | |
| 57 | + * buf1 ]|[ buf2 ]| ... | |
| 58 | + */ | |
| 59 | + SAME_PAGE_ALIGNED, | |
| 60 | + /** | |
| 61 | + * @NEXT_PAGE_UNALIGNED: The end of this buffer is on | |
| 62 | + * the page next to the end of the previous buffer and | |
| 63 | + * is not page aligned. Examples: | |
| 64 | + * buf1 ][ buf2 | buf2 ][ ... | |
| 65 | + * buf1 ]|[ buf2 | buf2 ][ ... | |
| 66 | + */ | |
| 67 | + NEXT_PAGE_UNALIGNED, | |
| 68 | + /** | |
| 69 | + * @NEXT_PAGE_ALIGNED: The end of this buffer is on | |
| 70 | + * the page next to the end of the previous buffer and | |
| 71 | + * is page aligned. Examples: | |
| 72 | + * buf1 ][ buf2 | buf2 ]| ... | |
| 73 | + * buf1 ]|[ buf2 | buf2 ]| ... | |
| 74 | + */ | |
| 75 | + NEXT_PAGE_ALIGNED, | |
| 76 | + /** | |
| 77 | + * @NEXT_NEXT_UNALIGNED: The end of this buffer is on | |
| 78 | + * the page that follows the page after the end of the | |
| 79 | + * previous buffer and is not page aligned. Examples: | |
| 80 | + * buf1 ][ buf2 | buf2 | buf2 ][ ... | |
| 81 | + * buf1 ]|[ buf2 | buf2 | buf2 ][ ... | |
| 82 | + */ | |
| 83 | + NEXT_NEXT_UNALIGNED, | |
| 84 | + LOOP_END, | |
| 85 | +}; | |
| 86 | + | |
| 87 | +static void pr_err_size_seq(size_t *sizes, int *seq) | |
| 88 | +{ | |
| 89 | + int i; | |
| 90 | + | |
| 91 | + pr_err("alloc sizes: "); | |
| 92 | + for (i = 0; i < BUFFER_NUM; i++) | |
| 93 | + pr_cont("[%zu]", sizes[i]); | |
| 94 | + pr_cont("\n"); | |
| 95 | + pr_err("free seq: "); | |
| 96 | + for (i = 0; i < BUFFER_NUM; i++) | |
| 97 | + pr_cont("[%d]", seq[i]); | |
| 98 | + pr_cont("\n"); | |
| 99 | +} | |
| 100 | + | |
| 101 | +static bool check_buffer_pages_allocated(struct binder_alloc *alloc, | |
| 102 | + struct binder_buffer *buffer, | |
| 103 | + size_t size) | |
| 104 | +{ | |
| 105 | + void *page_addr, *end; | |
| 106 | + int page_index; | |
| 107 | + | |
| 108 | + end = (void *)PAGE_ALIGN((uintptr_t)buffer->data + size); | |
| 109 | + page_addr = buffer->data; | |
| 110 | + for (; page_addr < end; page_addr += PAGE_SIZE) { | |
| 111 | + page_index = (page_addr - alloc->buffer) / PAGE_SIZE; | |
| 112 | + if (!alloc->pages[page_index].page_ptr || | |
| 113 | + !list_empty(&alloc->pages[page_index].lru)) { | |
| 114 | + pr_err("expect alloc but is %s at page index %d\n", | |
| 115 | + alloc->pages[page_index].page_ptr ? | |
| 116 | + "lru" : "free", page_index); | |
| 117 | + return false; | |
| 118 | + } | |
| 119 | + } | |
| 120 | + return true; | |
| 121 | +} | |
| 122 | + | |
| 123 | +static void binder_selftest_alloc_buf(struct binder_alloc *alloc, | |
| 124 | + struct binder_buffer *buffers[], | |
| 125 | + size_t *sizes, int *seq) | |
| 126 | +{ | |
| 127 | + int i; | |
| 128 | + | |
| 129 | + for (i = 0; i < BUFFER_NUM; i++) { | |
| 130 | + buffers[i] = binder_alloc_new_buf(alloc, sizes[i], 0, 0, 0); | |
| 131 | + if (IS_ERR(buffers[i]) || | |
| 132 | + !check_buffer_pages_allocated(alloc, buffers[i], | |
| 133 | + sizes[i])) { | |
| 134 | + pr_err_size_seq(sizes, seq); | |
| 135 | + binder_selftest_failures++; | |
| 136 | + } | |
| 137 | + } | |
| 138 | +} | |
| 139 | + | |
| 140 | +static void binder_selftest_free_buf(struct binder_alloc *alloc, | |
| 141 | + struct binder_buffer *buffers[], | |
| 142 | + size_t *sizes, int *seq, size_t end) | |
| 143 | +{ | |
| 144 | + int i; | |
| 145 | + | |
| 146 | + for (i = 0; i < BUFFER_NUM; i++) | |
| 147 | + binder_alloc_free_buf(alloc, buffers[seq[i]]); | |
| 148 | + | |
| 149 | + for (i = 0; i < end / PAGE_SIZE; i++) { | |
| 150 | + /** | |
| 151 | + * Error message on a free page can be false positive | |
| 152 | + * if binder shrinker ran during binder_alloc_free_buf | |
| 153 | + * calls above. | |
| 154 | + */ | |
| 155 | + if (list_empty(&alloc->pages[i].lru)) { | |
| 156 | + pr_err_size_seq(sizes, seq); | |
| 157 | + pr_err("expect lru but is %s at page index %d\n", | |
| 158 | + alloc->pages[i].page_ptr ? "alloc" : "free", i); | |
| 159 | + binder_selftest_failures++; | |
| 160 | + } | |
| 161 | + } | |
| 162 | +} | |
| 163 | + | |
| 164 | +static void binder_selftest_free_page(struct binder_alloc *alloc) | |
| 165 | +{ | |
| 166 | + int i; | |
| 167 | + unsigned long count; | |
| 168 | + | |
| 169 | + while ((count = list_lru_count(&binder_alloc_lru))) { | |
| 170 | + list_lru_walk(&binder_alloc_lru, binder_alloc_free_page, | |
| 171 | + NULL, count); | |
| 172 | + } | |
| 173 | + | |
| 174 | + for (i = 0; i < (alloc->buffer_size / PAGE_SIZE); i++) { | |
| 175 | + if (alloc->pages[i].page_ptr) { | |
| 176 | + pr_err("expect free but is %s at page index %d\n", | |
| 177 | + list_empty(&alloc->pages[i].lru) ? | |
| 178 | + "alloc" : "lru", i); | |
| 179 | + binder_selftest_failures++; | |
| 180 | + } | |
| 181 | + } | |
| 182 | +} | |
| 183 | + | |
| 184 | +static void binder_selftest_alloc_free(struct binder_alloc *alloc, | |
| 185 | + size_t *sizes, int *seq, size_t end) | |
| 186 | +{ | |
| 187 | + struct binder_buffer *buffers[BUFFER_NUM]; | |
| 188 | + | |
| 189 | + binder_selftest_alloc_buf(alloc, buffers, sizes, seq); | |
| 190 | + binder_selftest_free_buf(alloc, buffers, sizes, seq, end); | |
| 191 | + | |
| 192 | + /* Allocate from lru. */ | |
| 193 | + binder_selftest_alloc_buf(alloc, buffers, sizes, seq); | |
| 194 | + if (list_lru_count(&binder_alloc_lru)) | |
| 195 | + pr_err("lru list should be empty but is not\n"); | |
| 196 | + | |
| 197 | + binder_selftest_free_buf(alloc, buffers, sizes, seq, end); | |
| 198 | + binder_selftest_free_page(alloc); | |
| 199 | +} | |
| 200 | + | |
| 201 | +static bool is_dup(int *seq, int index, int val) | |
| 202 | +{ | |
| 203 | + int i; | |
| 204 | + | |
| 205 | + for (i = 0; i < index; i++) { | |
| 206 | + if (seq[i] == val) | |
| 207 | + return true; | |
| 208 | + } | |
| 209 | + return false; | |
| 210 | +} | |
| 211 | + | |
| 212 | +/* Generate BUFFER_NUM factorial free orders. */ | |
| 213 | +static void binder_selftest_free_seq(struct binder_alloc *alloc, | |
| 214 | + size_t *sizes, int *seq, | |
| 215 | + int index, size_t end) | |
| 216 | +{ | |
| 217 | + int i; | |
| 218 | + | |
| 219 | + if (index == BUFFER_NUM) { | |
| 220 | + binder_selftest_alloc_free(alloc, sizes, seq, end); | |
| 221 | + return; | |
| 222 | + } | |
| 223 | + for (i = 0; i < BUFFER_NUM; i++) { | |
| 224 | + if (is_dup(seq, index, i)) | |
| 225 | + continue; | |
| 226 | + seq[index] = i; | |
| 227 | + binder_selftest_free_seq(alloc, sizes, seq, index + 1, end); | |
| 228 | + } | |
| 229 | +} | |
| 230 | + | |
| 231 | +static void binder_selftest_alloc_size(struct binder_alloc *alloc, | |
| 232 | + size_t *end_offset) | |
| 233 | +{ | |
| 234 | + int i; | |
| 235 | + int seq[BUFFER_NUM] = {0}; | |
| 236 | + size_t front_sizes[BUFFER_NUM]; | |
| 237 | + size_t back_sizes[BUFFER_NUM]; | |
| 238 | + size_t last_offset, offset = 0; | |
| 239 | + | |
| 240 | + for (i = 0; i < BUFFER_NUM; i++) { | |
| 241 | + last_offset = offset; | |
| 242 | + offset = end_offset[i]; | |
| 243 | + front_sizes[i] = offset - last_offset; | |
| 244 | + back_sizes[BUFFER_NUM - i - 1] = front_sizes[i]; | |
| 245 | + } | |
| 246 | + /* | |
| 247 | + * Buffers share the first or last few pages. | |
| 248 | + * Only BUFFER_NUM - 1 buffer sizes are adjustable since | |
| 249 | + * we need one giant buffer before getting to the last page. | |
| 250 | + */ | |
| 251 | + back_sizes[0] += alloc->buffer_size - end_offset[BUFFER_NUM - 1]; | |
| 252 | + binder_selftest_free_seq(alloc, front_sizes, seq, 0, | |
| 253 | + end_offset[BUFFER_NUM - 1]); | |
| 254 | + binder_selftest_free_seq(alloc, back_sizes, seq, 0, alloc->buffer_size); | |
| 255 | +} | |
| 256 | + | |
| 257 | +static void binder_selftest_alloc_offset(struct binder_alloc *alloc, | |
| 258 | + size_t *end_offset, int index) | |
| 259 | +{ | |
| 260 | + int align; | |
| 261 | + size_t end, prev; | |
| 262 | + | |
| 263 | + if (index == BUFFER_NUM) { | |
| 264 | + binder_selftest_alloc_size(alloc, end_offset); | |
| 265 | + return; | |
| 266 | + } | |
| 267 | + prev = index == 0 ? 0 : end_offset[index - 1]; | |
| 268 | + end = prev; | |
| 269 | + | |
| 270 | + BUILD_BUG_ON(BUFFER_MIN_SIZE * BUFFER_NUM >= PAGE_SIZE); | |
| 271 | + | |
| 272 | + for (align = SAME_PAGE_UNALIGNED; align < LOOP_END; align++) { | |
| 273 | + if (align % 2) | |
| 274 | + end = ALIGN(end, PAGE_SIZE); | |
| 275 | + else | |
| 276 | + end += BUFFER_MIN_SIZE; | |
| 277 | + end_offset[index] = end; | |
| 278 | + binder_selftest_alloc_offset(alloc, end_offset, index + 1); | |
| 279 | + } | |
| 280 | +} | |
| 281 | + | |
| 282 | +/** | |
| 283 | + * binder_selftest_alloc() - Test alloc and free of buffer pages. | |
| 284 | + * @alloc: Pointer to alloc struct. | |
| 285 | + * | |
| 286 | + * Allocate BUFFER_NUM buffers to cover all page alignment cases, | |
| 287 | + * then free them in all orders possible. Check that pages are | |
| 288 | + * correctly allocated, put onto lru when buffers are freed, and | |
| 289 | + * are freed when binder_alloc_free_page is called. | |
| 290 | + */ | |
| 291 | +void binder_selftest_alloc(struct binder_alloc *alloc) | |
| 292 | +{ | |
| 293 | + size_t end_offset[BUFFER_NUM]; | |
| 294 | + | |
| 295 | + if (!binder_selftest_run) | |
| 296 | + return; | |
| 297 | + mutex_lock(&binder_selftest_lock); | |
| 298 | + if (!binder_selftest_run || !alloc->vma) | |
| 299 | + goto done; | |
| 300 | + pr_info("STARTED\n"); | |
| 301 | + binder_selftest_alloc_offset(alloc, end_offset, 0); | |
| 302 | + binder_selftest_run = false; | |
| 303 | + if (binder_selftest_failures > 0) | |
| 304 | + pr_info("%d tests FAILED\n", binder_selftest_failures); | |
| 305 | + else | |
| 306 | + pr_info("PASSED\n"); | |
| 307 | + | |
| 308 | +done: | |
| 309 | + mutex_unlock(&binder_selftest_lock); | |
| 310 | +} | |
| @@ -23,7 +23,8 @@ · 22 unmodified lines | ||
| 23 | 23 | struct binder_buffer; |
| 24 | 24 | struct binder_node; |
| 25 | 25 | struct binder_proc; |
| 26 | −struct binder_ref; | |
| 26 | +struct binder_alloc; | |
| 27 | +struct binder_ref_data; | |
| 27 | 28 | struct binder_thread; |
| 28 | 29 | struct binder_transaction; |
| 29 | 30 | |
| @@ -146,8 +147,8 @@ TRACE_EVENT(binder_transaction_received, · 116 unmodified lines | ||
| 146 | 147 | |
| 147 | 148 | TRACE_EVENT(binder_transaction_node_to_ref, |
| 148 | 149 | TP_PROTO(struct binder_transaction *t, struct binder_node *node, |
| 149 | − struct binder_ref *ref), | |
| 150 | − TP_ARGS(t, node, ref), | |
| 150 | + struct binder_ref_data *rdata), | |
| 151 | + TP_ARGS(t, node, rdata), | |
| 151 | 152 | |
| 152 | 153 | TP_STRUCT__entry( |
| 153 | 154 | __field(int, debug_id) |
| @@ -160,8 +161,8 @@ TRACE_EVENT(binder_transaction_node_to_ref, · 6 unmodified lines | ||
| 160 | 161 | __entry->debug_id = t->debug_id; |
| 161 | 162 | __entry->node_debug_id = node->debug_id; |
| 162 | 163 | __entry->node_ptr = node->ptr; |
| 163 | − __entry->ref_debug_id = ref->debug_id; | |
| 164 | − __entry->ref_desc = ref->desc; | |
| 164 | + __entry->ref_debug_id = rdata->debug_id; | |
| 165 | + __entry->ref_desc = rdata->desc; | |
| 165 | 166 | ), |
| 166 | 167 | TP_printk("transaction=%d node=%d src_ptr=0x%016llx ==> dest_ref=%d dest_desc=%d", |
| 167 | 168 | __entry->debug_id, __entry->node_debug_id, |
| @@ -170,8 +171,9 @@ TRACE_EVENT(binder_transaction_node_to_ref, · 2 unmodified lines | ||
| 170 | 171 | ); |
| 171 | 172 | |
| 172 | 173 | TRACE_EVENT(binder_transaction_ref_to_node, |
| 173 | − TP_PROTO(struct binder_transaction *t, struct binder_ref *ref), | |
| 174 | − TP_ARGS(t, ref), | |
| 174 | + TP_PROTO(struct binder_transaction *t, struct binder_node *node, | |
| 175 | + struct binder_ref_data *rdata), | |
| 176 | + TP_ARGS(t, node, rdata), | |
| 175 | 177 | |
| 176 | 178 | TP_STRUCT__entry( |
| 177 | 179 | __field(int, debug_id) |
| @@ -182,10 +184,10 @@ TRACE_EVENT(binder_transaction_ref_to_node, · 4 unmodified lines | ||
| 182 | 184 | ), |
| 183 | 185 | TP_fast_assign( |
| 184 | 186 | __entry->debug_id = t->debug_id; |
| 185 | − __entry->ref_debug_id = ref->debug_id; | |
| 186 | − __entry->ref_desc = ref->desc; | |
| 187 | − __entry->node_debug_id = ref->node->debug_id; | |
| 188 | − __entry->node_ptr = ref->node->ptr; | |
| 187 | + __entry->ref_debug_id = rdata->debug_id; | |
| 188 | + __entry->ref_desc = rdata->desc; | |
| 189 | + __entry->node_debug_id = node->debug_id; | |
| 190 | + __entry->node_ptr = node->ptr; | |
| 189 | 191 | ), |
| 190 | 192 | TP_printk("transaction=%d node=%d src_ref=%d src_desc=%d ==> dest_ptr=0x%016llx", |
| 191 | 193 | __entry->debug_id, __entry->node_debug_id, |
| @@ -194,9 +196,10 @@ TRACE_EVENT(binder_transaction_ref_to_node, · 2 unmodified lines | ||
| 194 | 196 | ); |
| 195 | 197 | |
| 196 | 198 | TRACE_EVENT(binder_transaction_ref_to_ref, |
| 197 | − TP_PROTO(struct binder_transaction *t, struct binder_ref *src_ref, | |
| 198 | − struct binder_ref *dest_ref), | |
| 199 | − TP_ARGS(t, src_ref, dest_ref), | |
| 199 | + TP_PROTO(struct binder_transaction *t, struct binder_node *node, | |
| 200 | + struct binder_ref_data *src_ref, | |
| 201 | + struct binder_ref_data *dest_ref), | |
| 202 | + TP_ARGS(t, node, src_ref, dest_ref), | |
| 200 | 203 | |
| 201 | 204 | TP_STRUCT__entry( |
| 202 | 205 | __field(int, debug_id) |
| @@ -208,7 +211,7 @@ TRACE_EVENT(binder_transaction_ref_to_ref, · 5 unmodified lines | ||
| 208 | 211 | ), |
| 209 | 212 | TP_fast_assign( |
| 210 | 213 | __entry->debug_id = t->debug_id; |
| 211 | − __entry->node_debug_id = src_ref->node->debug_id; | |
| 214 | + __entry->node_debug_id = node->debug_id; | |
| 212 | 215 | __entry->src_ref_debug_id = src_ref->debug_id; |
| 213 | 216 | __entry->src_ref_desc = src_ref->desc; |
| 214 | 217 | __entry->dest_ref_debug_id = dest_ref->debug_id; |
| @@ -268,9 +271,9 @@ DEFINE_EVENT(binder_buffer_class, binder_transaction_failed_buffer_release, · 53 unmodified lines | ||
| 268 | 271 | TP_ARGS(buffer)); |
| 269 | 272 | |
| 270 | 273 | TRACE_EVENT(binder_update_page_range, |
| 271 | − TP_PROTO(struct binder_proc *proc, bool allocate, | |
| 274 | + TP_PROTO(struct binder_alloc *alloc, bool allocate, | |
| 272 | 275 | void *start, void *end), |
| 273 | − TP_ARGS(proc, allocate, start, end), | |
| 276 | + TP_ARGS(alloc, allocate, start, end), | |
| 274 | 277 | TP_STRUCT__entry( |
| 275 | 278 | __field(int, proc) |
| 276 | 279 | __field(bool, allocate) |
| @@ -278,9 +281,9 @@ TRACE_EVENT(binder_update_page_range, · 1 unmodified line | ||
| 278 | 281 | __field(size_t, size) |
| 279 | 282 | ), |
| 280 | 283 | TP_fast_assign( |
| 281 | − __entry->proc = proc->pid; | |
| 284 | + __entry->proc = alloc->pid; | |
| 282 | 285 | __entry->allocate = allocate; |
| 283 | − __entry->offset = start - proc->buffer; | |
| 286 | + __entry->offset = start - alloc->buffer; | |
| 284 | 287 | __entry->size = end - start; |
| 285 | 288 | ), |
| 286 | 289 | TP_printk("proc=%d allocate=%d offset=%zu size=%zu", |
| @@ -288,6 +291,61 @@ TRACE_EVENT(binder_update_page_range, · 1 unmodified line | ||
| 288 | 291 | __entry->offset, __entry->size) |
| 289 | 292 | ); |
| 290 | 293 | |
| 294 | +DECLARE_EVENT_CLASS(binder_lru_page_class, | |
| 295 | + TP_PROTO(const struct binder_alloc *alloc, size_t page_index), | |
| 296 | + TP_ARGS(alloc, page_index), | |
| 297 | + TP_STRUCT__entry( | |
| 298 | + __field(int, proc) | |
| 299 | + __field(size_t, page_index) | |
| 300 | + ), | |
| 301 | + TP_fast_assign( | |
| 302 | + __entry->proc = alloc->pid; | |
| 303 | + __entry->page_index = page_index; | |
| 304 | + ), | |
| 305 | + TP_printk("proc=%d page_index=%zu", | |
| 306 | + __entry->proc, __entry->page_index) | |
| 307 | +); | |
| 308 | + | |
| 309 | +DEFINE_EVENT(binder_lru_page_class, binder_alloc_lru_start, | |
| 310 | + TP_PROTO(const struct binder_alloc *alloc, size_t page_index), | |
| 311 | + TP_ARGS(alloc, page_index)); | |
| 312 | + | |
| 313 | +DEFINE_EVENT(binder_lru_page_class, binder_alloc_lru_end, | |
| 314 | + TP_PROTO(const struct binder_alloc *alloc, size_t page_index), | |
| 315 | + TP_ARGS(alloc, page_index)); | |
| 316 | + | |
| 317 | +DEFINE_EVENT(binder_lru_page_class, binder_free_lru_start, | |
| 318 | + TP_PROTO(const struct binder_alloc *alloc, size_t page_index), | |
| 319 | + TP_ARGS(alloc, page_index)); | |
| 320 | + | |
| 321 | +DEFINE_EVENT(binder_lru_page_class, binder_free_lru_end, | |
| 322 | + TP_PROTO(const struct binder_alloc *alloc, size_t page_index), | |
| 323 | + TP_ARGS(alloc, page_index)); | |
| 324 | + | |
| 325 | +DEFINE_EVENT(binder_lru_page_class, binder_alloc_page_start, | |
| 326 | + TP_PROTO(const struct binder_alloc *alloc, size_t page_index), | |
| 327 | + TP_ARGS(alloc, page_index)); | |
| 328 | + | |
| 329 | +DEFINE_EVENT(binder_lru_page_class, binder_alloc_page_end, | |
| 330 | + TP_PROTO(const struct binder_alloc *alloc, size_t page_index), | |
| 331 | + TP_ARGS(alloc, page_index)); | |
| 332 | + | |
| 333 | +DEFINE_EVENT(binder_lru_page_class, binder_unmap_user_start, | |
| 334 | + TP_PROTO(const struct binder_alloc *alloc, size_t page_index), | |
| 335 | + TP_ARGS(alloc, page_index)); | |
| 336 | + | |
| 337 | +DEFINE_EVENT(binder_lru_page_class, binder_unmap_user_end, | |
| 338 | + TP_PROTO(const struct binder_alloc *alloc, size_t page_index), | |
| 339 | + TP_ARGS(alloc, page_index)); | |
| 340 | + | |
| 341 | +DEFINE_EVENT(binder_lru_page_class, binder_unmap_kernel_start, | |
| 342 | + TP_PROTO(const struct binder_alloc *alloc, size_t page_index), | |
| 343 | + TP_ARGS(alloc, page_index)); | |
| 344 | + | |
| 345 | +DEFINE_EVENT(binder_lru_page_class, binder_unmap_kernel_end, | |
| 346 | + TP_PROTO(const struct binder_alloc *alloc, size_t page_index), | |
| 347 | + TP_ARGS(alloc, page_index)); | |
| 348 | + | |
| 291 | 349 | TRACE_EVENT(binder_command, |
| 292 | 350 | TP_PROTO(uint32_t cmd), |
| 293 | 351 | TP_ARGS(cmd), |
| @@ -877,21 +877,21 @@ static void lcd_clear_fast_tilcd(struct charlcd *charlcd) · 876 unmodified lines | ||
| 877 | 877 | spin_unlock_irq(&pprt_lock); |
| 878 | 878 | } |
| 879 | 879 | |
| 880 | −static struct charlcd_ops charlcd_serial_ops = { | |
| 880 | +static const struct charlcd_ops charlcd_serial_ops = { | |
| 881 | 881 | .write_cmd = lcd_write_cmd_s, |
| 882 | 882 | .write_data = lcd_write_data_s, |
| 883 | 883 | .clear_fast = lcd_clear_fast_s, |
| 884 | 884 | .backlight = lcd_backlight, |
| 885 | 885 | }; |
| 886 | 886 | |
| 887 | −static struct charlcd_ops charlcd_parallel_ops = { | |
| 887 | +static const struct charlcd_ops charlcd_parallel_ops = { | |
| 888 | 888 | .write_cmd = lcd_write_cmd_p8, |
| 889 | 889 | .write_data = lcd_write_data_p8, |
| 890 | 890 | .clear_fast = lcd_clear_fast_p8, |
| 891 | 891 | .backlight = lcd_backlight, |
| 892 | 892 | }; |
| 893 | 893 | |
| 894 | −static struct charlcd_ops charlcd_tilcd_ops = { | |
| 894 | +static const struct charlcd_ops charlcd_tilcd_ops = { | |
| 895 | 895 | .write_cmd = lcd_write_cmd_tilcd, |
| 896 | 896 | .write_data = lcd_write_data_tilcd, |
| 897 | 897 | .clear_fast = lcd_clear_fast_tilcd, |
| @@ -67,7 +67,7 @@ static char *applicom_pci_devnames[] = { · 66 unmodified lines | ||
| 67 | 67 | "PCI2000PFB" |
| 68 | 68 | }; |
| 69 | 69 | |
| 70 | −static struct pci_device_id applicom_pci_tbl[] = { | |
| 70 | +static const struct pci_device_id applicom_pci_tbl[] = { | |
| 71 | 71 | { PCI_VDEVICE(APPLICOM, PCI_DEVICE_ID_APPLICOM_PCIGENERIC) }, |
| 72 | 72 | { PCI_VDEVICE(APPLICOM, PCI_DEVICE_ID_APPLICOM_PCI2000IBS_CAN) }, |
| 73 | 73 | { PCI_VDEVICE(APPLICOM, PCI_DEVICE_ID_APPLICOM_PCI2000PFB) }, |
| @@ -128,10 +128,11 @@ int smapi_query_DSP_cfg(SMAPI_DSP_SETTINGS * pSettings) · 127 unmodified lines | ||
| 128 | 128 | { |
| 129 | 129 | int bRC = -EIO; |
| 130 | 130 | unsigned short usAX, usBX, usCX, usDX, usDI, usSI; |
| 131 | − unsigned short ausDspBases[] = { 0x0030, 0x4E30, 0x8E30, 0xCE30, 0x0130, 0x0350, 0x0070, 0x0DB0 }; | |
| 132 | − unsigned short ausUartBases[] = { 0x03F8, 0x02F8, 0x03E8, 0x02E8 }; | |
| 133 | − unsigned short numDspBases = 8; | |
| 134 | − unsigned short numUartBases = 4; | |
| 131 | + static const unsigned short ausDspBases[] = { | |
| 132 | + 0x0030, 0x4E30, 0x8E30, 0xCE30, | |
| 133 | + 0x0130, 0x0350, 0x0070, 0x0DB0 }; | |
| 134 | + static const unsigned short ausUartBases[] = { | |
| 135 | + 0x03F8, 0x02F8, 0x03E8, 0x02E8 }; | |
| 135 | 136 | |
| 136 | 137 | PRINTK_1(TRACE_SMAPI, "smapi::smapi_query_DSP_cfg entry\n"); |
| 137 | 138 | |
| @@ -148,7 +149,7 @@ int smapi_query_DSP_cfg(SMAPI_DSP_SETTINGS * pSettings) · 10 unmodified lines | ||
| 148 | 149 | pSettings->bDSPEnabled = ((usCX & 0x0001) != 0); |
| 149 | 150 | pSettings->usDspIRQ = usSI & 0x00FF; |
| 150 | 151 | pSettings->usDspDMA = (usSI & 0xFF00) >> 8; |
| 151 | − if ((usDI & 0x00FF) < numDspBases) { | |
| 152 | + if ((usDI & 0x00FF) < ARRAY_SIZE(ausDspBases)) { | |
| 152 | 153 | pSettings->usDspBaseIO = ausDspBases[usDI & 0x00FF]; |
| 153 | 154 | } else { |
| 154 | 155 | pSettings->usDspBaseIO = 0; |
| @@ -176,7 +177,7 @@ int smapi_query_DSP_cfg(SMAPI_DSP_SETTINGS * pSettings) · 21 unmodified lines | ||
| 176 | 177 | |
| 177 | 178 | pSettings->bModemEnabled = ((usCX & 0x0001) != 0); |
| 178 | 179 | pSettings->usUartIRQ = usSI & 0x000F; |
| 179 | − if (((usSI & 0xFF00) >> 8) < numUartBases) { | |
| 180 | + if (((usSI & 0xFF00) >> 8) < ARRAY_SIZE(ausUartBases)) { | |
| 180 | 181 | pSettings->usUartBaseIO = ausUartBases[(usSI & 0xFF00) >> 8]; |
| 181 | 182 | } else { |
| 182 | 183 | pSettings->usUartBaseIO = 0; |
| @@ -205,15 +206,16 @@ int smapi_set_DSP_cfg(void) · 22 unmodified lines | ||
| 205 | 206 | int bRC = -EIO; |
| 206 | 207 | int i; |
| 207 | 208 | unsigned short usAX, usBX, usCX, usDX, usDI, usSI; |
| 208 | − unsigned short ausDspBases[] = { 0x0030, 0x4E30, 0x8E30, 0xCE30, 0x0130, 0x0350, 0x0070, 0x0DB0 }; | |
| 209 | − unsigned short ausUartBases[] = { 0x03F8, 0x02F8, 0x03E8, 0x02E8 }; | |
| 210 | − unsigned short ausDspIrqs[] = { 5, 7, 10, 11, 15 }; | |
| 211 | − unsigned short ausUartIrqs[] = { 3, 4 }; | |
| 212 | − | |
| 213 | − unsigned short numDspBases = 8; | |
| 214 | − unsigned short numUartBases = 4; | |
| 215 | − unsigned short numDspIrqs = 5; | |
| 216 | − unsigned short numUartIrqs = 2; | |
| 209 | + static const unsigned short ausDspBases[] = { | |
| 210 | + 0x0030, 0x4E30, 0x8E30, 0xCE30, | |
| 211 | + 0x0130, 0x0350, 0x0070, 0x0DB0 }; | |
| 212 | + static const unsigned short ausUartBases[] = { | |
| 213 | + 0x03F8, 0x02F8, 0x03E8, 0x02E8 }; | |
| 214 | + static const unsigned short ausDspIrqs[] = { | |
| 215 | + 5, 7, 10, 11, 15 }; | |
| 216 | + static const unsigned short ausUartIrqs[] = { | |
| 217 | + 3, 4 }; | |
| 218 | + | |
| 217 | 219 | unsigned short dspio_index = 0, uartio_index = 0; |
| 218 | 220 | |
| 219 | 221 | PRINTK_5(TRACE_SMAPI, |
| @@ -221,11 +223,11 @@ int smapi_set_DSP_cfg(void) · 1 unmodified line | ||
| 221 | 223 | mwave_3780i_irq, mwave_3780i_io, mwave_uart_irq, mwave_uart_io); |
| 222 | 224 | |
| 223 | 225 | if (mwave_3780i_io) { |
| 224 | − for (i = 0; i < numDspBases; i++) { | |
| 226 | + for (i = 0; i < ARRAY_SIZE(ausDspBases); i++) { | |
| 225 | 227 | if (mwave_3780i_io == ausDspBases[i]) |
| 226 | 228 | break; |
| 227 | 229 | } |
| 228 | − if (i == numDspBases) { | |
| 230 | + if (i == ARRAY_SIZE(ausDspBases)) { | |
| 229 | 231 | PRINTK_ERROR(KERN_ERR_MWAVE "smapi::smapi_set_DSP_cfg: Error: Invalid mwave_3780i_io address %x. Aborting.\n", mwave_3780i_io); |
| 230 | 232 | return bRC; |
| 231 | 233 | } |
| @@ -233,22 +235,22 @@ int smapi_set_DSP_cfg(void) · 1 unmodified line | ||
| 233 | 235 | } |
| 234 | 236 | |
| 235 | 237 | if (mwave_3780i_irq) { |
| 236 | − for (i = 0; i < numDspIrqs; i++) { | |
| 238 | + for (i = 0; i < ARRAY_SIZE(ausDspIrqs); i++) { | |
| 237 | 239 | if (mwave_3780i_irq == ausDspIrqs[i]) |
| 238 | 240 | break; |
| 239 | 241 | } |
| 240 | − if (i == numDspIrqs) { | |
| 242 | + if (i == ARRAY_SIZE(ausDspIrqs)) { | |
| 241 | 243 | PRINTK_ERROR(KERN_ERR_MWAVE "smapi::smapi_set_DSP_cfg: Error: Invalid mwave_3780i_irq %x. Aborting.\n", mwave_3780i_irq); |
| 242 | 244 | return bRC; |
| 243 | 245 | } |
| 244 | 246 | } |
| 245 | 247 | |
| 246 | 248 | if (mwave_uart_io) { |
| 247 | − for (i = 0; i < numUartBases; i++) { | |
| 249 | + for (i = 0; i < ARRAY_SIZE(ausUartBases); i++) { | |
| 248 | 250 | if (mwave_uart_io == ausUartBases[i]) |
| 249 | 251 | break; |
| 250 | 252 | } |
| 251 | − if (i == numUartBases) { | |
| 253 | + if (i == ARRAY_SIZE(ausUartBases)) { | |
| 252 | 254 | PRINTK_ERROR(KERN_ERR_MWAVE "smapi::smapi_set_DSP_cfg: Error: Invalid mwave_uart_io address %x. Aborting.\n", mwave_uart_io); |
| 253 | 255 | return bRC; |
| 254 | 256 | } |
| @@ -257,11 +259,11 @@ int smapi_set_DSP_cfg(void) · 2 unmodified lines | ||
| 257 | 259 | |
| 258 | 260 | |
| 259 | 261 | if (mwave_uart_irq) { |
| 260 | − for (i = 0; i < numUartIrqs; i++) { | |
| 262 | + for (i = 0; i < ARRAY_SIZE(ausUartIrqs); i++) { | |
| 261 | 263 | if (mwave_uart_irq == ausUartIrqs[i]) |
| 262 | 264 | break; |
| 263 | 265 | } |
| 264 | − if (i == numUartIrqs) { | |
| 266 | + if (i == ARRAY_SIZE(ausUartIrqs)) { | |
| 265 | 267 | PRINTK_ERROR(KERN_ERR_MWAVE "smapi::smapi_set_DSP_cfg: Error: Invalid mwave_uart_irq %x. Aborting.\n", mwave_uart_irq); |
| 266 | 268 | return bRC; |
| 267 | 269 | } |
| @@ -101,9 +101,6 @@ static DEFINE_IDA(ida_index); · 100 unmodified lines | ||
| 101 | 101 | #define PP_BUFFER_SIZE 1024 |
| 102 | 102 | #define PARDEVICE_MAX 8 |
| 103 | 103 | |
| 104 | −/* ROUND_UP macro from fs/select.c */ | |
| 105 | −#define ROUND_UP(x,y) (((x)+(y)-1)/(y)) | |
| 106 | − | |
| 107 | 104 | static DEFINE_MUTEX(pp_do_mutex); |
| 108 | 105 | |
| 109 | 106 | /* define fixed sized ioctl cmd for y2038 migration */ |
| @@ -766,7 +766,7 @@ static struct attribute *tlclk_sysfs_entries[] = { · 765 unmodified lines | ||
| 766 | 766 | NULL |
| 767 | 767 | }; |
| 768 | 768 | |
| 769 | −static struct attribute_group tlclk_attribute_group = { | |
| 769 | +static const struct attribute_group tlclk_attribute_group = { | |
| 770 | 770 | .name = NULL, /* put in device directory */ |
| 771 | 771 | .attrs = tlclk_sysfs_entries, |
| 772 | 772 | }; |
| @@ -1308,7 +1308,7 @@ static struct attribute *port_sysfs_entries[] = { · 1307 unmodified lines | ||
| 1308 | 1308 | NULL |
| 1309 | 1309 | }; |
| 1310 | 1310 | |
| 1311 | −static struct attribute_group port_attribute_group = { | |
| 1311 | +static const struct attribute_group port_attribute_group = { | |
| 1312 | 1312 | .name = NULL, /* put in device directory */ |
| 1313 | 1313 | .attrs = port_sysfs_entries, |
| 1314 | 1314 | }; |
| @@ -86,8 +86,7 @@ · 85 unmodified lines | ||
| 86 | 86 | #include <linux/cdev.h> |
| 87 | 87 | #include <linux/platform_device.h> |
| 88 | 88 | #include <linux/slab.h> |
| 89 | − | |
| 90 | −#include <asm/io.h> | |
| 89 | +#include <linux/io.h> | |
| 91 | 90 | #include <linux/uaccess.h> |
| 92 | 91 | |
| 93 | 92 | #ifdef CONFIG_OF |
| @@ -222,6 +221,8 @@ static const struct config_registers v6_config_registers = { · 128 unmodified lines | ||
| 222 | 221 | * hwicap_command_desync - Send a DESYNC command to the ICAP port. |
| 223 | 222 | * @drvdata: a pointer to the drvdata. |
| 224 | 223 | * |
| 224 | + * Returns: '0' on success and failure value on error | |
| 225 | + * | |
| 225 | 226 | * This command desynchronizes the ICAP After this command, a |
| 226 | 227 | * bitstream containing a NULL packet, followed by a SYNCH packet is |
| 227 | 228 | * required before the ICAP will recognize commands. |
| @@ -251,10 +252,12 @@ static int hwicap_command_desync(struct hwicap_drvdata *drvdata) · 23 unmodified lines | ||
| 251 | 252 | * hwicap_get_configuration_register - Query a configuration register. |
| 252 | 253 | * @drvdata: a pointer to the drvdata. |
| 253 | 254 | * @reg: a constant which represents the configuration |
| 254 | − * register value to be returned. | |
| 255 | − * Examples: XHI_IDCODE, XHI_FLR. | |
| 255 | + * register value to be returned. | |
| 256 | + * Examples: XHI_IDCODE, XHI_FLR. | |
| 256 | 257 | * @reg_data: returns the value of the register. |
| 257 | 258 | * |
| 259 | + * Returns: '0' on success and failure value on error | |
| 260 | + * | |
| 258 | 261 | * Sends a query packet to the ICAP and then receives the response. |
| 259 | 262 | * The icap is left in Synched state. |
| 260 | 263 | */ |
| @@ -320,7 +323,8 @@ static int hwicap_initialize_hwicap(struct hwicap_drvdata *drvdata) · 59 unmodified lines | ||
| 320 | 323 | dev_dbg(drvdata->dev, "initializing\n"); |
| 321 | 324 | |
| 322 | 325 | /* Abort any current transaction, to make sure we have the |
| 323 | − * ICAP in a good state. */ | |
| 326 | + * ICAP in a good state. | |
| 327 | + */ | |
| 324 | 328 | dev_dbg(drvdata->dev, "Reset...\n"); |
| 325 | 329 | drvdata->config->reset(drvdata); |
| 326 | 330 | |
| @@ -632,7 +636,6 @@ static int hwicap_setup(struct device *dev, int id, · 305 unmodified lines | ||
| 632 | 636 | |
| 633 | 637 | drvdata = kzalloc(sizeof(struct hwicap_drvdata), GFP_KERNEL); |
| 634 | 638 | if (!drvdata) { |
| 635 | − dev_err(dev, "Couldn't allocate device private record\n"); | |
| 636 | 639 | retval = -ENOMEM; |
| 637 | 640 | goto failed0; |
| 638 | 641 | } |
| @@ -759,20 +762,20 @@ static int hwicap_of_probe(struct platform_device *op, · 120 unmodified lines | ||
| 759 | 762 | id = of_get_property(op->dev.of_node, "port-number", NULL); |
| 760 | 763 | |
| 761 | 764 | /* It's most likely that we're using V4, if the family is not |
| 762 | − specified */ | |
| 765 | + * specified | |
| 766 | + */ | |
| 763 | 767 | regs = &v4_config_registers; |
| 764 | 768 | family = of_get_property(op->dev.of_node, "xlnx,family", NULL); |
| 765 | 769 | |
| 766 | 770 | if (family) { |
| 767 | − if (!strcmp(family, "virtex2p")) { | |
| 771 | + if (!strcmp(family, "virtex2p")) | |
| 768 | 772 | regs = &v2_config_registers; |
| 769 | − } else if (!strcmp(family, "virtex4")) { | |
| 773 | + else if (!strcmp(family, "virtex4")) | |
| 770 | 774 | regs = &v4_config_registers; |
| 771 | − } else if (!strcmp(family, "virtex5")) { | |
| 775 | + else if (!strcmp(family, "virtex5")) | |
| 772 | 776 | regs = &v5_config_registers; |
| 773 | − } else if (!strcmp(family, "virtex6")) { | |
| 777 | + else if (!strcmp(family, "virtex6")) | |
| 774 | 778 | regs = &v6_config_registers; |
| 775 | − } | |
| 776 | 779 | } |
| 777 | 780 | return hwicap_setup(&op->dev, id ? *id : -1, &res, config, |
| 778 | 781 | regs); |
| @@ -802,20 +805,20 @@ static int hwicap_drv_probe(struct platform_device *pdev) · 23 unmodified lines | ||
| 802 | 805 | return -ENODEV; |
| 803 | 806 | |
| 804 | 807 | /* It's most likely that we're using V4, if the family is not |
| 805 | − specified */ | |
| 808 | + * specified | |
| 809 | + */ | |
| 806 | 810 | regs = &v4_config_registers; |
| 807 | 811 | family = pdev->dev.platform_data; |
| 808 | 812 | |
| 809 | 813 | if (family) { |
| 810 | − if (!strcmp(family, "virtex2p")) { | |
| 814 | + if (!strcmp(family, "virtex2p")) | |
| 811 | 815 | regs = &v2_config_registers; |
| 812 | − } else if (!strcmp(family, "virtex4")) { | |
| 816 | + else if (!strcmp(family, "virtex4")) | |
| 813 | 817 | regs = &v4_config_registers; |
| 814 | − } else if (!strcmp(family, "virtex5")) { | |
| 818 | + else if (!strcmp(family, "virtex5")) | |
| 815 | 819 | regs = &v5_config_registers; |
| 816 | − } else if (!strcmp(family, "virtex6")) { | |
| 820 | + else if (!strcmp(family, "virtex6")) | |
| 817 | 821 | regs = &v6_config_registers; |
| 818 | − } | |
| 819 | 822 | } |
| 820 | 823 | |
| 821 | 824 | return hwicap_setup(&pdev->dev, pdev->id, res, |
| @@ -62,11 +62,13 @@ struct hwicap_drvdata { · 61 unmodified lines | ||
| 62 | 62 | |
| 63 | 63 | struct hwicap_driver_config { |
| 64 | 64 | /* Read configuration data given by size into the data buffer. |
| 65 | − Return 0 if successful. */ | |
| 65 | + * Return 0 if successful. | |
| 66 | + */ | |
| 66 | 67 | int (*get_configuration)(struct hwicap_drvdata *drvdata, u32 *data, |
| 67 | 68 | u32 size); |
| 68 | 69 | /* Write configuration data given by size from the data buffer. |
| 69 | − Return 0 if successful. */ | |
| 70 | + * Return 0 if successful. | |
| 71 | + */ | |
| 70 | 72 | int (*set_configuration)(struct hwicap_drvdata *drvdata, u32 *data, |
| 71 | 73 | u32 size); |
| 72 | 74 | /* Get the status register, bit pattern given by: |
| @@ -193,11 +195,12 @@ struct config_registers { · 120 unmodified lines | ||
| 193 | 195 | * hwicap_type_1_read - Generates a Type 1 read packet header. |
| 194 | 196 | * @reg: is the address of the register to be read back. |
| 195 | 197 | * |
| 198 | + * Return: | |
| 196 | 199 | * Generates a Type 1 read packet header, which is used to indirectly |
| 197 | 200 | * read registers in the configuration logic. This packet must then |
| 198 | 201 | * be sent through the icap device, and a return packet received with |
| 199 | 202 | * the information. |
| 200 | − **/ | |
| 203 | + */ | |
| 201 | 204 | static inline u32 hwicap_type_1_read(u32 reg) |
| 202 | 205 | { |
| 203 | 206 | return (XHI_TYPE_1 << XHI_TYPE_SHIFT) | |
| @@ -208,7 +211,9 @@ static inline u32 hwicap_type_1_read(u32 reg) · 4 unmodified lines | ||
| 208 | 211 | /** |
| 209 | 212 | * hwicap_type_1_write - Generates a Type 1 write packet header |
| 210 | 213 | * @reg: is the address of the register to be read back. |
| 211 | − **/ | |
| 214 | + * | |
| 215 | + * Return: Type 1 write packet header | |
| 216 | + */ | |
| 212 | 217 | static inline u32 hwicap_type_1_write(u32 reg) |
| 213 | 218 | { |
| 214 | 219 | return (XHI_TYPE_1 << XHI_TYPE_SHIFT) | |
| @@ -150,4 +150,11 @@ config EXTCON_USB_GPIO · 149 unmodified lines | ||
| 150 | 150 | Say Y here to enable GPIO based USB cable detection extcon support. |
| 151 | 151 | Used typically if GPIO is used for USB ID pin detection. |
| 152 | 152 | |
| 153 | +config EXTCON_USBC_CROS_EC | |
| 154 | + tristate "ChromeOS Embedded Controller EXTCON support" | |
| 155 | + depends on MFD_CROS_EC | |
| 156 | + help | |
| 157 | + Say Y here to enable USB Type C cable detection extcon support when | |
| 158 | + using Chrome OS EC based USB Type-C ports. | |
| 159 | + | |
| 153 | 160 | endif |
| @@ -20,3 +20,4 @@ obj-$(CONFIG_EXTCON_QCOM_SPMI_MISC) += extcon-qcom-spmi-misc.o · 19 unmodified lines | ||
| 20 | 20 | obj-$(CONFIG_EXTCON_RT8973A) += extcon-rt8973a.o |
| 21 | 21 | obj-$(CONFIG_EXTCON_SM5502) += extcon-sm5502.o |
| 22 | 22 | obj-$(CONFIG_EXTCON_USB_GPIO) += extcon-usb-gpio.o |
| 23 | +obj-$(CONFIG_EXTCON_USBC_CROS_EC) += extcon-usbc-cros-ec.o | |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | /* |
| 2 | − * drivers/extcon/devres.c - EXTCON device's resource management | |
| 2 | + * drivers/extcon/devres.c - EXTCON device's resource management | |
| 3 | 3 | * |
| 4 | 4 | * Copyright (C) 2016 Samsung Electronics |
| 5 | 5 | * Author: Chanwoo Choi <cw00.choi@samsung.com> |
| @@ -59,10 +59,9 @@ static void devm_extcon_dev_notifier_all_unreg(struct device *dev, void *res) · 53 unmodified lines | ||
| 59 | 59 | |
| 60 | 60 | /** |
| 61 | 61 | * devm_extcon_dev_allocate - Allocate managed extcon device |
| 62 | − * @dev: device owning the extcon device being created | |
| 63 | − * @supported_cable: Array of supported extcon ending with EXTCON_NONE. | |
| 64 | − * If supported_cable is NULL, cable name related APIs | |
| 65 | − * are disabled. | |
| 62 | + * @dev: the device owning the extcon device being created | |
| 63 | + * @supported_cable: the array of the supported external connectors | |
| 64 | + * ending with EXTCON_NONE. | |
| 66 | 65 | * |
| 67 | 66 | * This function manages automatically the memory of extcon device using device |
| 68 | 67 | * resource management and simplify the control of freeing the memory of extcon |
| @@ -97,8 +96,8 @@ EXPORT_SYMBOL_GPL(devm_extcon_dev_allocate); · 28 unmodified lines | ||
| 97 | 96 | |
| 98 | 97 | /** |
| 99 | 98 | * devm_extcon_dev_free() - Resource-managed extcon_dev_unregister() |
| 100 | − * @dev: device the extcon belongs to | |
| 101 | − * @edev: the extcon device to unregister | |
| 99 | + * @dev: the device owning the extcon device being created | |
| 100 | + * @edev: the extcon device to be freed | |
| 102 | 101 | * |
| 103 | 102 | * Free the memory that is allocated with devm_extcon_dev_allocate() |
| 104 | 103 | * function. |
| @@ -112,10 +111,9 @@ EXPORT_SYMBOL_GPL(devm_extcon_dev_free); · 7 unmodified lines | ||
| 112 | 111 | |
| 113 | 112 | /** |
| 114 | 113 | * devm_extcon_dev_register() - Resource-managed extcon_dev_register() |
| 115 | − * @dev: device to allocate extcon device | |
| 116 | − * @edev: the new extcon device to register | |
| 114 | + * @dev: the device owning the extcon device being created | |
| 115 | + * @edev: the extcon device to be registered | |
| 117 | 116 | * |
| 118 | − * Managed extcon_dev_register() function. If extcon device is attached with | |
| 119 | 117 | * this function, that extcon device is automatically unregistered on driver |
| 120 | 118 | * detach. Internally this function calls extcon_dev_register() function. |
| 121 | 119 | * To get more information, refer that function. |
| @@ -149,8 +147,8 @@ EXPORT_SYMBOL_GPL(devm_extcon_dev_register); · 27 unmodified lines | ||
| 149 | 147 | |
| 150 | 148 | /** |
| 151 | 149 | * devm_extcon_dev_unregister() - Resource-managed extcon_dev_unregister() |
| 152 | − * @dev: device the extcon belongs to | |
| 153 | − * @edev: the extcon device to unregister | |
| 150 | + * @dev: the device owning the extcon device being created | |
| 151 | + * @edev: the extcon device to unregistered | |
| 154 | 152 | * |
| 155 | 153 | * Unregister extcon device that is registered with devm_extcon_dev_register() |
| 156 | 154 | * function. |
| @@ -164,10 +162,10 @@ EXPORT_SYMBOL_GPL(devm_extcon_dev_unregister); · 7 unmodified lines | ||
| 164 | 162 | |
| 165 | 163 | /** |
| 166 | 164 | * devm_extcon_register_notifier() - Resource-managed extcon_register_notifier() |
| 167 | − * @dev: device to allocate extcon device | |
| 168 | − * @edev: the extcon device that has the external connecotr. | |
| 169 | − * @id: the unique id of each external connector in extcon enumeration. | |
| 170 | − * @nb: a notifier block to be registered. | |
| 165 | + * @dev: the device owning the extcon device being created | |
| 166 | + * @edev: the extcon device | |
| 167 | + * @id: the unique id among the extcon enumeration | |
| 168 | + * @nb: a notifier block to be registered | |
| 171 | 169 | * |
| 172 | 170 | * This function manages automatically the notifier of extcon device using |
| 173 | 171 | * device resource management and simplify the control of unregistering |
| @@ -208,10 +206,10 @@ EXPORT_SYMBOL(devm_extcon_register_notifier); · 34 unmodified lines | ||
| 208 | 206 | /** |
| 209 | 207 | * devm_extcon_unregister_notifier() |
| 210 | 208 | - Resource-managed extcon_unregister_notifier() |
| 211 | − * @dev: device to allocate extcon device | |
| 212 | − * @edev: the extcon device that has the external connecotr. | |
| 213 | − * @id: the unique id of each external connector in extcon enumeration. | |
| 214 | − * @nb: a notifier block to be registered. | |
| 209 | + * @dev: the device owning the extcon device being created | |
| 210 | + * @edev: the extcon device | |
| 211 | + * @id: the unique id among the extcon enumeration | |
| 212 | + * @nb: a notifier block to be registered | |
| 215 | 213 | */ |
| 216 | 214 | void devm_extcon_unregister_notifier(struct device *dev, |
| 217 | 215 | struct extcon_dev *edev, unsigned int id, |
| @@ -225,9 +223,9 @@ EXPORT_SYMBOL(devm_extcon_unregister_notifier); · 7 unmodified lines | ||
| 225 | 223 | /** |
| 226 | 224 | * devm_extcon_register_notifier_all() |
| 227 | 225 | * - Resource-managed extcon_register_notifier_all() |
| 228 | − * @dev: device to allocate extcon device | |
| 229 | − * @edev: the extcon device that has the external connecotr. | |
| 230 | − * @nb: a notifier block to be registered. | |
| 226 | + * @dev: the device owning the extcon device being created | |
| 227 | + * @edev: the extcon device | |
| 228 | + * @nb: a notifier block to be registered | |
| 231 | 229 | * |
| 232 | 230 | * This function manages automatically the notifier of extcon device using |
| 233 | 231 | * device resource management and simplify the control of unregistering |
| @@ -263,9 +261,9 @@ EXPORT_SYMBOL(devm_extcon_register_notifier_all); · 29 unmodified lines | ||
| 263 | 261 | /** |
| 264 | 262 | * devm_extcon_unregister_notifier_all() |
| 265 | 263 | * - Resource-managed extcon_unregister_notifier_all() |
| 266 | − * @dev: device to allocate extcon device | |
| 267 | − * @edev: the extcon device that has the external connecotr. | |
| 268 | − * @nb: a notifier block to be registered. | |
| 264 | + * @dev: the device owning the extcon device being created | |
| 265 | + * @edev: the extcon device | |
| 266 | + * @nb: a notifier block to be registered | |
| 269 | 267 | */ |
| 270 | 268 | void devm_extcon_unregister_notifier_all(struct device *dev, |
| 271 | 269 | struct extcon_dev *edev, |
| @@ -171,7 +171,7 @@ static int int3496_remove(struct platform_device *pdev) · 170 unmodified lines | ||
| 171 | 171 | return 0; |
| 172 | 172 | } |
| 173 | 173 | |
| 174 | −static struct acpi_device_id int3496_acpi_match[] = { | |
| 174 | +static const struct acpi_device_id int3496_acpi_match[] = { | |
| 175 | 175 | { "INT3496" }, |
| 176 | 176 | { } |
| 177 | 177 | }; |
| @@ -811,9 +811,8 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info) · 810 unmodified lines | ||
| 811 | 811 | */ |
| 812 | 812 | extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP, |
| 813 | 813 | attached); |
| 814 | − if (!cable_attached) | |
| 815 | − extcon_set_state_sync(info->edev, | |
| 816 | − EXTCON_DISP_MHL, cable_attached); | |
| 814 | + extcon_set_state_sync(info->edev, EXTCON_DISP_MHL, | |
| 815 | + cable_attached); | |
| 817 | 816 | break; |
| 818 | 817 | } |
| 819 | 818 | |
| @@ -0,0 +1,417 @@ | ||
| 1 | +/** | |
| 2 | + * drivers/extcon/extcon-usbc-cros-ec - ChromeOS Embedded Controller extcon | |
| 3 | + * | |
| 4 | + * Copyright (C) 2017 Google, Inc | |
| 5 | + * Author: Benson Leung <bleung@chromium.org> | |
| 6 | + * | |
| 7 | + * This software is licensed under the terms of the GNU General Public | |
| 8 | + * License version 2, as published by the Free Software Foundation, and | |
| 9 | + * may be copied, distributed, and modified under those terms. | |
| 10 | + * | |
| 11 | + * This program is distributed in the hope that it will be useful, | |
| 12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 14 | + * GNU General Public License for more details. | |
| 15 | + */ | |
| 16 | + | |
| 17 | +#include <linux/extcon.h> | |
| 18 | +#include <linux/kernel.h> | |
| 19 | +#include <linux/mfd/cros_ec.h> | |
| 20 | +#include <linux/module.h> | |
| 21 | +#include <linux/notifier.h> | |
| 22 | +#include <linux/of.h> | |
| 23 | +#include <linux/platform_device.h> | |
| 24 | +#include <linux/slab.h> | |
| 25 | +#include <linux/sched.h> | |
| 26 | + | |
| 27 | +struct cros_ec_extcon_info { | |
| 28 | + struct device *dev; | |
| 29 | + struct extcon_dev *edev; | |
| 30 | + | |
| 31 | + int port_id; | |
| 32 | + | |
| 33 | + struct cros_ec_device *ec; | |
| 34 | + | |
| 35 | + struct notifier_block notifier; | |
| 36 | + | |
| 37 | + bool dp; /* DisplayPort enabled */ | |
| 38 | + bool mux; /* SuperSpeed (usb3) enabled */ | |
| 39 | + unsigned int power_type; | |
| 40 | +}; | |
| 41 | + | |
| 42 | +static const unsigned int usb_type_c_cable[] = { | |
| 43 | + EXTCON_DISP_DP, | |
| 44 | + EXTCON_NONE, | |
| 45 | +}; | |
| 46 | + | |
| 47 | +/** | |
| 48 | + * cros_ec_pd_command() - Send a command to the EC. | |
| 49 | + * @info: pointer to struct cros_ec_extcon_info | |
| 50 | + * @command: EC command | |
| 51 | + * @version: EC command version | |
| 52 | + * @outdata: EC command output data | |
| 53 | + * @outsize: Size of outdata | |
| 54 | + * @indata: EC command input data | |
| 55 | + * @insize: Size of indata | |
| 56 | + * | |
| 57 | + * Return: 0 on success, <0 on failure. | |
| 58 | + */ | |
| 59 | +static int cros_ec_pd_command(struct cros_ec_extcon_info *info, | |
| 60 | + unsigned int command, | |
| 61 | + unsigned int version, | |
| 62 | + void *outdata, | |
| 63 | + unsigned int outsize, | |
| 64 | + void *indata, | |
| 65 | + unsigned int insize) | |
| 66 | +{ | |
| 67 | + struct cros_ec_command *msg; | |
| 68 | + int ret; | |
| 69 | + | |
| 70 | + msg = kzalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL); | |
| 71 | + if (!msg) | |
| 72 | + return -ENOMEM; | |
| 73 | + | |
| 74 | + msg->version = version; | |
| 75 | + msg->command = command; | |
| 76 | + msg->outsize = outsize; | |
| 77 | + msg->insize = insize; | |
| 78 | + | |
| 79 | + if (outsize) | |
| 80 | + memcpy(msg->data, outdata, outsize); | |
| 81 | + | |
| 82 | + ret = cros_ec_cmd_xfer_status(info->ec, msg); | |
| 83 | + if (ret >= 0 && insize) | |
| 84 | + memcpy(indata, msg->data, insize); | |
| 85 | + | |
| 86 | + kfree(msg); | |
| 87 | + return ret; | |
| 88 | +} | |
| 89 | + | |
| 90 | +/** | |
| 91 | + * cros_ec_usb_get_power_type() - Get power type info about PD device attached | |
| 92 | + * to given port. | |
| 93 | + * @info: pointer to struct cros_ec_extcon_info | |
| 94 | + * | |
| 95 | + * Return: power type on success, <0 on failure. | |
| 96 | + */ | |
| 97 | +static int cros_ec_usb_get_power_type(struct cros_ec_extcon_info *info) | |
| 98 | +{ | |
| 99 | + struct ec_params_usb_pd_power_info req; | |
| 100 | + struct ec_response_usb_pd_power_info resp; | |
| 101 | + int ret; | |
| 102 | + | |
| 103 | + req.port = info->port_id; | |
| 104 | + ret = cros_ec_pd_command(info, EC_CMD_USB_PD_POWER_INFO, 0, | |
| 105 | + &req, sizeof(req), &resp, sizeof(resp)); | |
| 106 | + if (ret < 0) | |
| 107 | + return ret; | |
| 108 | + | |
| 109 | + return resp.type; | |
| 110 | +} | |
| 111 | + | |
| 112 | +/** | |
| 113 | + * cros_ec_usb_get_pd_mux_state() - Get PD mux state for given port. | |
| 114 | + * @info: pointer to struct cros_ec_extcon_info | |
| 115 | + * | |
| 116 | + * Return: PD mux state on success, <0 on failure. | |
| 117 | + */ | |
| 118 | +static int cros_ec_usb_get_pd_mux_state(struct cros_ec_extcon_info *info) | |
| 119 | +{ | |
| 120 | + struct ec_params_usb_pd_mux_info req; | |
| 121 | + struct ec_response_usb_pd_mux_info resp; | |
| 122 | + int ret; | |
| 123 | + | |
| 124 | + req.port = info->port_id; | |
| 125 | + ret = cros_ec_pd_command(info, EC_CMD_USB_PD_MUX_INFO, 0, | |
| 126 | + &req, sizeof(req), | |
| 127 | + &resp, sizeof(resp)); | |
| 128 | + if (ret < 0) | |
| 129 | + return ret; | |
| 130 | + | |
| 131 | + return resp.flags; | |
| 132 | +} | |
| 133 | + | |
| 134 | +/** | |
| 135 | + * cros_ec_usb_get_role() - Get role info about possible PD device attached to a | |
| 136 | + * given port. | |
| 137 | + * @info: pointer to struct cros_ec_extcon_info | |
| 138 | + * @polarity: pointer to cable polarity (return value) | |
| 139 | + * | |
| 140 | + * Return: role info on success, -ENOTCONN if no cable is connected, <0 on | |
| 141 | + * failure. | |
| 142 | + */ | |
| 143 | +static int cros_ec_usb_get_role(struct cros_ec_extcon_info *info, | |
| 144 | + bool *polarity) | |
| 145 | +{ | |
| 146 | + struct ec_params_usb_pd_control pd_control; | |
| 147 | + struct ec_response_usb_pd_control_v1 resp; | |
| 148 | + int ret; | |
| 149 | + | |
| 150 | + pd_control.port = info->port_id; | |
| 151 | + pd_control.role = USB_PD_CTRL_ROLE_NO_CHANGE; | |
| 152 | + pd_control.mux = USB_PD_CTRL_MUX_NO_CHANGE; | |
| 153 | + ret = cros_ec_pd_command(info, EC_CMD_USB_PD_CONTROL, 1, | |
| 154 | + &pd_control, sizeof(pd_control), | |
| 155 | + &resp, sizeof(resp)); | |
| 156 | + if (ret < 0) | |
| 157 | + return ret; | |
| 158 | + | |
| 159 | + if (!(resp.enabled & PD_CTRL_RESP_ENABLED_CONNECTED)) | |
| 160 | + return -ENOTCONN; | |
| 161 | + | |
| 162 | + *polarity = resp.polarity; | |
| 163 | + | |
| 164 | + return resp.role; | |
| 165 | +} | |
| 166 | + | |
| 167 | +/** | |
| 168 | + * cros_ec_pd_get_num_ports() - Get number of EC charge ports. | |
| 169 | + * @info: pointer to struct cros_ec_extcon_info | |
| 170 | + * | |
| 171 | + * Return: number of ports on success, <0 on failure. | |
| 172 | + */ | |
| 173 | +static int cros_ec_pd_get_num_ports(struct cros_ec_extcon_info *info) | |
| 174 | +{ | |
| 175 | + struct ec_response_usb_pd_ports resp; | |
| 176 | + int ret; | |
| 177 | + | |
| 178 | + ret = cros_ec_pd_command(info, EC_CMD_USB_PD_PORTS, | |
| 179 | + 0, NULL, 0, &resp, sizeof(resp)); | |
| 180 | + if (ret < 0) | |
| 181 | + return ret; | |
| 182 | + | |
| 183 | + return resp.num_ports; | |
| 184 | +} | |
| 185 | + | |
| 186 | +static int extcon_cros_ec_detect_cable(struct cros_ec_extcon_info *info, | |
| 187 | + bool force) | |
| 188 | +{ | |
| 189 | + struct device *dev = info->dev; | |
| 190 | + int role, power_type; | |
| 191 | + bool polarity = false; | |
| 192 | + bool dp = false; | |
| 193 | + bool mux = false; | |
| 194 | + bool hpd = false; | |
| 195 | + | |
| 196 | + power_type = cros_ec_usb_get_power_type(info); | |
| 197 | + if (power_type < 0) { | |
| 198 | + dev_err(dev, "failed getting power type err = %d\n", | |
| 199 | + power_type); | |
| 200 | + return power_type; | |
| 201 | + } | |
| 202 | + | |
| 203 | + role = cros_ec_usb_get_role(info, &polarity); | |
| 204 | + if (role < 0) { | |
| 205 | + if (role != -ENOTCONN) { | |
| 206 | + dev_err(dev, "failed getting role err = %d\n", role); | |
| 207 | + return role; | |
| 208 | + } | |
| 209 | + } else { | |
| 210 | + int pd_mux_state; | |
| 211 | + | |
| 212 | + pd_mux_state = cros_ec_usb_get_pd_mux_state(info); | |
| 213 | + if (pd_mux_state < 0) | |
| 214 | + pd_mux_state = USB_PD_MUX_USB_ENABLED; | |
| 215 | + | |
| 216 | + dp = pd_mux_state & USB_PD_MUX_DP_ENABLED; | |
| 217 | + mux = pd_mux_state & USB_PD_MUX_USB_ENABLED; | |
| 218 | + hpd = pd_mux_state & USB_PD_MUX_HPD_IRQ; | |
| 219 | + } | |
| 220 | + | |
| 221 | + if (force || info->dp != dp || info->mux != mux || | |
| 222 | + info->power_type != power_type) { | |
| 223 | + | |
| 224 | + info->dp = dp; | |
| 225 | + info->mux = mux; | |
| 226 | + info->power_type = power_type; | |
| 227 | + | |
| 228 | + extcon_set_state(info->edev, EXTCON_DISP_DP, dp); | |
| 229 | + | |
| 230 | + extcon_set_property(info->edev, EXTCON_DISP_DP, | |
| 231 | + EXTCON_PROP_USB_TYPEC_POLARITY, | |
| 232 | + (union extcon_property_value)(int)polarity); | |
| 233 | + extcon_set_property(info->edev, EXTCON_DISP_DP, | |
| 234 | + EXTCON_PROP_USB_SS, | |
| 235 | + (union extcon_property_value)(int)mux); | |
| 236 | + extcon_set_property(info->edev, EXTCON_DISP_DP, | |
| 237 | + EXTCON_PROP_DISP_HPD, | |
| 238 | + (union extcon_property_value)(int)hpd); | |
| 239 | + | |
| 240 | + extcon_sync(info->edev, EXTCON_DISP_DP); | |
| 241 | + | |
| 242 | + } else if (hpd) { | |
| 243 | + extcon_set_property(info->edev, EXTCON_DISP_DP, | |
| 244 | + EXTCON_PROP_DISP_HPD, | |
| 245 | + (union extcon_property_value)(int)hpd); | |
| 246 | + extcon_sync(info->edev, EXTCON_DISP_DP); | |
| 247 | + } | |
| 248 | + | |
| 249 | + return 0; | |
| 250 | +} | |
| 251 | + | |
| 252 | +static int extcon_cros_ec_event(struct notifier_block *nb, | |
| 253 | + unsigned long queued_during_suspend, | |
| 254 | + void *_notify) | |
| 255 | +{ | |
| 256 | + struct cros_ec_extcon_info *info; | |
| 257 | + struct cros_ec_device *ec; | |
| 258 | + u32 host_event; | |
| 259 | + | |
| 260 | + info = container_of(nb, struct cros_ec_extcon_info, notifier); | |
| 261 | + ec = info->ec; | |
| 262 | + | |
| 263 | + host_event = cros_ec_get_host_event(ec); | |
| 264 | + if (host_event & (EC_HOST_EVENT_MASK(EC_HOST_EVENT_PD_MCU) | | |
| 265 | + EC_HOST_EVENT_MASK(EC_HOST_EVENT_USB_MUX))) { | |
| 266 | + extcon_cros_ec_detect_cable(info, false); | |
| 267 | + return NOTIFY_OK; | |
| 268 | + } | |
| 269 | + | |
| 270 | + return NOTIFY_DONE; | |
| 271 | +} | |
| 272 | + | |
| 273 | +static int extcon_cros_ec_probe(struct platform_device *pdev) | |
| 274 | +{ | |
| 275 | + struct cros_ec_extcon_info *info; | |
| 276 | + struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); | |
| 277 | + struct device *dev = &pdev->dev; | |
| 278 | + struct device_node *np = dev->of_node; | |
| 279 | + int numports, ret; | |
| 280 | + | |
| 281 | + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); | |
| 282 | + if (!info) | |
| 283 | + return -ENOMEM; | |
| 284 | + | |
| 285 | + info->dev = dev; | |
| 286 | + info->ec = ec; | |
| 287 | + | |
| 288 | + if (np) { | |
| 289 | + u32 port; | |
| 290 | + | |
| 291 | + ret = of_property_read_u32(np, "google,usb-port-id", &port); | |
| 292 | + if (ret < 0) { | |
| 293 | + dev_err(dev, "Missing google,usb-port-id property\n"); | |
| 294 | + return ret; | |
| 295 | + } | |
| 296 | + info->port_id = port; | |
| 297 | + } else { | |
| 298 | + info->port_id = pdev->id; | |
| 299 | + } | |
| 300 | + | |
| 301 | + numports = cros_ec_pd_get_num_ports(info); | |
| 302 | + if (numports < 0) { | |
| 303 | + dev_err(dev, "failed getting number of ports! ret = %d\n", | |
| 304 | + numports); | |
| 305 | + return numports; | |
| 306 | + } | |
| 307 | + | |
| 308 | + if (info->port_id >= numports) { | |
| 309 | + dev_err(dev, "This system only supports %d ports\n", numports); | |
| 310 | + return -ENODEV; | |
| 311 | + } | |
| 312 | + | |
| 313 | + info->edev = devm_extcon_dev_allocate(dev, usb_type_c_cable); | |
| 314 | + if (IS_ERR(info->edev)) { | |
| 315 | + dev_err(dev, "failed to allocate extcon device\n"); | |
| 316 | + return -ENOMEM; | |
| 317 | + } | |
| 318 | + | |
| 319 | + ret = devm_extcon_dev_register(dev, info->edev); | |
| 320 | + if (ret < 0) { | |
| 321 | + dev_err(dev, "failed to register extcon device\n"); | |
| 322 | + return ret; | |
| 323 | + } | |
| 324 | + | |
| 325 | + extcon_set_property_capability(info->edev, EXTCON_DISP_DP, | |
| 326 | + EXTCON_PROP_USB_TYPEC_POLARITY); | |
| 327 | + extcon_set_property_capability(info->edev, EXTCON_DISP_DP, | |
| 328 | + EXTCON_PROP_USB_SS); | |
| 329 | + extcon_set_property_capability(info->edev, EXTCON_DISP_DP, | |
| 330 | + EXTCON_PROP_DISP_HPD); | |
| 331 | + | |
| 332 | + platform_set_drvdata(pdev, info); | |
| 333 | + | |
| 334 | + /* Get PD events from the EC */ | |
| 335 | + info->notifier.notifier_call = extcon_cros_ec_event; | |
| 336 | + ret = blocking_notifier_chain_register(&info->ec->event_notifier, | |
| 337 | + &info->notifier); | |
| 338 | + if (ret < 0) { | |
| 339 | + dev_err(dev, "failed to register notifier\n"); | |
| 340 | + return ret; | |
| 341 | + } | |
| 342 | + | |
| 343 | + /* Perform initial detection */ | |
| 344 | + ret = extcon_cros_ec_detect_cable(info, true); | |
| 345 | + if (ret < 0) { | |
| 346 | + dev_err(dev, "failed to detect initial cable state\n"); | |
| 347 | + goto unregister_notifier; | |
| 348 | + } | |
| 349 | + | |
| 350 | + return 0; | |
| 351 | + | |
| 352 | +unregister_notifier: | |
| 353 | + blocking_notifier_chain_unregister(&info->ec->event_notifier, | |
| 354 | + &info->notifier); | |
| 355 | + return ret; | |
| 356 | +} | |
| 357 | + | |
| 358 | +static int extcon_cros_ec_remove(struct platform_device *pdev) | |
| 359 | +{ | |
| 360 | + struct cros_ec_extcon_info *info = platform_get_drvdata(pdev); | |
| 361 | + | |
| 362 | + blocking_notifier_chain_unregister(&info->ec->event_notifier, | |
| 363 | + &info->notifier); | |
| 364 | + | |
| 365 | + return 0; | |
| 366 | +} | |
| 367 | + | |
| 368 | +#ifdef CONFIG_PM_SLEEP | |
| 369 | +static int extcon_cros_ec_suspend(struct device *dev) | |
| 370 | +{ | |
| 371 | + return 0; | |
| 372 | +} | |
| 373 | + | |
| 374 | +static int extcon_cros_ec_resume(struct device *dev) | |
| 375 | +{ | |
| 376 | + int ret; | |
| 377 | + struct cros_ec_extcon_info *info = dev_get_drvdata(dev); | |
| 378 | + | |
| 379 | + ret = extcon_cros_ec_detect_cable(info, true); | |
| 380 | + if (ret < 0) | |
| 381 | + dev_err(dev, "failed to detect cable state on resume\n"); | |
| 382 | + | |
| 383 | + return 0; | |
| 384 | +} | |
| 385 | + | |
| 386 | +static const struct dev_pm_ops extcon_cros_ec_dev_pm_ops = { | |
| 387 | + SET_SYSTEM_SLEEP_PM_OPS(extcon_cros_ec_suspend, extcon_cros_ec_resume) | |
| 388 | +}; | |
| 389 | + | |
| 390 | +#define DEV_PM_OPS (&extcon_cros_ec_dev_pm_ops) | |
| 391 | +#else | |
| 392 | +#define DEV_PM_OPS NULL | |
| 393 | +#endif /* CONFIG_PM_SLEEP */ | |
| 394 | + | |
| 395 | +#ifdef CONFIG_OF | |
| 396 | +static const struct of_device_id extcon_cros_ec_of_match[] = { | |
| 397 | + { .compatible = "google,extcon-usbc-cros-ec" }, | |
| 398 | + { /* sentinel */ } | |
| 399 | +}; | |
| 400 | +MODULE_DEVICE_TABLE(of, extcon_cros_ec_of_match); | |
| 401 | +#endif /* CONFIG_OF */ | |
| 402 | + | |
| 403 | +static struct platform_driver extcon_cros_ec_driver = { | |
| 404 | + .driver = { | |
| 405 | + .name = "extcon-usbc-cros-ec", | |
| 406 | + .of_match_table = of_match_ptr(extcon_cros_ec_of_match), | |
| 407 | + .pm = DEV_PM_OPS, | |
| 408 | + }, | |
| 409 | + .remove = extcon_cros_ec_remove, | |
| 410 | + .probe = extcon_cros_ec_probe, | |
| 411 | +}; | |
| 412 | + | |
| 413 | +module_platform_driver(extcon_cros_ec_driver); | |
| 414 | + | |
| 415 | +MODULE_DESCRIPTION("ChromeOS Embedded Controller extcon driver"); | |
| 416 | +MODULE_AUTHOR("Benson Leung <bleung@chromium.org>"); | |
| 417 | +MODULE_LICENSE("GPL"); | |
| @@ -1,7 +1,5 @@ | ||
| 1 | 1 | /* |
| 2 | − * drivers/extcon/extcon.c - External Connector (extcon) framework. | |
| 3 | − * | |
| 4 | − * External connector (extcon) class driver | |
| 2 | + * drivers/extcon/extcon.c - External Connector (extcon) framework. | |
| 5 | 3 | * |
| 6 | 4 | * Copyright (C) 2015 Samsung Electronics |
| 7 | 5 | * Author: Chanwoo Choi <cw00.choi@samsung.com> |
| @@ -37,7 +35,6 @@ · 29 unmodified lines | ||
| 37 | 35 | #include "extcon.h" |
| 38 | 36 | |
| 39 | 37 | #define SUPPORTED_CABLE_MAX 32 |
| 40 | −#define CABLE_NAME_MAX 30 | |
| 41 | 38 | |
| 42 | 39 | struct __extcon_info { |
| 43 | 40 | unsigned int type; |
| @@ -200,13 +197,13 @@ struct __extcon_info { · 156 unmodified lines | ||
| 200 | 197 | }; |
| 201 | 198 | |
| 202 | 199 | /** |
| 203 | − * struct extcon_cable - An internal data for each cable of extcon device. | |
| 204 | − * @edev: The extcon device | |
| 205 | − * @cable_index: Index of this cable in the edev | |
| 206 | − * @attr_g: Attribute group for the cable | |
| 200 | + * struct extcon_cable - An internal data for an external connector. | |
| 201 | + * @edev: the extcon device | |
| 202 | + * @cable_index: the index of this cable in the edev | |
| 203 | + * @attr_g: the attribute group for the cable | |
| 207 | 204 | * @attr_name: "name" sysfs entry |
| 208 | 205 | * @attr_state: "state" sysfs entry |
| 209 | − * @attrs: Array pointing to attr_name and attr_state for attr_g | |
| 206 | + * @attrs: the array pointing to attr_name and attr_state for attr_g | |
| 210 | 207 | */ |
| 211 | 208 | struct extcon_cable { |
| 212 | 209 | struct extcon_dev *edev; |
| @@ -234,15 +231,6 @@ static struct class *extcon_class; · 21 unmodified lines | ||
| 234 | 231 | static LIST_HEAD(extcon_dev_list); |
| 235 | 232 | static DEFINE_MUTEX(extcon_dev_list_lock); |
| 236 | 233 | |
| 237 | −/** | |
| 238 | − * check_mutually_exclusive - Check if new_state violates mutually_exclusive | |
| 239 | − * condition. | |
| 240 | − * @edev: the extcon device | |
| 241 | − * @new_state: new cable attach status for @edev | |
| 242 | − * | |
| 243 | − * Returns 0 if nothing violates. Returns the index + 1 for the first | |
| 244 | − * violated condition. | |
| 245 | − */ | |
| 246 | 234 | static int check_mutually_exclusive(struct extcon_dev *edev, u32 new_state) |
| 247 | 235 | { |
| 248 | 236 | int i = 0; |
| @@ -417,11 +405,13 @@ static ssize_t cable_state_show(struct device *dev, · 168 unmodified lines | ||
| 417 | 405 | } |
| 418 | 406 | |
| 419 | 407 | /** |
| 420 | − * extcon_sync() - Synchronize the states for both the attached/detached | |
| 421 | − * @edev: the extcon device that has the cable. | |
| 408 | + * extcon_sync() - Synchronize the state for an external connector. | |
| 409 | + * @edev: the extcon device | |
| 410 | + * | |
| 411 | + * Note that this function send a notification in order to synchronize | |
| 412 | + * the state and property of an external connector. | |
| 422 | 413 | * |
| 423 | − * This function send a notification to synchronize the all states of a | |
| 424 | − * specific external connector | |
| 414 | + * Returns 0 if success or error number if fail. | |
| 425 | 415 | */ |
| 426 | 416 | int extcon_sync(struct extcon_dev *edev, unsigned int id) |
| 427 | 417 | { |
| @@ -497,9 +487,11 @@ int extcon_sync(struct extcon_dev *edev, unsigned int id) · 69 unmodified lines | ||
| 497 | 487 | EXPORT_SYMBOL_GPL(extcon_sync); |
| 498 | 488 | |
| 499 | 489 | /** |
| 500 | − * extcon_get_state() - Get the state of a external connector. | |
| 501 | − * @edev: the extcon device that has the cable. | |
| 502 | − * @id: the unique id of each external connector in extcon enumeration. | |
| 490 | + * extcon_get_state() - Get the state of an external connector. | |
| 491 | + * @edev: the extcon device | |
| 492 | + * @id: the unique id indicating an external connector | |
| 493 | + * | |
| 494 | + * Returns 0 if success or error number if fail. | |
| 503 | 495 | */ |
| 504 | 496 | int extcon_get_state(struct extcon_dev *edev, const unsigned int id) |
| 505 | 497 | { |
| @@ -522,20 +514,19 @@ int extcon_get_state(struct extcon_dev *edev, const unsigned int id) · 16 unmodified lines | ||
| 522 | 514 | EXPORT_SYMBOL_GPL(extcon_get_state); |
| 523 | 515 | |
| 524 | 516 | /** |
| 525 | − * extcon_set_state() - Set the state of a external connector. | |
| 526 | − * without a notification. | |
| 527 | − * @edev: the extcon device that has the cable. | |
| 528 | − * @id: the unique id of each external connector | |
| 529 | − * in extcon enumeration. | |
| 530 | − * @state: the new cable status. The default semantics is | |
| 531 | − * true: attached / false: detached. | |
| 517 | + * extcon_set_state() - Set the state of an external connector. | |
| 518 | + * @edev: the extcon device | |
| 519 | + * @id: the unique id indicating an external connector | |
| 520 | + * @state: the new state of an external connector. | |
| 521 | + * the default semantics is true: attached / false: detached. | |
| 522 | + * | |
| 523 | + * Note that this function set the state of an external connector without | |
| 524 | + * a notification. To synchronize the state of an external connector, | |
| 525 | + * have to use extcon_set_state_sync() and extcon_sync(). | |
| 532 | 526 | * |
| 533 | − * This function only set the state of a external connector without | |
| 534 | − * a notification. To synchronize the data of a external connector, | |
| 535 | − * use extcon_set_state_sync() and extcon_sync(). | |
| 527 | + * Returns 0 if success or error number if fail. | |
| 536 | 528 | */ |
| 537 | −int extcon_set_state(struct extcon_dev *edev, unsigned int id, | |
| 538 | − bool cable_state) | |
| 529 | +int extcon_set_state(struct extcon_dev *edev, unsigned int id, bool state) | |
| 539 | 530 | { |
| 540 | 531 | unsigned long flags; |
| 541 | 532 | int index, ret = 0; |
| @@ -550,11 +541,11 @@ int extcon_set_state(struct extcon_dev *edev, unsigned int id, · 8 unmodified lines | ||
| 550 | 541 | spin_lock_irqsave(&edev->lock, flags); |
| 551 | 542 | |
| 552 | 543 | /* Check whether the external connector's state is changed. */ |
| 553 | − if (!is_extcon_changed(edev, index, cable_state)) | |
| 544 | + if (!is_extcon_changed(edev, index, state)) | |
| 554 | 545 | goto out; |
| 555 | 546 | |
| 556 | 547 | if (check_mutually_exclusive(edev, |
| 557 | − (edev->state & ~BIT(index)) | (cable_state & BIT(index)))) { | |
| 548 | + (edev->state & ~BIT(index)) | (state & BIT(index)))) { | |
| 558 | 549 | ret = -EPERM; |
| 559 | 550 | goto out; |
| 560 | 551 | } |
| @@ -563,11 +554,11 @@ int extcon_set_state(struct extcon_dev *edev, unsigned int id, · 2 unmodified lines | ||
| 563 | 554 | * Initialize the value of extcon property before setting |
| 564 | 555 | * the detached state for an external connector. |
| 565 | 556 | */ |
| 566 | − if (!cable_state) | |
| 557 | + if (!state) | |
| 567 | 558 | init_property(edev, id, index); |
| 568 | 559 | |
| 569 | − /* Update the state for a external connector. */ | |
| 570 | − if (cable_state) | |
| 560 | + /* Update the state for an external connector. */ | |
| 561 | + if (state) | |
| 571 | 562 | edev->state |= BIT(index); |
| 572 | 563 | else |
| 573 | 564 | edev->state &= ~(BIT(index)); |
| @@ -579,19 +570,18 @@ out: · 5 unmodified lines | ||
| 579 | 570 | EXPORT_SYMBOL_GPL(extcon_set_state); |
| 580 | 571 | |
| 581 | 572 | /** |
| 582 | − * extcon_set_state_sync() - Set the state of a external connector | |
| 583 | − * with a notification. | |
| 584 | − * @edev: the extcon device that has the cable. | |
| 585 | − * @id: the unique id of each external connector | |
| 586 | − * in extcon enumeration. | |
| 587 | − * @state: the new cable status. The default semantics is | |
| 588 | − * true: attached / false: detached. | |
| 573 | + * extcon_set_state_sync() - Set the state of an external connector with sync. | |
| 574 | + * @edev: the extcon device | |
| 575 | + * @id: the unique id indicating an external connector | |
| 576 | + * @state: the new state of external connector. | |
| 577 | + * the default semantics is true: attached / false: detached. | |
| 578 | + * | |
| 579 | + * Note that this function set the state of external connector | |
| 580 | + * and synchronize the state by sending a notification. | |
| 589 | 581 | * |
| 590 | − * This function set the state of external connector and synchronize the data | |
| 591 | − * by usning a notification. | |
| 582 | + * Returns 0 if success or error number if fail. | |
| 592 | 583 | */ |
| 593 | −int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id, | |
| 594 | − bool cable_state) | |
| 584 | +int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id, bool state) | |
| 595 | 585 | { |
| 596 | 586 | int ret, index; |
| 597 | 587 | unsigned long flags; |
| @@ -602,12 +592,12 @@ int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id, · 4 unmodified lines | ||
| 602 | 592 | |
| 603 | 593 | /* Check whether the external connector's state is changed. */ |
| 604 | 594 | spin_lock_irqsave(&edev->lock, flags); |
| 605 | − ret = is_extcon_changed(edev, index, cable_state); | |
| 595 | + ret = is_extcon_changed(edev, index, state); | |
| 606 | 596 | spin_unlock_irqrestore(&edev->lock, flags); |
| 607 | 597 | if (!ret) |
| 608 | 598 | return 0; |
| 609 | 599 | |
| 610 | − ret = extcon_set_state(edev, id, cable_state); | |
| 600 | + ret = extcon_set_state(edev, id, state); | |
| 611 | 601 | if (ret < 0) |
| 612 | 602 | return ret; |
| 613 | 603 | |
| @@ -616,19 +606,18 @@ int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id, · 2 unmodified lines | ||
| 616 | 606 | EXPORT_SYMBOL_GPL(extcon_set_state_sync); |
| 617 | 607 | |
| 618 | 608 | /** |
| 619 | − * extcon_get_property() - Get the property value of a specific cable. | |
| 620 | − * @edev: the extcon device that has the cable. | |
| 621 | − * @id: the unique id of each external connector | |
| 622 | − * in extcon enumeration. | |
| 623 | − * @prop: the property id among enum extcon_property. | |
| 624 | − * @prop_val: the pointer which store the value of property. | |
| 609 | + * extcon_get_property() - Get the property value of an external connector. | |
| 610 | + * @edev: the extcon device | |
| 611 | + * @id: the unique id indicating an external connector | |
| 612 | + * @prop: the property id indicating an extcon property | |
| 613 | + * @prop_val: the pointer which store the value of extcon property | |
| 625 | 614 | * |
| 626 | − * When getting the property value of external connector, the external connector | |
| 627 | − * should be attached. If detached state, function just return 0 without | |
| 628 | − * property value. Also, the each property should be included in the list of | |
| 629 | − * supported properties according to the type of external connectors. | |
| 615 | + * Note that when getting the property value of external connector, | |
| 616 | + * the external connector should be attached. If detached state, function | |
| 617 | + * return 0 without property value. Also, the each property should be | |
| 618 | + * included in the list of supported properties according to extcon type. | |
| 630 | 619 | * |
| 631 | − * Returns 0 if success or error number if fail | |
| 620 | + * Returns 0 if success or error number if fail. | |
| 632 | 621 | */ |
| 633 | 622 | int extcon_get_property(struct extcon_dev *edev, unsigned int id, |
| 634 | 623 | unsigned int prop, |
| @@ -698,17 +687,16 @@ int extcon_get_property(struct extcon_dev *edev, unsigned int id, · 63 unmodified lines | ||
| 698 | 687 | EXPORT_SYMBOL_GPL(extcon_get_property); |
| 699 | 688 | |
| 700 | 689 | /** |
| 701 | − * extcon_set_property() - Set the property value of a specific cable. | |
| 702 | − * @edev: the extcon device that has the cable. | |
| 703 | − * @id: the unique id of each external connector | |
| 704 | − * in extcon enumeration. | |
| 705 | − * @prop: the property id among enum extcon_property. | |
| 706 | − * @prop_val: the pointer including the new value of property. | |
| 690 | + * extcon_set_property() - Set the property value of an external connector. | |
| 691 | + * @edev: the extcon device | |
| 692 | + * @id: the unique id indicating an external connector | |
| 693 | + * @prop: the property id indicating an extcon property | |
| 694 | + * @prop_val: the pointer including the new value of extcon property | |
| 707 | 695 | * |
| 708 | − * The each property should be included in the list of supported properties | |
| 709 | − * according to the type of external connectors. | |
| 696 | + * Note that each property should be included in the list of supported | |
| 697 | + * properties according to the extcon type. | |
| 710 | 698 | * |
| 711 | − * Returns 0 if success or error number if fail | |
| 699 | + * Returns 0 if success or error number if fail. | |
| 712 | 700 | */ |
| 713 | 701 | int extcon_set_property(struct extcon_dev *edev, unsigned int id, |
| 714 | 702 | unsigned int prop, |
| @@ -766,15 +754,14 @@ int extcon_set_property(struct extcon_dev *edev, unsigned int id, · 51 unmodified lines | ||
| 766 | 754 | EXPORT_SYMBOL_GPL(extcon_set_property); |
| 767 | 755 | |
| 768 | 756 | /** |
| 769 | − * extcon_set_property_sync() - Set the property value of a specific cable | |
| 770 | − with a notification. | |
| 771 | − * @prop_val: the pointer including the new value of property. | |
| 757 | + * extcon_set_property_sync() - Set property of an external connector with sync. | |
| 758 | + * @prop_val: the pointer including the new value of extcon property | |
| 772 | 759 | * |
| 773 | − * When setting the property value of external connector, the external connector | |
| 774 | − * should be attached. The each property should be included in the list of | |
| 775 | − * supported properties according to the type of external connectors. | |
| 760 | + * Note that when setting the property value of external connector, | |
| 761 | + * the external connector should be attached. The each property should | |
| 762 | + * be included in the list of supported properties according to extcon type. | |
| 776 | 763 | * |
| 777 | − * Returns 0 if success or error number if fail | |
| 764 | + * Returns 0 if success or error number if fail. | |
| 778 | 765 | */ |
| 779 | 766 | int extcon_set_property_sync(struct extcon_dev *edev, unsigned int id, |
| 780 | 767 | unsigned int prop, |
| @@ -791,12 +778,11 @@ int extcon_set_property_sync(struct extcon_dev *edev, unsigned int id, · 10 unmodified lines | ||
| 791 | 778 | EXPORT_SYMBOL_GPL(extcon_set_property_sync); |
| 792 | 779 | |
| 793 | 780 | /** |
| 794 | − * extcon_get_property_capability() - Get the capability of property | |
| 795 | − * of an external connector. | |
| 796 | − * @edev: the extcon device that has the cable. | |
| 797 | − * @id: the unique id of each external connector | |
| 798 | − * in extcon enumeration. | |
| 799 | − * @prop: the property id among enum extcon_property. | |
| 781 | + * extcon_get_property_capability() - Get the capability of the property | |
| 782 | + * for an external connector. | |
| 783 | + * @edev: the extcon device | |
| 784 | + * @id: the unique id indicating an external connector | |
| 785 | + * @prop: the property id indicating an extcon property | |
| 800 | 786 | * |
| 801 | 787 | * Returns 1 if the property is available or 0 if not available. |
| 802 | 788 | */ |
| @@ -822,18 +808,17 @@ int extcon_get_property_capability(struct extcon_dev *edev, unsigned int id, · 19 unmodified lines | ||
| 822 | 808 | EXPORT_SYMBOL_GPL(extcon_get_property_capability); |
| 823 | 809 | |
| 824 | 810 | /** |
| 825 | − * extcon_set_property_capability() - Set the capability of a property | |
| 826 | − * of an external connector. | |
| 827 | − * @edev: the extcon device that has the cable. | |
| 828 | − * @id: the unique id of each external connector | |
| 829 | − * in extcon enumeration. | |
| 830 | − * @prop: the property id among enum extcon_property. | |
| 811 | + * extcon_set_property_capability() - Set the capability of the property | |
| 812 | + * for an external connector. | |
| 813 | + * @edev: the extcon device | |
| 814 | + * @id: the unique id indicating an external connector | |
| 815 | + * @prop: the property id indicating an extcon property | |
| 831 | 816 | * |
| 832 | − * This function set the capability of a property for an external connector | |
| 833 | − * to mark the bit in capability bitmap which mean the available state of | |
| 834 | − * a property. | |
| 817 | + * Note that this function set the capability of the property | |
| 818 | + * for an external connector in order to mark the bit in capability | |
| 819 | + * bitmap which mean the available state of the property. | |
| 835 | 820 | * |
| 836 | − * Returns 0 if success or error number if fail | |
| 821 | + * Returns 0 if success or error number if fail. | |
| 837 | 822 | */ |
| 838 | 823 | int extcon_set_property_capability(struct extcon_dev *edev, unsigned int id, |
| 839 | 824 | unsigned int prop) |
| @@ -881,8 +866,10 @@ int extcon_set_property_capability(struct extcon_dev *edev, unsigned int id, · 41 unmodified lines | ||
| 881 | 866 | EXPORT_SYMBOL_GPL(extcon_set_property_capability); |
| 882 | 867 | |
| 883 | 868 | /** |
| 884 | − * extcon_get_extcon_dev() - Get the extcon device instance from the name | |
| 885 | − * @extcon_name: The extcon name provided with extcon_dev_register() | |
| 869 | + * extcon_get_extcon_dev() - Get the extcon device instance from the name. | |
| 870 | + * @extcon_name: the extcon name provided with extcon_dev_register() | |
| 871 | + * | |
| 872 | + * Return the pointer of extcon device if success or ERR_PTR(err) if fail. | |
| 886 | 873 | */ |
| 887 | 874 | struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name) |
| 888 | 875 | { |
| @@ -904,15 +891,17 @@ out: · 15 unmodified lines | ||
| 904 | 891 | EXPORT_SYMBOL_GPL(extcon_get_extcon_dev); |
| 905 | 892 | |
| 906 | 893 | /** |
| 907 | − * extcon_register_notifier() - Register a notifiee to get notified by | |
| 908 | − * any attach status changes from the extcon. | |
| 909 | − * @edev: the extcon device that has the external connecotr. | |
| 910 | − * @id: the unique id of each external connector in extcon enumeration. | |
| 911 | − * @nb: a notifier block to be registered. | |
| 894 | + * extcon_register_notifier() - Register a notifier block to get notified by | |
| 895 | + * any state changes from the extcon. | |
| 896 | + * @edev: the extcon device | |
| 897 | + * @id: the unique id indicating an external connector | |
| 898 | + * @nb: a notifier block to be registered | |
| 912 | 899 | * |
| 913 | 900 | * Note that the second parameter given to the callback of nb (val) is |
| 914 | − * "old_state", not the current state. The current state can be retrieved | |
| 915 | − * by looking at the third pameter (edev pointer)'s state value. | |
| 901 | + * the current state of an external connector and the third pameter | |
| 902 | + * is the pointer of extcon device. | |
| 903 | + * | |
| 904 | + * Returns 0 if success or error number if fail. | |
| 916 | 905 | */ |
| 917 | 906 | int extcon_register_notifier(struct extcon_dev *edev, unsigned int id, |
| 918 | 907 | struct notifier_block *nb) |
| @@ -936,10 +925,12 @@ int extcon_register_notifier(struct extcon_dev *edev, unsigned int id, · 17 unmodified lines | ||
| 936 | 925 | EXPORT_SYMBOL_GPL(extcon_register_notifier); |
| 937 | 926 | |
| 938 | 927 | /** |
| 939 | − * extcon_unregister_notifier() - Unregister a notifiee from the extcon device. | |
| 940 | − * @edev: the extcon device that has the external connecotr. | |
| 941 | − * @id: the unique id of each external connector in extcon enumeration. | |
| 942 | − * @nb: a notifier block to be registered. | |
| 928 | + * extcon_unregister_notifier() - Unregister a notifier block from the extcon. | |
| 929 | + * @edev: the extcon device | |
| 930 | + * @id: the unique id indicating an external connector | |
| 931 | + * @nb: a notifier block to be registered | |
| 932 | + * | |
| 933 | + * Returns 0 if success or error number if fail. | |
| 943 | 934 | */ |
| 944 | 935 | int extcon_unregister_notifier(struct extcon_dev *edev, unsigned int id, |
| 945 | 936 | struct notifier_block *nb) |
| @@ -963,16 +954,16 @@ int extcon_unregister_notifier(struct extcon_dev *edev, unsigned int id, · 17 unmodified lines | ||
| 963 | 954 | EXPORT_SYMBOL_GPL(extcon_unregister_notifier); |
| 964 | 955 | |
| 965 | 956 | /** |
| 966 | − * extcon_register_notifier_all() - Register a notifier block for all connectors | |
| 967 | − * @edev: the extcon device that has the external connector. | |
| 968 | − * @nb: a notifier block to be registered. | |
| 957 | + * extcon_register_notifier_all() - Register a notifier block for all connectors. | |
| 958 | + * @edev: the extcon device | |
| 959 | + * @nb: a notifier block to be registered | |
| 969 | 960 | * |
| 970 | − * This function registers a notifier block in order to receive the state | |
| 971 | − * change of all supported external connectors from extcon device. | |
| 961 | + * Note that this function registers a notifier block in order to receive | |
| 962 | + * the state change of all supported external connectors from extcon device. | |
| 972 | 963 | * And the second parameter given to the callback of nb (val) is |
| 973 | − * the current state and third parameter is the edev pointer. | |
| 964 | + * the current state and the third pameter is the pointer of extcon device. | |
| 974 | 965 | * |
| 975 | − * Returns 0 if success or error number if fail | |
| 966 | + * Returns 0 if success or error number if fail. | |
| 976 | 967 | */ |
| 977 | 968 | int extcon_register_notifier_all(struct extcon_dev *edev, |
| 978 | 969 | struct notifier_block *nb) |
| @@ -993,10 +984,10 @@ EXPORT_SYMBOL_GPL(extcon_register_notifier_all); · 14 unmodified lines | ||
| 993 | 984 | |
| 994 | 985 | /** |
| 995 | 986 | * extcon_unregister_notifier_all() - Unregister a notifier block from extcon. |
| 996 | − * @edev: the extcon device that has the external connecotr. | |
| 997 | − * @nb: a notifier block to be registered. | |
| 987 | + * @edev: the extcon device | |
| 988 | + * @nb: a notifier block to be registered | |
| 998 | 989 | * |
| 999 | − * Returns 0 if success or error number if fail | |
| 990 | + * Returns 0 if success or error number if fail. | |
| 1000 | 991 | */ |
| 1001 | 992 | int extcon_unregister_notifier_all(struct extcon_dev *edev, |
| 1002 | 993 | struct notifier_block *nb) |
| @@ -1045,15 +1036,14 @@ static void dummy_sysfs_dev_release(struct device *dev) · 42 unmodified lines | ||
| 1045 | 1036 | |
| 1046 | 1037 | /* |
| 1047 | 1038 | * extcon_dev_allocate() - Allocate the memory of extcon device. |
| 1048 | − * @supported_cable: Array of supported extcon ending with EXTCON_NONE. | |
| 1049 | − * If supported_cable is NULL, cable name related APIs | |
| 1050 | − * are disabled. | |
| 1039 | + * @supported_cable: the array of the supported external connectors | |
| 1040 | + * ending with EXTCON_NONE. | |
| 1051 | 1041 | * |
| 1052 | − * This function allocates the memory for extcon device without allocating | |
| 1053 | − * memory in each extcon provider driver and initialize default setting for | |
| 1054 | − * extcon device. | |
| 1042 | + * Note that this function allocates the memory for extcon device | |
| 1043 | + * and initialize default setting for the extcon device. | |
| 1055 | 1044 | * |
| 1056 | − * Return the pointer of extcon device if success or ERR_PTR(err) if fail | |
| 1045 | + * Returns the pointer memory of allocated extcon_dev if success | |
| 1046 | + * or ERR_PTR(err) if fail. | |
| 1057 | 1047 | */ |
| 1058 | 1048 | struct extcon_dev *extcon_dev_allocate(const unsigned int *supported_cable) |
| 1059 | 1049 | { |
| @@ -1074,7 +1064,7 @@ struct extcon_dev *extcon_dev_allocate(const unsigned int *supported_cable) · 14 unmodified lines | ||
| 1074 | 1064 | |
| 1075 | 1065 | /* |
| 1076 | 1066 | * extcon_dev_free() - Free the memory of extcon device. |
| 1077 | − * @edev: the extcon device to free | |
| 1067 | + * @edev: the extcon device | |
| 1078 | 1068 | */ |
| 1079 | 1069 | void extcon_dev_free(struct extcon_dev *edev) |
| 1080 | 1070 | { |
| @@ -1083,13 +1073,18 @@ void extcon_dev_free(struct extcon_dev *edev) · 2 unmodified lines | ||
| 1083 | 1073 | EXPORT_SYMBOL_GPL(extcon_dev_free); |
| 1084 | 1074 | |
| 1085 | 1075 | /** |
| 1086 | − * extcon_dev_register() - Register a new extcon device | |
| 1087 | − * @edev : the new extcon device (should be allocated before calling) | |
| 1076 | + * extcon_dev_register() - Register an new extcon device | |
| 1077 | + * @edev: the extcon device to be registered | |
| 1088 | 1078 | * |
| 1089 | 1079 | * Among the members of edev struct, please set the "user initializing data" |
| 1090 | − * in any case and set the "optional callbacks" if required. However, please | |
| 1091 | 1080 | * do not set the values of "internal data", which are initialized by |
| 1092 | 1081 | * this function. |
| 1082 | + * | |
| 1083 | + * Note that before calling this funciton, have to allocate the memory | |
| 1084 | + * of an extcon device by using the extcon_dev_allocate(). And the extcon | |
| 1085 | + * dev should include the supported_cable information. | |
| 1086 | + * | |
| 1087 | + * Returns 0 if success or error number if fail. | |
| 1093 | 1088 | */ |
| 1094 | 1089 | int extcon_dev_register(struct extcon_dev *edev) |
| 1095 | 1090 | { |
| @@ -1296,7 +1291,7 @@ EXPORT_SYMBOL_GPL(extcon_dev_register); · 200 unmodified lines | ||
| 1296 | 1291 | |
| 1297 | 1292 | /** |
| 1298 | 1293 | * extcon_dev_unregister() - Unregister the extcon device. |
| 1299 | − * @edev: the extcon device instance to be unregistered. | |
| 1294 | + * @edev: the extcon device to be unregistered. | |
| 1300 | 1295 | * |
| 1301 | 1296 | * Note that this does not call kfree(edev) because edev was not allocated |
| 1302 | 1297 | * by this class. |
| @@ -1342,11 +1337,11 @@ EXPORT_SYMBOL_GPL(extcon_dev_unregister); · 39 unmodified lines | ||
| 1342 | 1337 | |
| 1343 | 1338 | #ifdef CONFIG_OF |
| 1344 | 1339 | /* |
| 1345 | − * extcon_get_edev_by_phandle - Get the extcon device from devicetree | |
| 1346 | − * @dev - instance to the given device | |
| 1347 | − * @index - index into list of extcon_dev | |
| 1340 | + * extcon_get_edev_by_phandle - Get the extcon device from devicetree. | |
| 1341 | + * @dev : the instance to the given device | |
| 1342 | + * @index : the index into list of extcon_dev | |
| 1348 | 1343 | * |
| 1349 | − * return the instance of extcon device | |
| 1344 | + * Return the pointer of extcon device if success or ERR_PTR(err) if fail. | |
| 1350 | 1345 | */ |
| 1351 | 1346 | struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index) |
| 1352 | 1347 | { |
| @@ -1363,8 +1358,8 @@ struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index) · 10 unmodified lines | ||
| 1363 | 1358 | |
| 1364 | 1359 | node = of_parse_phandle(dev->of_node, "extcon", index); |
| 1365 | 1360 | if (!node) { |
| 1366 | − dev_dbg(dev, "failed to get phandle in %s node\n", | |
| 1367 | − dev->of_node->full_name); | |
| 1361 | + dev_dbg(dev, "failed to get phandle in %pOF node\n", | |
| 1362 | + dev->of_node); | |
| 1368 | 1363 | return ERR_PTR(-ENODEV); |
| 1369 | 1364 | } |
| 1370 | 1365 | |
| @@ -1411,8 +1406,6 @@ static void __exit extcon_class_exit(void) · 40 unmodified lines | ||
| 1411 | 1406 | module_exit(extcon_class_exit); |
| 1412 | 1407 | |
| 1413 | 1408 | MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); |
| 1414 | −MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>"); | |
| 1415 | −MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); | |
| 1416 | 1409 | MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); |
| 1417 | −MODULE_DESCRIPTION("External connector (extcon) class driver"); | |
| 1418 | −MODULE_LICENSE("GPL"); | |
| 1410 | +MODULE_DESCRIPTION("External Connector (extcon) framework"); | |
| 1411 | +MODULE_LICENSE("GPL v2"); | |
| @@ -202,7 +202,7 @@ static int vpd_section_init(const char *name, struct vpd_section *sec, · 201 unmodified lines | ||
| 202 | 202 | sec->raw_name = kasprintf(GFP_KERNEL, "%s_raw", name); |
| 203 | 203 | if (!sec->raw_name) { |
| 204 | 204 | err = -ENOMEM; |
| 205 | − goto err_iounmap; | |
| 205 | + goto err_memunmap; | |
| 206 | 206 | } |
| 207 | 207 | |
| 208 | 208 | sysfs_bin_attr_init(&sec->bin_attr); |
| @@ -233,8 +233,8 @@ err_sysfs_remove: · 24 unmodified lines | ||
| 233 | 233 | sysfs_remove_bin_file(vpd_kobj, &sec->bin_attr); |
| 234 | 234 | err_free_raw_name: |
| 235 | 235 | kfree(sec->raw_name); |
| 236 | −err_iounmap: | |
| 237 | − iounmap(sec->baseaddr); | |
| 236 | +err_memunmap: | |
| 237 | + memunmap(sec->baseaddr); | |
| 238 | 238 | return err; |
| 239 | 239 | } |
| 240 | 240 | |
| @@ -245,7 +245,7 @@ static int vpd_section_destroy(struct vpd_section *sec) · 4 unmodified lines | ||
| 245 | 245 | kobject_put(sec->kobj); |
| 246 | 246 | sysfs_remove_bin_file(vpd_kobj, &sec->bin_attr); |
| 247 | 247 | kfree(sec->raw_name); |
| 248 | − iounmap(sec->baseaddr); | |
| 248 | + memunmap(sec->baseaddr); | |
| 249 | 249 | } |
| 250 | 250 | |
| 251 | 251 | return 0; |
| @@ -262,7 +262,7 @@ static int vpd_sections_init(phys_addr_t physaddr) · 10 unmodified lines | ||
| 262 | 262 | return -ENOMEM; |
| 263 | 263 | |
| 264 | 264 | memcpy_fromio(&header, temp, sizeof(struct vpd_cbmem)); |
| 265 | − iounmap(temp); | |
| 265 | + memunmap(temp); | |
| 266 | 266 | |
| 267 | 267 | if (header.magic != VPD_CBMEM_MAGIC) |
| 268 | 268 | return -ENODEV; |
| @@ -6,6 +6,7 @@ fmc-y += fmc-match.o · 5 unmodified lines | ||
| 6 | 6 | fmc-y += fmc-sdb.o |
| 7 | 7 | fmc-y += fru-parse.o |
| 8 | 8 | fmc-y += fmc-dump.o |
| 9 | +fmc-y += fmc-debug.o | |
| 9 | 10 | |
| 10 | 11 | obj-$(CONFIG_FMC_FAKEDEV) += fmc-fakedev.o |
| 11 | 12 | obj-$(CONFIG_FMC_TRIVIAL) += fmc-trivial.o |
| @@ -129,8 +129,7 @@ static int fc_probe(struct fmc_device *fmc) · 128 unmodified lines | ||
| 129 | 129 | |
| 130 | 130 | struct fc_instance *fc; |
| 131 | 131 | |
| 132 | − if (fmc->op->validate) | |
| 133 | − index = fmc->op->validate(fmc, &fc_drv); | |
| 132 | + index = fmc_validate(fmc, &fc_drv); | |
| 134 | 133 | if (index < 0) |
| 135 | 134 | return -EINVAL; /* not our device: invalid */ |
| 136 | 135 | |
| @@ -13,6 +13,9 @@ · 12 unmodified lines | ||
| 13 | 13 | #include <linux/init.h> |
| 14 | 14 | #include <linux/device.h> |
| 15 | 15 | #include <linux/fmc.h> |
| 16 | +#include <linux/fmc-sdb.h> | |
| 17 | + | |
| 18 | +#include "fmc-private.h" | |
| 16 | 19 | |
| 17 | 20 | static int fmc_check_version(unsigned long version, const char *name) |
| 18 | 21 | { |
| @@ -118,6 +121,61 @@ static struct bin_attribute fmc_eeprom_attr = { · 99 unmodified lines | ||
| 118 | 121 | .write = fmc_write_eeprom, |
| 119 | 122 | }; |
| 120 | 123 | |
| 124 | +int fmc_irq_request(struct fmc_device *fmc, irq_handler_t h, | |
| 125 | + char *name, int flags) | |
| 126 | +{ | |
| 127 | + if (fmc->op->irq_request) | |
| 128 | + return fmc->op->irq_request(fmc, h, name, flags); | |
| 129 | + return -EPERM; | |
| 130 | +} | |
| 131 | +EXPORT_SYMBOL(fmc_irq_request); | |
| 132 | + | |
| 133 | +void fmc_irq_free(struct fmc_device *fmc) | |
| 134 | +{ | |
| 135 | + if (fmc->op->irq_free) | |
| 136 | + fmc->op->irq_free(fmc); | |
| 137 | +} | |
| 138 | +EXPORT_SYMBOL(fmc_irq_free); | |
| 139 | + | |
| 140 | +void fmc_irq_ack(struct fmc_device *fmc) | |
| 141 | +{ | |
| 142 | + if (likely(fmc->op->irq_ack)) | |
| 143 | + fmc->op->irq_ack(fmc); | |
| 144 | +} | |
| 145 | +EXPORT_SYMBOL(fmc_irq_ack); | |
| 146 | + | |
| 147 | +int fmc_validate(struct fmc_device *fmc, struct fmc_driver *drv) | |
| 148 | +{ | |
| 149 | + if (fmc->op->validate) | |
| 150 | + return fmc->op->validate(fmc, drv); | |
| 151 | + return -EPERM; | |
| 152 | +} | |
| 153 | +EXPORT_SYMBOL(fmc_validate); | |
| 154 | + | |
| 155 | +int fmc_gpio_config(struct fmc_device *fmc, struct fmc_gpio *gpio, int ngpio) | |
| 156 | +{ | |
| 157 | + if (fmc->op->gpio_config) | |
| 158 | + return fmc->op->gpio_config(fmc, gpio, ngpio); | |
| 159 | + return -EPERM; | |
| 160 | +} | |
| 161 | +EXPORT_SYMBOL(fmc_gpio_config); | |
| 162 | + | |
| 163 | +int fmc_read_ee(struct fmc_device *fmc, int pos, void *d, int l) | |
| 164 | +{ | |
| 165 | + if (fmc->op->read_ee) | |
| 166 | + return fmc->op->read_ee(fmc, pos, d, l); | |
| 167 | + return -EPERM; | |
| 168 | +} | |
| 169 | +EXPORT_SYMBOL(fmc_read_ee); | |
| 170 | + | |
| 171 | +int fmc_write_ee(struct fmc_device *fmc, int pos, const void *d, int l) | |
| 172 | +{ | |
| 173 | + if (fmc->op->write_ee) | |
| 174 | + return fmc->op->write_ee(fmc, pos, d, l); | |
| 175 | + return -EPERM; | |
| 176 | +} | |
| 177 | +EXPORT_SYMBOL(fmc_write_ee); | |
| 178 | + | |
| 121 | 179 | /* |
| 122 | 180 | * Functions for client modules follow |
| 123 | 181 | */ |
| @@ -141,7 +199,8 @@ EXPORT_SYMBOL(fmc_driver_unregister); · 17 unmodified lines | ||
| 141 | 199 | * When a device set is registered, all eeproms must be read |
| 142 | 200 | * and all FRUs must be parsed |
| 143 | 201 | */ |
| 144 | −int fmc_device_register_n(struct fmc_device **devs, int n) | |
| 202 | +int fmc_device_register_n_gw(struct fmc_device **devs, int n, | |
| 203 | + struct fmc_gateware *gw) | |
| 145 | 204 | { |
| 146 | 205 | struct fmc_device *fmc, **devarray; |
| 147 | 206 | uint32_t device_id; |
| @@ -221,6 +280,21 @@ int fmc_device_register_n(struct fmc_device **devs, int n) · 73 unmodified lines | ||
| 221 | 280 | else |
| 222 | 281 | dev_set_name(&fmc->dev, "%s-%04x", fmc->mezzanine_name, |
| 223 | 282 | device_id); |
| 283 | + | |
| 284 | + if (gw) { | |
| 285 | + /* | |
| 286 | + * The carrier already know the bitstream to load | |
| 287 | + * for this set of FMC mezzanines. | |
| 288 | + */ | |
| 289 | + ret = fmc->op->reprogram_raw(fmc, NULL, | |
| 290 | + gw->bitstream, gw->len); | |
| 291 | + if (ret) { | |
| 292 | + dev_warn(fmc->hwdev, | |
| 293 | + "Invalid gateware for FMC mezzanine\n"); | |
| 294 | + goto out; | |
| 295 | + } | |
| 296 | + } | |
| 297 | + | |
| 224 | 298 | ret = device_add(&fmc->dev); |
| 225 | 299 | if (ret < 0) { |
| 226 | 300 | dev_err(fmc->hwdev, "Slot %i: Failed in registering " |
| @@ -234,18 +308,16 @@ int fmc_device_register_n(struct fmc_device **devs, int n) · 7 unmodified lines | ||
| 234 | 308 | } |
| 235 | 309 | /* This device went well, give information to the user */ |
| 236 | 310 | fmc_dump_eeprom(fmc); |
| 237 | − fmc_dump_sdb(fmc); | |
| 311 | + fmc_debug_init(fmc); | |
| 238 | 312 | } |
| 239 | 313 | return 0; |
| 240 | 314 | |
| 241 | 315 | out1: |
| 242 | 316 | device_del(&fmc->dev); |
| 243 | 317 | out: |
| 244 | − fmc_free_id_info(fmc); | |
| 245 | − put_device(&fmc->dev); | |
| 246 | − | |
| 247 | 318 | kfree(devarray); |
| 248 | 319 | for (i--; i >= 0; i--) { |
| 320 | + fmc_debug_exit(devs[i]); | |
| 249 | 321 | sysfs_remove_bin_file(&devs[i]->dev.kobj, &fmc_eeprom_attr); |
| 250 | 322 | device_del(&devs[i]->dev); |
| 251 | 323 | fmc_free_id_info(devs[i]); |
| @@ -254,8 +326,20 @@ out: · 2 unmodified lines | ||
| 254 | 326 | return ret; |
| 255 | 327 | |
| 256 | 328 | } |
| 329 | +EXPORT_SYMBOL(fmc_device_register_n_gw); | |
| 330 | + | |
| 331 | +int fmc_device_register_n(struct fmc_device **devs, int n) | |
| 332 | +{ | |
| 333 | + return fmc_device_register_n_gw(devs, n, NULL); | |
| 334 | +} | |
| 257 | 335 | EXPORT_SYMBOL(fmc_device_register_n); |
| 258 | 336 | |
| 337 | +int fmc_device_register_gw(struct fmc_device *fmc, struct fmc_gateware *gw) | |
| 338 | +{ | |
| 339 | + return fmc_device_register_n_gw(&fmc, 1, gw); | |
| 340 | +} | |
| 341 | +EXPORT_SYMBOL(fmc_device_register_gw); | |
| 342 | + | |
| 259 | 343 | int fmc_device_register(struct fmc_device *fmc) |
| 260 | 344 | { |
| 261 | 345 | return fmc_device_register_n(&fmc, 1); |
| @@ -273,6 +357,7 @@ void fmc_device_unregister_n(struct fmc_device **devs, int n) · 11 unmodified lines | ||
| 273 | 357 | kfree(devs[0]->devarray); |
| 274 | 358 | |
| 275 | 359 | for (i = 0; i < n; i++) { |
| 360 | + fmc_debug_exit(devs[i]); | |
| 276 | 361 | sysfs_remove_bin_file(&devs[i]->dev.kobj, &fmc_eeprom_attr); |
| 277 | 362 | device_del(&devs[i]->dev); |
| 278 | 363 | fmc_free_id_info(devs[i]); |
| @@ -0,0 +1,173 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (C) 2015 CERN (www.cern.ch) | |
| 3 | + * Author: Federico Vaga <federico.vaga@cern.ch> | |
| 4 | + * | |
| 5 | + * Released according to the GNU GPL, version 2 or any later version. | |
| 6 | + */ | |
| 7 | + | |
| 8 | +#include <linux/module.h> | |
| 9 | +#include <linux/device.h> | |
| 10 | +#include <linux/init.h> | |
| 11 | +#include <linux/fs.h> | |
| 12 | +#include <linux/debugfs.h> | |
| 13 | +#include <linux/seq_file.h> | |
| 14 | +#include <asm/byteorder.h> | |
| 15 | + | |
| 16 | +#include <linux/fmc.h> | |
| 17 | +#include <linux/sdb.h> | |
| 18 | +#include <linux/fmc-sdb.h> | |
| 19 | + | |
| 20 | +#define FMC_DBG_SDB_DUMP "dump_sdb" | |
| 21 | + | |
| 22 | +static char *__strip_trailing_space(char *buf, char *str, int len) | |
| 23 | +{ | |
| 24 | + int i = len - 1; | |
| 25 | + | |
| 26 | + memcpy(buf, str, len); | |
| 27 | + buf[len] = '\0'; | |
| 28 | + while (i >= 0 && buf[i] == ' ') | |
| 29 | + buf[i--] = '\0'; | |
| 30 | + return buf; | |
| 31 | +} | |
| 32 | + | |
| 33 | +#define __sdb_string(buf, field) ({ \ | |
| 34 | + BUILD_BUG_ON(sizeof(buf) < sizeof(field)); \ | |
| 35 | + __strip_trailing_space(buf, (void *)(field), sizeof(field)); \ | |
| 36 | + }) | |
| 37 | + | |
| 38 | +/** | |
| 39 | + * We do not check seq_printf() errors because we want to see things in any case | |
| 40 | + */ | |
| 41 | +static void fmc_sdb_dump_recursive(struct fmc_device *fmc, struct seq_file *s, | |
| 42 | + const struct sdb_array *arr) | |
| 43 | +{ | |
| 44 | + unsigned long base = arr->baseaddr; | |
| 45 | + int i, j, n = arr->len, level = arr->level; | |
| 46 | + char tmp[64]; | |
| 47 | + | |
| 48 | + for (i = 0; i < n; i++) { | |
| 49 | + union sdb_record *r; | |
| 50 | + struct sdb_product *p; | |
| 51 | + struct sdb_component *c; | |
| 52 | + | |
| 53 | + r = &arr->record[i]; | |
| 54 | + c = &r->dev.sdb_component; | |
| 55 | + p = &c->product; | |
| 56 | + | |
| 57 | + for (j = 0; j < level; j++) | |
| 58 | + seq_printf(s, " "); | |
| 59 | + switch (r->empty.record_type) { | |
| 60 | + case sdb_type_interconnect: | |
| 61 | + seq_printf(s, "%08llx:%08x %.19s\n", | |
| 62 | + __be64_to_cpu(p->vendor_id), | |
| 63 | + __be32_to_cpu(p->device_id), | |
| 64 | + p->name); | |
| 65 | + break; | |
| 66 | + case sdb_type_device: | |
| 67 | + seq_printf(s, "%08llx:%08x %.19s (%08llx-%08llx)\n", | |
| 68 | + __be64_to_cpu(p->vendor_id), | |
| 69 | + __be32_to_cpu(p->device_id), | |
| 70 | + p->name, | |
| 71 | + __be64_to_cpu(c->addr_first) + base, | |
| 72 | + __be64_to_cpu(c->addr_last) + base); | |
| 73 | + break; | |
| 74 | + case sdb_type_bridge: | |
| 75 | + seq_printf(s, "%08llx:%08x %.19s (bridge: %08llx)\n", | |
| 76 | + __be64_to_cpu(p->vendor_id), | |
| 77 | + __be32_to_cpu(p->device_id), | |
| 78 | + p->name, | |
| 79 | + __be64_to_cpu(c->addr_first) + base); | |
| 80 | + if (IS_ERR(arr->subtree[i])) { | |
| 81 | + seq_printf(s, "SDB: (bridge error %li)\n", | |
| 82 | + PTR_ERR(arr->subtree[i])); | |
| 83 | + break; | |
| 84 | + } | |
| 85 | + fmc_sdb_dump_recursive(fmc, s, arr->subtree[i]); | |
| 86 | + break; | |
| 87 | + case sdb_type_integration: | |
| 88 | + seq_printf(s, "integration\n"); | |
| 89 | + break; | |
| 90 | + case sdb_type_repo_url: | |
| 91 | + seq_printf(s, "Synthesis repository: %s\n", | |
| 92 | + __sdb_string(tmp, r->repo_url.repo_url)); | |
| 93 | + break; | |
| 94 | + case sdb_type_synthesis: | |
| 95 | + seq_printf(s, "Bitstream '%s' ", | |
| 96 | + __sdb_string(tmp, r->synthesis.syn_name)); | |
| 97 | + seq_printf(s, "synthesized %08x by %s ", | |
| 98 | + __be32_to_cpu(r->synthesis.date), | |
| 99 | + __sdb_string(tmp, r->synthesis.user_name)); | |
| 100 | + seq_printf(s, "(%s version %x), ", | |
| 101 | + __sdb_string(tmp, r->synthesis.tool_name), | |
| 102 | + __be32_to_cpu(r->synthesis.tool_version)); | |
| 103 | + seq_printf(s, "commit %pm\n", | |
| 104 | + r->synthesis.commit_id); | |
| 105 | + break; | |
| 106 | + case sdb_type_empty: | |
| 107 | + seq_printf(s, "empty\n"); | |
| 108 | + break; | |
| 109 | + default: | |
| 110 | + seq_printf(s, "UNKNOWN TYPE 0x%02x\n", | |
| 111 | + r->empty.record_type); | |
| 112 | + break; | |
| 113 | + } | |
| 114 | + } | |
| 115 | +} | |
| 116 | + | |
| 117 | +static int fmc_sdb_dump(struct seq_file *s, void *offset) | |
| 118 | +{ | |
| 119 | + struct fmc_device *fmc = s->private; | |
| 120 | + | |
| 121 | + if (!fmc->sdb) { | |
| 122 | + seq_printf(s, "no SDB information\n"); | |
| 123 | + return 0; | |
| 124 | + } | |
| 125 | + | |
| 126 | + seq_printf(s, "FMC: %s (%s), slot %i, device %s\n", dev_name(fmc->hwdev), | |
| 127 | + fmc->carrier_name, fmc->slot_id, dev_name(&fmc->dev)); | |
| 128 | + /* Dump SDB information */ | |
| 129 | + fmc_sdb_dump_recursive(fmc, s, fmc->sdb); | |
| 130 | + | |
| 131 | + return 0; | |
| 132 | +} | |
| 133 | + | |
| 134 | + | |
| 135 | +static int fmc_sdb_dump_open(struct inode *inode, struct file *file) | |
| 136 | +{ | |
| 137 | + struct fmc_device *fmc = inode->i_private; | |
| 138 | + | |
| 139 | + return single_open(file, fmc_sdb_dump, fmc); | |
| 140 | +} | |
| 141 | + | |
| 142 | + | |
| 143 | +const struct file_operations fmc_dbgfs_sdb_dump = { | |
| 144 | + .owner = THIS_MODULE, | |
| 145 | + .open = fmc_sdb_dump_open, | |
| 146 | + .read = seq_read, | |
| 147 | + .llseek = seq_lseek, | |
| 148 | + .release = single_release, | |
| 149 | +}; | |
| 150 | + | |
| 151 | +int fmc_debug_init(struct fmc_device *fmc) | |
| 152 | +{ | |
| 153 | + fmc->dbg_dir = debugfs_create_dir(dev_name(&fmc->dev), NULL); | |
| 154 | + if (IS_ERR_OR_NULL(fmc->dbg_dir)) { | |
| 155 | + pr_err("FMC: Cannot create debugfs\n"); | |
| 156 | + return PTR_ERR(fmc->dbg_dir); | |
| 157 | + } | |
| 158 | + | |
| 159 | + fmc->dbg_sdb_dump = debugfs_create_file(FMC_DBG_SDB_DUMP, 0444, | |
| 160 | + fmc->dbg_dir, fmc, | |
| 161 | + &fmc_dbgfs_sdb_dump); | |
| 162 | + if (IS_ERR_OR_NULL(fmc->dbg_sdb_dump)) | |
| 163 | + pr_err("FMC: Cannot create debugfs file %s\n", | |
| 164 | + FMC_DBG_SDB_DUMP); | |
| 165 | + | |
| 166 | + return 0; | |
| 167 | +} | |
| 168 | + | |
| 169 | +void fmc_debug_exit(struct fmc_device *fmc) | |
| 170 | +{ | |
| 171 | + if (fmc->dbg_dir) | |
| 172 | + debugfs_remove_recursive(fmc->dbg_dir); | |
| 173 | +} | |
| @@ -15,8 +15,6 @@ · 14 unmodified lines | ||
| 15 | 15 | |
| 16 | 16 | static int fmc_must_dump_eeprom; |
| 17 | 17 | module_param_named(dump_eeprom, fmc_must_dump_eeprom, int, 0644); |
| 18 | −static int fmc_must_dump_sdb; | |
| 19 | −module_param_named(dump_sdb, fmc_must_dump_sdb, int, 0644); | |
| 20 | 18 | |
| 21 | 19 | #define LINELEN 16 |
| 22 | 20 | |
| @@ -59,42 +57,3 @@ void fmc_dump_eeprom(const struct fmc_device *fmc) · 36 unmodified lines | ||
| 59 | 57 | for (i = 0; i < fmc->eeprom_len; i += LINELEN, line += LINELEN) |
| 60 | 58 | prev = dump_line(i, line, prev); |
| 61 | 59 | } |
| 62 | − | |
| 63 | −void fmc_dump_sdb(const struct fmc_device *fmc) | |
| 64 | −{ | |
| 65 | − const uint8_t *line, *prev; | |
| 66 | − int i, len; | |
| 67 | − | |
| 68 | − if (!fmc->sdb) | |
| 69 | − return; | |
| 70 | − if (!fmc_must_dump_sdb) | |
| 71 | − return; | |
| 72 | − | |
| 73 | − /* If the argument is not-zero, do simple dump (== show) */ | |
| 74 | − if (fmc_must_dump_sdb > 0) | |
| 75 | − fmc_show_sdb_tree(fmc); | |
| 76 | − | |
| 77 | − if (fmc_must_dump_sdb == 1) | |
| 78 | − return; | |
| 79 | − | |
| 80 | − /* If bigger than 1, dump it seriously, to help debugging */ | |
| 81 | − | |
| 82 | − /* | |
| 83 | − * Here we should really use libsdbfs (which is designed to | |
| 84 | − * work in kernel space as well) , but it doesn't support | |
| 85 | − * directories yet, and it requires better intergration (it | |
| 86 | − * should be used instead of fmc-specific code). | |
| 87 | − * | |
| 88 | − * So, lazily, just dump the top-level array | |
| 89 | − */ | |
| 90 | − pr_info("FMC: %s (%s), slot %i, device %s\n", dev_name(fmc->hwdev), | |
| 91 | − fmc->carrier_name, fmc->slot_id, dev_name(&fmc->dev)); | |
| 92 | − pr_info("FMC: poor dump of sdb first level:\n"); | |
| 93 | − | |
| 94 | − len = fmc->sdb->len * sizeof(union sdb_record); | |
| 95 | − line = (void *)fmc->sdb->record; | |
| 96 | − prev = NULL; | |
| 97 | − for (i = 0; i < len; i += LINELEN, line += LINELEN) | |
| 98 | − prev = dump_line(i, line, prev); | |
| 99 | − return; | |
| 100 | −} | |
| @@ -63,7 +63,7 @@ int fmc_fill_id_info(struct fmc_device *fmc) · 62 unmodified lines | ||
| 63 | 63 | if (!fmc->eeprom) |
| 64 | 64 | return -ENOMEM; |
| 65 | 65 | allocated = 1; |
| 66 | − ret = fmc->op->read_ee(fmc, 0, fmc->eeprom, fmc->eeprom_len); | |
| 66 | + ret = fmc_read_ee(fmc, 0, fmc->eeprom, fmc->eeprom_len); | |
| 67 | 67 | if (ret < 0) |
| 68 | 68 | goto out; |
| 69 | 69 | } |
| @@ -0,0 +1,9 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (C) 2015 CERN (www.cern.ch) | |
| 3 | + * Author: Federico Vaga <federico.vaga@cern.ch> | |
| 4 | + * | |
| 5 | + * Released according to the GNU GPL, version 2 or any later version. | |
| 6 | + */ | |
| 7 | + | |
| 8 | +extern int fmc_debug_init(struct fmc_device *fmc); | |
| 9 | +extern void fmc_debug_exit(struct fmc_device *fmc); | |
| @@ -127,12 +127,12 @@ int fmc_free_sdb_tree(struct fmc_device *fmc) · 126 unmodified lines | ||
| 127 | 127 | EXPORT_SYMBOL(fmc_free_sdb_tree); |
| 128 | 128 | |
| 129 | 129 | /* This helper calls reprogram and inizialized sdb as well */ |
| 130 | −int fmc_reprogram(struct fmc_device *fmc, struct fmc_driver *d, char *gw, | |
| 131 | − int sdb_entry) | |
| 130 | +int fmc_reprogram_raw(struct fmc_device *fmc, struct fmc_driver *d, | |
| 131 | + void *gw, unsigned long len, int sdb_entry) | |
| 132 | 132 | { |
| 133 | 133 | int ret; |
| 134 | 134 | |
| 135 | − ret = fmc->op->reprogram(fmc, d, gw); | |
| 135 | + ret = fmc->op->reprogram_raw(fmc, d, gw, len); | |
| 136 | 136 | if (ret < 0) |
| 137 | 137 | return ret; |
| 138 | 138 | if (sdb_entry < 0) |
| @@ -145,108 +145,39 @@ int fmc_reprogram(struct fmc_device *fmc, struct fmc_driver *d, char *gw, · 6 unmodified lines | ||
| 145 | 145 | sdb_entry); |
| 146 | 146 | return -ENODEV; |
| 147 | 147 | } |
| 148 | − fmc_dump_sdb(fmc); | |
| 149 | − return 0; | |
| 150 | −} | |
| 151 | −EXPORT_SYMBOL(fmc_reprogram); | |
| 152 | − | |
| 153 | −static char *__strip_trailing_space(char *buf, char *str, int len) | |
| 154 | −{ | |
| 155 | − int i = len - 1; | |
| 156 | 148 | |
| 157 | − memcpy(buf, str, len); | |
| 158 | − while(i >= 0 && buf[i] == ' ') | |
| 159 | − buf[i--] = '\0'; | |
| 160 | − return buf; | |
| 149 | + return 0; | |
| 161 | 150 | } |
| 151 | +EXPORT_SYMBOL(fmc_reprogram_raw); | |
| 162 | 152 | |
| 163 | −#define __sdb_string(buf, field) ({ \ | |
| 164 | − BUILD_BUG_ON(sizeof(buf) < sizeof(field)); \ | |
| 165 | − __strip_trailing_space(buf, (void *)(field), sizeof(field)); \ | |
| 166 | − }) | |
| 167 | − | |
| 168 | −static void __fmc_show_sdb_tree(const struct fmc_device *fmc, | |
| 169 | − const struct sdb_array *arr) | |
| 153 | +/* This helper calls reprogram and inizialized sdb as well */ | |
| 154 | +int fmc_reprogram(struct fmc_device *fmc, struct fmc_driver *d, char *gw, | |
| 155 | + int sdb_entry) | |
| 170 | 156 | { |
| 171 | − unsigned long base = arr->baseaddr; | |
| 172 | − int i, j, n = arr->len, level = arr->level; | |
| 173 | − char buf[64]; | |
| 174 | − | |
| 175 | − for (i = 0; i < n; i++) { | |
| 176 | − union sdb_record *r; | |
| 177 | − struct sdb_product *p; | |
| 178 | − struct sdb_component *c; | |
| 179 | − r = &arr->record[i]; | |
| 180 | − c = &r->dev.sdb_component; | |
| 181 | − p = &c->product; | |
| 157 | + int ret; | |
| 182 | 158 | |
| 183 | − dev_info(&fmc->dev, "SDB: "); | |
| 159 | + ret = fmc->op->reprogram(fmc, d, gw); | |
| 160 | + if (ret < 0) | |
| 161 | + return ret; | |
| 162 | + if (sdb_entry < 0) | |
| 163 | + return ret; | |
| 184 | 164 | |
| 185 | − for (j = 0; j < level; j++) | |
| 186 | − printk(KERN_CONT " "); | |
| 187 | − switch (r->empty.record_type) { | |
| 188 | − case sdb_type_interconnect: | |
| 189 | − printk(KERN_CONT "%08llx:%08x %.19s\n", | |
| 190 | − __be64_to_cpu(p->vendor_id), | |
| 191 | − __be32_to_cpu(p->device_id), | |
| 192 | − p->name); | |
| 193 | − break; | |
| 194 | − case sdb_type_device: | |
| 195 | − printk(KERN_CONT "%08llx:%08x %.19s (%08llx-%08llx)\n", | |
| 196 | − __be64_to_cpu(p->vendor_id), | |
| 197 | − __be32_to_cpu(p->device_id), | |
| 198 | − p->name, | |
| 199 | − __be64_to_cpu(c->addr_first) + base, | |
| 200 | − __be64_to_cpu(c->addr_last) + base); | |
| 201 | − break; | |
| 202 | − case sdb_type_bridge: | |
| 203 | − printk(KERN_CONT "%08llx:%08x %.19s (bridge: %08llx)\n", | |
| 204 | − __be64_to_cpu(p->vendor_id), | |
| 205 | − __be32_to_cpu(p->device_id), | |
| 206 | − p->name, | |
| 207 | − __be64_to_cpu(c->addr_first) + base); | |
| 208 | − if (IS_ERR(arr->subtree[i])) { | |
| 209 | − dev_info(&fmc->dev, "SDB: (bridge error %li)\n", | |
| 210 | − PTR_ERR(arr->subtree[i])); | |
| 211 | − break; | |
| 212 | − } | |
| 213 | − __fmc_show_sdb_tree(fmc, arr->subtree[i]); | |
| 214 | − break; | |
| 215 | − case sdb_type_integration: | |
| 216 | − printk(KERN_CONT "integration\n"); | |
| 217 | − break; | |
| 218 | − case sdb_type_repo_url: | |
| 219 | − printk(KERN_CONT "Synthesis repository: %s\n", | |
| 220 | − __sdb_string(buf, r->repo_url.repo_url)); | |
| 221 | − break; | |
| 222 | − case sdb_type_synthesis: | |
| 223 | − printk(KERN_CONT "Bitstream '%s' ", | |
| 224 | − __sdb_string(buf, r->synthesis.syn_name)); | |
| 225 | − printk(KERN_CONT "synthesized %08x by %s ", | |
| 226 | − __be32_to_cpu(r->synthesis.date), | |
| 227 | − __sdb_string(buf, r->synthesis.user_name)); | |
| 228 | − printk(KERN_CONT "(%s version %x), ", | |
| 229 | − __sdb_string(buf, r->synthesis.tool_name), | |
| 230 | − __be32_to_cpu(r->synthesis.tool_version)); | |
| 231 | − printk(KERN_CONT "commit %pm\n", | |
| 232 | − r->synthesis.commit_id); | |
| 233 | − break; | |
| 234 | − case sdb_type_empty: | |
| 235 | − printk(KERN_CONT "empty\n"); | |
| 236 | − break; | |
| 237 | − default: | |
| 238 | − printk(KERN_CONT "UNKNOWN TYPE 0x%02x\n", | |
| 239 | − r->empty.record_type); | |
| 240 | − break; | |
| 241 | − } | |
| 165 | + /* We are required to find SDB at a given offset */ | |
| 166 | + ret = fmc_scan_sdb_tree(fmc, sdb_entry); | |
| 167 | + if (ret < 0) { | |
| 168 | + dev_err(&fmc->dev, "Can't find SDB at address 0x%x\n", | |
| 169 | + sdb_entry); | |
| 170 | + return -ENODEV; | |
| 242 | 171 | } |
| 172 | + | |
| 173 | + return 0; | |
| 243 | 174 | } |
| 175 | +EXPORT_SYMBOL(fmc_reprogram); | |
| 244 | 176 | |
| 245 | 177 | void fmc_show_sdb_tree(const struct fmc_device *fmc) |
| 246 | 178 | { |
| 247 | − if (!fmc->sdb) | |
| 248 | − return; | |
| 249 | − __fmc_show_sdb_tree(fmc, fmc->sdb); | |
| 179 | + pr_err("%s: not supported anymore, use debugfs to dump SDB\n", | |
| 180 | + __func__); | |
| 250 | 181 | } |
| 251 | 182 | EXPORT_SYMBOL(fmc_show_sdb_tree); |
| 252 | 183 | |
| @@ -24,7 +24,7 @@ static irqreturn_t t_handler(int irq, void *dev_id) · 23 unmodified lines | ||
| 24 | 24 | { |
| 25 | 25 | struct fmc_device *fmc = dev_id; |
| 26 | 26 | |
| 27 | − fmc->op->irq_ack(fmc); | |
| 27 | + fmc_irq_ack(fmc); | |
| 28 | 28 | dev_info(&fmc->dev, "received irq %i\n", irq); |
| 29 | 29 | return IRQ_HANDLED; |
| 30 | 30 | } |
| @@ -46,25 +46,21 @@ static int t_probe(struct fmc_device *fmc) · 15 unmodified lines | ||
| 46 | 46 | int ret; |
| 47 | 47 | int index = 0; |
| 48 | 48 | |
| 49 | − if (fmc->op->validate) | |
| 50 | − index = fmc->op->validate(fmc, &t_drv); | |
| 49 | + index = fmc_validate(fmc, &t_drv); | |
| 51 | 50 | if (index < 0) |
| 52 | 51 | return -EINVAL; /* not our device: invalid */ |
| 53 | 52 | |
| 54 | − ret = fmc->op->irq_request(fmc, t_handler, "fmc-trivial", IRQF_SHARED); | |
| 53 | + ret = fmc_irq_request(fmc, t_handler, "fmc-trivial", IRQF_SHARED); | |
| 55 | 54 | if (ret < 0) |
| 56 | 55 | return ret; |
| 57 | 56 | /* ignore error code of call below, we really don't care */ |
| 58 | − fmc->op->gpio_config(fmc, t_gpio, ARRAY_SIZE(t_gpio)); | |
| 57 | + fmc_gpio_config(fmc, t_gpio, ARRAY_SIZE(t_gpio)); | |
| 59 | 58 | |
| 60 | − /* Reprogram, if asked to. ESRCH == no filename specified */ | |
| 61 | − ret = -ESRCH; | |
| 62 | − if (fmc->op->reprogram) | |
| 63 | − ret = fmc->op->reprogram(fmc, &t_drv, ""); | |
| 64 | − if (ret == -ESRCH) | |
| 59 | + ret = fmc_reprogram(fmc, &t_drv, "", 0); | |
| 60 | + if (ret == -EPERM) /* programming not supported */ | |
| 65 | 61 | ret = 0; |
| 66 | 62 | if (ret < 0) |
| 67 | − fmc->op->irq_free(fmc); | |
| 63 | + fmc_irq_free(fmc); | |
| 68 | 64 | |
| 69 | 65 | /* FIXME: reprogram LM32 too */ |
| 70 | 66 | return ret; |
| @@ -72,7 +68,7 @@ static int t_probe(struct fmc_device *fmc) · 1 unmodified line | ||
| 72 | 68 | |
| 73 | 69 | static int t_remove(struct fmc_device *fmc) |
| 74 | 70 | { |
| 75 | − fmc->op->irq_free(fmc); | |
| 71 | + fmc_irq_free(fmc); | |
| 76 | 72 | return 0; |
| 77 | 73 | } |
| 78 | 74 | |
| @@ -50,7 +50,7 @@ static int fwe_run_tlv(struct fmc_device *fmc, const struct firmware *fw, · 49 unmodified lines | ||
| 50 | 50 | if (write) { |
| 51 | 51 | dev_info(&fmc->dev, "write %i bytes at 0x%04x\n", |
| 52 | 52 | thislen, thisaddr); |
| 53 | − err = fmc->op->write_ee(fmc, thisaddr, p + 5, thislen); | |
| 53 | + err = fmc_write_ee(fmc, thisaddr, p + 5, thislen); | |
| 54 | 54 | } |
| 55 | 55 | if (err < 0) { |
| 56 | 56 | dev_err(&fmc->dev, "write failure @0x%04x\n", |
| @@ -70,7 +70,7 @@ static int fwe_run_bin(struct fmc_device *fmc, const struct firmware *fw) · 13 unmodified lines | ||
| 70 | 70 | int ret; |
| 71 | 71 | |
| 72 | 72 | dev_info(&fmc->dev, "programming %zi bytes\n", fw->size); |
| 73 | − ret = fmc->op->write_ee(fmc, 0, (void *)fw->data, fw->size); | |
| 73 | + ret = fmc_write_ee(fmc, 0, (void *)fw->data, fw->size); | |
| 74 | 74 | if (ret < 0) { |
| 75 | 75 | dev_info(&fmc->dev, "write_eeprom: error %i\n", ret); |
| 76 | 76 | return ret; |
| @@ -115,8 +115,8 @@ static int fwe_probe(struct fmc_device *fmc) · 38 unmodified lines | ||
| 115 | 115 | KBUILD_MODNAME); |
| 116 | 116 | return -ENODEV; |
| 117 | 117 | } |
| 118 | − if (fmc->op->validate) | |
| 119 | − index = fmc->op->validate(fmc, &fwe_drv); | |
| 118 | + | |
| 119 | + index = fmc_validate(fmc, &fwe_drv); | |
| 120 | 120 | if (index < 0) { |
| 121 | 121 | pr_err("%s: refusing device \"%s\"\n", KBUILD_MODNAME, |
| 122 | 122 | dev_name(dev)); |
| @@ -31,12 +31,11 @@ static char *__fru_alloc_get_tl(struct fru_common_header *header, int nr) · 30 unmodified lines | ||
| 31 | 31 | { |
| 32 | 32 | struct fru_type_length *tl; |
| 33 | 33 | char *res; |
| 34 | − int len; | |
| 35 | 34 | |
| 36 | 35 | tl = __fru_get_board_tl(header, nr); |
| 37 | 36 | if (!tl) |
| 38 | 37 | return NULL; |
| 39 | − len = fru_strlen(tl); | |
| 38 | + | |
| 40 | 39 | res = fru_alloc(fru_strlen(tl) + 1); |
| 41 | 40 | if (!res) |
| 42 | 41 | return NULL; |
| @@ -2,9 +2,7 @@ · 1 unmodified line | ||
| 2 | 2 | # FPGA framework configuration |
| 3 | 3 | # |
| 4 | 4 | |
| 5 | −menu "FPGA Configuration Support" | |
| 6 | − | |
| 7 | −config FPGA | |
| 5 | +menuconfig FPGA | |
| 8 | 6 | tristate "FPGA Configuration Framework" |
| 9 | 7 | help |
| 10 | 8 | Say Y here if you want support for configuring FPGAs from the |
| @@ -26,6 +24,20 @@ config FPGA_MGR_ICE40_SPI · 15 unmodified lines | ||
| 26 | 24 | help |
| 27 | 25 | FPGA manager driver support for Lattice iCE40 FPGAs over SPI. |
| 28 | 26 | |
| 27 | +config FPGA_MGR_ALTERA_CVP | |
| 28 | + tristate "Altera Arria-V/Cyclone-V/Stratix-V CvP FPGA Manager" | |
| 29 | + depends on PCI | |
| 30 | + help | |
| 31 | + FPGA manager driver support for Arria-V, Cyclone-V, Stratix-V | |
| 32 | + and Arria 10 Altera FPGAs using the CvP interface over PCIe. | |
| 33 | + | |
| 34 | +config FPGA_MGR_ALTERA_PS_SPI | |
| 35 | + tristate "Altera FPGA Passive Serial over SPI" | |
| 36 | + depends on SPI | |
| 37 | + help | |
| 38 | + FPGA manager driver support for Altera Arria/Cyclone/Stratix | |
| 39 | + using the passive serial interface over SPI. | |
| 40 | + | |
| 29 | 41 | config FPGA_MGR_SOCFPGA |
| 30 | 42 | tristate "Altera SOCFPGA FPGA Manager" |
| 31 | 43 | depends on ARCH_SOCFPGA || COMPILE_TEST |
| @@ -106,5 +118,3 @@ config XILINX_PR_DECOUPLER · 74 unmodified lines | ||
| 106 | 118 | being reprogrammed during partial reconfig. |
| 107 | 119 | |
| 108 | 120 | endif # FPGA |
| 109 | − | |
| 110 | −endmenu | |
| @@ -6,6 +6,8 @@ · 5 unmodified lines | ||
| 6 | 6 | obj-$(CONFIG_FPGA) += fpga-mgr.o |
| 7 | 7 | |
| 8 | 8 | # FPGA Manager Drivers |
| 9 | +obj-$(CONFIG_FPGA_MGR_ALTERA_CVP) += altera-cvp.o | |
| 10 | +obj-$(CONFIG_FPGA_MGR_ALTERA_PS_SPI) += altera-ps-spi.o | |
| 9 | 11 | obj-$(CONFIG_FPGA_MGR_ICE40_SPI) += ice40-spi.o |
| 10 | 12 | obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o |
| 11 | 13 | obj-$(CONFIG_FPGA_MGR_SOCFPGA_A10) += socfpga-a10.o |
| @@ -0,0 +1,500 @@ | ||
| 1 | +/* | |
| 2 | + * FPGA Manager Driver for Altera Arria/Cyclone/Stratix CvP | |
| 3 | + * | |
| 4 | + * Copyright (C) 2017 DENX Software Engineering | |
| 5 | + * | |
| 6 | + * Anatolij Gustschin <agust@denx.de> | |
| 7 | + * | |
| 8 | + * This program is free software; you can redistribute it and/or modify | |
| 9 | + * it under the terms of the GNU General Public License as published by | |
| 10 | + * the Free Software Foundation; version 2 of the License. | |
| 11 | + * | |
| 12 | + * This program is distributed in the hope that it will be useful, | |
| 13 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 14 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 15 | + * GNU General Public License for more details. | |
| 16 | + * | |
| 17 | + * Manage Altera FPGA firmware using PCIe CvP. | |
| 18 | + * Firmware must be in binary "rbf" format. | |
| 19 | + */ | |
| 20 | + | |
| 21 | +#include <linux/delay.h> | |
| 22 | +#include <linux/device.h> | |
| 23 | +#include <linux/fpga/fpga-mgr.h> | |
| 24 | +#include <linux/module.h> | |
| 25 | +#include <linux/pci.h> | |
| 26 | +#include <linux/sizes.h> | |
| 27 | + | |
| 28 | +#define CVP_BAR 0 /* BAR used for data transfer in memory mode */ | |
| 29 | +#define CVP_DUMMY_WR 244 /* dummy writes to clear CvP state machine */ | |
| 30 | +#define TIMEOUT_US 2000 /* CVP STATUS timeout for USERMODE polling */ | |
| 31 | + | |
| 32 | +/* Vendor Specific Extended Capability Registers */ | |
| 33 | +#define VSE_PCIE_EXT_CAP_ID 0x200 | |
| 34 | +#define VSE_PCIE_EXT_CAP_ID_VAL 0x000b /* 16bit */ | |
| 35 | + | |
| 36 | +#define VSE_CVP_STATUS 0x21c /* 32bit */ | |
| 37 | +#define VSE_CVP_STATUS_CFG_RDY BIT(18) /* CVP_CONFIG_READY */ | |
| 38 | +#define VSE_CVP_STATUS_CFG_ERR BIT(19) /* CVP_CONFIG_ERROR */ | |
| 39 | +#define VSE_CVP_STATUS_CVP_EN BIT(20) /* ctrl block is enabling CVP */ | |
| 40 | +#define VSE_CVP_STATUS_USERMODE BIT(21) /* USERMODE */ | |
| 41 | +#define VSE_CVP_STATUS_CFG_DONE BIT(23) /* CVP_CONFIG_DONE */ | |
| 42 | +#define VSE_CVP_STATUS_PLD_CLK_IN_USE BIT(24) /* PLD_CLK_IN_USE */ | |
| 43 | + | |
| 44 | +#define VSE_CVP_MODE_CTRL 0x220 /* 32bit */ | |
| 45 | +#define VSE_CVP_MODE_CTRL_CVP_MODE BIT(0) /* CVP (1) or normal mode (0) */ | |
| 46 | +#define VSE_CVP_MODE_CTRL_HIP_CLK_SEL BIT(1) /* PMA (1) or fabric clock (0) */ | |
| 47 | +#define VSE_CVP_MODE_CTRL_NUMCLKS_OFF 8 /* NUMCLKS bits offset */ | |
| 48 | +#define VSE_CVP_MODE_CTRL_NUMCLKS_MASK GENMASK(15, 8) | |
| 49 | + | |
| 50 | +#define VSE_CVP_DATA 0x228 /* 32bit */ | |
| 51 | +#define VSE_CVP_PROG_CTRL 0x22c /* 32bit */ | |
| 52 | +#define VSE_CVP_PROG_CTRL_CONFIG BIT(0) | |
| 53 | +#define VSE_CVP_PROG_CTRL_START_XFER BIT(1) | |
| 54 | + | |
| 55 | +#define VSE_UNCOR_ERR_STATUS 0x234 /* 32bit */ | |
| 56 | +#define VSE_UNCOR_ERR_CVP_CFG_ERR BIT(5) /* CVP_CONFIG_ERROR_LATCHED */ | |
| 57 | + | |
| 58 | +#define DRV_NAME "altera-cvp" | |
| 59 | +#define ALTERA_CVP_MGR_NAME "Altera CvP FPGA Manager" | |
| 60 | + | |
| 61 | +/* Optional CvP config error status check for debugging */ | |
| 62 | +static bool altera_cvp_chkcfg; | |
| 63 | + | |
| 64 | +struct altera_cvp_conf { | |
| 65 | + struct fpga_manager *mgr; | |
| 66 | + struct pci_dev *pci_dev; | |
| 67 | + void __iomem *map; | |
| 68 | + void (*write_data)(struct altera_cvp_conf *, u32); | |
| 69 | + char mgr_name[64]; | |
| 70 | + u8 numclks; | |
| 71 | +}; | |
| 72 | + | |
| 73 | +static enum fpga_mgr_states altera_cvp_state(struct fpga_manager *mgr) | |
| 74 | +{ | |
| 75 | + struct altera_cvp_conf *conf = mgr->priv; | |
| 76 | + u32 status; | |
| 77 | + | |
| 78 | + pci_read_config_dword(conf->pci_dev, VSE_CVP_STATUS, &status); | |
| 79 | + | |
| 80 | + if (status & VSE_CVP_STATUS_CFG_DONE) | |
| 81 | + return FPGA_MGR_STATE_OPERATING; | |
| 82 | + | |
| 83 | + if (status & VSE_CVP_STATUS_CVP_EN) | |
| 84 | + return FPGA_MGR_STATE_POWER_UP; | |
| 85 | + | |
| 86 | + return FPGA_MGR_STATE_UNKNOWN; | |
| 87 | +} | |
| 88 | + | |
| 89 | +static void altera_cvp_write_data_iomem(struct altera_cvp_conf *conf, u32 val) | |
| 90 | +{ | |
| 91 | + writel(val, conf->map); | |
| 92 | +} | |
| 93 | + | |
| 94 | +static void altera_cvp_write_data_config(struct altera_cvp_conf *conf, u32 val) | |
| 95 | +{ | |
| 96 | + pci_write_config_dword(conf->pci_dev, VSE_CVP_DATA, val); | |
| 97 | +} | |
| 98 | + | |
| 99 | +/* switches between CvP clock and internal clock */ | |
| 100 | +static void altera_cvp_dummy_write(struct altera_cvp_conf *conf) | |
| 101 | +{ | |
| 102 | + unsigned int i; | |
| 103 | + u32 val; | |
| 104 | + | |
| 105 | + /* set 1 CVP clock cycle for every CVP Data Register Write */ | |
| 106 | + pci_read_config_dword(conf->pci_dev, VSE_CVP_MODE_CTRL, &val); | |
| 107 | + val &= ~VSE_CVP_MODE_CTRL_NUMCLKS_MASK; | |
| 108 | + val |= 1 << VSE_CVP_MODE_CTRL_NUMCLKS_OFF; | |
| 109 | + pci_write_config_dword(conf->pci_dev, VSE_CVP_MODE_CTRL, val); | |
| 110 | + | |
| 111 | + for (i = 0; i < CVP_DUMMY_WR; i++) | |
| 112 | + conf->write_data(conf, 0); /* dummy data, could be any value */ | |
| 113 | +} | |
| 114 | + | |
| 115 | +static int altera_cvp_wait_status(struct altera_cvp_conf *conf, u32 status_mask, | |
| 116 | + u32 status_val, int timeout_us) | |
| 117 | +{ | |
| 118 | + unsigned int retries; | |
| 119 | + u32 val; | |
| 120 | + | |
| 121 | + retries = timeout_us / 10; | |
| 122 | + if (timeout_us % 10) | |
| 123 | + retries++; | |
| 124 | + | |
| 125 | + do { | |
| 126 | + pci_read_config_dword(conf->pci_dev, VSE_CVP_STATUS, &val); | |
| 127 | + if ((val & status_mask) == status_val) | |
| 128 | + return 0; | |
| 129 | + | |
| 130 | + /* use small usleep value to re-check and break early */ | |
| 131 | + usleep_range(10, 11); | |
| 132 | + } while (--retries); | |
| 133 | + | |
| 134 | + return -ETIMEDOUT; | |
| 135 | +} | |
| 136 | + | |
| 137 | +static int altera_cvp_teardown(struct fpga_manager *mgr, | |
| 138 | + struct fpga_image_info *info) | |
| 139 | +{ | |
| 140 | + struct altera_cvp_conf *conf = mgr->priv; | |
| 141 | + struct pci_dev *pdev = conf->pci_dev; | |
| 142 | + int ret; | |
| 143 | + u32 val; | |
| 144 | + | |
| 145 | + /* STEP 12 - reset START_XFER bit */ | |
| 146 | + pci_read_config_dword(pdev, VSE_CVP_PROG_CTRL, &val); | |
| 147 | + val &= ~VSE_CVP_PROG_CTRL_START_XFER; | |
| 148 | + pci_write_config_dword(pdev, VSE_CVP_PROG_CTRL, val); | |
| 149 | + | |
| 150 | + /* STEP 13 - reset CVP_CONFIG bit */ | |
| 151 | + val &= ~VSE_CVP_PROG_CTRL_CONFIG; | |
| 152 | + pci_write_config_dword(pdev, VSE_CVP_PROG_CTRL, val); | |
| 153 | + | |
| 154 | + /* | |
| 155 | + * STEP 14 | |
| 156 | + * - set CVP_NUMCLKS to 1 and then issue CVP_DUMMY_WR dummy | |
| 157 | + * writes to the HIP | |
| 158 | + */ | |
| 159 | + altera_cvp_dummy_write(conf); /* from CVP clock to internal clock */ | |
| 160 | + | |
| 161 | + /* STEP 15 - poll CVP_CONFIG_READY bit for 0 with 10us timeout */ | |
| 162 | + ret = altera_cvp_wait_status(conf, VSE_CVP_STATUS_CFG_RDY, 0, 10); | |
| 163 | + if (ret) | |
| 164 | + dev_err(&mgr->dev, "CFG_RDY == 0 timeout\n"); | |
| 165 | + | |
| 166 | + return ret; | |
| 167 | +} | |
| 168 | + | |
| 169 | +static int altera_cvp_write_init(struct fpga_manager *mgr, | |
| 170 | + struct fpga_image_info *info, | |
| 171 | + const char *buf, size_t count) | |
| 172 | +{ | |
| 173 | + struct altera_cvp_conf *conf = mgr->priv; | |
| 174 | + struct pci_dev *pdev = conf->pci_dev; | |
| 175 | + u32 iflags, val; | |
| 176 | + int ret; | |
| 177 | + | |
| 178 | + iflags = info ? info->flags : 0; | |
| 179 | + | |
| 180 | + if (iflags & FPGA_MGR_PARTIAL_RECONFIG) { | |
| 181 | + dev_err(&mgr->dev, "Partial reconfiguration not supported.\n"); | |
| 182 | + return -EINVAL; | |
| 183 | + } | |
| 184 | + | |
| 185 | + /* Determine allowed clock to data ratio */ | |
| 186 | + if (iflags & FPGA_MGR_COMPRESSED_BITSTREAM) | |
| 187 | + conf->numclks = 8; /* ratio for all compressed images */ | |
| 188 | + else if (iflags & FPGA_MGR_ENCRYPTED_BITSTREAM) | |
| 189 | + conf->numclks = 4; /* for uncompressed and encrypted images */ | |
| 190 | + else | |
| 191 | + conf->numclks = 1; /* for uncompressed and unencrypted images */ | |
| 192 | + | |
| 193 | + /* STEP 1 - read CVP status and check CVP_EN flag */ | |
| 194 | + pci_read_config_dword(pdev, VSE_CVP_STATUS, &val); | |
| 195 | + if (!(val & VSE_CVP_STATUS_CVP_EN)) { | |
| 196 | + dev_err(&mgr->dev, "CVP mode off: 0x%04x\n", val); | |
| 197 | + return -ENODEV; | |
| 198 | + } | |
| 199 | + | |
| 200 | + if (val & VSE_CVP_STATUS_CFG_RDY) { | |
| 201 | + dev_warn(&mgr->dev, "CvP already started, teardown first\n"); | |
| 202 | + ret = altera_cvp_teardown(mgr, info); | |
| 203 | + if (ret) | |
| 204 | + return ret; | |
| 205 | + } | |
| 206 | + | |
| 207 | + /* | |
| 208 | + * STEP 2 | |
| 209 | + * - set HIP_CLK_SEL and CVP_MODE (must be set in the order mentioned) | |
| 210 | + */ | |
| 211 | + /* switch from fabric to PMA clock */ | |
| 212 | + pci_read_config_dword(pdev, VSE_CVP_MODE_CTRL, &val); | |
| 213 | + val |= VSE_CVP_MODE_CTRL_HIP_CLK_SEL; | |
| 214 | + pci_write_config_dword(pdev, VSE_CVP_MODE_CTRL, val); | |
| 215 | + | |
| 216 | + /* set CVP mode */ | |
| 217 | + pci_read_config_dword(pdev, VSE_CVP_MODE_CTRL, &val); | |
| 218 | + val |= VSE_CVP_MODE_CTRL_CVP_MODE; | |
| 219 | + pci_write_config_dword(pdev, VSE_CVP_MODE_CTRL, val); | |
| 220 | + | |
| 221 | + /* | |
| 222 | + * STEP 3 | |
| 223 | + * - set CVP_NUMCLKS to 1 and issue CVP_DUMMY_WR dummy writes to the HIP | |
| 224 | + */ | |
| 225 | + altera_cvp_dummy_write(conf); | |
| 226 | + | |
| 227 | + /* STEP 4 - set CVP_CONFIG bit */ | |
| 228 | + pci_read_config_dword(pdev, VSE_CVP_PROG_CTRL, &val); | |
| 229 | + /* request control block to begin transfer using CVP */ | |
| 230 | + val |= VSE_CVP_PROG_CTRL_CONFIG; | |
| 231 | + pci_write_config_dword(pdev, VSE_CVP_PROG_CTRL, val); | |
| 232 | + | |
| 233 | + /* STEP 5 - poll CVP_CONFIG READY for 1 with 10us timeout */ | |
| 234 | + ret = altera_cvp_wait_status(conf, VSE_CVP_STATUS_CFG_RDY, | |
| 235 | + VSE_CVP_STATUS_CFG_RDY, 10); | |
| 236 | + if (ret) { | |
| 237 | + dev_warn(&mgr->dev, "CFG_RDY == 1 timeout\n"); | |
| 238 | + return ret; | |
| 239 | + } | |
| 240 | + | |
| 241 | + /* | |
| 242 | + * STEP 6 | |
| 243 | + * - set CVP_NUMCLKS to 1 and issue CVP_DUMMY_WR dummy writes to the HIP | |
| 244 | + */ | |
| 245 | + altera_cvp_dummy_write(conf); | |
| 246 | + | |
| 247 | + /* STEP 7 - set START_XFER */ | |
| 248 | + pci_read_config_dword(pdev, VSE_CVP_PROG_CTRL, &val); | |
| 249 | + val |= VSE_CVP_PROG_CTRL_START_XFER; | |
| 250 | + pci_write_config_dword(pdev, VSE_CVP_PROG_CTRL, val); | |
| 251 | + | |
| 252 | + /* STEP 8 - start transfer (set CVP_NUMCLKS for bitstream) */ | |
| 253 | + pci_read_config_dword(pdev, VSE_CVP_MODE_CTRL, &val); | |
| 254 | + val &= ~VSE_CVP_MODE_CTRL_NUMCLKS_MASK; | |
| 255 | + val |= conf->numclks << VSE_CVP_MODE_CTRL_NUMCLKS_OFF; | |
| 256 | + pci_write_config_dword(pdev, VSE_CVP_MODE_CTRL, val); | |
| 257 | + | |
| 258 | + return 0; | |
| 259 | +} | |
| 260 | + | |
| 261 | +static inline int altera_cvp_chk_error(struct fpga_manager *mgr, size_t bytes) | |
| 262 | +{ | |
| 263 | + struct altera_cvp_conf *conf = mgr->priv; | |
| 264 | + u32 val; | |
| 265 | + | |
| 266 | + /* STEP 10 (optional) - check CVP_CONFIG_ERROR flag */ | |
| 267 | + pci_read_config_dword(conf->pci_dev, VSE_CVP_STATUS, &val); | |
| 268 | + if (val & VSE_CVP_STATUS_CFG_ERR) { | |
| 269 | + dev_err(&mgr->dev, "CVP_CONFIG_ERROR after %zu bytes!\n", | |
| 270 | + bytes); | |
| 271 | + return -EPROTO; | |
| 272 | + } | |
| 273 | + return 0; | |
| 274 | +} | |
| 275 | + | |
| 276 | +static int altera_cvp_write(struct fpga_manager *mgr, const char *buf, | |
| 277 | + size_t count) | |
| 278 | +{ | |
| 279 | + struct altera_cvp_conf *conf = mgr->priv; | |
| 280 | + const u32 *data; | |
| 281 | + size_t done, remaining; | |
| 282 | + int status = 0; | |
| 283 | + u32 mask; | |
| 284 | + | |
| 285 | + /* STEP 9 - write 32-bit data from RBF file to CVP data register */ | |
| 286 | + data = (u32 *)buf; | |
| 287 | + remaining = count; | |
| 288 | + done = 0; | |
| 289 | + | |
| 290 | + while (remaining >= 4) { | |
| 291 | + conf->write_data(conf, *data++); | |
| 292 | + done += 4; | |
| 293 | + remaining -= 4; | |
| 294 | + | |
| 295 | + /* | |
| 296 | + * STEP 10 (optional) and STEP 11 | |
| 297 | + * - check error flag | |
| 298 | + * - loop until data transfer completed | |
| 299 | + * Config images can be huge (more than 40 MiB), so | |
| 300 | + * only check after a new 4k data block has been written. | |
| 301 | + * This reduces the number of checks and speeds up the | |
| 302 | + * configuration process. | |
| 303 | + */ | |
| 304 | + if (altera_cvp_chkcfg && !(done % SZ_4K)) { | |
| 305 | + status = altera_cvp_chk_error(mgr, done); | |
| 306 | + if (status < 0) | |
| 307 | + return status; | |
| 308 | + } | |
| 309 | + } | |
| 310 | + | |
| 311 | + /* write up to 3 trailing bytes, if any */ | |
| 312 | + mask = BIT(remaining * 8) - 1; | |
| 313 | + if (mask) | |
| 314 | + conf->write_data(conf, *data & mask); | |
| 315 | + | |
| 316 | + if (altera_cvp_chkcfg) | |
| 317 | + status = altera_cvp_chk_error(mgr, count); | |
| 318 | + | |
| 319 | + return status; | |
| 320 | +} | |
| 321 | + | |
| 322 | +static int altera_cvp_write_complete(struct fpga_manager *mgr, | |
| 323 | + struct fpga_image_info *info) | |
| 324 | +{ | |
| 325 | + struct altera_cvp_conf *conf = mgr->priv; | |
| 326 | + struct pci_dev *pdev = conf->pci_dev; | |
| 327 | + int ret; | |
| 328 | + u32 mask; | |
| 329 | + u32 val; | |
| 330 | + | |
| 331 | + ret = altera_cvp_teardown(mgr, info); | |
| 332 | + if (ret) | |
| 333 | + return ret; | |
| 334 | + | |
| 335 | + /* STEP 16 - check CVP_CONFIG_ERROR_LATCHED bit */ | |
| 336 | + pci_read_config_dword(pdev, VSE_UNCOR_ERR_STATUS, &val); | |
| 337 | + if (val & VSE_UNCOR_ERR_CVP_CFG_ERR) { | |
| 338 | + dev_err(&mgr->dev, "detected CVP_CONFIG_ERROR_LATCHED!\n"); | |
| 339 | + return -EPROTO; | |
| 340 | + } | |
| 341 | + | |
| 342 | + /* STEP 17 - reset CVP_MODE and HIP_CLK_SEL bit */ | |
| 343 | + pci_read_config_dword(pdev, VSE_CVP_MODE_CTRL, &val); | |
| 344 | + val &= ~VSE_CVP_MODE_CTRL_HIP_CLK_SEL; | |
| 345 | + val &= ~VSE_CVP_MODE_CTRL_CVP_MODE; | |
| 346 | + pci_write_config_dword(pdev, VSE_CVP_MODE_CTRL, val); | |
| 347 | + | |
| 348 | + /* STEP 18 - poll PLD_CLK_IN_USE and USER_MODE bits */ | |
| 349 | + mask = VSE_CVP_STATUS_PLD_CLK_IN_USE | VSE_CVP_STATUS_USERMODE; | |
| 350 | + ret = altera_cvp_wait_status(conf, mask, mask, TIMEOUT_US); | |
| 351 | + if (ret) | |
| 352 | + dev_err(&mgr->dev, "PLD_CLK_IN_USE|USERMODE timeout\n"); | |
| 353 | + | |
| 354 | + return ret; | |
| 355 | +} | |
| 356 | + | |
| 357 | +static const struct fpga_manager_ops altera_cvp_ops = { | |
| 358 | + .state = altera_cvp_state, | |
| 359 | + .write_init = altera_cvp_write_init, | |
| 360 | + .write = altera_cvp_write, | |
| 361 | + .write_complete = altera_cvp_write_complete, | |
| 362 | +}; | |
| 363 | + | |
| 364 | +static ssize_t show_chkcfg(struct device_driver *dev, char *buf) | |
| 365 | +{ | |
| 366 | + return snprintf(buf, 3, "%d\n", altera_cvp_chkcfg); | |
| 367 | +} | |
| 368 | + | |
| 369 | +static ssize_t store_chkcfg(struct device_driver *drv, const char *buf, | |
| 370 | + size_t count) | |
| 371 | +{ | |
| 372 | + int ret; | |
| 373 | + | |
| 374 | + ret = kstrtobool(buf, &altera_cvp_chkcfg); | |
| 375 | + if (ret) | |
| 376 | + return ret; | |
| 377 | + | |
| 378 | + return count; | |
| 379 | +} | |
| 380 | + | |
| 381 | +static DRIVER_ATTR(chkcfg, 0600, show_chkcfg, store_chkcfg); | |
| 382 | + | |
| 383 | +static int altera_cvp_probe(struct pci_dev *pdev, | |
| 384 | + const struct pci_device_id *dev_id); | |
| 385 | +static void altera_cvp_remove(struct pci_dev *pdev); | |
| 386 | + | |
| 387 | +#define PCI_VENDOR_ID_ALTERA 0x1172 | |
| 388 | + | |
| 389 | +static struct pci_device_id altera_cvp_id_tbl[] = { | |
| 390 | + { PCI_VDEVICE(ALTERA, PCI_ANY_ID) }, | |
| 391 | + { } | |
| 392 | +}; | |
| 393 | +MODULE_DEVICE_TABLE(pci, altera_cvp_id_tbl); | |
| 394 | + | |
| 395 | +static struct pci_driver altera_cvp_driver = { | |
| 396 | + .name = DRV_NAME, | |
| 397 | + .id_table = altera_cvp_id_tbl, | |
| 398 | + .probe = altera_cvp_probe, | |
| 399 | + .remove = altera_cvp_remove, | |
| 400 | +}; | |
| 401 | + | |
| 402 | +static int altera_cvp_probe(struct pci_dev *pdev, | |
| 403 | + const struct pci_device_id *dev_id) | |
| 404 | +{ | |
| 405 | + struct altera_cvp_conf *conf; | |
| 406 | + u16 cmd, val; | |
| 407 | + int ret; | |
| 408 | + | |
| 409 | + /* | |
| 410 | + * First check if this is the expected FPGA device. PCI config | |
| 411 | + * space access works without enabling the PCI device, memory | |
| 412 | + * space access is enabled further down. | |
| 413 | + */ | |
| 414 | + pci_read_config_word(pdev, VSE_PCIE_EXT_CAP_ID, &val); | |
| 415 | + if (val != VSE_PCIE_EXT_CAP_ID_VAL) { | |
| 416 | + dev_err(&pdev->dev, "Wrong EXT_CAP_ID value 0x%x\n", val); | |
| 417 | + return -ENODEV; | |
| 418 | + } | |
| 419 | + | |
| 420 | + conf = devm_kzalloc(&pdev->dev, sizeof(*conf), GFP_KERNEL); | |
| 421 | + if (!conf) | |
| 422 | + return -ENOMEM; | |
| 423 | + | |
| 424 | + /* | |
| 425 | + * Enable memory BAR access. We cannot use pci_enable_device() here | |
| 426 | + * because it will make the driver unusable with FPGA devices that | |
| 427 | + * have additional big IOMEM resources (e.g. 4GiB BARs) on 32-bit | |
| 428 | + * platform. Such BARs will not have an assigned address range and | |
| 429 | + * pci_enable_device() will fail, complaining about not claimed BAR, | |
| 430 | + * even if the concerned BAR is not needed for FPGA configuration | |
| 431 | + * at all. Thus, enable the device via PCI config space command. | |
| 432 | + */ | |
| 433 | + pci_read_config_word(pdev, PCI_COMMAND, &cmd); | |
| 434 | + if (!(cmd & PCI_COMMAND_MEMORY)) { | |
| 435 | + cmd |= PCI_COMMAND_MEMORY; | |
| 436 | + pci_write_config_word(pdev, PCI_COMMAND, cmd); | |
| 437 | + } | |
| 438 | + | |
| 439 | + ret = pci_request_region(pdev, CVP_BAR, "CVP"); | |
| 440 | + if (ret) { | |
| 441 | + dev_err(&pdev->dev, "Requesting CVP BAR region failed\n"); | |
| 442 | + goto err_disable; | |
| 443 | + } | |
| 444 | + | |
| 445 | + conf->pci_dev = pdev; | |
| 446 | + conf->write_data = altera_cvp_write_data_iomem; | |
| 447 | + | |
| 448 | + conf->map = pci_iomap(pdev, CVP_BAR, 0); | |
| 449 | + if (!conf->map) { | |
| 450 | + dev_warn(&pdev->dev, "Mapping CVP BAR failed\n"); | |
| 451 | + conf->write_data = altera_cvp_write_data_config; | |
| 452 | + } | |
| 453 | + | |
| 454 | + snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s", | |
| 455 | + ALTERA_CVP_MGR_NAME, pci_name(pdev)); | |
| 456 | + | |
| 457 | + ret = fpga_mgr_register(&pdev->dev, conf->mgr_name, | |
| 458 | + &altera_cvp_ops, conf); | |
| 459 | + if (ret) | |
| 460 | + goto err_unmap; | |
| 461 | + | |
| 462 | + ret = driver_create_file(&altera_cvp_driver.driver, | |
| 463 | + &driver_attr_chkcfg); | |
| 464 | + if (ret) { | |
| 465 | + dev_err(&pdev->dev, "Can't create sysfs chkcfg file\n"); | |
| 466 | + fpga_mgr_unregister(&pdev->dev); | |
| 467 | + goto err_unmap; | |
| 468 | + } | |
| 469 | + | |
| 470 | + return 0; | |
| 471 | + | |
| 472 | +err_unmap: | |
| 473 | + pci_iounmap(pdev, conf->map); | |
| 474 | + pci_release_region(pdev, CVP_BAR); | |
| 475 | +err_disable: | |
| 476 | + cmd &= ~PCI_COMMAND_MEMORY; | |
| 477 | + pci_write_config_word(pdev, PCI_COMMAND, cmd); | |
| 478 | + return ret; | |
| 479 | +} | |
| 480 | + | |
| 481 | +static void altera_cvp_remove(struct pci_dev *pdev) | |
| 482 | +{ | |
| 483 | + struct fpga_manager *mgr = pci_get_drvdata(pdev); | |
| 484 | + struct altera_cvp_conf *conf = mgr->priv; | |
| 485 | + u16 cmd; | |
| 486 | + | |
| 487 | + driver_remove_file(&altera_cvp_driver.driver, &driver_attr_chkcfg); | |
| 488 | + fpga_mgr_unregister(&pdev->dev); | |
| 489 | + pci_iounmap(pdev, conf->map); | |
| 490 | + pci_release_region(pdev, CVP_BAR); | |
| 491 | + pci_read_config_word(pdev, PCI_COMMAND, &cmd); | |
| 492 | + cmd &= ~PCI_COMMAND_MEMORY; | |
| 493 | + pci_write_config_word(pdev, PCI_COMMAND, cmd); | |
| 494 | +} | |
| 495 | + | |
| 496 | +module_pci_driver(altera_cvp_driver); | |
| 497 | + | |
| 498 | +MODULE_LICENSE("GPL v2"); | |
| 499 | +MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>"); | |
| 500 | +MODULE_DESCRIPTION("Module to load Altera FPGA over CvP"); | |
| @@ -66,7 +66,7 @@ static int alt_hps2fpga_enable_show(struct fpga_bridge *bridge) · 65 unmodified lines | ||
| 66 | 66 | |
| 67 | 67 | /* The L3 REMAP register is write only, so keep a cached value. */ |
| 68 | 68 | static unsigned int l3_remap_shadow; |
| 69 | −static spinlock_t l3_remap_lock; | |
| 69 | +static DEFINE_SPINLOCK(l3_remap_lock); | |
| 70 | 70 | |
| 71 | 71 | static int _alt_hps2fpga_enable_set(struct altera_hps2fpga_data *priv, |
| 72 | 72 | bool enable) |
| @@ -143,9 +143,15 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) · 70 unmodified lines | ||
| 143 | 143 | int ret; |
| 144 | 144 | |
| 145 | 145 | of_id = of_match_device(altera_fpga_of_match, dev); |
| 146 | + if (!of_id) { | |
| 147 | + dev_err(dev, "failed to match device\n"); | |
| 148 | + return -ENODEV; | |
| 149 | + } | |
| 150 | + | |
| 146 | 151 | priv = (struct altera_hps2fpga_data *)of_id->data; |
| 147 | 152 | |
| 148 | − priv->bridge_reset = of_reset_control_get_by_index(dev->of_node, 0); | |
| 153 | + priv->bridge_reset = of_reset_control_get_exclusive_by_index(dev->of_node, | |
| 154 | + 0); | |
| 149 | 155 | if (IS_ERR(priv->bridge_reset)) { |
| 150 | 156 | dev_err(dev, "Could not get %s reset control\n", priv->name); |
| 151 | 157 | return PTR_ERR(priv->bridge_reset); |
| @@ -171,8 +177,6 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) · 19 unmodified lines | ||
| 171 | 177 | return -EBUSY; |
| 172 | 178 | } |
| 173 | 179 | |
| 174 | − spin_lock_init(&l3_remap_lock); | |
| 175 | − | |
| 176 | 180 | if (!of_property_read_u32(dev->of_node, "bridge-enable", &enable)) { |
| 177 | 181 | if (enable > 1) { |
| 178 | 182 | dev_warn(dev, "invalid bridge-enable %u > 1\n", enable); |
| @@ -0,0 +1,308 @@ | ||
| 1 | +/* | |
| 2 | + * Altera Passive Serial SPI Driver | |
| 3 | + * | |
| 4 | + * Copyright (c) 2017 United Western Technologies, Corporation | |
| 5 | + * | |
| 6 | + * Joshua Clayton <stillcompiling@gmail.com> | |
| 7 | + * | |
| 8 | + * This program is free software; you can redistribute it and/or modify it | |
| 9 | + * under the terms and conditions of the GNU General Public License, | |
| 10 | + * version 2, as published by the Free Software Foundation. | |
| 11 | + * | |
| 12 | + * Manage Altera FPGA firmware that is loaded over SPI using the passive | |
| 13 | + * serial configuration method. | |
| 14 | + * Firmware must be in binary "rbf" format. | |
| 15 | + * Works on Arria 10, Cyclone V and Stratix V. Should work on Cyclone series. | |
| 16 | + * May work on other Altera FPGAs. | |
| 17 | + */ | |
| 18 | + | |
| 19 | +#include <linux/bitrev.h> | |
| 20 | +#include <linux/delay.h> | |
| 21 | +#include <linux/fpga/fpga-mgr.h> | |
| 22 | +#include <linux/gpio/consumer.h> | |
| 23 | +#include <linux/module.h> | |
| 24 | +#include <linux/of_gpio.h> | |
| 25 | +#include <linux/of_device.h> | |
| 26 | +#include <linux/spi/spi.h> | |
| 27 | +#include <linux/sizes.h> | |
| 28 | + | |
| 29 | +enum altera_ps_devtype { | |
| 30 | + CYCLONE5, | |
| 31 | + ARRIA10, | |
| 32 | +}; | |
| 33 | + | |
| 34 | +struct altera_ps_data { | |
| 35 | + enum altera_ps_devtype devtype; | |
| 36 | + int status_wait_min_us; | |
| 37 | + int status_wait_max_us; | |
| 38 | + int t_cfg_us; | |
| 39 | + int t_st2ck_us; | |
| 40 | +}; | |
| 41 | + | |
| 42 | +struct altera_ps_conf { | |
| 43 | + struct gpio_desc *config; | |
| 44 | + struct gpio_desc *confd; | |
| 45 | + struct gpio_desc *status; | |
| 46 | + struct spi_device *spi; | |
| 47 | + const struct altera_ps_data *data; | |
| 48 | + u32 info_flags; | |
| 49 | + char mgr_name[64]; | |
| 50 | +}; | |
| 51 | + | |
| 52 | +/* | Arria 10 | Cyclone5 | Stratix5 | | |
| 53 | + * t_CF2ST0 | [; 600] | [; 600] | [; 600] |ns | |
| 54 | + * t_CFG | [2;] | [2;] | [2;] |µs | |
| 55 | + * t_STATUS | [268; 3000] | [268; 1506] | [268; 1506] |µs | |
| 56 | + * t_CF2ST1 | [; 3000] | [; 1506] | [; 1506] |µs | |
| 57 | + * t_CF2CK | [3010;] | [1506;] | [1506;] |µs | |
| 58 | + * t_ST2CK | [10;] | [2;] | [2;] |µs | |
| 59 | + * t_CD2UM | [175; 830] | [175; 437] | [175; 437] |µs | |
| 60 | + */ | |
| 61 | +static struct altera_ps_data c5_data = { | |
| 62 | + /* these values for Cyclone5 are compatible with Stratix5 */ | |
| 63 | + .devtype = CYCLONE5, | |
| 64 | + .status_wait_min_us = 268, | |
| 65 | + .status_wait_max_us = 1506, | |
| 66 | + .t_cfg_us = 2, | |
| 67 | + .t_st2ck_us = 2, | |
| 68 | +}; | |
| 69 | + | |
| 70 | +static struct altera_ps_data a10_data = { | |
| 71 | + .devtype = ARRIA10, | |
| 72 | + .status_wait_min_us = 268, /* min(t_STATUS) */ | |
| 73 | + .status_wait_max_us = 3000, /* max(t_CF2ST1) */ | |
| 74 | + .t_cfg_us = 2, /* max { min(t_CFG), max(tCF2ST0) } */ | |
| 75 | + .t_st2ck_us = 10, /* min(t_ST2CK) */ | |
| 76 | +}; | |
| 77 | + | |
| 78 | +static const struct of_device_id of_ef_match[] = { | |
| 79 | + { .compatible = "altr,fpga-passive-serial", .data = &c5_data }, | |
| 80 | + { .compatible = "altr,fpga-arria10-passive-serial", .data = &a10_data }, | |
| 81 | + {} | |
| 82 | +}; | |
| 83 | +MODULE_DEVICE_TABLE(of, of_ef_match); | |
| 84 | + | |
| 85 | +static enum fpga_mgr_states altera_ps_state(struct fpga_manager *mgr) | |
| 86 | +{ | |
| 87 | + struct altera_ps_conf *conf = mgr->priv; | |
| 88 | + | |
| 89 | + if (gpiod_get_value_cansleep(conf->status)) | |
| 90 | + return FPGA_MGR_STATE_RESET; | |
| 91 | + | |
| 92 | + return FPGA_MGR_STATE_UNKNOWN; | |
| 93 | +} | |
| 94 | + | |
| 95 | +static inline void altera_ps_delay(int delay_us) | |
| 96 | +{ | |
| 97 | + if (delay_us > 10) | |
| 98 | + usleep_range(delay_us, delay_us + 5); | |
| 99 | + else | |
| 100 | + udelay(delay_us); | |
| 101 | +} | |
| 102 | + | |
| 103 | +static int altera_ps_write_init(struct fpga_manager *mgr, | |
| 104 | + struct fpga_image_info *info, | |
| 105 | + const char *buf, size_t count) | |
| 106 | +{ | |
| 107 | + struct altera_ps_conf *conf = mgr->priv; | |
| 108 | + int min, max, waits; | |
| 109 | + int i; | |
| 110 | + | |
| 111 | + conf->info_flags = info->flags; | |
| 112 | + | |
| 113 | + if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) { | |
| 114 | + dev_err(&mgr->dev, "Partial reconfiguration not supported.\n"); | |
| 115 | + return -EINVAL; | |
| 116 | + } | |
| 117 | + | |
| 118 | + gpiod_set_value_cansleep(conf->config, 1); | |
| 119 | + | |
| 120 | + /* wait min reset pulse time */ | |
| 121 | + altera_ps_delay(conf->data->t_cfg_us); | |
| 122 | + | |
| 123 | + if (!gpiod_get_value_cansleep(conf->status)) { | |
| 124 | + dev_err(&mgr->dev, "Status pin failed to show a reset\n"); | |
| 125 | + return -EIO; | |
| 126 | + } | |
| 127 | + | |
| 128 | + gpiod_set_value_cansleep(conf->config, 0); | |
| 129 | + | |
| 130 | + min = conf->data->status_wait_min_us; | |
| 131 | + max = conf->data->status_wait_max_us; | |
| 132 | + waits = max / min; | |
| 133 | + if (max % min) | |
| 134 | + waits++; | |
| 135 | + | |
| 136 | + /* wait for max { max(t_STATUS), max(t_CF2ST1) } */ | |
| 137 | + for (i = 0; i < waits; i++) { | |
| 138 | + usleep_range(min, min + 10); | |
| 139 | + if (!gpiod_get_value_cansleep(conf->status)) { | |
| 140 | + /* wait for min(t_ST2CK)*/ | |
| 141 | + altera_ps_delay(conf->data->t_st2ck_us); | |
| 142 | + return 0; | |
| 143 | + } | |
| 144 | + } | |
| 145 | + | |
| 146 | + dev_err(&mgr->dev, "Status pin not ready.\n"); | |
| 147 | + return -EIO; | |
| 148 | +} | |
| 149 | + | |
| 150 | +static void rev_buf(char *buf, size_t len) | |
| 151 | +{ | |
| 152 | + u32 *fw32 = (u32 *)buf; | |
| 153 | + size_t extra_bytes = (len & 0x03); | |
| 154 | + const u32 *fw_end = (u32 *)(buf + len - extra_bytes); | |
| 155 | + | |
| 156 | + /* set buffer to lsb first */ | |
| 157 | + while (fw32 < fw_end) { | |
| 158 | + *fw32 = bitrev8x4(*fw32); | |
| 159 | + fw32++; | |
| 160 | + } | |
| 161 | + | |
| 162 | + if (extra_bytes) { | |
| 163 | + buf = (char *)fw_end; | |
| 164 | + while (extra_bytes) { | |
| 165 | + *buf = bitrev8(*buf); | |
| 166 | + buf++; | |
| 167 | + extra_bytes--; | |
| 168 | + } | |
| 169 | + } | |
| 170 | +} | |
| 171 | + | |
| 172 | +static int altera_ps_write(struct fpga_manager *mgr, const char *buf, | |
| 173 | + size_t count) | |
| 174 | +{ | |
| 175 | + struct altera_ps_conf *conf = mgr->priv; | |
| 176 | + const char *fw_data = buf; | |
| 177 | + const char *fw_data_end = fw_data + count; | |
| 178 | + | |
| 179 | + while (fw_data < fw_data_end) { | |
| 180 | + int ret; | |
| 181 | + size_t stride = min_t(size_t, fw_data_end - fw_data, SZ_4K); | |
| 182 | + | |
| 183 | + if (!(conf->info_flags & FPGA_MGR_BITSTREAM_LSB_FIRST)) | |
| 184 | + rev_buf((char *)fw_data, stride); | |
| 185 | + | |
| 186 | + ret = spi_write(conf->spi, fw_data, stride); | |
| 187 | + if (ret) { | |
| 188 | + dev_err(&mgr->dev, "spi error in firmware write: %d\n", | |
| 189 | + ret); | |
| 190 | + return ret; | |
| 191 | + } | |
| 192 | + fw_data += stride; | |
| 193 | + } | |
| 194 | + | |
| 195 | + return 0; | |
| 196 | +} | |
| 197 | + | |
| 198 | +static int altera_ps_write_complete(struct fpga_manager *mgr, | |
| 199 | + struct fpga_image_info *info) | |
| 200 | +{ | |
| 201 | + struct altera_ps_conf *conf = mgr->priv; | |
| 202 | + const char dummy[] = {0}; | |
| 203 | + int ret; | |
| 204 | + | |
| 205 | + if (gpiod_get_value_cansleep(conf->status)) { | |
| 206 | + dev_err(&mgr->dev, "Error during configuration.\n"); | |
| 207 | + return -EIO; | |
| 208 | + } | |
| 209 | + | |
| 210 | + if (!IS_ERR(conf->confd)) { | |
| 211 | + if (!gpiod_get_raw_value_cansleep(conf->confd)) { | |
| 212 | + dev_err(&mgr->dev, "CONF_DONE is inactive!\n"); | |
| 213 | + return -EIO; | |
| 214 | + } | |
| 215 | + } | |
| 216 | + | |
| 217 | + /* | |
| 218 | + * After CONF_DONE goes high, send two additional falling edges on DCLK | |
| 219 | + * to begin initialization and enter user mode | |
| 220 | + */ | |
| 221 | + ret = spi_write(conf->spi, dummy, 1); | |
| 222 | + if (ret) { | |
| 223 | + dev_err(&mgr->dev, "spi error during end sequence: %d\n", ret); | |
| 224 | + return ret; | |
| 225 | + } | |
| 226 | + | |
| 227 | + return 0; | |
| 228 | +} | |
| 229 | + | |
| 230 | +static const struct fpga_manager_ops altera_ps_ops = { | |
| 231 | + .state = altera_ps_state, | |
| 232 | + .write_init = altera_ps_write_init, | |
| 233 | + .write = altera_ps_write, | |
| 234 | + .write_complete = altera_ps_write_complete, | |
| 235 | +}; | |
| 236 | + | |
| 237 | +static int altera_ps_probe(struct spi_device *spi) | |
| 238 | +{ | |
| 239 | + struct altera_ps_conf *conf; | |
| 240 | + const struct of_device_id *of_id; | |
| 241 | + | |
| 242 | + conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL); | |
| 243 | + if (!conf) | |
| 244 | + return -ENOMEM; | |
| 245 | + | |
| 246 | + of_id = of_match_device(of_ef_match, &spi->dev); | |
| 247 | + if (!of_id) | |
| 248 | + return -ENODEV; | |
| 249 | + | |
| 250 | + conf->data = of_id->data; | |
| 251 | + conf->spi = spi; | |
| 252 | + conf->config = devm_gpiod_get(&spi->dev, "nconfig", GPIOD_OUT_HIGH); | |
| 253 | + if (IS_ERR(conf->config)) { | |
| 254 | + dev_err(&spi->dev, "Failed to get config gpio: %ld\n", | |
| 255 | + PTR_ERR(conf->config)); | |
| 256 | + return PTR_ERR(conf->config); | |
| 257 | + } | |
| 258 | + | |
| 259 | + conf->status = devm_gpiod_get(&spi->dev, "nstat", GPIOD_IN); | |
| 260 | + if (IS_ERR(conf->status)) { | |
| 261 | + dev_err(&spi->dev, "Failed to get status gpio: %ld\n", | |
| 262 | + PTR_ERR(conf->status)); | |
| 263 | + return PTR_ERR(conf->status); | |
| 264 | + } | |
| 265 | + | |
| 266 | + conf->confd = devm_gpiod_get(&spi->dev, "confd", GPIOD_IN); | |
| 267 | + if (IS_ERR(conf->confd)) { | |
| 268 | + dev_warn(&spi->dev, "Not using confd gpio: %ld\n", | |
| 269 | + PTR_ERR(conf->confd)); | |
| 270 | + } | |
| 271 | + | |
| 272 | + /* Register manager with unique name */ | |
| 273 | + snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s", | |
| 274 | + dev_driver_string(&spi->dev), dev_name(&spi->dev)); | |
| 275 | + | |
| 276 | + return fpga_mgr_register(&spi->dev, conf->mgr_name, | |
| 277 | + &altera_ps_ops, conf); | |
| 278 | +} | |
| 279 | + | |
| 280 | +static int altera_ps_remove(struct spi_device *spi) | |
| 281 | +{ | |
| 282 | + fpga_mgr_unregister(&spi->dev); | |
| 283 | + | |
| 284 | + return 0; | |
| 285 | +} | |
| 286 | + | |
| 287 | +static const struct spi_device_id altera_ps_spi_ids[] = { | |
| 288 | + {"cyclone-ps-spi", 0}, | |
| 289 | + {} | |
| 290 | +}; | |
| 291 | +MODULE_DEVICE_TABLE(spi, altera_ps_spi_ids); | |
| 292 | + | |
| 293 | +static struct spi_driver altera_ps_driver = { | |
| 294 | + .driver = { | |
| 295 | + .name = "altera-ps-spi", | |
| 296 | + .owner = THIS_MODULE, | |
| 297 | + .of_match_table = of_match_ptr(of_ef_match), | |
| 298 | + }, | |
| 299 | + .id_table = altera_ps_spi_ids, | |
| 300 | + .probe = altera_ps_probe, | |
| 301 | + .remove = altera_ps_remove, | |
| 302 | +}; | |
| 303 | + | |
| 304 | +module_spi_driver(altera_ps_driver) | |
| 305 | + | |
| 306 | +MODULE_LICENSE("GPL v2"); | |
| 307 | +MODULE_AUTHOR("Joshua Clayton <stillcompiling@gmail.com>"); | |
| 308 | +MODULE_DESCRIPTION("Module to load Altera FPGA firmware over SPI"); | |
| @@ -319,8 +319,8 @@ static int child_regions_with_firmware(struct device_node *overlay) · 318 unmodified lines | ||
| 319 | 319 | of_node_put(child_region); |
| 320 | 320 | |
| 321 | 321 | if (ret) |
| 322 | − pr_err("firmware-name not allowed in child FPGA region: %s", | |
| 323 | − child_region->full_name); | |
| 322 | + pr_err("firmware-name not allowed in child FPGA region: %pOF", | |
| 323 | + child_region); | |
| 324 | 324 | |
| 325 | 325 | return ret; |
| 326 | 326 | } |
| @@ -475,7 +475,7 @@ static ssize_t fsi_slave_sysfs_raw_write(struct file *file, · 474 unmodified lines | ||
| 475 | 475 | return count; |
| 476 | 476 | } |
| 477 | 477 | |
| 478 | −static struct bin_attribute fsi_slave_raw_attr = { | |
| 478 | +static const struct bin_attribute fsi_slave_raw_attr = { | |
| 479 | 479 | .attr = { |
| 480 | 480 | .name = "raw", |
| 481 | 481 | .mode = 0600, |
| @@ -499,7 +499,7 @@ static ssize_t fsi_slave_sysfs_term_write(struct file *file, · 17 unmodified lines | ||
| 499 | 499 | return count; |
| 500 | 500 | } |
| 501 | 501 | |
| 502 | −static struct bin_attribute fsi_slave_term_attr = { | |
| 502 | +static const struct bin_attribute fsi_slave_term_attr = { | |
| 503 | 503 | .attr = { |
| 504 | 504 | .name = "term", |
| 505 | 505 | .mode = 0200, |
| @@ -57,12 +57,6 @@ static int put_scom(struct scom_device *scom_dev, uint64_t value, · 56 unmodified lines | ||
| 57 | 57 | int rc; |
| 58 | 58 | uint32_t data; |
| 59 | 59 | |
| 60 | − data = cpu_to_be32(SCOM_RESET_CMD); | |
| 61 | − rc = fsi_device_write(scom_dev->fsi_dev, SCOM_RESET_REG, &data, | |
| 62 | − sizeof(uint32_t)); | |
| 63 | − if (rc) | |
| 64 | − return rc; | |
| 65 | − | |
| 66 | 60 | data = cpu_to_be32((value >> 32) & 0xffffffff); |
| 67 | 61 | rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA0_REG, &data, |
| 68 | 62 | sizeof(uint32_t)); |
| @@ -186,6 +180,7 @@ static const struct file_operations scom_fops = { · 117 unmodified lines | ||
| 186 | 180 | |
| 187 | 181 | static int scom_probe(struct device *dev) |
| 188 | 182 | { |
| 183 | + uint32_t data; | |
| 189 | 184 | struct fsi_device *fsi_dev = to_fsi_dev(dev); |
| 190 | 185 | struct scom_device *scom; |
| 191 | 186 | |
| @@ -202,6 +197,9 @@ static int scom_probe(struct device *dev) · 10 unmodified lines | ||
| 202 | 197 | scom->mdev.parent = dev; |
| 203 | 198 | list_add(&scom->link, &scom_devices); |
| 204 | 199 | |
| 200 | + data = cpu_to_be32(SCOM_RESET_CMD); | |
| 201 | + fsi_device_write(fsi_dev, SCOM_RESET_REG, &data, sizeof(uint32_t)); | |
| 202 | + | |
| 205 | 203 | return misc_register(&scom->mdev); |
| 206 | 204 | } |
| 207 | 205 | |
| @@ -177,6 +177,11 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, · 176 unmodified lines | ||
| 177 | 177 | &vmbus_connection.chn_msg_list); |
| 178 | 178 | spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); |
| 179 | 179 | |
| 180 | + if (newchannel->rescind) { | |
| 181 | + err = -ENODEV; | |
| 182 | + goto error_free_gpadl; | |
| 183 | + } | |
| 184 | + | |
| 180 | 185 | ret = vmbus_post_msg(open_msg, |
| 181 | 186 | sizeof(struct vmbus_channel_open_channel), true); |
| 182 | 187 | |
| @@ -421,6 +426,11 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, · 238 unmodified lines | ||
| 421 | 426 | |
| 422 | 427 | spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); |
| 423 | 428 | |
| 429 | + if (channel->rescind) { | |
| 430 | + ret = -ENODEV; | |
| 431 | + goto cleanup; | |
| 432 | + } | |
| 433 | + | |
| 424 | 434 | ret = vmbus_post_msg(gpadlmsg, msginfo->msgsize - |
| 425 | 435 | sizeof(*msginfo), true); |
| 426 | 436 | if (ret != 0) |
| @@ -494,6 +504,10 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle) · 67 unmodified lines | ||
| 494 | 504 | list_add_tail(&info->msglistentry, |
| 495 | 505 | &vmbus_connection.chn_msg_list); |
| 496 | 506 | spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); |
| 507 | + | |
| 508 | + if (channel->rescind) | |
| 509 | + goto post_msg_err; | |
| 510 | + | |
| 497 | 511 | ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_gpadl_teardown), |
| 498 | 512 | true); |
| 499 | 513 | |
| @@ -451,6 +451,12 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) · 450 unmodified lines | ||
| 451 | 451 | /* Make sure this is a new offer */ |
| 452 | 452 | mutex_lock(&vmbus_connection.channel_mutex); |
| 453 | 453 | |
| 454 | + /* | |
| 455 | + * Now that we have acquired the channel_mutex, | |
| 456 | + * we can release the potentially racing rescind thread. | |
| 457 | + */ | |
| 458 | + atomic_dec(&vmbus_connection.offer_in_progress); | |
| 459 | + | |
| 454 | 460 | list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { |
| 455 | 461 | if (!uuid_le_cmp(channel->offermsg.offer.if_type, |
| 456 | 462 | newchannel->offermsg.offer.if_type) && |
| @@ -481,7 +487,6 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) · 24 unmodified lines | ||
| 481 | 487 | channel->num_sc++; |
| 482 | 488 | spin_unlock_irqrestore(&channel->lock, flags); |
| 483 | 489 | } else { |
| 484 | − atomic_dec(&vmbus_connection.offer_in_progress); | |
| 485 | 490 | goto err_free_chan; |
| 486 | 491 | } |
| 487 | 492 | } |
| @@ -510,7 +515,6 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) · 22 unmodified lines | ||
| 510 | 515 | if (!fnew) { |
| 511 | 516 | if (channel->sc_creation_callback != NULL) |
| 512 | 517 | channel->sc_creation_callback(newchannel); |
| 513 | − atomic_dec(&vmbus_connection.offer_in_progress); | |
| 514 | 518 | return; |
| 515 | 519 | } |
| 516 | 520 | |
| @@ -541,7 +545,7 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) · 24 unmodified lines | ||
| 541 | 545 | goto err_deq_chan; |
| 542 | 546 | } |
| 543 | 547 | |
| 544 | − atomic_dec(&vmbus_connection.offer_in_progress); | |
| 548 | + newchannel->probe_done = true; | |
| 545 | 549 | return; |
| 546 | 550 | |
| 547 | 551 | err_deq_chan: |
| @@ -882,8 +886,27 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr) · 334 unmodified lines | ||
| 882 | 886 | channel->rescind = true; |
| 883 | 887 | spin_unlock_irqrestore(&channel->lock, flags); |
| 884 | 888 | |
| 889 | + /* | |
| 890 | + * Now that we have posted the rescind state, perform | |
| 891 | + * rescind related cleanup. | |
| 892 | + */ | |
| 885 | 893 | vmbus_rescind_cleanup(channel); |
| 886 | 894 | |
| 895 | + /* | |
| 896 | + * Now wait for offer handling to complete. | |
| 897 | + */ | |
| 898 | + while (READ_ONCE(channel->probe_done) == false) { | |
| 899 | + /* | |
| 900 | + * We wait here until any channel offer is currently | |
| 901 | + * being processed. | |
| 902 | + */ | |
| 903 | + msleep(1); | |
| 904 | + } | |
| 905 | + | |
| 906 | + /* | |
| 907 | + * At this point, the rescind handling can proceed safely. | |
| 908 | + */ | |
| 909 | + | |
| 887 | 910 | if (channel->device_obj) { |
| 888 | 911 | if (channel->chn_rescind_callback) { |
| 889 | 912 | channel->chn_rescind_callback(channel); |
| @@ -584,10 +584,6 @@ static int hv_memory_notifier(struct notifier_block *nb, unsigned long val, · 583 unmodified lines | ||
| 584 | 584 | |
| 585 | 585 | switch (val) { |
| 586 | 586 | case MEM_ONLINE: |
| 587 | − spin_lock_irqsave(&dm_device.ha_lock, flags); | |
| 588 | − dm_device.num_pages_onlined += mem->nr_pages; | |
| 589 | − spin_unlock_irqrestore(&dm_device.ha_lock, flags); | |
| 590 | − /* Fall through */ | |
| 591 | 587 | case MEM_CANCEL_ONLINE: |
| 592 | 588 | if (dm_device.ha_waiting) { |
| 593 | 589 | dm_device.ha_waiting = false; |
| @@ -644,6 +640,9 @@ static void hv_page_online_one(struct hv_hotadd_state *has, struct page *pg) · 50 unmodified lines | ||
| 644 | 640 | __online_page_set_limits(pg); |
| 645 | 641 | __online_page_increment_counters(pg); |
| 646 | 642 | __online_page_free(pg); |
| 643 | + | |
| 644 | + WARN_ON_ONCE(!spin_is_locked(&dm_device.ha_lock)); | |
| 645 | + dm_device.num_pages_onlined++; | |
| 647 | 646 | } |
| 648 | 647 | |
| 649 | 648 | static void hv_bring_pgs_online(struct hv_hotadd_state *has, |
| @@ -1036,8 +1035,8 @@ static void process_info(struct hv_dynmem_device *dm, struct dm_info_msg *msg) · 386 unmodified lines | ||
| 1036 | 1035 | if (info_hdr->data_size == sizeof(__u64)) { |
| 1037 | 1036 | __u64 *max_page_count = (__u64 *)&info_hdr[1]; |
| 1038 | 1037 | |
| 1039 | − pr_info("INFO_TYPE_MAX_PAGE_CNT = %llu\n", | |
| 1040 | − *max_page_count); | |
| 1038 | + pr_info("Max. dynamic memory size: %llu MB\n", | |
| 1039 | + (*max_page_count) >> (20 - PAGE_SHIFT)); | |
| 1041 | 1040 | } |
| 1042 | 1041 | |
| 1043 | 1042 | break; |
| @@ -1656,6 +1655,7 @@ static int balloon_probe(struct hv_device *dev, · 612 unmodified lines | ||
| 1656 | 1655 | } |
| 1657 | 1656 | |
| 1658 | 1657 | dm_device.state = DM_INITIALIZED; |
| 1658 | + last_post_time = jiffies; | |
| 1659 | 1659 | |
| 1660 | 1660 | return 0; |
| 1661 | 1661 | |
| @@ -304,7 +304,7 @@ static int process_ob_ipinfo(void *in_msg, void *out_msg, int op) · 303 unmodified lines | ||
| 304 | 304 | strlen((char *)in->body.kvp_ip_val.adapter_id), |
| 305 | 305 | UTF16_HOST_ENDIAN, |
| 306 | 306 | (wchar_t *)out->kvp_ip_val.adapter_id, |
| 307 | − MAX_IP_ADDR_SIZE); | |
| 307 | + MAX_ADAPTER_ID_SIZE); | |
| 308 | 308 | if (len < 0) |
| 309 | 309 | return len; |
| 310 | 310 | |
| @@ -29,6 +29,7 @@ · 28 unmodified lines | ||
| 29 | 29 | #include <linux/uio.h> |
| 30 | 30 | #include <linux/vmalloc.h> |
| 31 | 31 | #include <linux/slab.h> |
| 32 | +#include <linux/prefetch.h> | |
| 32 | 33 | |
| 33 | 34 | #include "hyperv_vmbus.h" |
| 34 | 35 | |
| @@ -94,30 +95,6 @@ hv_set_next_write_location(struct hv_ring_buffer_info *ring_info, · 59 unmodified lines | ||
| 94 | 95 | ring_info->ring_buffer->write_index = next_write_location; |
| 95 | 96 | } |
| 96 | 97 | |
| 97 | −/* Get the next read location for the specified ring buffer. */ | |
| 98 | −static inline u32 | |
| 99 | −hv_get_next_read_location(const struct hv_ring_buffer_info *ring_info) | |
| 100 | −{ | |
| 101 | − return ring_info->ring_buffer->read_index; | |
| 102 | −} | |
| 103 | − | |
| 104 | −/* | |
| 105 | − * Get the next read location + offset for the specified ring buffer. | |
| 106 | − * This allows the caller to skip. | |
| 107 | − */ | |
| 108 | −static inline u32 | |
| 109 | −hv_get_next_readlocation_withoffset(const struct hv_ring_buffer_info *ring_info, | |
| 110 | − u32 offset) | |
| 111 | −{ | |
| 112 | − u32 next = ring_info->ring_buffer->read_index; | |
| 113 | − | |
| 114 | − next += offset; | |
| 115 | − if (next >= ring_info->ring_datasize) | |
| 116 | − next -= ring_info->ring_datasize; | |
| 117 | − | |
| 118 | − return next; | |
| 119 | −} | |
| 120 | − | |
| 121 | 98 | /* Set the next read location for the specified ring buffer. */ |
| 122 | 99 | static inline void |
| 123 | 100 | hv_set_next_read_location(struct hv_ring_buffer_info *ring_info, |
| @@ -141,29 +118,6 @@ hv_get_ring_bufferindices(struct hv_ring_buffer_info *ring_info) · 17 unmodified lines | ||
| 141 | 118 | return (u64)ring_info->ring_buffer->write_index << 32; |
| 142 | 119 | } |
| 143 | 120 | |
| 144 | −/* | |
| 145 | − * Helper routine to copy to source from ring buffer. | |
| 146 | − * Assume there is enough room. Handles wrap-around in src case only!! | |
| 147 | − */ | |
| 148 | −static u32 hv_copyfrom_ringbuffer( | |
| 149 | − const struct hv_ring_buffer_info *ring_info, | |
| 150 | − void *dest, | |
| 151 | − u32 destlen, | |
| 152 | − u32 start_read_offset) | |
| 153 | −{ | |
| 154 | − void *ring_buffer = hv_get_ring_buffer(ring_info); | |
| 155 | − u32 ring_buffer_size = hv_get_ring_buffersize(ring_info); | |
| 156 | − | |
| 157 | − memcpy(dest, ring_buffer + start_read_offset, destlen); | |
| 158 | − | |
| 159 | − start_read_offset += destlen; | |
| 160 | − if (start_read_offset >= ring_buffer_size) | |
| 161 | − start_read_offset -= ring_buffer_size; | |
| 162 | − | |
| 163 | − return start_read_offset; | |
| 164 | −} | |
| 165 | − | |
| 166 | − | |
| 167 | 121 | /* |
| 168 | 122 | * Helper routine to copy from source to ring buffer. |
| 169 | 123 | * Assume there is enough room. Handles wrap-around in dest case only!! |
| @@ -334,33 +288,22 @@ int hv_ringbuffer_write(struct vmbus_channel *channel, · 164 unmodified lines | ||
| 334 | 288 | return 0; |
| 335 | 289 | } |
| 336 | 290 | |
| 337 | −static inline void | |
| 338 | −init_cached_read_index(struct hv_ring_buffer_info *rbi) | |
| 339 | −{ | |
| 340 | − rbi->cached_read_index = rbi->ring_buffer->read_index; | |
| 341 | −} | |
| 342 | − | |
| 343 | 291 | int hv_ringbuffer_read(struct vmbus_channel *channel, |
| 344 | 292 | void *buffer, u32 buflen, u32 *buffer_actual_len, |
| 345 | 293 | u64 *requestid, bool raw) |
| 346 | 294 | { |
| 347 | − u32 bytes_avail_toread; | |
| 348 | − u32 next_read_location; | |
| 349 | − u64 prev_indices = 0; | |
| 350 | − struct vmpacket_descriptor desc; | |
| 351 | − u32 offset; | |
| 352 | − u32 packetlen; | |
| 353 | − struct hv_ring_buffer_info *inring_info = &channel->inbound; | |
| 354 | − | |
| 355 | − if (buflen <= 0) | |
| 295 | + struct vmpacket_descriptor *desc; | |
| 296 | + u32 packetlen, offset; | |
| 297 | + | |
| 298 | + if (unlikely(buflen == 0)) | |
| 356 | 299 | return -EINVAL; |
| 357 | 300 | |
| 358 | 301 | *buffer_actual_len = 0; |
| 359 | 302 | *requestid = 0; |
| 360 | 303 | |
| 361 | − bytes_avail_toread = hv_get_bytes_to_read(inring_info); | |
| 362 | 304 | /* Make sure there is something to read */ |
| 363 | − if (bytes_avail_toread < sizeof(desc)) { | |
| 305 | + desc = hv_pkt_iter_first(channel); | |
| 306 | + if (desc == NULL) { | |
| 364 | 307 | /* |
| 365 | 308 | * No error is set when there is even no header, drivers are |
| 366 | 309 | * supposed to analyze buffer_actual_len. |
| @@ -368,48 +311,22 @@ int hv_ringbuffer_read(struct vmbus_channel *channel, · 1 unmodified line | ||
| 368 | 311 | return 0; |
| 369 | 312 | } |
| 370 | 313 | |
| 371 | − init_cached_read_index(inring_info); | |
| 372 | − | |
| 373 | − next_read_location = hv_get_next_read_location(inring_info); | |
| 374 | − next_read_location = hv_copyfrom_ringbuffer(inring_info, &desc, | |
| 375 | − sizeof(desc), | |
| 376 | − next_read_location); | |
| 377 | − | |
| 378 | − offset = raw ? 0 : (desc.offset8 << 3); | |
| 379 | − packetlen = (desc.len8 << 3) - offset; | |
| 314 | + offset = raw ? 0 : (desc->offset8 << 3); | |
| 315 | + packetlen = (desc->len8 << 3) - offset; | |
| 380 | 316 | *buffer_actual_len = packetlen; |
| 381 | − *requestid = desc.trans_id; | |
| 382 | − | |
| 383 | − if (bytes_avail_toread < packetlen + offset) | |
| 384 | − return -EAGAIN; | |
| 317 | + *requestid = desc->trans_id; | |
| 385 | 318 | |
| 386 | − if (packetlen > buflen) | |
| 319 | + if (unlikely(packetlen > buflen)) | |
| 387 | 320 | return -ENOBUFS; |
| 388 | 321 | |
| 389 | − next_read_location = | |
| 390 | − hv_get_next_readlocation_withoffset(inring_info, offset); | |
| 322 | + /* since ring is double mapped, only one copy is necessary */ | |
| 323 | + memcpy(buffer, (const char *)desc + offset, packetlen); | |
| 391 | 324 | |
| 392 | − next_read_location = hv_copyfrom_ringbuffer(inring_info, | |
| 393 | − buffer, | |
| 394 | − packetlen, | |
| 395 | − next_read_location); | |
| 325 | + /* Advance ring index to next packet descriptor */ | |
| 326 | + __hv_pkt_iter_next(channel, desc); | |
| 396 | 327 | |
| 397 | − next_read_location = hv_copyfrom_ringbuffer(inring_info, | |
| 398 | − &prev_indices, | |
| 399 | − sizeof(u64), | |
| 400 | − next_read_location); | |
| 401 | − | |
| 402 | − /* | |
| 403 | − * Make sure all reads are done before we update the read index since | |
| 404 | − * the writer may start writing to the read area once the read index | |
| 405 | − * is updated. | |
| 406 | − */ | |
| 407 | − virt_mb(); | |
| 408 | − | |
| 409 | − /* Update the read index */ | |
| 410 | − hv_set_next_read_location(inring_info, next_read_location); | |
| 411 | − | |
| 412 | − hv_signal_on_read(channel); | |
| 328 | + /* Notify host of update */ | |
| 329 | + hv_pkt_iter_close(channel); | |
| 413 | 330 | |
| 414 | 331 | return 0; |
| 415 | 332 | } |
| @@ -440,14 +357,16 @@ static u32 hv_pkt_iter_avail(const struct hv_ring_buffer_info *rbi) · 24 unmodified lines | ||
| 440 | 357 | struct vmpacket_descriptor *hv_pkt_iter_first(struct vmbus_channel *channel) |
| 441 | 358 | { |
| 442 | 359 | struct hv_ring_buffer_info *rbi = &channel->inbound; |
| 443 | − | |
| 444 | − /* set state for later hv_signal_on_read() */ | |
| 445 | − init_cached_read_index(rbi); | |
| 360 | + struct vmpacket_descriptor *desc; | |
| 446 | 361 | |
| 447 | 362 | if (hv_pkt_iter_avail(rbi) < sizeof(struct vmpacket_descriptor)) |
| 448 | 363 | return NULL; |
| 449 | 364 | |
| 450 | − return hv_get_ring_buffer(rbi) + rbi->priv_read_index; | |
| 365 | + desc = hv_get_ring_buffer(rbi) + rbi->priv_read_index; | |
| 366 | + if (desc) | |
| 367 | + prefetch((char *)desc + (desc->len8 << 3)); | |
| 368 | + | |
| 369 | + return desc; | |
| 451 | 370 | } |
| 452 | 371 | EXPORT_SYMBOL_GPL(hv_pkt_iter_first); |
| 453 | 372 | |
| @@ -471,10 +390,7 @@ __hv_pkt_iter_next(struct vmbus_channel *channel, · 17 unmodified lines | ||
| 471 | 390 | rbi->priv_read_index -= dsize; |
| 472 | 391 | |
| 473 | 392 | /* more data? */ |
| 474 | − if (hv_pkt_iter_avail(rbi) < sizeof(struct vmpacket_descriptor)) | |
| 475 | − return NULL; | |
| 476 | − else | |
| 477 | − return hv_get_ring_buffer(rbi) + rbi->priv_read_index; | |
| 393 | + return hv_pkt_iter_first(channel); | |
| 478 | 394 | } |
| 479 | 395 | EXPORT_SYMBOL_GPL(__hv_pkt_iter_next); |
| 480 | 396 | |
| @@ -484,6 +400,7 @@ EXPORT_SYMBOL_GPL(__hv_pkt_iter_next); · 3 unmodified lines | ||
| 484 | 400 | void hv_pkt_iter_close(struct vmbus_channel *channel) |
| 485 | 401 | { |
| 486 | 402 | struct hv_ring_buffer_info *rbi = &channel->inbound; |
| 403 | + u32 orig_write_sz = hv_get_bytes_to_write(rbi); | |
| 487 | 404 | |
| 488 | 405 | /* |
| 489 | 406 | * Make sure all reads are done before we update the read index since |
| @@ -493,6 +410,40 @@ void hv_pkt_iter_close(struct vmbus_channel *channel) · 3 unmodified lines | ||
| 493 | 410 | virt_rmb(); |
| 494 | 411 | rbi->ring_buffer->read_index = rbi->priv_read_index; |
| 495 | 412 | |
| 496 | − hv_signal_on_read(channel); | |
| 413 | + /* | |
| 414 | + * Issue a full memory barrier before making the signaling decision. | |
| 415 | + * Here is the reason for having this barrier: | |
| 416 | + * If the reading of the pend_sz (in this function) | |
| 417 | + * were to be reordered and read before we commit the new read | |
| 418 | + * index (in the calling function) we could | |
| 419 | + * have a problem. If the host were to set the pending_sz after we | |
| 420 | + * have sampled pending_sz and go to sleep before we commit the | |
| 421 | + * read index, we could miss sending the interrupt. Issue a full | |
| 422 | + * memory barrier to address this. | |
| 423 | + */ | |
| 424 | + virt_mb(); | |
| 425 | + | |
| 426 | + /* If host has disabled notifications then skip */ | |
| 427 | + if (rbi->ring_buffer->interrupt_mask) | |
| 428 | + return; | |
| 429 | + | |
| 430 | + if (rbi->ring_buffer->feature_bits.feat_pending_send_sz) { | |
| 431 | + u32 pending_sz = READ_ONCE(rbi->ring_buffer->pending_send_sz); | |
| 432 | + | |
| 433 | + /* | |
| 434 | + * If there was space before we began iteration, | |
| 435 | + * then host was not blocked. Also handles case where | |
| 436 | + * pending_sz is zero then host has nothing pending | |
| 437 | + * and does not need to be signaled. | |
| 438 | + */ | |
| 439 | + if (orig_write_sz > pending_sz) | |
| 440 | + return; | |
| 441 | + | |
| 442 | + /* If pending write will not fit, don't give false hope. */ | |
| 443 | + if (hv_get_bytes_to_write(rbi) < pending_sz) | |
| 444 | + return; | |
| 445 | + } | |
| 446 | + | |
| 447 | + vmbus_setevent(channel); | |
| 497 | 448 | } |
| 498 | 449 | EXPORT_SYMBOL_GPL(hv_pkt_iter_close); |
| @@ -940,6 +940,9 @@ static void vmbus_chan_sched(struct hv_per_cpu_context *hv_cpu) · 939 unmodified lines | ||
| 940 | 940 | if (channel->offermsg.child_relid != relid) |
| 941 | 941 | continue; |
| 942 | 942 | |
| 943 | + if (channel->rescind) | |
| 944 | + continue; | |
| 945 | + | |
| 943 | 946 | switch (channel->callback_mode) { |
| 944 | 947 | case HV_CALL_ISR: |
| 945 | 948 | vmbus_channel_isr(channel); |
| @@ -70,13 +70,13 @@ config CORESIGHT_SOURCE_ETM4X · 69 unmodified lines | ||
| 70 | 70 | for instruction level tracing. Depending on the implemented version |
| 71 | 71 | data tracing may also be available. |
| 72 | 72 | |
| 73 | −config CORESIGHT_QCOM_REPLICATOR | |
| 74 | − bool "Qualcomm CoreSight Replicator driver" | |
| 73 | +config CORESIGHT_DYNAMIC_REPLICATOR | |
| 74 | + bool "CoreSight Programmable Replicator driver" | |
| 75 | 75 | depends on CORESIGHT_LINKS_AND_SINKS |
| 76 | 76 | help |
| 77 | − This enables support for Qualcomm CoreSight link driver. The | |
| 78 | − programmable ATB replicator sends the ATB trace stream from the | |
| 79 | − ETB/ETF to the TPIUi and ETR. | |
| 77 | + This enables support for dynamic CoreSight replicator link driver. | |
| 78 | + The programmable ATB replicator allows independent filtering of the | |
| 79 | + trace data based on the traceid. | |
| 80 | 80 | |
| 81 | 81 | config CORESIGHT_STM |
| 82 | 82 | bool "CoreSight System Trace Macrocell driver" |
| @@ -14,6 +14,6 @@ obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \ · 13 unmodified lines | ||
| 14 | 14 | coresight-etm3x-sysfs.o |
| 15 | 15 | obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o \ |
| 16 | 16 | coresight-etm4x-sysfs.o |
| 17 | −obj-$(CONFIG_CORESIGHT_QCOM_REPLICATOR) += coresight-replicator-qcom.o | |
| 17 | +obj-$(CONFIG_CORESIGHT_DYNAMIC_REPLICATOR) += coresight-dynamic-replicator.o | |
| 18 | 18 | obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o |
| 19 | 19 | obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o |
| @@ -667,7 +667,7 @@ static int debug_remove(struct amba_device *adev) · 666 unmodified lines | ||
| 667 | 667 | return 0; |
| 668 | 668 | } |
| 669 | 669 | |
| 670 | −static struct amba_id debug_ids[] = { | |
| 670 | +static const struct amba_id debug_ids[] = { | |
| 671 | 671 | { /* Debug for Cortex-A53 */ |
| 672 | 672 | .id = 0x000bbd03, |
| 673 | 673 | .mask = 0x000fffff, |
| @@ -95,6 +95,28 @@ static const struct coresight_ops replicator_cs_ops = { · 94 unmodified lines | ||
| 95 | 95 | .link_ops = &replicator_link_ops, |
| 96 | 96 | }; |
| 97 | 97 | |
| 98 | +#define coresight_replicator_reg(name, offset) \ | |
| 99 | + coresight_simple_reg32(struct replicator_state, name, offset) | |
| 100 | + | |
| 101 | +coresight_replicator_reg(idfilter0, REPLICATOR_IDFILTER0); | |
| 102 | +coresight_replicator_reg(idfilter1, REPLICATOR_IDFILTER1); | |
| 103 | + | |
| 104 | +static struct attribute *replicator_mgmt_attrs[] = { | |
| 105 | + &dev_attr_idfilter0.attr, | |
| 106 | + &dev_attr_idfilter1.attr, | |
| 107 | + NULL, | |
| 108 | +}; | |
| 109 | + | |
| 110 | +static const struct attribute_group replicator_mgmt_group = { | |
| 111 | + .attrs = replicator_mgmt_attrs, | |
| 112 | + .name = "mgmt", | |
| 113 | +}; | |
| 114 | + | |
| 115 | +static const struct attribute_group *replicator_groups[] = { | |
| 116 | + &replicator_mgmt_group, | |
| 117 | + NULL, | |
| 118 | +}; | |
| 119 | + | |
| 98 | 120 | static int replicator_probe(struct amba_device *adev, const struct amba_id *id) |
| 99 | 121 | { |
| 100 | 122 | int ret; |
| @@ -139,11 +161,11 @@ static int replicator_probe(struct amba_device *adev, const struct amba_id *id) · 38 unmodified lines | ||
| 139 | 161 | desc.ops = &replicator_cs_ops; |
| 140 | 162 | desc.pdata = adev->dev.platform_data; |
| 141 | 163 | desc.dev = &adev->dev; |
| 164 | + desc.groups = replicator_groups; | |
| 142 | 165 | drvdata->csdev = coresight_register(&desc); |
| 143 | 166 | if (IS_ERR(drvdata->csdev)) |
| 144 | 167 | return PTR_ERR(drvdata->csdev); |
| 145 | 168 | |
| 146 | − dev_info(dev, "%s initialized\n", (char *)id->data); | |
| 147 | 169 | return 0; |
| 148 | 170 | } |
| 149 | 171 | |
| @@ -175,18 +197,22 @@ static const struct dev_pm_ops replicator_dev_pm_ops = { · 25 unmodified lines | ||
| 175 | 197 | NULL) |
| 176 | 198 | }; |
| 177 | 199 | |
| 178 | −static struct amba_id replicator_ids[] = { | |
| 200 | +static const struct amba_id replicator_ids[] = { | |
| 179 | 201 | { |
| 180 | 202 | .id = 0x0003b909, |
| 181 | 203 | .mask = 0x0003ffff, |
| 182 | − .data = "REPLICATOR 1.0", | |
| 204 | + }, | |
| 205 | + { | |
| 206 | + /* Coresight SoC-600 */ | |
| 207 | + .id = 0x000bb9ec, | |
| 208 | + .mask = 0x000fffff, | |
| 183 | 209 | }, |
| 184 | 210 | { 0, 0 }, |
| 185 | 211 | }; |
| 186 | 212 | |
| 187 | 213 | static struct amba_driver replicator_driver = { |
| 188 | 214 | .drv = { |
| 189 | − .name = "coresight-replicator-qcom", | |
| 215 | + .name = "coresight-dynamic-replicator", | |
| 190 | 216 | .pm = &replicator_dev_pm_ops, |
| 191 | 217 | .suppress_bind_attrs = true, |
| 192 | 218 | }, |
| @@ -200,8 +200,10 @@ static void etb_disable_hw(struct etb_drvdata *drvdata) · 199 unmodified lines | ||
| 200 | 200 | |
| 201 | 201 | static void etb_dump_hw(struct etb_drvdata *drvdata) |
| 202 | 202 | { |
| 203 | + bool lost = false; | |
| 203 | 204 | int i; |
| 204 | 205 | u8 *buf_ptr; |
| 206 | + const u32 *barrier; | |
| 205 | 207 | u32 read_data, depth; |
| 206 | 208 | u32 read_ptr, write_ptr; |
| 207 | 209 | u32 frame_off, frame_endoff; |
| @@ -223,20 +225,26 @@ static void etb_dump_hw(struct etb_drvdata *drvdata) · 15 unmodified lines | ||
| 223 | 225 | } |
| 224 | 226 | |
| 225 | 227 | if ((readl_relaxed(drvdata->base + ETB_STATUS_REG) |
| 226 | − & ETB_STATUS_RAM_FULL) == 0) | |
| 228 | + & ETB_STATUS_RAM_FULL) == 0) { | |
| 227 | 229 | writel_relaxed(0x0, drvdata->base + ETB_RAM_READ_POINTER); |
| 228 | − else | |
| 230 | + } else { | |
| 229 | 231 | writel_relaxed(write_ptr, drvdata->base + ETB_RAM_READ_POINTER); |
| 232 | + lost = true; | |
| 233 | + } | |
| 230 | 234 | |
| 231 | 235 | depth = drvdata->buffer_depth; |
| 232 | 236 | buf_ptr = drvdata->buf; |
| 237 | + barrier = barrier_pkt; | |
| 233 | 238 | for (i = 0; i < depth; i++) { |
| 234 | 239 | read_data = readl_relaxed(drvdata->base + |
| 235 | 240 | ETB_RAM_READ_DATA_REG); |
| 236 | − *buf_ptr++ = read_data >> 0; | |
| 237 | − *buf_ptr++ = read_data >> 8; | |
| 238 | − *buf_ptr++ = read_data >> 16; | |
| 239 | − *buf_ptr++ = read_data >> 24; | |
| 241 | + if (lost && *barrier) { | |
| 242 | + read_data = *barrier; | |
| 243 | + barrier++; | |
| 244 | + } | |
| 245 | + | |
| 246 | + *(u32 *)buf_ptr = read_data; | |
| 247 | + buf_ptr += 4; | |
| 240 | 248 | } |
| 241 | 249 | |
| 242 | 250 | if (frame_off) { |
| @@ -353,8 +361,10 @@ static void etb_update_buffer(struct coresight_device *csdev, · 110 unmodified lines | ||
| 353 | 361 | struct perf_output_handle *handle, |
| 354 | 362 | void *sink_config) |
| 355 | 363 | { |
| 364 | + bool lost = false; | |
| 356 | 365 | int i, cur; |
| 357 | 366 | u8 *buf_ptr; |
| 367 | + const u32 *barrier; | |
| 358 | 368 | u32 read_ptr, write_ptr, capacity; |
| 359 | 369 | u32 status, read_data, to_read; |
| 360 | 370 | unsigned long offset; |
| @@ -366,8 +376,8 @@ static void etb_update_buffer(struct coresight_device *csdev, · 5 unmodified lines | ||
| 366 | 376 | |
| 367 | 377 | capacity = drvdata->buffer_depth * ETB_FRAME_SIZE_WORDS; |
| 368 | 378 | |
| 369 | − CS_UNLOCK(drvdata->base); | |
| 370 | 379 | etb_disable_hw(drvdata); |
| 380 | + CS_UNLOCK(drvdata->base); | |
| 371 | 381 | |
| 372 | 382 | /* unit is in words, not bytes */ |
| 373 | 383 | read_ptr = readl_relaxed(drvdata->base + ETB_RAM_READ_POINTER); |
| @@ -384,7 +394,7 @@ static void etb_update_buffer(struct coresight_device *csdev, · 10 unmodified lines | ||
| 384 | 394 | (unsigned long)write_ptr); |
| 385 | 395 | |
| 386 | 396 | write_ptr &= ~(ETB_FRAME_SIZE_WORDS - 1); |
| 387 | − perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); | |
| 397 | + lost = true; | |
| 388 | 398 | } |
| 389 | 399 | |
| 390 | 400 | /* |
| @@ -395,7 +405,7 @@ static void etb_update_buffer(struct coresight_device *csdev, · 4 unmodified lines | ||
| 395 | 405 | */ |
| 396 | 406 | status = readl_relaxed(drvdata->base + ETB_STATUS_REG); |
| 397 | 407 | if (status & ETB_STATUS_RAM_FULL) { |
| 398 | − perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); | |
| 408 | + lost = true; | |
| 399 | 409 | to_read = capacity; |
| 400 | 410 | read_ptr = write_ptr; |
| 401 | 411 | } else { |
| @@ -428,22 +438,30 @@ static void etb_update_buffer(struct coresight_device *csdev, · 26 unmodified lines | ||
| 428 | 438 | if (read_ptr > (drvdata->buffer_depth - 1)) |
| 429 | 439 | read_ptr -= drvdata->buffer_depth; |
| 430 | 440 | /* let the decoder know we've skipped ahead */ |
| 431 | − perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); | |
| 441 | + lost = true; | |
| 432 | 442 | } |
| 433 | 443 | |
| 444 | + if (lost) | |
| 445 | + perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); | |
| 446 | + | |
| 434 | 447 | /* finally tell HW where we want to start reading from */ |
| 435 | 448 | writel_relaxed(read_ptr, drvdata->base + ETB_RAM_READ_POINTER); |
| 436 | 449 | |
| 437 | 450 | cur = buf->cur; |
| 438 | 451 | offset = buf->offset; |
| 452 | + barrier = barrier_pkt; | |
| 453 | + | |
| 439 | 454 | for (i = 0; i < to_read; i += 4) { |
| 440 | 455 | buf_ptr = buf->data_pages[cur] + offset; |
| 441 | 456 | read_data = readl_relaxed(drvdata->base + |
| 442 | 457 | ETB_RAM_READ_DATA_REG); |
| 443 | − *buf_ptr++ = read_data >> 0; | |
| 444 | − *buf_ptr++ = read_data >> 8; | |
| 445 | − *buf_ptr++ = read_data >> 16; | |
| 446 | − *buf_ptr++ = read_data >> 24; | |
| 458 | + if (lost && *barrier) { | |
| 459 | + read_data = *barrier; | |
| 460 | + barrier++; | |
| 461 | + } | |
| 462 | + | |
| 463 | + *(u32 *)buf_ptr = read_data; | |
| 464 | + buf_ptr += 4; | |
| 447 | 465 | |
| 448 | 466 | offset += 4; |
| 449 | 467 | if (offset >= PAGE_SIZE) { |
| @@ -557,17 +575,17 @@ static const struct file_operations etb_fops = { · 107 unmodified lines | ||
| 557 | 575 | .llseek = no_llseek, |
| 558 | 576 | }; |
| 559 | 577 | |
| 560 | −#define coresight_etb10_simple_func(name, offset) \ | |
| 561 | − coresight_simple_func(struct etb_drvdata, NULL, name, offset) | |
| 578 | +#define coresight_etb10_reg(name, offset) \ | |
| 579 | + coresight_simple_reg32(struct etb_drvdata, name, offset) | |
| 562 | 580 | |
| 563 | −coresight_etb10_simple_func(rdp, ETB_RAM_DEPTH_REG); | |
| 564 | −coresight_etb10_simple_func(sts, ETB_STATUS_REG); | |
| 565 | −coresight_etb10_simple_func(rrp, ETB_RAM_READ_POINTER); | |
| 566 | −coresight_etb10_simple_func(rwp, ETB_RAM_WRITE_POINTER); | |
| 567 | −coresight_etb10_simple_func(trg, ETB_TRG); | |
| 568 | −coresight_etb10_simple_func(ctl, ETB_CTL_REG); | |
| 569 | −coresight_etb10_simple_func(ffsr, ETB_FFSR); | |
| 570 | −coresight_etb10_simple_func(ffcr, ETB_FFCR); | |
| 581 | +coresight_etb10_reg(rdp, ETB_RAM_DEPTH_REG); | |
| 582 | +coresight_etb10_reg(sts, ETB_STATUS_REG); | |
| 583 | +coresight_etb10_reg(rrp, ETB_RAM_READ_POINTER); | |
| 584 | +coresight_etb10_reg(rwp, ETB_RAM_WRITE_POINTER); | |
| 585 | +coresight_etb10_reg(trg, ETB_TRG); | |
| 586 | +coresight_etb10_reg(ctl, ETB_CTL_REG); | |
| 587 | +coresight_etb10_reg(ffsr, ETB_FFSR); | |
| 588 | +coresight_etb10_reg(ffcr, ETB_FFCR); | |
| 571 | 589 | |
| 572 | 590 | static struct attribute *coresight_etb_mgmt_attrs[] = { |
| 573 | 591 | &dev_attr_rdp.attr, |
| @@ -728,7 +746,7 @@ static const struct dev_pm_ops etb_dev_pm_ops = { · 154 unmodified lines | ||
| 728 | 746 | SET_RUNTIME_PM_OPS(etb_runtime_suspend, etb_runtime_resume, NULL) |
| 729 | 747 | }; |
| 730 | 748 | |
| 731 | −static struct amba_id etb_ids[] = { | |
| 749 | +static const struct amba_id etb_ids[] = { | |
| 732 | 750 | { |
| 733 | 751 | .id = 0x0003b907, |
| 734 | 752 | .mask = 0x0003ffff, |
| @@ -53,14 +53,16 @@ static DEFINE_PER_CPU(struct coresight_device *, csdev_src); · 52 unmodified lines | ||
| 53 | 53 | /* ETMv3.5/PTM's ETMCR is 'config' */ |
| 54 | 54 | PMU_FORMAT_ATTR(cycacc, "config:" __stringify(ETM_OPT_CYCACC)); |
| 55 | 55 | PMU_FORMAT_ATTR(timestamp, "config:" __stringify(ETM_OPT_TS)); |
| 56 | +PMU_FORMAT_ATTR(retstack, "config:" __stringify(ETM_OPT_RETSTK)); | |
| 56 | 57 | |
| 57 | 58 | static struct attribute *etm_config_formats_attr[] = { |
| 58 | 59 | &format_attr_cycacc.attr, |
| 59 | 60 | &format_attr_timestamp.attr, |
| 61 | + &format_attr_retstack.attr, | |
| 60 | 62 | NULL, |
| 61 | 63 | }; |
| 62 | 64 | |
| 63 | −static struct attribute_group etm_pmu_format_group = { | |
| 65 | +static const struct attribute_group etm_pmu_format_group = { | |
| 64 | 66 | .name = "format", |
| 65 | 67 | .attrs = etm_config_formats_attr, |
| 66 | 68 | }; |
| @@ -106,6 +106,7 @@ · 105 unmodified lines | ||
| 106 | 106 | #define ETMTECR1_START_STOP BIT(25) |
| 107 | 107 | /* ETMCCER - 0x1E8 */ |
| 108 | 108 | #define ETMCCER_TIMESTAMP BIT(22) |
| 109 | +#define ETMCCER_RETSTACK BIT(23) | |
| 109 | 110 | |
| 110 | 111 | #define ETM_MODE_EXCLUDE BIT(0) |
| 111 | 112 | #define ETM_MODE_CYCACC BIT(1) |
| @@ -1232,19 +1232,19 @@ static struct attribute *coresight_etm_attrs[] = { · 1231 unmodified lines | ||
| 1232 | 1232 | NULL, |
| 1233 | 1233 | }; |
| 1234 | 1234 | |
| 1235 | −#define coresight_etm3x_simple_func(name, offset) \ | |
| 1236 | − coresight_simple_func(struct etm_drvdata, NULL, name, offset) | |
| 1237 | − | |
| 1238 | −coresight_etm3x_simple_func(etmccr, ETMCCR); | |
| 1239 | −coresight_etm3x_simple_func(etmccer, ETMCCER); | |
| 1240 | −coresight_etm3x_simple_func(etmscr, ETMSCR); | |
| 1241 | −coresight_etm3x_simple_func(etmidr, ETMIDR); | |
| 1242 | −coresight_etm3x_simple_func(etmcr, ETMCR); | |
| 1243 | −coresight_etm3x_simple_func(etmtraceidr, ETMTRACEIDR); | |
| 1244 | −coresight_etm3x_simple_func(etmteevr, ETMTEEVR); | |
| 1245 | −coresight_etm3x_simple_func(etmtssvr, ETMTSSCR); | |
| 1246 | −coresight_etm3x_simple_func(etmtecr1, ETMTECR1); | |
| 1247 | −coresight_etm3x_simple_func(etmtecr2, ETMTECR2); | |
| 1235 | +#define coresight_etm3x_reg(name, offset) \ | |
| 1236 | + coresight_simple_reg32(struct etm_drvdata, name, offset) | |
| 1237 | + | |
| 1238 | +coresight_etm3x_reg(etmccr, ETMCCR); | |
| 1239 | +coresight_etm3x_reg(etmccer, ETMCCER); | |
| 1240 | +coresight_etm3x_reg(etmscr, ETMSCR); | |
| 1241 | +coresight_etm3x_reg(etmidr, ETMIDR); | |
| 1242 | +coresight_etm3x_reg(etmcr, ETMCR); | |
| 1243 | +coresight_etm3x_reg(etmtraceidr, ETMTRACEIDR); | |
| 1244 | +coresight_etm3x_reg(etmteevr, ETMTEEVR); | |
| 1245 | +coresight_etm3x_reg(etmtssvr, ETMTSSCR); | |
| 1246 | +coresight_etm3x_reg(etmtecr1, ETMTECR1); | |
| 1247 | +coresight_etm3x_reg(etmtecr2, ETMTECR2); | |
| 1248 | 1248 | |
| 1249 | 1249 | static struct attribute *coresight_etm_mgmt_attrs[] = { |
| 1250 | 1250 | &dev_attr_etmccr.attr, |
| @@ -243,6 +243,8 @@ void etm_set_default(struct etm_config *config) · 242 unmodified lines | ||
| 243 | 243 | } |
| 244 | 244 | |
| 245 | 245 | config->ctxid_mask = 0x0; |
| 246 | + /* Setting default to 1024 as per TRM recommendation */ | |
| 247 | + config->sync_freq = 0x400; | |
| 246 | 248 | } |
| 247 | 249 | |
| 248 | 250 | void etm_config_trace_mode(struct etm_config *config) |
| @@ -308,7 +310,9 @@ void etm_config_trace_mode(struct etm_config *config) · 59 unmodified lines | ||
| 308 | 310 | config->addr_type[1] = ETM_ADDR_TYPE_RANGE; |
| 309 | 311 | } |
| 310 | 312 | |
| 311 | −#define ETM3X_SUPPORTED_OPTIONS (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN) | |
| 313 | +#define ETM3X_SUPPORTED_OPTIONS (ETMCR_CYC_ACC | \ | |
| 314 | + ETMCR_TIMESTAMP_EN | \ | |
| 315 | + ETMCR_RETURN_STACK) | |
| 312 | 316 | |
| 313 | 317 | static int etm_parse_event_config(struct etm_drvdata *drvdata, |
| 314 | 318 | struct perf_event *event) |
| @@ -339,14 +343,24 @@ static int etm_parse_event_config(struct etm_drvdata *drvdata, · 24 unmodified lines | ||
| 339 | 343 | etm_config_trace_mode(config); |
| 340 | 344 | |
| 341 | 345 | /* |
| 342 | − * At this time only cycle accurate and timestamp options are | |
| 343 | − * available. | |
| 346 | + * At this time only cycle accurate, return stack and timestamp | |
| 347 | + * options are available. | |
| 344 | 348 | */ |
| 345 | 349 | if (attr->config & ~ETM3X_SUPPORTED_OPTIONS) |
| 346 | 350 | return -EINVAL; |
| 347 | 351 | |
| 348 | 352 | config->ctrl = attr->config; |
| 349 | 353 | |
| 354 | + /* | |
| 355 | + * Possible to have cores with PTM (supports ret stack) and ETM | |
| 356 | + * (never has ret stack) on the same SoC. So if we have a request | |
| 357 | + * for return stack that can't be honoured on this core then | |
| 358 | + * clear the bit - trace will still continue normally | |
| 359 | + */ | |
| 360 | + if ((config->ctrl & ETMCR_RETURN_STACK) && | |
| 361 | + !(drvdata->etmccer & ETMCCER_RETSTACK)) | |
| 362 | + config->ctrl &= ~ETMCR_RETURN_STACK; | |
| 363 | + | |
| 350 | 364 | return 0; |
| 351 | 365 | } |
| 352 | 366 | |
| @@ -885,7 +899,7 @@ static const struct dev_pm_ops etm_dev_pm_ops = { · 532 unmodified lines | ||
| 885 | 899 | SET_RUNTIME_PM_OPS(etm_runtime_suspend, etm_runtime_resume, NULL) |
| 886 | 900 | }; |
| 887 | 901 | |
| 888 | −static struct amba_id etm_ids[] = { | |
| 902 | +static const struct amba_id etm_ids[] = { | |
| 889 | 903 | { /* ETM 3.3 */ |
| 890 | 904 | .id = 0x0003b921, |
| 891 | 905 | .mask = 0x0003ffff, |
| @@ -2066,23 +2066,23 @@ static u32 etmv4_cross_read(const struct device *dev, u32 offset) · 2065 unmodified lines | ||
| 2066 | 2066 | return reg.data; |
| 2067 | 2067 | } |
| 2068 | 2068 | |
| 2069 | −#define coresight_etm4x_simple_func(name, offset) \ | |
| 2070 | − coresight_simple_func(struct etmv4_drvdata, NULL, name, offset) | |
| 2069 | +#define coresight_etm4x_reg(name, offset) \ | |
| 2070 | + coresight_simple_reg32(struct etmv4_drvdata, name, offset) | |
| 2071 | 2071 | |
| 2072 | 2072 | #define coresight_etm4x_cross_read(name, offset) \ |
| 2073 | 2073 | coresight_simple_func(struct etmv4_drvdata, etmv4_cross_read, \ |
| 2074 | 2074 | name, offset) |
| 2075 | 2075 | |
| 2076 | −coresight_etm4x_simple_func(trcpdcr, TRCPDCR); | |
| 2077 | −coresight_etm4x_simple_func(trcpdsr, TRCPDSR); | |
| 2078 | −coresight_etm4x_simple_func(trclsr, TRCLSR); | |
| 2079 | −coresight_etm4x_simple_func(trcauthstatus, TRCAUTHSTATUS); | |
| 2080 | −coresight_etm4x_simple_func(trcdevid, TRCDEVID); | |
| 2081 | −coresight_etm4x_simple_func(trcdevtype, TRCDEVTYPE); | |
| 2082 | −coresight_etm4x_simple_func(trcpidr0, TRCPIDR0); | |
| 2083 | −coresight_etm4x_simple_func(trcpidr1, TRCPIDR1); | |
| 2084 | −coresight_etm4x_simple_func(trcpidr2, TRCPIDR2); | |
| 2085 | −coresight_etm4x_simple_func(trcpidr3, TRCPIDR3); | |
| 2076 | +coresight_etm4x_reg(trcpdcr, TRCPDCR); | |
| 2077 | +coresight_etm4x_reg(trcpdsr, TRCPDSR); | |
| 2078 | +coresight_etm4x_reg(trclsr, TRCLSR); | |
| 2079 | +coresight_etm4x_reg(trcauthstatus, TRCAUTHSTATUS); | |
| 2080 | +coresight_etm4x_reg(trcdevid, TRCDEVID); | |
| 2081 | +coresight_etm4x_reg(trcdevtype, TRCDEVTYPE); | |
| 2082 | +coresight_etm4x_reg(trcpidr0, TRCPIDR0); | |
| 2083 | +coresight_etm4x_reg(trcpidr1, TRCPIDR1); | |
| 2084 | +coresight_etm4x_reg(trcpidr2, TRCPIDR2); | |
| 2085 | +coresight_etm4x_reg(trcpidr3, TRCPIDR3); | |
| 2086 | 2086 | coresight_etm4x_cross_read(trcoslsr, TRCOSLSR); |
| 2087 | 2087 | coresight_etm4x_cross_read(trcconfig, TRCCONFIGR); |
| 2088 | 2088 | coresight_etm4x_cross_read(trctraceid, TRCTRACEIDR); |
| @@ -224,6 +224,10 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, · 223 unmodified lines | ||
| 224 | 224 | if (attr->config & BIT(ETM_OPT_TS)) |
| 225 | 225 | /* bit[11], Global timestamp tracing bit */ |
| 226 | 226 | config->cfg |= BIT(11); |
| 227 | + /* return stack - enable if selected and supported */ | |
| 228 | + if ((attr->config & BIT(ETM_OPT_RETSTK)) && drvdata->retstack) | |
| 229 | + /* bit[12], Return stack enable bit */ | |
| 230 | + config->cfg |= BIT(12); | |
| 227 | 231 | |
| 228 | 232 | out: |
| 229 | 233 | return ret; |
| @@ -1048,7 +1052,7 @@ err_arch_supported: · 818 unmodified lines | ||
| 1048 | 1052 | return ret; |
| 1049 | 1053 | } |
| 1050 | 1054 | |
| 1051 | −static struct amba_id etm4_ids[] = { | |
| 1055 | +static const struct amba_id etm4_ids[] = { | |
| 1052 | 1056 | { /* ETM 4.0 - Cortex-A53 */ |
| 1053 | 1057 | .id = 0x000bb95d, |
| 1054 | 1058 | .mask = 0x000fffff, |
| @@ -246,11 +246,16 @@ static const struct dev_pm_ops funnel_dev_pm_ops = { · 245 unmodified lines | ||
| 246 | 246 | SET_RUNTIME_PM_OPS(funnel_runtime_suspend, funnel_runtime_resume, NULL) |
| 247 | 247 | }; |
| 248 | 248 | |
| 249 | −static struct amba_id funnel_ids[] = { | |
| 249 | +static const struct amba_id funnel_ids[] = { | |
| 250 | 250 | { |
| 251 | 251 | .id = 0x0003b908, |
| 252 | 252 | .mask = 0x0003ffff, |
| 253 | 253 | }, |
| 254 | + { | |
| 255 | + /* Coresight SoC-600 */ | |
| 256 | + .id = 0x000bb9eb, | |
| 257 | + .mask = 0x000fffff, | |
| 258 | + }, | |
| 254 | 259 | { 0, 0}, |
| 255 | 260 | }; |
| 256 | 261 | |
| @@ -39,23 +39,33 @@ · 38 unmodified lines | ||
| 39 | 39 | #define ETM_MODE_EXCL_USER BIT(31) |
| 40 | 40 | |
| 41 | 41 | typedef u32 (*coresight_read_fn)(const struct device *, u32 offset); |
| 42 | −#define coresight_simple_func(type, func, name, offset) \ | |
| 42 | +#define __coresight_simple_func(type, func, name, lo_off, hi_off) \ | |
| 43 | 43 | static ssize_t name##_show(struct device *_dev, \ |
| 44 | 44 | struct device_attribute *attr, char *buf) \ |
| 45 | 45 | { \ |
| 46 | 46 | type *drvdata = dev_get_drvdata(_dev->parent); \ |
| 47 | 47 | coresight_read_fn fn = func; \ |
| 48 | − u32 val; \ | |
| 48 | + u64 val; \ | |
| 49 | 49 | pm_runtime_get_sync(_dev->parent); \ |
| 50 | 50 | if (fn) \ |
| 51 | − val = fn(_dev->parent, offset); \ | |
| 51 | + val = (u64)fn(_dev->parent, lo_off); \ | |
| 52 | 52 | else \ |
| 53 | − val = readl_relaxed(drvdata->base + offset); \ | |
| 53 | + val = coresight_read_reg_pair(drvdata->base, \ | |
| 54 | + lo_off, hi_off); \ | |
| 54 | 55 | pm_runtime_put_sync(_dev->parent); \ |
| 55 | − return scnprintf(buf, PAGE_SIZE, "0x%x\n", val); \ | |
| 56 | + return scnprintf(buf, PAGE_SIZE, "0x%llx\n", val); \ | |
| 56 | 57 | } \ |
| 57 | 58 | static DEVICE_ATTR_RO(name) |
| 58 | 59 | |
| 60 | +#define coresight_simple_func(type, func, name, offset) \ | |
| 61 | + __coresight_simple_func(type, func, name, offset, -1) | |
| 62 | +#define coresight_simple_reg32(type, name, offset) \ | |
| 63 | + __coresight_simple_func(type, NULL, name, offset, -1) | |
| 64 | +#define coresight_simple_reg64(type, name, lo_off, hi_off) \ | |
| 65 | + __coresight_simple_func(type, NULL, name, lo_off, hi_off) | |
| 66 | + | |
| 67 | +extern const u32 barrier_pkt[5]; | |
| 68 | + | |
| 59 | 69 | enum etm_addr_type { |
| 60 | 70 | ETM_ADDR_TYPE_NONE, |
| 61 | 71 | ETM_ADDR_TYPE_SINGLE, |
| @@ -106,6 +116,25 @@ static inline void CS_UNLOCK(void __iomem *addr) · 44 unmodified lines | ||
| 106 | 116 | } while (0); |
| 107 | 117 | } |
| 108 | 118 | |
| 119 | +static inline u64 | |
| 120 | +coresight_read_reg_pair(void __iomem *addr, s32 lo_offset, s32 hi_offset) | |
| 121 | +{ | |
| 122 | + u64 val; | |
| 123 | + | |
| 124 | + val = readl_relaxed(addr + lo_offset); | |
| 125 | + val |= (hi_offset < 0) ? 0 : | |
| 126 | + (u64)readl_relaxed(addr + hi_offset) << 32; | |
| 127 | + return val; | |
| 128 | +} | |
| 129 | + | |
| 130 | +static inline void coresight_write_reg_pair(void __iomem *addr, u64 val, | |
| 131 | + s32 lo_offset, s32 hi_offset) | |
| 132 | +{ | |
| 133 | + writel_relaxed((u32)val, addr + lo_offset); | |
| 134 | + if (hi_offset >= 0) | |
| 135 | + writel_relaxed((u32)(val >> 32), addr + hi_offset); | |
| 136 | +} | |
| 137 | + | |
| 109 | 138 | void coresight_disable_path(struct list_head *path); |
| 110 | 139 | int coresight_enable_path(struct list_head *path, u32 mode); |
| 111 | 140 | struct coresight_device *coresight_get_sink(struct list_head *path); |
| @@ -276,7 +276,7 @@ static void stm_disable(struct coresight_device *csdev, · 275 unmodified lines | ||
| 276 | 276 | spin_unlock(&drvdata->spinlock); |
| 277 | 277 | |
| 278 | 278 | /* Wait until the engine has completely stopped */ |
| 279 | − coresight_timeout(drvdata, STMTCSR, STMTCSR_BUSY_BIT, 0); | |
| 279 | + coresight_timeout(drvdata->base, STMTCSR, STMTCSR_BUSY_BIT, 0); | |
| 280 | 280 | |
| 281 | 281 | pm_runtime_put(drvdata->dev); |
| 282 | 282 | |
| @@ -307,7 +307,8 @@ static inline bool stm_addr_unaligned(const void *addr, u8 write_bytes) · 24 unmodified lines | ||
| 307 | 307 | return ((unsigned long)addr & (write_bytes - 1)); |
| 308 | 308 | } |
| 309 | 309 | |
| 310 | −static void stm_send(void *addr, const void *data, u32 size, u8 write_bytes) | |
| 310 | +static void stm_send(void __iomem *addr, const void *data, | |
| 311 | + u32 size, u8 write_bytes) | |
| 311 | 312 | { |
| 312 | 313 | u8 paload[8]; |
| 313 | 314 | |
| @@ -414,7 +415,7 @@ static ssize_t notrace stm_generic_packet(struct stm_data *stm_data, · 100 unmodified lines | ||
| 414 | 415 | unsigned int size, |
| 415 | 416 | const unsigned char *payload) |
| 416 | 417 | { |
| 417 | − unsigned long ch_addr; | |
| 418 | + void __iomem *ch_addr; | |
| 418 | 419 | struct stm_drvdata *drvdata = container_of(stm_data, |
| 419 | 420 | struct stm_drvdata, stm); |
| 420 | 421 | |
| @@ -424,7 +425,7 @@ static ssize_t notrace stm_generic_packet(struct stm_data *stm_data, · 3 unmodified lines | ||
| 424 | 425 | if (channel >= drvdata->numsp) |
| 425 | 426 | return -EINVAL; |
| 426 | 427 | |
| 427 | − ch_addr = (unsigned long)stm_channel_addr(drvdata, channel); | |
| 428 | + ch_addr = stm_channel_addr(drvdata, channel); | |
| 428 | 429 | |
| 429 | 430 | flags = (flags == STP_PACKET_TIMESTAMPED) ? STM_FLAG_TIMESTAMPED : 0; |
| 430 | 431 | flags |= test_bit(channel, drvdata->chs.guaranteed) ? |
| @@ -437,20 +438,20 @@ static ssize_t notrace stm_generic_packet(struct stm_data *stm_data, · 6 unmodified lines | ||
| 437 | 438 | |
| 438 | 439 | switch (packet) { |
| 439 | 440 | case STP_PACKET_FLAG: |
| 440 | − ch_addr |= stm_channel_off(STM_PKT_TYPE_FLAG, flags); | |
| 441 | + ch_addr += stm_channel_off(STM_PKT_TYPE_FLAG, flags); | |
| 441 | 442 | |
| 442 | 443 | /* |
| 443 | 444 | * The generic STM core sets a size of '0' on flag packets. |
| 444 | 445 | * As such send a flag packet of size '1' and tell the |
| 445 | 446 | * core we did so. |
| 446 | 447 | */ |
| 447 | − stm_send((void *)ch_addr, payload, 1, drvdata->write_bytes); | |
| 448 | + stm_send(ch_addr, payload, 1, drvdata->write_bytes); | |
| 448 | 449 | size = 1; |
| 449 | 450 | break; |
| 450 | 451 | |
| 451 | 452 | case STP_PACKET_DATA: |
| 452 | − ch_addr |= stm_channel_off(STM_PKT_TYPE_DATA, flags); | |
| 453 | − stm_send((void *)ch_addr, payload, size, | |
| 453 | + ch_addr += stm_channel_off(STM_PKT_TYPE_DATA, flags); | |
| 454 | + stm_send(ch_addr, payload, size, | |
| 454 | 455 | drvdata->write_bytes); |
| 455 | 456 | break; |
| 456 | 457 | |
| @@ -635,21 +636,21 @@ static ssize_t traceid_store(struct device *dev, · 178 unmodified lines | ||
| 635 | 636 | } |
| 636 | 637 | static DEVICE_ATTR_RW(traceid); |
| 637 | 638 | |
| 638 | −#define coresight_stm_simple_func(name, offset) \ | |
| 639 | − coresight_simple_func(struct stm_drvdata, NULL, name, offset) | |
| 640 | − | |
| 641 | −coresight_stm_simple_func(tcsr, STMTCSR); | |
| 642 | −coresight_stm_simple_func(tsfreqr, STMTSFREQR); | |
| 643 | −coresight_stm_simple_func(syncr, STMSYNCR); | |
| 644 | −coresight_stm_simple_func(sper, STMSPER); | |
| 645 | −coresight_stm_simple_func(spter, STMSPTER); | |
| 646 | −coresight_stm_simple_func(privmaskr, STMPRIVMASKR); | |
| 647 | −coresight_stm_simple_func(spscr, STMSPSCR); | |
| 648 | −coresight_stm_simple_func(spmscr, STMSPMSCR); | |
| 649 | −coresight_stm_simple_func(spfeat1r, STMSPFEAT1R); | |
| 650 | −coresight_stm_simple_func(spfeat2r, STMSPFEAT2R); | |
| 651 | −coresight_stm_simple_func(spfeat3r, STMSPFEAT3R); | |
| 652 | −coresight_stm_simple_func(devid, CORESIGHT_DEVID); | |
| 639 | +#define coresight_stm_reg(name, offset) \ | |
| 640 | + coresight_simple_reg32(struct stm_drvdata, name, offset) | |
| 641 | + | |
| 642 | +coresight_stm_reg(tcsr, STMTCSR); | |
| 643 | +coresight_stm_reg(tsfreqr, STMTSFREQR); | |
| 644 | +coresight_stm_reg(syncr, STMSYNCR); | |
| 645 | +coresight_stm_reg(sper, STMSPER); | |
| 646 | +coresight_stm_reg(spter, STMSPTER); | |
| 647 | +coresight_stm_reg(privmaskr, STMPRIVMASKR); | |
| 648 | +coresight_stm_reg(spscr, STMSPSCR); | |
| 649 | +coresight_stm_reg(spmscr, STMSPMSCR); | |
| 650 | +coresight_stm_reg(spfeat1r, STMSPFEAT1R); | |
| 651 | +coresight_stm_reg(spfeat2r, STMSPFEAT2R); | |
| 652 | +coresight_stm_reg(spfeat3r, STMSPFEAT3R); | |
| 653 | +coresight_stm_reg(devid, CORESIGHT_DEVID); | |
| 653 | 654 | |
| 654 | 655 | static struct attribute *coresight_stm_attrs[] = { |
| 655 | 656 | &dev_attr_hwevent_enable.attr, |
| @@ -914,7 +915,7 @@ static const struct dev_pm_ops stm_dev_pm_ops = { · 258 unmodified lines | ||
| 914 | 915 | SET_RUNTIME_PM_OPS(stm_runtime_suspend, stm_runtime_resume, NULL) |
| 915 | 916 | }; |
| 916 | 917 | |
| 917 | −static struct amba_id stm_ids[] = { | |
| 918 | +static const struct amba_id stm_ids[] = { | |
| 918 | 919 | { |
| 919 | 920 | .id = 0x0003b962, |
| 920 | 921 | .mask = 0x0003ffff, |
| @@ -43,17 +43,34 @@ static void tmc_etb_enable_hw(struct tmc_drvdata *drvdata) · 42 unmodified lines | ||
| 43 | 43 | |
| 44 | 44 | static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata) |
| 45 | 45 | { |
| 46 | + bool lost = false; | |
| 46 | 47 | char *bufp; |
| 47 | − u32 read_data; | |
| 48 | + const u32 *barrier; | |
| 49 | + u32 read_data, status; | |
| 48 | 50 | int i; |
| 49 | 51 | |
| 52 | + /* | |
| 53 | + * Get a hold of the status register and see if a wrap around | |
| 54 | + * has occurred. | |
| 55 | + */ | |
| 56 | + status = readl_relaxed(drvdata->base + TMC_STS); | |
| 57 | + if (status & TMC_STS_FULL) | |
| 58 | + lost = true; | |
| 59 | + | |
| 50 | 60 | bufp = drvdata->buf; |
| 51 | 61 | drvdata->len = 0; |
| 62 | + barrier = barrier_pkt; | |
| 52 | 63 | while (1) { |
| 53 | 64 | for (i = 0; i < drvdata->memwidth; i++) { |
| 54 | 65 | read_data = readl_relaxed(drvdata->base + TMC_RRD); |
| 55 | 66 | if (read_data == 0xFFFFFFFF) |
| 56 | 67 | return; |
| 68 | + | |
| 69 | + if (lost && *barrier) { | |
| 70 | + read_data = *barrier; | |
| 71 | + barrier++; | |
| 72 | + } | |
| 73 | + | |
| 57 | 74 | memcpy(bufp, &read_data, 4); |
| 58 | 75 | bufp += 4; |
| 59 | 76 | drvdata->len += 4; |
| @@ -369,9 +386,11 @@ static void tmc_update_etf_buffer(struct coresight_device *csdev, · 309 unmodified lines | ||
| 369 | 386 | struct perf_output_handle *handle, |
| 370 | 387 | void *sink_config) |
| 371 | 388 | { |
| 389 | + bool lost = false; | |
| 372 | 390 | int i, cur; |
| 391 | + const u32 *barrier; | |
| 373 | 392 | u32 *buf_ptr; |
| 374 | − u32 read_ptr, write_ptr; | |
| 393 | + u64 read_ptr, write_ptr; | |
| 375 | 394 | u32 status, to_read; |
| 376 | 395 | unsigned long offset; |
| 377 | 396 | struct cs_buffers *buf = sink_config; |
| @@ -388,8 +407,8 @@ static void tmc_update_etf_buffer(struct coresight_device *csdev, · 10 unmodified lines | ||
| 388 | 407 | |
| 389 | 408 | tmc_flush_and_stop(drvdata); |
| 390 | 409 | |
| 391 | − read_ptr = readl_relaxed(drvdata->base + TMC_RRP); | |
| 392 | − write_ptr = readl_relaxed(drvdata->base + TMC_RWP); | |
| 410 | + read_ptr = tmc_read_rrp(drvdata); | |
| 411 | + write_ptr = tmc_read_rwp(drvdata); | |
| 393 | 412 | |
| 394 | 413 | /* |
| 395 | 414 | * Get a hold of the status register and see if a wrap around |
| @@ -397,7 +416,7 @@ static void tmc_update_etf_buffer(struct coresight_device *csdev, · 1 unmodified line | ||
| 397 | 416 | */ |
| 398 | 417 | status = readl_relaxed(drvdata->base + TMC_STS); |
| 399 | 418 | if (status & TMC_STS_FULL) { |
| 400 | − perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); | |
| 419 | + lost = true; | |
| 401 | 420 | to_read = drvdata->size; |
| 402 | 421 | } else { |
| 403 | 422 | to_read = CIRC_CNT(write_ptr, read_ptr, drvdata->size); |
| @@ -441,18 +460,27 @@ static void tmc_update_etf_buffer(struct coresight_device *csdev, · 37 unmodified lines | ||
| 441 | 460 | if (read_ptr > (drvdata->size - 1)) |
| 442 | 461 | read_ptr -= drvdata->size; |
| 443 | 462 | /* Tell the HW */ |
| 444 | − writel_relaxed(read_ptr, drvdata->base + TMC_RRP); | |
| 445 | − perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); | |
| 463 | + tmc_write_rrp(drvdata, read_ptr); | |
| 464 | + lost = true; | |
| 446 | 465 | } |
| 447 | 466 | |
| 467 | + if (lost) | |
| 468 | + perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); | |
| 469 | + | |
| 448 | 470 | cur = buf->cur; |
| 449 | 471 | offset = buf->offset; |
| 472 | + barrier = barrier_pkt; | |
| 450 | 473 | |
| 451 | 474 | /* for every byte to read */ |
| 452 | 475 | for (i = 0; i < to_read; i += 4) { |
| 453 | 476 | buf_ptr = buf->data_pages[cur] + offset; |
| 454 | 477 | *buf_ptr = readl_relaxed(drvdata->base + TMC_RRD); |
| 455 | 478 | |
| 479 | + if (lost && *barrier) { | |
| 480 | + *buf_ptr = *barrier; | |
| 481 | + barrier++; | |
| 482 | + } | |
| 483 | + | |
| 456 | 484 | offset += 4; |
| 457 | 485 | if (offset >= PAGE_SIZE) { |
| 458 | 486 | offset = 0; |
| @@ -22,7 +22,7 @@ · 21 unmodified lines | ||
| 22 | 22 | |
| 23 | 23 | static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata) |
| 24 | 24 | { |
| 25 | − u32 axictl; | |
| 25 | + u32 axictl, sts; | |
| 26 | 26 | |
| 27 | 27 | /* Zero out the memory to help with debug */ |
| 28 | 28 | memset(drvdata->vaddr, 0, drvdata->size); |
| @@ -36,17 +36,29 @@ static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata) · 7 unmodified lines | ||
| 36 | 36 | writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE); |
| 37 | 37 | |
| 38 | 38 | axictl = readl_relaxed(drvdata->base + TMC_AXICTL); |
| 39 | − axictl |= TMC_AXICTL_WR_BURST_16; | |
| 40 | − writel_relaxed(axictl, drvdata->base + TMC_AXICTL); | |
| 41 | − axictl &= ~TMC_AXICTL_SCT_GAT_MODE; | |
| 42 | − writel_relaxed(axictl, drvdata->base + TMC_AXICTL); | |
| 43 | − axictl = (axictl & | |
| 44 | − ~(TMC_AXICTL_PROT_CTL_B0 | TMC_AXICTL_PROT_CTL_B1)) | | |
| 45 | − TMC_AXICTL_PROT_CTL_B1; | |
| 39 | + axictl &= ~TMC_AXICTL_CLEAR_MASK; | |
| 40 | + axictl |= (TMC_AXICTL_PROT_CTL_B1 | TMC_AXICTL_WR_BURST_16); | |
| 41 | + axictl |= TMC_AXICTL_AXCACHE_OS; | |
| 42 | + | |
| 43 | + if (tmc_etr_has_cap(drvdata, TMC_ETR_AXI_ARCACHE)) { | |
| 44 | + axictl &= ~TMC_AXICTL_ARCACHE_MASK; | |
| 45 | + axictl |= TMC_AXICTL_ARCACHE_OS; | |
| 46 | + } | |
| 47 | + | |
| 46 | 48 | writel_relaxed(axictl, drvdata->base + TMC_AXICTL); |
| 49 | + tmc_write_dba(drvdata, drvdata->paddr); | |
| 50 | + /* | |
| 51 | + * If the TMC pointers must be programmed before the session, | |
| 52 | + * we have to set it properly (i.e, RRP/RWP to base address and | |
| 53 | + * STS to "not full"). | |
| 54 | + */ | |
| 55 | + if (tmc_etr_has_cap(drvdata, TMC_ETR_SAVE_RESTORE)) { | |
| 56 | + tmc_write_rrp(drvdata, drvdata->paddr); | |
| 57 | + tmc_write_rwp(drvdata, drvdata->paddr); | |
| 58 | + sts = readl_relaxed(drvdata->base + TMC_STS) & ~TMC_STS_FULL; | |
| 59 | + writel_relaxed(sts, drvdata->base + TMC_STS); | |
| 60 | + } | |
| 47 | 61 | |
| 48 | − writel_relaxed(drvdata->paddr, drvdata->base + TMC_DBALO); | |
| 49 | − writel_relaxed(0x0, drvdata->base + TMC_DBAHI); | |
| 50 | 62 | writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI | |
| 51 | 63 | TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT | |
| 52 | 64 | TMC_FFCR_TRIGON_TRIGIN, |
| @@ -59,9 +71,12 @@ static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata) · 6 unmodified lines | ||
| 59 | 71 | |
| 60 | 72 | static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata) |
| 61 | 73 | { |
| 62 | − u32 rwp, val; | |
| 74 | + const u32 *barrier; | |
| 75 | + u32 val; | |
| 76 | + u32 *temp; | |
| 77 | + u64 rwp; | |
| 63 | 78 | |
| 64 | − rwp = readl_relaxed(drvdata->base + TMC_RWP); | |
| 79 | + rwp = tmc_read_rwp(drvdata); | |
| 65 | 80 | val = readl_relaxed(drvdata->base + TMC_STS); |
| 66 | 81 | |
| 67 | 82 | /* |
| @@ -71,6 +86,16 @@ static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata) · 3 unmodified lines | ||
| 71 | 86 | if (val & TMC_STS_FULL) { |
| 72 | 87 | drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr; |
| 73 | 88 | drvdata->len = drvdata->size; |
| 89 | + | |
| 90 | + barrier = barrier_pkt; | |
| 91 | + temp = (u32 *)drvdata->buf; | |
| 92 | + | |
| 93 | + while (*barrier) { | |
| 94 | + *temp = *barrier; | |
| 95 | + temp++; | |
| 96 | + barrier++; | |
| 97 | + } | |
| 98 | + | |
| 74 | 99 | } else { |
| 75 | 100 | drvdata->buf = drvdata->vaddr; |
| 76 | 101 | drvdata->len = rwp - drvdata->paddr; |
| @@ -217,20 +217,24 @@ static enum tmc_mem_intf_width tmc_get_memwidth(u32 devid) · 216 unmodified lines | ||
| 217 | 217 | return memwidth; |
| 218 | 218 | } |
| 219 | 219 | |
| 220 | −#define coresight_tmc_simple_func(name, offset) \ | |
| 221 | − coresight_simple_func(struct tmc_drvdata, NULL, name, offset) | |
| 222 | − | |
| 223 | −coresight_tmc_simple_func(rsz, TMC_RSZ); | |
| 224 | −coresight_tmc_simple_func(sts, TMC_STS); | |
| 225 | −coresight_tmc_simple_func(rrp, TMC_RRP); | |
| 226 | −coresight_tmc_simple_func(rwp, TMC_RWP); | |
| 227 | −coresight_tmc_simple_func(trg, TMC_TRG); | |
| 228 | −coresight_tmc_simple_func(ctl, TMC_CTL); | |
| 229 | −coresight_tmc_simple_func(ffsr, TMC_FFSR); | |
| 230 | −coresight_tmc_simple_func(ffcr, TMC_FFCR); | |
| 231 | −coresight_tmc_simple_func(mode, TMC_MODE); | |
| 232 | −coresight_tmc_simple_func(pscr, TMC_PSCR); | |
| 233 | −coresight_tmc_simple_func(devid, CORESIGHT_DEVID); | |
| 220 | +#define coresight_tmc_reg(name, offset) \ | |
| 221 | + coresight_simple_reg32(struct tmc_drvdata, name, offset) | |
| 222 | +#define coresight_tmc_reg64(name, lo_off, hi_off) \ | |
| 223 | + coresight_simple_reg64(struct tmc_drvdata, name, lo_off, hi_off) | |
| 224 | + | |
| 225 | +coresight_tmc_reg(rsz, TMC_RSZ); | |
| 226 | +coresight_tmc_reg(sts, TMC_STS); | |
| 227 | +coresight_tmc_reg(trg, TMC_TRG); | |
| 228 | +coresight_tmc_reg(ctl, TMC_CTL); | |
| 229 | +coresight_tmc_reg(ffsr, TMC_FFSR); | |
| 230 | +coresight_tmc_reg(ffcr, TMC_FFCR); | |
| 231 | +coresight_tmc_reg(mode, TMC_MODE); | |
| 232 | +coresight_tmc_reg(pscr, TMC_PSCR); | |
| 233 | +coresight_tmc_reg(axictl, TMC_AXICTL); | |
| 234 | +coresight_tmc_reg(devid, CORESIGHT_DEVID); | |
| 235 | +coresight_tmc_reg64(rrp, TMC_RRP, TMC_RRPHI); | |
| 236 | +coresight_tmc_reg64(rwp, TMC_RWP, TMC_RWPHI); | |
| 237 | +coresight_tmc_reg64(dba, TMC_DBALO, TMC_DBAHI); | |
| 234 | 238 | |
| 235 | 239 | static struct attribute *coresight_tmc_mgmt_attrs[] = { |
| 236 | 240 | &dev_attr_rsz.attr, |
| @@ -244,6 +248,8 @@ static struct attribute *coresight_tmc_mgmt_attrs[] = { · 7 unmodified lines | ||
| 244 | 248 | &dev_attr_mode.attr, |
| 245 | 249 | &dev_attr_pscr.attr, |
| 246 | 250 | &dev_attr_devid.attr, |
| 251 | + &dev_attr_dba.attr, | |
| 252 | + &dev_attr_axictl.attr, | |
| 247 | 253 | NULL, |
| 248 | 254 | }; |
| 249 | 255 | |
| @@ -293,6 +299,42 @@ const struct attribute_group *coresight_tmc_groups[] = { · 43 unmodified lines | ||
| 293 | 299 | NULL, |
| 294 | 300 | }; |
| 295 | 301 | |
| 302 | +/* Detect and initialise the capabilities of a TMC ETR */ | |
| 303 | +static int tmc_etr_setup_caps(struct tmc_drvdata *drvdata, | |
| 304 | + u32 devid, void *dev_caps) | |
| 305 | +{ | |
| 306 | + u32 dma_mask = 0; | |
| 307 | + | |
| 308 | + /* Set the unadvertised capabilities */ | |
| 309 | + tmc_etr_init_caps(drvdata, (u32)(unsigned long)dev_caps); | |
| 310 | + | |
| 311 | + if (!(devid & TMC_DEVID_NOSCAT)) | |
| 312 | + tmc_etr_set_cap(drvdata, TMC_ETR_SG); | |
| 313 | + | |
| 314 | + /* Check if the AXI address width is available */ | |
| 315 | + if (devid & TMC_DEVID_AXIAW_VALID) | |
| 316 | + dma_mask = ((devid >> TMC_DEVID_AXIAW_SHIFT) & | |
| 317 | + TMC_DEVID_AXIAW_MASK); | |
| 318 | + | |
| 319 | + /* | |
| 320 | + * Unless specified in the device configuration, ETR uses a 40-bit | |
| 321 | + * AXI master in place of the embedded SRAM of ETB/ETF. | |
| 322 | + */ | |
| 323 | + switch (dma_mask) { | |
| 324 | + case 32: | |
| 325 | + case 40: | |
| 326 | + case 44: | |
| 327 | + case 48: | |
| 328 | + case 52: | |
| 329 | + dev_info(drvdata->dev, "Detected dma mask %dbits\n", dma_mask); | |
| 330 | + break; | |
| 331 | + default: | |
| 332 | + dma_mask = 40; | |
| 333 | + } | |
| 334 | + | |
| 335 | + return dma_set_mask_and_coherent(drvdata->dev, DMA_BIT_MASK(dma_mask)); | |
| 336 | +} | |
| 337 | + | |
| 296 | 338 | static int tmc_probe(struct amba_device *adev, const struct amba_id *id) |
| 297 | 339 | { |
| 298 | 340 | int ret = 0; |
| @@ -354,25 +396,29 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) · 55 unmodified lines | ||
| 354 | 396 | desc.dev = dev; |
| 355 | 397 | desc.groups = coresight_tmc_groups; |
| 356 | 398 | |
| 357 | − if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) { | |
| 399 | + switch (drvdata->config_type) { | |
| 400 | + case TMC_CONFIG_TYPE_ETB: | |
| 358 | 401 | desc.type = CORESIGHT_DEV_TYPE_SINK; |
| 359 | 402 | desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; |
| 360 | 403 | desc.ops = &tmc_etb_cs_ops; |
| 361 | − } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { | |
| 404 | + break; | |
| 405 | + case TMC_CONFIG_TYPE_ETR: | |
| 362 | 406 | desc.type = CORESIGHT_DEV_TYPE_SINK; |
| 363 | 407 | desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; |
| 364 | 408 | desc.ops = &tmc_etr_cs_ops; |
| 365 | − /* | |
| 366 | − * ETR configuration uses a 40-bit AXI master in place of | |
| 367 | − * the embedded SRAM of ETB/ETF. | |
| 368 | − */ | |
| 369 | − ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40)); | |
| 409 | + ret = tmc_etr_setup_caps(drvdata, devid, id->data); | |
| 370 | 410 | if (ret) |
| 371 | 411 | goto out; |
| 372 | − } else { | |
| 412 | + break; | |
| 413 | + case TMC_CONFIG_TYPE_ETF: | |
| 373 | 414 | desc.type = CORESIGHT_DEV_TYPE_LINKSINK; |
| 374 | 415 | desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO; |
| 375 | 416 | desc.ops = &tmc_etf_cs_ops; |
| 417 | + break; | |
| 418 | + default: | |
| 419 | + pr_err("%s: Unsupported TMC config\n", pdata->name); | |
| 420 | + ret = -EINVAL; | |
| 421 | + goto out; | |
| 376 | 422 | } |
| 377 | 423 | |
| 378 | 424 | drvdata->csdev = coresight_register(&desc); |
| @@ -391,11 +437,27 @@ out: · 12 unmodified lines | ||
| 391 | 437 | return ret; |
| 392 | 438 | } |
| 393 | 439 | |
| 394 | −static struct amba_id tmc_ids[] = { | |
| 440 | +static const struct amba_id tmc_ids[] = { | |
| 395 | 441 | { |
| 396 | 442 | .id = 0x0003b961, |
| 397 | 443 | .mask = 0x0003ffff, |
| 398 | 444 | }, |
| 445 | + { | |
| 446 | + /* Coresight SoC 600 TMC-ETR/ETS */ | |
| 447 | + .id = 0x000bb9e8, | |
| 448 | + .mask = 0x000fffff, | |
| 449 | + .data = (void *)(unsigned long)CORESIGHT_SOC_600_ETR_CAPS, | |
| 450 | + }, | |
| 451 | + { | |
| 452 | + /* Coresight SoC 600 TMC-ETB */ | |
| 453 | + .id = 0x000bb9e9, | |
| 454 | + .mask = 0x000fffff, | |
| 455 | + }, | |
| 456 | + { | |
| 457 | + /* Coresight SoC 600 TMC-ETF */ | |
| 458 | + .id = 0x000bb9ea, | |
| 459 | + .mask = 0x000fffff, | |
| 460 | + }, | |
| 399 | 461 | { 0, 0}, |
| 400 | 462 | }; |
| 401 | 463 | |
| @@ -54,11 +54,32 @@ · 53 unmodified lines | ||
| 54 | 54 | #define TMC_STS_TMCREADY_BIT 2 |
| 55 | 55 | #define TMC_STS_FULL BIT(0) |
| 56 | 56 | #define TMC_STS_TRIGGERED BIT(1) |
| 57 | −/* TMC_AXICTL - 0x110 */ | |
| 57 | +/* | |
| 58 | + * TMC_AXICTL - 0x110 | |
| 59 | + * | |
| 60 | + * TMC AXICTL format for SoC-400 | |
| 61 | + * Bits [0-1] : ProtCtrlBit0-1 | |
| 62 | + * Bits [2-5] : CacheCtrlBits 0-3 (AXCACHE) | |
| 63 | + * Bit 6 : Reserved | |
| 64 | + * Bit 7 : ScatterGatherMode | |
| 65 | + * Bits [8-11] : WrBurstLen | |
| 66 | + * Bits [12-31] : Reserved. | |
| 67 | + * TMC AXICTL format for SoC-600, as above except: | |
| 68 | + * Bits [2-5] : AXI WCACHE | |
| 69 | + * Bits [16-19] : AXI RCACHE | |
| 70 | + * Bits [20-31] : Reserved | |
| 71 | + */ | |
| 72 | +#define TMC_AXICTL_CLEAR_MASK 0xfbf | |
| 73 | +#define TMC_AXICTL_ARCACHE_MASK (0xf << 16) | |
| 74 | + | |
| 58 | 75 | #define TMC_AXICTL_PROT_CTL_B0 BIT(0) |
| 59 | 76 | #define TMC_AXICTL_PROT_CTL_B1 BIT(1) |
| 60 | 77 | #define TMC_AXICTL_SCT_GAT_MODE BIT(7) |
| 61 | 78 | #define TMC_AXICTL_WR_BURST_16 0xF00 |
| 79 | +/* Write-back Read and Write-allocate */ | |
| 80 | +#define TMC_AXICTL_AXCACHE_OS (0xf << 2) | |
| 81 | +#define TMC_AXICTL_ARCACHE_OS (0xf << 16) | |
| 82 | + | |
| 62 | 83 | /* TMC_FFCR - 0x304 */ |
| 63 | 84 | #define TMC_FFCR_FLUSHMAN_BIT 6 |
| 64 | 85 | #define TMC_FFCR_EN_FMT BIT(0) |
| @@ -69,6 +90,12 @@ · 4 unmodified lines | ||
| 69 | 90 | #define TMC_FFCR_STOP_ON_FLUSH BIT(12) |
| 70 | 91 | |
| 71 | 92 | |
| 93 | +#define TMC_DEVID_NOSCAT BIT(24) | |
| 94 | + | |
| 95 | +#define TMC_DEVID_AXIAW_VALID BIT(16) | |
| 96 | +#define TMC_DEVID_AXIAW_SHIFT 17 | |
| 97 | +#define TMC_DEVID_AXIAW_MASK 0x7f | |
| 98 | + | |
| 72 | 99 | enum tmc_config_type { |
| 73 | 100 | TMC_CONFIG_TYPE_ETB, |
| 74 | 101 | TMC_CONFIG_TYPE_ETR, |
| @@ -88,6 +115,24 @@ enum tmc_mem_intf_width { · 13 unmodified lines | ||
| 88 | 115 | TMC_MEM_INTF_WIDTH_256BITS = 8, |
| 89 | 116 | }; |
| 90 | 117 | |
| 118 | +/* TMC ETR Capability bit definitions */ | |
| 119 | +#define TMC_ETR_SG (0x1U << 0) | |
| 120 | +/* ETR has separate read/write cache encodings */ | |
| 121 | +#define TMC_ETR_AXI_ARCACHE (0x1U << 1) | |
| 122 | +/* | |
| 123 | + * TMC_ETR_SAVE_RESTORE - Values of RRP/RWP/STS.Full are | |
| 124 | + * retained when TMC leaves Disabled state, allowing us to continue | |
| 125 | + * the tracing from a point where we stopped. This also implies that | |
| 126 | + * the RRP/RWP/STS.Full should always be programmed to the correct | |
| 127 | + * value. Unfortunately this is not advertised by the hardware, | |
| 128 | + * so we have to rely on PID of the IP to detect the functionality. | |
| 129 | + */ | |
| 130 | +#define TMC_ETR_SAVE_RESTORE (0x1U << 2) | |
| 131 | + | |
| 132 | +/* Coresight SoC-600 TMC-ETR unadvertised capabilities */ | |
| 133 | +#define CORESIGHT_SOC_600_ETR_CAPS \ | |
| 134 | + (TMC_ETR_SAVE_RESTORE | TMC_ETR_AXI_ARCACHE) | |
| 135 | + | |
| 91 | 136 | /** |
| 92 | 137 | * struct tmc_drvdata - specifics associated to an TMC component |
| 93 | 138 | * @base: memory mapped base address for this component. |
| @@ -104,6 +149,8 @@ enum tmc_mem_intf_width { · 10 unmodified lines | ||
| 104 | 149 | * @config_type: TMC variant, must be of type @tmc_config_type. |
| 105 | 150 | * @memwidth: width of the memory interface databus, in bytes. |
| 106 | 151 | * @trigger_cntr: amount of words to store after a trigger. |
| 152 | + * @etr_caps: Bitmask of capabilities of the TMC ETR, inferred from the | |
| 153 | + * device configuration register (DEVID) | |
| 107 | 154 | */ |
| 108 | 155 | struct tmc_drvdata { |
| 109 | 156 | void __iomem *base; |
| @@ -121,6 +168,7 @@ struct tmc_drvdata { · 11 unmodified lines | ||
| 121 | 168 | enum tmc_config_type config_type; |
| 122 | 169 | enum tmc_mem_intf_width memwidth; |
| 123 | 170 | u32 trigger_cntr; |
| 171 | + u32 etr_caps; | |
| 124 | 172 | }; |
| 125 | 173 | |
| 126 | 174 | /* Generic functions */ |
| @@ -139,4 +187,39 @@ extern const struct coresight_ops tmc_etf_cs_ops; · 12 unmodified lines | ||
| 139 | 187 | int tmc_read_prepare_etr(struct tmc_drvdata *drvdata); |
| 140 | 188 | int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata); |
| 141 | 189 | extern const struct coresight_ops tmc_etr_cs_ops; |
| 190 | + | |
| 191 | + | |
| 192 | +#define TMC_REG_PAIR(name, lo_off, hi_off) \ | |
| 193 | +static inline u64 \ | |
| 194 | +tmc_read_##name(struct tmc_drvdata *drvdata) \ | |
| 195 | +{ \ | |
| 196 | + return coresight_read_reg_pair(drvdata->base, lo_off, hi_off); \ | |
| 197 | +} \ | |
| 198 | +static inline void \ | |
| 199 | +tmc_write_##name(struct tmc_drvdata *drvdata, u64 val) \ | |
| 200 | +{ \ | |
| 201 | + coresight_write_reg_pair(drvdata->base, val, lo_off, hi_off); \ | |
| 202 | +} | |
| 203 | + | |
| 204 | +TMC_REG_PAIR(rrp, TMC_RRP, TMC_RRPHI) | |
| 205 | +TMC_REG_PAIR(rwp, TMC_RWP, TMC_RWPHI) | |
| 206 | +TMC_REG_PAIR(dba, TMC_DBALO, TMC_DBAHI) | |
| 207 | + | |
| 208 | +/* Initialise the caps from unadvertised static capabilities of the device */ | |
| 209 | +static inline void tmc_etr_init_caps(struct tmc_drvdata *drvdata, u32 dev_caps) | |
| 210 | +{ | |
| 211 | + WARN_ON(drvdata->etr_caps); | |
| 212 | + drvdata->etr_caps = dev_caps; | |
| 213 | +} | |
| 214 | + | |
| 215 | +static inline void tmc_etr_set_cap(struct tmc_drvdata *drvdata, u32 cap) | |
| 216 | +{ | |
| 217 | + drvdata->etr_caps |= cap; | |
| 218 | +} | |
| 219 | + | |
| 220 | +static inline bool tmc_etr_has_cap(struct tmc_drvdata *drvdata, u32 cap) | |
| 221 | +{ | |
| 222 | + return !!(drvdata->etr_caps & cap); | |
| 223 | +} | |
| 224 | + | |
| 142 | 225 | #endif |
| @@ -192,7 +192,7 @@ static const struct dev_pm_ops tpiu_dev_pm_ops = { · 191 unmodified lines | ||
| 192 | 192 | SET_RUNTIME_PM_OPS(tpiu_runtime_suspend, tpiu_runtime_resume, NULL) |
| 193 | 193 | }; |
| 194 | 194 | |
| 195 | −static struct amba_id tpiu_ids[] = { | |
| 195 | +static const struct amba_id tpiu_ids[] = { | |
| 196 | 196 | { |
| 197 | 197 | .id = 0x0003b912, |
| 198 | 198 | .mask = 0x0003ffff, |
| @@ -201,6 +201,11 @@ static struct amba_id tpiu_ids[] = { · 2 unmodified lines | ||
| 201 | 201 | .id = 0x0004b912, |
| 202 | 202 | .mask = 0x0007ffff, |
| 203 | 203 | }, |
| 204 | + { | |
| 205 | + /* Coresight SoC-600 */ | |
| 206 | + .id = 0x000bb9e7, | |
| 207 | + .mask = 0x000fffff, | |
| 208 | + }, | |
| 204 | 209 | { 0, 0}, |
| 205 | 210 | }; |
| 206 | 211 | |
| @@ -53,6 +53,14 @@ static DEFINE_PER_CPU(struct list_head *, tracer_path); · 52 unmodified lines | ||
| 53 | 53 | */ |
| 54 | 54 | static struct list_head *stm_path; |
| 55 | 55 | |
| 56 | +/* | |
| 57 | + * When losing synchronisation a new barrier packet needs to be inserted at the | |
| 58 | + * beginning of the data collected in a buffer. That way the decoder knows that | |
| 59 | + * it needs to look for another sync sequence. | |
| 60 | + */ | |
| 61 | +const u32 barrier_pkt[5] = {0x7fffffff, 0x7fffffff, | |
| 62 | + 0x7fffffff, 0x7fffffff, 0x0}; | |
| 63 | + | |
| 56 | 64 | static int coresight_id_match(struct device *dev, void *data) |
| 57 | 65 | { |
| 58 | 66 | int trace_id, i_trace_id; |
| @@ -101,17 +101,53 @@ out_pm: · 100 unmodified lines | ||
| 101 | 101 | return ret; |
| 102 | 102 | } |
| 103 | 103 | |
| 104 | +static void intel_th_device_remove(struct intel_th_device *thdev); | |
| 105 | + | |
| 104 | 106 | static int intel_th_remove(struct device *dev) |
| 105 | 107 | { |
| 106 | 108 | struct intel_th_driver *thdrv = to_intel_th_driver(dev->driver); |
| 107 | 109 | struct intel_th_device *thdev = to_intel_th_device(dev); |
| 108 | − struct intel_th_device *hub = to_intel_th_device(dev->parent); | |
| 110 | + struct intel_th_device *hub = to_intel_th_hub(thdev); | |
| 109 | 111 | int err; |
| 110 | 112 | |
| 111 | 113 | if (thdev->type == INTEL_TH_SWITCH) { |
| 114 | + struct intel_th *th = to_intel_th(hub); | |
| 115 | + int i, lowest; | |
| 116 | + | |
| 117 | + /* disconnect outputs */ | |
| 112 | 118 | err = device_for_each_child(dev, thdev, intel_th_child_remove); |
| 113 | 119 | if (err) |
| 114 | 120 | return err; |
| 121 | + | |
| 122 | + /* | |
| 123 | + * Remove outputs, that is, hub's children: they are created | |
| 124 | + * at hub's probe time by having the hub call | |
| 125 | + * intel_th_output_enable() for each of them. | |
| 126 | + */ | |
| 127 | + for (i = 0, lowest = -1; i < th->num_thdevs; i++) { | |
| 128 | + /* | |
| 129 | + * Move the non-output devices from higher up the | |
| 130 | + * th->thdev[] array to lower positions to maintain | |
| 131 | + * a contiguous array. | |
| 132 | + */ | |
| 133 | + if (th->thdev[i]->type != INTEL_TH_OUTPUT) { | |
| 134 | + if (lowest >= 0) { | |
| 135 | + th->thdev[lowest] = th->thdev[i]; | |
| 136 | + th->thdev[i] = NULL; | |
| 137 | + ++lowest; | |
| 138 | + } | |
| 139 | + | |
| 140 | + continue; | |
| 141 | + } | |
| 142 | + | |
| 143 | + if (lowest == -1) | |
| 144 | + lowest = i; | |
| 145 | + | |
| 146 | + intel_th_device_remove(th->thdev[i]); | |
| 147 | + th->thdev[i] = NULL; | |
| 148 | + } | |
| 149 | + | |
| 150 | + th->num_thdevs = lowest; | |
| 115 | 151 | } |
| 116 | 152 | |
| 117 | 153 | if (thdrv->attr_group) |
| @@ -156,21 +192,6 @@ static struct device_type intel_th_source_device_type = { · 38 unmodified lines | ||
| 156 | 192 | .release = intel_th_device_release, |
| 157 | 193 | }; |
| 158 | 194 | |
| 159 | −static struct intel_th *to_intel_th(struct intel_th_device *thdev) | |
| 160 | −{ | |
| 161 | − /* | |
| 162 | − * subdevice tree is flat: if this one is not a switch, its | |
| 163 | − * parent must be | |
| 164 | − */ | |
| 165 | − if (thdev->type != INTEL_TH_SWITCH) | |
| 166 | − thdev = to_intel_th_hub(thdev); | |
| 167 | − | |
| 168 | − if (WARN_ON_ONCE(!thdev || thdev->type != INTEL_TH_SWITCH)) | |
| 169 | − return NULL; | |
| 170 | − | |
| 171 | − return dev_get_drvdata(thdev->dev.parent); | |
| 172 | −} | |
| 173 | − | |
| 174 | 195 | static char *intel_th_output_devnode(struct device *dev, umode_t *mode, |
| 175 | 196 | kuid_t *uid, kgid_t *gid) |
| 176 | 197 | { |
| @@ -205,6 +226,7 @@ static int intel_th_output_activate(struct intel_th_device *thdev) · 28 unmodified lines | ||
| 205 | 226 | { |
| 206 | 227 | struct intel_th_driver *thdrv = |
| 207 | 228 | to_intel_th_driver_or_null(thdev->dev.driver); |
| 229 | + struct intel_th *th = to_intel_th(thdev); | |
| 208 | 230 | int ret = 0; |
| 209 | 231 | |
| 210 | 232 | if (!thdrv) |
| @@ -215,15 +237,28 @@ static int intel_th_output_activate(struct intel_th_device *thdev) · 4 unmodified lines | ||
| 215 | 237 | |
| 216 | 238 | pm_runtime_get_sync(&thdev->dev); |
| 217 | 239 | |
| 240 | + if (th->activate) | |
| 241 | + ret = th->activate(th); | |
| 242 | + if (ret) | |
| 243 | + goto fail_put; | |
| 244 | + | |
| 218 | 245 | if (thdrv->activate) |
| 219 | 246 | ret = thdrv->activate(thdev); |
| 220 | 247 | else |
| 221 | 248 | intel_th_trace_enable(thdev); |
| 222 | 249 | |
| 223 | − if (ret) { | |
| 224 | − pm_runtime_put(&thdev->dev); | |
| 225 | − module_put(thdrv->driver.owner); | |
| 226 | − } | |
| 250 | + if (ret) | |
| 251 | + goto fail_deactivate; | |
| 252 | + | |
| 253 | + return 0; | |
| 254 | + | |
| 255 | +fail_deactivate: | |
| 256 | + if (th->deactivate) | |
| 257 | + th->deactivate(th); | |
| 258 | + | |
| 259 | +fail_put: | |
| 260 | + pm_runtime_put(&thdev->dev); | |
| 261 | + module_put(thdrv->driver.owner); | |
| 227 | 262 | |
| 228 | 263 | return ret; |
| 229 | 264 | } |
| @@ -232,6 +267,7 @@ static void intel_th_output_deactivate(struct intel_th_device *thdev) · 2 unmodified lines | ||
| 232 | 267 | { |
| 233 | 268 | struct intel_th_driver *thdrv = |
| 234 | 269 | to_intel_th_driver_or_null(thdev->dev.driver); |
| 270 | + struct intel_th *th = to_intel_th(thdev); | |
| 235 | 271 | |
| 236 | 272 | if (!thdrv) |
| 237 | 273 | return; |
| @@ -241,6 +277,9 @@ static void intel_th_output_deactivate(struct intel_th_device *thdev) · 3 unmodified lines | ||
| 241 | 277 | else |
| 242 | 278 | intel_th_trace_disable(thdev); |
| 243 | 279 | |
| 280 | + if (th->deactivate) | |
| 281 | + th->deactivate(th); | |
| 282 | + | |
| 244 | 283 | pm_runtime_put(&thdev->dev); |
| 245 | 284 | module_put(thdrv->driver.owner); |
| 246 | 285 | } |
| @@ -326,10 +365,10 @@ intel_th_device_alloc(struct intel_th *th, unsigned int type, const char *name, · 79 unmodified lines | ||
| 326 | 365 | struct device *parent; |
| 327 | 366 | struct intel_th_device *thdev; |
| 328 | 367 | |
| 329 | − if (type == INTEL_TH_SWITCH) | |
| 330 | − parent = th->dev; | |
| 331 | − else | |
| 368 | + if (type == INTEL_TH_OUTPUT) | |
| 332 | 369 | parent = &th->hub->dev; |
| 370 | + else | |
| 371 | + parent = th->dev; | |
| 333 | 372 | |
| 334 | 373 | thdev = kzalloc(sizeof(*thdev) + strlen(name) + 1, GFP_KERNEL); |
| 335 | 374 | if (!thdev) |
| @@ -392,13 +431,14 @@ static const struct intel_th_subdevice { · 56 unmodified lines | ||
| 392 | 431 | unsigned otype; |
| 393 | 432 | unsigned scrpd; |
| 394 | 433 | int id; |
| 395 | −} intel_th_subdevices[TH_SUBDEVICE_MAX] = { | |
| 434 | +} intel_th_subdevices[] = { | |
| 396 | 435 | { |
| 397 | 436 | .nres = 1, |
| 398 | 437 | .res = { |
| 399 | 438 | { |
| 439 | + /* Handle TSCU from GTH driver */ | |
| 400 | 440 | .start = REG_GTH_OFFSET, |
| 401 | − .end = REG_GTH_OFFSET + REG_GTH_LENGTH - 1, | |
| 441 | + .end = REG_TSCU_OFFSET + REG_TSCU_LENGTH - 1, | |
| 402 | 442 | .flags = IORESOURCE_MEM, |
| 403 | 443 | }, |
| 404 | 444 | }, |
| @@ -479,6 +519,21 @@ static const struct intel_th_subdevice { · 74 unmodified lines | ||
| 479 | 519 | .otype = GTH_PTI, |
| 480 | 520 | .scrpd = SCRPD_PTI_IS_PRIM_DEST, |
| 481 | 521 | }, |
| 522 | + { | |
| 523 | + .nres = 1, | |
| 524 | + .res = { | |
| 525 | + { | |
| 526 | + .start = REG_PTI_OFFSET, | |
| 527 | + .end = REG_PTI_OFFSET + REG_PTI_LENGTH - 1, | |
| 528 | + .flags = IORESOURCE_MEM, | |
| 529 | + }, | |
| 530 | + }, | |
| 531 | + .id = -1, | |
| 532 | + .name = "lpp", | |
| 533 | + .type = INTEL_TH_OUTPUT, | |
| 534 | + .otype = GTH_LPP, | |
| 535 | + .scrpd = SCRPD_PTI_IS_PRIM_DEST, | |
| 536 | + }, | |
| 482 | 537 | { |
| 483 | 538 | .nres = 1, |
| 484 | 539 | .res = { |
| @@ -526,98 +581,182 @@ static inline void intel_th_request_hub_module_flush(struct intel_th *th) · 41 unmodified lines | ||
| 526 | 581 | } |
| 527 | 582 | #endif /* CONFIG_MODULES */ |
| 528 | 583 | |
| 529 | −static int intel_th_populate(struct intel_th *th, struct resource *devres, | |
| 530 | − unsigned int ndevres, int irq) | |
| 584 | +static struct intel_th_device * | |
| 585 | +intel_th_subdevice_alloc(struct intel_th *th, | |
| 586 | + const struct intel_th_subdevice *subdev) | |
| 531 | 587 | { |
| 588 | + struct intel_th_device *thdev; | |
| 532 | 589 | struct resource res[3]; |
| 533 | 590 | unsigned int req = 0; |
| 534 | − int src, dst, err; | |
| 591 | + int r, err; | |
| 535 | 592 | |
| 536 | − /* create devices for each intel_th_subdevice */ | |
| 537 | − for (src = 0, dst = 0; src < ARRAY_SIZE(intel_th_subdevices); src++) { | |
| 538 | − const struct intel_th_subdevice *subdev = | |
| 539 | − &intel_th_subdevices[src]; | |
| 540 | − struct intel_th_device *thdev; | |
| 541 | − int r; | |
| 593 | + thdev = intel_th_device_alloc(th, subdev->type, subdev->name, | |
| 594 | + subdev->id); | |
| 595 | + if (!thdev) | |
| 596 | + return ERR_PTR(-ENOMEM); | |
| 542 | 597 | |
| 543 | − /* only allow SOURCE and SWITCH devices in host mode */ | |
| 544 | − if (host_mode && subdev->type == INTEL_TH_OUTPUT) | |
| 545 | − continue; | |
| 598 | + thdev->drvdata = th->drvdata; | |
| 599 | + | |
| 600 | + memcpy(res, subdev->res, | |
| 601 | + sizeof(struct resource) * subdev->nres); | |
| 602 | + | |
| 603 | + for (r = 0; r < subdev->nres; r++) { | |
| 604 | + struct resource *devres = th->resource; | |
| 605 | + int bar = TH_MMIO_CONFIG; | |
| 606 | + | |
| 607 | + /* | |
| 608 | + * Take .end == 0 to mean 'take the whole bar', | |
| 609 | + * .start then tells us which bar it is. Default to | |
| 610 | + * TH_MMIO_CONFIG. | |
| 611 | + */ | |
| 612 | + if (!res[r].end && res[r].flags == IORESOURCE_MEM) { | |
| 613 | + bar = res[r].start; | |
| 614 | + res[r].start = 0; | |
| 615 | + res[r].end = resource_size(&devres[bar]) - 1; | |
| 616 | + } | |
| 617 | + | |
| 618 | + if (res[r].flags & IORESOURCE_MEM) { | |
| 619 | + res[r].start += devres[bar].start; | |
| 620 | + res[r].end += devres[bar].start; | |
| 546 | 621 | |
| 547 | − thdev = intel_th_device_alloc(th, subdev->type, subdev->name, | |
| 548 | − subdev->id); | |
| 549 | − if (!thdev) { | |
| 550 | − err = -ENOMEM; | |
| 551 | − goto kill_subdevs; | |
| 622 | + dev_dbg(th->dev, "%s:%d @ %pR\n", | |
| 623 | + subdev->name, r, &res[r]); | |
| 624 | + } else if (res[r].flags & IORESOURCE_IRQ) { | |
| 625 | + res[r].start = th->irq; | |
| 552 | 626 | } |
| 627 | + } | |
| 553 | 628 | |
| 554 | − memcpy(res, subdev->res, | |
| 555 | − sizeof(struct resource) * subdev->nres); | |
| 629 | + err = intel_th_device_add_resources(thdev, res, subdev->nres); | |
| 630 | + if (err) { | |
| 631 | + put_device(&thdev->dev); | |
| 632 | + goto fail_put_device; | |
| 633 | + } | |
| 556 | 634 | |
| 557 | − for (r = 0; r < subdev->nres; r++) { | |
| 558 | − int bar = TH_MMIO_CONFIG; | |
| 635 | + if (subdev->type == INTEL_TH_OUTPUT) { | |
| 636 | + thdev->dev.devt = MKDEV(th->major, th->num_thdevs); | |
| 637 | + thdev->output.type = subdev->otype; | |
| 638 | + thdev->output.port = -1; | |
| 639 | + thdev->output.scratchpad = subdev->scrpd; | |
| 640 | + } else if (subdev->type == INTEL_TH_SWITCH) { | |
| 641 | + thdev->host_mode = host_mode; | |
| 642 | + th->hub = thdev; | |
| 643 | + } | |
| 559 | 644 | |
| 560 | − /* | |
| 561 | − * Take .end == 0 to mean 'take the whole bar', | |
| 562 | − * .start then tells us which bar it is. Default to | |
| 563 | − * TH_MMIO_CONFIG. | |
| 564 | − */ | |
| 565 | − if (!res[r].end && res[r].flags == IORESOURCE_MEM) { | |
| 566 | − bar = res[r].start; | |
| 567 | − res[r].start = 0; | |
| 568 | − res[r].end = resource_size(&devres[bar]) - 1; | |
| 569 | − } | |
| 645 | + err = device_add(&thdev->dev); | |
| 646 | + if (err) { | |
| 647 | + put_device(&thdev->dev); | |
| 648 | + goto fail_free_res; | |
| 649 | + } | |
| 570 | 650 | |
| 571 | − if (res[r].flags & IORESOURCE_MEM) { | |
| 572 | − res[r].start += devres[bar].start; | |
| 573 | − res[r].end += devres[bar].start; | |
| 651 | + /* need switch driver to be loaded to enumerate the rest */ | |
| 652 | + if (subdev->type == INTEL_TH_SWITCH && !req) { | |
| 653 | + err = intel_th_request_hub_module(th); | |
| 654 | + if (!err) | |
| 655 | + req++; | |
| 656 | + } | |
| 574 | 657 | |
| 575 | − dev_dbg(th->dev, "%s:%d @ %pR\n", | |
| 576 | − subdev->name, r, &res[r]); | |
| 577 | − } else if (res[r].flags & IORESOURCE_IRQ) { | |
| 578 | − res[r].start = irq; | |
| 579 | − } | |
| 580 | − } | |
| 658 | + return thdev; | |
| 581 | 659 | |
| 582 | − err = intel_th_device_add_resources(thdev, res, subdev->nres); | |
| 583 | − if (err) { | |
| 584 | − put_device(&thdev->dev); | |
| 585 | − goto kill_subdevs; | |
| 586 | − } | |
| 660 | +fail_free_res: | |
| 661 | + kfree(thdev->resource); | |
| 587 | 662 | |
| 588 | − if (subdev->type == INTEL_TH_OUTPUT) { | |
| 589 | − thdev->dev.devt = MKDEV(th->major, dst); | |
| 590 | − thdev->output.type = subdev->otype; | |
| 591 | − thdev->output.port = -1; | |
| 592 | − thdev->output.scratchpad = subdev->scrpd; | |
| 593 | − } else if (subdev->type == INTEL_TH_SWITCH) { | |
| 594 | − thdev->host_mode = host_mode; | |
| 595 | − } | |
| 663 | +fail_put_device: | |
| 664 | + put_device(&thdev->dev); | |
| 665 | + | |
| 666 | + return ERR_PTR(err); | |
| 667 | +} | |
| 596 | 668 | |
| 597 | − err = device_add(&thdev->dev); | |
| 598 | − if (err) { | |
| 599 | − put_device(&thdev->dev); | |
| 600 | − goto kill_subdevs; | |
| 669 | +/** | |
| 670 | + * intel_th_output_enable() - find and enable a device for a given output type | |
| 671 | + * @th: Intel TH instance | |
| 672 | + * @otype: output type | |
| 673 | + * | |
| 674 | + * Go through the unallocated output devices, find the first one whos type | |
| 675 | + * matches @otype and instantiate it. These devices are removed when the hub | |
| 676 | + * device is removed, see intel_th_remove(). | |
| 677 | + */ | |
| 678 | +int intel_th_output_enable(struct intel_th *th, unsigned int otype) | |
| 679 | +{ | |
| 680 | + struct intel_th_device *thdev; | |
| 681 | + int src = 0, dst = 0; | |
| 682 | + | |
| 683 | + for (src = 0, dst = 0; dst <= th->num_thdevs; src++, dst++) { | |
| 684 | + for (; src < ARRAY_SIZE(intel_th_subdevices); src++) { | |
| 685 | + if (intel_th_subdevices[src].type != INTEL_TH_OUTPUT) | |
| 686 | + continue; | |
| 687 | + | |
| 688 | + if (intel_th_subdevices[src].otype != otype) | |
| 689 | + continue; | |
| 690 | + | |
| 691 | + break; | |
| 601 | 692 | } |
| 602 | 693 | |
| 603 | − /* need switch driver to be loaded to enumerate the rest */ | |
| 604 | − if (subdev->type == INTEL_TH_SWITCH && !req) { | |
| 605 | − th->hub = thdev; | |
| 606 | − err = intel_th_request_hub_module(th); | |
| 607 | − if (!err) | |
| 608 | − req++; | |
| 694 | + /* no unallocated matching subdevices */ | |
| 695 | + if (src == ARRAY_SIZE(intel_th_subdevices)) | |
| 696 | + return -ENODEV; | |
| 697 | + | |
| 698 | + for (; dst < th->num_thdevs; dst++) { | |
| 699 | + if (th->thdev[dst]->type != INTEL_TH_OUTPUT) | |
| 700 | + continue; | |
| 701 | + | |
| 702 | + if (th->thdev[dst]->output.type != otype) | |
| 703 | + continue; | |
| 704 | + | |
| 705 | + break; | |
| 609 | 706 | } |
| 610 | 707 | |
| 611 | − th->thdev[dst++] = thdev; | |
| 708 | + /* | |
| 709 | + * intel_th_subdevices[src] matches our requirements and is | |
| 710 | + * not matched in th::thdev[] | |
| 711 | + */ | |
| 712 | + if (dst == th->num_thdevs) | |
| 713 | + goto found; | |
| 612 | 714 | } |
| 613 | 715 | |
| 716 | + return -ENODEV; | |
| 717 | + | |
| 718 | +found: | |
| 719 | + thdev = intel_th_subdevice_alloc(th, &intel_th_subdevices[src]); | |
| 720 | + if (IS_ERR(thdev)) | |
| 721 | + return PTR_ERR(thdev); | |
| 722 | + | |
| 723 | + th->thdev[th->num_thdevs++] = thdev; | |
| 724 | + | |
| 614 | 725 | return 0; |
| 726 | +} | |
| 727 | +EXPORT_SYMBOL_GPL(intel_th_output_enable); | |
| 728 | + | |
| 729 | +static int intel_th_populate(struct intel_th *th) | |
| 730 | +{ | |
| 731 | + int src; | |
| 732 | + | |
| 733 | + /* create devices for each intel_th_subdevice */ | |
| 734 | + for (src = 0; src < ARRAY_SIZE(intel_th_subdevices); src++) { | |
| 735 | + const struct intel_th_subdevice *subdev = | |
| 736 | + &intel_th_subdevices[src]; | |
| 737 | + struct intel_th_device *thdev; | |
| 738 | + | |
| 739 | + /* only allow SOURCE and SWITCH devices in host mode */ | |
| 740 | + if (host_mode && subdev->type == INTEL_TH_OUTPUT) | |
| 741 | + continue; | |
| 615 | 742 | |
| 616 | −kill_subdevs: | |
| 617 | − for (; dst >= 0; dst--) | |
| 618 | − intel_th_device_remove(th->thdev[dst]); | |
| 743 | + /* | |
| 744 | + * don't enable port OUTPUTs in this path; SWITCH enables them | |
| 745 | + * via intel_th_output_enable() | |
| 746 | + */ | |
| 747 | + if (subdev->type == INTEL_TH_OUTPUT && | |
| 748 | + subdev->otype != GTH_NONE) | |
| 749 | + continue; | |
| 750 | + | |
| 751 | + thdev = intel_th_subdevice_alloc(th, subdev); | |
| 752 | + /* note: caller should free subdevices from th::thdev[] */ | |
| 753 | + if (IS_ERR(thdev)) | |
| 754 | + return PTR_ERR(thdev); | |
| 755 | + | |
| 756 | + th->thdev[th->num_thdevs++] = thdev; | |
| 757 | + } | |
| 619 | 758 | |
| 620 | − return err; | |
| 759 | + return 0; | |
| 621 | 760 | } |
| 622 | 761 | |
| 623 | 762 | static int match_devt(struct device *dev, void *data) |
| @@ -670,8 +809,8 @@ static const struct file_operations intel_th_output_fops = { · 46 unmodified lines | ||
| 670 | 809 | * @irq: irq number |
| 671 | 810 | */ |
| 672 | 811 | struct intel_th * |
| 673 | −intel_th_alloc(struct device *dev, struct resource *devres, | |
| 674 | − unsigned int ndevres, int irq) | |
| 812 | +intel_th_alloc(struct device *dev, struct intel_th_drvdata *drvdata, | |
| 813 | + struct resource *devres, unsigned int ndevres, int irq) | |
| 675 | 814 | { |
| 676 | 815 | struct intel_th *th; |
| 677 | 816 | int err; |
| @@ -693,6 +832,11 @@ intel_th_alloc(struct device *dev, struct resource *devres, · 15 unmodified lines | ||
| 693 | 832 | goto err_ida; |
| 694 | 833 | } |
| 695 | 834 | th->dev = dev; |
| 835 | + th->drvdata = drvdata; | |
| 836 | + | |
| 837 | + th->resource = devres; | |
| 838 | + th->num_resources = ndevres; | |
| 839 | + th->irq = irq; | |
| 696 | 840 | |
| 697 | 841 | dev_set_drvdata(dev, th); |
| 698 | 842 | |
| @@ -700,18 +844,15 @@ intel_th_alloc(struct device *dev, struct resource *devres, · 1 unmodified line | ||
| 700 | 844 | pm_runtime_put(dev); |
| 701 | 845 | pm_runtime_allow(dev); |
| 702 | 846 | |
| 703 | − err = intel_th_populate(th, devres, ndevres, irq); | |
| 704 | − if (err) | |
| 705 | − goto err_chrdev; | |
| 847 | + err = intel_th_populate(th); | |
| 848 | + if (err) { | |
| 849 | + /* free the subdevices and undo everything */ | |
| 850 | + intel_th_free(th); | |
| 851 | + return ERR_PTR(err); | |
| 852 | + } | |
| 706 | 853 | |
| 707 | 854 | return th; |
| 708 | 855 | |
| 709 | −err_chrdev: | |
| 710 | − pm_runtime_forbid(dev); | |
| 711 | − | |
| 712 | − __unregister_chrdev(th->major, 0, TH_POSSIBLE_OUTPUTS, | |
| 713 | − "intel_th/output"); | |
| 714 | − | |
| 715 | 856 | err_ida: |
| 716 | 857 | ida_simple_remove(&intel_th_ida, th->id); |
| 717 | 858 | |
| @@ -727,11 +868,15 @@ void intel_th_free(struct intel_th *th) · 9 unmodified lines | ||
| 727 | 868 | int i; |
| 728 | 869 | |
| 729 | 870 | intel_th_request_hub_module_flush(th); |
| 730 | − for (i = 0; i < TH_SUBDEVICE_MAX; i++) | |
| 731 | − if (th->thdev[i] && th->thdev[i] != th->hub) | |
| 732 | − intel_th_device_remove(th->thdev[i]); | |
| 733 | 871 | |
| 734 | 872 | intel_th_device_remove(th->hub); |
| 873 | + for (i = 0; i < th->num_thdevs; i++) { | |
| 874 | + if (th->thdev[i] != th->hub) | |
| 875 | + intel_th_device_remove(th->thdev[i]); | |
| 876 | + th->thdev[i] = NULL; | |
| 877 | + } | |
| 878 | + | |
| 879 | + th->num_thdevs = 0; | |
| 735 | 880 | |
| 736 | 881 | pm_runtime_get_sync(th->dev); |
| 737 | 882 | pm_runtime_forbid(th->dev); |
| @@ -285,16 +285,16 @@ gth_output_parm_get(struct gth_device *gth, int port, unsigned int parm) · 284 unmodified lines | ||
| 285 | 285 | */ |
| 286 | 286 | static int intel_th_gth_reset(struct gth_device *gth) |
| 287 | 287 | { |
| 288 | − u32 scratchpad; | |
| 288 | + u32 reg; | |
| 289 | 289 | int port, i; |
| 290 | 290 | |
| 291 | − scratchpad = ioread32(gth->base + REG_GTH_SCRPD0); | |
| 292 | − if (scratchpad & SCRPD_DEBUGGER_IN_USE) | |
| 291 | + reg = ioread32(gth->base + REG_GTH_SCRPD0); | |
| 292 | + if (reg & SCRPD_DEBUGGER_IN_USE) | |
| 293 | 293 | return -EBUSY; |
| 294 | 294 | |
| 295 | 295 | /* Always save/restore STH and TU registers in S0ix entry/exit */ |
| 296 | − scratchpad |= SCRPD_STH_IS_ENABLED | SCRPD_TRIGGER_IS_ENABLED; | |
| 297 | − iowrite32(scratchpad, gth->base + REG_GTH_SCRPD0); | |
| 296 | + reg |= SCRPD_STH_IS_ENABLED | SCRPD_TRIGGER_IS_ENABLED; | |
| 297 | + iowrite32(reg, gth->base + REG_GTH_SCRPD0); | |
| 298 | 298 | |
| 299 | 299 | /* output ports */ |
| 300 | 300 | for (port = 0; port < 8; port++) { |
| @@ -512,6 +512,15 @@ static void intel_th_gth_disable(struct intel_th_device *thdev, · 211 unmodified lines | ||
| 512 | 512 | iowrite32(reg, gth->base + REG_GTH_SCRPD0); |
| 513 | 513 | } |
| 514 | 514 | |
| 515 | +static void gth_tscu_resync(struct gth_device *gth) | |
| 516 | +{ | |
| 517 | + u32 reg; | |
| 518 | + | |
| 519 | + reg = ioread32(gth->base + REG_TSCU_TSUCTRL); | |
| 520 | + reg &= ~TSUCTRL_CTCRESYNC; | |
| 521 | + iowrite32(reg, gth->base + REG_TSCU_TSUCTRL); | |
| 522 | +} | |
| 523 | + | |
| 515 | 524 | /** |
| 516 | 525 | * intel_th_gth_enable() - enable tracing to an output device |
| 517 | 526 | * @thdev: GTH device |
| @@ -524,6 +533,7 @@ static void intel_th_gth_enable(struct intel_th_device *thdev, · 6 unmodified lines | ||
| 524 | 533 | struct intel_th_output *output) |
| 525 | 534 | { |
| 526 | 535 | struct gth_device *gth = dev_get_drvdata(&thdev->dev); |
| 536 | + struct intel_th *th = to_intel_th(thdev); | |
| 527 | 537 | u32 scr = 0xfc0000, scrpd; |
| 528 | 538 | int master; |
| 529 | 539 | |
| @@ -539,6 +549,9 @@ static void intel_th_gth_enable(struct intel_th_device *thdev, · 9 unmodified lines | ||
| 539 | 549 | output->active = true; |
| 540 | 550 | spin_unlock(>h->gth_lock); |
| 541 | 551 | |
| 552 | + if (INTEL_TH_CAP(th, tscu_enable)) | |
| 553 | + gth_tscu_resync(gth); | |
| 554 | + | |
| 542 | 555 | scrpd = ioread32(gth->base + REG_GTH_SCRPD0); |
| 543 | 556 | scrpd |= output->scratchpad; |
| 544 | 557 | iowrite32(scrpd, gth->base + REG_GTH_SCRPD0); |
| @@ -639,6 +652,7 @@ intel_th_gth_set_output(struct intel_th_device *thdev, unsigned int master) · 94 unmodified lines | ||
| 639 | 652 | static int intel_th_gth_probe(struct intel_th_device *thdev) |
| 640 | 653 | { |
| 641 | 654 | struct device *dev = &thdev->dev; |
| 655 | + struct intel_th *th = dev_get_drvdata(dev->parent); | |
| 642 | 656 | struct gth_device *gth; |
| 643 | 657 | struct resource *res; |
| 644 | 658 | void __iomem *base; |
| @@ -660,6 +674,8 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) · 15 unmodified lines | ||
| 660 | 674 | gth->base = base; |
| 661 | 675 | spin_lock_init(>h->gth_lock); |
| 662 | 676 | |
| 677 | + dev_set_drvdata(dev, gth); | |
| 678 | + | |
| 663 | 679 | /* |
| 664 | 680 | * Host mode can be signalled via SW means or via SCRPD_DEBUGGER_IN_USE |
| 665 | 681 | * bit. Either way, don't reset HW in this case, and don't export any |
| @@ -667,7 +683,7 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) · 1 unmodified line | ||
| 667 | 683 | * drivers to ports, see intel_th_gth_assign(). |
| 668 | 684 | */ |
| 669 | 685 | if (thdev->host_mode) |
| 670 | − goto done; | |
| 686 | + return 0; | |
| 671 | 687 | |
| 672 | 688 | ret = intel_th_gth_reset(gth); |
| 673 | 689 | if (ret) { |
| @@ -676,7 +692,7 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) · 2 unmodified lines | ||
| 676 | 692 | |
| 677 | 693 | thdev->host_mode = true; |
| 678 | 694 | |
| 679 | − goto done; | |
| 695 | + return 0; | |
| 680 | 696 | } |
| 681 | 697 | |
| 682 | 698 | for (i = 0; i < TH_CONFIGURABLE_MASTERS + 1; i++) |
| @@ -687,6 +703,13 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) · 4 unmodified lines | ||
| 687 | 703 | gth->output[i].index = i; |
| 688 | 704 | gth->output[i].port_type = |
| 689 | 705 | gth_output_parm_get(gth, i, TH_OUTPUT_PARM(port)); |
| 706 | + if (gth->output[i].port_type == GTH_NONE) | |
| 707 | + continue; | |
| 708 | + | |
| 709 | + ret = intel_th_output_enable(th, gth->output[i].port_type); | |
| 710 | + /* -ENODEV is ok, we just won't have that device enumerated */ | |
| 711 | + if (ret && ret != -ENODEV) | |
| 712 | + return ret; | |
| 690 | 713 | } |
| 691 | 714 | |
| 692 | 715 | if (intel_th_output_attributes(gth) || |
| @@ -698,9 +721,6 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) · 5 unmodified lines | ||
| 698 | 721 | return -ENOMEM; |
| 699 | 722 | } |
| 700 | 723 | |
| 701 | −done: | |
| 702 | − dev_set_drvdata(dev, gth); | |
| 703 | − | |
| 704 | 724 | return 0; |
| 705 | 725 | } |
| 706 | 726 | |
| @@ -55,9 +55,14 @@ enum { · 54 unmodified lines | ||
| 55 | 55 | REG_GTH_SCRPD1 = 0xe4, /* ScratchPad[1] */ |
| 56 | 56 | REG_GTH_SCRPD2 = 0xe8, /* ScratchPad[2] */ |
| 57 | 57 | REG_GTH_SCRPD3 = 0xec, /* ScratchPad[3] */ |
| 58 | + REG_TSCU_TSUCTRL = 0x2000, /* TSCU control register */ | |
| 59 | + REG_TSCU_TSCUSTAT = 0x2004, /* TSCU status register */ | |
| 58 | 60 | }; |
| 59 | 61 | |
| 60 | 62 | /* waiting for Pipeline Empty bit(s) to assert for GTH */ |
| 61 | 63 | #define GTH_PLE_WAITLOOP_DEPTH 10000 |
| 62 | 64 | |
| 65 | +#define TSUCTRL_CTCRESYNC BIT(0) | |
| 66 | +#define TSCUSTAT_CTCSYNCING BIT(1) | |
| 67 | + | |
| 63 | 68 | #endif /* __INTEL_TH_GTH_H__ */ |
| @@ -47,9 +47,20 @@ struct intel_th_output { · 46 unmodified lines | ||
| 47 | 47 | bool active; |
| 48 | 48 | }; |
| 49 | 49 | |
| 50 | +/** | |
| 51 | + * struct intel_th_drvdata - describes hardware capabilities and quirks | |
| 52 | + * @tscu_enable: device needs SW to enable time stamping unit | |
| 53 | + */ | |
| 54 | +struct intel_th_drvdata { | |
| 55 | + unsigned int tscu_enable : 1; | |
| 56 | +}; | |
| 57 | + | |
| 58 | +#define INTEL_TH_CAP(_th, _cap) ((_th)->drvdata ? (_th)->drvdata->_cap : 0) | |
| 59 | + | |
| 50 | 60 | /** |
| 51 | 61 | * struct intel_th_device - device on the intel_th bus |
| 52 | 62 | * @dev: device |
| 63 | + * @drvdata: hardware capabilities/quirks | |
| 53 | 64 | * @resource: array of resources available to this device |
| 54 | 65 | * @num_resources: number of resources in @resource array |
| 55 | 66 | * @type: INTEL_TH_{SOURCE,OUTPUT,SWITCH} |
| @@ -59,11 +70,12 @@ struct intel_th_output { · 3 unmodified lines | ||
| 59 | 70 | * @name: device name to match the driver |
| 60 | 71 | */ |
| 61 | 72 | struct intel_th_device { |
| 62 | − struct device dev; | |
| 63 | − struct resource *resource; | |
| 64 | − unsigned int num_resources; | |
| 65 | − unsigned int type; | |
| 66 | − int id; | |
| 73 | + struct device dev; | |
| 74 | + struct intel_th_drvdata *drvdata; | |
| 75 | + struct resource *resource; | |
| 76 | + unsigned int num_resources; | |
| 77 | + unsigned int type; | |
| 78 | + int id; | |
| 67 | 79 | |
| 68 | 80 | /* INTEL_TH_SWITCH specific */ |
| 69 | 81 | bool host_mode; |
| @@ -96,6 +108,17 @@ intel_th_device_get_resource(struct intel_th_device *thdev, unsigned int type, · 26 unmodified lines | ||
| 96 | 108 | return NULL; |
| 97 | 109 | } |
| 98 | 110 | |
| 111 | +/* | |
| 112 | + * GTH, output ports configuration | |
| 113 | + */ | |
| 114 | +enum { | |
| 115 | + GTH_NONE = 0, | |
| 116 | + GTH_MSU, /* memory/usb */ | |
| 117 | + GTH_CTP, /* Common Trace Port */ | |
| 118 | + GTH_LPP, /* Low Power Path */ | |
| 119 | + GTH_PTI, /* MIPI-PTI */ | |
| 120 | +}; | |
| 121 | + | |
| 99 | 122 | /** |
| 100 | 123 | * intel_th_output_assigned() - if an output device is assigned to a switch port |
| 101 | 124 | * @thdev: the output device |
| @@ -106,7 +129,8 @@ static inline bool · 4 unmodified lines | ||
| 106 | 129 | intel_th_output_assigned(struct intel_th_device *thdev) |
| 107 | 130 | { |
| 108 | 131 | return thdev->type == INTEL_TH_OUTPUT && |
| 109 | − thdev->output.port >= 0; | |
| 132 | + (thdev->output.port >= 0 || | |
| 133 | + thdev->output.type == GTH_NONE); | |
| 110 | 134 | } |
| 111 | 135 | |
| 112 | 136 | /** |
| @@ -161,8 +185,18 @@ struct intel_th_driver { · 48 unmodified lines | ||
| 161 | 185 | #define to_intel_th_driver_or_null(_d) \ |
| 162 | 186 | ((_d) ? to_intel_th_driver(_d) : NULL) |
| 163 | 187 | |
| 188 | +/* | |
| 189 | + * Subdevice tree structure is as follows: | |
| 190 | + * + struct intel_th device (pci; dev_{get,set}_drvdata() | |
| 191 | + * + struct intel_th_device INTEL_TH_SWITCH (GTH) | |
| 192 | + * + struct intel_th_device INTEL_TH_OUTPUT (MSU, PTI) | |
| 193 | + * + struct intel_th_device INTEL_TH_SOURCE (STH) | |
| 194 | + * | |
| 195 | + * In other words, INTEL_TH_OUTPUT devices are children of INTEL_TH_SWITCH; | |
| 196 | + * INTEL_TH_SWITCH and INTEL_TH_SOURCE are children of the intel_th device. | |
| 197 | + */ | |
| 164 | 198 | static inline struct intel_th_device * |
| 165 | −to_intel_th_hub(struct intel_th_device *thdev) | |
| 199 | +to_intel_th_parent(struct intel_th_device *thdev) | |
| 166 | 200 | { |
| 167 | 201 | struct device *parent = thdev->dev.parent; |
| 168 | 202 | |
| @@ -172,9 +206,20 @@ to_intel_th_hub(struct intel_th_device *thdev) · 3 unmodified lines | ||
| 172 | 206 | return to_intel_th_device(parent); |
| 173 | 207 | } |
| 174 | 208 | |
| 209 | +static inline struct intel_th *to_intel_th(struct intel_th_device *thdev) | |
| 210 | +{ | |
| 211 | + if (thdev->type == INTEL_TH_OUTPUT) | |
| 212 | + thdev = to_intel_th_parent(thdev); | |
| 213 | + | |
| 214 | + if (WARN_ON_ONCE(!thdev || thdev->type == INTEL_TH_OUTPUT)) | |
| 215 | + return NULL; | |
| 216 | + | |
| 217 | + return dev_get_drvdata(thdev->dev.parent); | |
| 218 | +} | |
| 219 | + | |
| 175 | 220 | struct intel_th * |
| 176 | −intel_th_alloc(struct device *dev, struct resource *devres, | |
| 177 | − unsigned int ndevres, int irq); | |
| 221 | +intel_th_alloc(struct device *dev, struct intel_th_drvdata *drvdata, | |
| 222 | + struct resource *devres, unsigned int ndevres, int irq); | |
| 178 | 223 | void intel_th_free(struct intel_th *th); |
| 179 | 224 | |
| 180 | 225 | int intel_th_driver_register(struct intel_th_driver *thdrv); |
| @@ -184,6 +229,7 @@ int intel_th_trace_enable(struct intel_th_device *thdev); · 3 unmodified lines | ||
| 184 | 229 | int intel_th_trace_disable(struct intel_th_device *thdev); |
| 185 | 230 | int intel_th_set_output(struct intel_th_device *thdev, |
| 186 | 231 | unsigned int master); |
| 232 | +int intel_th_output_enable(struct intel_th *th, unsigned int otype); | |
| 187 | 233 | |
| 188 | 234 | enum { |
| 189 | 235 | TH_MMIO_CONFIG = 0, |
| @@ -191,8 +237,9 @@ enum { · 1 unmodified line | ||
| 191 | 237 | TH_MMIO_END, |
| 192 | 238 | }; |
| 193 | 239 | |
| 194 | −#define TH_SUBDEVICE_MAX 6 | |
| 195 | 240 | #define TH_POSSIBLE_OUTPUTS 8 |
| 241 | +/* Total number of possible subdevices: outputs + GTH + STH */ | |
| 242 | +#define TH_SUBDEVICE_MAX (TH_POSSIBLE_OUTPUTS + 2) | |
| 196 | 243 | #define TH_CONFIGURABLE_MASTERS 256 |
| 197 | 244 | #define TH_MSC_MAX 2 |
| 198 | 245 | |
| @@ -201,6 +248,10 @@ enum { · 2 unmodified lines | ||
| 201 | 248 | * @dev: driver core's device |
| 202 | 249 | * @thdev: subdevices |
| 203 | 250 | * @hub: "switch" subdevice (GTH) |
| 251 | + * @resource: resources of the entire controller | |
| 252 | + * @num_thdevs: number of devices in the @thdev array | |
| 253 | + * @num_resources: number or resources in the @resource array | |
| 254 | + * @irq: irq number | |
| 204 | 255 | * @id: this Intel TH controller's device ID in the system |
| 205 | 256 | * @major: device node major for output devices |
| 206 | 257 | */ |
| @@ -209,6 +260,14 @@ struct intel_th { · 2 unmodified lines | ||
| 209 | 260 | |
| 210 | 261 | struct intel_th_device *thdev[TH_SUBDEVICE_MAX]; |
| 211 | 262 | struct intel_th_device *hub; |
| 263 | + struct intel_th_drvdata *drvdata; | |
| 264 | + | |
| 265 | + struct resource *resource; | |
| 266 | + int (*activate)(struct intel_th *); | |
| 267 | + void (*deactivate)(struct intel_th *); | |
| 268 | + unsigned int num_thdevs; | |
| 269 | + unsigned int num_resources; | |
| 270 | + int irq; | |
| 212 | 271 | |
| 213 | 272 | int id; |
| 214 | 273 | int major; |
| @@ -220,6 +279,17 @@ struct intel_th { · 5 unmodified lines | ||
| 220 | 279 | #endif |
| 221 | 280 | }; |
| 222 | 281 | |
| 282 | +static inline struct intel_th_device * | |
| 283 | +to_intel_th_hub(struct intel_th_device *thdev) | |
| 284 | +{ | |
| 285 | + if (thdev->type == INTEL_TH_SWITCH) | |
| 286 | + return thdev; | |
| 287 | + else if (thdev->type == INTEL_TH_OUTPUT) | |
| 288 | + return to_intel_th_parent(thdev); | |
| 289 | + | |
| 290 | + return to_intel_th(thdev)->hub; | |
| 291 | +} | |
| 292 | + | |
| 223 | 293 | /* |
| 224 | 294 | * Register windows |
| 225 | 295 | */ |
| @@ -228,6 +298,10 @@ enum { · 2 unmodified lines | ||
| 228 | 298 | REG_GTH_OFFSET = 0x0000, |
| 229 | 299 | REG_GTH_LENGTH = 0x2000, |
| 230 | 300 | |
| 301 | + /* Timestamp counter unit (TSCU) */ | |
| 302 | + REG_TSCU_OFFSET = 0x2000, | |
| 303 | + REG_TSCU_LENGTH = 0x1000, | |
| 304 | + | |
| 231 | 305 | /* Software Trace Hub (STH) [0x4000..0x4fff] */ |
| 232 | 306 | REG_STH_OFFSET = 0x4000, |
| 233 | 307 | REG_STH_LENGTH = 0x2000, |
| @@ -249,16 +323,6 @@ enum { · 15 unmodified lines | ||
| 249 | 323 | REG_DCIH_LENGTH = REG_MSU_LENGTH, |
| 250 | 324 | }; |
| 251 | 325 | |
| 252 | −/* | |
| 253 | − * GTH, output ports configuration | |
| 254 | − */ | |
| 255 | −enum { | |
| 256 | − GTH_NONE = 0, | |
| 257 | − GTH_MSU, /* memory/usb */ | |
| 258 | − GTH_CTP, /* Common Trace Port */ | |
| 259 | − GTH_PTI = 4, /* MIPI-PTI */ | |
| 260 | −}; | |
| 261 | − | |
| 262 | 326 | /* |
| 263 | 327 | * Scratchpad bits: tell firmware and external debuggers |
| 264 | 328 | * what we are up to. |
| @@ -709,17 +709,17 @@ static int msc_buffer_win_alloc(struct msc *msc, unsigned int nr_blocks) · 708 unmodified lines | ||
| 709 | 709 | } |
| 710 | 710 | |
| 711 | 711 | for (i = 0; i < nr_blocks; i++) { |
| 712 | − win->block[i].bdesc = dma_alloc_coherent(msc_dev(msc), size, | |
| 713 | − &win->block[i].addr, | |
| 714 | − GFP_KERNEL); | |
| 712 | + win->block[i].bdesc = | |
| 713 | + dma_alloc_coherent(msc_dev(msc)->parent->parent, size, | |
| 714 | + &win->block[i].addr, GFP_KERNEL); | |
| 715 | + | |
| 716 | + if (!win->block[i].bdesc) | |
| 717 | + goto err_nomem; | |
| 715 | 718 | |
| 716 | 719 | #ifdef CONFIG_X86 |
| 717 | 720 | /* Set the page as uncached */ |
| 718 | 721 | set_memory_uc((unsigned long)win->block[i].bdesc, 1); |
| 719 | 722 | #endif |
| 720 | − | |
| 721 | − if (!win->block[i].bdesc) | |
| 722 | − goto err_nomem; | |
| 723 | 723 | } |
| 724 | 724 | |
| 725 | 725 | win->msc = msc; |
| @@ -27,9 +27,53 @@ · 26 unmodified lines | ||
| 27 | 27 | |
| 28 | 28 | #define BAR_MASK (BIT(TH_MMIO_CONFIG) | BIT(TH_MMIO_SW)) |
| 29 | 29 | |
| 30 | +#define PCI_REG_NPKDSC 0x80 | |
| 31 | +#define NPKDSC_TSACT BIT(5) | |
| 32 | + | |
| 33 | +static int intel_th_pci_activate(struct intel_th *th) | |
| 34 | +{ | |
| 35 | + struct pci_dev *pdev = to_pci_dev(th->dev); | |
| 36 | + u32 npkdsc; | |
| 37 | + int err; | |
| 38 | + | |
| 39 | + if (!INTEL_TH_CAP(th, tscu_enable)) | |
| 40 | + return 0; | |
| 41 | + | |
| 42 | + err = pci_read_config_dword(pdev, PCI_REG_NPKDSC, &npkdsc); | |
| 43 | + if (!err) { | |
| 44 | + npkdsc |= NPKDSC_TSACT; | |
| 45 | + err = pci_write_config_dword(pdev, PCI_REG_NPKDSC, npkdsc); | |
| 46 | + } | |
| 47 | + | |
| 48 | + if (err) | |
| 49 | + dev_err(&pdev->dev, "failed to read NPKDSC register\n"); | |
| 50 | + | |
| 51 | + return err; | |
| 52 | +} | |
| 53 | + | |
| 54 | +static void intel_th_pci_deactivate(struct intel_th *th) | |
| 55 | +{ | |
| 56 | + struct pci_dev *pdev = to_pci_dev(th->dev); | |
| 57 | + u32 npkdsc; | |
| 58 | + int err; | |
| 59 | + | |
| 60 | + if (!INTEL_TH_CAP(th, tscu_enable)) | |
| 61 | + return; | |
| 62 | + | |
| 63 | + err = pci_read_config_dword(pdev, PCI_REG_NPKDSC, &npkdsc); | |
| 64 | + if (!err) { | |
| 65 | + npkdsc |= NPKDSC_TSACT; | |
| 66 | + err = pci_write_config_dword(pdev, PCI_REG_NPKDSC, npkdsc); | |
| 67 | + } | |
| 68 | + | |
| 69 | + if (err) | |
| 70 | + dev_err(&pdev->dev, "failed to read NPKDSC register\n"); | |
| 71 | +} | |
| 72 | + | |
| 30 | 73 | static int intel_th_pci_probe(struct pci_dev *pdev, |
| 31 | 74 | const struct pci_device_id *id) |
| 32 | 75 | { |
| 76 | + struct intel_th_drvdata *drvdata = (void *)id->driver_data; | |
| 33 | 77 | struct intel_th *th; |
| 34 | 78 | int err; |
| 35 | 79 | |
| @@ -41,11 +85,16 @@ static int intel_th_pci_probe(struct pci_dev *pdev, · 5 unmodified lines | ||
| 41 | 85 | if (err) |
| 42 | 86 | return err; |
| 43 | 87 | |
| 44 | − th = intel_th_alloc(&pdev->dev, pdev->resource, | |
| 88 | + th = intel_th_alloc(&pdev->dev, drvdata, pdev->resource, | |
| 45 | 89 | DEVICE_COUNT_RESOURCE, pdev->irq); |
| 46 | 90 | if (IS_ERR(th)) |
| 47 | 91 | return PTR_ERR(th); |
| 48 | 92 | |
| 93 | + th->activate = intel_th_pci_activate; | |
| 94 | + th->deactivate = intel_th_pci_deactivate; | |
| 95 | + | |
| 96 | + pci_set_master(pdev); | |
| 97 | + | |
| 49 | 98 | return 0; |
| 50 | 99 | } |
| 51 | 100 | |
| @@ -56,6 +105,10 @@ static void intel_th_pci_remove(struct pci_dev *pdev) · 4 unmodified lines | ||
| 56 | 105 | intel_th_free(th); |
| 57 | 106 | } |
| 58 | 107 | |
| 108 | +static const struct intel_th_drvdata intel_th_2x = { | |
| 109 | + .tscu_enable = 1, | |
| 110 | +}; | |
| 111 | + | |
| 59 | 112 | static const struct pci_device_id intel_th_pci_id_table[] = { |
| 60 | 113 | { |
| 61 | 114 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9d26), |
| @@ -93,7 +146,17 @@ static const struct pci_device_id intel_th_pci_id_table[] = { · 31 unmodified lines | ||
| 93 | 146 | { |
| 94 | 147 | /* Gemini Lake */ |
| 95 | 148 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x318e), |
| 96 | − .driver_data = (kernel_ulong_t)0, | |
| 149 | + .driver_data = (kernel_ulong_t)&intel_th_2x, | |
| 150 | + }, | |
| 151 | + { | |
| 152 | + /* Cannon Lake H */ | |
| 153 | + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa326), | |
| 154 | + .driver_data = (kernel_ulong_t)&intel_th_2x, | |
| 155 | + }, | |
| 156 | + { | |
| 157 | + /* Cannon Lake LP */ | |
| 158 | + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9da6), | |
| 159 | + .driver_data = (kernel_ulong_t)&intel_th_2x, | |
| 97 | 160 | }, |
| 98 | 161 | { 0 }, |
| 99 | 162 | }; |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | /* |
| 2 | 2 | * Intel(R) Trace Hub PTI output driver |
| 3 | 3 | * |
| 4 | − * Copyright (C) 2014-2015 Intel Corporation. | |
| 4 | + * Copyright (C) 2014-2016 Intel Corporation. | |
| 5 | 5 | * |
| 6 | 6 | * This program is free software; you can redistribute it and/or modify it |
| 7 | 7 | * under the terms and conditions of the GNU General Public License, |
| @@ -34,6 +34,8 @@ struct pti_device { · 26 unmodified lines | ||
| 34 | 34 | unsigned int freeclk; |
| 35 | 35 | unsigned int clkdiv; |
| 36 | 36 | unsigned int patgen; |
| 37 | + unsigned int lpp_dest_mask; | |
| 38 | + unsigned int lpp_dest; | |
| 37 | 39 | }; |
| 38 | 40 | |
| 39 | 41 | /* map PTI widths to MODE settings of PTI_CTL register */ |
| @@ -163,6 +165,7 @@ static int intel_th_pti_activate(struct intel_th_device *thdev) · 123 unmodified lines | ||
| 163 | 165 | ctl |= PTI_FCEN; |
| 164 | 166 | ctl |= pti->mode << __ffs(PTI_MODE); |
| 165 | 167 | ctl |= pti->clkdiv << __ffs(PTI_CLKDIV); |
| 168 | + ctl |= pti->lpp_dest << __ffs(LPP_DEST); | |
| 166 | 169 | |
| 167 | 170 | iowrite32(ctl, pti->base + REG_PTI_CTL); |
| 168 | 171 | |
| @@ -192,6 +195,15 @@ static void read_hw_config(struct pti_device *pti) · 23 unmodified lines | ||
| 192 | 195 | pti->mode = pti_width_mode(4); |
| 193 | 196 | if (!pti->clkdiv) |
| 194 | 197 | pti->clkdiv = 1; |
| 198 | + | |
| 199 | + if (pti->thdev->output.type == GTH_LPP) { | |
| 200 | + if (ctl & LPP_PTIPRESENT) | |
| 201 | + pti->lpp_dest_mask |= LPP_DEST_PTI; | |
| 202 | + if (ctl & LPP_BSSBPRESENT) | |
| 203 | + pti->lpp_dest_mask |= LPP_DEST_EXI; | |
| 204 | + if (ctl & LPP_DEST) | |
| 205 | + pti->lpp_dest = 1; | |
| 206 | + } | |
| 195 | 207 | } |
| 196 | 208 | |
| 197 | 209 | static int intel_th_pti_probe(struct intel_th_device *thdev) |
| @@ -239,10 +251,103 @@ static struct intel_th_driver intel_th_pti_driver = { · 41 unmodified lines | ||
| 239 | 251 | }, |
| 240 | 252 | }; |
| 241 | 253 | |
| 242 | −module_driver(intel_th_pti_driver, | |
| 243 | − intel_th_driver_register, | |
| 244 | − intel_th_driver_unregister); | |
| 254 | +static const char * const lpp_dest_str[] = { "pti", "exi" }; | |
| 255 | + | |
| 256 | +static ssize_t lpp_dest_show(struct device *dev, struct device_attribute *attr, | |
| 257 | + char *buf) | |
| 258 | +{ | |
| 259 | + struct pti_device *pti = dev_get_drvdata(dev); | |
| 260 | + ssize_t ret = 0; | |
| 261 | + int i; | |
| 262 | + | |
| 263 | + for (i = ARRAY_SIZE(lpp_dest_str) - 1; i >= 0; i--) { | |
| 264 | + const char *fmt = pti->lpp_dest == i ? "[%s] " : "%s "; | |
| 265 | + | |
| 266 | + if (!(pti->lpp_dest_mask & BIT(i))) | |
| 267 | + continue; | |
| 268 | + | |
| 269 | + ret += scnprintf(buf + ret, PAGE_SIZE - ret, | |
| 270 | + fmt, lpp_dest_str[i]); | |
| 271 | + } | |
| 272 | + | |
| 273 | + if (ret) | |
| 274 | + buf[ret - 1] = '\n'; | |
| 275 | + | |
| 276 | + return ret; | |
| 277 | +} | |
| 278 | + | |
| 279 | +static ssize_t lpp_dest_store(struct device *dev, struct device_attribute *attr, | |
| 280 | + const char *buf, size_t size) | |
| 281 | +{ | |
| 282 | + struct pti_device *pti = dev_get_drvdata(dev); | |
| 283 | + ssize_t ret = -EINVAL; | |
| 284 | + int i; | |
| 285 | + | |
| 286 | + for (i = 0; i < ARRAY_SIZE(lpp_dest_str); i++) | |
| 287 | + if (sysfs_streq(buf, lpp_dest_str[i])) | |
| 288 | + break; | |
| 289 | + | |
| 290 | + if (i < ARRAY_SIZE(lpp_dest_str) && pti->lpp_dest_mask & BIT(i)) { | |
| 291 | + pti->lpp_dest = i; | |
| 292 | + ret = size; | |
| 293 | + } | |
| 294 | + | |
| 295 | + return ret; | |
| 296 | +} | |
| 297 | + | |
| 298 | +static DEVICE_ATTR_RW(lpp_dest); | |
| 299 | + | |
| 300 | +static struct attribute *lpp_output_attrs[] = { | |
| 301 | + &dev_attr_mode.attr, | |
| 302 | + &dev_attr_freerunning_clock.attr, | |
| 303 | + &dev_attr_clock_divider.attr, | |
| 304 | + &dev_attr_lpp_dest.attr, | |
| 305 | + NULL, | |
| 306 | +}; | |
| 307 | + | |
| 308 | +static struct attribute_group lpp_output_group = { | |
| 309 | + .attrs = lpp_output_attrs, | |
| 310 | +}; | |
| 311 | + | |
| 312 | +static struct intel_th_driver intel_th_lpp_driver = { | |
| 313 | + .probe = intel_th_pti_probe, | |
| 314 | + .remove = intel_th_pti_remove, | |
| 315 | + .activate = intel_th_pti_activate, | |
| 316 | + .deactivate = intel_th_pti_deactivate, | |
| 317 | + .attr_group = &lpp_output_group, | |
| 318 | + .driver = { | |
| 319 | + .name = "lpp", | |
| 320 | + .owner = THIS_MODULE, | |
| 321 | + }, | |
| 322 | +}; | |
| 323 | + | |
| 324 | +static int __init intel_th_pti_lpp_init(void) | |
| 325 | +{ | |
| 326 | + int err; | |
| 327 | + | |
| 328 | + err = intel_th_driver_register(&intel_th_pti_driver); | |
| 329 | + if (err) | |
| 330 | + return err; | |
| 331 | + | |
| 332 | + err = intel_th_driver_register(&intel_th_lpp_driver); | |
| 333 | + if (err) { | |
| 334 | + intel_th_driver_unregister(&intel_th_pti_driver); | |
| 335 | + return err; | |
| 336 | + } | |
| 337 | + | |
| 338 | + return 0; | |
| 339 | +} | |
| 340 | + | |
| 341 | +module_init(intel_th_pti_lpp_init); | |
| 342 | + | |
| 343 | +static void __exit intel_th_pti_lpp_exit(void) | |
| 344 | +{ | |
| 345 | + intel_th_driver_unregister(&intel_th_pti_driver); | |
| 346 | + intel_th_driver_unregister(&intel_th_lpp_driver); | |
| 347 | +} | |
| 348 | + | |
| 349 | +module_exit(intel_th_pti_lpp_exit); | |
| 245 | 350 | |
| 246 | 351 | MODULE_LICENSE("GPL v2"); |
| 247 | −MODULE_DESCRIPTION("Intel(R) Trace Hub PTI output driver"); | |
| 352 | +MODULE_DESCRIPTION("Intel(R) Trace Hub PTI/LPP output driver"); | |
| 248 | 353 | MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>"); |
| @@ -23,7 +23,15 @@ enum { · 22 unmodified lines | ||
| 23 | 23 | #define PTI_EN BIT(0) |
| 24 | 24 | #define PTI_FCEN BIT(1) |
| 25 | 25 | #define PTI_MODE 0xf0 |
| 26 | +#define LPP_PTIPRESENT BIT(8) | |
| 27 | +#define LPP_BSSBPRESENT BIT(9) | |
| 26 | 28 | #define PTI_CLKDIV 0x000f0000 |
| 27 | 29 | #define PTI_PATGENMODE 0x00f00000 |
| 30 | +#define LPP_DEST BIT(25) | |
| 31 | +#define LPP_BSSBACT BIT(30) | |
| 32 | +#define LPP_LPPBUSY BIT(31) | |
| 33 | + | |
| 34 | +#define LPP_DEST_PTI BIT(0) | |
| 35 | +#define LPP_DEST_EXI BIT(1) | |
| 28 | 36 | |
| 29 | 37 | #endif /* __INTEL_TH_STH_H__ */ |
| @@ -566,7 +566,7 @@ static int stm_char_policy_set_ioctl(struct stm_file *stmf, void __user *arg) · 565 unmodified lines | ||
| 566 | 566 | if (copy_from_user(&size, arg, sizeof(size))) |
| 567 | 567 | return -EFAULT; |
| 568 | 568 | |
| 569 | − if (size >= PATH_MAX + sizeof(*id)) | |
| 569 | + if (size < sizeof(*id) || size >= PATH_MAX + sizeof(*id)) | |
| 570 | 570 | return -EINVAL; |
| 571 | 571 | |
| 572 | 572 | /* |
| @@ -114,6 +114,12 @@ static struct resource sc24_fpga_resource = { · 113 unmodified lines | ||
| 114 | 114 | .flags = IORESOURCE_MEM, |
| 115 | 115 | }; |
| 116 | 116 | |
| 117 | +static struct resource sc31_fpga_resource = { | |
| 118 | + .start = 0xf000e000, | |
| 119 | + .end = 0xf000e000 + CHAM_HEADER_SIZE, | |
| 120 | + .flags = IORESOURCE_MEM, | |
| 121 | +}; | |
| 122 | + | |
| 117 | 123 | static struct platform_driver mcb_lpc_driver = { |
| 118 | 124 | .driver = { |
| 119 | 125 | .name = "mcb-lpc", |
| @@ -132,6 +138,15 @@ static const struct dmi_system_id mcb_lpc_dmi_table[] = { · 12 unmodified lines | ||
| 132 | 138 | .driver_data = (void *)&sc24_fpga_resource, |
| 133 | 139 | .callback = mcb_lpc_create_platform_device, |
| 134 | 140 | }, |
| 141 | + { | |
| 142 | + .ident = "SC31", | |
| 143 | + .matches = { | |
| 144 | + DMI_MATCH(DMI_SYS_VENDOR, "MEN"), | |
| 145 | + DMI_MATCH(DMI_PRODUCT_VERSION, "14SC31"), | |
| 146 | + }, | |
| 147 | + .driver_data = (void *)&sc31_fpga_resource, | |
| 148 | + .callback = mcb_lpc_create_platform_device, | |
| 149 | + }, | |
| 135 | 150 | {} |
| 136 | 151 | }; |
| 137 | 152 | MODULE_DEVICE_TABLE(dmi, mcb_lpc_dmi_table); |
| @@ -182,7 +182,7 @@ int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase, · 181 unmodified lines | ||
| 182 | 182 | int num_cells = 0; |
| 183 | 183 | uint32_t dtype; |
| 184 | 184 | int bar_count; |
| 185 | − int ret = 0; | |
| 185 | + int ret; | |
| 186 | 186 | u32 hsize; |
| 187 | 187 | |
| 188 | 188 | hsize = sizeof(struct chameleon_fpga_header); |
| @@ -210,8 +210,10 @@ int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase, · 21 unmodified lines | ||
| 210 | 210 | header->filename); |
| 211 | 211 | |
| 212 | 212 | bar_count = chameleon_get_bar(&p, mapbase, &cb); |
| 213 | − if (bar_count < 0) | |
| 213 | + if (bar_count < 0) { | |
| 214 | + ret = bar_count; | |
| 214 | 215 | goto free_header; |
| 216 | + } | |
| 215 | 217 | |
| 216 | 218 | for_each_chameleon_cell(dtype, p) { |
| 217 | 219 | switch (dtype) { |
| @@ -60,6 +60,7 @@ lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o · 59 unmodified lines | ||
| 60 | 60 | lkdtm-$(CONFIG_LKDTM) += lkdtm_bugs.o |
| 61 | 61 | lkdtm-$(CONFIG_LKDTM) += lkdtm_heap.o |
| 62 | 62 | lkdtm-$(CONFIG_LKDTM) += lkdtm_perms.o |
| 63 | +lkdtm-$(CONFIG_LKDTM) += lkdtm_refcount.o | |
| 63 | 64 | lkdtm-$(CONFIG_LKDTM) += lkdtm_rodata_objcopy.o |
| 64 | 65 | lkdtm-$(CONFIG_LKDTM) += lkdtm_usercopy.o |
| 65 | 66 | |
| @@ -197,7 +197,7 @@ static struct attribute *mid_att_als[] = { · 196 unmodified lines | ||
| 197 | 197 | NULL |
| 198 | 198 | }; |
| 199 | 199 | |
| 200 | −static struct attribute_group m_als_gr = { | |
| 200 | +static const struct attribute_group m_als_gr = { | |
| 201 | 201 | .name = "apds9802als", |
| 202 | 202 | .attrs = mid_att_als |
| 203 | 203 | }; |
| @@ -298,7 +298,7 @@ static UNIVERSAL_DEV_PM_OPS(apds9802als_pm_ops, apds9802als_suspend, · 94 unmodified lines | ||
| 298 | 298 | #define APDS9802ALS_PM_OPS NULL |
| 299 | 299 | #endif /* CONFIG_PM */ |
| 300 | 300 | |
| 301 | −static struct i2c_device_id apds9802als_id[] = { | |
| 301 | +static const struct i2c_device_id apds9802als_id[] = { | |
| 302 | 302 | { DRIVER_NAME, 0 }, |
| 303 | 303 | { } |
| 304 | 304 | }; |
| @@ -1051,7 +1051,7 @@ static struct attribute *sysfs_attrs_ctrl[] = { · 1050 unmodified lines | ||
| 1051 | 1051 | NULL |
| 1052 | 1052 | }; |
| 1053 | 1053 | |
| 1054 | −static struct attribute_group apds990x_attribute_group[] = { | |
| 1054 | +static const struct attribute_group apds990x_attribute_group[] = { | |
| 1055 | 1055 | {.attrs = sysfs_attrs_ctrl }, |
| 1056 | 1056 | }; |
| 1057 | 1057 | |
| @@ -20,6 +20,7 @@ · 19 unmodified lines | ||
| 20 | 20 | #include <linux/mfd/syscon.h> |
| 21 | 21 | #include <linux/module.h> |
| 22 | 22 | #include <linux/of.h> |
| 23 | +#include <linux/of_device.h> | |
| 23 | 24 | #include <linux/platform_device.h> |
| 24 | 25 | #include <linux/regmap.h> |
| 25 | 26 | |
| @@ -51,6 +52,13 @@ · 25 unmodified lines | ||
| 51 | 52 | #define HICRB_ENSNP0D BIT(14) |
| 52 | 53 | #define HICRB_ENSNP1D BIT(15) |
| 53 | 54 | |
| 55 | +struct aspeed_lpc_snoop_model_data { | |
| 56 | + /* The ast2400 has bits 14 and 15 as reserved, whereas the ast2500 | |
| 57 | + * can use them. | |
| 58 | + */ | |
| 59 | + unsigned int has_hicrb_ensnp; | |
| 60 | +}; | |
| 61 | + | |
| 54 | 62 | struct aspeed_lpc_snoop { |
| 55 | 63 | struct regmap *regmap; |
| 56 | 64 | int irq; |
| @@ -123,10 +131,13 @@ static int aspeed_lpc_snoop_config_irq(struct aspeed_lpc_snoop *lpc_snoop, · 66 unmodified lines | ||
| 123 | 131 | } |
| 124 | 132 | |
| 125 | 133 | static int aspeed_lpc_enable_snoop(struct aspeed_lpc_snoop *lpc_snoop, |
| 126 | − int channel, u16 lpc_port) | |
| 134 | + struct device *dev, | |
| 135 | + int channel, u16 lpc_port) | |
| 127 | 136 | { |
| 128 | 137 | int rc = 0; |
| 129 | 138 | u32 hicr5_en, snpwadr_mask, snpwadr_shift, hicrb_en; |
| 139 | + const struct aspeed_lpc_snoop_model_data *model_data = | |
| 140 | + of_device_get_match_data(dev); | |
| 130 | 141 | |
| 131 | 142 | /* Create FIFO datastructure */ |
| 132 | 143 | rc = kfifo_alloc(&lpc_snoop->snoop_fifo[channel], |
| @@ -155,7 +166,9 @@ static int aspeed_lpc_enable_snoop(struct aspeed_lpc_snoop *lpc_snoop, · 22 unmodified lines | ||
| 155 | 166 | regmap_update_bits(lpc_snoop->regmap, HICR5, hicr5_en, hicr5_en); |
| 156 | 167 | regmap_update_bits(lpc_snoop->regmap, SNPWADR, snpwadr_mask, |
| 157 | 168 | lpc_port << snpwadr_shift); |
| 158 | − regmap_update_bits(lpc_snoop->regmap, HICRB, hicrb_en, hicrb_en); | |
| 169 | + if (model_data->has_hicrb_ensnp) | |
| 170 | + regmap_update_bits(lpc_snoop->regmap, HICRB, | |
| 171 | + hicrb_en, hicrb_en); | |
| 159 | 172 | |
| 160 | 173 | return rc; |
| 161 | 174 | } |
| @@ -213,14 +226,14 @@ static int aspeed_lpc_snoop_probe(struct platform_device *pdev) · 51 unmodified lines | ||
| 213 | 226 | if (rc) |
| 214 | 227 | return rc; |
| 215 | 228 | |
| 216 | − rc = aspeed_lpc_enable_snoop(lpc_snoop, 0, port); | |
| 229 | + rc = aspeed_lpc_enable_snoop(lpc_snoop, dev, 0, port); | |
| 217 | 230 | if (rc) |
| 218 | 231 | return rc; |
| 219 | 232 | |
| 220 | 233 | /* Configuration of 2nd snoop channel port is optional */ |
| 221 | 234 | if (of_property_read_u32_index(dev->of_node, "snoop-ports", |
| 222 | 235 | 1, &port) == 0) { |
| 223 | − rc = aspeed_lpc_enable_snoop(lpc_snoop, 1, port); | |
| 236 | + rc = aspeed_lpc_enable_snoop(lpc_snoop, dev, 1, port); | |
| 224 | 237 | if (rc) |
| 225 | 238 | aspeed_lpc_disable_snoop(lpc_snoop, 0); |
| 226 | 239 | } |
| @@ -239,8 +252,19 @@ static int aspeed_lpc_snoop_remove(struct platform_device *pdev) · 12 unmodified lines | ||
| 239 | 252 | return 0; |
| 240 | 253 | } |
| 241 | 254 | |
| 255 | +static const struct aspeed_lpc_snoop_model_data ast2400_model_data = { | |
| 256 | + .has_hicrb_ensnp = 0, | |
| 257 | +}; | |
| 258 | + | |
| 259 | +static const struct aspeed_lpc_snoop_model_data ast2500_model_data = { | |
| 260 | + .has_hicrb_ensnp = 1, | |
| 261 | +}; | |
| 262 | + | |
| 242 | 263 | static const struct of_device_id aspeed_lpc_snoop_match[] = { |
| 243 | − { .compatible = "aspeed,ast2500-lpc-snoop" }, | |
| 264 | + { .compatible = "aspeed,ast2400-lpc-snoop", | |
| 265 | + .data = &ast2400_model_data }, | |
| 266 | + { .compatible = "aspeed,ast2500-lpc-snoop", | |
| 267 | + .data = &ast2500_model_data }, | |
| 244 | 268 | { }, |
| 245 | 269 | }; |
| 246 | 270 | |
| @@ -1175,7 +1175,7 @@ static struct attribute *sysfs_attrs[] = { · 1174 unmodified lines | ||
| 1175 | 1175 | NULL |
| 1176 | 1176 | }; |
| 1177 | 1177 | |
| 1178 | −static struct attribute_group bh1770_attribute_group = { | |
| 1178 | +static const struct attribute_group bh1770_attribute_group = { | |
| 1179 | 1179 | .attrs = sysfs_attrs |
| 1180 | 1180 | }; |
| 1181 | 1181 | |
| @@ -173,7 +173,7 @@ static ssize_t ds1682_eeprom_write(struct file *filp, struct kobject *kobj, · 172 unmodified lines | ||
| 173 | 173 | return count; |
| 174 | 174 | } |
| 175 | 175 | |
| 176 | −static struct bin_attribute ds1682_eeprom_attr = { | |
| 176 | +static const struct bin_attribute ds1682_eeprom_attr = { | |
| 177 | 177 | .attr = { |
| 178 | 178 | .name = "eeprom", |
| 179 | 179 | .mode = S_IRUGO | S_IWUSR, |
| @@ -114,7 +114,7 @@ static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, · 113 unmodified lines | ||
| 114 | 114 | return count; |
| 115 | 115 | } |
| 116 | 116 | |
| 117 | −static struct bin_attribute eeprom_attr = { | |
| 117 | +static const struct bin_attribute eeprom_attr = { | |
| 118 | 118 | .attr = { |
| 119 | 119 | .name = "eeprom", |
| 120 | 120 | .mode = S_IRUGO, |
| @@ -377,8 +377,6 @@ static int eeprom_93xx46_probe_dt(struct spi_device *spi) · 376 unmodified lines | ||
| 377 | 377 | struct device_node *np = spi->dev.of_node; |
| 378 | 378 | struct eeprom_93xx46_platform_data *pd; |
| 379 | 379 | u32 tmp; |
| 380 | − int gpio; | |
| 381 | − enum of_gpio_flags of_flags; | |
| 382 | 380 | int ret; |
| 383 | 381 | |
| 384 | 382 | pd = devm_kzalloc(&spi->dev, sizeof(*pd), GFP_KERNEL); |
| @@ -403,22 +401,14 @@ static int eeprom_93xx46_probe_dt(struct spi_device *spi) · 18 unmodified lines | ||
| 403 | 401 | if (of_property_read_bool(np, "read-only")) |
| 404 | 402 | pd->flags |= EE_READONLY; |
| 405 | 403 | |
| 406 | − gpio = of_get_named_gpio_flags(np, "select-gpios", 0, &of_flags); | |
| 407 | − if (gpio_is_valid(gpio)) { | |
| 408 | − unsigned long flags = | |
| 409 | − of_flags == OF_GPIO_ACTIVE_LOW ? GPIOF_ACTIVE_LOW : 0; | |
| 404 | + pd->select = devm_gpiod_get_optional(&spi->dev, "select", | |
| 405 | + GPIOD_OUT_LOW); | |
| 406 | + if (IS_ERR(pd->select)) | |
| 407 | + return PTR_ERR(pd->select); | |
| 410 | 408 | |
| 411 | − ret = devm_gpio_request_one(&spi->dev, gpio, flags, | |
| 412 | − "eeprom_93xx46_select"); | |
| 413 | − if (ret) | |
| 414 | − return ret; | |
| 415 | − | |
| 416 | − pd->select = gpio_to_desc(gpio); | |
| 417 | − pd->prepare = select_assert; | |
| 418 | − pd->finish = select_deassert; | |
| 419 | − | |
| 420 | − gpiod_direction_output(pd->select, 0); | |
| 421 | − } | |
| 409 | + pd->prepare = select_assert; | |
| 410 | + pd->finish = select_deassert; | |
| 411 | + gpiod_direction_output(pd->select, 0); | |
| 422 | 412 | |
| 423 | 413 | if (of_id->data) { |
| 424 | 414 | const struct eeprom_93xx46_devtype_data *data = of_id->data; |
| @@ -77,7 +77,7 @@ · 76 unmodified lines | ||
| 77 | 77 | #include <linux/sysfs.h> |
| 78 | 78 | #include <linux/debugfs.h> |
| 79 | 79 | #include <linux/mod_devicetable.h> |
| 80 | −#include <linux/of.h> | |
| 80 | +#include <linux/property.h> | |
| 81 | 81 | #include <linux/i2c.h> |
| 82 | 82 | #include <linux/pci_ids.h> |
| 83 | 83 | #include <linux/delay.h> |
| @@ -1089,101 +1089,85 @@ static void idt_set_defval(struct idt_89hpesx_dev *pdev) · 1005 unmodified lines | ||
| 1089 | 1089 | pdev->eeaddr = 0; |
| 1090 | 1090 | } |
| 1091 | 1091 | |
| 1092 | −#ifdef CONFIG_OF | |
| 1093 | 1092 | static const struct i2c_device_id ee_ids[]; |
| 1093 | + | |
| 1094 | 1094 | /* |
| 1095 | 1095 | * idt_ee_match_id() - check whether the node belongs to compatible EEPROMs |
| 1096 | 1096 | */ |
| 1097 | −static const struct i2c_device_id *idt_ee_match_id(struct device_node *node) | |
| 1097 | +static const struct i2c_device_id *idt_ee_match_id(struct fwnode_handle *fwnode) | |
| 1098 | 1098 | { |
| 1099 | 1099 | const struct i2c_device_id *id = ee_ids; |
| 1100 | + const char *compatible, *p; | |
| 1100 | 1101 | char devname[I2C_NAME_SIZE]; |
| 1102 | + int ret; | |
| 1101 | 1103 | |
| 1102 | − /* Retrieve the device name without manufacturer name */ | |
| 1103 | − if (of_modalias_node(node, devname, sizeof(devname))) | |
| 1104 | + ret = fwnode_property_read_string(fwnode, "compatible", &compatible); | |
| 1105 | + if (ret) | |
| 1104 | 1106 | return NULL; |
| 1105 | 1107 | |
| 1108 | + p = strchr(compatible, ','); | |
| 1109 | + strlcpy(devname, p ? p + 1 : compatible, sizeof(devname)); | |
| 1106 | 1110 | /* Search through the device name */ |
| 1107 | − while (id->name[0]) { | |
| 1108 | − if (strcmp(devname, id->name) == 0) | |
| 1109 | − return id; | |
| 1110 | − id++; | |
| 1111 | − } | |
| 1112 | − return NULL; | |
| 1111 | + while (id->name[0]) { | |
| 1112 | + if (strcmp(devname, id->name) == 0) | |
| 1113 | + return id; | |
| 1114 | + id++; | |
| 1115 | + } | |
| 1116 | + return NULL; | |
| 1113 | 1117 | } |
| 1114 | 1118 | |
| 1115 | 1119 | /* |
| 1116 | − * idt_get_ofdata() - get IDT i2c-device parameters from device tree | |
| 1120 | + * idt_get_fw_data() - get IDT i2c-device parameters from device tree | |
| 1117 | 1121 | * @pdev: Pointer to the driver data |
| 1118 | 1122 | */ |
| 1119 | −static void idt_get_ofdata(struct idt_89hpesx_dev *pdev) | |
| 1123 | +static void idt_get_fw_data(struct idt_89hpesx_dev *pdev) | |
| 1120 | 1124 | { |
| 1121 | − const struct device_node *node = pdev->client->dev.of_node; | |
| 1122 | 1125 | struct device *dev = &pdev->client->dev; |
| 1126 | + struct fwnode_handle *fwnode; | |
| 1127 | + const struct i2c_device_id *ee_id = NULL; | |
| 1128 | + u32 eeprom_addr; | |
| 1129 | + int ret; | |
| 1123 | 1130 | |
| 1124 | − /* Read dts node parameters */ | |
| 1125 | − if (node) { | |
| 1126 | − const struct i2c_device_id *ee_id = NULL; | |
| 1127 | − struct device_node *child; | |
| 1128 | − const __be32 *addr_be; | |
| 1129 | − int len; | |
| 1130 | − | |
| 1131 | − /* Walk through all child nodes looking for compatible one */ | |
| 1132 | − for_each_available_child_of_node(node, child) { | |
| 1133 | − ee_id = idt_ee_match_id(child); | |
| 1134 | − if (IS_ERR_OR_NULL(ee_id)) { | |
| 1135 | − dev_warn(dev, "Skip unsupported child node %s", | |
| 1136 | − child->full_name); | |
| 1137 | − continue; | |
| 1138 | − } else | |
| 1139 | − break; | |
| 1140 | − } | |
| 1141 | − | |
| 1142 | − /* If there is no child EEPROM device, then set zero size */ | |
| 1143 | − if (!ee_id) { | |
| 1144 | − idt_set_defval(pdev); | |
| 1145 | − return; | |
| 1146 | − } | |
| 1131 | + device_for_each_child_node(dev, fwnode) { | |
| 1132 | + ee_id = idt_ee_match_id(fwnode); | |
| 1133 | + if (IS_ERR_OR_NULL(ee_id)) { | |
| 1134 | + dev_warn(dev, "Skip unsupported EEPROM device"); | |
| 1135 | + continue; | |
| 1136 | + } else | |
| 1137 | + break; | |
| 1138 | + } | |
| 1147 | 1139 | |
| 1148 | − /* Retrieve EEPROM size */ | |
| 1149 | − pdev->eesize = (u32)ee_id->driver_data; | |
| 1150 | − | |
| 1151 | − /* Get custom EEPROM address from 'reg' attribute */ | |
| 1152 | − addr_be = of_get_property(child, "reg", &len); | |
| 1153 | − if (!addr_be || (len < sizeof(*addr_be))) { | |
| 1154 | − dev_warn(dev, "No reg on %s, use default address %d", | |
| 1155 | − child->full_name, EEPROM_DEF_ADDR); | |
| 1156 | − pdev->inieecmd = 0; | |
| 1157 | − pdev->eeaddr = EEPROM_DEF_ADDR << 1; | |
| 1158 | − } else { | |
| 1159 | − pdev->inieecmd = EEPROM_USA; | |
| 1160 | − pdev->eeaddr = be32_to_cpup(addr_be) << 1; | |
| 1161 | − } | |
| 1140 | + /* If there is no fwnode EEPROM device, then set zero size */ | |
| 1141 | + if (!ee_id) { | |
| 1142 | + dev_warn(dev, "No fwnode, EEPROM access disabled"); | |
| 1143 | + idt_set_defval(pdev); | |
| 1144 | + return; | |
| 1145 | + } | |
| 1162 | 1146 | |
| 1163 | − /* Check EEPROM 'read-only' flag */ | |
| 1164 | − if (of_get_property(child, "read-only", NULL)) | |
| 1165 | − pdev->eero = true; | |
| 1166 | − else /* if (!of_get_property(node, "read-only", NULL)) */ | |
| 1167 | − pdev->eero = false; | |
| 1147 | + /* Retrieve EEPROM size */ | |
| 1148 | + pdev->eesize = (u32)ee_id->driver_data; | |
| 1168 | 1149 | |
| 1169 | − dev_dbg(dev, "EEPROM of %u bytes found by %hhu", | |
| 1170 | − pdev->eesize, pdev->eeaddr); | |
| 1150 | + /* Get custom EEPROM address from 'reg' attribute */ | |
| 1151 | + ret = fwnode_property_read_u32(fwnode, "reg", &eeprom_addr); | |
| 1152 | + if (ret || (eeprom_addr == 0)) { | |
| 1153 | + dev_warn(dev, "No EEPROM reg found, use default address 0x%x", | |
| 1154 | + EEPROM_DEF_ADDR); | |
| 1155 | + pdev->inieecmd = 0; | |
| 1156 | + pdev->eeaddr = EEPROM_DEF_ADDR << 1; | |
| 1171 | 1157 | } else { |
| 1172 | − dev_warn(dev, "No dts node, EEPROM access disabled"); | |
| 1173 | − idt_set_defval(pdev); | |
| 1158 | + pdev->inieecmd = EEPROM_USA; | |
| 1159 | + pdev->eeaddr = eeprom_addr << 1; | |
| 1174 | 1160 | } |
| 1175 | −} | |
| 1176 | −#else | |
| 1177 | −static void idt_get_ofdata(struct idt_89hpesx_dev *pdev) | |
| 1178 | −{ | |
| 1179 | − struct device *dev = &pdev->client->dev; | |
| 1180 | 1161 | |
| 1181 | − dev_warn(dev, "OF table is unsupported, EEPROM access disabled"); | |
| 1162 | + /* Check EEPROM 'read-only' flag */ | |
| 1163 | + if (fwnode_property_read_bool(fwnode, "read-only")) | |
| 1164 | + pdev->eero = true; | |
| 1165 | + else /* if (!fwnode_property_read_bool(node, "read-only")) */ | |
| 1166 | + pdev->eero = false; | |
| 1182 | 1167 | |
| 1183 | − /* Nothing we can do, just set the default values */ | |
| 1184 | − idt_set_defval(pdev); | |
| 1168 | + dev_info(dev, "EEPROM of %d bytes found by 0x%x", | |
| 1169 | + pdev->eesize, pdev->eeaddr); | |
| 1185 | 1170 | } |
| 1186 | −#endif /* CONFIG_OF */ | |
| 1187 | 1171 | |
| 1188 | 1172 | /* |
| 1189 | 1173 | * idt_create_pdev() - create and init data structure of the driver |
| @@ -1203,8 +1187,8 @@ static struct idt_89hpesx_dev *idt_create_pdev(struct i2c_client *client) · 13 unmodified lines | ||
| 1203 | 1187 | pdev->client = client; |
| 1204 | 1188 | i2c_set_clientdata(client, pdev); |
| 1205 | 1189 | |
| 1206 | − /* Read OF nodes information */ | |
| 1207 | − idt_get_ofdata(pdev); | |
| 1190 | + /* Read firmware nodes information */ | |
| 1191 | + idt_get_fw_data(pdev); | |
| 1208 | 1192 | |
| 1209 | 1193 | /* Initialize basic CSR CMD field - use full DWORD-sized r/w ops */ |
| 1210 | 1194 | pdev->inicsrcmd = CSR_DWE; |
| @@ -124,7 +124,7 @@ static ssize_t max6875_read(struct file *filp, struct kobject *kobj, · 123 unmodified lines | ||
| 124 | 124 | return count; |
| 125 | 125 | } |
| 126 | 126 | |
| 127 | −static struct bin_attribute user_eeprom_attr = { | |
| 127 | +static const struct bin_attribute user_eeprom_attr = { | |
| 128 | 128 | .attr = { |
| 129 | 129 | .name = "eeprom", |
| 130 | 130 | .mode = S_IRUGO, |
| @@ -132,7 +132,7 @@ static int hmc6352_remove(struct i2c_client *client) · 131 unmodified lines | ||
| 132 | 132 | return 0; |
| 133 | 133 | } |
| 134 | 134 | |
| 135 | −static struct i2c_device_id hmc6352_id[] = { | |
| 135 | +static const struct i2c_device_id hmc6352_id[] = { | |
| 136 | 136 | { "hmc6352", 0 }, |
| 137 | 137 | { } |
| 138 | 138 | }; |
| @@ -857,7 +857,7 @@ out: · 856 unmodified lines | ||
| 857 | 857 | return error; |
| 858 | 858 | } |
| 859 | 859 | |
| 860 | −static struct pci_device_id ilo_devices[] = { | |
| 860 | +static const struct pci_device_id ilo_devices[] = { | |
| 861 | 861 | { PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xB204) }, |
| 862 | 862 | { PCI_DEVICE(PCI_VENDOR_ID_HP, 0x3307) }, |
| 863 | 863 | { } |
| @@ -454,7 +454,7 @@ ioc4_remove(struct pci_dev *pdev) · 453 unmodified lines | ||
| 454 | 454 | kfree(idd); |
| 455 | 455 | } |
| 456 | 456 | |
| 457 | −static struct pci_device_id ioc4_id_table[] = { | |
| 457 | +static const struct pci_device_id ioc4_id_table[] = { | |
| 458 | 458 | {PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC4, PCI_ANY_ID, |
| 459 | 459 | PCI_ANY_ID, 0x0b4000, 0xFFFFFF}, |
| 460 | 460 | {0} |
| @@ -145,7 +145,7 @@ static struct attribute *mid_att_als[] = { · 144 unmodified lines | ||
| 145 | 145 | NULL |
| 146 | 146 | }; |
| 147 | 147 | |
| 148 | −static struct attribute_group m_als_gr = { | |
| 148 | +static const struct attribute_group m_als_gr = { | |
| 149 | 149 | .name = "isl29020", |
| 150 | 150 | .attrs = mid_att_als |
| 151 | 151 | }; |
| @@ -188,7 +188,7 @@ static int isl29020_remove(struct i2c_client *client) · 36 unmodified lines | ||
| 188 | 188 | return 0; |
| 189 | 189 | } |
| 190 | 190 | |
| 191 | −static struct i2c_device_id isl29020_id[] = { | |
| 191 | +static const struct i2c_device_id isl29020_id[] = { | |
| 192 | 192 | { "isl29020", 0 }, |
| 193 | 193 | { } |
| 194 | 194 | }; |
| @@ -856,7 +856,7 @@ static struct attribute *lis3lv02d_attributes[] = { · 855 unmodified lines | ||
| 856 | 856 | NULL |
| 857 | 857 | }; |
| 858 | 858 | |
| 859 | −static struct attribute_group lis3lv02d_attribute_group = { | |
| 859 | +static const struct attribute_group lis3lv02d_attribute_group = { | |
| 860 | 860 | .attrs = lis3lv02d_attributes |
| 861 | 861 | }; |
| 862 | 862 | |
| @@ -14,20 +14,17 @@ void lkdtm_EXCEPTION(void); · 13 unmodified lines | ||
| 14 | 14 | void lkdtm_LOOP(void); |
| 15 | 15 | void lkdtm_OVERFLOW(void); |
| 16 | 16 | void lkdtm_CORRUPT_STACK(void); |
| 17 | +void lkdtm_CORRUPT_STACK_STRONG(void); | |
| 17 | 18 | void lkdtm_UNALIGNED_LOAD_STORE_WRITE(void); |
| 18 | 19 | void lkdtm_SOFTLOCKUP(void); |
| 19 | 20 | void lkdtm_HARDLOCKUP(void); |
| 20 | 21 | void lkdtm_SPINLOCKUP(void); |
| 21 | 22 | void lkdtm_HUNG_TASK(void); |
| 22 | −void lkdtm_REFCOUNT_SATURATE_INC(void); | |
| 23 | −void lkdtm_REFCOUNT_SATURATE_ADD(void); | |
| 24 | −void lkdtm_REFCOUNT_ZERO_DEC(void); | |
| 25 | −void lkdtm_REFCOUNT_ZERO_INC(void); | |
| 26 | −void lkdtm_REFCOUNT_ZERO_SUB(void); | |
| 27 | −void lkdtm_REFCOUNT_ZERO_ADD(void); | |
| 28 | 23 | void lkdtm_CORRUPT_LIST_ADD(void); |
| 29 | 24 | void lkdtm_CORRUPT_LIST_DEL(void); |
| 30 | 25 | void lkdtm_CORRUPT_USER_DS(void); |
| 26 | +void lkdtm_STACK_GUARD_PAGE_LEADING(void); | |
| 27 | +void lkdtm_STACK_GUARD_PAGE_TRAILING(void); | |
| 31 | 28 | |
| 32 | 29 | /* lkdtm_heap.c */ |
| 33 | 30 | void lkdtm_OVERWRITE_ALLOCATION(void); |
| @@ -49,6 +46,27 @@ void lkdtm_EXEC_RODATA(void); · 15 unmodified lines | ||
| 49 | 46 | void lkdtm_EXEC_USERSPACE(void); |
| 50 | 47 | void lkdtm_ACCESS_USERSPACE(void); |
| 51 | 48 | |
| 49 | +/* lkdtm_refcount.c */ | |
| 50 | +void lkdtm_REFCOUNT_INC_OVERFLOW(void); | |
| 51 | +void lkdtm_REFCOUNT_ADD_OVERFLOW(void); | |
| 52 | +void lkdtm_REFCOUNT_INC_NOT_ZERO_OVERFLOW(void); | |
| 53 | +void lkdtm_REFCOUNT_ADD_NOT_ZERO_OVERFLOW(void); | |
| 54 | +void lkdtm_REFCOUNT_DEC_ZERO(void); | |
| 55 | +void lkdtm_REFCOUNT_DEC_NEGATIVE(void); | |
| 56 | +void lkdtm_REFCOUNT_DEC_AND_TEST_NEGATIVE(void); | |
| 57 | +void lkdtm_REFCOUNT_SUB_AND_TEST_NEGATIVE(void); | |
| 58 | +void lkdtm_REFCOUNT_INC_ZERO(void); | |
| 59 | +void lkdtm_REFCOUNT_ADD_ZERO(void); | |
| 60 | +void lkdtm_REFCOUNT_INC_SATURATED(void); | |
| 61 | +void lkdtm_REFCOUNT_DEC_SATURATED(void); | |
| 62 | +void lkdtm_REFCOUNT_ADD_SATURATED(void); | |
| 63 | +void lkdtm_REFCOUNT_INC_NOT_ZERO_SATURATED(void); | |
| 64 | +void lkdtm_REFCOUNT_ADD_NOT_ZERO_SATURATED(void); | |
| 65 | +void lkdtm_REFCOUNT_DEC_AND_TEST_SATURATED(void); | |
| 66 | +void lkdtm_REFCOUNT_SUB_AND_TEST_SATURATED(void); | |
| 67 | +void lkdtm_REFCOUNT_TIMING(void); | |
| 68 | +void lkdtm_ATOMIC_TIMING(void); | |
| 69 | + | |
| 52 | 70 | /* lkdtm_rodata.c */ |
| 53 | 71 | void lkdtm_rodata_do_nothing(void); |
| 54 | 72 | |
| @@ -6,9 +6,9 @@ · 5 unmodified lines | ||
| 6 | 6 | */ |
| 7 | 7 | #include "lkdtm.h" |
| 8 | 8 | #include <linux/list.h> |
| 9 | −#include <linux/refcount.h> | |
| 10 | 9 | #include <linux/sched.h> |
| 11 | 10 | #include <linux/sched/signal.h> |
| 11 | +#include <linux/sched/task_stack.h> | |
| 12 | 12 | #include <linux/uaccess.h> |
| 13 | 13 | |
| 14 | 14 | struct lkdtm_list { |
| @@ -85,16 +85,31 @@ void lkdtm_OVERFLOW(void) · 70 unmodified lines | ||
| 85 | 85 | |
| 86 | 86 | static noinline void __lkdtm_CORRUPT_STACK(void *stack) |
| 87 | 87 | { |
| 88 | − memset(stack, 'a', 64); | |
| 88 | + memset(stack, '\xff', 64); | |
| 89 | 89 | } |
| 90 | 90 | |
| 91 | +/* This should trip the stack canary, not corrupt the return address. */ | |
| 91 | 92 | noinline void lkdtm_CORRUPT_STACK(void) |
| 92 | 93 | { |
| 93 | 94 | /* Use default char array length that triggers stack protection. */ |
| 94 | − char data[8]; | |
| 95 | + char data[8] __aligned(sizeof(void *)); | |
| 96 | + | |
| 97 | + __lkdtm_CORRUPT_STACK(&data); | |
| 98 | + | |
| 99 | + pr_info("Corrupted stack containing char array ...\n"); | |
| 100 | +} | |
| 101 | + | |
| 102 | +/* Same as above but will only get a canary with -fstack-protector-strong */ | |
| 103 | +noinline void lkdtm_CORRUPT_STACK_STRONG(void) | |
| 104 | +{ | |
| 105 | + union { | |
| 106 | + unsigned short shorts[4]; | |
| 107 | + unsigned long *ptr; | |
| 108 | + } data __aligned(sizeof(void *)); | |
| 109 | + | |
| 95 | 110 | __lkdtm_CORRUPT_STACK(&data); |
| 96 | 111 | |
| 97 | − pr_info("Corrupted stack with '%16s'...\n", data); | |
| 112 | + pr_info("Corrupted stack containing union ...\n"); | |
| 98 | 113 | } |
| 99 | 114 | |
| 100 | 115 | void lkdtm_UNALIGNED_LOAD_STORE_WRITE(void) |
| @@ -137,88 +152,6 @@ void lkdtm_HUNG_TASK(void) · 36 unmodified lines | ||
| 137 | 152 | schedule(); |
| 138 | 153 | } |
| 139 | 154 | |
| 140 | −void lkdtm_REFCOUNT_SATURATE_INC(void) | |
| 141 | −{ | |
| 142 | − refcount_t over = REFCOUNT_INIT(UINT_MAX - 1); | |
| 143 | − | |
| 144 | − pr_info("attempting good refcount decrement\n"); | |
| 145 | − refcount_dec(&over); | |
| 146 | − refcount_inc(&over); | |
| 147 | − | |
| 148 | − pr_info("attempting bad refcount inc overflow\n"); | |
| 149 | − refcount_inc(&over); | |
| 150 | − refcount_inc(&over); | |
| 151 | − if (refcount_read(&over) == UINT_MAX) | |
| 152 | − pr_err("Correctly stayed saturated, but no BUG?!\n"); | |
| 153 | − else | |
| 154 | − pr_err("Fail: refcount wrapped\n"); | |
| 155 | −} | |
| 156 | − | |
| 157 | −void lkdtm_REFCOUNT_SATURATE_ADD(void) | |
| 158 | −{ | |
| 159 | − refcount_t over = REFCOUNT_INIT(UINT_MAX - 1); | |
| 160 | − | |
| 161 | − pr_info("attempting good refcount decrement\n"); | |
| 162 | − refcount_dec(&over); | |
| 163 | − refcount_inc(&over); | |
| 164 | − | |
| 165 | − pr_info("attempting bad refcount add overflow\n"); | |
| 166 | − refcount_add(2, &over); | |
| 167 | − if (refcount_read(&over) == UINT_MAX) | |
| 168 | − pr_err("Correctly stayed saturated, but no BUG?!\n"); | |
| 169 | − else | |
| 170 | − pr_err("Fail: refcount wrapped\n"); | |
| 171 | −} | |
| 172 | − | |
| 173 | −void lkdtm_REFCOUNT_ZERO_DEC(void) | |
| 174 | −{ | |
| 175 | − refcount_t zero = REFCOUNT_INIT(1); | |
| 176 | − | |
| 177 | − pr_info("attempting bad refcount decrement to zero\n"); | |
| 178 | − refcount_dec(&zero); | |
| 179 | − if (refcount_read(&zero) == 0) | |
| 180 | − pr_err("Stayed at zero, but no BUG?!\n"); | |
| 181 | − else | |
| 182 | − pr_err("Fail: refcount went crazy\n"); | |
| 183 | −} | |
| 184 | − | |
| 185 | −void lkdtm_REFCOUNT_ZERO_SUB(void) | |
| 186 | −{ | |
| 187 | − refcount_t zero = REFCOUNT_INIT(1); | |
| 188 | − | |
| 189 | − pr_info("attempting bad refcount subtract past zero\n"); | |
| 190 | − if (!refcount_sub_and_test(2, &zero)) | |
| 191 | − pr_info("wrap attempt was noticed\n"); | |
| 192 | − if (refcount_read(&zero) == 1) | |
| 193 | − pr_err("Correctly stayed above 0, but no BUG?!\n"); | |
| 194 | − else | |
| 195 | − pr_err("Fail: refcount wrapped\n"); | |
| 196 | −} | |
| 197 | − | |
| 198 | −void lkdtm_REFCOUNT_ZERO_INC(void) | |
| 199 | −{ | |
| 200 | − refcount_t zero = REFCOUNT_INIT(0); | |
| 201 | − | |
| 202 | − pr_info("attempting bad refcount increment from zero\n"); | |
| 203 | − refcount_inc(&zero); | |
| 204 | − if (refcount_read(&zero) == 0) | |
| 205 | − pr_err("Stayed at zero, but no BUG?!\n"); | |
| 206 | − else | |
| 207 | − pr_err("Fail: refcount went past zero\n"); | |
| 208 | −} | |
| 209 | − | |
| 210 | −void lkdtm_REFCOUNT_ZERO_ADD(void) | |
| 211 | −{ | |
| 212 | − refcount_t zero = REFCOUNT_INIT(0); | |
| 213 | − | |
| 214 | − pr_info("attempting bad refcount addition from zero\n"); | |
| 215 | − refcount_add(2, &zero); | |
| 216 | − if (refcount_read(&zero) == 0) | |
| 217 | − pr_err("Stayed at zero, but no BUG?!\n"); | |
| 218 | − else | |
| 219 | − pr_err("Fail: refcount went past zero\n"); | |
| 220 | −} | |
| 221 | − | |
| 222 | 155 | void lkdtm_CORRUPT_LIST_ADD(void) |
| 223 | 156 | { |
| 224 | 157 | /* |
| @@ -282,6 +215,7 @@ void lkdtm_CORRUPT_LIST_DEL(void) · 57 unmodified lines | ||
| 282 | 215 | pr_err("list_del() corruption not detected!\n"); |
| 283 | 216 | } |
| 284 | 217 | |
| 218 | +/* Test if unbalanced set_fs(KERNEL_DS)/set_fs(USER_DS) check exists. */ | |
| 285 | 219 | void lkdtm_CORRUPT_USER_DS(void) |
| 286 | 220 | { |
| 287 | 221 | pr_info("setting bad task size limit\n"); |
| @@ -290,3 +224,31 @@ void lkdtm_CORRUPT_USER_DS(void) · 2 unmodified lines | ||
| 290 | 224 | /* Make sure we do not keep running with a KERNEL_DS! */ |
| 291 | 225 | force_sig(SIGKILL, current); |
| 292 | 226 | } |
| 227 | + | |
| 228 | +/* Test that VMAP_STACK is actually allocating with a leading guard page */ | |
| 229 | +void lkdtm_STACK_GUARD_PAGE_LEADING(void) | |
| 230 | +{ | |
| 231 | + const unsigned char *stack = task_stack_page(current); | |
| 232 | + const unsigned char *ptr = stack - 1; | |
| 233 | + volatile unsigned char byte; | |
| 234 | + | |
| 235 | + pr_info("attempting bad read from page below current stack\n"); | |
| 236 | + | |
| 237 | + byte = *ptr; | |
| 238 | + | |
| 239 | + pr_err("FAIL: accessed page before stack!\n"); | |
| 240 | +} | |
| 241 | + | |
| 242 | +/* Test that VMAP_STACK is actually allocating with a trailing guard page */ | |
| 243 | +void lkdtm_STACK_GUARD_PAGE_TRAILING(void) | |
| 244 | +{ | |
| 245 | + const unsigned char *stack = task_stack_page(current); | |
| 246 | + const unsigned char *ptr = stack + THREAD_SIZE; | |
| 247 | + volatile unsigned char byte; | |
| 248 | + | |
| 249 | + pr_info("attempting bad read from page above current stack\n"); | |
| 250 | + | |
| 251 | + byte = *ptr; | |
| 252 | + | |
| 253 | + pr_err("FAIL: accessed page after stack!\n"); | |
| 254 | +} | |
| @@ -201,6 +201,9 @@ struct crashtype crashtypes[] = { · 200 unmodified lines | ||
| 201 | 201 | CRASHTYPE(CORRUPT_LIST_DEL), |
| 202 | 202 | CRASHTYPE(CORRUPT_USER_DS), |
| 203 | 203 | CRASHTYPE(CORRUPT_STACK), |
| 204 | + CRASHTYPE(CORRUPT_STACK_STRONG), | |
| 205 | + CRASHTYPE(STACK_GUARD_PAGE_LEADING), | |
| 206 | + CRASHTYPE(STACK_GUARD_PAGE_TRAILING), | |
| 204 | 207 | CRASHTYPE(UNALIGNED_LOAD_STORE_WRITE), |
| 205 | 208 | CRASHTYPE(OVERWRITE_ALLOCATION), |
| 206 | 209 | CRASHTYPE(WRITE_AFTER_FREE), |
| @@ -221,12 +224,25 @@ struct crashtype crashtypes[] = { · 14 unmodified lines | ||
| 221 | 224 | CRASHTYPE(WRITE_RO), |
| 222 | 225 | CRASHTYPE(WRITE_RO_AFTER_INIT), |
| 223 | 226 | CRASHTYPE(WRITE_KERN), |
| 224 | − CRASHTYPE(REFCOUNT_SATURATE_INC), | |
| 225 | − CRASHTYPE(REFCOUNT_SATURATE_ADD), | |
| 226 | − CRASHTYPE(REFCOUNT_ZERO_DEC), | |
| 227 | − CRASHTYPE(REFCOUNT_ZERO_INC), | |
| 228 | − CRASHTYPE(REFCOUNT_ZERO_SUB), | |
| 229 | − CRASHTYPE(REFCOUNT_ZERO_ADD), | |
| 227 | + CRASHTYPE(REFCOUNT_INC_OVERFLOW), | |
| 228 | + CRASHTYPE(REFCOUNT_ADD_OVERFLOW), | |
| 229 | + CRASHTYPE(REFCOUNT_INC_NOT_ZERO_OVERFLOW), | |
| 230 | + CRASHTYPE(REFCOUNT_ADD_NOT_ZERO_OVERFLOW), | |
| 231 | + CRASHTYPE(REFCOUNT_DEC_ZERO), | |
| 232 | + CRASHTYPE(REFCOUNT_DEC_NEGATIVE), | |
| 233 | + CRASHTYPE(REFCOUNT_DEC_AND_TEST_NEGATIVE), | |
| 234 | + CRASHTYPE(REFCOUNT_SUB_AND_TEST_NEGATIVE), | |
| 235 | + CRASHTYPE(REFCOUNT_INC_ZERO), | |
| 236 | + CRASHTYPE(REFCOUNT_ADD_ZERO), | |
| 237 | + CRASHTYPE(REFCOUNT_INC_SATURATED), | |
| 238 | + CRASHTYPE(REFCOUNT_DEC_SATURATED), | |
| 239 | + CRASHTYPE(REFCOUNT_ADD_SATURATED), | |
| 240 | + CRASHTYPE(REFCOUNT_INC_NOT_ZERO_SATURATED), | |
| 241 | + CRASHTYPE(REFCOUNT_ADD_NOT_ZERO_SATURATED), | |
| 242 | + CRASHTYPE(REFCOUNT_DEC_AND_TEST_SATURATED), | |
| 243 | + CRASHTYPE(REFCOUNT_SUB_AND_TEST_SATURATED), | |
| 244 | + CRASHTYPE(REFCOUNT_TIMING), | |
| 245 | + CRASHTYPE(ATOMIC_TIMING), | |
| 230 | 246 | CRASHTYPE(USERCOPY_HEAP_SIZE_TO), |
| 231 | 247 | CRASHTYPE(USERCOPY_HEAP_SIZE_FROM), |
| 232 | 248 | CRASHTYPE(USERCOPY_HEAP_FLAG_TO), |
| @@ -0,0 +1,400 @@ | ||
| 1 | +/* | |
| 2 | + * This is for all the tests related to refcount bugs (e.g. overflow, | |
| 3 | + * underflow, reaching zero untested, etc). | |
| 4 | + */ | |
| 5 | +#include "lkdtm.h" | |
| 6 | +#include <linux/refcount.h> | |
| 7 | + | |
| 8 | +#ifdef CONFIG_REFCOUNT_FULL | |
| 9 | +#define REFCOUNT_MAX (UINT_MAX - 1) | |
| 10 | +#define REFCOUNT_SATURATED UINT_MAX | |
| 11 | +#else | |
| 12 | +#define REFCOUNT_MAX INT_MAX | |
| 13 | +#define REFCOUNT_SATURATED (INT_MIN / 2) | |
| 14 | +#endif | |
| 15 | + | |
| 16 | +static void overflow_check(refcount_t *ref) | |
| 17 | +{ | |
| 18 | + switch (refcount_read(ref)) { | |
| 19 | + case REFCOUNT_SATURATED: | |
| 20 | + pr_info("Overflow detected: saturated\n"); | |
| 21 | + break; | |
| 22 | + case REFCOUNT_MAX: | |
| 23 | + pr_warn("Overflow detected: unsafely reset to max\n"); | |
| 24 | + break; | |
| 25 | + default: | |
| 26 | + pr_err("Fail: refcount wrapped to %d\n", refcount_read(ref)); | |
| 27 | + } | |
| 28 | +} | |
| 29 | + | |
| 30 | +/* | |
| 31 | + * A refcount_inc() above the maximum value of the refcount implementation, | |
| 32 | + * should at least saturate, and at most also WARN. | |
| 33 | + */ | |
| 34 | +void lkdtm_REFCOUNT_INC_OVERFLOW(void) | |
| 35 | +{ | |
| 36 | + refcount_t over = REFCOUNT_INIT(REFCOUNT_MAX - 1); | |
| 37 | + | |
| 38 | + pr_info("attempting good refcount_inc() without overflow\n"); | |
| 39 | + refcount_dec(&over); | |
| 40 | + refcount_inc(&over); | |
| 41 | + | |
| 42 | + pr_info("attempting bad refcount_inc() overflow\n"); | |
| 43 | + refcount_inc(&over); | |
| 44 | + refcount_inc(&over); | |
| 45 | + | |
| 46 | + overflow_check(&over); | |
| 47 | +} | |
| 48 | + | |
| 49 | +/* refcount_add() should behave just like refcount_inc() above. */ | |
| 50 | +void lkdtm_REFCOUNT_ADD_OVERFLOW(void) | |
| 51 | +{ | |
| 52 | + refcount_t over = REFCOUNT_INIT(REFCOUNT_MAX - 1); | |
| 53 | + | |
| 54 | + pr_info("attempting good refcount_add() without overflow\n"); | |
| 55 | + refcount_dec(&over); | |
| 56 | + refcount_dec(&over); | |
| 57 | + refcount_dec(&over); | |
| 58 | + refcount_dec(&over); | |
| 59 | + refcount_add(4, &over); | |
| 60 | + | |
| 61 | + pr_info("attempting bad refcount_add() overflow\n"); | |
| 62 | + refcount_add(4, &over); | |
| 63 | + | |
| 64 | + overflow_check(&over); | |
| 65 | +} | |
| 66 | + | |
| 67 | +/* refcount_inc_not_zero() should behave just like refcount_inc() above. */ | |
| 68 | +void lkdtm_REFCOUNT_INC_NOT_ZERO_OVERFLOW(void) | |
| 69 | +{ | |
| 70 | + refcount_t over = REFCOUNT_INIT(REFCOUNT_MAX); | |
| 71 | + | |
| 72 | + pr_info("attempting bad refcount_inc_not_zero() overflow\n"); | |
| 73 | + if (!refcount_inc_not_zero(&over)) | |
| 74 | + pr_warn("Weird: refcount_inc_not_zero() reported zero\n"); | |
| 75 | + | |
| 76 | + overflow_check(&over); | |
| 77 | +} | |
| 78 | + | |
| 79 | +/* refcount_add_not_zero() should behave just like refcount_inc() above. */ | |
| 80 | +void lkdtm_REFCOUNT_ADD_NOT_ZERO_OVERFLOW(void) | |
| 81 | +{ | |
| 82 | + refcount_t over = REFCOUNT_INIT(REFCOUNT_MAX); | |
| 83 | + | |
| 84 | + pr_info("attempting bad refcount_add_not_zero() overflow\n"); | |
| 85 | + if (!refcount_add_not_zero(6, &over)) | |
| 86 | + pr_warn("Weird: refcount_add_not_zero() reported zero\n"); | |
| 87 | + | |
| 88 | + overflow_check(&over); | |
| 89 | +} | |
| 90 | + | |
| 91 | +static void check_zero(refcount_t *ref) | |
| 92 | +{ | |
| 93 | + switch (refcount_read(ref)) { | |
| 94 | + case REFCOUNT_SATURATED: | |
| 95 | + pr_info("Zero detected: saturated\n"); | |
| 96 | + break; | |
| 97 | + case REFCOUNT_MAX: | |
| 98 | + pr_warn("Zero detected: unsafely reset to max\n"); | |
| 99 | + break; | |
| 100 | + case 0: | |
| 101 | + pr_warn("Still at zero: refcount_inc/add() must not inc-from-0\n"); | |
| 102 | + break; | |
| 103 | + default: | |
| 104 | + pr_err("Fail: refcount went crazy: %d\n", refcount_read(ref)); | |
| 105 | + } | |
| 106 | +} | |
| 107 | + | |
| 108 | +/* | |
| 109 | + * A refcount_dec(), as opposed to a refcount_dec_and_test(), when it hits | |
| 110 | + * zero it should either saturate (when inc-from-zero isn't protected) | |
| 111 | + * or stay at zero (when inc-from-zero is protected) and should WARN for both. | |
| 112 | + */ | |
| 113 | +void lkdtm_REFCOUNT_DEC_ZERO(void) | |
| 114 | +{ | |
| 115 | + refcount_t zero = REFCOUNT_INIT(2); | |
| 116 | + | |
| 117 | + pr_info("attempting good refcount_dec()\n"); | |
| 118 | + refcount_dec(&zero); | |
| 119 | + | |
| 120 | + pr_info("attempting bad refcount_dec() to zero\n"); | |
| 121 | + refcount_dec(&zero); | |
| 122 | + | |
| 123 | + check_zero(&zero); | |
| 124 | +} | |
| 125 | + | |
| 126 | +static void check_negative(refcount_t *ref, int start) | |
| 127 | +{ | |
| 128 | + /* | |
| 129 | + * CONFIG_REFCOUNT_FULL refuses to move a refcount at all on an | |
| 130 | + * over-sub, so we have to track our starting position instead of | |
| 131 | + * looking only at zero-pinning. | |
| 132 | + */ | |
| 133 | + if (refcount_read(ref) == start) { | |
| 134 | + pr_warn("Still at %d: refcount_inc/add() must not inc-from-0\n", | |
| 135 | + start); | |
| 136 | + return; | |
| 137 | + } | |
| 138 | + | |
| 139 | + switch (refcount_read(ref)) { | |
| 140 | + case REFCOUNT_SATURATED: | |
| 141 | + pr_info("Negative detected: saturated\n"); | |
| 142 | + break; | |
| 143 | + case REFCOUNT_MAX: | |
| 144 | + pr_warn("Negative detected: unsafely reset to max\n"); | |
| 145 | + break; | |
| 146 | + default: | |
| 147 | + pr_err("Fail: refcount went crazy: %d\n", refcount_read(ref)); | |
| 148 | + } | |
| 149 | +} | |
| 150 | + | |
| 151 | +/* A refcount_dec() going negative should saturate and may WARN. */ | |
| 152 | +void lkdtm_REFCOUNT_DEC_NEGATIVE(void) | |
| 153 | +{ | |
| 154 | + refcount_t neg = REFCOUNT_INIT(0); | |
| 155 | + | |
| 156 | + pr_info("attempting bad refcount_dec() below zero\n"); | |
| 157 | + refcount_dec(&neg); | |
| 158 | + | |
| 159 | + check_negative(&neg, 0); | |
| 160 | +} | |
| 161 | + | |
| 162 | +/* | |
| 163 | + * A refcount_dec_and_test() should act like refcount_dec() above when | |
| 164 | + * going negative. | |
| 165 | + */ | |
| 166 | +void lkdtm_REFCOUNT_DEC_AND_TEST_NEGATIVE(void) | |
| 167 | +{ | |
| 168 | + refcount_t neg = REFCOUNT_INIT(0); | |
| 169 | + | |
| 170 | + pr_info("attempting bad refcount_dec_and_test() below zero\n"); | |
| 171 | + if (refcount_dec_and_test(&neg)) | |
| 172 | + pr_warn("Weird: refcount_dec_and_test() reported zero\n"); | |
| 173 | + | |
| 174 | + check_negative(&neg, 0); | |
| 175 | +} | |
| 176 | + | |
| 177 | +/* | |
| 178 | + * A refcount_sub_and_test() should act like refcount_dec_and_test() | |
| 179 | + * above when going negative. | |
| 180 | + */ | |
| 181 | +void lkdtm_REFCOUNT_SUB_AND_TEST_NEGATIVE(void) | |
| 182 | +{ | |
| 183 | + refcount_t neg = REFCOUNT_INIT(3); | |
| 184 | + | |
| 185 | + pr_info("attempting bad refcount_sub_and_test() below zero\n"); | |
| 186 | + if (refcount_sub_and_test(5, &neg)) | |
| 187 | + pr_warn("Weird: refcount_sub_and_test() reported zero\n"); | |
| 188 | + | |
| 189 | + check_negative(&neg, 3); | |
| 190 | +} | |
| 191 | + | |
| 192 | +static void check_from_zero(refcount_t *ref) | |
| 193 | +{ | |
| 194 | + switch (refcount_read(ref)) { | |
| 195 | + case 0: | |
| 196 | + pr_info("Zero detected: stayed at zero\n"); | |
| 197 | + break; | |
| 198 | + case REFCOUNT_SATURATED: | |
| 199 | + pr_info("Zero detected: saturated\n"); | |
| 200 | + break; | |
| 201 | + case REFCOUNT_MAX: | |
| 202 | + pr_warn("Zero detected: unsafely reset to max\n"); | |
| 203 | + break; | |
| 204 | + default: | |
| 205 | + pr_info("Fail: zero not detected, incremented to %d\n", | |
| 206 | + refcount_read(ref)); | |
| 207 | + } | |
| 208 | +} | |
| 209 | + | |
| 210 | +/* | |
| 211 | + * A refcount_inc() from zero should pin to zero or saturate and may WARN. | |
| 212 | + * Only CONFIG_REFCOUNT_FULL provides this protection currently. | |
| 213 | + */ | |
| 214 | +void lkdtm_REFCOUNT_INC_ZERO(void) | |
| 215 | +{ | |
| 216 | + refcount_t zero = REFCOUNT_INIT(0); | |
| 217 | + | |
| 218 | + pr_info("attempting safe refcount_inc_not_zero() from zero\n"); | |
| 219 | + if (!refcount_inc_not_zero(&zero)) { | |
| 220 | + pr_info("Good: zero detected\n"); | |
| 221 | + if (refcount_read(&zero) == 0) | |
| 222 | + pr_info("Correctly stayed at zero\n"); | |
| 223 | + else | |
| 224 | + pr_err("Fail: refcount went past zero!\n"); | |
| 225 | + } else { | |
| 226 | + pr_err("Fail: Zero not detected!?\n"); | |
| 227 | + } | |
| 228 | + | |
| 229 | + pr_info("attempting bad refcount_inc() from zero\n"); | |
| 230 | + refcount_inc(&zero); | |
| 231 | + | |
| 232 | + check_from_zero(&zero); | |
| 233 | +} | |
| 234 | + | |
| 235 | +/* | |
| 236 | + * A refcount_add() should act like refcount_inc() above when starting | |
| 237 | + * at zero. | |
| 238 | + */ | |
| 239 | +void lkdtm_REFCOUNT_ADD_ZERO(void) | |
| 240 | +{ | |
| 241 | + refcount_t zero = REFCOUNT_INIT(0); | |
| 242 | + | |
| 243 | + pr_info("attempting safe refcount_add_not_zero() from zero\n"); | |
| 244 | + if (!refcount_add_not_zero(3, &zero)) { | |
| 245 | + pr_info("Good: zero detected\n"); | |
| 246 | + if (refcount_read(&zero) == 0) | |
| 247 | + pr_info("Correctly stayed at zero\n"); | |
| 248 | + else | |
| 249 | + pr_err("Fail: refcount went past zero\n"); | |
| 250 | + } else { | |
| 251 | + pr_err("Fail: Zero not detected!?\n"); | |
| 252 | + } | |
| 253 | + | |
| 254 | + pr_info("attempting bad refcount_add() from zero\n"); | |
| 255 | + refcount_add(3, &zero); | |
| 256 | + | |
| 257 | + check_from_zero(&zero); | |
| 258 | +} | |
| 259 | + | |
| 260 | +static void check_saturated(refcount_t *ref) | |
| 261 | +{ | |
| 262 | + switch (refcount_read(ref)) { | |
| 263 | + case REFCOUNT_SATURATED: | |
| 264 | + pr_info("Saturation detected: still saturated\n"); | |
| 265 | + break; | |
| 266 | + case REFCOUNT_MAX: | |
| 267 | + pr_warn("Saturation detected: unsafely reset to max\n"); | |
| 268 | + break; | |
| 269 | + default: | |
| 270 | + pr_err("Fail: refcount went crazy: %d\n", refcount_read(ref)); | |
| 271 | + } | |
| 272 | +} | |
| 273 | + | |
| 274 | +/* | |
| 275 | + * A refcount_inc() from a saturated value should at most warn about | |
| 276 | + * being saturated already. | |
| 277 | + */ | |
| 278 | +void lkdtm_REFCOUNT_INC_SATURATED(void) | |
| 279 | +{ | |
| 280 | + refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED); | |
| 281 | + | |
| 282 | + pr_info("attempting bad refcount_inc() from saturated\n"); | |
| 283 | + refcount_inc(&sat); | |
| 284 | + | |
| 285 | + check_saturated(&sat); | |
| 286 | +} | |
| 287 | + | |
| 288 | +/* Should act like refcount_inc() above from saturated. */ | |
| 289 | +void lkdtm_REFCOUNT_DEC_SATURATED(void) | |
| 290 | +{ | |
| 291 | + refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED); | |
| 292 | + | |
| 293 | + pr_info("attempting bad refcount_dec() from saturated\n"); | |
| 294 | + refcount_dec(&sat); | |
| 295 | + | |
| 296 | + check_saturated(&sat); | |
| 297 | +} | |
| 298 | + | |
| 299 | +/* Should act like refcount_inc() above from saturated. */ | |
| 300 | +void lkdtm_REFCOUNT_ADD_SATURATED(void) | |
| 301 | +{ | |
| 302 | + refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED); | |
| 303 | + | |
| 304 | + pr_info("attempting bad refcount_dec() from saturated\n"); | |
| 305 | + refcount_add(8, &sat); | |
| 306 | + | |
| 307 | + check_saturated(&sat); | |
| 308 | +} | |
| 309 | + | |
| 310 | +/* Should act like refcount_inc() above from saturated. */ | |
| 311 | +void lkdtm_REFCOUNT_INC_NOT_ZERO_SATURATED(void) | |
| 312 | +{ | |
| 313 | + refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED); | |
| 314 | + | |
| 315 | + pr_info("attempting bad refcount_inc_not_zero() from saturated\n"); | |
| 316 | + if (!refcount_inc_not_zero(&sat)) | |
| 317 | + pr_warn("Weird: refcount_inc_not_zero() reported zero\n"); | |
| 318 | + | |
| 319 | + check_saturated(&sat); | |
| 320 | +} | |
| 321 | + | |
| 322 | +/* Should act like refcount_inc() above from saturated. */ | |
| 323 | +void lkdtm_REFCOUNT_ADD_NOT_ZERO_SATURATED(void) | |
| 324 | +{ | |
| 325 | + refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED); | |
| 326 | + | |
| 327 | + pr_info("attempting bad refcount_add_not_zero() from saturated\n"); | |
| 328 | + if (!refcount_add_not_zero(7, &sat)) | |
| 329 | + pr_warn("Weird: refcount_add_not_zero() reported zero\n"); | |
| 330 | + | |
| 331 | + check_saturated(&sat); | |
| 332 | +} | |
| 333 | + | |
| 334 | +/* Should act like refcount_inc() above from saturated. */ | |
| 335 | +void lkdtm_REFCOUNT_DEC_AND_TEST_SATURATED(void) | |
| 336 | +{ | |
| 337 | + refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED); | |
| 338 | + | |
| 339 | + pr_info("attempting bad refcount_dec_and_test() from saturated\n"); | |
| 340 | + if (refcount_dec_and_test(&sat)) | |
| 341 | + pr_warn("Weird: refcount_dec_and_test() reported zero\n"); | |
| 342 | + | |
| 343 | + check_saturated(&sat); | |
| 344 | +} | |
| 345 | + | |
| 346 | +/* Should act like refcount_inc() above from saturated. */ | |
| 347 | +void lkdtm_REFCOUNT_SUB_AND_TEST_SATURATED(void) | |
| 348 | +{ | |
| 349 | + refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED); | |
| 350 | + | |
| 351 | + pr_info("attempting bad refcount_sub_and_test() from saturated\n"); | |
| 352 | + if (refcount_sub_and_test(8, &sat)) | |
| 353 | + pr_warn("Weird: refcount_sub_and_test() reported zero\n"); | |
| 354 | + | |
| 355 | + check_saturated(&sat); | |
| 356 | +} | |
| 357 | + | |
| 358 | +/* Used to time the existing atomic_t when used for reference counting */ | |
| 359 | +void lkdtm_ATOMIC_TIMING(void) | |
| 360 | +{ | |
| 361 | + unsigned int i; | |
| 362 | + atomic_t count = ATOMIC_INIT(1); | |
| 363 | + | |
| 364 | + for (i = 0; i < INT_MAX - 1; i++) | |
| 365 | + atomic_inc(&count); | |
| 366 | + | |
| 367 | + for (i = INT_MAX; i > 0; i--) | |
| 368 | + if (atomic_dec_and_test(&count)) | |
| 369 | + break; | |
| 370 | + | |
| 371 | + if (i != 1) | |
| 372 | + pr_err("atomic timing: out of sync up/down cycle: %u\n", i - 1); | |
| 373 | + else | |
| 374 | + pr_info("atomic timing: done\n"); | |
| 375 | +} | |
| 376 | + | |
| 377 | +/* | |
| 378 | + * This can be compared to ATOMIC_TIMING when implementing fast refcount | |
| 379 | + * protections. Looking at the number of CPU cycles tells the real story | |
| 380 | + * about performance. For example: | |
| 381 | + * cd /sys/kernel/debug/provoke-crash | |
| 382 | + * perf stat -B -- cat <(echo REFCOUNT_TIMING) > DIRECT | |
| 383 | + */ | |
| 384 | +void lkdtm_REFCOUNT_TIMING(void) | |
| 385 | +{ | |
| 386 | + unsigned int i; | |
| 387 | + refcount_t count = REFCOUNT_INIT(1); | |
| 388 | + | |
| 389 | + for (i = 0; i < INT_MAX - 1; i++) | |
| 390 | + refcount_inc(&count); | |
| 391 | + | |
| 392 | + for (i = INT_MAX; i > 0; i--) | |
| 393 | + if (refcount_dec_and_test(&count)) | |
| 394 | + break; | |
| 395 | + | |
| 396 | + if (i != 1) | |
| 397 | + pr_err("refcount: out of sync up/down cycle: %u\n", i - 1); | |
| 398 | + else | |
| 399 | + pr_info("refcount timing: done\n"); | |
| 400 | +} | |
| @@ -845,7 +845,7 @@ static void mei_cl_bus_dev_release(struct device *dev) · 844 unmodified lines | ||
| 845 | 845 | kfree(cldev); |
| 846 | 846 | } |
| 847 | 847 | |
| 848 | −static struct device_type mei_cl_device_type = { | |
| 848 | +static const struct device_type mei_cl_device_type = { | |
| 849 | 849 | .release = mei_cl_bus_dev_release, |
| 850 | 850 | }; |
| 851 | 851 | |
| @@ -1354,10 +1354,10 @@ static bool mei_me_fw_type_sps(struct pci_dev *pdev) · 1353 unmodified lines | ||
| 1354 | 1354 | .quirk_probe = mei_me_fw_type_sps |
| 1355 | 1355 | |
| 1356 | 1356 | |
| 1357 | −#define MEI_CFG_LEGACY_HFS \ | |
| 1357 | +#define MEI_CFG_ICH_HFS \ | |
| 1358 | 1358 | .fw_status.count = 0 |
| 1359 | 1359 | |
| 1360 | −#define MEI_CFG_ICH_HFS \ | |
| 1360 | +#define MEI_CFG_ICH10_HFS \ | |
| 1361 | 1361 | .fw_status.count = 1, \ |
| 1362 | 1362 | .fw_status.status[0] = PCI_CFG_HFS_1 |
| 1363 | 1363 | |
| @@ -1376,38 +1376,61 @@ static bool mei_me_fw_type_sps(struct pci_dev *pdev) · 12 unmodified lines | ||
| 1376 | 1376 | .fw_status.status[5] = PCI_CFG_HFS_6 |
| 1377 | 1377 | |
| 1378 | 1378 | /* ICH Legacy devices */ |
| 1379 | −const struct mei_cfg mei_me_legacy_cfg = { | |
| 1380 | − MEI_CFG_LEGACY_HFS, | |
| 1379 | +static const struct mei_cfg mei_me_ich_cfg = { | |
| 1380 | + MEI_CFG_ICH_HFS, | |
| 1381 | 1381 | }; |
| 1382 | 1382 | |
| 1383 | 1383 | /* ICH devices */ |
| 1384 | −const struct mei_cfg mei_me_ich_cfg = { | |
| 1385 | − MEI_CFG_ICH_HFS, | |
| 1384 | +static const struct mei_cfg mei_me_ich10_cfg = { | |
| 1385 | + MEI_CFG_ICH10_HFS, | |
| 1386 | 1386 | }; |
| 1387 | 1387 | |
| 1388 | 1388 | /* PCH devices */ |
| 1389 | −const struct mei_cfg mei_me_pch_cfg = { | |
| 1389 | +static const struct mei_cfg mei_me_pch_cfg = { | |
| 1390 | 1390 | MEI_CFG_PCH_HFS, |
| 1391 | 1391 | }; |
| 1392 | 1392 | |
| 1393 | − | |
| 1394 | 1393 | /* PCH Cougar Point and Patsburg with quirk for Node Manager exclusion */ |
| 1395 | −const struct mei_cfg mei_me_pch_cpt_pbg_cfg = { | |
| 1394 | +static const struct mei_cfg mei_me_pch_cpt_pbg_cfg = { | |
| 1396 | 1395 | MEI_CFG_PCH_HFS, |
| 1397 | 1396 | MEI_CFG_FW_NM, |
| 1398 | 1397 | }; |
| 1399 | 1398 | |
| 1400 | 1399 | /* PCH8 Lynx Point and newer devices */ |
| 1401 | −const struct mei_cfg mei_me_pch8_cfg = { | |
| 1400 | +static const struct mei_cfg mei_me_pch8_cfg = { | |
| 1402 | 1401 | MEI_CFG_PCH8_HFS, |
| 1403 | 1402 | }; |
| 1404 | 1403 | |
| 1405 | 1404 | /* PCH8 Lynx Point with quirk for SPS Firmware exclusion */ |
| 1406 | −const struct mei_cfg mei_me_pch8_sps_cfg = { | |
| 1405 | +static const struct mei_cfg mei_me_pch8_sps_cfg = { | |
| 1407 | 1406 | MEI_CFG_PCH8_HFS, |
| 1408 | 1407 | MEI_CFG_FW_SPS, |
| 1409 | 1408 | }; |
| 1410 | 1409 | |
| 1410 | +/* | |
| 1411 | + * mei_cfg_list - A list of platform platform specific configurations. | |
| 1412 | + * Note: has to be synchronized with enum mei_cfg_idx. | |
| 1413 | + */ | |
| 1414 | +static const struct mei_cfg *const mei_cfg_list[] = { | |
| 1415 | + [MEI_ME_UNDEF_CFG] = NULL, | |
| 1416 | + [MEI_ME_ICH_CFG] = &mei_me_ich_cfg, | |
| 1417 | + [MEI_ME_ICH10_CFG] = &mei_me_ich10_cfg, | |
| 1418 | + [MEI_ME_PCH_CFG] = &mei_me_pch_cfg, | |
| 1419 | + [MEI_ME_PCH_CPT_PBG_CFG] = &mei_me_pch_cpt_pbg_cfg, | |
| 1420 | + [MEI_ME_PCH8_CFG] = &mei_me_pch8_cfg, | |
| 1421 | + [MEI_ME_PCH8_SPS_CFG] = &mei_me_pch8_sps_cfg, | |
| 1422 | +}; | |
| 1423 | + | |
| 1424 | +const struct mei_cfg *mei_me_get_cfg(kernel_ulong_t idx) | |
| 1425 | +{ | |
| 1426 | + BUILD_BUG_ON(ARRAY_SIZE(mei_cfg_list) != MEI_ME_NUM_CFG); | |
| 1427 | + | |
| 1428 | + if (idx >= MEI_ME_NUM_CFG) | |
| 1429 | + return NULL; | |
| 1430 | + | |
| 1431 | + return mei_cfg_list[idx]; | |
| 1432 | +}; | |
| 1433 | + | |
| 1411 | 1434 | /** |
| 1412 | 1435 | * mei_me_dev_init - allocates and initializes the mei device structure |
| 1413 | 1436 | * |
| @@ -41,8 +41,7 @@ struct mei_cfg { · 40 unmodified lines | ||
| 41 | 41 | #define MEI_PCI_DEVICE(dev, cfg) \ |
| 42 | 42 | .vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \ |
| 43 | 43 | .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, \ |
| 44 | − .driver_data = (kernel_ulong_t)&(cfg) | |
| 45 | − | |
| 44 | + .driver_data = (kernel_ulong_t)(cfg), | |
| 46 | 45 | |
| 47 | 46 | #define MEI_ME_RPM_TIMEOUT 500 /* ms */ |
| 48 | 47 | |
| @@ -63,12 +62,36 @@ struct mei_me_hw { · 14 unmodified lines | ||
| 63 | 62 | |
| 64 | 63 | #define to_me_hw(dev) (struct mei_me_hw *)((dev)->hw) |
| 65 | 64 | |
| 66 | −extern const struct mei_cfg mei_me_legacy_cfg; | |
| 67 | −extern const struct mei_cfg mei_me_ich_cfg; | |
| 68 | −extern const struct mei_cfg mei_me_pch_cfg; | |
| 69 | −extern const struct mei_cfg mei_me_pch_cpt_pbg_cfg; | |
| 70 | −extern const struct mei_cfg mei_me_pch8_cfg; | |
| 71 | −extern const struct mei_cfg mei_me_pch8_sps_cfg; | |
| 65 | +/** | |
| 66 | + * enum mei_cfg_idx - indices to platform specific configurations. | |
| 67 | + * | |
| 68 | + * Note: has to be synchronized with mei_cfg_list[] | |
| 69 | + * | |
| 70 | + * @MEI_ME_UNDEF_CFG: Lower sentinel. | |
| 71 | + * @MEI_ME_ICH_CFG: I/O Controller Hub legacy devices. | |
| 72 | + * @MEI_ME_ICH10_CFG: I/O Controller Hub platforms Gen10 | |
| 73 | + * @MEI_ME_PCH_CFG: Platform Controller Hub platforms (Up to Gen8). | |
| 74 | + * @MEI_ME_PCH_CPT_PBG_CFG:Platform Controller Hub workstations | |
| 75 | + * with quirk for Node Manager exclusion. | |
| 76 | + * @MEI_ME_PCH8_CFG: Platform Controller Hub Gen8 and newer | |
| 77 | + * client platforms. | |
| 78 | + * @MEI_ME_PCH8_SPS_CFG: Platform Controller Hub Gen8 and newer | |
| 79 | + * servers platforms with quirk for | |
| 80 | + * SPS firmware exclusion. | |
| 81 | + * @MEI_ME_NUM_CFG: Upper Sentinel. | |
| 82 | + */ | |
| 83 | +enum mei_cfg_idx { | |
| 84 | + MEI_ME_UNDEF_CFG, | |
| 85 | + MEI_ME_ICH_CFG, | |
| 86 | + MEI_ME_ICH10_CFG, | |
| 87 | + MEI_ME_PCH_CFG, | |
| 88 | + MEI_ME_PCH_CPT_PBG_CFG, | |
| 89 | + MEI_ME_PCH8_CFG, | |
| 90 | + MEI_ME_PCH8_SPS_CFG, | |
| 91 | + MEI_ME_NUM_CFG, | |
| 92 | +}; | |
| 93 | + | |
| 94 | +const struct mei_cfg *mei_me_get_cfg(kernel_ulong_t idx); | |
| 72 | 95 | |
| 73 | 96 | struct mei_device *mei_me_dev_init(struct pci_dev *pdev, |
| 74 | 97 | const struct mei_cfg *cfg); |
| @@ -43,57 +43,58 @@ · 42 unmodified lines | ||
| 43 | 43 | |
| 44 | 44 | /* mei_pci_tbl - PCI Device ID Table */ |
| 45 | 45 | static const struct pci_device_id mei_me_pci_tbl[] = { |
| 46 | − {MEI_PCI_DEVICE(MEI_DEV_ID_82946GZ, mei_me_legacy_cfg)}, | |
| 47 | − {MEI_PCI_DEVICE(MEI_DEV_ID_82G35, mei_me_legacy_cfg)}, | |
| 48 | − {MEI_PCI_DEVICE(MEI_DEV_ID_82Q965, mei_me_legacy_cfg)}, | |
| 49 | − {MEI_PCI_DEVICE(MEI_DEV_ID_82G965, mei_me_legacy_cfg)}, | |
| 50 | − {MEI_PCI_DEVICE(MEI_DEV_ID_82GM965, mei_me_legacy_cfg)}, | |
| 51 | − {MEI_PCI_DEVICE(MEI_DEV_ID_82GME965, mei_me_legacy_cfg)}, | |
| 52 | − {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82Q35, mei_me_legacy_cfg)}, | |
| 53 | − {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82G33, mei_me_legacy_cfg)}, | |
| 54 | − {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82Q33, mei_me_legacy_cfg)}, | |
| 55 | − {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82X38, mei_me_legacy_cfg)}, | |
| 56 | − {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_3200, mei_me_legacy_cfg)}, | |
| 57 | − | |
| 58 | − {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_6, mei_me_legacy_cfg)}, | |
| 59 | − {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_7, mei_me_legacy_cfg)}, | |
| 60 | − {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_8, mei_me_legacy_cfg)}, | |
| 61 | − {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_9, mei_me_legacy_cfg)}, | |
| 62 | − {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_10, mei_me_legacy_cfg)}, | |
| 63 | − {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_1, mei_me_legacy_cfg)}, | |
| 64 | − {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_2, mei_me_legacy_cfg)}, | |
| 65 | − {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_3, mei_me_legacy_cfg)}, | |
| 66 | − {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_4, mei_me_legacy_cfg)}, | |
| 67 | − {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_1, mei_me_ich_cfg)}, | |
| 68 | − {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_2, mei_me_ich_cfg)}, | |
| 69 | − {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_3, mei_me_ich_cfg)}, | |
| 70 | − {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_4, mei_me_ich_cfg)}, | |
| 71 | − | |
| 72 | − {MEI_PCI_DEVICE(MEI_DEV_ID_IBXPK_1, mei_me_pch_cfg)}, | |
| 73 | − {MEI_PCI_DEVICE(MEI_DEV_ID_IBXPK_2, mei_me_pch_cfg)}, | |
| 74 | − {MEI_PCI_DEVICE(MEI_DEV_ID_CPT_1, mei_me_pch_cpt_pbg_cfg)}, | |
| 75 | − {MEI_PCI_DEVICE(MEI_DEV_ID_PBG_1, mei_me_pch_cpt_pbg_cfg)}, | |
| 76 | − {MEI_PCI_DEVICE(MEI_DEV_ID_PPT_1, mei_me_pch_cfg)}, | |
| 77 | − {MEI_PCI_DEVICE(MEI_DEV_ID_PPT_2, mei_me_pch_cfg)}, | |
| 78 | − {MEI_PCI_DEVICE(MEI_DEV_ID_PPT_3, mei_me_pch_cfg)}, | |
| 79 | − {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_H, mei_me_pch8_sps_cfg)}, | |
| 80 | − {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_W, mei_me_pch8_sps_cfg)}, | |
| 81 | − {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_LP, mei_me_pch8_cfg)}, | |
| 82 | − {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_HR, mei_me_pch8_sps_cfg)}, | |
| 83 | − {MEI_PCI_DEVICE(MEI_DEV_ID_WPT_LP, mei_me_pch8_cfg)}, | |
| 84 | − {MEI_PCI_DEVICE(MEI_DEV_ID_WPT_LP_2, mei_me_pch8_cfg)}, | |
| 85 | − | |
| 86 | − {MEI_PCI_DEVICE(MEI_DEV_ID_SPT, mei_me_pch8_cfg)}, | |
| 87 | − {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_2, mei_me_pch8_cfg)}, | |
| 88 | − {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H, mei_me_pch8_sps_cfg)}, | |
| 89 | − {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H_2, mei_me_pch8_sps_cfg)}, | |
| 90 | − {MEI_PCI_DEVICE(MEI_DEV_ID_LBG, mei_me_pch8_cfg)}, | |
| 91 | − | |
| 92 | − {MEI_PCI_DEVICE(MEI_DEV_ID_BXT_M, mei_me_pch8_cfg)}, | |
| 93 | − {MEI_PCI_DEVICE(MEI_DEV_ID_APL_I, mei_me_pch8_cfg)}, | |
| 94 | − | |
| 95 | − {MEI_PCI_DEVICE(MEI_DEV_ID_KBP, mei_me_pch8_cfg)}, | |
| 96 | − {MEI_PCI_DEVICE(MEI_DEV_ID_KBP_2, mei_me_pch8_cfg)}, | |
| 46 | + {MEI_PCI_DEVICE(MEI_DEV_ID_82946GZ, MEI_ME_ICH_CFG)}, | |
| 47 | + {MEI_PCI_DEVICE(MEI_DEV_ID_82G35, MEI_ME_ICH_CFG)}, | |
| 48 | + {MEI_PCI_DEVICE(MEI_DEV_ID_82Q965, MEI_ME_ICH_CFG)}, | |
| 49 | + {MEI_PCI_DEVICE(MEI_DEV_ID_82G965, MEI_ME_ICH_CFG)}, | |
| 50 | + {MEI_PCI_DEVICE(MEI_DEV_ID_82GM965, MEI_ME_ICH_CFG)}, | |
| 51 | + {MEI_PCI_DEVICE(MEI_DEV_ID_82GME965, MEI_ME_ICH_CFG)}, | |
| 52 | + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82Q35, MEI_ME_ICH_CFG)}, | |
| 53 | + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82G33, MEI_ME_ICH_CFG)}, | |
| 54 | + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82Q33, MEI_ME_ICH_CFG)}, | |
| 55 | + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82X38, MEI_ME_ICH_CFG)}, | |
| 56 | + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_3200, MEI_ME_ICH_CFG)}, | |
| 57 | + | |
| 58 | + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_6, MEI_ME_ICH_CFG)}, | |
| 59 | + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_7, MEI_ME_ICH_CFG)}, | |
| 60 | + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_8, MEI_ME_ICH_CFG)}, | |
| 61 | + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_9, MEI_ME_ICH_CFG)}, | |
| 62 | + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_10, MEI_ME_ICH_CFG)}, | |
| 63 | + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_1, MEI_ME_ICH_CFG)}, | |
| 64 | + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_2, MEI_ME_ICH_CFG)}, | |
| 65 | + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_3, MEI_ME_ICH_CFG)}, | |
| 66 | + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_4, MEI_ME_ICH_CFG)}, | |
| 67 | + | |
| 68 | + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_1, MEI_ME_ICH10_CFG)}, | |
| 69 | + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_2, MEI_ME_ICH10_CFG)}, | |
| 70 | + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_3, MEI_ME_ICH10_CFG)}, | |
| 71 | + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_4, MEI_ME_ICH10_CFG)}, | |
| 72 | + | |
| 73 | + {MEI_PCI_DEVICE(MEI_DEV_ID_IBXPK_1, MEI_ME_PCH_CFG)}, | |
| 74 | + {MEI_PCI_DEVICE(MEI_DEV_ID_IBXPK_2, MEI_ME_PCH_CFG)}, | |
| 75 | + {MEI_PCI_DEVICE(MEI_DEV_ID_CPT_1, MEI_ME_PCH_CPT_PBG_CFG)}, | |
| 76 | + {MEI_PCI_DEVICE(MEI_DEV_ID_PBG_1, MEI_ME_PCH_CPT_PBG_CFG)}, | |
| 77 | + {MEI_PCI_DEVICE(MEI_DEV_ID_PPT_1, MEI_ME_PCH_CFG)}, | |
| 78 | + {MEI_PCI_DEVICE(MEI_DEV_ID_PPT_2, MEI_ME_PCH_CFG)}, | |
| 79 | + {MEI_PCI_DEVICE(MEI_DEV_ID_PPT_3, MEI_ME_PCH_CFG)}, | |
| 80 | + {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_H, MEI_ME_PCH8_SPS_CFG)}, | |
| 81 | + {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_W, MEI_ME_PCH8_SPS_CFG)}, | |
| 82 | + {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_LP, MEI_ME_PCH8_CFG)}, | |
| 83 | + {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_HR, MEI_ME_PCH8_SPS_CFG)}, | |
| 84 | + {MEI_PCI_DEVICE(MEI_DEV_ID_WPT_LP, MEI_ME_PCH8_CFG)}, | |
| 85 | + {MEI_PCI_DEVICE(MEI_DEV_ID_WPT_LP_2, MEI_ME_PCH8_CFG)}, | |
| 86 | + | |
| 87 | + {MEI_PCI_DEVICE(MEI_DEV_ID_SPT, MEI_ME_PCH8_CFG)}, | |
| 88 | + {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_2, MEI_ME_PCH8_CFG)}, | |
| 89 | + {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H, MEI_ME_PCH8_SPS_CFG)}, | |
| 90 | + {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H_2, MEI_ME_PCH8_SPS_CFG)}, | |
| 91 | + {MEI_PCI_DEVICE(MEI_DEV_ID_LBG, MEI_ME_PCH8_CFG)}, | |
| 92 | + | |
| 93 | + {MEI_PCI_DEVICE(MEI_DEV_ID_BXT_M, MEI_ME_PCH8_CFG)}, | |
| 94 | + {MEI_PCI_DEVICE(MEI_DEV_ID_APL_I, MEI_ME_PCH8_CFG)}, | |
| 95 | + | |
| 96 | + {MEI_PCI_DEVICE(MEI_DEV_ID_KBP, MEI_ME_PCH8_CFG)}, | |
| 97 | + {MEI_PCI_DEVICE(MEI_DEV_ID_KBP_2, MEI_ME_PCH8_CFG)}, | |
| 97 | 98 | |
| 98 | 99 | /* required last entry */ |
| 99 | 100 | {0, } |
| @@ -138,12 +139,15 @@ static bool mei_me_quirk_probe(struct pci_dev *pdev, · 38 unmodified lines | ||
| 138 | 139 | */ |
| 139 | 140 | static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) |
| 140 | 141 | { |
| 141 | − const struct mei_cfg *cfg = (struct mei_cfg *)(ent->driver_data); | |
| 142 | + const struct mei_cfg *cfg; | |
| 142 | 143 | struct mei_device *dev; |
| 143 | 144 | struct mei_me_hw *hw; |
| 144 | 145 | unsigned int irqflags; |
| 145 | 146 | int err; |
| 146 | 147 | |
| 148 | + cfg = mei_me_get_cfg(ent->driver_data); | |
| 149 | + if (!cfg) | |
| 150 | + return -ENODEV; | |
| 147 | 151 | |
| 148 | 152 | if (!mei_me_quirk_probe(pdev, cfg)) |
| 149 | 153 | return -ENODEV; |
| @@ -491,6 +495,7 @@ static struct pci_driver mei_me_driver = { · 341 unmodified lines | ||
| 491 | 495 | .remove = mei_me_remove, |
| 492 | 496 | .shutdown = mei_me_shutdown, |
| 493 | 497 | .driver.pm = MEI_ME_PM_OPS, |
| 498 | + .driver.probe_type = PROBE_PREFER_ASYNCHRONOUS, | |
| 494 | 499 | }; |
| 495 | 500 | |
| 496 | 501 | module_pci_driver(mei_me_driver); |
| @@ -659,7 +659,7 @@ static ssize_t store_pch_mac(struct device *dev, struct device_attribute *attr, · 658 unmodified lines | ||
| 659 | 659 | |
| 660 | 660 | static DEVICE_ATTR(pch_mac, S_IRUGO | S_IWUSR, show_pch_mac, store_pch_mac); |
| 661 | 661 | |
| 662 | −static struct bin_attribute pch_bin_attr = { | |
| 662 | +static const struct bin_attribute pch_bin_attr = { | |
| 663 | 663 | .attr = { |
| 664 | 664 | .name = "pch_firmware", |
| 665 | 665 | .mode = S_IRUGO | S_IWUSR, |
| @@ -891,7 +891,7 @@ static int pch_phub_resume(struct pci_dev *pdev) · 225 unmodified lines | ||
| 891 | 891 | #define pch_phub_resume NULL |
| 892 | 892 | #endif /* CONFIG_PM */ |
| 893 | 893 | |
| 894 | −static struct pci_device_id pch_phub_pcidev_id[] = { | |
| 894 | +static const struct pci_device_id pch_phub_pcidev_id[] = { | |
| 895 | 895 | { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PCH1_PHUB), 1, }, |
| 896 | 896 | { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ROHM_ML7213_PHUB), 2, }, |
| 897 | 897 | { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ROHM_ML7223_mPHUB), 3, }, |
| @@ -196,15 +196,15 @@ static int sram_reserve_regions(struct sram_dev *sram, struct resource *res) · 195 unmodified lines | ||
| 196 | 196 | ret = of_address_to_resource(child, 0, &child_res); |
| 197 | 197 | if (ret < 0) { |
| 198 | 198 | dev_err(sram->dev, |
| 199 | − "could not get address for node %s\n", | |
| 200 | − child->full_name); | |
| 199 | + "could not get address for node %pOF\n", | |
| 200 | + child); | |
| 201 | 201 | goto err_chunks; |
| 202 | 202 | } |
| 203 | 203 | |
| 204 | 204 | if (child_res.start < res->start || child_res.end > res->end) { |
| 205 | 205 | dev_err(sram->dev, |
| 206 | − "reserved block %s outside the sram area\n", | |
| 207 | − child->full_name); | |
| 206 | + "reserved block %pOF outside the sram area\n", | |
| 207 | + child); | |
| 208 | 208 | ret = -EINVAL; |
| 209 | 209 | goto err_chunks; |
| 210 | 210 | } |
| @@ -230,8 +230,8 @@ static int sram_reserve_regions(struct sram_dev *sram, struct resource *res) · 19 unmodified lines | ||
| 230 | 230 | ret = of_property_read_string(child, "label", &label); |
| 231 | 231 | if (ret && ret != -EINVAL) { |
| 232 | 232 | dev_err(sram->dev, |
| 233 | − "%s has invalid label name\n", | |
| 234 | − child->full_name); | |
| 233 | + "%pOF has invalid label name\n", | |
| 234 | + child); | |
| 235 | 235 | goto err_chunks; |
| 236 | 236 | } |
| 237 | 237 | if (!label) |
| @@ -660,7 +660,7 @@ static struct attribute *uim_attrs[] = { · 659 unmodified lines | ||
| 660 | 660 | NULL, |
| 661 | 661 | }; |
| 662 | 662 | |
| 663 | −static struct attribute_group uim_attr_grp = { | |
| 663 | +static const struct attribute_group uim_attr_grp = { | |
| 664 | 664 | .attrs = uim_attrs, |
| 665 | 665 | }; |
| 666 | 666 | |
| @@ -415,7 +415,7 @@ static void tifm_7xx1_remove(struct pci_dev *dev) · 414 unmodified lines | ||
| 415 | 415 | tifm_free_adapter(fm); |
| 416 | 416 | } |
| 417 | 417 | |
| 418 | −static struct pci_device_id tifm_7xx1_pci_tbl [] = { | |
| 418 | +static const struct pci_device_id tifm_7xx1_pci_tbl[] = { | |
| 419 | 419 | { PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_XX21_XX11_FM, PCI_ANY_ID, |
| 420 | 420 | PCI_ANY_ID, 0, 0, 0 }, /* xx21 - the one I have */ |
| 421 | 421 | { PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_XX12_FM, PCI_ANY_ID, |
| @@ -2235,14 +2235,8 @@ int vmci_qp_broker_detach(struct vmci_handle handle, struct vmci_ctx *context) · 2234 unmodified lines | ||
| 2235 | 2235 | handle.context, handle.resource, |
| 2236 | 2236 | result); |
| 2237 | 2237 | |
| 2238 | − if (entry->vmci_page_files) | |
| 2239 | − qp_host_unregister_user_memory(entry->produce_q, | |
| 2240 | − entry-> | |
| 2241 | − consume_q); | |
| 2242 | − else | |
| 2243 | − qp_host_unregister_user_memory(entry->produce_q, | |
| 2244 | − entry-> | |
| 2245 | − consume_q); | |
| 2238 | + qp_host_unregister_user_memory(entry->produce_q, | |
| 2239 | + entry->consume_q); | |
| 2246 | 2240 | |
| 2247 | 2241 | } |
| 2248 | 2242 | |
| @@ -2,6 +2,11 @@ · 1 unmodified line | ||
| 2 | 2 | # Makefile for multiplexer devices. |
| 3 | 3 | # |
| 4 | 4 | |
| 5 | +mux-core-objs := core.o | |
| 6 | +mux-adg792a-objs := adg792a.o | |
| 7 | +mux-gpio-objs := gpio.o | |
| 8 | +mux-mmio-objs := mmio.o | |
| 9 | + | |
| 5 | 10 | obj-$(CONFIG_MULTIPLEXER) += mux-core.o |
| 6 | 11 | obj-$(CONFIG_MUX_ADG792A) += mux-adg792a.o |
| 7 | 12 | obj-$(CONFIG_MUX_GPIO) += mux-gpio.o |
File renamed without changes.
| @@ -58,7 +58,7 @@ static void mux_chip_release(struct device *dev) · 57 unmodified lines | ||
| 58 | 58 | kfree(mux_chip); |
| 59 | 59 | } |
| 60 | 60 | |
| 61 | −static struct device_type mux_type = { | |
| 61 | +static const struct device_type mux_type = { | |
| 62 | 62 | .name = "mux-chip", |
| 63 | 63 | .release = mux_chip_release, |
| 64 | 64 | }; |
| @@ -452,8 +452,8 @@ struct mux_control *mux_control_get(struct device *dev, const char *mux_name) · 387 unmodified lines | ||
| 452 | 452 | "mux-controls", "#mux-control-cells", |
| 453 | 453 | index, &args); |
| 454 | 454 | if (ret) { |
| 455 | − dev_err(dev, "%s: failed to get mux-control %s(%i)\n", | |
| 456 | − np->full_name, mux_name ?: "", index); | |
| 455 | + dev_err(dev, "%pOF: failed to get mux-control %s(%i)\n", | |
| 456 | + np, mux_name ?: "", index); | |
| 457 | 457 | return ERR_PTR(ret); |
| 458 | 458 | } |
| 459 | 459 | |
| @@ -464,8 +464,8 @@ struct mux_control *mux_control_get(struct device *dev, const char *mux_name) · 4 unmodified lines | ||
| 464 | 464 | |
| 465 | 465 | if (args.args_count > 1 || |
| 466 | 466 | (!args.args_count && (mux_chip->controllers > 1))) { |
| 467 | − dev_err(dev, "%s: wrong #mux-control-cells for %s\n", | |
| 468 | − np->full_name, args.np->full_name); | |
| 467 | + dev_err(dev, "%pOF: wrong #mux-control-cells for %pOF\n", | |
| 468 | + np, args.np); | |
| 469 | 469 | return ERR_PTR(-EINVAL); |
| 470 | 470 | } |
| 471 | 471 | |
| @@ -474,8 +474,8 @@ struct mux_control *mux_control_get(struct device *dev, const char *mux_name) · 2 unmodified lines | ||
| 474 | 474 | controller = args.args[0]; |
| 475 | 475 | |
| 476 | 476 | if (controller >= mux_chip->controllers) { |
| 477 | − dev_err(dev, "%s: bad mux controller %u specified in %s\n", | |
| 478 | − np->full_name, controller, args.np->full_name); | |
| 477 | + dev_err(dev, "%pOF: bad mux controller %u specified in %pOF\n", | |
| 478 | + np, controller, args.np); | |
| 479 | 479 | return ERR_PTR(-EINVAL); |
| 480 | 480 | } |
| 481 | 481 | |
File renamed without changes.
File renamed without changes.
| @@ -290,7 +290,7 @@ static struct nvmem_cell *nvmem_find_cell(const char *cell_id) · 289 unmodified lines | ||
| 290 | 290 | mutex_lock(&nvmem_cells_mutex); |
| 291 | 291 | |
| 292 | 292 | list_for_each_entry(p, &nvmem_cells, node) |
| 293 | − if (p && !strcmp(p->name, cell_id)) { | |
| 293 | + if (!strcmp(p->name, cell_id)) { | |
| 294 | 294 | mutex_unlock(&nvmem_cells_mutex); |
| 295 | 295 | return p; |
| 296 | 296 | } |
| @@ -794,8 +794,8 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, · 497 unmodified lines | ||
| 794 | 794 | |
| 795 | 795 | addr = of_get_property(cell_np, "reg", &len); |
| 796 | 796 | if (!addr || (len < 2 * sizeof(u32))) { |
| 797 | − dev_err(&nvmem->dev, "nvmem: invalid reg on %s\n", | |
| 798 | − cell_np->full_name); | |
| 797 | + dev_err(&nvmem->dev, "nvmem: invalid reg on %pOF\n", | |
| 798 | + cell_np); | |
| 799 | 799 | rval = -EINVAL; |
| 800 | 800 | goto err_mem; |
| 801 | 801 | } |
| @@ -1110,6 +1110,43 @@ int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len) · 308 unmodified lines | ||
| 1110 | 1110 | } |
| 1111 | 1111 | EXPORT_SYMBOL_GPL(nvmem_cell_write); |
| 1112 | 1112 | |
| 1113 | +/** | |
| 1114 | + * nvmem_cell_read_u32() - Read a cell value as an u32 | |
| 1115 | + * | |
| 1116 | + * @dev: Device that requests the nvmem cell. | |
| 1117 | + * @cell_id: Name of nvmem cell to read. | |
| 1118 | + * @val: pointer to output value. | |
| 1119 | + * | |
| 1120 | + * Return: 0 on success or negative errno. | |
| 1121 | + */ | |
| 1122 | +int nvmem_cell_read_u32(struct device *dev, const char *cell_id, u32 *val) | |
| 1123 | +{ | |
| 1124 | + struct nvmem_cell *cell; | |
| 1125 | + void *buf; | |
| 1126 | + size_t len; | |
| 1127 | + | |
| 1128 | + cell = nvmem_cell_get(dev, cell_id); | |
| 1129 | + if (IS_ERR(cell)) | |
| 1130 | + return PTR_ERR(cell); | |
| 1131 | + | |
| 1132 | + buf = nvmem_cell_read(cell, &len); | |
| 1133 | + if (IS_ERR(buf)) { | |
| 1134 | + nvmem_cell_put(cell); | |
| 1135 | + return PTR_ERR(buf); | |
| 1136 | + } | |
| 1137 | + if (len != sizeof(*val)) { | |
| 1138 | + kfree(buf); | |
| 1139 | + nvmem_cell_put(cell); | |
| 1140 | + return -EINVAL; | |
| 1141 | + } | |
| 1142 | + memcpy(val, buf, sizeof(*val)); | |
| 1143 | + | |
| 1144 | + kfree(buf); | |
| 1145 | + nvmem_cell_put(cell); | |
| 1146 | + return 0; | |
| 1147 | +} | |
| 1148 | +EXPORT_SYMBOL_GPL(nvmem_cell_read_u32); | |
| 1149 | + | |
| 1113 | 1150 | /** |
| 1114 | 1151 | * nvmem_device_cell_read() - Read a given nvmem device and cell |
| 1115 | 1152 | * |
| @@ -197,7 +197,7 @@ static int lpc18xx_eeprom_probe(struct platform_device *pdev) · 196 unmodified lines | ||
| 197 | 197 | return ret; |
| 198 | 198 | } |
| 199 | 199 | |
| 200 | − rst = devm_reset_control_get(dev, NULL); | |
| 200 | + rst = devm_reset_control_get_exclusive(dev, NULL); | |
| 201 | 201 | if (IS_ERR(rst)) { |
| 202 | 202 | dev_err(dev, "failed to get reset: %ld\n", PTR_ERR(rst)); |
| 203 | 203 | ret = PTR_ERR(rst); |
| @@ -44,7 +44,7 @@ static struct daisydev { · 43 unmodified lines | ||
| 44 | 44 | } *topology = NULL; |
| 45 | 45 | static DEFINE_SPINLOCK(topology_lock); |
| 46 | 46 | |
| 47 | −static int numdevs = 0; | |
| 47 | +static int numdevs; | |
| 48 | 48 | |
| 49 | 49 | /* Forward-declaration of lower-level functions. */ |
| 50 | 50 | static int mux_present(struct parport *port); |
| @@ -18,7 +18,7 @@ · 17 unmodified lines | ||
| 18 | 18 | #include <asm/irq.h> |
| 19 | 19 | #include <asm/atariints.h> |
| 20 | 20 | |
| 21 | −static struct parport *this_port = NULL; | |
| 21 | +static struct parport *this_port; | |
| 22 | 22 | |
| 23 | 23 | static unsigned char |
| 24 | 24 | parport_atari_read_data(struct parport *p) |
| @@ -358,8 +358,7 @@ static int parport_ax88796_probe(struct platform_device *pdev) · 357 unmodified lines | ||
| 358 | 358 | exit_unmap: |
| 359 | 359 | iounmap(dd->base); |
| 360 | 360 | exit_res: |
| 361 | − release_resource(dd->io); | |
| 362 | − kfree(dd->io); | |
| 361 | + release_mem_region(dd->io->start, size); | |
| 363 | 362 | exit_mem: |
| 364 | 363 | kfree(dd); |
| 365 | 364 | return ret; |
| @@ -373,8 +372,7 @@ static int parport_ax88796_remove(struct platform_device *pdev) · 7 unmodified lines | ||
| 373 | 372 | free_irq(p->irq, p); |
| 374 | 373 | parport_remove_port(p); |
| 375 | 374 | iounmap(dd->base); |
| 376 | − release_resource(dd->io); | |
| 377 | − kfree(dd->io); | |
| 375 | + release_mem_region(dd->io->start, resource_size(dd->io)); | |
| 378 | 376 | kfree(dd); |
| 379 | 377 | |
| 380 | 378 | return 0; |
| @@ -138,7 +138,7 @@ static unsigned int features = ~0U; · 137 unmodified lines | ||
| 138 | 138 | static bool verbose_probing = DEFAULT_VERBOSE_PROBING; |
| 139 | 139 | |
| 140 | 140 | /* We do not support more than one port. */ |
| 141 | −static struct parport *this_port = NULL; | |
| 141 | +static struct parport *this_port; | |
| 142 | 142 | |
| 143 | 143 | /* Timing constants for FIFO modes. */ |
| 144 | 144 | #define FIFO_NFAULT_TIMEOUT 100 /* milliseconds */ |
| @@ -174,7 +174,7 @@ DPRINTK(KERN_DEBUG "read_status %02x\n", status); · 173 unmodified lines | ||
| 174 | 174 | return status; |
| 175 | 175 | } |
| 176 | 176 | |
| 177 | −static int use_cnt = 0; | |
| 177 | +static int use_cnt; | |
| 178 | 178 | |
| 179 | 179 | static irqreturn_t mfc3_interrupt(int irq, void *dev_id) |
| 180 | 180 | { |
| @@ -1083,9 +1083,9 @@ static void show_parconfig_winbond(int io, int key) · 1082 unmodified lines | ||
| 1083 | 1083 | printk(KERN_INFO "Winbond LPT Config: active=%s, io=0x%02x%02x irq=%d, ", |
| 1084 | 1084 | (cr30 & 0x01) ? "yes" : "no", cr60, cr61, cr70 & 0x0f); |
| 1085 | 1085 | if ((cr74 & 0x07) > 3) |
| 1086 | − printk("dma=none\n"); | |
| 1086 | + pr_cont("dma=none\n"); | |
| 1087 | 1087 | else |
| 1088 | − printk("dma=%d\n", cr74 & 0x07); | |
| 1088 | + pr_cont("dma=%d\n", cr74 & 0x07); | |
| 1089 | 1089 | printk(KERN_INFO |
| 1090 | 1090 | "Winbond LPT Config: irqtype=%s, ECP fifo threshold=%d\n", |
| 1091 | 1091 | irqtypes[crf0>>7], (crf0>>3)&0x0f); |
| @@ -1685,14 +1685,14 @@ static int parport_ECP_supported(struct parport *pb) · 593 unmodified lines | ||
| 1685 | 1685 | pb->base, config, configb); |
| 1686 | 1686 | printk(KERN_DEBUG "0x%lx: ECP settings irq=", pb->base); |
| 1687 | 1687 | if ((configb >> 3) & 0x07) |
| 1688 | − printk("%d", intrline[(configb >> 3) & 0x07]); | |
| 1688 | + pr_cont("%d", intrline[(configb >> 3) & 0x07]); | |
| 1689 | 1689 | else |
| 1690 | − printk("<none or set by other means>"); | |
| 1691 | − printk(" dma="); | |
| 1690 | + pr_cont("<none or set by other means>"); | |
| 1691 | + pr_cont(" dma="); | |
| 1692 | 1692 | if ((configb & 0x03) == 0x00) |
| 1693 | − printk("<none or set by other means>\n"); | |
| 1693 | + pr_cont("<none or set by other means>\n"); | |
| 1694 | 1694 | else |
| 1695 | − printk("%d\n", configb & 0x07); | |
| 1695 | + pr_cont("%d\n", configb & 0x07); | |
| 1696 | 1696 | } |
| 1697 | 1697 | |
| 1698 | 1698 | /* Go back to mode 000 */ |
| @@ -2399,8 +2399,8 @@ static int sio_ite_8872_probe(struct pci_dev *pdev, int autoirq, int autodma, · 700 unmodified lines | ||
| 2399 | 2399 | "parport_pc: ITE 8872 parallel port: io=0x%X", |
| 2400 | 2400 | ite8872_lpt); |
| 2401 | 2401 | if (irq != PARPORT_IRQ_NONE) |
| 2402 | − printk(", irq=%d", irq); | |
| 2403 | − printk("\n"); | |
| 2402 | + pr_cont(", irq=%d", irq); | |
| 2403 | + pr_cont("\n"); | |
| 2404 | 2404 | return 1; |
| 2405 | 2405 | } |
| 2406 | 2406 | |
| @@ -2581,10 +2581,10 @@ static int sio_via_probe(struct pci_dev *pdev, int autoirq, int autodma, · 174 unmodified lines | ||
| 2581 | 2581 | printk(KERN_INFO |
| 2582 | 2582 | "parport_pc: VIA parallel port: io=0x%X", port1); |
| 2583 | 2583 | if (irq != PARPORT_IRQ_NONE) |
| 2584 | − printk(", irq=%d", irq); | |
| 2584 | + pr_cont(", irq=%d", irq); | |
| 2585 | 2585 | if (dma != PARPORT_DMA_NONE) |
| 2586 | − printk(", dma=%d", dma); | |
| 2587 | − printk("\n"); | |
| 2586 | + pr_cont(", dma=%d", dma); | |
| 2587 | + pr_cont("\n"); | |
| 2588 | 2588 | return 1; |
| 2589 | 2589 | } |
| 2590 | 2590 | |
| @@ -253,16 +253,16 @@ static void extcon_work(struct work_struct *work) · 252 unmodified lines | ||
| 253 | 253 | vbus = gpiod_get_value_cansleep(driver->vbus_gpiod); |
| 254 | 254 | |
| 255 | 255 | if (!id && vbus) { /* Host connected */ |
| 256 | − extcon_set_cable_state_(driver->edev, EXTCON_USB_HOST, true); | |
| 256 | + extcon_set_state_sync(driver->edev, EXTCON_USB_HOST, true); | |
| 257 | 257 | pr_debug("Host cable connected\n"); |
| 258 | 258 | driver->data->new_state = EVT_HOST; |
| 259 | 259 | connect_change(driver); |
| 260 | 260 | } else if (id && !vbus) { /* Disconnected */ |
| 261 | − extcon_set_cable_state_(driver->edev, EXTCON_USB_HOST, false); | |
| 262 | − extcon_set_cable_state_(driver->edev, EXTCON_USB, false); | |
| 261 | + extcon_set_state_sync(driver->edev, EXTCON_USB_HOST, false); | |
| 262 | + extcon_set_state_sync(driver->edev, EXTCON_USB, false); | |
| 263 | 263 | pr_debug("Cable disconnected\n"); |
| 264 | 264 | } else if (id && vbus) { /* Device connected */ |
| 265 | − extcon_set_cable_state_(driver->edev, EXTCON_USB, true); | |
| 265 | + extcon_set_state_sync(driver->edev, EXTCON_USB, true); | |
| 266 | 266 | pr_debug("Device cable connected\n"); |
| 267 | 267 | driver->data->new_state = EVT_DEVICE; |
| 268 | 268 | connect_change(driver); |
| @@ -155,12 +155,12 @@ static int qcom_usb_hs_phy_power_on(struct phy *phy) · 154 unmodified lines | ||
| 155 | 155 | } |
| 156 | 156 | |
| 157 | 157 | if (uphy->vbus_edev) { |
| 158 | − state = extcon_get_cable_state_(uphy->vbus_edev, EXTCON_USB); | |
| 158 | + state = extcon_get_state(uphy->vbus_edev, EXTCON_USB); | |
| 159 | 159 | /* setup initial state */ |
| 160 | 160 | qcom_usb_hs_phy_vbus_notifier(&uphy->vbus_notify, state, |
| 161 | 161 | uphy->vbus_edev); |
| 162 | − ret = extcon_register_notifier(uphy->vbus_edev, EXTCON_USB, | |
| 163 | − &uphy->vbus_notify); | |
| 162 | + ret = devm_extcon_register_notifier(&ulpi->dev, uphy->vbus_edev, | |
| 163 | + EXTCON_USB, &uphy->vbus_notify); | |
| 164 | 164 | if (ret) |
| 165 | 165 | goto err_ulpi; |
| 166 | 166 | } |
| @@ -179,16 +179,8 @@ err_sleep: · 12 unmodified lines | ||
| 179 | 179 | |
| 180 | 180 | static int qcom_usb_hs_phy_power_off(struct phy *phy) |
| 181 | 181 | { |
| 182 | − int ret; | |
| 183 | 182 | struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy); |
| 184 | 183 | |
| 185 | − if (uphy->vbus_edev) { | |
| 186 | − ret = extcon_unregister_notifier(uphy->vbus_edev, EXTCON_USB, | |
| 187 | − &uphy->vbus_notify); | |
| 188 | − if (ret) | |
| 189 | − return ret; | |
| 190 | − } | |
| 191 | − | |
| 192 | 184 | regulator_disable(uphy->v3p3); |
| 193 | 185 | regulator_disable(uphy->v1p8); |
| 194 | 186 | clk_disable_unprepare(uphy->sleep_clk); |
| @@ -562,7 +562,7 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) · 561 unmodified lines | ||
| 562 | 562 | rockchip_usb2phy_power_off(rport->phy); |
| 563 | 563 | /* fall through */ |
| 564 | 564 | case OTG_STATE_B_IDLE: |
| 565 | − if (extcon_get_cable_state_(rphy->edev, EXTCON_USB_HOST) > 0) { | |
| 565 | + if (extcon_get_state(rphy->edev, EXTCON_USB_HOST) > 0) { | |
| 566 | 566 | dev_dbg(&rport->phy->dev, "usb otg host connect\n"); |
| 567 | 567 | rport->state = OTG_STATE_A_HOST; |
| 568 | 568 | rockchip_usb2phy_power_on(rport->phy); |
| @@ -615,7 +615,7 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) · 46 unmodified lines | ||
| 615 | 615 | rport->vbus_attached = vbus_attach; |
| 616 | 616 | |
| 617 | 617 | if (notify_charger && rphy->edev) { |
| 618 | − extcon_set_cable_state_(rphy->edev, | |
| 618 | + extcon_set_state_sync(rphy->edev, | |
| 619 | 619 | cable, vbus_attach); |
| 620 | 620 | if (cable == EXTCON_CHG_USB_SDP) |
| 621 | 621 | extcon_set_state_sync(rphy->edev, |
| @@ -636,7 +636,7 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) · 14 unmodified lines | ||
| 636 | 636 | sch_work = true; |
| 637 | 637 | break; |
| 638 | 638 | case OTG_STATE_A_HOST: |
| 639 | − if (extcon_get_cable_state_(rphy->edev, EXTCON_USB_HOST) == 0) { | |
| 639 | + if (extcon_get_state(rphy->edev, EXTCON_USB_HOST) == 0) { | |
| 640 | 640 | dev_dbg(&rport->phy->dev, "usb otg host disconnect\n"); |
| 641 | 641 | rport->state = OTG_STATE_B_IDLE; |
| 642 | 642 | rockchip_usb2phy_power_off(rport->phy); |
| @@ -1066,8 +1066,8 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, · 423 unmodified lines | ||
| 1066 | 1066 | if (!IS_ERR(rphy->edev)) { |
| 1067 | 1067 | rport->event_nb.notifier_call = rockchip_otg_event; |
| 1068 | 1068 | |
| 1069 | − ret = extcon_register_notifier(rphy->edev, EXTCON_USB_HOST, | |
| 1070 | − &rport->event_nb); | |
| 1069 | + ret = devm_extcon_register_notifier(rphy->dev, rphy->edev, | |
| 1070 | + EXTCON_USB_HOST, &rport->event_nb); | |
| 1071 | 1071 | if (ret) |
| 1072 | 1072 | dev_err(rphy->dev, "register USB HOST notifier failed\n"); |
| 1073 | 1073 | } |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | /* |
| 2 | − * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. | |
| 2 | + * Copyright (c) 2012-2015, 2017, The Linux Foundation. All rights reserved. | |
| 3 | 3 | * |
| 4 | 4 | * This program is free software; you can redistribute it and/or modify |
| 5 | 5 | * it under the terms of the GNU General Public License version 2 and |
| @@ -29,6 +29,7 @@ · 23 unmodified lines | ||
| 29 | 29 | #define PMIC_ARB_VERSION 0x0000 |
| 30 | 30 | #define PMIC_ARB_VERSION_V2_MIN 0x20010000 |
| 31 | 31 | #define PMIC_ARB_VERSION_V3_MIN 0x30000000 |
| 32 | +#define PMIC_ARB_VERSION_V5_MIN 0x50000000 | |
| 32 | 33 | #define PMIC_ARB_INT_EN 0x0004 |
| 33 | 34 | |
| 34 | 35 | /* PMIC Arbiter channel registers offsets */ |
| @@ -39,7 +40,6 @@ · 4 unmodified lines | ||
| 39 | 40 | #define PMIC_ARB_WDATA1 0x14 |
| 40 | 41 | #define PMIC_ARB_RDATA0 0x18 |
| 41 | 42 | #define PMIC_ARB_RDATA1 0x1C |
| 42 | −#define PMIC_ARB_REG_CHNL(N) (0x800 + 0x4 * (N)) | |
| 43 | 43 | |
| 44 | 44 | /* Mapping Table */ |
| 45 | 45 | #define SPMI_MAPPING_TABLE_REG(N) (0x0B00 + (4 * (N))) |
| @@ -51,7 +51,9 @@ · 5 unmodified lines | ||
| 51 | 51 | |
| 52 | 52 | #define SPMI_MAPPING_TABLE_TREE_DEPTH 16 /* Maximum of 16-bits */ |
| 53 | 53 | #define PMIC_ARB_MAX_PPID BIT(12) /* PPID is 12bit */ |
| 54 | −#define PMIC_ARB_CHAN_VALID BIT(15) | |
| 54 | +#define PMIC_ARB_APID_VALID BIT(15) | |
| 55 | +#define PMIC_ARB_CHAN_IS_IRQ_OWNER(reg) ((reg) & BIT(24)) | |
| 56 | +#define INVALID_EE 0xFF | |
| 55 | 57 | |
| 56 | 58 | /* Ownership Table */ |
| 57 | 59 | #define SPMI_OWNERSHIP_TABLE_REG(N) (0x0700 + (4 * (N))) |
| @@ -86,6 +88,15 @@ enum pmic_arb_cmd_op_code { · 28 unmodified lines | ||
| 86 | 88 | PMIC_ARB_OP_ZERO_WRITE = 16, |
| 87 | 89 | }; |
| 88 | 90 | |
| 91 | +/* | |
| 92 | + * PMIC arbiter version 5 uses different register offsets for read/write vs | |
| 93 | + * observer channels. | |
| 94 | + */ | |
| 95 | +enum pmic_arb_channel { | |
| 96 | + PMIC_ARB_CHANNEL_RW, | |
| 97 | + PMIC_ARB_CHANNEL_OBS, | |
| 98 | +}; | |
| 99 | + | |
| 89 | 100 | /* Maximum number of support PMIC peripherals */ |
| 90 | 101 | #define PMIC_ARB_MAX_PERIPHS 512 |
| 91 | 102 | #define PMIC_ARB_TIMEOUT_US 100 |
| @@ -97,22 +108,23 @@ enum pmic_arb_cmd_op_code { · 5 unmodified lines | ||
| 97 | 108 | /* interrupt enable bit */ |
| 98 | 109 | #define SPMI_PIC_ACC_ENABLE_BIT BIT(0) |
| 99 | 110 | |
| 100 | −#define HWIRQ(slave_id, periph_id, irq_id, apid) \ | |
| 111 | +#define spec_to_hwirq(slave_id, periph_id, irq_id, apid) \ | |
| 101 | 112 | ((((slave_id) & 0xF) << 28) | \ |
| 102 | 113 | (((periph_id) & 0xFF) << 20) | \ |
| 103 | 114 | (((irq_id) & 0x7) << 16) | \ |
| 104 | 115 | (((apid) & 0x1FF) << 0)) |
| 105 | 116 | |
| 106 | −#define HWIRQ_SID(hwirq) (((hwirq) >> 28) & 0xF) | |
| 107 | −#define HWIRQ_PER(hwirq) (((hwirq) >> 20) & 0xFF) | |
| 108 | −#define HWIRQ_IRQ(hwirq) (((hwirq) >> 16) & 0x7) | |
| 109 | −#define HWIRQ_APID(hwirq) (((hwirq) >> 0) & 0x1FF) | |
| 117 | +#define hwirq_to_sid(hwirq) (((hwirq) >> 28) & 0xF) | |
| 118 | +#define hwirq_to_per(hwirq) (((hwirq) >> 20) & 0xFF) | |
| 119 | +#define hwirq_to_irq(hwirq) (((hwirq) >> 16) & 0x7) | |
| 120 | +#define hwirq_to_apid(hwirq) (((hwirq) >> 0) & 0x1FF) | |
| 110 | 121 | |
| 111 | 122 | struct pmic_arb_ver_ops; |
| 112 | 123 | |
| 113 | 124 | struct apid_data { |
| 114 | 125 | u16 ppid; |
| 115 | − u8 owner; | |
| 126 | + u8 write_ee; | |
| 127 | + u8 irq_ee; | |
| 116 | 128 | }; |
| 117 | 129 | |
| 118 | 130 | /** |
| @@ -128,12 +140,11 @@ struct apid_data { · 9 unmodified lines | ||
| 128 | 140 | * @ee: the current Execution Environment |
| 129 | 141 | * @min_apid: minimum APID (used for bounding IRQ search) |
| 130 | 142 | * @max_apid: maximum APID |
| 131 | − * @max_periph: maximum number of PMIC peripherals supported by HW. | |
| 132 | 143 | * @mapping_table: in-memory copy of PPID -> APID mapping table. |
| 133 | 144 | * @domain: irq domain object for PMIC IRQ domain |
| 134 | 145 | * @spmic: SPMI controller object |
| 135 | 146 | * @ver_ops: version dependent operations. |
| 136 | − * @ppid_to_apid in-memory copy of PPID -> channel (APID) mapping table. | |
| 147 | + * @ppid_to_apid in-memory copy of PPID -> APID mapping table. | |
| 137 | 148 | */ |
| 138 | 149 | struct spmi_pmic_arb { |
| 139 | 150 | void __iomem *rd_base; |
| @@ -148,7 +159,6 @@ struct spmi_pmic_arb { · 8 unmodified lines | ||
| 148 | 159 | u8 ee; |
| 149 | 160 | u16 min_apid; |
| 150 | 161 | u16 max_apid; |
| 151 | − u16 max_periph; | |
| 152 | 162 | u32 *mapping_table; |
| 153 | 163 | DECLARE_BITMAP(mapping_table_valid, PMIC_ARB_MAX_PERIPHS); |
| 154 | 164 | struct irq_domain *domain; |
| @@ -164,92 +174,94 @@ struct spmi_pmic_arb { · 9 unmodified lines | ||
| 164 | 174 | * |
| 165 | 175 | * @ver_str: version string. |
| 166 | 176 | * @ppid_to_apid: finds the apid for a given ppid. |
| 167 | − * @mode: access rights to specified pmic peripheral. | |
| 168 | 177 | * @non_data_cmd: on v1 issues an spmi non-data command. |
| 169 | 178 | * on v2 no HW support, returns -EOPNOTSUPP. |
| 170 | 179 | * @offset: on v1 offset of per-ee channel. |
| 171 | 180 | * on v2 offset of per-ee and per-ppid channel. |
| 172 | 181 | * @fmt_cmd: formats a GENI/SPMI command. |
| 173 | − * @owner_acc_status: on v1 offset of PMIC_ARB_SPMI_PIC_OWNERm_ACC_STATUSn | |
| 174 | − * on v2 offset of SPMI_PIC_OWNERm_ACC_STATUSn. | |
| 175 | − * @acc_enable: on v1 offset of PMIC_ARB_SPMI_PIC_ACC_ENABLEn | |
| 176 | − * on v2 offset of SPMI_PIC_ACC_ENABLEn. | |
| 177 | − * @irq_status: on v1 offset of PMIC_ARB_SPMI_PIC_IRQ_STATUSn | |
| 178 | − * on v2 offset of SPMI_PIC_IRQ_STATUSn. | |
| 179 | − * @irq_clear: on v1 offset of PMIC_ARB_SPMI_PIC_IRQ_CLEARn | |
| 180 | − * on v2 offset of SPMI_PIC_IRQ_CLEARn. | |
| 182 | + * @owner_acc_status: on v1 address of PMIC_ARB_SPMI_PIC_OWNERm_ACC_STATUSn | |
| 183 | + * on v2 address of SPMI_PIC_OWNERm_ACC_STATUSn. | |
| 184 | + * @acc_enable: on v1 address of PMIC_ARB_SPMI_PIC_ACC_ENABLEn | |
| 185 | + * on v2 address of SPMI_PIC_ACC_ENABLEn. | |
| 186 | + * @irq_status: on v1 address of PMIC_ARB_SPMI_PIC_IRQ_STATUSn | |
| 187 | + * on v2 address of SPMI_PIC_IRQ_STATUSn. | |
| 188 | + * @irq_clear: on v1 address of PMIC_ARB_SPMI_PIC_IRQ_CLEARn | |
| 189 | + * on v2 address of SPMI_PIC_IRQ_CLEARn. | |
| 190 | + * @apid_map_offset: offset of PMIC_ARB_REG_CHNLn | |
| 181 | 191 | */ |
| 182 | 192 | struct pmic_arb_ver_ops { |
| 183 | 193 | const char *ver_str; |
| 184 | − int (*ppid_to_apid)(struct spmi_pmic_arb *pa, u8 sid, u16 addr, | |
| 185 | − u16 *apid); | |
| 186 | − int (*mode)(struct spmi_pmic_arb *dev, u8 sid, u16 addr, | |
| 187 | − mode_t *mode); | |
| 194 | + int (*ppid_to_apid)(struct spmi_pmic_arb *pmic_arb, u16 ppid); | |
| 188 | 195 | /* spmi commands (read_cmd, write_cmd, cmd) functionality */ |
| 189 | − int (*offset)(struct spmi_pmic_arb *dev, u8 sid, u16 addr, | |
| 190 | − u32 *offset); | |
| 196 | + int (*offset)(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr, | |
| 197 | + enum pmic_arb_channel ch_type); | |
| 191 | 198 | u32 (*fmt_cmd)(u8 opc, u8 sid, u16 addr, u8 bc); |
| 192 | 199 | int (*non_data_cmd)(struct spmi_controller *ctrl, u8 opc, u8 sid); |
| 193 | 200 | /* Interrupts controller functionality (offset of PIC registers) */ |
| 194 | − u32 (*owner_acc_status)(u8 m, u16 n); | |
| 195 | − u32 (*acc_enable)(u16 n); | |
| 196 | − u32 (*irq_status)(u16 n); | |
| 197 | − u32 (*irq_clear)(u16 n); | |
| 201 | + void __iomem *(*owner_acc_status)(struct spmi_pmic_arb *pmic_arb, u8 m, | |
| 202 | + u16 n); | |
| 203 | + void __iomem *(*acc_enable)(struct spmi_pmic_arb *pmic_arb, u16 n); | |
| 204 | + void __iomem *(*irq_status)(struct spmi_pmic_arb *pmic_arb, u16 n); | |
| 205 | + void __iomem *(*irq_clear)(struct spmi_pmic_arb *pmic_arb, u16 n); | |
| 206 | + u32 (*apid_map_offset)(u16 n); | |
| 198 | 207 | }; |
| 199 | 208 | |
| 200 | −static inline void pmic_arb_base_write(struct spmi_pmic_arb *pa, | |
| 209 | +static inline void pmic_arb_base_write(struct spmi_pmic_arb *pmic_arb, | |
| 201 | 210 | u32 offset, u32 val) |
| 202 | 211 | { |
| 203 | − writel_relaxed(val, pa->wr_base + offset); | |
| 212 | + writel_relaxed(val, pmic_arb->wr_base + offset); | |
| 204 | 213 | } |
| 205 | 214 | |
| 206 | −static inline void pmic_arb_set_rd_cmd(struct spmi_pmic_arb *pa, | |
| 215 | +static inline void pmic_arb_set_rd_cmd(struct spmi_pmic_arb *pmic_arb, | |
| 207 | 216 | u32 offset, u32 val) |
| 208 | 217 | { |
| 209 | − writel_relaxed(val, pa->rd_base + offset); | |
| 218 | + writel_relaxed(val, pmic_arb->rd_base + offset); | |
| 210 | 219 | } |
| 211 | 220 | |
| 212 | 221 | /** |
| 213 | − * pa_read_data: reads pmic-arb's register and copy 1..4 bytes to buf | |
| 222 | + * pmic_arb_read_data: reads pmic-arb's register and copy 1..4 bytes to buf | |
| 214 | 223 | * @bc: byte count -1. range: 0..3 |
| 215 | 224 | * @reg: register's address |
| 216 | 225 | * @buf: output parameter, length must be bc + 1 |
| 217 | 226 | */ |
| 218 | −static void pa_read_data(struct spmi_pmic_arb *pa, u8 *buf, u32 reg, u8 bc) | |
| 227 | +static void | |
| 228 | +pmic_arb_read_data(struct spmi_pmic_arb *pmic_arb, u8 *buf, u32 reg, u8 bc) | |
| 219 | 229 | { |
| 220 | − u32 data = __raw_readl(pa->rd_base + reg); | |
| 230 | + u32 data = __raw_readl(pmic_arb->rd_base + reg); | |
| 221 | 231 | |
| 222 | 232 | memcpy(buf, &data, (bc & 3) + 1); |
| 223 | 233 | } |
| 224 | 234 | |
| 225 | 235 | /** |
| 226 | − * pa_write_data: write 1..4 bytes from buf to pmic-arb's register | |
| 236 | + * pmic_arb_write_data: write 1..4 bytes from buf to pmic-arb's register | |
| 227 | 237 | * @bc: byte-count -1. range: 0..3. |
| 228 | 238 | * @reg: register's address. |
| 229 | 239 | * @buf: buffer to write. length must be bc + 1. |
| 230 | 240 | */ |
| 231 | −static void | |
| 232 | −pa_write_data(struct spmi_pmic_arb *pa, const u8 *buf, u32 reg, u8 bc) | |
| 241 | +static void pmic_arb_write_data(struct spmi_pmic_arb *pmic_arb, const u8 *buf, | |
| 242 | + u32 reg, u8 bc) | |
| 233 | 243 | { |
| 234 | 244 | u32 data = 0; |
| 235 | 245 | |
| 236 | 246 | memcpy(&data, buf, (bc & 3) + 1); |
| 237 | − pmic_arb_base_write(pa, reg, data); | |
| 247 | + __raw_writel(data, pmic_arb->wr_base + reg); | |
| 238 | 248 | } |
| 239 | 249 | |
| 240 | 250 | static int pmic_arb_wait_for_done(struct spmi_controller *ctrl, |
| 241 | − void __iomem *base, u8 sid, u16 addr) | |
| 251 | + void __iomem *base, u8 sid, u16 addr, | |
| 252 | + enum pmic_arb_channel ch_type) | |
| 242 | 253 | { |
| 243 | − struct spmi_pmic_arb *pa = spmi_controller_get_drvdata(ctrl); | |
| 254 | + struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); | |
| 244 | 255 | u32 status = 0; |
| 245 | 256 | u32 timeout = PMIC_ARB_TIMEOUT_US; |
| 246 | 257 | u32 offset; |
| 247 | 258 | int rc; |
| 248 | 259 | |
| 249 | − rc = pa->ver_ops->offset(pa, sid, addr, &offset); | |
| 250 | − if (rc) | |
| 260 | + rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr, ch_type); | |
| 261 | + if (rc < 0) | |
| 251 | 262 | return rc; |
| 252 | 263 | |
| 264 | + offset = rc; | |
| 253 | 265 | offset += PMIC_ARB_STATUS; |
| 254 | 266 | |
| 255 | 267 | while (timeout--) { |
| @@ -257,22 +269,19 @@ static int pmic_arb_wait_for_done(struct spmi_controller *ctrl, · 1 unmodified line | ||
| 257 | 269 | |
| 258 | 270 | if (status & PMIC_ARB_STATUS_DONE) { |
| 259 | 271 | if (status & PMIC_ARB_STATUS_DENIED) { |
| 260 | − dev_err(&ctrl->dev, | |
| 261 | − "%s: transaction denied (0x%x)\n", | |
| 272 | + dev_err(&ctrl->dev, "%s: transaction denied (0x%x)\n", | |
| 262 | 273 | __func__, status); |
| 263 | 274 | return -EPERM; |
| 264 | 275 | } |
| 265 | 276 | |
| 266 | 277 | if (status & PMIC_ARB_STATUS_FAILURE) { |
| 267 | − dev_err(&ctrl->dev, | |
| 268 | − "%s: transaction failed (0x%x)\n", | |
| 278 | + dev_err(&ctrl->dev, "%s: transaction failed (0x%x)\n", | |
| 269 | 279 | __func__, status); |
| 270 | 280 | return -EIO; |
| 271 | 281 | } |
| 272 | 282 | |
| 273 | 283 | if (status & PMIC_ARB_STATUS_DROPPED) { |
| 274 | − dev_err(&ctrl->dev, | |
| 275 | − "%s: transaction dropped (0x%x)\n", | |
| 284 | + dev_err(&ctrl->dev, "%s: transaction dropped (0x%x)\n", | |
| 276 | 285 | __func__, status); |
| 277 | 286 | return -EIO; |
| 278 | 287 | } |
| @@ -282,8 +291,7 @@ static int pmic_arb_wait_for_done(struct spmi_controller *ctrl, · 3 unmodified lines | ||
| 282 | 291 | udelay(1); |
| 283 | 292 | } |
| 284 | 293 | |
| 285 | − dev_err(&ctrl->dev, | |
| 286 | − "%s: timeout, status 0x%x\n", | |
| 294 | + dev_err(&ctrl->dev, "%s: timeout, status 0x%x\n", | |
| 287 | 295 | __func__, status); |
| 288 | 296 | return -ETIMEDOUT; |
| 289 | 297 | } |
| @@ -291,22 +299,24 @@ static int pmic_arb_wait_for_done(struct spmi_controller *ctrl, · 1 unmodified line | ||
| 291 | 299 | static int |
| 292 | 300 | pmic_arb_non_data_cmd_v1(struct spmi_controller *ctrl, u8 opc, u8 sid) |
| 293 | 301 | { |
| 294 | − struct spmi_pmic_arb *pa = spmi_controller_get_drvdata(ctrl); | |
| 302 | + struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); | |
| 295 | 303 | unsigned long flags; |
| 296 | 304 | u32 cmd; |
| 297 | 305 | int rc; |
| 298 | 306 | u32 offset; |
| 299 | 307 | |
| 300 | − rc = pa->ver_ops->offset(pa, sid, 0, &offset); | |
| 301 | − if (rc) | |
| 308 | + rc = pmic_arb->ver_ops->offset(pmic_arb, sid, 0, PMIC_ARB_CHANNEL_RW); | |
| 309 | + if (rc < 0) | |
| 302 | 310 | return rc; |
| 303 | 311 | |
| 312 | + offset = rc; | |
| 304 | 313 | cmd = ((opc | 0x40) << 27) | ((sid & 0xf) << 20); |
| 305 | 314 | |
| 306 | − raw_spin_lock_irqsave(&pa->lock, flags); | |
| 307 | − pmic_arb_base_write(pa, offset + PMIC_ARB_CMD, cmd); | |
| 308 | − rc = pmic_arb_wait_for_done(ctrl, pa->wr_base, sid, 0); | |
| 309 | − raw_spin_unlock_irqrestore(&pa->lock, flags); | |
| 315 | + raw_spin_lock_irqsave(&pmic_arb->lock, flags); | |
| 316 | + pmic_arb_base_write(pmic_arb, offset + PMIC_ARB_CMD, cmd); | |
| 317 | + rc = pmic_arb_wait_for_done(ctrl, pmic_arb->wr_base, sid, 0, | |
| 318 | + PMIC_ARB_CHANNEL_RW); | |
| 319 | + raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); | |
| 310 | 320 | |
| 311 | 321 | return rc; |
| 312 | 322 | } |
| @@ -320,7 +330,7 @@ pmic_arb_non_data_cmd_v2(struct spmi_controller *ctrl, u8 opc, u8 sid) · 7 unmodified lines | ||
| 320 | 330 | /* Non-data command */ |
| 321 | 331 | static int pmic_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid) |
| 322 | 332 | { |
| 323 | − struct spmi_pmic_arb *pa = spmi_controller_get_drvdata(ctrl); | |
| 333 | + struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); | |
| 324 | 334 | |
| 325 | 335 | dev_dbg(&ctrl->dev, "cmd op:0x%x sid:%d\n", opc, sid); |
| 326 | 336 | |
| @@ -328,38 +338,27 @@ static int pmic_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid) · 1 unmodified line | ||
| 328 | 338 | if (opc < SPMI_CMD_RESET || opc > SPMI_CMD_WAKEUP) |
| 329 | 339 | return -EINVAL; |
| 330 | 340 | |
| 331 | − return pa->ver_ops->non_data_cmd(ctrl, opc, sid); | |
| 341 | + return pmic_arb->ver_ops->non_data_cmd(ctrl, opc, sid); | |
| 332 | 342 | } |
| 333 | 343 | |
| 334 | 344 | static int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, |
| 335 | 345 | u16 addr, u8 *buf, size_t len) |
| 336 | 346 | { |
| 337 | − struct spmi_pmic_arb *pa = spmi_controller_get_drvdata(ctrl); | |
| 347 | + struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); | |
| 338 | 348 | unsigned long flags; |
| 339 | 349 | u8 bc = len - 1; |
| 340 | 350 | u32 cmd; |
| 341 | 351 | int rc; |
| 342 | 352 | u32 offset; |
| 343 | − mode_t mode; | |
| 344 | − | |
| 345 | − rc = pa->ver_ops->offset(pa, sid, addr, &offset); | |
| 346 | − if (rc) | |
| 347 | − return rc; | |
| 348 | 353 | |
| 349 | − rc = pa->ver_ops->mode(pa, sid, addr, &mode); | |
| 350 | − if (rc) | |
| 354 | + rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr, | |
| 355 | + PMIC_ARB_CHANNEL_OBS); | |
| 356 | + if (rc < 0) | |
| 351 | 357 | return rc; |
| 352 | 358 | |
| 353 | − if (!(mode & S_IRUSR)) { | |
| 354 | − dev_err(&pa->spmic->dev, | |
| 355 | − "error: impermissible read from peripheral sid:%d addr:0x%x\n", | |
| 356 | − sid, addr); | |
| 357 | − return -EPERM; | |
| 358 | − } | |
| 359 | − | |
| 359 | + offset = rc; | |
| 360 | 360 | if (bc >= PMIC_ARB_MAX_TRANS_BYTES) { |
| 361 | − dev_err(&ctrl->dev, | |
| 362 | − "pmic-arb supports 1..%d bytes per trans, but:%zu requested", | |
| 361 | + dev_err(&ctrl->dev, "pmic-arb supports 1..%d bytes per trans, but:%zu requested", | |
| 363 | 362 | PMIC_ARB_MAX_TRANS_BYTES, len); |
| 364 | 363 | return -EINVAL; |
| 365 | 364 | } |
| @@ -374,54 +373,45 @@ static int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, · 8 unmodified lines | ||
| 374 | 373 | else |
| 375 | 374 | return -EINVAL; |
| 376 | 375 | |
| 377 | − cmd = pa->ver_ops->fmt_cmd(opc, sid, addr, bc); | |
| 376 | + cmd = pmic_arb->ver_ops->fmt_cmd(opc, sid, addr, bc); | |
| 378 | 377 | |
| 379 | − raw_spin_lock_irqsave(&pa->lock, flags); | |
| 380 | − pmic_arb_set_rd_cmd(pa, offset + PMIC_ARB_CMD, cmd); | |
| 381 | − rc = pmic_arb_wait_for_done(ctrl, pa->rd_base, sid, addr); | |
| 378 | + raw_spin_lock_irqsave(&pmic_arb->lock, flags); | |
| 379 | + pmic_arb_set_rd_cmd(pmic_arb, offset + PMIC_ARB_CMD, cmd); | |
| 380 | + rc = pmic_arb_wait_for_done(ctrl, pmic_arb->rd_base, sid, addr, | |
| 381 | + PMIC_ARB_CHANNEL_OBS); | |
| 382 | 382 | if (rc) |
| 383 | 383 | goto done; |
| 384 | 384 | |
| 385 | − pa_read_data(pa, buf, offset + PMIC_ARB_RDATA0, | |
| 385 | + pmic_arb_read_data(pmic_arb, buf, offset + PMIC_ARB_RDATA0, | |
| 386 | 386 | min_t(u8, bc, 3)); |
| 387 | 387 | |
| 388 | 388 | if (bc > 3) |
| 389 | − pa_read_data(pa, buf + 4, offset + PMIC_ARB_RDATA1, bc - 4); | |
| 389 | + pmic_arb_read_data(pmic_arb, buf + 4, offset + PMIC_ARB_RDATA1, | |
| 390 | + bc - 4); | |
| 390 | 391 | |
| 391 | 392 | done: |
| 392 | − raw_spin_unlock_irqrestore(&pa->lock, flags); | |
| 393 | + raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); | |
| 393 | 394 | return rc; |
| 394 | 395 | } |
| 395 | 396 | |
| 396 | 397 | static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, |
| 397 | − u16 addr, const u8 *buf, size_t len) | |
| 398 | + u16 addr, const u8 *buf, size_t len) | |
| 398 | 399 | { |
| 399 | − struct spmi_pmic_arb *pa = spmi_controller_get_drvdata(ctrl); | |
| 400 | + struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); | |
| 400 | 401 | unsigned long flags; |
| 401 | 402 | u8 bc = len - 1; |
| 402 | 403 | u32 cmd; |
| 403 | 404 | int rc; |
| 404 | 405 | u32 offset; |
| 405 | − mode_t mode; | |
| 406 | − | |
| 407 | − rc = pa->ver_ops->offset(pa, sid, addr, &offset); | |
| 408 | − if (rc) | |
| 409 | − return rc; | |
| 410 | 406 | |
| 411 | − rc = pa->ver_ops->mode(pa, sid, addr, &mode); | |
| 412 | − if (rc) | |
| 407 | + rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr, | |
| 408 | + PMIC_ARB_CHANNEL_RW); | |
| 409 | + if (rc < 0) | |
| 413 | 410 | return rc; |
| 414 | 411 | |
| 415 | − if (!(mode & S_IWUSR)) { | |
| 416 | − dev_err(&pa->spmic->dev, | |
| 417 | − "error: impermissible write to peripheral sid:%d addr:0x%x\n", | |
| 418 | − sid, addr); | |
| 419 | − return -EPERM; | |
| 420 | − } | |
| 421 | − | |
| 412 | + offset = rc; | |
| 422 | 413 | if (bc >= PMIC_ARB_MAX_TRANS_BYTES) { |
| 423 | − dev_err(&ctrl->dev, | |
| 424 | − "pmic-arb supports 1..%d bytes per trans, but:%zu requested", | |
| 414 | + dev_err(&ctrl->dev, "pmic-arb supports 1..%d bytes per trans, but:%zu requested", | |
| 425 | 415 | PMIC_ARB_MAX_TRANS_BYTES, len); |
| 426 | 416 | return -EINVAL; |
| 427 | 417 | } |
| @@ -429,7 +419,7 @@ static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, · 1 unmodified line | ||
| 429 | 419 | /* Check the opcode */ |
| 430 | 420 | if (opc >= 0x40 && opc <= 0x5F) |
| 431 | 421 | opc = PMIC_ARB_OP_WRITE; |
| 432 | − else if (opc >= 0x00 && opc <= 0x0F) | |
| 422 | + else if (opc <= 0x0F) | |
| 433 | 423 | opc = PMIC_ARB_OP_EXT_WRITE; |
| 434 | 424 | else if (opc >= 0x30 && opc <= 0x37) |
| 435 | 425 | opc = PMIC_ARB_OP_EXT_WRITEL; |
| @@ -438,18 +428,21 @@ static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, · 2 unmodified lines | ||
| 438 | 428 | else |
| 439 | 429 | return -EINVAL; |
| 440 | 430 | |
| 441 | − cmd = pa->ver_ops->fmt_cmd(opc, sid, addr, bc); | |
| 431 | + cmd = pmic_arb->ver_ops->fmt_cmd(opc, sid, addr, bc); | |
| 442 | 432 | |
| 443 | 433 | /* Write data to FIFOs */ |
| 444 | − raw_spin_lock_irqsave(&pa->lock, flags); | |
| 445 | − pa_write_data(pa, buf, offset + PMIC_ARB_WDATA0, min_t(u8, bc, 3)); | |
| 434 | + raw_spin_lock_irqsave(&pmic_arb->lock, flags); | |
| 435 | + pmic_arb_write_data(pmic_arb, buf, offset + PMIC_ARB_WDATA0, | |
| 436 | + min_t(u8, bc, 3)); | |
| 446 | 437 | if (bc > 3) |
| 447 | − pa_write_data(pa, buf + 4, offset + PMIC_ARB_WDATA1, bc - 4); | |
| 438 | + pmic_arb_write_data(pmic_arb, buf + 4, offset + PMIC_ARB_WDATA1, | |
| 439 | + bc - 4); | |
| 448 | 440 | |
| 449 | 441 | /* Start the transaction */ |
| 450 | − pmic_arb_base_write(pa, offset + PMIC_ARB_CMD, cmd); | |
| 451 | − rc = pmic_arb_wait_for_done(ctrl, pa->wr_base, sid, addr); | |
| 452 | − raw_spin_unlock_irqrestore(&pa->lock, flags); | |
| 442 | + pmic_arb_base_write(pmic_arb, offset + PMIC_ARB_CMD, cmd); | |
| 443 | + rc = pmic_arb_wait_for_done(ctrl, pmic_arb->wr_base, sid, addr, | |
| 444 | + PMIC_ARB_CHANNEL_RW); | |
| 445 | + raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); | |
| 453 | 446 | |
| 454 | 447 | return rc; |
| 455 | 448 | } |
| @@ -475,67 +468,64 @@ struct spmi_pmic_arb_qpnpint_type { · 19 unmodified lines | ||
| 475 | 468 | static void qpnpint_spmi_write(struct irq_data *d, u8 reg, void *buf, |
| 476 | 469 | size_t len) |
| 477 | 470 | { |
| 478 | − struct spmi_pmic_arb *pa = irq_data_get_irq_chip_data(d); | |
| 479 | − u8 sid = HWIRQ_SID(d->hwirq); | |
| 480 | − u8 per = HWIRQ_PER(d->hwirq); | |
| 471 | + struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d); | |
| 472 | + u8 sid = hwirq_to_sid(d->hwirq); | |
| 473 | + u8 per = hwirq_to_per(d->hwirq); | |
| 481 | 474 | |
| 482 | − if (pmic_arb_write_cmd(pa->spmic, SPMI_CMD_EXT_WRITEL, sid, | |
| 475 | + if (pmic_arb_write_cmd(pmic_arb->spmic, SPMI_CMD_EXT_WRITEL, sid, | |
| 483 | 476 | (per << 8) + reg, buf, len)) |
| 484 | − dev_err_ratelimited(&pa->spmic->dev, | |
| 485 | − "failed irqchip transaction on %x\n", | |
| 477 | + dev_err_ratelimited(&pmic_arb->spmic->dev, "failed irqchip transaction on %x\n", | |
| 486 | 478 | d->irq); |
| 487 | 479 | } |
| 488 | 480 | |
| 489 | 481 | static void qpnpint_spmi_read(struct irq_data *d, u8 reg, void *buf, size_t len) |
| 490 | 482 | { |
| 491 | − struct spmi_pmic_arb *pa = irq_data_get_irq_chip_data(d); | |
| 492 | − u8 sid = HWIRQ_SID(d->hwirq); | |
| 493 | − u8 per = HWIRQ_PER(d->hwirq); | |
| 483 | + struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d); | |
| 484 | + u8 sid = hwirq_to_sid(d->hwirq); | |
| 485 | + u8 per = hwirq_to_per(d->hwirq); | |
| 494 | 486 | |
| 495 | − if (pmic_arb_read_cmd(pa->spmic, SPMI_CMD_EXT_READL, sid, | |
| 487 | + if (pmic_arb_read_cmd(pmic_arb->spmic, SPMI_CMD_EXT_READL, sid, | |
| 496 | 488 | (per << 8) + reg, buf, len)) |
| 497 | − dev_err_ratelimited(&pa->spmic->dev, | |
| 498 | − "failed irqchip transaction on %x\n", | |
| 489 | + dev_err_ratelimited(&pmic_arb->spmic->dev, "failed irqchip transaction on %x\n", | |
| 499 | 490 | d->irq); |
| 500 | 491 | } |
| 501 | 492 | |
| 502 | −static void cleanup_irq(struct spmi_pmic_arb *pa, u16 apid, int id) | |
| 493 | +static void cleanup_irq(struct spmi_pmic_arb *pmic_arb, u16 apid, int id) | |
| 503 | 494 | { |
| 504 | − u16 ppid = pa->apid_data[apid].ppid; | |
| 495 | + u16 ppid = pmic_arb->apid_data[apid].ppid; | |
| 505 | 496 | u8 sid = ppid >> 8; |
| 506 | 497 | u8 per = ppid & 0xFF; |
| 507 | 498 | u8 irq_mask = BIT(id); |
| 508 | 499 | |
| 509 | − writel_relaxed(irq_mask, pa->intr + pa->ver_ops->irq_clear(apid)); | |
| 500 | + writel_relaxed(irq_mask, pmic_arb->ver_ops->irq_clear(pmic_arb, apid)); | |
| 510 | 501 | |
| 511 | − if (pmic_arb_write_cmd(pa->spmic, SPMI_CMD_EXT_WRITEL, sid, | |
| 502 | + if (pmic_arb_write_cmd(pmic_arb->spmic, SPMI_CMD_EXT_WRITEL, sid, | |
| 512 | 503 | (per << 8) + QPNPINT_REG_LATCHED_CLR, &irq_mask, 1)) |
| 513 | − dev_err_ratelimited(&pa->spmic->dev, | |
| 514 | − "failed to ack irq_mask = 0x%x for ppid = %x\n", | |
| 504 | + dev_err_ratelimited(&pmic_arb->spmic->dev, "failed to ack irq_mask = 0x%x for ppid = %x\n", | |
| 515 | 505 | irq_mask, ppid); |
| 516 | 506 | |
| 517 | − if (pmic_arb_write_cmd(pa->spmic, SPMI_CMD_EXT_WRITEL, sid, | |
| 507 | + if (pmic_arb_write_cmd(pmic_arb->spmic, SPMI_CMD_EXT_WRITEL, sid, | |
| 518 | 508 | (per << 8) + QPNPINT_REG_EN_CLR, &irq_mask, 1)) |
| 519 | − dev_err_ratelimited(&pa->spmic->dev, | |
| 520 | − "failed to ack irq_mask = 0x%x for ppid = %x\n", | |
| 509 | + dev_err_ratelimited(&pmic_arb->spmic->dev, "failed to ack irq_mask = 0x%x for ppid = %x\n", | |
| 521 | 510 | irq_mask, ppid); |
| 522 | 511 | } |
| 523 | 512 | |
| 524 | −static void periph_interrupt(struct spmi_pmic_arb *pa, u16 apid) | |
| 513 | +static void periph_interrupt(struct spmi_pmic_arb *pmic_arb, u16 apid) | |
| 525 | 514 | { |
| 526 | 515 | unsigned int irq; |
| 527 | 516 | u32 status; |
| 528 | 517 | int id; |
| 529 | − u8 sid = (pa->apid_data[apid].ppid >> 8) & 0xF; | |
| 530 | − u8 per = pa->apid_data[apid].ppid & 0xFF; | |
| 518 | + u8 sid = (pmic_arb->apid_data[apid].ppid >> 8) & 0xF; | |
| 519 | + u8 per = pmic_arb->apid_data[apid].ppid & 0xFF; | |
| 531 | 520 | |
| 532 | − status = readl_relaxed(pa->intr + pa->ver_ops->irq_status(apid)); | |
| 521 | + status = readl_relaxed(pmic_arb->ver_ops->irq_status(pmic_arb, apid)); | |
| 533 | 522 | while (status) { |
| 534 | 523 | id = ffs(status) - 1; |
| 535 | 524 | status &= ~BIT(id); |
| 536 | − irq = irq_find_mapping(pa->domain, HWIRQ(sid, per, id, apid)); | |
| 525 | + irq = irq_find_mapping(pmic_arb->domain, | |
| 526 | + spec_to_hwirq(sid, per, id, apid)); | |
| 537 | 527 | if (irq == 0) { |
| 538 | − cleanup_irq(pa, apid, id); | |
| 528 | + cleanup_irq(pmic_arb, apid, id); | |
| 539 | 529 | continue; |
| 540 | 530 | } |
| 541 | 531 | generic_handle_irq(irq); |
| @@ -544,27 +534,28 @@ static void periph_interrupt(struct spmi_pmic_arb *pa, u16 apid) · 2 unmodified lines | ||
| 544 | 534 | |
| 545 | 535 | static void pmic_arb_chained_irq(struct irq_desc *desc) |
| 546 | 536 | { |
| 547 | − struct spmi_pmic_arb *pa = irq_desc_get_handler_data(desc); | |
| 537 | + struct spmi_pmic_arb *pmic_arb = irq_desc_get_handler_data(desc); | |
| 538 | + const struct pmic_arb_ver_ops *ver_ops = pmic_arb->ver_ops; | |
| 548 | 539 | struct irq_chip *chip = irq_desc_get_chip(desc); |
| 549 | − void __iomem *intr = pa->intr; | |
| 550 | − int first = pa->min_apid >> 5; | |
| 551 | − int last = pa->max_apid >> 5; | |
| 540 | + int first = pmic_arb->min_apid >> 5; | |
| 541 | + int last = pmic_arb->max_apid >> 5; | |
| 542 | + u8 ee = pmic_arb->ee; | |
| 552 | 543 | u32 status, enable; |
| 553 | 544 | int i, id, apid; |
| 554 | 545 | |
| 555 | 546 | chained_irq_enter(chip, desc); |
| 556 | 547 | |
| 557 | 548 | for (i = first; i <= last; ++i) { |
| 558 | − status = readl_relaxed(intr + | |
| 559 | − pa->ver_ops->owner_acc_status(pa->ee, i)); | |
| 549 | + status = readl_relaxed( | |
| 550 | + ver_ops->owner_acc_status(pmic_arb, ee, i)); | |
| 560 | 551 | while (status) { |
| 561 | 552 | id = ffs(status) - 1; |
| 562 | 553 | status &= ~BIT(id); |
| 563 | 554 | apid = id + i * 32; |
| 564 | − enable = readl_relaxed(intr + | |
| 565 | − pa->ver_ops->acc_enable(apid)); | |
| 555 | + enable = readl_relaxed( | |
| 556 | + ver_ops->acc_enable(pmic_arb, apid)); | |
| 566 | 557 | if (enable & SPMI_PIC_ACC_ENABLE_BIT) |
| 567 | − periph_interrupt(pa, apid); | |
| 558 | + periph_interrupt(pmic_arb, apid); | |
| 568 | 559 | } |
| 569 | 560 | } |
| 570 | 561 | |
| @@ -573,12 +564,12 @@ static void pmic_arb_chained_irq(struct irq_desc *desc) · 2 unmodified lines | ||
| 573 | 564 | |
| 574 | 565 | static void qpnpint_irq_ack(struct irq_data *d) |
| 575 | 566 | { |
| 576 | − struct spmi_pmic_arb *pa = irq_data_get_irq_chip_data(d); | |
| 577 | − u8 irq = HWIRQ_IRQ(d->hwirq); | |
| 578 | − u16 apid = HWIRQ_APID(d->hwirq); | |
| 567 | + struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d); | |
| 568 | + u8 irq = hwirq_to_irq(d->hwirq); | |
| 569 | + u16 apid = hwirq_to_apid(d->hwirq); | |
| 579 | 570 | u8 data; |
| 580 | 571 | |
| 581 | − writel_relaxed(BIT(irq), pa->intr + pa->ver_ops->irq_clear(apid)); | |
| 572 | + writel_relaxed(BIT(irq), pmic_arb->ver_ops->irq_clear(pmic_arb, apid)); | |
| 582 | 573 | |
| 583 | 574 | data = BIT(irq); |
| 584 | 575 | qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &data, 1); |
| @@ -586,7 +577,7 @@ static void qpnpint_irq_ack(struct irq_data *d) · 1 unmodified line | ||
| 586 | 577 | |
| 587 | 578 | static void qpnpint_irq_mask(struct irq_data *d) |
| 588 | 579 | { |
| 589 | − u8 irq = HWIRQ_IRQ(d->hwirq); | |
| 580 | + u8 irq = hwirq_to_irq(d->hwirq); | |
| 590 | 581 | u8 data = BIT(irq); |
| 591 | 582 | |
| 592 | 583 | qpnpint_spmi_write(d, QPNPINT_REG_EN_CLR, &data, 1); |
| @@ -594,13 +585,14 @@ static void qpnpint_irq_mask(struct irq_data *d) · 1 unmodified line | ||
| 594 | 585 | |
| 595 | 586 | static void qpnpint_irq_unmask(struct irq_data *d) |
| 596 | 587 | { |
| 597 | − struct spmi_pmic_arb *pa = irq_data_get_irq_chip_data(d); | |
| 598 | − u8 irq = HWIRQ_IRQ(d->hwirq); | |
| 599 | − u16 apid = HWIRQ_APID(d->hwirq); | |
| 588 | + struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d); | |
| 589 | + const struct pmic_arb_ver_ops *ver_ops = pmic_arb->ver_ops; | |
| 590 | + u8 irq = hwirq_to_irq(d->hwirq); | |
| 591 | + u16 apid = hwirq_to_apid(d->hwirq); | |
| 600 | 592 | u8 buf[2]; |
| 601 | 593 | |
| 602 | 594 | writel_relaxed(SPMI_PIC_ACC_ENABLE_BIT, |
| 603 | − pa->intr + pa->ver_ops->acc_enable(apid)); | |
| 595 | + ver_ops->acc_enable(pmic_arb, apid)); | |
| 604 | 596 | |
| 605 | 597 | qpnpint_spmi_read(d, QPNPINT_REG_EN_SET, &buf[0], 1); |
| 606 | 598 | if (!(buf[0] & BIT(irq))) { |
| @@ -618,44 +610,51 @@ static void qpnpint_irq_unmask(struct irq_data *d) · 11 unmodified lines | ||
| 618 | 610 | static int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type) |
| 619 | 611 | { |
| 620 | 612 | struct spmi_pmic_arb_qpnpint_type type; |
| 621 | − u8 irq = HWIRQ_IRQ(d->hwirq); | |
| 622 | − u8 bit_mask_irq = BIT(irq); | |
| 613 | + irq_flow_handler_t flow_handler; | |
| 614 | + u8 irq = hwirq_to_irq(d->hwirq); | |
| 623 | 615 | |
| 624 | 616 | qpnpint_spmi_read(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type)); |
| 625 | 617 | |
| 626 | 618 | if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { |
| 627 | − type.type |= bit_mask_irq; | |
| 619 | + type.type |= BIT(irq); | |
| 628 | 620 | if (flow_type & IRQF_TRIGGER_RISING) |
| 629 | − type.polarity_high |= bit_mask_irq; | |
| 621 | + type.polarity_high |= BIT(irq); | |
| 630 | 622 | if (flow_type & IRQF_TRIGGER_FALLING) |
| 631 | − type.polarity_low |= bit_mask_irq; | |
| 623 | + type.polarity_low |= BIT(irq); | |
| 624 | + | |
| 625 | + flow_handler = handle_edge_irq; | |
| 632 | 626 | } else { |
| 633 | 627 | if ((flow_type & (IRQF_TRIGGER_HIGH)) && |
| 634 | 628 | (flow_type & (IRQF_TRIGGER_LOW))) |
| 635 | 629 | return -EINVAL; |
| 636 | 630 | |
| 637 | − type.type &= ~bit_mask_irq; /* level trig */ | |
| 631 | + type.type &= ~BIT(irq); /* level trig */ | |
| 638 | 632 | if (flow_type & IRQF_TRIGGER_HIGH) |
| 639 | − type.polarity_high |= bit_mask_irq; | |
| 633 | + type.polarity_high |= BIT(irq); | |
| 640 | 634 | else |
| 641 | − type.polarity_low |= bit_mask_irq; | |
| 635 | + type.polarity_low |= BIT(irq); | |
| 636 | + | |
| 637 | + flow_handler = handle_level_irq; | |
| 642 | 638 | } |
| 643 | 639 | |
| 644 | 640 | qpnpint_spmi_write(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type)); |
| 645 | − | |
| 646 | − if (flow_type & IRQ_TYPE_EDGE_BOTH) | |
| 647 | − irq_set_handler_locked(d, handle_edge_irq); | |
| 648 | − else | |
| 649 | − irq_set_handler_locked(d, handle_level_irq); | |
| 641 | + irq_set_handler_locked(d, flow_handler); | |
| 650 | 642 | |
| 651 | 643 | return 0; |
| 652 | 644 | } |
| 653 | 645 | |
| 646 | +static int qpnpint_irq_set_wake(struct irq_data *d, unsigned int on) | |
| 647 | +{ | |
| 648 | + struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d); | |
| 649 | + | |
| 650 | + return irq_set_irq_wake(pmic_arb->irq, on); | |
| 651 | +} | |
| 652 | + | |
| 654 | 653 | static int qpnpint_get_irqchip_state(struct irq_data *d, |
| 655 | 654 | enum irqchip_irq_state which, |
| 656 | 655 | bool *state) |
| 657 | 656 | { |
| 658 | − u8 irq = HWIRQ_IRQ(d->hwirq); | |
| 657 | + u8 irq = hwirq_to_irq(d->hwirq); | |
| 659 | 658 | u8 status = 0; |
| 660 | 659 | |
| 661 | 660 | if (which != IRQCHIP_STATE_LINE_LEVEL) |
| @@ -667,15 +666,34 @@ static int qpnpint_get_irqchip_state(struct irq_data *d, · 5 unmodified lines | ||
| 667 | 666 | return 0; |
| 668 | 667 | } |
| 669 | 668 | |
| 669 | +static int qpnpint_irq_request_resources(struct irq_data *d) | |
| 670 | +{ | |
| 671 | + struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d); | |
| 672 | + u16 periph = hwirq_to_per(d->hwirq); | |
| 673 | + u16 apid = hwirq_to_apid(d->hwirq); | |
| 674 | + u16 sid = hwirq_to_sid(d->hwirq); | |
| 675 | + u16 irq = hwirq_to_irq(d->hwirq); | |
| 676 | + | |
| 677 | + if (pmic_arb->apid_data[apid].irq_ee != pmic_arb->ee) { | |
| 678 | + dev_err(&pmic_arb->spmic->dev, "failed to xlate sid = %#x, periph = %#x, irq = %u: ee=%u but owner=%u\n", | |
| 679 | + sid, periph, irq, pmic_arb->ee, | |
| 680 | + pmic_arb->apid_data[apid].irq_ee); | |
| 681 | + return -ENODEV; | |
| 682 | + } | |
| 683 | + | |
| 684 | + return 0; | |
| 685 | +} | |
| 686 | + | |
| 670 | 687 | static struct irq_chip pmic_arb_irqchip = { |
| 671 | 688 | .name = "pmic_arb", |
| 672 | 689 | .irq_ack = qpnpint_irq_ack, |
| 673 | 690 | .irq_mask = qpnpint_irq_mask, |
| 674 | 691 | .irq_unmask = qpnpint_irq_unmask, |
| 675 | 692 | .irq_set_type = qpnpint_irq_set_type, |
| 693 | + .irq_set_wake = qpnpint_irq_set_wake, | |
| 676 | 694 | .irq_get_irqchip_state = qpnpint_get_irqchip_state, |
| 677 | − .flags = IRQCHIP_MASK_ON_SUSPEND | |
| 678 | − | IRQCHIP_SKIP_SET_WAKE, | |
| 695 | + .irq_request_resources = qpnpint_irq_request_resources, | |
| 696 | + .flags = IRQCHIP_MASK_ON_SUSPEND, | |
| 679 | 697 | }; |
| 680 | 698 | |
| 681 | 699 | static int qpnpint_irq_domain_dt_translate(struct irq_domain *d, |
| @@ -685,12 +703,11 @@ static int qpnpint_irq_domain_dt_translate(struct irq_domain *d, · 3 unmodified lines | ||
| 685 | 703 | unsigned long *out_hwirq, |
| 686 | 704 | unsigned int *out_type) |
| 687 | 705 | { |
| 688 | − struct spmi_pmic_arb *pa = d->host_data; | |
| 706 | + struct spmi_pmic_arb *pmic_arb = d->host_data; | |
| 707 | + u16 apid, ppid; | |
| 689 | 708 | int rc; |
| 690 | − u16 apid; | |
| 691 | 709 | |
| 692 | − dev_dbg(&pa->spmic->dev, | |
| 693 | − "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n", | |
| 710 | + dev_dbg(&pmic_arb->spmic->dev, "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n", | |
| 694 | 711 | intspec[0], intspec[1], intspec[2]); |
| 695 | 712 | |
| 696 | 713 | if (irq_domain_get_of_node(d) != controller) |
| @@ -700,25 +717,25 @@ static int qpnpint_irq_domain_dt_translate(struct irq_domain *d, · 3 unmodified lines | ||
| 700 | 717 | if (intspec[0] > 0xF || intspec[1] > 0xFF || intspec[2] > 0x7) |
| 701 | 718 | return -EINVAL; |
| 702 | 719 | |
| 703 | − rc = pa->ver_ops->ppid_to_apid(pa, intspec[0], | |
| 704 | − (intspec[1] << 8), &apid); | |
| 720 | + ppid = intspec[0] << 8 | intspec[1]; | |
| 721 | + rc = pmic_arb->ver_ops->ppid_to_apid(pmic_arb, ppid); | |
| 705 | 722 | if (rc < 0) { |
| 706 | − dev_err(&pa->spmic->dev, | |
| 707 | − "failed to xlate sid = 0x%x, periph = 0x%x, irq = %x rc = %d\n", | |
| 723 | + dev_err(&pmic_arb->spmic->dev, "failed to xlate sid = %#x, periph = %#x, irq = %u rc = %d\n", | |
| 708 | 724 | intspec[0], intspec[1], intspec[2], rc); |
| 709 | 725 | return rc; |
| 710 | 726 | } |
| 711 | 727 | |
| 728 | + apid = rc; | |
| 712 | 729 | /* Keep track of {max,min}_apid for bounding search during interrupt */ |
| 713 | − if (apid > pa->max_apid) | |
| 714 | − pa->max_apid = apid; | |
| 715 | − if (apid < pa->min_apid) | |
| 716 | − pa->min_apid = apid; | |
| 730 | + if (apid > pmic_arb->max_apid) | |
| 731 | + pmic_arb->max_apid = apid; | |
| 732 | + if (apid < pmic_arb->min_apid) | |
| 733 | + pmic_arb->min_apid = apid; | |
| 717 | 734 | |
| 718 | − *out_hwirq = HWIRQ(intspec[0], intspec[1], intspec[2], apid); | |
| 735 | + *out_hwirq = spec_to_hwirq(intspec[0], intspec[1], intspec[2], apid); | |
| 719 | 736 | *out_type = intspec[3] & IRQ_TYPE_SENSE_MASK; |
| 720 | 737 | |
| 721 | − dev_dbg(&pa->spmic->dev, "out_hwirq = %lu\n", *out_hwirq); | |
| 738 | + dev_dbg(&pmic_arb->spmic->dev, "out_hwirq = %lu\n", *out_hwirq); | |
| 722 | 739 | |
| 723 | 740 | return 0; |
| 724 | 741 | } |
| @@ -727,9 +744,9 @@ static int qpnpint_irq_domain_map(struct irq_domain *d, · 2 unmodified lines | ||
| 727 | 744 | unsigned int virq, |
| 728 | 745 | irq_hw_number_t hwirq) |
| 729 | 746 | { |
| 730 | − struct spmi_pmic_arb *pa = d->host_data; | |
| 747 | + struct spmi_pmic_arb *pmic_arb = d->host_data; | |
| 731 | 748 | |
| 732 | − dev_dbg(&pa->spmic->dev, "virq = %u, hwirq = %lu\n", virq, hwirq); | |
| 749 | + dev_dbg(&pmic_arb->spmic->dev, "virq = %u, hwirq = %lu\n", virq, hwirq); | |
| 733 | 750 | |
| 734 | 751 | irq_set_chip_and_handler(virq, &pmic_arb_irqchip, handle_level_irq); |
| 735 | 752 | irq_set_chip_data(virq, d->host_data); |
| @@ -737,24 +754,23 @@ static int qpnpint_irq_domain_map(struct irq_domain *d, · 1 unmodified line | ||
| 737 | 754 | return 0; |
| 738 | 755 | } |
| 739 | 756 | |
| 740 | −static int | |
| 741 | −pmic_arb_ppid_to_apid_v1(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u16 *apid) | |
| 757 | +static int pmic_arb_ppid_to_apid_v1(struct spmi_pmic_arb *pmic_arb, u16 ppid) | |
| 742 | 758 | { |
| 743 | − u16 ppid = sid << 8 | ((addr >> 8) & 0xFF); | |
| 744 | − u32 *mapping_table = pa->mapping_table; | |
| 759 | + u32 *mapping_table = pmic_arb->mapping_table; | |
| 745 | 760 | int index = 0, i; |
| 746 | 761 | u16 apid_valid; |
| 762 | + u16 apid; | |
| 747 | 763 | u32 data; |
| 748 | 764 | |
| 749 | − apid_valid = pa->ppid_to_apid[ppid]; | |
| 750 | − if (apid_valid & PMIC_ARB_CHAN_VALID) { | |
| 751 | − *apid = (apid_valid & ~PMIC_ARB_CHAN_VALID); | |
| 752 | − return 0; | |
| 765 | + apid_valid = pmic_arb->ppid_to_apid[ppid]; | |
| 766 | + if (apid_valid & PMIC_ARB_APID_VALID) { | |
| 767 | + apid = apid_valid & ~PMIC_ARB_APID_VALID; | |
| 768 | + return apid; | |
| 753 | 769 | } |
| 754 | 770 | |
| 755 | 771 | for (i = 0; i < SPMI_MAPPING_TABLE_TREE_DEPTH; ++i) { |
| 756 | − if (!test_and_set_bit(index, pa->mapping_table_valid)) | |
| 757 | − mapping_table[index] = readl_relaxed(pa->cnfg + | |
| 772 | + if (!test_and_set_bit(index, pmic_arb->mapping_table_valid)) | |
| 773 | + mapping_table[index] = readl_relaxed(pmic_arb->cnfg + | |
| 758 | 774 | SPMI_MAPPING_TABLE_REG(index)); |
| 759 | 775 | |
| 760 | 776 | data = mapping_table[index]; |
| @@ -763,21 +779,21 @@ pmic_arb_ppid_to_apid_v1(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u16 *apid) · 2 unmodified lines | ||
| 763 | 779 | if (SPMI_MAPPING_BIT_IS_1_FLAG(data)) { |
| 764 | 780 | index = SPMI_MAPPING_BIT_IS_1_RESULT(data); |
| 765 | 781 | } else { |
| 766 | − *apid = SPMI_MAPPING_BIT_IS_1_RESULT(data); | |
| 767 | − pa->ppid_to_apid[ppid] | |
| 768 | − = *apid | PMIC_ARB_CHAN_VALID; | |
| 769 | − pa->apid_data[*apid].ppid = ppid; | |
| 770 | − return 0; | |
| 782 | + apid = SPMI_MAPPING_BIT_IS_1_RESULT(data); | |
| 783 | + pmic_arb->ppid_to_apid[ppid] | |
| 784 | + = apid | PMIC_ARB_APID_VALID; | |
| 785 | + pmic_arb->apid_data[apid].ppid = ppid; | |
| 786 | + return apid; | |
| 771 | 787 | } |
| 772 | 788 | } else { |
| 773 | 789 | if (SPMI_MAPPING_BIT_IS_0_FLAG(data)) { |
| 774 | 790 | index = SPMI_MAPPING_BIT_IS_0_RESULT(data); |
| 775 | 791 | } else { |
| 776 | − *apid = SPMI_MAPPING_BIT_IS_0_RESULT(data); | |
| 777 | − pa->ppid_to_apid[ppid] | |
| 778 | − = *apid | PMIC_ARB_CHAN_VALID; | |
| 779 | − pa->apid_data[*apid].ppid = ppid; | |
| 780 | − return 0; | |
| 792 | + apid = SPMI_MAPPING_BIT_IS_0_RESULT(data); | |
| 793 | + pmic_arb->ppid_to_apid[ppid] | |
| 794 | + = apid | PMIC_ARB_APID_VALID; | |
| 795 | + pmic_arb->apid_data[apid].ppid = ppid; | |
| 796 | + return apid; | |
| 781 | 797 | } |
| 782 | 798 | } |
| 783 | 799 | } |
| @@ -785,105 +801,178 @@ pmic_arb_ppid_to_apid_v1(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u16 *apid) · 1 unmodified line | ||
| 785 | 801 | return -ENODEV; |
| 786 | 802 | } |
| 787 | 803 | |
| 788 | −static int | |
| 789 | −pmic_arb_mode_v1_v3(struct spmi_pmic_arb *pa, u8 sid, u16 addr, mode_t *mode) | |
| 790 | −{ | |
| 791 | − *mode = S_IRUSR | S_IWUSR; | |
| 792 | − return 0; | |
| 793 | −} | |
| 794 | − | |
| 795 | 804 | /* v1 offset per ee */ |
| 796 | −static int | |
| 797 | −pmic_arb_offset_v1(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u32 *offset) | |
| 805 | +static int pmic_arb_offset_v1(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr, | |
| 806 | + enum pmic_arb_channel ch_type) | |
| 798 | 807 | { |
| 799 | − *offset = 0x800 + 0x80 * pa->channel; | |
| 800 | − return 0; | |
| 808 | + return 0x800 + 0x80 * pmic_arb->channel; | |
| 801 | 809 | } |
| 802 | 810 | |
| 803 | −static u16 pmic_arb_find_apid(struct spmi_pmic_arb *pa, u16 ppid) | |
| 811 | +static u16 pmic_arb_find_apid(struct spmi_pmic_arb *pmic_arb, u16 ppid) | |
| 804 | 812 | { |
| 813 | + struct apid_data *apidd = &pmic_arb->apid_data[pmic_arb->last_apid]; | |
| 805 | 814 | u32 regval, offset; |
| 806 | − u16 apid; | |
| 807 | − u16 id; | |
| 808 | − | |
| 809 | − /* | |
| 810 | − * PMIC_ARB_REG_CHNL is a table in HW mapping channel to ppid. | |
| 811 | − * ppid_to_apid is an in-memory invert of that table. | |
| 812 | − */ | |
| 813 | − for (apid = pa->last_apid; apid < pa->max_periph; apid++) { | |
| 814 | − regval = readl_relaxed(pa->cnfg + | |
| 815 | − SPMI_OWNERSHIP_TABLE_REG(apid)); | |
| 816 | − pa->apid_data[apid].owner = SPMI_OWNERSHIP_PERIPH2OWNER(regval); | |
| 815 | + u16 id, apid; | |
| 817 | 816 | |
| 818 | − offset = PMIC_ARB_REG_CHNL(apid); | |
| 819 | − if (offset >= pa->core_size) | |
| 817 | + for (apid = pmic_arb->last_apid; ; apid++, apidd++) { | |
| 818 | + offset = pmic_arb->ver_ops->apid_map_offset(apid); | |
| 819 | + if (offset >= pmic_arb->core_size) | |
| 820 | 820 | break; |
| 821 | 821 | |
| 822 | − regval = readl_relaxed(pa->core + offset); | |
| 822 | + regval = readl_relaxed(pmic_arb->cnfg + | |
| 823 | + SPMI_OWNERSHIP_TABLE_REG(apid)); | |
| 824 | + apidd->irq_ee = SPMI_OWNERSHIP_PERIPH2OWNER(regval); | |
| 825 | + apidd->write_ee = apidd->irq_ee; | |
| 826 | + | |
| 827 | + regval = readl_relaxed(pmic_arb->core + offset); | |
| 823 | 828 | if (!regval) |
| 824 | 829 | continue; |
| 825 | 830 | |
| 826 | 831 | id = (regval >> 8) & PMIC_ARB_PPID_MASK; |
| 827 | − pa->ppid_to_apid[id] = apid | PMIC_ARB_CHAN_VALID; | |
| 828 | − pa->apid_data[apid].ppid = id; | |
| 832 | + pmic_arb->ppid_to_apid[id] = apid | PMIC_ARB_APID_VALID; | |
| 833 | + apidd->ppid = id; | |
| 829 | 834 | if (id == ppid) { |
| 830 | − apid |= PMIC_ARB_CHAN_VALID; | |
| 835 | + apid |= PMIC_ARB_APID_VALID; | |
| 831 | 836 | break; |
| 832 | 837 | } |
| 833 | 838 | } |
| 834 | − pa->last_apid = apid & ~PMIC_ARB_CHAN_VALID; | |
| 839 | + pmic_arb->last_apid = apid & ~PMIC_ARB_APID_VALID; | |
| 835 | 840 | |
| 836 | 841 | return apid; |
| 837 | 842 | } |
| 838 | 843 | |
| 839 | − | |
| 840 | −static int | |
| 841 | −pmic_arb_ppid_to_apid_v2(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u16 *apid) | |
| 844 | +static int pmic_arb_ppid_to_apid_v2(struct spmi_pmic_arb *pmic_arb, u16 ppid) | |
| 842 | 845 | { |
| 843 | − u16 ppid = (sid << 8) | (addr >> 8); | |
| 844 | 846 | u16 apid_valid; |
| 845 | 847 | |
| 846 | − apid_valid = pa->ppid_to_apid[ppid]; | |
| 847 | − if (!(apid_valid & PMIC_ARB_CHAN_VALID)) | |
| 848 | − apid_valid = pmic_arb_find_apid(pa, ppid); | |
| 849 | − if (!(apid_valid & PMIC_ARB_CHAN_VALID)) | |
| 848 | + apid_valid = pmic_arb->ppid_to_apid[ppid]; | |
| 849 | + if (!(apid_valid & PMIC_ARB_APID_VALID)) | |
| 850 | + apid_valid = pmic_arb_find_apid(pmic_arb, ppid); | |
| 851 | + if (!(apid_valid & PMIC_ARB_APID_VALID)) | |
| 850 | 852 | return -ENODEV; |
| 851 | 853 | |
| 852 | − *apid = (apid_valid & ~PMIC_ARB_CHAN_VALID); | |
| 854 | + return apid_valid & ~PMIC_ARB_APID_VALID; | |
| 855 | +} | |
| 856 | + | |
| 857 | +static int pmic_arb_read_apid_map_v5(struct spmi_pmic_arb *pmic_arb) | |
| 858 | +{ | |
| 859 | + struct apid_data *apidd = pmic_arb->apid_data; | |
| 860 | + struct apid_data *prev_apidd; | |
| 861 | + u16 i, apid, ppid; | |
| 862 | + bool valid, is_irq_ee; | |
| 863 | + u32 regval, offset; | |
| 864 | + | |
| 865 | + /* | |
| 866 | + * In order to allow multiple EEs to write to a single PPID in arbiter | |
| 867 | + * version 5, there is more than one APID mapped to each PPID. | |
| 868 | + * The owner field for each of these mappings specifies the EE which is | |
| 869 | + * allowed to write to the APID. The owner of the last (highest) APID | |
| 870 | + * for a given PPID will receive interrupts from the PPID. | |
| 871 | + */ | |
| 872 | + for (i = 0; ; i++, apidd++) { | |
| 873 | + offset = pmic_arb->ver_ops->apid_map_offset(i); | |
| 874 | + if (offset >= pmic_arb->core_size) | |
| 875 | + break; | |
| 876 | + | |
| 877 | + regval = readl_relaxed(pmic_arb->core + offset); | |
| 878 | + if (!regval) | |
| 879 | + continue; | |
| 880 | + ppid = (regval >> 8) & PMIC_ARB_PPID_MASK; | |
| 881 | + is_irq_ee = PMIC_ARB_CHAN_IS_IRQ_OWNER(regval); | |
| 882 | + | |
| 883 | + regval = readl_relaxed(pmic_arb->cnfg + | |
| 884 | + SPMI_OWNERSHIP_TABLE_REG(i)); | |
| 885 | + apidd->write_ee = SPMI_OWNERSHIP_PERIPH2OWNER(regval); | |
| 886 | + | |
| 887 | + apidd->irq_ee = is_irq_ee ? apidd->write_ee : INVALID_EE; | |
| 888 | + | |
| 889 | + valid = pmic_arb->ppid_to_apid[ppid] & PMIC_ARB_APID_VALID; | |
| 890 | + apid = pmic_arb->ppid_to_apid[ppid] & ~PMIC_ARB_APID_VALID; | |
| 891 | + prev_apidd = &pmic_arb->apid_data[apid]; | |
| 892 | + | |
| 893 | + if (valid && is_irq_ee && | |
| 894 | + prev_apidd->write_ee == pmic_arb->ee) { | |
| 895 | + /* | |
| 896 | + * Duplicate PPID mapping after the one for this EE; | |
| 897 | + * override the irq owner | |
| 898 | + */ | |
| 899 | + prev_apidd->irq_ee = apidd->irq_ee; | |
| 900 | + } else if (!valid || is_irq_ee) { | |
| 901 | + /* First PPID mapping or duplicate for another EE */ | |
| 902 | + pmic_arb->ppid_to_apid[ppid] = i | PMIC_ARB_APID_VALID; | |
| 903 | + } | |
| 904 | + | |
| 905 | + apidd->ppid = ppid; | |
| 906 | + pmic_arb->last_apid = i; | |
| 907 | + } | |
| 908 | + | |
| 909 | + /* Dump the mapping table for debug purposes. */ | |
| 910 | + dev_dbg(&pmic_arb->spmic->dev, "PPID APID Write-EE IRQ-EE\n"); | |
| 911 | + for (ppid = 0; ppid < PMIC_ARB_MAX_PPID; ppid++) { | |
| 912 | + apid = pmic_arb->ppid_to_apid[ppid]; | |
| 913 | + if (apid & PMIC_ARB_APID_VALID) { | |
| 914 | + apid &= ~PMIC_ARB_APID_VALID; | |
| 915 | + apidd = &pmic_arb->apid_data[apid]; | |
| 916 | + dev_dbg(&pmic_arb->spmic->dev, "%#03X %3u %2u %2u\n", | |
| 917 | + ppid, apid, apidd->write_ee, apidd->irq_ee); | |
| 918 | + } | |
| 919 | + } | |
| 920 | + | |
| 853 | 921 | return 0; |
| 854 | 922 | } |
| 855 | 923 | |
| 856 | −static int | |
| 857 | −pmic_arb_mode_v2(struct spmi_pmic_arb *pa, u8 sid, u16 addr, mode_t *mode) | |
| 924 | +static int pmic_arb_ppid_to_apid_v5(struct spmi_pmic_arb *pmic_arb, u16 ppid) | |
| 925 | +{ | |
| 926 | + if (!(pmic_arb->ppid_to_apid[ppid] & PMIC_ARB_APID_VALID)) | |
| 927 | + return -ENODEV; | |
| 928 | + | |
| 929 | + return pmic_arb->ppid_to_apid[ppid] & ~PMIC_ARB_APID_VALID; | |
| 930 | +} | |
| 931 | + | |
| 932 | +/* v2 offset per ppid and per ee */ | |
| 933 | +static int pmic_arb_offset_v2(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr, | |
| 934 | + enum pmic_arb_channel ch_type) | |
| 858 | 935 | { |
| 859 | 936 | u16 apid; |
| 937 | + u16 ppid; | |
| 860 | 938 | int rc; |
| 861 | 939 | |
| 862 | − rc = pmic_arb_ppid_to_apid_v2(pa, sid, addr, &apid); | |
| 940 | + ppid = sid << 8 | ((addr >> 8) & 0xFF); | |
| 941 | + rc = pmic_arb_ppid_to_apid_v2(pmic_arb, ppid); | |
| 863 | 942 | if (rc < 0) |
| 864 | 943 | return rc; |
| 865 | 944 | |
| 866 | − *mode = 0; | |
| 867 | − *mode |= S_IRUSR; | |
| 868 | − | |
| 869 | − if (pa->ee == pa->apid_data[apid].owner) | |
| 870 | − *mode |= S_IWUSR; | |
| 871 | − return 0; | |
| 945 | + apid = rc; | |
| 946 | + return 0x1000 * pmic_arb->ee + 0x8000 * apid; | |
| 872 | 947 | } |
| 873 | 948 | |
| 874 | −/* v2 offset per ppid and per ee */ | |
| 875 | −static int | |
| 876 | −pmic_arb_offset_v2(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u32 *offset) | |
| 949 | +/* | |
| 950 | + * v5 offset per ee and per apid for observer channels and per apid for | |
| 951 | + * read/write channels. | |
| 952 | + */ | |
| 953 | +static int pmic_arb_offset_v5(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr, | |
| 954 | + enum pmic_arb_channel ch_type) | |
| 877 | 955 | { |
| 878 | 956 | u16 apid; |
| 879 | 957 | int rc; |
| 958 | + u32 offset = 0; | |
| 959 | + u16 ppid = (sid << 8) | (addr >> 8); | |
| 880 | 960 | |
| 881 | − rc = pmic_arb_ppid_to_apid_v2(pa, sid, addr, &apid); | |
| 961 | + rc = pmic_arb_ppid_to_apid_v5(pmic_arb, ppid); | |
| 882 | 962 | if (rc < 0) |
| 883 | 963 | return rc; |
| 884 | 964 | |
| 885 | − *offset = 0x1000 * pa->ee + 0x8000 * apid; | |
| 886 | − return 0; | |
| 965 | + apid = rc; | |
| 966 | + switch (ch_type) { | |
| 967 | + case PMIC_ARB_CHANNEL_OBS: | |
| 968 | + offset = 0x10000 * pmic_arb->ee + 0x80 * apid; | |
| 969 | + break; | |
| 970 | + case PMIC_ARB_CHANNEL_RW: | |
| 971 | + offset = 0x10000 * apid; | |
| 972 | + break; | |
| 973 | + } | |
| 974 | + | |
| 975 | + return offset; | |
| 887 | 976 | } |
| 888 | 977 | |
| 889 | 978 | static u32 pmic_arb_fmt_cmd_v1(u8 opc, u8 sid, u16 addr, u8 bc) |
| @@ -896,55 +985,97 @@ static u32 pmic_arb_fmt_cmd_v2(u8 opc, u8 sid, u16 addr, u8 bc) · 6 unmodified lines | ||
| 896 | 985 | return (opc << 27) | ((addr & 0xff) << 4) | (bc & 0x7); |
| 897 | 986 | } |
| 898 | 987 | |
| 899 | −static u32 pmic_arb_owner_acc_status_v1(u8 m, u16 n) | |
| 988 | +static void __iomem * | |
| 989 | +pmic_arb_owner_acc_status_v1(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n) | |
| 990 | +{ | |
| 991 | + return pmic_arb->intr + 0x20 * m + 0x4 * n; | |
| 992 | +} | |
| 993 | + | |
| 994 | +static void __iomem * | |
| 995 | +pmic_arb_owner_acc_status_v2(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n) | |
| 996 | +{ | |
| 997 | + return pmic_arb->intr + 0x100000 + 0x1000 * m + 0x4 * n; | |
| 998 | +} | |
| 999 | + | |
| 1000 | +static void __iomem * | |
| 1001 | +pmic_arb_owner_acc_status_v3(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n) | |
| 1002 | +{ | |
| 1003 | + return pmic_arb->intr + 0x200000 + 0x1000 * m + 0x4 * n; | |
| 1004 | +} | |
| 1005 | + | |
| 1006 | +static void __iomem * | |
| 1007 | +pmic_arb_owner_acc_status_v5(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n) | |
| 1008 | +{ | |
| 1009 | + return pmic_arb->intr + 0x10000 * m + 0x4 * n; | |
| 1010 | +} | |
| 1011 | + | |
| 1012 | +static void __iomem * | |
| 1013 | +pmic_arb_acc_enable_v1(struct spmi_pmic_arb *pmic_arb, u16 n) | |
| 1014 | +{ | |
| 1015 | + return pmic_arb->intr + 0x200 + 0x4 * n; | |
| 1016 | +} | |
| 1017 | + | |
| 1018 | +static void __iomem * | |
| 1019 | +pmic_arb_acc_enable_v2(struct spmi_pmic_arb *pmic_arb, u16 n) | |
| 1020 | +{ | |
| 1021 | + return pmic_arb->intr + 0x1000 * n; | |
| 1022 | +} | |
| 1023 | + | |
| 1024 | +static void __iomem * | |
| 1025 | +pmic_arb_acc_enable_v5(struct spmi_pmic_arb *pmic_arb, u16 n) | |
| 900 | 1026 | { |
| 901 | − return 0x20 * m + 0x4 * n; | |
| 1027 | + return pmic_arb->wr_base + 0x100 + 0x10000 * n; | |
| 902 | 1028 | } |
| 903 | 1029 | |
| 904 | −static u32 pmic_arb_owner_acc_status_v2(u8 m, u16 n) | |
| 1030 | +static void __iomem * | |
| 1031 | +pmic_arb_irq_status_v1(struct spmi_pmic_arb *pmic_arb, u16 n) | |
| 905 | 1032 | { |
| 906 | − return 0x100000 + 0x1000 * m + 0x4 * n; | |
| 1033 | + return pmic_arb->intr + 0x600 + 0x4 * n; | |
| 907 | 1034 | } |
| 908 | 1035 | |
| 909 | −static u32 pmic_arb_owner_acc_status_v3(u8 m, u16 n) | |
| 1036 | +static void __iomem * | |
| 1037 | +pmic_arb_irq_status_v2(struct spmi_pmic_arb *pmic_arb, u16 n) | |
| 910 | 1038 | { |
| 911 | − return 0x200000 + 0x1000 * m + 0x4 * n; | |
| 1039 | + return pmic_arb->intr + 0x4 + 0x1000 * n; | |
| 912 | 1040 | } |
| 913 | 1041 | |
| 914 | −static u32 pmic_arb_acc_enable_v1(u16 n) | |
| 1042 | +static void __iomem * | |
| 1043 | +pmic_arb_irq_status_v5(struct spmi_pmic_arb *pmic_arb, u16 n) | |
| 915 | 1044 | { |
| 916 | − return 0x200 + 0x4 * n; | |
| 1045 | + return pmic_arb->wr_base + 0x104 + 0x10000 * n; | |
| 917 | 1046 | } |
| 918 | 1047 | |
| 919 | −static u32 pmic_arb_acc_enable_v2(u16 n) | |
| 1048 | +static void __iomem * | |
| 1049 | +pmic_arb_irq_clear_v1(struct spmi_pmic_arb *pmic_arb, u16 n) | |
| 920 | 1050 | { |
| 921 | − return 0x1000 * n; | |
| 1051 | + return pmic_arb->intr + 0xA00 + 0x4 * n; | |
| 922 | 1052 | } |
| 923 | 1053 | |
| 924 | −static u32 pmic_arb_irq_status_v1(u16 n) | |
| 1054 | +static void __iomem * | |
| 1055 | +pmic_arb_irq_clear_v2(struct spmi_pmic_arb *pmic_arb, u16 n) | |
| 925 | 1056 | { |
| 926 | − return 0x600 + 0x4 * n; | |
| 1057 | + return pmic_arb->intr + 0x8 + 0x1000 * n; | |
| 927 | 1058 | } |
| 928 | 1059 | |
| 929 | −static u32 pmic_arb_irq_status_v2(u16 n) | |
| 1060 | +static void __iomem * | |
| 1061 | +pmic_arb_irq_clear_v5(struct spmi_pmic_arb *pmic_arb, u16 n) | |
| 930 | 1062 | { |
| 931 | − return 0x4 + 0x1000 * n; | |
| 1063 | + return pmic_arb->wr_base + 0x108 + 0x10000 * n; | |
| 932 | 1064 | } |
| 933 | 1065 | |
| 934 | −static u32 pmic_arb_irq_clear_v1(u16 n) | |
| 1066 | +static u32 pmic_arb_apid_map_offset_v2(u16 n) | |
| 935 | 1067 | { |
| 936 | − return 0xA00 + 0x4 * n; | |
| 1068 | + return 0x800 + 0x4 * n; | |
| 937 | 1069 | } |
| 938 | 1070 | |
| 939 | −static u32 pmic_arb_irq_clear_v2(u16 n) | |
| 1071 | +static u32 pmic_arb_apid_map_offset_v5(u16 n) | |
| 940 | 1072 | { |
| 941 | − return 0x8 + 0x1000 * n; | |
| 1073 | + return 0x900 + 0x4 * n; | |
| 942 | 1074 | } |
| 943 | 1075 | |
| 944 | 1076 | static const struct pmic_arb_ver_ops pmic_arb_v1 = { |
| 945 | 1077 | .ver_str = "v1", |
| 946 | 1078 | .ppid_to_apid = pmic_arb_ppid_to_apid_v1, |
| 947 | − .mode = pmic_arb_mode_v1_v3, | |
| 948 | 1079 | .non_data_cmd = pmic_arb_non_data_cmd_v1, |
| 949 | 1080 | .offset = pmic_arb_offset_v1, |
| 950 | 1081 | .fmt_cmd = pmic_arb_fmt_cmd_v1, |
| @@ -952,12 +1083,12 @@ static const struct pmic_arb_ver_ops pmic_arb_v1 = { · 1 unmodified line | ||
| 952 | 1083 | .acc_enable = pmic_arb_acc_enable_v1, |
| 953 | 1084 | .irq_status = pmic_arb_irq_status_v1, |
| 954 | 1085 | .irq_clear = pmic_arb_irq_clear_v1, |
| 1086 | + .apid_map_offset = pmic_arb_apid_map_offset_v2, | |
| 955 | 1087 | }; |
| 956 | 1088 | |
| 957 | 1089 | static const struct pmic_arb_ver_ops pmic_arb_v2 = { |
| 958 | 1090 | .ver_str = "v2", |
| 959 | 1091 | .ppid_to_apid = pmic_arb_ppid_to_apid_v2, |
| 960 | − .mode = pmic_arb_mode_v2, | |
| 961 | 1092 | .non_data_cmd = pmic_arb_non_data_cmd_v2, |
| 962 | 1093 | .offset = pmic_arb_offset_v2, |
| 963 | 1094 | .fmt_cmd = pmic_arb_fmt_cmd_v2, |
| @@ -965,12 +1096,12 @@ static const struct pmic_arb_ver_ops pmic_arb_v2 = { · 1 unmodified line | ||
| 965 | 1096 | .acc_enable = pmic_arb_acc_enable_v2, |
| 966 | 1097 | .irq_status = pmic_arb_irq_status_v2, |
| 967 | 1098 | .irq_clear = pmic_arb_irq_clear_v2, |
| 1099 | + .apid_map_offset = pmic_arb_apid_map_offset_v2, | |
| 968 | 1100 | }; |
| 969 | 1101 | |
| 970 | 1102 | static const struct pmic_arb_ver_ops pmic_arb_v3 = { |
| 971 | 1103 | .ver_str = "v3", |
| 972 | 1104 | .ppid_to_apid = pmic_arb_ppid_to_apid_v2, |
| 973 | − .mode = pmic_arb_mode_v1_v3, | |
| 974 | 1105 | .non_data_cmd = pmic_arb_non_data_cmd_v2, |
| 975 | 1106 | .offset = pmic_arb_offset_v2, |
| 976 | 1107 | .fmt_cmd = pmic_arb_fmt_cmd_v2, |
| @@ -978,6 +1109,20 @@ static const struct pmic_arb_ver_ops pmic_arb_v3 = { · 1 unmodified line | ||
| 978 | 1109 | .acc_enable = pmic_arb_acc_enable_v2, |
| 979 | 1110 | .irq_status = pmic_arb_irq_status_v2, |
| 980 | 1111 | .irq_clear = pmic_arb_irq_clear_v2, |
| 1112 | + .apid_map_offset = pmic_arb_apid_map_offset_v2, | |
| 1113 | +}; | |
| 1114 | + | |
| 1115 | +static const struct pmic_arb_ver_ops pmic_arb_v5 = { | |
| 1116 | + .ver_str = "v5", | |
| 1117 | + .ppid_to_apid = pmic_arb_ppid_to_apid_v5, | |
| 1118 | + .non_data_cmd = pmic_arb_non_data_cmd_v2, | |
| 1119 | + .offset = pmic_arb_offset_v5, | |
| 1120 | + .fmt_cmd = pmic_arb_fmt_cmd_v2, | |
| 1121 | + .owner_acc_status = pmic_arb_owner_acc_status_v5, | |
| 1122 | + .acc_enable = pmic_arb_acc_enable_v5, | |
| 1123 | + .irq_status = pmic_arb_irq_status_v5, | |
| 1124 | + .irq_clear = pmic_arb_irq_clear_v5, | |
| 1125 | + .apid_map_offset = pmic_arb_apid_map_offset_v5, | |
| 981 | 1126 | }; |
| 982 | 1127 | |
| 983 | 1128 | static const struct irq_domain_ops pmic_arb_irq_domain_ops = { |
| @@ -987,37 +1132,34 @@ static const struct irq_domain_ops pmic_arb_irq_domain_ops = { · 3 unmodified lines | ||
| 987 | 1132 | |
| 988 | 1133 | static int spmi_pmic_arb_probe(struct platform_device *pdev) |
| 989 | 1134 | { |
| 990 | − struct spmi_pmic_arb *pa; | |
| 1135 | + struct spmi_pmic_arb *pmic_arb; | |
| 991 | 1136 | struct spmi_controller *ctrl; |
| 992 | 1137 | struct resource *res; |
| 993 | 1138 | void __iomem *core; |
| 1139 | + u32 *mapping_table; | |
| 994 | 1140 | u32 channel, ee, hw_ver; |
| 995 | 1141 | int err; |
| 996 | 1142 | |
| 997 | − ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pa)); | |
| 1143 | + ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pmic_arb)); | |
| 998 | 1144 | if (!ctrl) |
| 999 | 1145 | return -ENOMEM; |
| 1000 | 1146 | |
| 1001 | − pa = spmi_controller_get_drvdata(ctrl); | |
| 1002 | − pa->spmic = ctrl; | |
| 1147 | + pmic_arb = spmi_controller_get_drvdata(ctrl); | |
| 1148 | + pmic_arb->spmic = ctrl; | |
| 1003 | 1149 | |
| 1004 | 1150 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core"); |
| 1005 | − pa->core_size = resource_size(res); | |
| 1006 | − if (pa->core_size <= 0x800) { | |
| 1007 | − dev_err(&pdev->dev, "core_size is smaller than 0x800. Failing Probe\n"); | |
| 1008 | − err = -EINVAL; | |
| 1009 | − goto err_put_ctrl; | |
| 1010 | − } | |
| 1011 | − | |
| 1012 | 1151 | core = devm_ioremap_resource(&ctrl->dev, res); |
| 1013 | 1152 | if (IS_ERR(core)) { |
| 1014 | 1153 | err = PTR_ERR(core); |
| 1015 | 1154 | goto err_put_ctrl; |
| 1016 | 1155 | } |
| 1017 | 1156 | |
| 1018 | − pa->ppid_to_apid = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PPID, | |
| 1019 | − sizeof(*pa->ppid_to_apid), GFP_KERNEL); | |
| 1020 | − if (!pa->ppid_to_apid) { | |
| 1157 | + pmic_arb->core_size = resource_size(res); | |
| 1158 | + | |
| 1159 | + pmic_arb->ppid_to_apid = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PPID, | |
| 1160 | + sizeof(*pmic_arb->ppid_to_apid), | |
| 1161 | + GFP_KERNEL); | |
| 1162 | + if (!pmic_arb->ppid_to_apid) { | |
| 1021 | 1163 | err = -ENOMEM; |
| 1022 | 1164 | goto err_put_ctrl; |
| 1023 | 1165 | } |
| @@ -1025,57 +1167,56 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) · 1 unmodified line | ||
| 1025 | 1167 | hw_ver = readl_relaxed(core + PMIC_ARB_VERSION); |
| 1026 | 1168 | |
| 1027 | 1169 | if (hw_ver < PMIC_ARB_VERSION_V2_MIN) { |
| 1028 | − pa->ver_ops = &pmic_arb_v1; | |
| 1029 | − pa->wr_base = core; | |
| 1030 | − pa->rd_base = core; | |
| 1170 | + pmic_arb->ver_ops = &pmic_arb_v1; | |
| 1171 | + pmic_arb->wr_base = core; | |
| 1172 | + pmic_arb->rd_base = core; | |
| 1031 | 1173 | } else { |
| 1032 | − pa->core = core; | |
| 1174 | + pmic_arb->core = core; | |
| 1033 | 1175 | |
| 1034 | 1176 | if (hw_ver < PMIC_ARB_VERSION_V3_MIN) |
| 1035 | − pa->ver_ops = &pmic_arb_v2; | |
| 1177 | + pmic_arb->ver_ops = &pmic_arb_v2; | |
| 1178 | + else if (hw_ver < PMIC_ARB_VERSION_V5_MIN) | |
| 1179 | + pmic_arb->ver_ops = &pmic_arb_v3; | |
| 1036 | 1180 | else |
| 1037 | − pa->ver_ops = &pmic_arb_v3; | |
| 1038 | − | |
| 1039 | − /* the apid to ppid table starts at PMIC_ARB_REG_CHNL(0) */ | |
| 1040 | − pa->max_periph = (pa->core_size - PMIC_ARB_REG_CHNL(0)) / 4; | |
| 1181 | + pmic_arb->ver_ops = &pmic_arb_v5; | |
| 1041 | 1182 | |
| 1042 | 1183 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, |
| 1043 | 1184 | "obsrvr"); |
| 1044 | − pa->rd_base = devm_ioremap_resource(&ctrl->dev, res); | |
| 1045 | − if (IS_ERR(pa->rd_base)) { | |
| 1046 | − err = PTR_ERR(pa->rd_base); | |
| 1185 | + pmic_arb->rd_base = devm_ioremap_resource(&ctrl->dev, res); | |
| 1186 | + if (IS_ERR(pmic_arb->rd_base)) { | |
| 1187 | + err = PTR_ERR(pmic_arb->rd_base); | |
| 1047 | 1188 | goto err_put_ctrl; |
| 1048 | 1189 | } |
| 1049 | 1190 | |
| 1050 | 1191 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, |
| 1051 | 1192 | "chnls"); |
| 1052 | − pa->wr_base = devm_ioremap_resource(&ctrl->dev, res); | |
| 1053 | − if (IS_ERR(pa->wr_base)) { | |
| 1054 | − err = PTR_ERR(pa->wr_base); | |
| 1193 | + pmic_arb->wr_base = devm_ioremap_resource(&ctrl->dev, res); | |
| 1194 | + if (IS_ERR(pmic_arb->wr_base)) { | |
| 1195 | + err = PTR_ERR(pmic_arb->wr_base); | |
| 1055 | 1196 | goto err_put_ctrl; |
| 1056 | 1197 | } |
| 1057 | 1198 | } |
| 1058 | 1199 | |
| 1059 | 1200 | dev_info(&ctrl->dev, "PMIC arbiter version %s (0x%x)\n", |
| 1060 | − pa->ver_ops->ver_str, hw_ver); | |
| 1201 | + pmic_arb->ver_ops->ver_str, hw_ver); | |
| 1061 | 1202 | |
| 1062 | 1203 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr"); |
| 1063 | − pa->intr = devm_ioremap_resource(&ctrl->dev, res); | |
| 1064 | − if (IS_ERR(pa->intr)) { | |
| 1065 | − err = PTR_ERR(pa->intr); | |
| 1204 | + pmic_arb->intr = devm_ioremap_resource(&ctrl->dev, res); | |
| 1205 | + if (IS_ERR(pmic_arb->intr)) { | |
| 1206 | + err = PTR_ERR(pmic_arb->intr); | |
| 1066 | 1207 | goto err_put_ctrl; |
| 1067 | 1208 | } |
| 1068 | 1209 | |
| 1069 | 1210 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cnfg"); |
| 1070 | − pa->cnfg = devm_ioremap_resource(&ctrl->dev, res); | |
| 1071 | − if (IS_ERR(pa->cnfg)) { | |
| 1072 | − err = PTR_ERR(pa->cnfg); | |
| 1211 | + pmic_arb->cnfg = devm_ioremap_resource(&ctrl->dev, res); | |
| 1212 | + if (IS_ERR(pmic_arb->cnfg)) { | |
| 1213 | + err = PTR_ERR(pmic_arb->cnfg); | |
| 1073 | 1214 | goto err_put_ctrl; |
| 1074 | 1215 | } |
| 1075 | 1216 | |
| 1076 | − pa->irq = platform_get_irq_byname(pdev, "periph_irq"); | |
| 1077 | − if (pa->irq < 0) { | |
| 1078 | − err = pa->irq; | |
| 1217 | + pmic_arb->irq = platform_get_irq_byname(pdev, "periph_irq"); | |
| 1218 | + if (pmic_arb->irq < 0) { | |
| 1219 | + err = pmic_arb->irq; | |
| 1079 | 1220 | goto err_put_ctrl; |
| 1080 | 1221 | } |
| 1081 | 1222 | |
| @@ -1092,7 +1233,7 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) · 10 unmodified lines | ||
| 1092 | 1233 | goto err_put_ctrl; |
| 1093 | 1234 | } |
| 1094 | 1235 | |
| 1095 | − pa->channel = channel; | |
| 1236 | + pmic_arb->channel = channel; | |
| 1096 | 1237 | |
| 1097 | 1238 | err = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &ee); |
| 1098 | 1239 | if (err) { |
| @@ -1106,39 +1247,47 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) · 7 unmodified lines | ||
| 1106 | 1247 | goto err_put_ctrl; |
| 1107 | 1248 | } |
| 1108 | 1249 | |
| 1109 | − pa->ee = ee; | |
| 1110 | − | |
| 1111 | − pa->mapping_table = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PERIPHS - 1, | |
| 1112 | − sizeof(*pa->mapping_table), GFP_KERNEL); | |
| 1113 | − if (!pa->mapping_table) { | |
| 1250 | + pmic_arb->ee = ee; | |
| 1251 | + mapping_table = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PERIPHS, | |
| 1252 | + sizeof(*mapping_table), GFP_KERNEL); | |
| 1253 | + if (!mapping_table) { | |
| 1114 | 1254 | err = -ENOMEM; |
| 1115 | 1255 | goto err_put_ctrl; |
| 1116 | 1256 | } |
| 1117 | 1257 | |
| 1258 | + pmic_arb->mapping_table = mapping_table; | |
| 1118 | 1259 | /* Initialize max_apid/min_apid to the opposite bounds, during |
| 1119 | 1260 | * the irq domain translation, we are sure to update these */ |
| 1120 | − pa->max_apid = 0; | |
| 1121 | − pa->min_apid = PMIC_ARB_MAX_PERIPHS - 1; | |
| 1261 | + pmic_arb->max_apid = 0; | |
| 1262 | + pmic_arb->min_apid = PMIC_ARB_MAX_PERIPHS - 1; | |
| 1122 | 1263 | |
| 1123 | 1264 | platform_set_drvdata(pdev, ctrl); |
| 1124 | − raw_spin_lock_init(&pa->lock); | |
| 1265 | + raw_spin_lock_init(&pmic_arb->lock); | |
| 1125 | 1266 | |
| 1126 | 1267 | ctrl->cmd = pmic_arb_cmd; |
| 1127 | 1268 | ctrl->read_cmd = pmic_arb_read_cmd; |
| 1128 | 1269 | ctrl->write_cmd = pmic_arb_write_cmd; |
| 1129 | 1270 | |
| 1271 | + if (hw_ver >= PMIC_ARB_VERSION_V5_MIN) { | |
| 1272 | + err = pmic_arb_read_apid_map_v5(pmic_arb); | |
| 1273 | + if (err) { | |
| 1274 | + dev_err(&pdev->dev, "could not read APID->PPID mapping table, rc= %d\n", | |
| 1275 | + err); | |
| 1276 | + goto err_put_ctrl; | |
| 1277 | + } | |
| 1278 | + } | |
| 1279 | + | |
| 1130 | 1280 | dev_dbg(&pdev->dev, "adding irq domain\n"); |
| 1131 | − pa->domain = irq_domain_add_tree(pdev->dev.of_node, | |
| 1132 | − &pmic_arb_irq_domain_ops, pa); | |
| 1133 | − if (!pa->domain) { | |
| 1281 | + pmic_arb->domain = irq_domain_add_tree(pdev->dev.of_node, | |
| 1282 | + &pmic_arb_irq_domain_ops, pmic_arb); | |
| 1283 | + if (!pmic_arb->domain) { | |
| 1134 | 1284 | dev_err(&pdev->dev, "unable to create irq_domain\n"); |
| 1135 | 1285 | err = -ENOMEM; |
| 1136 | 1286 | goto err_put_ctrl; |
| 1137 | 1287 | } |
| 1138 | 1288 | |
| 1139 | − irq_set_chained_handler_and_data(pa->irq, pmic_arb_chained_irq, pa); | |
| 1140 | − enable_irq_wake(pa->irq); | |
| 1141 | − | |
| 1289 | + irq_set_chained_handler_and_data(pmic_arb->irq, pmic_arb_chained_irq, | |
| 1290 | + pmic_arb); | |
| 1142 | 1291 | err = spmi_controller_add(ctrl); |
| 1143 | 1292 | if (err) |
| 1144 | 1293 | goto err_domain_remove; |
| @@ -1146,8 +1295,8 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) · 1 unmodified line | ||
| 1146 | 1295 | return 0; |
| 1147 | 1296 | |
| 1148 | 1297 | err_domain_remove: |
| 1149 | − irq_set_chained_handler_and_data(pa->irq, NULL, NULL); | |
| 1150 | − irq_domain_remove(pa->domain); | |
| 1298 | + irq_set_chained_handler_and_data(pmic_arb->irq, NULL, NULL); | |
| 1299 | + irq_domain_remove(pmic_arb->domain); | |
| 1151 | 1300 | err_put_ctrl: |
| 1152 | 1301 | spmi_controller_put(ctrl); |
| 1153 | 1302 | return err; |
| @@ -1156,10 +1305,10 @@ err_put_ctrl: · 2 unmodified lines | ||
| 1156 | 1305 | static int spmi_pmic_arb_remove(struct platform_device *pdev) |
| 1157 | 1306 | { |
| 1158 | 1307 | struct spmi_controller *ctrl = platform_get_drvdata(pdev); |
| 1159 | − struct spmi_pmic_arb *pa = spmi_controller_get_drvdata(ctrl); | |
| 1308 | + struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); | |
| 1160 | 1309 | spmi_controller_remove(ctrl); |
| 1161 | − irq_set_chained_handler_and_data(pa->irq, NULL, NULL); | |
| 1162 | − irq_domain_remove(pa->domain); | |
| 1310 | + irq_set_chained_handler_and_data(pmic_arb->irq, NULL, NULL); | |
| 1311 | + irq_domain_remove(pmic_arb->domain); | |
| 1163 | 1312 | spmi_controller_put(ctrl); |
| 1164 | 1313 | return 0; |
| 1165 | 1314 | } |
| @@ -466,27 +466,25 @@ static void of_spmi_register_devices(struct spmi_controller *ctrl) · 465 unmodified lines | ||
| 466 | 466 | struct spmi_device *sdev; |
| 467 | 467 | u32 reg[2]; |
| 468 | 468 | |
| 469 | − dev_dbg(&ctrl->dev, "adding child %s\n", node->full_name); | |
| 469 | + dev_dbg(&ctrl->dev, "adding child %pOF\n", node); | |
| 470 | 470 | |
| 471 | 471 | err = of_property_read_u32_array(node, "reg", reg, 2); |
| 472 | 472 | if (err) { |
| 473 | 473 | dev_err(&ctrl->dev, |
| 474 | − "node %s err (%d) does not have 'reg' property\n", | |
| 475 | − node->full_name, err); | |
| 474 | + "node %pOF err (%d) does not have 'reg' property\n", | |
| 475 | + node, err); | |
| 476 | 476 | continue; |
| 477 | 477 | } |
| 478 | 478 | |
| 479 | 479 | if (reg[1] != SPMI_USID) { |
| 480 | 480 | dev_err(&ctrl->dev, |
| 481 | − "node %s contains unsupported 'reg' entry\n", | |
| 482 | − node->full_name); | |
| 481 | + "node %pOF contains unsupported 'reg' entry\n", | |
| 482 | + node); | |
| 483 | 483 | continue; |
| 484 | 484 | } |
| 485 | 485 | |
| 486 | 486 | if (reg[0] >= SPMI_MAX_SLAVE_ID) { |
| 487 | − dev_err(&ctrl->dev, | |
| 488 | − "invalid usid on node %s\n", | |
| 489 | − node->full_name); | |
| 487 | + dev_err(&ctrl->dev, "invalid usid on node %pOF\n", node); | |
| 490 | 488 | continue; |
| 491 | 489 | } |
| 492 | 490 | |
| @@ -804,7 +804,7 @@ struct tb_cfg_result tb_cfg_reset(struct tb_ctl *ctl, u64 route, · 803 unmodified lines | ||
| 804 | 804 | req->request_type = TB_CFG_PKG_RESET; |
| 805 | 805 | req->response = &reply; |
| 806 | 806 | req->response_size = sizeof(reply); |
| 807 | − req->response_type = sizeof(TB_CFG_PKG_RESET); | |
| 807 | + req->response_type = TB_CFG_PKG_RESET; | |
| 808 | 808 | |
| 809 | 809 | res = tb_cfg_request_sync(ctl, req, timeout_msec); |
| 810 | 810 | |
| @@ -807,11 +807,11 @@ static ssize_t key_store(struct device *dev, struct device_attribute *attr, · 806 unmodified lines | ||
| 807 | 807 | struct tb_switch *sw = tb_to_switch(dev); |
| 808 | 808 | u8 key[TB_SWITCH_KEY_SIZE]; |
| 809 | 809 | ssize_t ret = count; |
| 810 | + bool clear = false; | |
| 810 | 811 | |
| 811 | − if (count < 64) | |
| 812 | − return -EINVAL; | |
| 813 | − | |
| 814 | − if (hex2bin(key, buf, sizeof(key))) | |
| 812 | + if (!strcmp(buf, "\n")) | |
| 813 | + clear = true; | |
| 814 | + else if (hex2bin(key, buf, sizeof(key))) | |
| 815 | 815 | return -EINVAL; |
| 816 | 816 | |
| 817 | 817 | if (mutex_lock_interruptible(&switch_lock)) |
| @@ -821,15 +821,19 @@ static ssize_t key_store(struct device *dev, struct device_attribute *attr, · 3 unmodified lines | ||
| 821 | 821 | ret = -EBUSY; |
| 822 | 822 | } else { |
| 823 | 823 | kfree(sw->key); |
| 824 | − sw->key = kmemdup(key, sizeof(key), GFP_KERNEL); | |
| 825 | − if (!sw->key) | |
| 826 | − ret = -ENOMEM; | |
| 824 | + if (clear) { | |
| 825 | + sw->key = NULL; | |
| 826 | + } else { | |
| 827 | + sw->key = kmemdup(key, sizeof(key), GFP_KERNEL); | |
| 828 | + if (!sw->key) | |
| 829 | + ret = -ENOMEM; | |
| 830 | + } | |
| 827 | 831 | } |
| 828 | 832 | |
| 829 | 833 | mutex_unlock(&switch_lock); |
| 830 | 834 | return ret; |
| 831 | 835 | } |
| 832 | −static DEVICE_ATTR_RW(key); | |
| 836 | +static DEVICE_ATTR(key, 0600, key_show, key_store); | |
| 833 | 837 | |
| 834 | 838 | static ssize_t nvm_authenticate_show(struct device *dev, |
| 835 | 839 | struct device_attribute *attr, char *buf) |
| @@ -184,7 +184,7 @@ static int udc_plat_probe(struct platform_device *pdev) · 183 unmodified lines | ||
| 184 | 184 | goto exit_phy; |
| 185 | 185 | } |
| 186 | 186 | |
| 187 | − ret = extcon_get_cable_state_(udc->edev, EXTCON_USB); | |
| 187 | + ret = extcon_get_state(udc->edev, EXTCON_USB); | |
| 188 | 188 | if (ret < 0) { |
| 189 | 189 | dev_err(dev, "Can't get cable state\n"); |
| 190 | 190 | goto exit_extcon; |
| @@ -273,7 +273,7 @@ static int udc_plat_suspend(struct device *dev) · 82 unmodified lines | ||
| 273 | 273 | udc = dev_get_drvdata(dev); |
| 274 | 274 | stop_udc(udc); |
| 275 | 275 | |
| 276 | − if (extcon_get_cable_state_(udc->edev, EXTCON_USB) > 0) { | |
| 276 | + if (extcon_get_state(udc->edev, EXTCON_USB) > 0) { | |
| 277 | 277 | dev_dbg(udc->dev, "device -> idle\n"); |
| 278 | 278 | stop_udc(udc); |
| 279 | 279 | } |
| @@ -303,7 +303,7 @@ static int udc_plat_resume(struct device *dev) · 23 unmodified lines | ||
| 303 | 303 | return ret; |
| 304 | 304 | } |
| 305 | 305 | |
| 306 | − if (extcon_get_cable_state_(udc->edev, EXTCON_USB) > 0) { | |
| 306 | + if (extcon_get_state(udc->edev, EXTCON_USB) > 0) { | |
| 307 | 307 | dev_dbg(udc->dev, "idle -> device\n"); |
| 308 | 308 | start_udc(udc); |
| 309 | 309 | } |
| @@ -95,7 +95,8 @@ static struct { · 94 unmodified lines | ||
| 95 | 95 | |
| 96 | 96 | struct ds1wm_data { |
| 97 | 97 | void __iomem *map; |
| 98 | − int bus_shift; /* # of shifts to calc register offsets */ | |
| 98 | + unsigned int bus_shift; /* # of shifts to calc register offsets */ | |
| 99 | + bool is_hw_big_endian; | |
| 99 | 100 | struct platform_device *pdev; |
| 100 | 101 | const struct mfd_cell *cell; |
| 101 | 102 | int irq; |
| @@ -115,12 +116,65 @@ struct ds1wm_data { · 13 unmodified lines | ||
| 115 | 116 | static inline void ds1wm_write_register(struct ds1wm_data *ds1wm_data, u32 reg, |
| 116 | 117 | u8 val) |
| 117 | 118 | { |
| 118 | − __raw_writeb(val, ds1wm_data->map + (reg << ds1wm_data->bus_shift)); | |
| 119 | + if (ds1wm_data->is_hw_big_endian) { | |
| 120 | + switch (ds1wm_data->bus_shift) { | |
| 121 | + case 0: | |
| 122 | + iowrite8(val, ds1wm_data->map + (reg << 0)); | |
| 123 | + break; | |
| 124 | + case 1: | |
| 125 | + iowrite16be((u16)val, ds1wm_data->map + (reg << 1)); | |
| 126 | + break; | |
| 127 | + case 2: | |
| 128 | + iowrite32be((u32)val, ds1wm_data->map + (reg << 2)); | |
| 129 | + break; | |
| 130 | + } | |
| 131 | + } else { | |
| 132 | + switch (ds1wm_data->bus_shift) { | |
| 133 | + case 0: | |
| 134 | + iowrite8(val, ds1wm_data->map + (reg << 0)); | |
| 135 | + break; | |
| 136 | + case 1: | |
| 137 | + iowrite16((u16)val, ds1wm_data->map + (reg << 1)); | |
| 138 | + break; | |
| 139 | + case 2: | |
| 140 | + iowrite32((u32)val, ds1wm_data->map + (reg << 2)); | |
| 141 | + break; | |
| 142 | + } | |
| 143 | + } | |
| 119 | 144 | } |
| 120 | 145 | |
| 121 | 146 | static inline u8 ds1wm_read_register(struct ds1wm_data *ds1wm_data, u32 reg) |
| 122 | 147 | { |
| 123 | − return __raw_readb(ds1wm_data->map + (reg << ds1wm_data->bus_shift)); | |
| 148 | + u32 val = 0; | |
| 149 | + | |
| 150 | + if (ds1wm_data->is_hw_big_endian) { | |
| 151 | + switch (ds1wm_data->bus_shift) { | |
| 152 | + case 0: | |
| 153 | + val = ioread8(ds1wm_data->map + (reg << 0)); | |
| 154 | + break; | |
| 155 | + case 1: | |
| 156 | + val = ioread16be(ds1wm_data->map + (reg << 1)); | |
| 157 | + break; | |
| 158 | + case 2: | |
| 159 | + val = ioread32be(ds1wm_data->map + (reg << 2)); | |
| 160 | + break; | |
| 161 | + } | |
| 162 | + } else { | |
| 163 | + switch (ds1wm_data->bus_shift) { | |
| 164 | + case 0: | |
| 165 | + val = ioread8(ds1wm_data->map + (reg << 0)); | |
| 166 | + break; | |
| 167 | + case 1: | |
| 168 | + val = ioread16(ds1wm_data->map + (reg << 1)); | |
| 169 | + break; | |
| 170 | + case 2: | |
| 171 | + val = ioread32(ds1wm_data->map + (reg << 2)); | |
| 172 | + break; | |
| 173 | + } | |
| 174 | + } | |
| 175 | + dev_dbg(&ds1wm_data->pdev->dev, | |
| 176 | + "ds1wm_read_register reg: %d, 32 bit val:%x\n", reg, val); | |
| 177 | + return (u8)val; | |
| 124 | 178 | } |
| 125 | 179 | |
| 126 | 180 | |
| @@ -455,6 +509,7 @@ static int ds1wm_probe(struct platform_device *pdev) · 328 unmodified lines | ||
| 455 | 509 | struct ds1wm_driver_data *plat; |
| 456 | 510 | struct resource *res; |
| 457 | 511 | int ret; |
| 512 | + u8 inten; | |
| 458 | 513 | |
| 459 | 514 | if (!pdev) |
| 460 | 515 | return -ENODEV; |
| @@ -473,9 +528,6 @@ static int ds1wm_probe(struct platform_device *pdev) · 12 unmodified lines | ||
| 473 | 528 | if (!ds1wm_data->map) |
| 474 | 529 | return -ENOMEM; |
| 475 | 530 | |
| 476 | − /* calculate bus shift from mem resource */ | |
| 477 | − ds1wm_data->bus_shift = resource_size(res) >> 3; | |
| 478 | − | |
| 479 | 531 | ds1wm_data->pdev = pdev; |
| 480 | 532 | ds1wm_data->cell = mfd_get_cell(pdev); |
| 481 | 533 | if (!ds1wm_data->cell) |
| @@ -484,6 +536,26 @@ static int ds1wm_probe(struct platform_device *pdev) · 2 unmodified lines | ||
| 484 | 536 | if (!plat) |
| 485 | 537 | return -ENODEV; |
| 486 | 538 | |
| 539 | + /* how many bits to shift register number to get register offset */ | |
| 540 | + if (plat->bus_shift > 2) { | |
| 541 | + dev_err(&ds1wm_data->pdev->dev, | |
| 542 | + "illegal bus shift %d, not written", | |
| 543 | + ds1wm_data->bus_shift); | |
| 544 | + return -EINVAL; | |
| 545 | + } | |
| 546 | + | |
| 547 | + ds1wm_data->bus_shift = plat->bus_shift; | |
| 548 | + /* make sure resource has space for 8 registers */ | |
| 549 | + if ((8 << ds1wm_data->bus_shift) > resource_size(res)) { | |
| 550 | + dev_err(&ds1wm_data->pdev->dev, | |
| 551 | + "memory resource size %d to small, should be %d\n", | |
| 552 | + (int)resource_size(res), | |
| 553 | + 8 << ds1wm_data->bus_shift); | |
| 554 | + return -EINVAL; | |
| 555 | + } | |
| 556 | + | |
| 557 | + ds1wm_data->is_hw_big_endian = plat->is_hw_big_endian; | |
| 558 | + | |
| 487 | 559 | res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); |
| 488 | 560 | if (!res) |
| 489 | 561 | return -ENXIO; |
| @@ -491,15 +563,30 @@ static int ds1wm_probe(struct platform_device *pdev) · 1 unmodified line | ||
| 491 | 563 | ds1wm_data->int_en_reg_none = (plat->active_high ? DS1WM_INTEN_IAS : 0); |
| 492 | 564 | ds1wm_data->reset_recover_delay = plat->reset_recover_delay; |
| 493 | 565 | |
| 566 | + /* Mask interrupts, set IAS before claiming interrupt */ | |
| 567 | + inten = ds1wm_read_register(ds1wm_data, DS1WM_INT_EN); | |
| 568 | + ds1wm_write_register(ds1wm_data, | |
| 569 | + DS1WM_INT_EN, ds1wm_data->int_en_reg_none); | |
| 570 | + | |
| 494 | 571 | if (res->flags & IORESOURCE_IRQ_HIGHEDGE) |
| 495 | 572 | irq_set_irq_type(ds1wm_data->irq, IRQ_TYPE_EDGE_RISING); |
| 496 | 573 | if (res->flags & IORESOURCE_IRQ_LOWEDGE) |
| 497 | 574 | irq_set_irq_type(ds1wm_data->irq, IRQ_TYPE_EDGE_FALLING); |
| 575 | + if (res->flags & IORESOURCE_IRQ_HIGHLEVEL) | |
| 576 | + irq_set_irq_type(ds1wm_data->irq, IRQ_TYPE_LEVEL_HIGH); | |
| 577 | + if (res->flags & IORESOURCE_IRQ_LOWLEVEL) | |
| 578 | + irq_set_irq_type(ds1wm_data->irq, IRQ_TYPE_LEVEL_LOW); | |
| 498 | 579 | |
| 499 | 580 | ret = devm_request_irq(&pdev->dev, ds1wm_data->irq, ds1wm_isr, |
| 500 | 581 | IRQF_SHARED, "ds1wm", ds1wm_data); |
| 501 | − if (ret) | |
| 582 | + if (ret) { | |
| 583 | + dev_err(&ds1wm_data->pdev->dev, | |
| 584 | + "devm_request_irq %d failed with errno %d\n", | |
| 585 | + ds1wm_data->irq, | |
| 586 | + ret); | |
| 587 | + | |
| 502 | 588 | return ret; |
| 589 | + } | |
| 503 | 590 | |
| 504 | 591 | ds1wm_up(ds1wm_data); |
| 505 | 592 | |
| @@ -509,6 +596,13 @@ static int ds1wm_probe(struct platform_device *pdev) · 3 unmodified lines | ||
| 509 | 596 | if (ret) |
| 510 | 597 | goto err; |
| 511 | 598 | |
| 599 | + dev_dbg(&ds1wm_data->pdev->dev, | |
| 600 | + "ds1wm: probe successful, IAS: %d, rec.delay: %d, clockrate: %d, bus-shift: %d, is Hw Big Endian: %d\n", | |
| 601 | + plat->active_high, | |
| 602 | + plat->reset_recover_delay, | |
| 603 | + plat->clock_rate, | |
| 604 | + ds1wm_data->bus_shift, | |
| 605 | + ds1wm_data->is_hw_big_endian); | |
| 512 | 606 | return 0; |
| 513 | 607 | |
| 514 | 608 | err: |
| @@ -70,6 +70,8 @@ MODULE_PARM_DESC(active_pullup, "Active pullup (apply to all buses): " \ · 69 unmodified lines | ||
| 70 | 70 | #define DS2482_REG_CFG_PPM 0x02 /* presence pulse masking */ |
| 71 | 71 | #define DS2482_REG_CFG_APU 0x01 /* active pull-up */ |
| 72 | 72 | |
| 73 | +/* extra configurations - e.g. 1WS */ | |
| 74 | +int extra_config; | |
| 73 | 75 | |
| 74 | 76 | /** |
| 75 | 77 | * Write and verify codes for the CHANNEL_SELECT command (DS2482-800 only). |
| @@ -403,7 +405,7 @@ static u8 ds2482_w1_reset_bus(void *data) · 327 unmodified lines | ||
| 403 | 405 | /* If the chip did reset since detect, re-config it */ |
| 404 | 406 | if (err & DS2482_REG_STS_RST) |
| 405 | 407 | ds2482_send_cmd_data(pdev, DS2482_CMD_WRITE_CONFIG, |
| 406 | − ds2482_calculate_config(0x00)); | |
| 408 | + ds2482_calculate_config(extra_config)); | |
| 407 | 409 | } |
| 408 | 410 | |
| 409 | 411 | mutex_unlock(&pdev->access_lock); |
| @@ -429,8 +431,7 @@ static u8 ds2482_w1_set_pullup(void *data, int delay) · 19 unmodified lines | ||
| 429 | 431 | ds2482_wait_1wire_idle(pdev); |
| 430 | 432 | /* note: it seems like both SPU and APU have to be set! */ |
| 431 | 433 | retval = ds2482_send_cmd_data(pdev, DS2482_CMD_WRITE_CONFIG, |
| 432 | − ds2482_calculate_config(DS2482_REG_CFG_SPU | | |
| 433 | − DS2482_REG_CFG_APU)); | |
| 434 | + ds2482_calculate_config(extra_config|DS2482_REG_CFG_SPU|DS2482_REG_CFG_APU)); | |
| 434 | 435 | ds2482_wait_1wire_idle(pdev); |
| 435 | 436 | } |
| 436 | 437 | |
| @@ -483,7 +484,7 @@ static int ds2482_probe(struct i2c_client *client, · 46 unmodified lines | ||
| 483 | 484 | |
| 484 | 485 | /* Set all config items to 0 (off) */ |
| 485 | 486 | ds2482_send_cmd_data(data, DS2482_CMD_WRITE_CONFIG, |
| 486 | − ds2482_calculate_config(0x00)); | |
| 487 | + ds2482_calculate_config(extra_config)); | |
| 487 | 488 | |
| 488 | 489 | mutex_init(&data->access_lock); |
| 489 | 490 | |
| @@ -558,4 +559,7 @@ module_i2c_driver(ds2482_driver); · 68 unmodified lines | ||
| 558 | 559 | |
| 559 | 560 | MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>"); |
| 560 | 561 | MODULE_DESCRIPTION("DS2482 driver"); |
| 562 | +module_param(extra_config, int, S_IRUGO | S_IWUSR); | |
| 563 | +MODULE_PARM_DESC(extra_config, "Extra Configuration settings 1=APU,2=PPM,3=SPU,8=1WS"); | |
| 564 | + | |
| 561 | 565 | MODULE_LICENSE("GPL"); |
| @@ -1088,7 +1088,7 @@ static void ds_disconnect(struct usb_interface *intf) · 1087 unmodified lines | ||
| 1088 | 1088 | kfree(dev); |
| 1089 | 1089 | } |
| 1090 | 1090 | |
| 1091 | −static struct usb_device_id ds_id_table [] = { | |
| 1091 | +static const struct usb_device_id ds_id_table[] = { | |
| 1092 | 1092 | { USB_DEVICE(0x04fa, 0x2490) }, |
| 1093 | 1093 | { }, |
| 1094 | 1094 | }; |
| @@ -65,6 +65,14 @@ config W1_SLAVE_DS2423 · 64 unmodified lines | ||
| 65 | 65 | Say Y here if you want to use a 1-wire |
| 66 | 66 | counter family device (DS2423). |
| 67 | 67 | |
| 68 | +config W1_SLAVE_DS2805 | |
| 69 | + tristate "112-byte EEPROM support (DS28E05)" | |
| 70 | + help | |
| 71 | + Say Y here if you want to use a 1-wire | |
| 72 | + is a 112-byte user-programmable EEPROM is | |
| 73 | + organized as 7 pages of 16 bytes each with 64bit | |
| 74 | + unique number. Requires OverDrive Speed to talk to. | |
| 75 | + | |
| 68 | 76 | config W1_SLAVE_DS2431 |
| 69 | 77 | tristate "1kb EEPROM family support (DS2431)" |
| 70 | 78 | help |
| @@ -10,6 +10,7 @@ obj-$(CONFIG_W1_SLAVE_DS2413) += w1_ds2413.o · 9 unmodified lines | ||
| 10 | 10 | obj-$(CONFIG_W1_SLAVE_DS2406) += w1_ds2406.o |
| 11 | 11 | obj-$(CONFIG_W1_SLAVE_DS2423) += w1_ds2423.o |
| 12 | 12 | obj-$(CONFIG_W1_SLAVE_DS2431) += w1_ds2431.o |
| 13 | +obj-$(CONFIG_W1_SLAVE_DS2805) += w1_ds2805.o | |
| 13 | 14 | obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o |
| 14 | 15 | obj-$(CONFIG_W1_SLAVE_DS2438) += w1_ds2438.o |
| 15 | 16 | obj-$(CONFIG_W1_SLAVE_DS2760) += w1_ds2760.o |
| @@ -51,7 +51,7 @@ · 50 unmodified lines | ||
| 51 | 51 | #define DS2438_CURRENT_MSB 0x06 |
| 52 | 52 | #define DS2438_THRESHOLD 0x07 |
| 53 | 53 | |
| 54 | −int w1_ds2438_get_page(struct w1_slave *sl, int pageno, u8 *buf) | |
| 54 | +static int w1_ds2438_get_page(struct w1_slave *sl, int pageno, u8 *buf) | |
| 55 | 55 | { |
| 56 | 56 | unsigned int retries = W1_DS2438_RETRIES; |
| 57 | 57 | u8 w1_buf[2]; |
| @@ -85,7 +85,7 @@ int w1_ds2438_get_page(struct w1_slave *sl, int pageno, u8 *buf) · 27 unmodified lines | ||
| 85 | 85 | return -1; |
| 86 | 86 | } |
| 87 | 87 | |
| 88 | −int w1_ds2438_get_temperature(struct w1_slave *sl, int16_t *temperature) | |
| 88 | +static int w1_ds2438_get_temperature(struct w1_slave *sl, int16_t *temperature) | |
| 89 | 89 | { |
| 90 | 90 | unsigned int retries = W1_DS2438_RETRIES; |
| 91 | 91 | u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/]; |
| @@ -127,7 +127,7 @@ post_unlock: · 35 unmodified lines | ||
| 127 | 127 | return ret; |
| 128 | 128 | } |
| 129 | 129 | |
| 130 | −int w1_ds2438_change_config_bit(struct w1_slave *sl, u8 mask, u8 value) | |
| 130 | +static int w1_ds2438_change_config_bit(struct w1_slave *sl, u8 mask, u8 value) | |
| 131 | 131 | { |
| 132 | 132 | unsigned int retries = W1_DS2438_RETRIES; |
| 133 | 133 | u8 w1_buf[3]; |
| @@ -186,7 +186,8 @@ int w1_ds2438_change_config_bit(struct w1_slave *sl, u8 mask, u8 value) · 52 unmodified lines | ||
| 186 | 186 | return -1; |
| 187 | 187 | } |
| 188 | 188 | |
| 189 | −uint16_t w1_ds2438_get_voltage(struct w1_slave *sl, int adc_input, uint16_t *voltage) | |
| 189 | +static uint16_t w1_ds2438_get_voltage(struct w1_slave *sl, | |
| 190 | + int adc_input, uint16_t *voltage) | |
| 190 | 191 | { |
| 191 | 192 | unsigned int retries = W1_DS2438_RETRIES; |
| 192 | 193 | u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/]; |
| @@ -0,0 +1,313 @@ | ||
| 1 | +/* | |
| 2 | + * w1_ds2805 - w1 family 0d (DS28E05) driver | |
| 3 | + * | |
| 4 | + * Copyright (c) 2016 Andrew Worsley amworsley@gmail.com | |
| 5 | + * | |
| 6 | + * This source code is licensed under the GNU General Public License, | |
| 7 | + * Version 2. See the file COPYING for more details. | |
| 8 | + */ | |
| 9 | + | |
| 10 | +#include <linux/kernel.h> | |
| 11 | +#include <linux/module.h> | |
| 12 | +#include <linux/moduleparam.h> | |
| 13 | +#include <linux/device.h> | |
| 14 | +#include <linux/types.h> | |
| 15 | +#include <linux/delay.h> | |
| 16 | + | |
| 17 | +#include <linux/w1.h> | |
| 18 | + | |
| 19 | +#define W1_EEPROM_DS2805 0x0D | |
| 20 | + | |
| 21 | +#define W1_F0D_EEPROM_SIZE 128 | |
| 22 | +#define W1_F0D_PAGE_BITS 3 | |
| 23 | +#define W1_F0D_PAGE_SIZE (1<<W1_F0D_PAGE_BITS) | |
| 24 | +#define W1_F0D_PAGE_MASK 0x0F | |
| 25 | + | |
| 26 | +#define W1_F0D_SCRATCH_BITS 1 | |
| 27 | +#define W1_F0D_SCRATCH_SIZE (1<<W1_F0D_SCRATCH_BITS) | |
| 28 | +#define W1_F0D_SCRATCH_MASK (W1_F0D_SCRATCH_SIZE-1) | |
| 29 | + | |
| 30 | +#define W1_F0D_READ_EEPROM 0xF0 | |
| 31 | +#define W1_F0D_WRITE_EEPROM 0x55 | |
| 32 | +#define W1_F0D_RELEASE 0xFF | |
| 33 | + | |
| 34 | +#define W1_F0D_CS_OK 0xAA /* Chip Status Ok */ | |
| 35 | + | |
| 36 | +#define W1_F0D_TPROG_MS 16 | |
| 37 | + | |
| 38 | +#define W1_F0D_READ_RETRIES 10 | |
| 39 | +#define W1_F0D_READ_MAXLEN W1_F0D_EEPROM_SIZE | |
| 40 | + | |
| 41 | +/* | |
| 42 | + * Check the file size bounds and adjusts count as needed. | |
| 43 | + * This would not be needed if the file size didn't reset to 0 after a write. | |
| 44 | + */ | |
| 45 | +static inline size_t w1_f0d_fix_count(loff_t off, size_t count, size_t size) | |
| 46 | +{ | |
| 47 | + if (off > size) | |
| 48 | + return 0; | |
| 49 | + | |
| 50 | + if ((off + count) > size) | |
| 51 | + return size - off; | |
| 52 | + | |
| 53 | + return count; | |
| 54 | +} | |
| 55 | + | |
| 56 | +/* | |
| 57 | + * Read a block from W1 ROM two times and compares the results. | |
| 58 | + * If they are equal they are returned, otherwise the read | |
| 59 | + * is repeated W1_F0D_READ_RETRIES times. | |
| 60 | + * | |
| 61 | + * count must not exceed W1_F0D_READ_MAXLEN. | |
| 62 | + */ | |
| 63 | +static int w1_f0d_readblock(struct w1_slave *sl, int off, int count, char *buf) | |
| 64 | +{ | |
| 65 | + u8 wrbuf[3]; | |
| 66 | + u8 cmp[W1_F0D_READ_MAXLEN]; | |
| 67 | + int tries = W1_F0D_READ_RETRIES; | |
| 68 | + | |
| 69 | + do { | |
| 70 | + wrbuf[0] = W1_F0D_READ_EEPROM; | |
| 71 | + wrbuf[1] = off & 0x7f; | |
| 72 | + wrbuf[2] = 0; | |
| 73 | + | |
| 74 | + if (w1_reset_select_slave(sl)) | |
| 75 | + return -1; | |
| 76 | + | |
| 77 | + w1_write_block(sl->master, wrbuf, sizeof(wrbuf)); | |
| 78 | + w1_read_block(sl->master, buf, count); | |
| 79 | + | |
| 80 | + if (w1_reset_select_slave(sl)) | |
| 81 | + return -1; | |
| 82 | + | |
| 83 | + w1_write_block(sl->master, wrbuf, sizeof(wrbuf)); | |
| 84 | + w1_read_block(sl->master, cmp, count); | |
| 85 | + | |
| 86 | + if (!memcmp(cmp, buf, count)) | |
| 87 | + return 0; | |
| 88 | + } while (--tries); | |
| 89 | + | |
| 90 | + dev_err(&sl->dev, "proof reading failed %d times\n", | |
| 91 | + W1_F0D_READ_RETRIES); | |
| 92 | + | |
| 93 | + return -1; | |
| 94 | +} | |
| 95 | + | |
| 96 | +static ssize_t w1_f0d_read_bin(struct file *filp, struct kobject *kobj, | |
| 97 | + struct bin_attribute *bin_attr, | |
| 98 | + char *buf, loff_t off, size_t count) | |
| 99 | +{ | |
| 100 | + struct w1_slave *sl = kobj_to_w1_slave(kobj); | |
| 101 | + int todo = count; | |
| 102 | + | |
| 103 | + count = w1_f0d_fix_count(off, count, W1_F0D_EEPROM_SIZE); | |
| 104 | + if (count == 0) | |
| 105 | + return 0; | |
| 106 | + | |
| 107 | + mutex_lock(&sl->master->mutex); | |
| 108 | + | |
| 109 | + /* read directly from the EEPROM in chunks of W1_F0D_READ_MAXLEN */ | |
| 110 | + while (todo > 0) { | |
| 111 | + int block_read; | |
| 112 | + | |
| 113 | + if (todo >= W1_F0D_READ_MAXLEN) | |
| 114 | + block_read = W1_F0D_READ_MAXLEN; | |
| 115 | + else | |
| 116 | + block_read = todo; | |
| 117 | + | |
| 118 | + if (w1_f0d_readblock(sl, off, block_read, buf) < 0) { | |
| 119 | + count = -EIO; | |
| 120 | + break; | |
| 121 | + } | |
| 122 | + | |
| 123 | + todo -= W1_F0D_READ_MAXLEN; | |
| 124 | + buf += W1_F0D_READ_MAXLEN; | |
| 125 | + off += W1_F0D_READ_MAXLEN; | |
| 126 | + } | |
| 127 | + | |
| 128 | + mutex_unlock(&sl->master->mutex); | |
| 129 | + | |
| 130 | + return count; | |
| 131 | +} | |
| 132 | + | |
| 133 | +/* | |
| 134 | + * Writes to the scratchpad and reads it back for verification. | |
| 135 | + * Then copies the scratchpad to EEPROM. | |
| 136 | + * The data must be aligned at W1_F0D_SCRATCH_SIZE bytes and | |
| 137 | + * must be W1_F0D_SCRATCH_SIZE bytes long. | |
| 138 | + * The master must be locked. | |
| 139 | + * | |
| 140 | + * @param sl The slave structure | |
| 141 | + * @param addr Address for the write | |
| 142 | + * @param len length must be <= (W1_F0D_PAGE_SIZE - (addr & W1_F0D_PAGE_MASK)) | |
| 143 | + * @param data The data to write | |
| 144 | + * @return 0=Success -1=failure | |
| 145 | + */ | |
| 146 | +static int w1_f0d_write(struct w1_slave *sl, int addr, int len, const u8 *data) | |
| 147 | +{ | |
| 148 | + int tries = W1_F0D_READ_RETRIES; | |
| 149 | + u8 wrbuf[3]; | |
| 150 | + u8 rdbuf[W1_F0D_SCRATCH_SIZE]; | |
| 151 | + u8 cs; | |
| 152 | + | |
| 153 | + if ((addr & 1) || (len != 2)) { | |
| 154 | + dev_err(&sl->dev, "%s: bad addr/len - addr=%#x len=%d\n", | |
| 155 | + __func__, addr, len); | |
| 156 | + return -1; | |
| 157 | + } | |
| 158 | + | |
| 159 | +retry: | |
| 160 | + | |
| 161 | + /* Write the data to the scratchpad */ | |
| 162 | + if (w1_reset_select_slave(sl)) | |
| 163 | + return -1; | |
| 164 | + | |
| 165 | + wrbuf[0] = W1_F0D_WRITE_EEPROM; | |
| 166 | + wrbuf[1] = addr & 0xff; | |
| 167 | + wrbuf[2] = 0xff; /* ?? from Example */ | |
| 168 | + | |
| 169 | + w1_write_block(sl->master, wrbuf, sizeof(wrbuf)); | |
| 170 | + w1_write_block(sl->master, data, len); | |
| 171 | + | |
| 172 | + w1_read_block(sl->master, rdbuf, sizeof(rdbuf)); | |
| 173 | + /* Compare what was read against the data written */ | |
| 174 | + if ((rdbuf[0] != data[0]) || (rdbuf[1] != data[1])) { | |
| 175 | + | |
| 176 | + if (--tries) | |
| 177 | + goto retry; | |
| 178 | + | |
| 179 | + dev_err(&sl->dev, | |
| 180 | + "could not write to eeprom, scratchpad compare failed %d times\n", | |
| 181 | + W1_F0D_READ_RETRIES); | |
| 182 | + pr_info("%s: rdbuf = %#x %#x data = %#x %#x\n", | |
| 183 | + __func__, rdbuf[0], rdbuf[1], data[0], data[1]); | |
| 184 | + | |
| 185 | + return -1; | |
| 186 | + } | |
| 187 | + | |
| 188 | + /* Trigger write out to EEPROM */ | |
| 189 | + w1_write_8(sl->master, W1_F0D_RELEASE); | |
| 190 | + | |
| 191 | + /* Sleep for tprog ms to wait for the write to complete */ | |
| 192 | + msleep(W1_F0D_TPROG_MS); | |
| 193 | + | |
| 194 | + /* Check CS (Command Status) == 0xAA ? */ | |
| 195 | + cs = w1_read_8(sl->master); | |
| 196 | + if (cs != W1_F0D_CS_OK) { | |
| 197 | + dev_err(&sl->dev, "save to eeprom failed = CS=%#x\n", cs); | |
| 198 | + return -1; | |
| 199 | + } | |
| 200 | + | |
| 201 | + return 0; | |
| 202 | +} | |
| 203 | + | |
| 204 | +static ssize_t w1_f0d_write_bin(struct file *filp, struct kobject *kobj, | |
| 205 | + struct bin_attribute *bin_attr, | |
| 206 | + char *buf, loff_t off, size_t count) | |
| 207 | +{ | |
| 208 | + struct w1_slave *sl = kobj_to_w1_slave(kobj); | |
| 209 | + int addr, len; | |
| 210 | + int copy; | |
| 211 | + | |
| 212 | + count = w1_f0d_fix_count(off, count, W1_F0D_EEPROM_SIZE); | |
| 213 | + if (count == 0) | |
| 214 | + return 0; | |
| 215 | + | |
| 216 | + mutex_lock(&sl->master->mutex); | |
| 217 | + | |
| 218 | + /* Can only write data in blocks of the size of the scratchpad */ | |
| 219 | + addr = off; | |
| 220 | + len = count; | |
| 221 | + while (len > 0) { | |
| 222 | + | |
| 223 | + /* if len too short or addr not aligned */ | |
| 224 | + if (len < W1_F0D_SCRATCH_SIZE || addr & W1_F0D_SCRATCH_MASK) { | |
| 225 | + char tmp[W1_F0D_SCRATCH_SIZE]; | |
| 226 | + | |
| 227 | + /* read the block and update the parts to be written */ | |
| 228 | + if (w1_f0d_readblock(sl, addr & ~W1_F0D_SCRATCH_MASK, | |
| 229 | + W1_F0D_SCRATCH_SIZE, tmp)) { | |
| 230 | + count = -EIO; | |
| 231 | + goto out_up; | |
| 232 | + } | |
| 233 | + | |
| 234 | + /* copy at most to the boundary of the PAGE or len */ | |
| 235 | + copy = W1_F0D_SCRATCH_SIZE - | |
| 236 | + (addr & W1_F0D_SCRATCH_MASK); | |
| 237 | + | |
| 238 | + if (copy > len) | |
| 239 | + copy = len; | |
| 240 | + | |
| 241 | + memcpy(&tmp[addr & W1_F0D_SCRATCH_MASK], buf, copy); | |
| 242 | + if (w1_f0d_write(sl, addr & ~W1_F0D_SCRATCH_MASK, | |
| 243 | + W1_F0D_SCRATCH_SIZE, tmp) < 0) { | |
| 244 | + count = -EIO; | |
| 245 | + goto out_up; | |
| 246 | + } | |
| 247 | + } else { | |
| 248 | + | |
| 249 | + copy = W1_F0D_SCRATCH_SIZE; | |
| 250 | + if (w1_f0d_write(sl, addr, copy, buf) < 0) { | |
| 251 | + count = -EIO; | |
| 252 | + goto out_up; | |
| 253 | + } | |
| 254 | + } | |
| 255 | + buf += copy; | |
| 256 | + addr += copy; | |
| 257 | + len -= copy; | |
| 258 | + } | |
| 259 | + | |
| 260 | +out_up: | |
| 261 | + mutex_unlock(&sl->master->mutex); | |
| 262 | + | |
| 263 | + return count; | |
| 264 | +} | |
| 265 | + | |
| 266 | +static struct bin_attribute w1_f0d_bin_attr = { | |
| 267 | + .attr = { | |
| 268 | + .name = "eeprom", | |
| 269 | + .mode = S_IRUGO | S_IWUSR, | |
| 270 | + }, | |
| 271 | + .size = W1_F0D_EEPROM_SIZE, | |
| 272 | + .read = w1_f0d_read_bin, | |
| 273 | + .write = w1_f0d_write_bin, | |
| 274 | +}; | |
| 275 | + | |
| 276 | +static int w1_f0d_add_slave(struct w1_slave *sl) | |
| 277 | +{ | |
| 278 | + return sysfs_create_bin_file(&sl->dev.kobj, &w1_f0d_bin_attr); | |
| 279 | +} | |
| 280 | + | |
| 281 | +static void w1_f0d_remove_slave(struct w1_slave *sl) | |
| 282 | +{ | |
| 283 | + sysfs_remove_bin_file(&sl->dev.kobj, &w1_f0d_bin_attr); | |
| 284 | +} | |
| 285 | + | |
| 286 | +static struct w1_family_ops w1_f0d_fops = { | |
| 287 | + .add_slave = w1_f0d_add_slave, | |
| 288 | + .remove_slave = w1_f0d_remove_slave, | |
| 289 | +}; | |
| 290 | + | |
| 291 | +static struct w1_family w1_family_2d = { | |
| 292 | + .fid = W1_EEPROM_DS2805, | |
| 293 | + .fops = &w1_f0d_fops, | |
| 294 | +}; | |
| 295 | + | |
| 296 | +static int __init w1_f0d_init(void) | |
| 297 | +{ | |
| 298 | + pr_info("%s()\n", __func__); | |
| 299 | + return w1_register_family(&w1_family_2d); | |
| 300 | +} | |
| 301 | + | |
| 302 | +static void __exit w1_f0d_fini(void) | |
| 303 | +{ | |
| 304 | + pr_info("%s()\n", __func__); | |
| 305 | + w1_unregister_family(&w1_family_2d); | |
| 306 | +} | |
| 307 | + | |
| 308 | +module_init(w1_f0d_init); | |
| 309 | +module_exit(w1_f0d_fini); | |
| 310 | + | |
| 311 | +MODULE_LICENSE("GPL"); | |
| 312 | +MODULE_AUTHOR("Andrew Worsley amworsley@gmail.com"); | |
| 313 | +MODULE_DESCRIPTION("w1 family 0d driver for DS2805, 1kb EEPROM"); | |
| @@ -29,6 +29,7 @@ · 28 unmodified lines | ||
| 29 | 29 | #include <linux/types.h> |
| 30 | 30 | #include <linux/slab.h> |
| 31 | 31 | #include <linux/delay.h> |
| 32 | +#include <linux/hwmon.h> | |
| 32 | 33 | |
| 33 | 34 | #include <linux/w1.h> |
| 34 | 35 | |
| @@ -59,6 +60,12 @@ struct w1_therm_family_data { · 24 unmodified lines | ||
| 59 | 60 | atomic_t refcnt; |
| 60 | 61 | }; |
| 61 | 62 | |
| 63 | +struct therm_info { | |
| 64 | + u8 rom[9]; | |
| 65 | + u8 crc; | |
| 66 | + u8 verdict; | |
| 67 | +}; | |
| 68 | + | |
| 62 | 69 | /* return the address of the refcnt in the family data */ |
| 63 | 70 | #define THERM_REFCNT(family_data) \ |
| 64 | 71 | (&((struct w1_therm_family_data *)family_data)->refcnt) |
| @@ -107,19 +114,72 @@ static struct attribute *w1_ds28ea00_attrs[] = { · 42 unmodified lines | ||
| 107 | 114 | &dev_attr_w1_seq.attr, |
| 108 | 115 | NULL, |
| 109 | 116 | }; |
| 117 | + | |
| 110 | 118 | ATTRIBUTE_GROUPS(w1_therm); |
| 111 | 119 | ATTRIBUTE_GROUPS(w1_ds28ea00); |
| 112 | 120 | |
| 121 | +#if IS_REACHABLE(CONFIG_HWMON) | |
| 122 | +static int w1_read_temp(struct device *dev, u32 attr, int channel, | |
| 123 | + long *val); | |
| 124 | + | |
| 125 | +static umode_t w1_is_visible(const void *_data, enum hwmon_sensor_types type, | |
| 126 | + u32 attr, int channel) | |
| 127 | +{ | |
| 128 | + return attr == hwmon_temp_input ? 0444 : 0; | |
| 129 | +} | |
| 130 | + | |
| 131 | +static int w1_read(struct device *dev, enum hwmon_sensor_types type, | |
| 132 | + u32 attr, int channel, long *val) | |
| 133 | +{ | |
| 134 | + switch (type) { | |
| 135 | + case hwmon_temp: | |
| 136 | + return w1_read_temp(dev, attr, channel, val); | |
| 137 | + default: | |
| 138 | + return -EOPNOTSUPP; | |
| 139 | + } | |
| 140 | +} | |
| 141 | + | |
| 142 | +static const u32 w1_temp_config[] = { | |
| 143 | + HWMON_T_INPUT, | |
| 144 | + 0 | |
| 145 | +}; | |
| 146 | + | |
| 147 | +static const struct hwmon_channel_info w1_temp = { | |
| 148 | + .type = hwmon_temp, | |
| 149 | + .config = w1_temp_config, | |
| 150 | +}; | |
| 151 | + | |
| 152 | +static const struct hwmon_channel_info *w1_info[] = { | |
| 153 | + &w1_temp, | |
| 154 | + NULL | |
| 155 | +}; | |
| 156 | + | |
| 157 | +static const struct hwmon_ops w1_hwmon_ops = { | |
| 158 | + .is_visible = w1_is_visible, | |
| 159 | + .read = w1_read, | |
| 160 | +}; | |
| 161 | + | |
| 162 | +static const struct hwmon_chip_info w1_chip_info = { | |
| 163 | + .ops = &w1_hwmon_ops, | |
| 164 | + .info = w1_info, | |
| 165 | +}; | |
| 166 | +#define W1_CHIPINFO (&w1_chip_info) | |
| 167 | +#else | |
| 168 | +#define W1_CHIPINFO NULL | |
| 169 | +#endif | |
| 170 | + | |
| 113 | 171 | static struct w1_family_ops w1_therm_fops = { |
| 114 | 172 | .add_slave = w1_therm_add_slave, |
| 115 | 173 | .remove_slave = w1_therm_remove_slave, |
| 116 | 174 | .groups = w1_therm_groups, |
| 175 | + .chip_info = W1_CHIPINFO, | |
| 117 | 176 | }; |
| 118 | 177 | |
| 119 | 178 | static struct w1_family_ops w1_ds28ea00_fops = { |
| 120 | 179 | .add_slave = w1_therm_add_slave, |
| 121 | 180 | .remove_slave = w1_therm_remove_slave, |
| 122 | 181 | .groups = w1_ds28ea00_groups, |
| 182 | + .chip_info = W1_CHIPINFO, | |
| 123 | 183 | }; |
| 124 | 184 | |
| 125 | 185 | static struct w1_family w1_therm_family_DS18S20 = { |
| @@ -422,33 +482,31 @@ static ssize_t w1_slave_store(struct device *device, · 296 unmodified lines | ||
| 422 | 482 | return ret ? : size; |
| 423 | 483 | } |
| 424 | 484 | |
| 425 | −static ssize_t w1_slave_show(struct device *device, | |
| 426 | − struct device_attribute *attr, char *buf) | |
| 485 | +static ssize_t read_therm(struct device *device, | |
| 486 | + struct w1_slave *sl, struct therm_info *info) | |
| 427 | 487 | { |
| 428 | − struct w1_slave *sl = dev_to_w1_slave(device); | |
| 429 | 488 | struct w1_master *dev = sl->master; |
| 430 | − u8 rom[9], crc, verdict, external_power; | |
| 431 | − int i, ret, max_trying = 10; | |
| 432 | − ssize_t c = PAGE_SIZE; | |
| 489 | + u8 external_power; | |
| 490 | + int ret, max_trying = 10; | |
| 433 | 491 | u8 *family_data = sl->family_data; |
| 434 | 492 | |
| 435 | 493 | ret = mutex_lock_interruptible(&dev->bus_mutex); |
| 436 | 494 | if (ret != 0) |
| 437 | − goto post_unlock; | |
| 495 | + goto error; | |
| 438 | 496 | |
| 439 | − if (!sl->family_data) { | |
| 497 | + if (!family_data) { | |
| 440 | 498 | ret = -ENODEV; |
| 441 | − goto pre_unlock; | |
| 499 | + goto mt_unlock; | |
| 442 | 500 | } |
| 443 | 501 | |
| 444 | 502 | /* prevent the slave from going away in sleep */ |
| 445 | 503 | atomic_inc(THERM_REFCNT(family_data)); |
| 446 | − memset(rom, 0, sizeof(rom)); | |
| 504 | + memset(info->rom, 0, sizeof(info->rom)); | |
| 447 | 505 | |
| 448 | 506 | while (max_trying--) { |
| 449 | 507 | |
| 450 | − verdict = 0; | |
| 451 | − crc = 0; | |
| 508 | + info->verdict = 0; | |
| 509 | + info->crc = 0; | |
| 452 | 510 | |
| 453 | 511 | if (!w1_reset_select_slave(sl)) { |
| 454 | 512 | int count = 0; |
| @@ -474,47 +532,69 @@ static ssize_t w1_slave_show(struct device *device, · 19 unmodified lines | ||
| 474 | 532 | sleep_rem = msleep_interruptible(tm); |
| 475 | 533 | if (sleep_rem != 0) { |
| 476 | 534 | ret = -EINTR; |
| 477 | − goto post_unlock; | |
| 535 | + goto dec_refcnt; | |
| 478 | 536 | } |
| 479 | 537 | |
| 480 | 538 | ret = mutex_lock_interruptible(&dev->bus_mutex); |
| 481 | 539 | if (ret != 0) |
| 482 | − goto post_unlock; | |
| 540 | + goto dec_refcnt; | |
| 483 | 541 | } else if (!w1_strong_pullup) { |
| 484 | 542 | sleep_rem = msleep_interruptible(tm); |
| 485 | 543 | if (sleep_rem != 0) { |
| 486 | 544 | ret = -EINTR; |
| 487 | − goto pre_unlock; | |
| 545 | + goto dec_refcnt; | |
| 488 | 546 | } |
| 489 | 547 | } |
| 490 | 548 | |
| 491 | 549 | if (!w1_reset_select_slave(sl)) { |
| 492 | 550 | |
| 493 | 551 | w1_write_8(dev, W1_READ_SCRATCHPAD); |
| 494 | − count = w1_read_block(dev, rom, 9); | |
| 552 | + count = w1_read_block(dev, info->rom, 9); | |
| 495 | 553 | if (count != 9) { |
| 496 | 554 | dev_warn(device, "w1_read_block() " |
| 497 | 555 | "returned %u instead of 9.\n", |
| 498 | 556 | count); |
| 499 | 557 | } |
| 500 | 558 | |
| 501 | − crc = w1_calc_crc8(rom, 8); | |
| 559 | + info->crc = w1_calc_crc8(info->rom, 8); | |
| 502 | 560 | |
| 503 | − if (rom[8] == crc) | |
| 504 | − verdict = 1; | |
| 561 | + if (info->rom[8] == info->crc) | |
| 562 | + info->verdict = 1; | |
| 505 | 563 | } |
| 506 | 564 | } |
| 507 | 565 | |
| 508 | − if (verdict) | |
| 566 | + if (info->verdict) | |
| 509 | 567 | break; |
| 510 | 568 | } |
| 511 | 569 | |
| 570 | +dec_refcnt: | |
| 571 | + atomic_dec(THERM_REFCNT(family_data)); | |
| 572 | +mt_unlock: | |
| 573 | + mutex_unlock(&dev->bus_mutex); | |
| 574 | +error: | |
| 575 | + return ret; | |
| 576 | +} | |
| 577 | + | |
| 578 | +static ssize_t w1_slave_show(struct device *device, | |
| 579 | + struct device_attribute *attr, char *buf) | |
| 580 | +{ | |
| 581 | + struct w1_slave *sl = dev_to_w1_slave(device); | |
| 582 | + struct therm_info info; | |
| 583 | + u8 *family_data = sl->family_data; | |
| 584 | + int ret, i; | |
| 585 | + ssize_t c = PAGE_SIZE; | |
| 586 | + u8 fid = sl->family->fid; | |
| 587 | + | |
| 588 | + ret = read_therm(device, sl, &info); | |
| 589 | + if (ret) | |
| 590 | + return ret; | |
| 591 | + | |
| 512 | 592 | for (i = 0; i < 9; ++i) |
| 513 | − c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", rom[i]); | |
| 593 | + c -= snprintf(buf + PAGE_SIZE - c, c, "%02x ", info.rom[i]); | |
| 514 | 594 | c -= snprintf(buf + PAGE_SIZE - c, c, ": crc=%02x %s\n", |
| 515 | − crc, (verdict) ? "YES" : "NO"); | |
| 516 | − if (verdict) | |
| 517 | − memcpy(family_data, rom, sizeof(rom)); | |
| 595 | + info.crc, (info.verdict) ? "YES" : "NO"); | |
| 596 | + if (info.verdict) | |
| 597 | + memcpy(family_data, info.rom, sizeof(info.rom)); | |
| 518 | 598 | else |
| 519 | 599 | dev_warn(device, "Read failed CRC check\n"); |
| 520 | 600 | |
| @@ -523,16 +603,42 @@ static ssize_t w1_slave_show(struct device *device, · 2 unmodified lines | ||
| 523 | 603 | ((u8 *)family_data)[i]); |
| 524 | 604 | |
| 525 | 605 | c -= snprintf(buf + PAGE_SIZE - c, c, "t=%d\n", |
| 526 | − w1_convert_temp(rom, sl->family->fid)); | |
| 606 | + w1_convert_temp(info.rom, fid)); | |
| 527 | 607 | ret = PAGE_SIZE - c; |
| 608 | + return ret; | |
| 609 | +} | |
| 528 | 610 | |
| 529 | −pre_unlock: | |
| 530 | − mutex_unlock(&dev->bus_mutex); | |
| 611 | +#if IS_REACHABLE(CONFIG_HWMON) | |
| 612 | +static int w1_read_temp(struct device *device, u32 attr, int channel, | |
| 613 | + long *val) | |
| 614 | +{ | |
| 615 | + struct w1_slave *sl = dev_get_drvdata(device); | |
| 616 | + struct therm_info info; | |
| 617 | + u8 fid = sl->family->fid; | |
| 618 | + int ret; | |
| 619 | + | |
| 620 | + switch (attr) { | |
| 621 | + case hwmon_temp_input: | |
| 622 | + ret = read_therm(device, sl, &info); | |
| 623 | + if (ret) | |
| 624 | + return ret; | |
| 625 | + | |
| 626 | + if (!info.verdict) { | |
| 627 | + ret = -EIO; | |
| 628 | + return ret; | |
| 629 | + } | |
| 630 | + | |
| 631 | + *val = w1_convert_temp(info.rom, fid); | |
| 632 | + ret = 0; | |
| 633 | + break; | |
| 634 | + default: | |
| 635 | + ret = -EOPNOTSUPP; | |
| 636 | + break; | |
| 637 | + } | |
| 531 | 638 | |
| 532 | −post_unlock: | |
| 533 | − atomic_dec(THERM_REFCNT(family_data)); | |
| 534 | 639 | return ret; |
| 535 | 640 | } |
| 641 | +#endif | |
| 536 | 642 | |
| 537 | 643 | #define W1_42_CHAIN 0x99 |
| 538 | 644 | #define W1_42_CHAIN_OFF 0x3C |
| @@ -25,6 +25,7 @@ · 24 unmodified lines | ||
| 25 | 25 | #include <linux/sched.h> |
| 26 | 26 | #include <linux/kthread.h> |
| 27 | 27 | #include <linux/freezer.h> |
| 28 | +#include <linux/hwmon.h> | |
| 28 | 29 | |
| 29 | 30 | #include <linux/atomic.h> |
| 30 | 31 | |
| @@ -568,7 +569,7 @@ static struct attribute *w1_master_default_attrs[] = { · 537 unmodified lines | ||
| 568 | 569 | NULL |
| 569 | 570 | }; |
| 570 | 571 | |
| 571 | −static struct attribute_group w1_master_defattr_group = { | |
| 572 | +static const struct attribute_group w1_master_defattr_group = { | |
| 572 | 573 | .attrs = w1_master_default_attrs, |
| 573 | 574 | }; |
| 574 | 575 | |
| @@ -649,9 +650,24 @@ static int w1_family_notify(unsigned long action, struct w1_slave *sl) · 74 unmodified lines | ||
| 649 | 650 | return err; |
| 650 | 651 | } |
| 651 | 652 | } |
| 652 | − | |
| 653 | + if (IS_REACHABLE(CONFIG_HWMON) && fops->chip_info) { | |
| 654 | + struct device *hwmon | |
| 655 | + = hwmon_device_register_with_info(&sl->dev, | |
| 656 | + "w1_slave_temp", sl, | |
| 657 | + fops->chip_info, | |
| 658 | + NULL); | |
| 659 | + if (IS_ERR(hwmon)) { | |
| 660 | + dev_warn(&sl->dev, | |
| 661 | + "could not create hwmon device\n"); | |
| 662 | + } else { | |
| 663 | + sl->hwmon = hwmon; | |
| 664 | + } | |
| 665 | + } | |
| 653 | 666 | break; |
| 654 | 667 | case BUS_NOTIFY_DEL_DEVICE: |
| 668 | + if (IS_REACHABLE(CONFIG_HWMON) && fops->chip_info && | |
| 669 | + sl->hwmon) | |
| 670 | + hwmon_device_unregister(sl->hwmon); | |
| 655 | 671 | if (fops->remove_slave) |
| 656 | 672 | sl->family->fops->remove_slave(sl); |
| 657 | 673 | if (fops->groups) |
| @@ -729,6 +745,8 @@ int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn) · 71 unmodified lines | ||
| 729 | 745 | atomic_set(&sl->refcnt, 1); |
| 730 | 746 | atomic_inc(&sl->master->refcnt); |
| 731 | 747 | dev->slave_count++; |
| 748 | + dev_info(&dev->dev, "Attaching one wire slave %02x.%012llx crc %02x\n", | |
| 749 | + rn->family, (unsigned long long)rn->id, rn->crc); | |
| 732 | 750 | |
| 733 | 751 | /* slave modules need to be loaded in a context with unlocked mutex */ |
| 734 | 752 | mutex_unlock(&dev->mutex); |
| @@ -28,6 +28,8 @@ static struct kobj_map *cdev_map; · 27 unmodified lines | ||
| 28 | 28 | |
| 29 | 29 | static DEFINE_MUTEX(chrdevs_lock); |
| 30 | 30 | |
| 31 | +#define CHRDEV_MAJOR_HASH_SIZE 255 | |
| 32 | + | |
| 31 | 33 | static struct char_device_struct { |
| 32 | 34 | struct char_device_struct *next; |
| 33 | 35 | unsigned int major; |
| @@ -49,16 +51,39 @@ void chrdev_show(struct seq_file *f, off_t offset) · 15 unmodified lines | ||
| 49 | 51 | { |
| 50 | 52 | struct char_device_struct *cd; |
| 51 | 53 | |
| 52 | − if (offset < CHRDEV_MAJOR_HASH_SIZE) { | |
| 53 | − mutex_lock(&chrdevs_lock); | |
| 54 | − for (cd = chrdevs[offset]; cd; cd = cd->next) | |
| 54 | + mutex_lock(&chrdevs_lock); | |
| 55 | + for (cd = chrdevs[major_to_index(offset)]; cd; cd = cd->next) { | |
| 56 | + if (cd->major == offset) | |
| 55 | 57 | seq_printf(f, "%3d %s\n", cd->major, cd->name); |
| 56 | − mutex_unlock(&chrdevs_lock); | |
| 57 | 58 | } |
| 59 | + mutex_unlock(&chrdevs_lock); | |
| 58 | 60 | } |
| 59 | 61 | |
| 60 | 62 | #endif /* CONFIG_PROC_FS */ |
| 61 | 63 | |
| 64 | +static int find_dynamic_major(void) | |
| 65 | +{ | |
| 66 | + int i; | |
| 67 | + struct char_device_struct *cd; | |
| 68 | + | |
| 69 | + for (i = ARRAY_SIZE(chrdevs)-1; i > CHRDEV_MAJOR_DYN_END; i--) { | |
| 70 | + if (chrdevs[i] == NULL) | |
| 71 | + return i; | |
| 72 | + } | |
| 73 | + | |
| 74 | + for (i = CHRDEV_MAJOR_DYN_EXT_START; | |
| 75 | + i > CHRDEV_MAJOR_DYN_EXT_END; i--) { | |
| 76 | + for (cd = chrdevs[major_to_index(i)]; cd; cd = cd->next) | |
| 77 | + if (cd->major == i) | |
| 78 | + break; | |
| 79 | + | |
| 80 | + if (cd == NULL || cd->major != i) | |
| 81 | + return i; | |
| 82 | + } | |
| 83 | + | |
| 84 | + return -EBUSY; | |
| 85 | +} | |
| 86 | + | |
| 62 | 87 | /* |
| 63 | 88 | * Register a single major with a specified minor range. |
| 64 | 89 | * |
| @@ -84,22 +109,21 @@ __register_chrdev_region(unsigned int major, unsigned int baseminor, · 19 unmodified lines | ||
| 84 | 109 | |
| 85 | 110 | mutex_lock(&chrdevs_lock); |
| 86 | 111 | |
| 87 | − /* temporary */ | |
| 88 | 112 | if (major == 0) { |
| 89 | − for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) { | |
| 90 | − if (chrdevs[i] == NULL) | |
| 91 | − break; | |
| 92 | − } | |
| 93 | − | |
| 94 | − if (i < CHRDEV_MAJOR_DYN_END) | |
| 95 | − pr_warn("CHRDEV \"%s\" major number %d goes below the dynamic allocation range\n", | |
| 96 | − name, i); | |
| 97 | − | |
| 98 | − if (i == 0) { | |
| 99 | − ret = -EBUSY; | |
| 113 | + ret = find_dynamic_major(); | |
| 114 | + if (ret < 0) { | |
| 115 | + pr_err("CHRDEV \"%s\" dynamic allocation region is full\n", | |
| 116 | + name); | |
| 100 | 117 | goto out; |
| 101 | 118 | } |
| 102 | − major = i; | |
| 119 | + major = ret; | |
| 120 | + } | |
| 121 | + | |
| 122 | + if (major >= CHRDEV_MAJOR_MAX) { | |
| 123 | + pr_err("CHRDEV \"%s\" major requested (%d) is greater than the maximum (%d)\n", | |
| 124 | + name, major, CHRDEV_MAJOR_MAX); | |
| 125 | + ret = -EINVAL; | |
| 126 | + goto out; | |
| 103 | 127 | } |
| 104 | 128 | |
| 105 | 129 | cd->major = major; |
| @@ -7,14 +7,14 @@ static int devinfo_show(struct seq_file *f, void *v) · 6 unmodified lines | ||
| 7 | 7 | { |
| 8 | 8 | int i = *(loff_t *) v; |
| 9 | 9 | |
| 10 | − if (i < CHRDEV_MAJOR_HASH_SIZE) { | |
| 10 | + if (i < CHRDEV_MAJOR_MAX) { | |
| 11 | 11 | if (i == 0) |
| 12 | 12 | seq_puts(f, "Character devices:\n"); |
| 13 | 13 | chrdev_show(f, i); |
| 14 | 14 | } |
| 15 | 15 | #ifdef CONFIG_BLOCK |
| 16 | 16 | else { |
| 17 | − i -= CHRDEV_MAJOR_HASH_SIZE; | |
| 17 | + i -= CHRDEV_MAJOR_MAX; | |
| 18 | 18 | if (i == 0) |
| 19 | 19 | seq_puts(f, "\nBlock devices:\n"); |
| 20 | 20 | blkdev_show(f, i); |
| @@ -25,7 +25,7 @@ static int devinfo_show(struct seq_file *f, void *v) · 4 unmodified lines | ||
| 25 | 25 | |
| 26 | 26 | static void *devinfo_start(struct seq_file *f, loff_t *pos) |
| 27 | 27 | { |
| 28 | − if (*pos < (BLKDEV_MAJOR_HASH_SIZE + CHRDEV_MAJOR_HASH_SIZE)) | |
| 28 | + if (*pos < (BLKDEV_MAJOR_MAX + CHRDEV_MAJOR_MAX)) | |
| 29 | 29 | return pos; |
| 30 | 30 | return NULL; |
| 31 | 31 | } |
| @@ -33,7 +33,7 @@ static void *devinfo_start(struct seq_file *f, loff_t *pos) · 1 unmodified line | ||
| 33 | 33 | static void *devinfo_next(struct seq_file *f, void *v, loff_t *pos) |
| 34 | 34 | { |
| 35 | 35 | (*pos)++; |
| 36 | − if (*pos >= (BLKDEV_MAJOR_HASH_SIZE + CHRDEV_MAJOR_HASH_SIZE)) | |
| 36 | + if (*pos >= (BLKDEV_MAJOR_MAX + CHRDEV_MAJOR_MAX)) | |
| 37 | 37 | return NULL; |
| 38 | 38 | return pos; |
| 39 | 39 | } |
| @@ -29,6 +29,8 @@ static inline u32 __bitrev32(u32 x) · 28 unmodified lines | ||
| 29 | 29 | |
| 30 | 30 | #endif /* CONFIG_HAVE_ARCH_BITREVERSE */ |
| 31 | 31 | |
| 32 | +#define __bitrev8x4(x) (__bitrev32(swab32(x))) | |
| 33 | + | |
| 32 | 34 | #define __constant_bitrev32(x) \ |
| 33 | 35 | ({ \ |
| 34 | 36 | u32 __x = x; \ |
| @@ -50,6 +52,15 @@ static inline u32 __bitrev32(u32 x) · 15 unmodified lines | ||
| 50 | 52 | __x; \ |
| 51 | 53 | }) |
| 52 | 54 | |
| 55 | +#define __constant_bitrev8x4(x) \ | |
| 56 | +({ \ | |
| 57 | + u32 __x = x; \ | |
| 58 | + __x = ((__x & (u32)0xF0F0F0F0UL) >> 4) | ((__x & (u32)0x0F0F0F0FUL) << 4); \ | |
| 59 | + __x = ((__x & (u32)0xCCCCCCCCUL) >> 2) | ((__x & (u32)0x33333333UL) << 2); \ | |
| 60 | + __x = ((__x & (u32)0xAAAAAAAAUL) >> 1) | ((__x & (u32)0x55555555UL) << 1); \ | |
| 61 | + __x; \ | |
| 62 | +}) | |
| 63 | + | |
| 53 | 64 | #define __constant_bitrev8(x) \ |
| 54 | 65 | ({ \ |
| 55 | 66 | u8 __x = x; \ |
| @@ -75,6 +86,14 @@ static inline u32 __bitrev32(u32 x) · 19 unmodified lines | ||
| 75 | 86 | __bitrev16(__x); \ |
| 76 | 87 | }) |
| 77 | 88 | |
| 89 | +#define bitrev8x4(x) \ | |
| 90 | +({ \ | |
| 91 | + u32 __x = x; \ | |
| 92 | + __builtin_constant_p(__x) ? \ | |
| 93 | + __constant_bitrev8x4(__x) : \ | |
| 94 | + __bitrev8x4(__x); \ | |
| 95 | + }) | |
| 96 | + | |
| 78 | 97 | #define bitrev8(x) \ |
| 79 | 98 | ({ \ |
| 80 | 99 | u8 __x = x; \ |
| @@ -24,6 +24,12 @@ · 23 unmodified lines | ||
| 24 | 24 | /* ETMv3.5/PTM's ETMCR config bit */ |
| 25 | 25 | #define ETM_OPT_CYCACC 12 |
| 26 | 26 | #define ETM_OPT_TS 28 |
| 27 | +#define ETM_OPT_RETSTK 29 | |
| 28 | + | |
| 29 | +/* ETMv4 CONFIGR programming bits for the ETM OPTs */ | |
| 30 | +#define ETM4_CFG_BIT_CYCACC 4 | |
| 31 | +#define ETM4_CFG_BIT_TS 11 | |
| 32 | +#define ETM4_CFG_BIT_RETSTK 12 | |
| 27 | 33 | |
| 28 | 34 | static inline int coresight_get_trace_id(int cpu) |
| 29 | 35 | { |
| @@ -2,8 +2,7 @@ · 1 unmodified line | ||
| 2 | 2 | * Module: eeprom_93xx46 |
| 3 | 3 | * platform description for 93xx46 EEPROMs. |
| 4 | 4 | */ |
| 5 | − | |
| 6 | −struct gpio_desc; | |
| 5 | +#include <linux/gpio/consumer.h> | |
| 7 | 6 | |
| 8 | 7 | struct eeprom_93xx46_platform_data { |
| 9 | 8 | unsigned char flags; |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | /* |
| 2 | − * External connector (extcon) class driver | |
| 2 | + * External Connector (extcon) framework | |
| 3 | 3 | * |
| 4 | 4 | * Copyright (C) 2015 Samsung Electronics |
| 5 | 5 | * Author: Chanwoo Choi <cw00.choi@samsung.com> |
| @@ -20,8 +20,7 @@ · 14 unmodified lines | ||
| 20 | 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 21 | 21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 22 | 22 | * GNU General Public License for more details. |
| 23 | − * | |
| 24 | −*/ | |
| 23 | + */ | |
| 25 | 24 | |
| 26 | 25 | #ifndef __LINUX_EXTCON_H__ |
| 27 | 26 | #define __LINUX_EXTCON_H__ |
| @@ -93,7 +92,7 @@ · 65 unmodified lines | ||
| 93 | 92 | #define EXTCON_NUM 63 |
| 94 | 93 | |
| 95 | 94 | /* |
| 96 | − * Define the property of supported external connectors. | |
| 95 | + * Define the properties of supported external connectors. | |
| 97 | 96 | * |
| 98 | 97 | * When adding the new extcon property, they *must* have |
| 99 | 98 | * the type/value/default information. Also, you *have to* |
| @@ -176,44 +175,42 @@ struct extcon_dev; · 76 unmodified lines | ||
| 176 | 175 | |
| 177 | 176 | #if IS_ENABLED(CONFIG_EXTCON) |
| 178 | 177 | |
| 179 | −/* | |
| 180 | − * Following APIs are for notifiers or configurations. | |
| 181 | − * Notifiers are the external port and connection devices. | |
| 182 | − */ | |
| 178 | +/* Following APIs register/unregister the extcon device. */ | |
| 183 | 179 | extern int extcon_dev_register(struct extcon_dev *edev); |
| 184 | 180 | extern void extcon_dev_unregister(struct extcon_dev *edev); |
| 185 | 181 | extern int devm_extcon_dev_register(struct device *dev, |
| 186 | − struct extcon_dev *edev); | |
| 182 | + struct extcon_dev *edev); | |
| 187 | 183 | extern void devm_extcon_dev_unregister(struct device *dev, |
| 188 | − struct extcon_dev *edev); | |
| 189 | −extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name); | |
| 184 | + struct extcon_dev *edev); | |
| 190 | 185 | |
| 191 | −/* | |
| 192 | − * Following APIs control the memory of extcon device. | |
| 193 | − */ | |
| 186 | +/* Following APIs allocate/free the memory of the extcon device. */ | |
| 194 | 187 | extern struct extcon_dev *extcon_dev_allocate(const unsigned int *cable); |
| 195 | 188 | extern void extcon_dev_free(struct extcon_dev *edev); |
| 196 | 189 | extern struct extcon_dev *devm_extcon_dev_allocate(struct device *dev, |
| 197 | − const unsigned int *cable); | |
| 190 | + const unsigned int *cable); | |
| 198 | 191 | extern void devm_extcon_dev_free(struct device *dev, struct extcon_dev *edev); |
| 199 | 192 | |
| 193 | +/* Synchronize the state and property value for each external connector. */ | |
| 194 | +extern int extcon_sync(struct extcon_dev *edev, unsigned int id); | |
| 195 | + | |
| 200 | 196 | /* |
| 201 | − * get/set_state access each bit of the 32b encoded state value. | |
| 202 | − * They are used to access the status of each cable based on the cable id. | |
| 197 | + * Following APIs get/set the connected state of each external connector. | |
| 198 | + * The 'id' argument indicates the defined external connector. | |
| 203 | 199 | */ |
| 204 | 200 | extern int extcon_get_state(struct extcon_dev *edev, unsigned int id); |
| 205 | 201 | extern int extcon_set_state(struct extcon_dev *edev, unsigned int id, |
| 206 | − bool cable_state); | |
| 202 | + bool state); | |
| 207 | 203 | extern int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id, |
| 208 | − bool cable_state); | |
| 209 | −/* | |
| 210 | − * Synchronize the state and property data for a specific external connector. | |
| 211 | − */ | |
| 212 | −extern int extcon_sync(struct extcon_dev *edev, unsigned int id); | |
| 204 | + bool state); | |
| 213 | 205 | |
| 214 | 206 | /* |
| 215 | − * get/set_property access the property value of each external connector. | |
| 216 | − * They are used to access the property of each cable based on the property id. | |
| 207 | + * Following APIs get/set the property of each external connector. | |
| 208 | + * The 'id' argument indicates the defined external connector | |
| 209 | + * and the 'prop' indicates the extcon property. | |
| 210 | + * | |
| 211 | + * And extcon_get/set_property_capability() set the capability of the property | |
| 212 | + * for each external connector. They are used to set the capability of the | |
| 213 | + * property of each external connector based on the id and property. | |
| 217 | 214 | */ |
| 218 | 215 | extern int extcon_get_property(struct extcon_dev *edev, unsigned int id, |
| 219 | 216 | unsigned int prop, |
| @@ -224,28 +221,24 @@ extern int extcon_set_property(struct extcon_dev *edev, unsigned int id, · 4 unmodified lines | ||
| 224 | 221 | extern int extcon_set_property_sync(struct extcon_dev *edev, unsigned int id, |
| 225 | 222 | unsigned int prop, |
| 226 | 223 | union extcon_property_value prop_val); |
| 227 | − | |
| 228 | −/* | |
| 229 | − * get/set_property_capability set the capability of the property for each | |
| 230 | − * external connector. They are used to set the capability of the property | |
| 231 | − * of each external connector based on the id and property. | |
| 232 | − */ | |
| 233 | 224 | extern int extcon_get_property_capability(struct extcon_dev *edev, |
| 234 | 225 | unsigned int id, unsigned int prop); |
| 235 | 226 | extern int extcon_set_property_capability(struct extcon_dev *edev, |
| 236 | 227 | unsigned int id, unsigned int prop); |
| 237 | 228 | |
| 238 | 229 | /* |
| 239 | − * Following APIs are to monitor the status change of the external connectors. | |
| 230 | + * Following APIs register the notifier block in order to detect | |
| 231 | + * the change of both state and property value for each external connector. | |
| 232 | + * | |
| 240 | 233 | * extcon_register_notifier(*edev, id, *nb) : Register a notifier block |
| 241 | 234 | * for specific external connector of the extcon. |
| 242 | 235 | * extcon_register_notifier_all(*edev, *nb) : Register a notifier block |
| 243 | 236 | * for all supported external connectors of the extcon. |
| 244 | 237 | */ |
| 245 | 238 | extern int extcon_register_notifier(struct extcon_dev *edev, unsigned int id, |
| 246 | − struct notifier_block *nb); | |
| 239 | + struct notifier_block *nb); | |
| 247 | 240 | extern int extcon_unregister_notifier(struct extcon_dev *edev, unsigned int id, |
| 248 | − struct notifier_block *nb); | |
| 241 | + struct notifier_block *nb); | |
| 249 | 242 | extern int devm_extcon_register_notifier(struct device *dev, |
| 250 | 243 | struct extcon_dev *edev, unsigned int id, |
| 251 | 244 | struct notifier_block *nb); |
| @@ -265,16 +258,15 @@ extern void devm_extcon_unregister_notifier_all(struct device *dev, · 13 unmodified lines | ||
| 265 | 258 | struct notifier_block *nb); |
| 266 | 259 | |
| 267 | 260 | /* |
| 268 | − * Following API get the extcon device from devicetree. | |
| 269 | − * This function use phandle of devicetree to get extcon device directly. | |
| 261 | + * Following APIs get the extcon_dev from devicetree or by through extcon name. | |
| 270 | 262 | */ |
| 263 | +extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name); | |
| 271 | 264 | extern struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, |
| 272 | 265 | int index); |
| 273 | 266 | |
| 274 | −/* Following API to get information of extcon device */ | |
| 267 | +/* Following API get the name of extcon device. */ | |
| 275 | 268 | extern const char *extcon_get_edev_name(struct extcon_dev *edev); |
| 276 | 269 | |
| 277 | − | |
| 278 | 270 | #else /* CONFIG_EXTCON */ |
| 279 | 271 | static inline int extcon_dev_register(struct extcon_dev *edev) |
| 280 | 272 | { |
| @@ -284,13 +276,13 @@ static inline int extcon_dev_register(struct extcon_dev *edev) · 3 unmodified lines | ||
| 284 | 276 | static inline void extcon_dev_unregister(struct extcon_dev *edev) { } |
| 285 | 277 | |
| 286 | 278 | static inline int devm_extcon_dev_register(struct device *dev, |
| 287 | − struct extcon_dev *edev) | |
| 279 | + struct extcon_dev *edev) | |
| 288 | 280 | { |
| 289 | 281 | return -EINVAL; |
| 290 | 282 | } |
| 291 | 283 | |
| 292 | 284 | static inline void devm_extcon_dev_unregister(struct device *dev, |
| 293 | − struct extcon_dev *edev) { } | |
| 285 | + struct extcon_dev *edev) { } | |
| 294 | 286 | |
| 295 | 287 | static inline struct extcon_dev *extcon_dev_allocate(const unsigned int *cable) |
| 296 | 288 | { |
| @@ -300,7 +292,7 @@ static inline struct extcon_dev *extcon_dev_allocate(const unsigned int *cable) · 3 unmodified lines | ||
| 300 | 292 | static inline void extcon_dev_free(struct extcon_dev *edev) { } |
| 301 | 293 | |
| 302 | 294 | static inline struct extcon_dev *devm_extcon_dev_allocate(struct device *dev, |
| 303 | − const unsigned int *cable) | |
| 295 | + const unsigned int *cable) | |
| 304 | 296 | { |
| 305 | 297 | return ERR_PTR(-ENOSYS); |
| 306 | 298 | } |
| @@ -314,13 +306,13 @@ static inline int extcon_get_state(struct extcon_dev *edev, unsigned int id) · 7 unmodified lines | ||
| 314 | 306 | } |
| 315 | 307 | |
| 316 | 308 | static inline int extcon_set_state(struct extcon_dev *edev, unsigned int id, |
| 317 | − bool cable_state) | |
| 309 | + bool state) | |
| 318 | 310 | { |
| 319 | 311 | return 0; |
| 320 | 312 | } |
| 321 | 313 | |
| 322 | 314 | static inline int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id, |
| 323 | − bool cable_state) | |
| 315 | + bool state) | |
| 324 | 316 | { |
| 325 | 317 | return 0; |
| 326 | 318 | } |
| @@ -331,52 +323,45 @@ static inline int extcon_sync(struct extcon_dev *edev, unsigned int id) · 4 unmodified lines | ||
| 331 | 323 | } |
| 332 | 324 | |
| 333 | 325 | static inline int extcon_get_property(struct extcon_dev *edev, unsigned int id, |
| 334 | − unsigned int prop, | |
| 335 | − union extcon_property_value *prop_val) | |
| 326 | + unsigned int prop, | |
| 327 | + union extcon_property_value *prop_val) | |
| 336 | 328 | { |
| 337 | 329 | return 0; |
| 338 | 330 | } |
| 339 | 331 | static inline int extcon_set_property(struct extcon_dev *edev, unsigned int id, |
| 340 | − unsigned int prop, | |
| 341 | − union extcon_property_value prop_val) | |
| 332 | + unsigned int prop, | |
| 333 | + union extcon_property_value prop_val) | |
| 342 | 334 | { |
| 343 | 335 | return 0; |
| 344 | 336 | } |
| 345 | 337 | |
| 346 | 338 | static inline int extcon_set_property_sync(struct extcon_dev *edev, |
| 347 | − unsigned int id, unsigned int prop, | |
| 348 | − union extcon_property_value prop_val) | |
| 339 | + unsigned int id, unsigned int prop, | |
| 340 | + union extcon_property_value prop_val) | |
| 349 | 341 | { |
| 350 | 342 | return 0; |
| 351 | 343 | } |
| 352 | 344 | |
| 353 | 345 | static inline int extcon_get_property_capability(struct extcon_dev *edev, |
| 354 | − unsigned int id, unsigned int prop) | |
| 346 | + unsigned int id, unsigned int prop) | |
| 355 | 347 | { |
| 356 | 348 | return 0; |
| 357 | 349 | } |
| 358 | 350 | |
| 359 | 351 | static inline int extcon_set_property_capability(struct extcon_dev *edev, |
| 360 | − unsigned int id, unsigned int prop) | |
| 352 | + unsigned int id, unsigned int prop) | |
| 361 | 353 | { |
| 362 | 354 | return 0; |
| 363 | 355 | } |
| 364 | 356 | |
| 365 | −static inline struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name) | |
| 366 | −{ | |
| 367 | − return NULL; | |
| 368 | −} | |
| 369 | − | |
| 370 | 357 | static inline int extcon_register_notifier(struct extcon_dev *edev, |
| 371 | − unsigned int id, | |
| 372 | − struct notifier_block *nb) | |
| 358 | + unsigned int id, struct notifier_block *nb) | |
| 373 | 359 | { |
| 374 | 360 | return 0; |
| 375 | 361 | } |
| 376 | 362 | |
| 377 | 363 | static inline int extcon_unregister_notifier(struct extcon_dev *edev, |
| 378 | − unsigned int id, | |
| 379 | − struct notifier_block *nb) | |
| 364 | + unsigned int id, struct notifier_block *nb) | |
| 380 | 365 | { |
| 381 | 366 | return 0; |
| 382 | 367 | } |
| @@ -392,8 +377,13 @@ static inline void devm_extcon_unregister_notifier(struct device *dev, · 9 unmodified lines | ||
| 392 | 377 | struct extcon_dev *edev, unsigned int id, |
| 393 | 378 | struct notifier_block *nb) { } |
| 394 | 379 | |
| 380 | +static inline struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name) | |
| 381 | +{ | |
| 382 | + return ERR_PTR(-ENODEV); | |
| 383 | +} | |
| 384 | + | |
| 395 | 385 | static inline struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, |
| 396 | − int index) | |
| 386 | + int index) | |
| 397 | 387 | { |
| 398 | 388 | return ERR_PTR(-ENODEV); |
| 399 | 389 | } |
| @@ -411,26 +401,14 @@ struct extcon_specific_cable_nb { · 11 unmodified lines | ||
| 411 | 401 | }; |
| 412 | 402 | |
| 413 | 403 | static inline int extcon_register_interest(struct extcon_specific_cable_nb *obj, |
| 414 | − const char *extcon_name, const char *cable_name, | |
| 415 | − struct notifier_block *nb) | |
| 404 | + const char *extcon_name, const char *cable_name, | |
| 405 | + struct notifier_block *nb) | |
| 416 | 406 | { |
| 417 | 407 | return -EINVAL; |
| 418 | 408 | } |
| 419 | 409 | |
| 420 | −static inline int extcon_unregister_interest(struct extcon_specific_cable_nb | |
| 421 | − *obj) | |
| 410 | +static inline int extcon_unregister_interest(struct extcon_specific_cable_nb *obj) | |
| 422 | 411 | { |
| 423 | 412 | return -EINVAL; |
| 424 | 413 | } |
| 425 | − | |
| 426 | −static inline int extcon_get_cable_state_(struct extcon_dev *edev, unsigned int id) | |
| 427 | −{ | |
| 428 | − return extcon_get_state(edev, id); | |
| 429 | −} | |
| 430 | − | |
| 431 | −static inline int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id, | |
| 432 | − bool cable_state) | |
| 433 | −{ | |
| 434 | − return extcon_set_state_sync(edev, id, cable_state); | |
| 435 | −} | |
| 436 | 414 | #endif /* __LINUX_EXTCON_H__ */ |
| @@ -132,6 +132,8 @@ struct fmc_operations { · 131 unmodified lines | ||
| 132 | 132 | uint32_t (*read32)(struct fmc_device *fmc, int offset); |
| 133 | 133 | void (*write32)(struct fmc_device *fmc, uint32_t value, int offset); |
| 134 | 134 | int (*validate)(struct fmc_device *fmc, struct fmc_driver *drv); |
| 135 | + int (*reprogram_raw)(struct fmc_device *f, struct fmc_driver *d, | |
| 136 | + void *gw, unsigned long len); | |
| 135 | 137 | int (*reprogram)(struct fmc_device *f, struct fmc_driver *d, char *gw); |
| 136 | 138 | int (*irq_request)(struct fmc_device *fmc, irq_handler_t h, |
| 137 | 139 | char *name, int flags); |
| @@ -144,6 +146,8 @@ struct fmc_operations { · 6 unmodified lines | ||
| 144 | 146 | }; |
| 145 | 147 | |
| 146 | 148 | /* Prefer this helper rather than calling of fmc->reprogram directly */ |
| 149 | +int fmc_reprogram_raw(struct fmc_device *fmc, struct fmc_driver *d, | |
| 150 | + void *gw, unsigned long len, int sdb_entry); | |
| 147 | 151 | extern int fmc_reprogram(struct fmc_device *f, struct fmc_driver *d, char *gw, |
| 148 | 152 | int sdb_entry); |
| 149 | 153 | |
| @@ -180,6 +184,9 @@ struct fmc_device { · 30 unmodified lines | ||
| 180 | 184 | uint32_t device_id; /* Filled by the device */ |
| 181 | 185 | char *mezzanine_name; /* Defaults to ``fmc'' */ |
| 182 | 186 | void *mezzanine_data; |
| 187 | + | |
| 188 | + struct dentry *dbg_dir; | |
| 189 | + struct dentry *dbg_sdb_dump; | |
| 183 | 190 | }; |
| 184 | 191 | #define to_fmc_device(x) container_of((x), struct fmc_device, dev) |
| 185 | 192 | |
| @@ -217,14 +224,23 @@ static inline void fmc_set_drvdata(struct fmc_device *fmc, void *data) · 31 unmodified lines | ||
| 217 | 224 | dev_set_drvdata(&fmc->dev, data); |
| 218 | 225 | } |
| 219 | 226 | |
| 220 | −/* The 4 access points */ | |
| 227 | +struct fmc_gateware { | |
| 228 | + void *bitstream; | |
| 229 | + unsigned long len; | |
| 230 | +}; | |
| 231 | + | |
| 232 | +/* The 5 access points */ | |
| 221 | 233 | extern int fmc_driver_register(struct fmc_driver *drv); |
| 222 | 234 | extern void fmc_driver_unregister(struct fmc_driver *drv); |
| 223 | 235 | extern int fmc_device_register(struct fmc_device *tdev); |
| 236 | +extern int fmc_device_register_gw(struct fmc_device *tdev, | |
| 237 | + struct fmc_gateware *gw); | |
| 224 | 238 | extern void fmc_device_unregister(struct fmc_device *tdev); |
| 225 | 239 | |
| 226 | −/* Two more for device sets, all driven by the same FPGA */ | |
| 240 | +/* Three more for device sets, all driven by the same FPGA */ | |
| 227 | 241 | extern int fmc_device_register_n(struct fmc_device **devs, int n); |
| 242 | +extern int fmc_device_register_n_gw(struct fmc_device **devs, int n, | |
| 243 | + struct fmc_gateware *gw); | |
| 228 | 244 | extern void fmc_device_unregister_n(struct fmc_device **devs, int n); |
| 229 | 245 | |
| 230 | 246 | /* Internal cross-calls between files; not exported to other modules */ |
| @@ -232,6 +248,23 @@ extern int fmc_match(struct device *dev, struct device_driver *drv); · 1 unmodified line | ||
| 232 | 248 | extern int fmc_fill_id_info(struct fmc_device *fmc); |
| 233 | 249 | extern void fmc_free_id_info(struct fmc_device *fmc); |
| 234 | 250 | extern void fmc_dump_eeprom(const struct fmc_device *fmc); |
| 235 | −extern void fmc_dump_sdb(const struct fmc_device *fmc); | |
| 251 | + | |
| 252 | +/* helpers for FMC operations */ | |
| 253 | +extern int fmc_irq_request(struct fmc_device *fmc, irq_handler_t h, | |
| 254 | + char *name, int flags); | |
| 255 | +extern void fmc_irq_free(struct fmc_device *fmc); | |
| 256 | +extern void fmc_irq_ack(struct fmc_device *fmc); | |
| 257 | +extern int fmc_validate(struct fmc_device *fmc, struct fmc_driver *drv); | |
| 258 | +extern int fmc_gpio_config(struct fmc_device *fmc, struct fmc_gpio *gpio, | |
| 259 | + int ngpio); | |
| 260 | +extern int fmc_read_ee(struct fmc_device *fmc, int pos, void *d, int l); | |
| 261 | +extern int fmc_write_ee(struct fmc_device *fmc, int pos, const void *d, int l); | |
| 262 | + | |
| 263 | +/* helpers for FMC operations */ | |
| 264 | +extern int fmc_irq_request(struct fmc_device *fmc, irq_handler_t h, | |
| 265 | + char *name, int flags); | |
| 266 | +extern void fmc_irq_free(struct fmc_device *fmc); | |
| 267 | +extern void fmc_irq_ack(struct fmc_device *fmc); | |
| 268 | +extern int fmc_validate(struct fmc_device *fmc, struct fmc_driver *drv); | |
| 236 | 269 | |
| 237 | 270 | #endif /* __LINUX_FMC_H__ */ |
| @@ -67,10 +67,14 @@ enum fpga_mgr_states { · 66 unmodified lines | ||
| 67 | 67 | * FPGA Manager flags |
| 68 | 68 | * FPGA_MGR_PARTIAL_RECONFIG: do partial reconfiguration if supported |
| 69 | 69 | * FPGA_MGR_EXTERNAL_CONFIG: FPGA has been configured prior to Linux booting |
| 70 | + * FPGA_MGR_BITSTREAM_LSB_FIRST: SPI bitstream bit order is LSB first | |
| 71 | + * FPGA_MGR_COMPRESSED_BITSTREAM: FPGA bitstream is compressed | |
| 70 | 72 | */ |
| 71 | 73 | #define FPGA_MGR_PARTIAL_RECONFIG BIT(0) |
| 72 | 74 | #define FPGA_MGR_EXTERNAL_CONFIG BIT(1) |
| 73 | 75 | #define FPGA_MGR_ENCRYPTED_BITSTREAM BIT(2) |
| 76 | +#define FPGA_MGR_BITSTREAM_LSB_FIRST BIT(3) | |
| 77 | +#define FPGA_MGR_COMPRESSED_BITSTREAM BIT(4) | |
| 74 | 78 | |
| 75 | 79 | /** |
| 76 | 80 | * struct fpga_image_info - information specific to a FPGA image |
| @@ -2473,9 +2473,13 @@ static inline void bd_unlink_disk_holder(struct block_device *bdev, · 2472 unmodified lines | ||
| 2473 | 2473 | #endif |
| 2474 | 2474 | |
| 2475 | 2475 | /* fs/char_dev.c */ |
| 2476 | −#define CHRDEV_MAJOR_HASH_SIZE 255 | |
| 2476 | +#define CHRDEV_MAJOR_MAX 512 | |
| 2477 | 2477 | /* Marks the bottom of the first segment of free char majors */ |
| 2478 | 2478 | #define CHRDEV_MAJOR_DYN_END 234 |
| 2479 | +/* Marks the top and bottom of the second segment of free char majors */ | |
| 2480 | +#define CHRDEV_MAJOR_DYN_EXT_START 511 | |
| 2481 | +#define CHRDEV_MAJOR_DYN_EXT_END 384 | |
| 2482 | + | |
| 2479 | 2483 | extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *); |
| 2480 | 2484 | extern int register_chrdev_region(dev_t, unsigned, const char *); |
| 2481 | 2485 | extern int __register_chrdev(unsigned int major, unsigned int baseminor, |
| @@ -2502,14 +2506,14 @@ static inline void unregister_chrdev(unsigned int major, const char *name) · 20 unmodified lines | ||
| 2502 | 2506 | #define BDEVT_SIZE 10 /* Largest string for MAJ:MIN for blkdev */ |
| 2503 | 2507 | |
| 2504 | 2508 | #ifdef CONFIG_BLOCK |
| 2505 | −#define BLKDEV_MAJOR_HASH_SIZE 255 | |
| 2509 | +#define BLKDEV_MAJOR_MAX 512 | |
| 2506 | 2510 | extern const char *__bdevname(dev_t, char *buffer); |
| 2507 | 2511 | extern const char *bdevname(struct block_device *bdev, char *buffer); |
| 2508 | 2512 | extern struct block_device *lookup_bdev(const char *); |
| 2509 | 2513 | extern void blkdev_show(struct seq_file *,off_t); |
| 2510 | 2514 | |
| 2511 | 2515 | #else |
| 2512 | −#define BLKDEV_MAJOR_HASH_SIZE 0 | |
| 2516 | +#define BLKDEV_MAJOR_MAX 0 | |
| 2513 | 2517 | #endif |
| 2514 | 2518 | |
| 2515 | 2519 | extern void init_special_inode(struct inode *, umode_t, dev_t); |
| @@ -124,10 +124,7 @@ struct hv_ring_buffer_info { · 123 unmodified lines | ||
| 124 | 124 | spinlock_t ring_lock; |
| 125 | 125 | |
| 126 | 126 | u32 ring_datasize; /* < ring_size */ |
| 127 | − u32 ring_data_startoffset; | |
| 128 | − u32 priv_write_index; | |
| 129 | 127 | u32 priv_read_index; |
| 130 | − u32 cached_read_index; | |
| 131 | 128 | }; |
| 132 | 129 | |
| 133 | 130 | /* |
| @@ -180,19 +177,6 @@ static inline u32 hv_get_bytes_to_write(const struct hv_ring_buffer_info *rbi) · 46 unmodified lines | ||
| 180 | 177 | return write; |
| 181 | 178 | } |
| 182 | 179 | |
| 183 | −static inline u32 hv_get_cached_bytes_to_write( | |
| 184 | − const struct hv_ring_buffer_info *rbi) | |
| 185 | −{ | |
| 186 | − u32 read_loc, write_loc, dsize, write; | |
| 187 | − | |
| 188 | − dsize = rbi->ring_datasize; | |
| 189 | − read_loc = rbi->cached_read_index; | |
| 190 | − write_loc = rbi->ring_buffer->write_index; | |
| 191 | − | |
| 192 | − write = write_loc >= read_loc ? dsize - (write_loc - read_loc) : | |
| 193 | − read_loc - write_loc; | |
| 194 | − return write; | |
| 195 | −} | |
| 196 | 180 | /* |
| 197 | 181 | * VMBUS version is 32 bit entity broken up into |
| 198 | 182 | * two 16 bit quantities: major_number. minor_number. |
| @@ -895,6 +879,8 @@ struct vmbus_channel { · 696 unmodified lines | ||
| 895 | 879 | */ |
| 896 | 880 | enum hv_numa_policy affinity_policy; |
| 897 | 881 | |
| 882 | + bool probe_done; | |
| 883 | + | |
| 898 | 884 | }; |
| 899 | 885 | |
| 900 | 886 | static inline bool is_hvsock_channel(const struct vmbus_channel *c) |
| @@ -1473,55 +1459,6 @@ hv_get_ring_buffer(const struct hv_ring_buffer_info *ring_info) · 572 unmodified lines | ||
| 1473 | 1459 | return ring_info->ring_buffer->buffer; |
| 1474 | 1460 | } |
| 1475 | 1461 | |
| 1476 | −/* | |
| 1477 | − * To optimize the flow management on the send-side, | |
| 1478 | − * when the sender is blocked because of lack of | |
| 1479 | − * sufficient space in the ring buffer, potential the | |
| 1480 | − * consumer of the ring buffer can signal the producer. | |
| 1481 | − * This is controlled by the following parameters: | |
| 1482 | − * | |
| 1483 | − * 1. pending_send_sz: This is the size in bytes that the | |
| 1484 | − * producer is trying to send. | |
| 1485 | − * 2. The feature bit feat_pending_send_sz set to indicate if | |
| 1486 | − * the consumer of the ring will signal when the ring | |
| 1487 | − * state transitions from being full to a state where | |
| 1488 | − * there is room for the producer to send the pending packet. | |
| 1489 | − */ | |
| 1490 | − | |
| 1491 | −static inline void hv_signal_on_read(struct vmbus_channel *channel) | |
| 1492 | −{ | |
| 1493 | − u32 cur_write_sz, cached_write_sz; | |
| 1494 | − u32 pending_sz; | |
| 1495 | − struct hv_ring_buffer_info *rbi = &channel->inbound; | |
| 1496 | − | |
| 1497 | − /* | |
| 1498 | − * Issue a full memory barrier before making the signaling decision. | |
| 1499 | − * Here is the reason for having this barrier: | |
| 1500 | − * If the reading of the pend_sz (in this function) | |
| 1501 | − * were to be reordered and read before we commit the new read | |
| 1502 | − * index (in the calling function) we could | |
| 1503 | − * have a problem. If the host were to set the pending_sz after we | |
| 1504 | − * have sampled pending_sz and go to sleep before we commit the | |
| 1505 | − * read index, we could miss sending the interrupt. Issue a full | |
| 1506 | − * memory barrier to address this. | |
| 1507 | − */ | |
| 1508 | − virt_mb(); | |
| 1509 | − | |
| 1510 | − pending_sz = READ_ONCE(rbi->ring_buffer->pending_send_sz); | |
| 1511 | − /* If the other end is not blocked on write don't bother. */ | |
| 1512 | − if (pending_sz == 0) | |
| 1513 | − return; | |
| 1514 | − | |
| 1515 | − cur_write_sz = hv_get_bytes_to_write(rbi); | |
| 1516 | − | |
| 1517 | − if (cur_write_sz < pending_sz) | |
| 1518 | − return; | |
| 1519 | − | |
| 1520 | − cached_write_sz = hv_get_cached_bytes_to_write(rbi); | |
| 1521 | − if (cached_write_sz < pending_sz) | |
| 1522 | − vmbus_setevent(channel); | |
| 1523 | −} | |
| 1524 | − | |
| 1525 | 1462 | /* |
| 1526 | 1463 | * Mask off host interrupt callback notifications |
| 1527 | 1464 | */ |
| @@ -285,6 +285,11 @@ enum host_event_code { · 284 unmodified lines | ||
| 285 | 285 | EC_HOST_EVENT_HANG_DETECT = 20, |
| 286 | 286 | /* Hang detect logic detected a hang and warm rebooted the AP */ |
| 287 | 287 | EC_HOST_EVENT_HANG_REBOOT = 21, |
| 288 | + /* PD MCU triggering host event */ | |
| 289 | + EC_HOST_EVENT_PD_MCU = 22, | |
| 290 | + | |
| 291 | + /* EC desires to change state of host-controlled USB mux */ | |
| 292 | + EC_HOST_EVENT_USB_MUX = 28, | |
| 288 | 293 | |
| 289 | 294 | /* |
| 290 | 295 | * The high bit of the event mask is not used as a host event code. If |
| @@ -2905,6 +2910,76 @@ struct ec_params_usb_pd_control { · 2614 unmodified lines | ||
| 2905 | 2910 | uint8_t mux; |
| 2906 | 2911 | } __packed; |
| 2907 | 2912 | |
| 2913 | +#define PD_CTRL_RESP_ENABLED_COMMS (1 << 0) /* Communication enabled */ | |
| 2914 | +#define PD_CTRL_RESP_ENABLED_CONNECTED (1 << 1) /* Device connected */ | |
| 2915 | +#define PD_CTRL_RESP_ENABLED_PD_CAPABLE (1 << 2) /* Partner is PD capable */ | |
| 2916 | + | |
| 2917 | +struct ec_response_usb_pd_control_v1 { | |
| 2918 | + uint8_t enabled; | |
| 2919 | + uint8_t role; | |
| 2920 | + uint8_t polarity; | |
| 2921 | + char state[32]; | |
| 2922 | +} __packed; | |
| 2923 | + | |
| 2924 | +#define EC_CMD_USB_PD_PORTS 0x102 | |
| 2925 | + | |
| 2926 | +struct ec_response_usb_pd_ports { | |
| 2927 | + uint8_t num_ports; | |
| 2928 | +} __packed; | |
| 2929 | + | |
| 2930 | +#define EC_CMD_USB_PD_POWER_INFO 0x103 | |
| 2931 | + | |
| 2932 | +#define PD_POWER_CHARGING_PORT 0xff | |
| 2933 | +struct ec_params_usb_pd_power_info { | |
| 2934 | + uint8_t port; | |
| 2935 | +} __packed; | |
| 2936 | + | |
| 2937 | +enum usb_chg_type { | |
| 2938 | + USB_CHG_TYPE_NONE, | |
| 2939 | + USB_CHG_TYPE_PD, | |
| 2940 | + USB_CHG_TYPE_C, | |
| 2941 | + USB_CHG_TYPE_PROPRIETARY, | |
| 2942 | + USB_CHG_TYPE_BC12_DCP, | |
| 2943 | + USB_CHG_TYPE_BC12_CDP, | |
| 2944 | + USB_CHG_TYPE_BC12_SDP, | |
| 2945 | + USB_CHG_TYPE_OTHER, | |
| 2946 | + USB_CHG_TYPE_VBUS, | |
| 2947 | + USB_CHG_TYPE_UNKNOWN, | |
| 2948 | +}; | |
| 2949 | + | |
| 2950 | +struct usb_chg_measures { | |
| 2951 | + uint16_t voltage_max; | |
| 2952 | + uint16_t voltage_now; | |
| 2953 | + uint16_t current_max; | |
| 2954 | + uint16_t current_lim; | |
| 2955 | +} __packed; | |
| 2956 | + | |
| 2957 | +struct ec_response_usb_pd_power_info { | |
| 2958 | + uint8_t role; | |
| 2959 | + uint8_t type; | |
| 2960 | + uint8_t dualrole; | |
| 2961 | + uint8_t reserved1; | |
| 2962 | + struct usb_chg_measures meas; | |
| 2963 | + uint32_t max_power; | |
| 2964 | +} __packed; | |
| 2965 | + | |
| 2966 | +/* Get info about USB-C SS muxes */ | |
| 2967 | +#define EC_CMD_USB_PD_MUX_INFO 0x11a | |
| 2968 | + | |
| 2969 | +struct ec_params_usb_pd_mux_info { | |
| 2970 | + uint8_t port; /* USB-C port number */ | |
| 2971 | +} __packed; | |
| 2972 | + | |
| 2973 | +/* Flags representing mux state */ | |
| 2974 | +#define USB_PD_MUX_USB_ENABLED (1 << 0) | |
| 2975 | +#define USB_PD_MUX_DP_ENABLED (1 << 1) | |
| 2976 | +#define USB_PD_MUX_POLARITY_INVERTED (1 << 2) | |
| 2977 | +#define USB_PD_MUX_HPD_IRQ (1 << 3) | |
| 2978 | + | |
| 2979 | +struct ec_response_usb_pd_mux_info { | |
| 2980 | + uint8_t flags; /* USB_PD_MUX_*-encoded USB mux state */ | |
| 2981 | +} __packed; | |
| 2982 | + | |
| 2908 | 2983 | /*****************************************************************************/ |
| 2909 | 2984 | /* |
| 2910 | 2985 | * Passthru commands |
| @@ -1,13 +1,28 @@ | ||
| 1 | −/* MFD cell driver data for the DS1WM driver */ | |
| 1 | +/* MFD cell driver data for the DS1WM driver | |
| 2 | + * | |
| 3 | + * to be defined in the MFD device that is | |
| 4 | + * using this driver for one of his sub devices | |
| 5 | + */ | |
| 2 | 6 | |
| 3 | 7 | struct ds1wm_driver_data { |
| 4 | 8 | int active_high; |
| 5 | 9 | int clock_rate; |
| 6 | − /* in milliseconds, the amount of time to */ | |
| 7 | − /* sleep following a reset pulse. Zero */ | |
| 8 | − /* should work if your bus devices recover*/ | |
| 9 | − /* time respects the 1-wire spec since the*/ | |
| 10 | − /* ds1wm implements the precise timings of*/ | |
| 11 | − /* a reset pulse/presence detect sequence.*/ | |
| 10 | + /* in milliseconds, the amount of time to | |
| 11 | + * sleep following a reset pulse. Zero | |
| 12 | + * should work if your bus devices recover | |
| 13 | + * time respects the 1-wire spec since the | |
| 14 | + * ds1wm implements the precise timings of | |
| 15 | + * a reset pulse/presence detect sequence. | |
| 16 | + */ | |
| 12 | 17 | unsigned int reset_recover_delay; |
| 18 | + | |
| 19 | + /* Say 1 here for big endian Hardware | |
| 20 | + * (only relevant with bus-shift > 0 | |
| 21 | + */ | |
| 22 | + bool is_hw_big_endian; | |
| 23 | + | |
| 24 | + /* left shift of register number to get register address offsett. | |
| 25 | + * Only 0,1,2 allowed for 8,16 or 32 bit bus width respectively | |
| 26 | + */ | |
| 27 | + unsigned int bus_shift; | |
| 13 | 28 | }; |
| @@ -13,6 +13,8 @@ · 12 unmodified lines | ||
| 13 | 13 | #ifndef _LINUX_MUX_CONSUMER_H |
| 14 | 14 | #define _LINUX_MUX_CONSUMER_H |
| 15 | 15 | |
| 16 | +#include <linux/compiler.h> | |
| 17 | + | |
| 16 | 18 | struct device; |
| 17 | 19 | struct mux_control; |
| 18 | 20 | |
| @@ -12,6 +12,9 @@ · 11 unmodified lines | ||
| 12 | 12 | #ifndef _LINUX_NVMEM_CONSUMER_H |
| 13 | 13 | #define _LINUX_NVMEM_CONSUMER_H |
| 14 | 14 | |
| 15 | +#include <linux/err.h> | |
| 16 | +#include <linux/errno.h> | |
| 17 | + | |
| 15 | 18 | struct device; |
| 16 | 19 | struct device_node; |
| 17 | 20 | /* consumer cookie */ |
| @@ -35,6 +38,7 @@ void nvmem_cell_put(struct nvmem_cell *cell); · 17 unmodified lines | ||
| 35 | 38 | void devm_nvmem_cell_put(struct device *dev, struct nvmem_cell *cell); |
| 36 | 39 | void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len); |
| 37 | 40 | int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len); |
| 41 | +int nvmem_cell_read_u32(struct device *dev, const char *cell_id, u32 *val); | |
| 38 | 42 | |
| 39 | 43 | /* direct nvmem device read/write interface */ |
| 40 | 44 | struct nvmem_device *nvmem_device_get(struct device *dev, const char *name); |
| @@ -85,6 +89,12 @@ static inline int nvmem_cell_write(struct nvmem_cell *cell, · 44 unmodified lines | ||
| 85 | 89 | return -ENOSYS; |
| 86 | 90 | } |
| 87 | 91 | |
| 92 | +static inline int nvmem_cell_read_u32(struct device *dev, | |
| 93 | + const char *cell_id, u32 *val) | |
| 94 | +{ | |
| 95 | + return -ENOSYS; | |
| 96 | +} | |
| 97 | + | |
| 88 | 98 | static inline struct nvmem_device *nvmem_device_get(struct device *dev, |
| 89 | 99 | const char *name) |
| 90 | 100 | { |
| @@ -68,6 +68,7 @@ struct w1_reg_num { · 67 unmodified lines | ||
| 68 | 68 | * @family: module for device family type |
| 69 | 69 | * @family_data: pointer for use by the family module |
| 70 | 70 | * @dev: kernel device identifier |
| 71 | + * @hwmon: pointer to hwmon device | |
| 71 | 72 | * |
| 72 | 73 | */ |
| 73 | 74 | struct w1_slave { |
| @@ -83,6 +84,7 @@ struct w1_slave { · 9 unmodified lines | ||
| 83 | 84 | struct w1_family *family; |
| 84 | 85 | void *family_data; |
| 85 | 86 | struct device dev; |
| 87 | + struct device *hwmon; | |
| 86 | 88 | }; |
| 87 | 89 | |
| 88 | 90 | typedef void (*w1_slave_found_callback)(struct w1_master *, u64); |
| @@ -250,11 +252,13 @@ void w1_remove_master_device(struct w1_bus_master *master); · 161 unmodified lines | ||
| 250 | 252 | * @add_slave: add_slave |
| 251 | 253 | * @remove_slave: remove_slave |
| 252 | 254 | * @groups: sysfs group |
| 255 | + * @chip_info: pointer to struct hwmon_chip_info | |
| 253 | 256 | */ |
| 254 | 257 | struct w1_family_ops { |
| 255 | 258 | int (*add_slave)(struct w1_slave *sl); |
| 256 | 259 | void (*remove_slave)(struct w1_slave *sl); |
| 257 | 260 | const struct attribute_group **groups; |
| 261 | + const struct hwmon_chip_info *chip_info; | |
| 258 | 262 | }; |
| 259 | 263 | |
| 260 | 264 | /** |
| @@ -132,6 +132,7 @@ enum { · 131 unmodified lines | ||
| 132 | 132 | |
| 133 | 133 | /* struct binder_fd_array_object - object describing an array of fds in a buffer |
| 134 | 134 | * @hdr: common header structure |
| 135 | + * @pad: padding to ensure correct alignment | |
| 135 | 136 | * @num_fds: number of file descriptors in the buffer |
| 136 | 137 | * @parent: index in offset array to buffer holding the fd array |
| 137 | 138 | * @parent_offset: start offset of fd array in the buffer |
| @@ -152,6 +153,7 @@ enum { · 14 unmodified lines | ||
| 152 | 153 | */ |
| 153 | 154 | struct binder_fd_array_object { |
| 154 | 155 | struct binder_object_header hdr; |
| 156 | + __u32 pad; | |
| 155 | 157 | binder_size_t num_fds; |
| 156 | 158 | binder_size_t parent; |
| 157 | 159 | binder_size_t parent_offset; |
| @@ -184,6 +186,19 @@ struct binder_version { · 26 unmodified lines | ||
| 184 | 186 | #define BINDER_CURRENT_PROTOCOL_VERSION 8 |
| 185 | 187 | #endif |
| 186 | 188 | |
| 189 | +/* | |
| 190 | + * Use with BINDER_GET_NODE_DEBUG_INFO, driver reads ptr, writes to all fields. | |
| 191 | + * Set ptr to NULL for the first call to get the info for the first node, and | |
| 192 | + * then repeat the call passing the previously returned value to get the next | |
| 193 | + * nodes. ptr will be 0 when there are no more nodes. | |
| 194 | + */ | |
| 195 | +struct binder_node_debug_info { | |
| 196 | + binder_uintptr_t ptr; | |
| 197 | + binder_uintptr_t cookie; | |
| 198 | + __u32 has_strong_ref; | |
| 199 | + __u32 has_weak_ref; | |
| 200 | +}; | |
| 201 | + | |
| 187 | 202 | #define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read) |
| 188 | 203 | #define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, __s64) |
| 189 | 204 | #define BINDER_SET_MAX_THREADS _IOW('b', 5, __u32) |
| @@ -191,6 +206,7 @@ struct binder_version { · 1 unmodified line | ||
| 191 | 206 | #define BINDER_SET_CONTEXT_MGR _IOW('b', 7, __s32) |
| 192 | 207 | #define BINDER_THREAD_EXIT _IOW('b', 8, __s32) |
| 193 | 208 | #define BINDER_VERSION _IOWR('b', 9, struct binder_version) |
| 209 | +#define BINDER_GET_NODE_DEBUG_INFO _IOWR('b', 11, struct binder_node_debug_info) | |
| 194 | 210 | |
| 195 | 211 | /* |
| 196 | 212 | * NOTE: Two special error codes you should check for when calling |
| @@ -10,6 +10,7 @@ · 9 unmodified lines | ||
| 10 | 10 | # CONFIG_USELIB is not set |
| 11 | 11 | CONFIG_ANDROID=y |
| 12 | 12 | CONFIG_ANDROID_BINDER_IPC=y |
| 13 | +CONFIG_ANDROID_BINDER_DEVICES=binder,hwbinder,vndbinder | |
| 13 | 14 | CONFIG_ANDROID_LOW_MEMORY_KILLER=y |
| 14 | 15 | CONFIG_ARMV8_DEPRECATED=y |
| 15 | 16 | CONFIG_ASHMEM=y |
| @@ -138,14 +138,17 @@ void print_usage(char *argv[]) · 137 unmodified lines | ||
| 138 | 138 | |
| 139 | 139 | int main(int argc, char *argv[]) |
| 140 | 140 | { |
| 141 | − int fcopy_fd, len; | |
| 141 | + int fcopy_fd; | |
| 142 | 142 | int error; |
| 143 | 143 | int daemonize = 1, long_index = 0, opt; |
| 144 | 144 | int version = FCOPY_CURRENT_VERSION; |
| 145 | − char *buffer[4096 * 2]; | |
| 146 | − struct hv_fcopy_hdr *in_msg; | |
| 145 | + union { | |
| 146 | + struct hv_fcopy_hdr hdr; | |
| 147 | + struct hv_start_fcopy start; | |
| 148 | + struct hv_do_fcopy copy; | |
| 149 | + __u32 kernel_modver; | |
| 150 | + } buffer = { }; | |
| 147 | 151 | int in_handshake = 1; |
| 148 | − __u32 kernel_modver; | |
| 149 | 152 | |
| 150 | 153 | static struct option long_options[] = { |
| 151 | 154 | {"help", no_argument, 0, 'h' }, |
| @@ -195,32 +198,31 @@ int main(int argc, char *argv[]) · 43 unmodified lines | ||
| 195 | 198 | * In this loop we process fcopy messages after the |
| 196 | 199 | * handshake is complete. |
| 197 | 200 | */ |
| 198 | − len = pread(fcopy_fd, buffer, (4096 * 2), 0); | |
| 201 | + ssize_t len; | |
| 202 | + | |
| 203 | + len = pread(fcopy_fd, &buffer, sizeof(buffer), 0); | |
| 199 | 204 | if (len < 0) { |
| 200 | 205 | syslog(LOG_ERR, "pread failed: %s", strerror(errno)); |
| 201 | 206 | exit(EXIT_FAILURE); |
| 202 | 207 | } |
| 203 | 208 | |
| 204 | 209 | if (in_handshake) { |
| 205 | − if (len != sizeof(kernel_modver)) { | |
| 210 | + if (len != sizeof(buffer.kernel_modver)) { | |
| 206 | 211 | syslog(LOG_ERR, "invalid version negotiation"); |
| 207 | 212 | exit(EXIT_FAILURE); |
| 208 | 213 | } |
| 209 | − kernel_modver = *(__u32 *)buffer; | |
| 210 | 214 | in_handshake = 0; |
| 211 | − syslog(LOG_INFO, "kernel module version: %d", | |
| 212 | − kernel_modver); | |
| 215 | + syslog(LOG_INFO, "kernel module version: %u", | |
| 216 | + buffer.kernel_modver); | |
| 213 | 217 | continue; |
| 214 | 218 | } |
| 215 | 219 | |
| 216 | − in_msg = (struct hv_fcopy_hdr *)buffer; | |
| 217 | − | |
| 218 | − switch (in_msg->operation) { | |
| 220 | + switch (buffer.hdr.operation) { | |
| 219 | 221 | case START_FILE_COPY: |
| 220 | − error = hv_start_fcopy((struct hv_start_fcopy *)in_msg); | |
| 222 | + error = hv_start_fcopy(&buffer.start); | |
| 221 | 223 | break; |
| 222 | 224 | case WRITE_TO_FILE: |
| 223 | − error = hv_copy_data((struct hv_do_fcopy *)in_msg); | |
| 225 | + error = hv_copy_data(&buffer.copy); | |
| 224 | 226 | break; |
| 225 | 227 | case COMPLETE_FCOPY: |
| 226 | 228 | error = hv_copy_finished(); |
| @@ -231,7 +233,7 @@ int main(int argc, char *argv[]) · 4 unmodified lines | ||
| 231 | 233 | |
| 232 | 234 | default: |
| 233 | 235 | syslog(LOG_ERR, "Unknown operation: %d", |
| 234 | − in_msg->operation); | |
| 236 | + buffer.hdr.operation); | |
| 235 | 237 | |
| 236 | 238 | } |
| 237 | 239 | |
| @@ -1136,7 +1136,7 @@ static int process_ip_string(FILE *f, char *ip_string, int type) · 1135 unmodified lines | ||
| 1136 | 1136 | int i = 0; |
| 1137 | 1137 | int j = 0; |
| 1138 | 1138 | char str[256]; |
| 1139 | − char sub_str[10]; | |
| 1139 | + char sub_str[13]; | |
| 1140 | 1140 | int offset = 0; |
| 1141 | 1141 | |
| 1142 | 1142 | memset(addr, 0, sizeof(addr)); |
| @@ -21,6 +21,7 @@ · 20 unmodified lines | ||
| 21 | 21 | #include <sys/types.h> |
| 22 | 22 | #include <sys/poll.h> |
| 23 | 23 | #include <sys/ioctl.h> |
| 24 | +#include <sys/stat.h> | |
| 24 | 25 | #include <fcntl.h> |
| 25 | 26 | #include <stdio.h> |
| 26 | 27 | #include <mntent.h> |
| @@ -30,6 +31,7 @@ · 3 unmodified lines | ||
| 30 | 31 | #include <ctype.h> |
| 31 | 32 | #include <errno.h> |
| 32 | 33 | #include <linux/fs.h> |
| 34 | +#include <linux/major.h> | |
| 33 | 35 | #include <linux/hyperv.h> |
| 34 | 36 | #include <syslog.h> |
| 35 | 37 | #include <getopt.h> |
| @@ -70,6 +72,7 @@ static int vss_operate(int operation) · 34 unmodified lines | ||
| 70 | 72 | char match[] = "/dev/"; |
| 71 | 73 | FILE *mounts; |
| 72 | 74 | struct mntent *ent; |
| 75 | + struct stat sb; | |
| 73 | 76 | char errdir[1024] = {0}; |
| 74 | 77 | unsigned int cmd; |
| 75 | 78 | int error = 0, root_seen = 0, save_errno = 0; |
| @@ -92,6 +95,10 @@ static int vss_operate(int operation) · 16 unmodified lines | ||
| 92 | 95 | while ((ent = getmntent(mounts))) { |
| 93 | 96 | if (strncmp(ent->mnt_fsname, match, strlen(match))) |
| 94 | 97 | continue; |
| 98 | + if (stat(ent->mnt_fsname, &sb) == -1) | |
| 99 | + continue; | |
| 100 | + if (S_ISBLK(sb.st_mode) && major(sb.st_rdev) == LOOP_MAJOR) | |
| 101 | + continue; | |
| 95 | 102 | if (hasmntopt(ent, MNTOPT_RO) != NULL) |
| 96 | 103 | continue; |
| 97 | 104 | if (strcmp(ent->mnt_type, "vfat") == 0) |
| @@ -24,6 +24,12 @@ · 23 unmodified lines | ||
| 24 | 24 | /* ETMv3.5/PTM's ETMCR config bit */ |
| 25 | 25 | #define ETM_OPT_CYCACC 12 |
| 26 | 26 | #define ETM_OPT_TS 28 |
| 27 | +#define ETM_OPT_RETSTK 29 | |
| 28 | + | |
| 29 | +/* ETMv4 CONFIGR programming bits for the ETM OPTs */ | |
| 30 | +#define ETM4_CFG_BIT_CYCACC 4 | |
| 31 | +#define ETM4_CFG_BIT_TS 11 | |
| 32 | +#define ETM4_CFG_BIT_RETSTK 12 | |
| 27 | 33 | |
| 28 | 34 | static inline int coresight_get_trace_id(int cpu) |
| 29 | 35 | { |
| @@ -266,6 +266,32 @@ static u64 cs_etm_get_config(struct auxtrace_record *itr) · 265 unmodified lines | ||
| 266 | 266 | return config; |
| 267 | 267 | } |
| 268 | 268 | |
| 269 | +#ifndef BIT | |
| 270 | +#define BIT(N) (1UL << (N)) | |
| 271 | +#endif | |
| 272 | + | |
| 273 | +static u64 cs_etmv4_get_config(struct auxtrace_record *itr) | |
| 274 | +{ | |
| 275 | + u64 config = 0; | |
| 276 | + u64 config_opts = 0; | |
| 277 | + | |
| 278 | + /* | |
| 279 | + * The perf event variable config bits represent both | |
| 280 | + * the command line options and register programming | |
| 281 | + * bits in ETMv3/PTM. For ETMv4 we must remap options | |
| 282 | + * to real bits | |
| 283 | + */ | |
| 284 | + config_opts = cs_etm_get_config(itr); | |
| 285 | + if (config_opts & BIT(ETM_OPT_CYCACC)) | |
| 286 | + config |= BIT(ETM4_CFG_BIT_CYCACC); | |
| 287 | + if (config_opts & BIT(ETM_OPT_TS)) | |
| 288 | + config |= BIT(ETM4_CFG_BIT_TS); | |
| 289 | + if (config_opts & BIT(ETM_OPT_RETSTK)) | |
| 290 | + config |= BIT(ETM4_CFG_BIT_RETSTK); | |
| 291 | + | |
| 292 | + return config; | |
| 293 | +} | |
| 294 | + | |
| 269 | 295 | static size_t |
| 270 | 296 | cs_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused, |
| 271 | 297 | struct perf_evlist *evlist __maybe_unused) |
| @@ -363,7 +389,7 @@ static void cs_etm_get_metadata(int cpu, u32 *offset, · 91 unmodified lines | ||
| 363 | 389 | magic = __perf_cs_etmv4_magic; |
| 364 | 390 | /* Get trace configuration register */ |
| 365 | 391 | info->priv[*offset + CS_ETMV4_TRCCONFIGR] = |
| 366 | − cs_etm_get_config(itr); | |
| 392 | + cs_etmv4_get_config(itr); | |
| 367 | 393 | /* Get traceID from the framework */ |
| 368 | 394 | info->priv[*offset + CS_ETMV4_TRCTRACEIDR] = |
| 369 | 395 | coresight_get_trace_id(cpu); |